ncs_mdes 0.11.0 → 0.12.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.
Files changed (35) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/Gemfile +1 -1
  3. data/README.md +15 -5
  4. data/bin/mdes-console +3 -1
  5. data/documents/1.2/child_or_parent_instrument_tables.yml +28 -0
  6. data/documents/2.0/child_or_parent_instrument_tables.yml +68 -0
  7. data/documents/2.1/child_or_parent_instrument_tables.yml +74 -0
  8. data/documents/2.2/child_or_parent_instrument_tables.yml +98 -0
  9. data/documents/3.0/child_or_parent_instrument_tables.yml +119 -0
  10. data/documents/3.1/child_or_parent_instrument_tables.yml +187 -0
  11. data/documents/3.2/child_or_parent_instrument_tables.yml +192 -0
  12. data/documents/scan_p_ids_for_children.rb +39 -0
  13. data/lib/ncs_navigator/mdes/code_list.rb +94 -0
  14. data/lib/ncs_navigator/mdes/differences/collection.rb +47 -0
  15. data/lib/ncs_navigator/mdes/differences/collection_criterion.rb +59 -0
  16. data/lib/ncs_navigator/mdes/differences/entry.rb +48 -0
  17. data/lib/ncs_navigator/mdes/differences/value.rb +7 -0
  18. data/lib/ncs_navigator/mdes/differences/value_criterion.rb +58 -0
  19. data/lib/ncs_navigator/mdes/differences.rb +12 -0
  20. data/lib/ncs_navigator/mdes/source_documents.rb +27 -0
  21. data/lib/ncs_navigator/mdes/specification.rb +45 -0
  22. data/lib/ncs_navigator/mdes/transmission_table.rb +61 -0
  23. data/lib/ncs_navigator/mdes/variable.rb +58 -4
  24. data/lib/ncs_navigator/mdes/variable_type.rb +27 -62
  25. data/lib/ncs_navigator/mdes/version.rb +1 -1
  26. data/lib/ncs_navigator/mdes.rb +5 -1
  27. data/spec/differences_matchers.rb +40 -0
  28. data/spec/ncs_navigator/mdes/code_list_spec.rb +178 -0
  29. data/spec/ncs_navigator/mdes/source_documents_spec.rb +14 -0
  30. data/spec/ncs_navigator/mdes/specification_spec.rb +30 -0
  31. data/spec/ncs_navigator/mdes/transmission_table_spec.rb +77 -0
  32. data/spec/ncs_navigator/mdes/variable_spec.rb +244 -0
  33. data/spec/ncs_navigator/mdes/variable_type_spec.rb +161 -40
  34. data/spec/spec_helper.rb +6 -0
  35. metadata +24 -5
@@ -29,10 +29,19 @@ module NcsNavigator::Mdes
29
29
  # table.)
30
30
  attr_accessor :variables
31
31
 
32
+ ##
33
+ # @see #child_instrument_table?
34
+ # @return [void]
35
+ attr_writer :child_instrument_table
36
+
32
37
  def initialize(name)
33
38
  @name = name
34
39
  end
35
40
 
41
+ def variables
42
+ @variables ||= []
43
+ end
44
+
36
45
  ##
37
46
  # Search for a variable by name.
38
47
  #
@@ -110,5 +119,57 @@ module NcsNavigator::Mdes
110
119
  def operational_table?
111
120
  !instrument_table?
112
121
  end
