ncs_mdes 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,6 +61,8 @@ module NcsNavigator::Mdes
61
61
  create('3.1', '3.1/NCS_Transmission_Schema_3.1.01.00.xsd', '3.1.01.00')
62
62
  when '3.2'
63
63
  create('3.2', '3.2/NCS_Transmission_Schema_3.2.00.00.xsd', '3.2.00.00')
64
+ when '3.3'
65
+ create('3.3', '3.3/NCS_Transmission_Schema_3.3.00.00.xsd', '3.3.00.00')
64
66
  else
65
67
  raise "MDES #{version} is not supported by this version of ncs_mdes"
66
68
  end
@@ -83,7 +83,7 @@ module NcsNavigator::Mdes
83
83
  '//xs:element[@name="transmission_tables"]/xs:complexType/xs:sequence/xs:element',
84
84
  source_documents.xmlns
85
85
  ).collect { |table_elt|
86
- TransmissionTable.from_element(table_elt, :log => @log)
86
+ TransmissionTable.from_element(table_elt, :log => @log, :heuristic_overrides => heuristic_overrides)
87
87
  }.tap { |tables|
88
88
  tables.each { |t| t.variables.each { |v| v.resolve_type!(types, :log => @log) } }
89
89
  # All types must be resolved before doing FK resolution or
@@ -165,7 +165,7 @@ module NcsNavigator::Mdes
165
165
 
166
166
  def read_types
167
167
  xsd.xpath('//xs:simpleType[@name]', source_documents.xmlns).collect do |type_elt|
168
- VariableType.from_xsd_simple_type(type_elt, :log => @log)
168
+ VariableType.from_xsd_simple_type(type_elt, :log => @log, :heuristic_overrides => heuristic_overrides)
169
169
  end
170
170
  end
171
171
  private :read_types
@@ -12,9 +12,13 @@ module NcsNavigator::Mdes
12
12
  log = options[:log] || NcsNavigator::Mdes.default_logger
13
13
 
14
14
  new(element['name']).tap do |table|
15
+ opts = options.merge({
16
+ :log => log,
17
+ :current_table_name => table.name
18
+ })
15
19
  table.variables = element.
16
20
  xpath('xs:complexType/xs:sequence/xs:element', SourceDocuments.xmlns).
17
- collect { |col_elt| Variable.from_element(col_elt, options) }
21
+ collect { |col_elt| Variable.from_element(col_elt, opts) }
18
22
  end
19
23
  end
20
24
 
@@ -171,5 +175,24 @@ module NcsNavigator::Mdes
171
175
  def diff(other_table, options={})
172
176
  Differences::Entry.compute(self, other_table, DIFF_CRITERIA, options)
173
177
  end
178
+
179
+ ##
180
+ # This method allows for a table to have a multiple column PK. At this
181
+ # writing there are not any, so this is out of an abundance of caution.
182
+ #
183
+ # @return [Array<Variable>] the variables comprising the PK for this table,
184
+ # or an empty array if they can't be determined.
185
+ def primary_key_variables
186
+ pks = variables.select { |v| v.type.name == 'primaryKeyType' }
187
+ if !pks.empty?
188
+ pks
189
+ elsif name == 'study_center'
190
+ variables.select { |v| v.name == 'sc_id' }
191
+ elsif name == 'psu'
192
+ variables.select { |v| v.name == 'psu_id' }
193
+ else
194
+ []
195
+ end
196
+ end
174
197
  end
175
198
  end
@@ -90,21 +90,40 @@ module NcsNavigator::Mdes
90
90
  when '4'; :retired;
91
91
  else element['ncsdoc:status'];
92
92
  end
93
- var.type =
94
- if element['type']
95
- if element['type'] =~ /^xs:/
96
- VariableType.xml_schema_type(element['type'].sub(/^xs:/, ''))
97
- else
98
- VariableType.reference(element['type'])
99
- end
100
- elsif element.elements.collect { |e| e.name } == %w(simpleType)
101
- VariableType.from_xsd_simple_type(element.elements.first, options)
102
- else
103
- log.warn("Could not determine a type for variable #{var.name.inspect} on line #{element.line}")
104
- nil
105
- end
93
+ var.type = type_for(element, var.name, log, options)
94
+ end
95
+ end
96
+
97
+ def type_for(element, name, log, options)
98
+ variable_type_override = deep_key_or_nil(
99
+ options, :heuristic_overrides, 'variable_type_references', options[:current_table_name], name)
100
+
101
+ named_type = variable_type_override || element['type']
102
+
103
+ if named_type
104
+ if named_type =~ /^xs:/
105
+ VariableType.xml_schema_type(named_type.sub(/^xs:/, ''))
106
+ else
107
+ VariableType.reference(named_type)
108
+ end
109
+ elsif element.elements.collect { |e| e.name } == %w(simpleType)
110
+ VariableType.from_xsd_simple_type(element.elements.first, options)
111
+ else
112
+ log.warn("Could not determine a type for variable #{name.inspect} on line #{element.line}")
113
+ nil
114
+ end
115
+ end
116
+ private :type_for
117
+
118
+ def deep_key_or_nil(h, *keys)
119
+ value = h[keys.shift]
120
+ if value.respond_to?(:[]) && !keys.empty?
121
+ deep_key_or_nil(value, *keys)
122
+ else
123
+ value
106
124
  end
107
125
  end
126
+ private :deep_key_or_nil
108
127
  end
109
128
 
110
129
  def initialize(name)
@@ -1,5 +1,5 @@
1
1
  module NcsNavigator
2
2
  module Mdes
3
- VERSION = '0.12.0'
3
+ VERSION = '0.13.0'
4
4
  end
5
5
  end
@@ -19,7 +19,7 @@ National Children's Study's Master Data Element Specification.
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency 'nokogiri', '~> 1.5.6'
22
+ s.add_dependency 'nokogiri', '~> 1.5.7'
23
23
 
24
24
  s.add_development_dependency 'rspec', '~> 2.6.0' # Can't use 2.7.0 due to #477
25
25
  s.add_development_dependency 'rake', '~> 0.9.2'
@@ -200,6 +200,20 @@ module NcsNavigator::Mdes
200
200
  end
201
201
  end
202
202
 
203
+ describe '3.3' do
204
+ let(:version) { '3.3' }
205
+
206
+ include_context 'version docs'
207
+
208
+ it 'has the correct path for the schema' do
209
+ subject.schema.should =~ %r{3.3/NCS_Transmission_Schema_3.3.00.00.xsd$}
210
+ end
211
+
212
+ it 'has a different specification_version' do
213
+ subject.specification_version.should == '3.3.00.00'
214
+ end
215
+ end
216
+
203
217
  it 'fails for an unsupported version' do
204
218
  lambda { SourceDocuments.get('1.0') }.
205
219
  should raise_error('MDES 1.0 is not supported by this version of ncs_mdes')
@@ -31,6 +31,17 @@ module NcsNavigator::Mdes
31
31
  should be_a TransmissionTable
32
32
  end
33
33
 
34
+ it 'passes down heuristic overrides' do
35
+ spec = Specification.new('2.0', :log => logger)
36
+
37
+ TransmissionTable.should_receive(:from_element).
38
+ with(anything, include(:heuristic_overrides => spec.heuristic_overrides)).
39
+ at_least(:once).
40
+ and_return(TransmissionTable.new('mocked'))
41
+
42
+ spec.transmission_tables # force parsing
43
+ end
44
+
34
45
  shared_examples 'tables fully resolved' do
35
46
  let!(:tables) { Specification.new(version, :log => logger).transmission_tables }
36
47
 
@@ -65,6 +76,18 @@ module NcsNavigator::Mdes
65
76
  index = tables.each_with_object(Hash.new(0)) { |t, acc| acc[t.child_instrument_table?] += 1 }
66
77
  index.keys.sort_by { |k| k.inspect }.should == [false, nil, true]
67
78
  end
79
+
80
+ it 'has a primary key for every table' do
81
+ no_pk_tables = tables.select { |table| table.primary_key_variables.empty? }
82
+
83
+ no_pk_tables.should == []
84
+ end
85
+
86
+ it 'has no multi-column primary keys' do
87
+ multi_column_key_tables = tables.select { |table| table.primary_key_variables.size > 1 }
88
+
89
+ multi_column_key_tables.should == []
90
+ end
68
91
  end
69
92
 
70
93
  context 'in version 1.2' do
@@ -115,6 +138,13 @@ module NcsNavigator::Mdes
115
138
 
116
139
  include_examples 'tables fully resolved'
117
140
  end
141
+
142
+ context 'in version 3.3' do
143
+ let(:version) { '3.3' }
144
+ let(:expected_table_count) { 668 }
145
+
146
+ include_examples 'tables fully resolved'
147
+ end
118
148
  end
119
149
 
120
150
  describe '#specification_version' do
@@ -198,6 +228,14 @@ module NcsNavigator::Mdes
198
228
  disposition_codes.size.should == 332
199
229
  end
200
230
  end
231
+
232
+ context 'in version 3.3' do
233
+ let(:disposition_codes) { Specification.new('3.3', :log => logger).disposition_codes }
234
+
235
+ it 'has 332 codes' do
236
+ disposition_codes.size.should == 332
237
+ end
238
+ end
201
239
  end
202
240
 
203
241
  describe '#[]' do
@@ -226,6 +264,17 @@ module NcsNavigator::Mdes
226
264
  should be_a VariableType
227
265
  end
228
266
 
267
+ it 'passes down heuristic overrides when parsing' do
268
+ spec = Specification.new('2.0', :log => logger)
269
+
270
+ VariableType.should_receive(:from_xsd_simple_type).
271
+ with(anything, include(:heuristic_overrides => spec.heuristic_overrides)).
272
+ at_least(:once).
273
+ and_return(VariableType.new('mocked'))
274
+
275
+ spec.types # force parsing
276
+ end
277
+
229
278
  shared_examples 'types fully resolved' do
230
279
  let!(:types) { Specification.new(version, :log => logger).types }
231
280
 
@@ -286,6 +335,13 @@ module NcsNavigator::Mdes
286
335
 
287
336
  include_examples 'types fully resolved'
288
337
  end
338
+
339
+ context 'version 3.3' do
340
+ let(:version) { '3.3' }
341
+ let(:expected_type_count) { 656 }
342
+
343
+ include_examples 'types fully resolved'
344
+ end
289
345
  end
290
346
 
291
347
  describe '#diff' do
@@ -46,7 +46,8 @@ module NcsNavigator::Mdes
46
46
  XSD
47
47
  }