122
+
123
+ ##
124
+ # Is this a child instrument data table? (As opposed to a parent instrument
125
+ # data table or neither.)
126
+ #
127
+ # This reports the type of participant whose p_id should go in this table's
128
+ # p_id variable.
129
+ #
130
+ # Return values:
131
+ #
132
+ # * `true`: The p_id should be a child's p_id.
133
+ # * `false`: The p_id should be a parent's p_id.
134
+ # * `nil`: The table isn't an instrument data table, or it doesn't have a p_id
135
+ # variable, or the childness of the p_id isn't known.
136
+ #
137
+ # @return [true,false,nil]
138
+ def child_instrument_table?
139
+ @child_instrument_table
140
+ end
141
+
142
+ ##
143
+ # Is this a parent instrument data table? (As opposed to a child instrument
144
+ # data table or neither.)
145
+ #
146
+ # This reports the type of participant whose p_id should go in this table's
147
+ # p_id variable.
148
+ #
149
+ # Return values:
150
+ #
151
+ # * `true`: The p_id should be a parent's p_id.
152
+ # * `false`: The p_id should be a child's p_id.
153
+ # * `nil`: The table isn't an instrument data table, or it doesn't have a p_id
154
+ # variable, or the childness of the p_id isn't known.
155
+ #
156
+ # @return [true,false,nil]
157
+ def parent_instrument_table?
158
+ child_instrument_table?.nil? ? nil : !child_instrument_table?
159
+ end
160
+
161
+ # @private
162
+ DIFF_CRITERIA = {
163
+ :name => Differences::ValueCriterion.new,
164
+ :variables => Differences::CollectionCriterion.new(:name)
165
+ }
166
+
167
+ ##
168
+ # Computes the differences between this table and the other.
169
+ #
170
+ # @return [Differences::Entry,nil]
171
+ def diff(other_table, options={})
172
+ Differences::Entry.compute(self, other_table, DIFF_CRITERIA, options)
173
+ end
113
174
  end
114
175
  end
@@ -111,10 +111,6 @@ module NcsNavigator::Mdes
111
111
  @name = name
112
112
  end
113
113
 
114
- def constraints
115
- @constraints ||= []
116
- end
117
-
118
114
  ##
119
115
  # Is a value for the variable mandatory for a valid submission?
120
116
  #
@@ -205,5 +201,63 @@ module NcsNavigator::Mdes
205
201
  end
206
202
  end
207
203
  end
204
+
205
+ # @private
206
+ class EmbeddedVariableTypeCriterion
207
+ def apply(vt1, vt2, diff_options)
208
+ cvt1 = vt1 || VariableType.new
209
+ cvt2 = vt2 || VariableType.new
210
+
211
+
212
+ if cvt1.name && cvt2.name && cvt1.name == cvt2.name
213
+ # If they are named and have the same name, the differences will
214
+ # be reported once under the specification's entry for the named type.
215
+ nil
216
+ else
217
+ cvt1.diff(cvt2, diff_options)
218
+ end
219
+ end
220
+ end
221
+
222
+ def diff_criteria(diff_options={})
223
+ base = {
224
+ :name => Differences::ValueCriterion.new,
225
+ :type => EmbeddedVariableTypeCriterion.new,
226
+ :pii => Differences::ValueCriterion.new,
227
+ :omittable? => Differences::ValueCriterion.new(:comparator => :predicate),
228
+ :nillable? => Differences::ValueCriterion.new(:comparator => :predicate),
229
+ :table_reference => Differences::ValueCriterion.new(
230
+ :value_extractor => lambda { |o| o ? o.name : nil }
231
+ )
232
+ }
233
+
234
+ if diff_options[:strict]
235
+ base[:status] = Differences::ValueCriterion.new
236
+ else
237
+ base[:status] = Differences::ValueCriterion.new(
238
+ :comparator => lambda { |left, right|
239
+ no_change_changes = [
240
+ [:new, :active],
241
+ [:new, :modified],
242
+ [:active, :modified],
243
+ [:modified, :active]
244
+ ]
245
+
246
+ no_change_changes.include?([left, right]) || (left == right)
247
+ }
248
+ )
249
+ end
250
+
251
+ base
252
+ end
253
+ protected :diff_criteria
254
+
255
+ ##
256
+ # Computes the differences between this variable and the other.
257
+ #
258
+ # @return [Differences::Entry,nil]
259
+ def diff(other_variable, options={})
260
+ Differences::Entry.compute(self, other_variable, diff_criteria(options), options)
261
+ end
208
262
  end
209
263
  end
@@ -127,70 +127,35 @@ module NcsNavigator::Mdes
127
127
  "#<#{self.class} #{attrs.join(' ')}>"
128
128
  end
129
129
 
130
- ##
131
- # A specialization of `Array` for code lists.
132
- #
133
- # @see VariableType#code_list
134
- # @see CodeListEntry
135
- class CodeList < Array
136
- ##
137
- # @return [String,nil] the description of the code list if any.
138
- attr_accessor :description
139
- end
140
-
141
- ##
142
- # A single entry in a code list.
143
- #
144
- # @see VariableType#code_list
145
- # @see CodeList
146
- class CodeListEntry
147
- ##
148
- # @return [String] the local code value for the entry.
149
- attr_reader :value
150
-
151
- ##
152
- # @return [String] the human-readable label for the entry.
153
- attr_accessor :label
154
-
155
- ##
156
- # @return [String] the MDES's globally-unique identifier for
157
- # this coded value.
158
- attr_accessor :global_value
159
-
160
- ##
161
- # @return [String] the name of MDES's master code list from
162
- # which this value is derived.
163
- attr_accessor :master_cl
164
-
165
- class << self
166
- ##
167
- # Creates a new instance from a `xs:enumeration` simple type
168
- # restriction subelement.
169
- #
170
- # @param [Nokogiri::XML::Element] enum the `xs:enumeration`
171
- # element.
172
- # @param [Hash] options
173
- # @option options [#warn] :log the logger to which to direct warnings
174
- #
175
- # @return [CodeListEntry]
176
- def from_xsd_enumeration(enum, options={})
177
- log = options[:log] || NcsNavigator::Mdes.default_logger
178
-
179
- log.warn("Missing value for code list entry on line #{enum.line}") unless enum['value']
180
-
181
- new(enum['value'] && enum['value'].strip).tap do |cle|
182
- cle.label = enum['ncsdoc:label']
183
- cle.global_value = enum['ncsdoc:global_value']
184
- cle.master_cl = enum['ncsdoc:master_cl']
185
- end
186
- end
187
- end
188
-
189
- def initialize(value)
190
- @value = value
130
+ def diff_criteria(diff_options={})
131
+ base = {
132
+ :name => Differences::ValueCriterion.new,
133
+ :base_type => Differences::ValueCriterion.new,
134
+ :pattern => Differences::ValueCriterion.new,
135
+ :max_length => Differences::ValueCriterion.new,
136
+ :min_length => Differences::ValueCriterion.new,
137
+ }
138
+
139
+ if diff_options[:strict]
140
+ base.merge(
141
+ :code_list_by_value => Differences::CollectionCriterion.new(
142
+ :value, :collection => :code_list),
143
+ :code_list_by_label => Differences::CollectionCriterion.new(
144
+ :label, :collection => :code_list)
145
+ )
146
+ else
147
+ base.merge(
148
+ :code_list_by_value => Differences::CollectionCriterion.new(
149
+ :value, :collection => :code_list),
150
+ :code_list_by_label => Differences::CollectionCriterion.new(
151
+ :label, :collection => :code_list, :value_extractor => :word_chars_downcase)
152
+ )
191
153
  end
154
+ end
155
+ protected :diff_criteria
192
156
 
193
- alias :to_s :value
157
+ def diff(other_type, options={})
158
+ Differences::Entry.compute(self, other_type, diff_criteria(options), options)
194
159
  end
195
160
  end
196
161
  end
@@ -1,5 +1,5 @@
1
1
  module NcsNavigator
2
2
  module Mdes
3
- VERSION = '0.11.0'
3
+ VERSION = '0.12.0'
4
4
  end
5
5
  end
@@ -4,6 +4,8 @@ module NcsNavigator
4
4
  module Mdes
5
5
  autoload :VERSION, 'ncs_navigator/mdes/version'
6
6
 
7
+ autoload :CodeList, 'ncs_navigator/mdes/code_list'
8
+ autoload :CodeListEntry, 'ncs_navigator/mdes/code_list'
7
9
  autoload :SourceDocuments, 'ncs_navigator/mdes/source_documents'
8
10
  autoload :Specification, 'ncs_navigator/mdes/specification'
9
11
  autoload :TransmissionTable, 'ncs_navigator/mdes/transmission_table'
@@ -12,6 +14,8 @@ module NcsNavigator
12
14
 
13
15
  autoload :DispositionCode, 'ncs_navigator/mdes/disposition_code'
14
16
 
17
+ autoload :Differences, 'ncs_navigator/mdes/differences'
18
+
15
19
  ##
16
20
  # @return the default logger for this module when no other one is
17
21
  # specified. It logs to standard error.
@@ -22,7 +26,7 @@ module NcsNavigator
22
26
 
23
27
  ##
24
28
  # @return [Mdes::Specification] a new {Mdes::Specification} for the given