48
48
 
49
- subject { TransmissionTable.from_element(element) }
49
+ subject { TransmissionTable.from_element(element, options) }
50
+ let(:options) { { } }
50
51
 
51
52
  it 'has the right name' do
52
53
  subject.name.should == 'study_center'
@@ -56,6 +57,29 @@ XSD
56
57
  subject.variables.collect(&:name).
57
58
  should == %w(sc_id sc_name comments transaction_type)
58
59
  end
60
+
61
+ it 'sends the current table name when constructing variables' do
62
+ Variable.should_receive(:from_element).
63
+ with(anything, include(:current_table_name => 'study_center')).
64
+ at_least(:once)
65
+
66
+ subject # to force parsing
67
+ end
68
+
69
+ it 'does not leak the current table name to the outer options hash' do
70
+ subject # to force parsing
71
+ options.should_not have_key(:current_table_name)
72
+ end
73
+
74
+ it 'passes down provided options when constructing variables' do
75
+ options[:foo] = 'bar'
76
+
77
+ Variable.should_receive(:from_element).
78
+ with(anything, include(:foo => 'bar')).
79
+ at_least(:once)
80
+
81
+ subject # to force parsing
82
+ end
59
83
  end
60
84
 
61
85
  describe '#initialize' do
@@ -256,5 +280,58 @@ XSD
256
280
  end