25
- # version. See {Specification#initialize} for accepted options.
29
+ # version. See {Mdes::Specification#initialize} for accepted options.
26
30
  def self.Mdes(version, options={})
27
31
  Mdes::Specification.new(version, options)
28
32
  end
@@ -0,0 +1,40 @@
1
+ require 'rspec/expectations'
2
+
3
+ module NcsNavigator::Mdes::Spec::Matchers
4
+ class ValueDiffMatcher
5
+ attr_reader :left, :right, :actual
6
+
7
+ def initialize(left, right)
8
+ @left = left
9
+ @right = right
10
+ end
11
+
12
+ def left_matches?(actual)
13
+ left == actual.left
14
+ end
15
+
16
+ def right_matches?(actual)
17
+ right == actual.right
18
+ end
19
+
20
+ def matches?(actual)
21
+ @actual = actual
22
+ actual && left_matches?(actual) && right_matches?(actual)
23
+ end
24
+
25
+ def failure_message_for_should
26
+ if (!actual)
27
+ "expected a difference but got none"
28
+ else
29
+ [
30
+ ("expected left=#{left.inspect} but was #{actual.left.inspect}" unless left_matches?(actual)),
31
+ ("expected right=#{right.inspect} but was #{actual.right.inspect}" unless right_matches?(actual))
32
+ ].compact.join(' and ')
33
+ end
34
+ end
35
+ end
36
+
37
+ def be_a_value_diff(left, right)
38
+ ValueDiffMatcher.new(left, right)
39
+ end
40
+ end
@@ -0,0 +1,178 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'nokogiri'
4
+
5
+ module NcsNavigator::Mdes
6
+ describe CodeListEntry do
7
+ describe '.from_xsd_enumeration' do
8
+ def code_list_entry(xml_s)
9
+ CodeListEntry.from_xsd_enumeration(schema_element(xml_s), :log => logger)
10
+ end
11
+
12
+ let(:missing) {
13
+ code_list_entry(<<-XSD)
14
+ <xs:enumeration value="-4" ncsdoc:label="Missing in Error" ncsdoc:desc="" ncsdoc:global_value="99-4" ncsdoc:master_cl="missing_data"/>
15
+ XSD
16
+ }
17
+
18
+ describe '#value' do
19
+ it 'is set' do
20
+ missing.value.should == "-4"
21
+ end
22
+
23
+ it 'warns when missing' do
24
+ code_list_entry('<xs:enumeration ncsdoc:label="Foo"/>')
25
+ logger[:warn].first.should == 'Missing value for code list entry on line 2'
26
+ end
27
+
28
+ describe 'with external whitespace on the value' do
29
+ let(:missing) {
30
+ code_list_entry(<<-XSD)
31
+ <xs:enumeration value=" -4 " ncsdoc:label="Missing in Error" ncsdoc:desc="" ncsdoc:global_value="99-4" ncsdoc:master_cl="missing_data"/>
32
+ XSD
33
+ }
34
+
35
+ it 'removes the whitespace' do
36
+ missing.value.should == '-4'
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#label" do
42
+ it 'is set' do
43
+ missing.label.should == "Missing in Error"
44
+ end
45
+ end
46
+
47
+ describe '#global_value' do
48
+ it 'is set' do
49
+ missing.global_value.should == '99-4'
50
+ end
51
+ end
52
+
53
+ describe '#master_cl' do
54
+ it 'is set' do
55
+ missing.master_cl.should == 'missing_data'
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#to_s' do
61
+ it 'is the value' do
62
+ CodeListEntry.new('14').to_s.should == '14'
63
+ end
64
+ end
65
+
66
+ describe '#diff' do
67
+ let(:a) { CodeListEntry.new('A') }
68
+ let(:aprime) { CodeListEntry.new('A') }
69
+
70
+ let(:diff) { a.diff(aprime) }
71
+ let(:strict_diff) { a.diff(aprime, :strict => true) }
72
+
73
+ it 'reports nothing for no differences' do
74
+ diff.should be_nil
75
+ end
76
+
77
+ describe 'value' do
78
+ let(:b) { CodeListEntry.new('B') }
79
+
80
+ it 'reports a difference' do
81
+ b.diff(a)[:value].should be_a_value_diff('B', 'A')
82
+ end
83
+ end
84
+
85
+ describe 'label' do
86
+ describe 'with a text difference' do
87
+ before do
88
+ a.label = 'Aleph'
89
+ aprime.label = 'Alpha'
90
+ end
91
+
92
+ it 'when not strict, it reports the normalized difference' do
93
+ diff[:label].should be_a_value_diff('aleph', 'alpha')
94
+ end
95
+
96
+ it 'when strict, it reports the literal difference' do
97
+ strict_diff[:label].should be_a_value_diff('Aleph', 'Alpha')
98
+ end
99
+ end
100
+
101
+ describe 'with a whitespace-only difference' do
102
+ before do
103
+ a.label = " a\t\tb "
104
+ aprime.label = 'a b'
105
+ end
106
+
107
+ it 'reports a difference in strict mode' do
108
+ strict_diff[:label].should be_a_value_diff(a.label, aprime.label)
109
+ end
110
+
111
+ it 'does not report a difference when not strict' do
112
+ diff.should be_nil
113
+ end
114
+ end
115
+
116
+ describe 'with a case-only difference' do
117
+ before do
118
+ a.label = 'AbcdEf'
119
+ aprime.label = 'aBCDeF'
120
+ end
121
+
122
+ it 'reports a difference in strict mode' do
123
+ strict_diff[:label].should be_a_value_diff(a.label, aprime.label)
124
+ end
125
+
126
+ it 'does not report a difference when not strict' do
127
+ diff.should be_nil
128
+ end
129
+ end
130
+
131
+ describe 'with a punctuation-only difference' do
132
+ before do
133
+ a.label = 'a,b,|c[at+]-'
134
+ aprime.label = 'a!b*&c$#at'
135
+ end
136
+
137
+ it 'reports a difference in strict mode' do
138
+ strict_diff[:label].should be_a_value_diff(a.label, aprime.label)
139
+ end
140
+
141
+ it 'does not report a difference when not strict' do
142
+ diff.should be_nil
143
+ end
144
+ end
145
+ end
146
+
147
+ describe 'global_value' do
148
+ before do
149
+ a.global_value = '0'
150
+ aprime.global_value = '-0'
151
+ end
152
+
153
+ it 'reports a difference in strict mode' do
154
+ strict_diff[:global_value].should be_a_value_diff('0', '-0')
155
+ end
156
+
157
+ it 'does not report a difference otherwise' do
158
+ diff.should be_nil
159
+ end
160
+ end
161
+
162
+ describe 'master_cl' do
163
+ before do
164
+ a.master_cl = 'C'
165
+ aprime.master_cl = 'Cprime'
166
+ end
167
+
168
+ it 'reports a difference in strict mode' do
169
+ strict_diff[:master_cl].should be_a_value_diff('C', 'Cprime')
170
+ end
171
+
172
+ it 'does not report a difference otherwise' do
173
+ diff.should be_nil
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -71,6 +71,16 @@ module NcsNavigator::Mdes
71
71
  end
72
72
  end
73
73
 
74
+ describe '#child_or_parent_instrument_tables' do
75
+ let(:property) { :child_or_parent_instrument_tables }
76
+
77
+ it_behaves_like 'an absolutizing path accessor'
78
+
79
+ it 'is optional' do
80
+ SourceDocuments.new.child_or_parent_instrument_tables.should be_nil
81
+ end
82
+ end
83
+
74
84
  describe '.get' do
75
85
  shared_examples 'version docs' do
76
86
  subject { SourceDocuments.get(version) }
@@ -83,6 +93,10 @@ module NcsNavigator::Mdes
83
93
  subject.disposition_codes.should =~ %r{#{version}/disposition_codes.yml$}
84
94
  end
85
95
 
96
+ it 'has the correct path for the parent-child instrument tables list' do
97
+ subject.child_or_parent_instrument_tables.should =~ %r{#{version}/child_or_parent_instrument_tables.yml$}
98
+ end
99
+
86
100
  it 'is of the specified version' do
87
101
  subject.version.should == version
88
102
  end
@@ -53,6 +53,18 @@ module NcsNavigator::Mdes
53
53
  lambda { table.instrument_table? }.should_not raise_error
54
54
  end
55
55
  end
56
+
57
+ it 'knows the mother-childness of all p_id-bearing instrument tables' do
58
+ p_id_instrument_tables = tables.select { |t| t.instrument_table? && t.variables.collect(&:name).include?('p_id') }
59
+ p_id_instrument_tables.
60
+ reject { |t| [true, false].include?(t.child_instrument_table?) }.
61
+ collect { |t| t.name }.should == []
62
+ end
63
+
64
+ it 'has some child and some parent instrument data tables' do
65
+ index = tables.each_with_object(Hash.new(0)) { |t, acc| acc[t.child_instrument_table?] += 1 }
66
+ index.keys.sort_by { |k| k.inspect }.should == [false, nil, true]
67
+ end
56
68
  end
57
69
 
58
70
  context 'in version 1.2' do
@@ -275,5 +287,23 @@ module NcsNavigator::Mdes
275
287
  include_examples 'types fully resolved'
276
288
  end
277
289
  end
290
+
291
+ describe '#diff' do
292
+ let(:diff) {
293
+ Specification.new('2.0').diff(Specification.new('2.1'))
294
+ }
295
+
296
+ it 'finds different spec versions' do
297
+ diff[:specification_version].should be_a_value_diff('2.0.01.02', '2.1.00.00')
298
+ end
299
+
300
+ it 'finds different tables' do
301
+ diff[:transmission_tables].right_only.should include('preg_visit_1_saq_3')
302
+ end
303
+
304
+ it 'finds different types' do
305
+ diff[:types].right_only.should include('person_partcpnt_reltnshp_cl5')
306
+ end
307
+ end
278
308
  end
279
309
  end
@@ -179,5 +179,82 @@ XSD
179
179
  end
180
180
  end
181
181
  end
182
+
183
+ describe 'parent or child instrument table accessors' do
184
+ let(:t) { TransmissionTable.new('T') }
185
+
186
+ it 'is not a parent table when it is a child table' do
187
+ t.child_instrument_table = true
188
+ t.should_not be_a_parent_instrument_table
189
+ end
190
+
191
+ it 'is a parent table when it is definitely not a child table' do
192
+ t.child_instrument_table = false
193
+ t.should be_a_parent_instrument_table
194
+ end
195
+
196
+ it 'is not known whether it is a parent instrument table when it is not known whether it is a child instrument table' do
197
+ t.child_instrument_table = nil
198
+ t.parent_instrument_table?.should be_nil
199
+ end
200
+ end
201
+
202
+ describe '#diff' do
203
+ let(:a) { TransmissionTable.new('A') }
204
+ let(:aprime) { TransmissionTable.new('A') }
205
+
206
+ describe 'name' do
207
+ let(:b) { TransmissionTable.new('B') }
208
+
209
+ it 'reports a difference when they are different' do
210
+ a.diff(b)[:name].should be_a_value_diff('A', 'B')
211
+ end
212
+
213
+ it 'reports nothing when they are the same' do
214
+ a.diff(aprime).should be_nil
215
+ end
216
+ end
217
+
218
+ describe 'variables' do
219
+ let(:v1) { Variable.new('V1') }
220
+ let(:v1prime) { Variable.new('V1') }
221
+ let(:v2) { Variable.new('V2') }
222
+ let(:v3) { Variable.new('V3') }
223
+
224
+ let(:diff) { a.diff(aprime) }
225
+
226
+ before do
227
+ a.variables << v1
228
+ aprime.variables << v1prime
229
+ end
230
+
231
+ it 'lists variables which are in the lefthand side only' do
232
+ a.variables << v2 << v3
233
+ aprime.variables << v3
234
+
235
+ diff[:variables].left_only.should == ['V2']
236
+ end
237
+
238
+ it 'lists variables which are in the righthand side only' do
239
+ aprime.variables << v3
240
+
241
+ diff[:variables].right_only.should == ['V3']
242
+ end
243
+
244
+ it 'provides detailed differences for variables which are different' do
245
+ v1.pii = :possible
246
+ a.variables << v1
247
+
248
+ v1prime.pii = true
249
+ aprime.variables << v1prime
250
+
251
+ diff[:variables].entry_differences['V1'][:pii].should be_a_value_diff(:possible, true)
252
+ end
253
+
254
+ it 'does not list variables which are the same' do
255
+ diff.should be_nil
256
+ end
257
+ end
258
+ end
182
259
  end
183
260
  end