257
281
  end
258
282
  end
283
+
284
+ describe '#primary_key_variables' do
285
+ def table_with_typed_variables(table_name, variables_and_types)
286
+ TransmissionTable.new(table_name).tap do |t|
287
+ t.variables = variables_and_types.collect do |vn, type|
288
+ Variable.new(vn).tap do |v|
289
+ v.type = VariableType.new(type)
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ describe 'for a regular table' do
296
+ let(:table) {
297
+ table_with_typed_variables('example',
298
+ 'foo' => 'primaryKeyType',
299
+ 'bar' => 'primaryKeyType',
300
+ 'baz' => 'foreignKeyTypeRequired',
301
+ )
302
+ }
303
+
304
+ it 'finds all the variables with the PK type' do
305
+ table.primary_key_variables.collect(&:name).should == %w(foo bar)
306
+ end
307
+ end
308
+
309
+ describe 'for psu' do
310
+ let(:psu_table) {
311
+ table_with_typed_variables('psu',
312
+ 'sc_id' => 'study_center_cl1',
313
+ 'psu_id' => 'psu_cl1',
314
+ 'psu_name' => nil,
315
+ )
316
+ }
317
+
318
+ it 'finds the PK' do
319
+ psu_table.primary_key_variables.collect(&:name).should == ['psu_id']
320
+ end
321
+ end
322
+
323
+ describe 'for study_center' do
324
+ let(:sc_table) {
325
+ table_with_typed_variables('study_center',
326
+ 'sc_id' => 'study_center_cl1',
327
+ 'sc_name' => nil,
328
+ )
329
+ }
330
+
331
+ it 'finds the PK' do
332
+ sc_table.primary_key_variables.collect(&:name).should == ['sc_id']
333
+ end
334
+ end
335
+ end
259
336
  end
260
337
  end
@@ -5,8 +5,8 @@ require 'nokogiri'
5
5
  module NcsNavigator::Mdes
6
6
  describe Variable do
7
7
  describe '.from_element' do
8
- def variable(s)
9
- Variable.from_element(schema_element(s), :log => logger)
8
+ def variable(s, options={})
9
+ Variable.from_element(schema_element(s), { :log => logger }.merge(options))
10
10
  end
11
11
 
12
12
  let(:comments) {
@@ -74,6 +74,61 @@ XSD
74
74
  sc_id.type.should be_reference
75
75
  end
76
76
  end
77
+
78
+ context 'with an override' do
79
+ let(:options) {
80
+ {
81
+ :heuristic_overrides => {
82
+ 'variable_type_references' => {
83
+ overridden_table_name => {
84
+ 'not_a_pk' => override_type
85
+ }
86
+ }
87
+ },
88
+ :current_table_name => overridden_table_name
89
+ }
90
+ }
91
+
92
+ let(:overridden_table_name) { 'link_whatever' }
93
+ let(:type_in_xsd) { 'ncs:primaryKeyType' }
94
+ let(:override_type) { 'ncs:foreignKeyTypeRequired' }
95
+
96
+ let(:subject) {
97
+ variable("<xs:element name='not_a_pk' type='#{type_in_xsd}'/>", options)
98
+ }
99
+
100
+ it 'uses an override which matches' do
101
+ subject.type.name.should == 'ncs:foreignKeyTypeRequired'
102
+ end
103
+
104
+ it 'does not use an override which does not match by table name' do
105
+ options[:current_table_name] = 'another_table'
106
+
107
+ subject.type.name.should == type_in_xsd
108
+ end
109
+
110
+ it 'does not use an override which does not match by variable name' do
111
+ options[:heuristic_overrides]['variable_type_references']['link_whatever'] = {
112
+ 'another_overridden_variable' => 'xs:integer'
113
+ }
114
+
115
+ subject.type.name.should == type_in_xsd
116
+ end
117
+
118
+ context 'when the override is an schema type' do
119
+ let(:override_type) { 'xs:integer' }
120
+
121
+ it 'treats extracts the XSD type' do
122
+ subject.type.base_type.should == :integer
123
+ end
124
+ end
125
+
126
+ context 'when the override is an internal reference' do
127
+ it 'is a reference' do
128
+ subject.type.should be_reference
129
+ end
130
+ end
131
+ end
77
132
  end
78
133
 
79
134
  context 'when none present' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ncs_mdes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-12 00:00:00.000000000 Z
12
+ date: 2013-05-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.5.6
21
+ version: 1.5.7
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.5.6
29
+ version: 1.5.7
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +148,11 @@ files:
148
148
  - documents/3.2/disposition_codes.yml
149
149
  - documents/3.2/extract_disposition_codes.rb
150
150
  - documents/3.2/heuristic_overrides.yml
151
+ - documents/3.3/NCS_Transmission_Schema_3.3.00.00.xsd
152
+ - documents/3.3/child_or_parent_instrument_tables.yml
153
+ - documents/3.3/disposition_codes.yml
154
+ - documents/3.3/extract_disposition_codes.rb
155
+ - documents/3.3/heuristic_overrides.yml
151
156
  - documents/scan_p_ids_for_children.rb
152
157
  - lib/ncs_navigator/mdes.rb
153
158
  - lib/ncs_navigator/mdes/code_list.rb
@@ -190,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
190
195
  version: '0'
191
196
  segments:
192
197
  - 0
193
- hash: -1618062826933135286
198
+ hash: 2077666531396508902
194
199
  required_rubygems_version: !ruby/object:Gem::Requirement
195
200
  none: false
196
201
  requirements:
@@ -199,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
204
  version: '0'
200
205
  segments:
201
206
  - 0
202
- hash: -1618062826933135286
207
+ hash: 2077666531396508902
203
208
  requirements: []
204
209
  rubyforge_project:
205
210
  rubygems_version: 1.8.25