fhir_models 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -1156
- data/.rubocop_todo.yml +76 -0
- data/.travis.yml +9 -3
- data/Gemfile +1 -1
- data/Guardfile +50 -0
- data/Rakefile +4 -5
- data/bin/console +4 -4
- data/fhir_models.gemspec +6 -3
- data/lib/fhir_models.rb +2 -1
- data/lib/fhir_models/bootstrap/definitions.rb +100 -94
- data/lib/fhir_models/bootstrap/field.rb +1 -1
- data/lib/fhir_models/bootstrap/generator.rb +11 -11
- data/lib/fhir_models/bootstrap/hashable.rb +18 -5
- data/lib/fhir_models/bootstrap/json.rb +2 -1
- data/lib/fhir_models/bootstrap/model.rb +21 -39
- data/lib/fhir_models/bootstrap/preprocess.rb +21 -36
- data/lib/fhir_models/bootstrap/template.rb +5 -9
- data/lib/fhir_models/bootstrap/xml.rb +8 -5
- data/lib/fhir_models/deprecate.rb +22 -0
- data/lib/fhir_models/fhir.rb +58 -0
- data/lib/fhir_models/fhir/metadata.rb +1 -1
- data/lib/fhir_models/fhir_ext/element_definition.rb +47 -0
- data/lib/fhir_models/fhir_ext/structure_definition.rb +203 -524
- data/lib/fhir_models/fhir_ext/structure_definition_compare.rb +375 -0
- data/lib/fhir_models/fluentpath/evaluate.rb +9 -2
- data/lib/fhir_models/fluentpath/expression.rb +8 -1
- data/lib/fhir_models/fluentpath/parse.rb +2 -2
- data/lib/fhir_models/version.rb +1 -1
- metadata +62 -15
- data/lib/fhir_models/fhir/resources/MetadataResource.rb +0 -52
@@ -0,0 +1,375 @@
|
|
1
|
+
module FHIR
|
2
|
+
# Extend StructureDefinition for profile comparison code
|
3
|
+
class StructureDefinition
|
4
|
+
extend FHIR::Deprecate
|
5
|
+
|
6
|
+
# -------------------------------------------------------------------------
|
7
|
+
# Profile Comparison
|
8
|
+
# -------------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Checks whether or not "another_definition" is compatible with this definition.
|
11
|
+
# If they have conflicting elements, restrictions, bindings, modifying extensions, etc.
|
12
|
+
def compatible?(another_definition)
|
13
|
+
@errors = []
|
14
|
+
@warnings = []
|
15
|
+
|
16
|
+
@finding = FHIR::StructureDefinitionFinding.new
|
17
|
+
@finding.resourceType = snapshot.element[0].path
|
18
|
+
@finding.profileIdA = id
|
19
|
+
@finding.profileIdB = another_definition.id if another_definition.respond_to?(:id)
|
20
|
+
|
21
|
+
if !(another_definition.is_a? FHIR::StructureDefinition)
|
22
|
+
@errors << @finding.error('', '', 'Not a StructureDefinition', 'StructureDefinition', another_definition.class.name.to_s)
|
23
|
+
return false
|
24
|
+
elsif another_definition.snapshot.element[0].path != snapshot.element[0].path
|
25
|
+
@errors << @finding.error('', '', 'Incompatible resourceType', @finding.resourceType, another_definition.snapshot.element[0].path.to_s)
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
left_elements = Array.new(snapshot.element)
|
30
|
+
right_elements = Array.new(another_definition.snapshot.element)
|
31
|
+
|
32
|
+
left_paths = left_elements.map(&:path)
|
33
|
+
right_paths = right_elements.map(&:path)
|
34
|
+
|
35
|
+
# StructureDefinitions don't always include all base attributes (for example, of a ContactPoint)
|
36
|
+
# if nothing is modified from the base definition, so we have to add them in if they are missing.
|
37
|
+
base_definition = FHIR::Definitions.get_resource_definition(snapshot.element[0].path)
|
38
|
+
base_elements = base_definition.snapshot.element
|
39
|
+
|
40
|
+
left_missing = right_paths - left_paths
|
41
|
+
# left_missing_roots = left_missing.map{|e| e.split('.')[0..-2].join('.') }.uniq
|
42
|
+
add_missing_elements(id, left_missing, left_elements, base_elements)
|
43
|
+
|
44
|
+
right_missing = left_paths - right_paths
|
45
|
+
# right_missing_roots = right_missing.map{|e| e.split('.')[0..-2].join('.') }.uniq
|
46
|
+
add_missing_elements(another_definition.id, right_missing, right_elements, base_elements)
|
47
|
+
|
48
|
+
# update paths
|
49
|
+
left_paths = left_elements.map(&:path)
|
50
|
+
right_paths = right_elements.map(&:path)
|
51
|
+
|
52
|
+
# recalculate the missing attributes
|
53
|
+
left_missing = right_paths - left_paths
|
54
|
+
right_missing = left_paths - right_paths
|
55
|
+
|
56
|
+
# generate warnings for missing fields (ignoring extensions)
|
57
|
+
left_missing.each do |e|
|
58
|
+
next if e.include? 'extension'
|
59
|
+
elem = get_element_by_path(e, right_elements)
|
60
|
+
if !elem.min.nil? && elem.min > 0
|
61
|
+
@errors << @finding.error(e, 'min', 'Missing REQUIRED element', 'Missing', elem.min.to_s)
|
62
|
+
elsif elem.isModifier == true
|
63
|
+
@errors << @finding.error(e, 'isModifier', 'Missing MODIFIER element', 'Missing', elem.isModifier.to_s)
|
64
|
+
else
|
65
|
+
@warnings << @finding.warning(e, '', 'Missing element', 'Missing', 'Defined')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
right_missing.each do |e|
|
69
|
+
next if e.include? 'extension'
|
70
|
+
elem = get_element_by_path(e, left_elements)
|
71
|
+
if !elem.min.nil? && elem.min > 0
|
72
|
+
@errors << @finding.error(e, 'min', 'Missing REQUIRED element', elem.min.to_s, 'Missing')
|
73
|
+
elsif elem.isModifier == true
|
74
|
+
@errors << @finding.error(e, 'isModifier', 'Missing MODIFIER element', elem.isModifier.to_s, 'Missing')
|
75
|
+
else
|
76
|
+
@warnings << @finding.warning(e, '', 'Missing element', 'Defined', 'Missing')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
left_extensions = []
|
81
|
+
right_extensions = []
|
82
|
+
|
83
|
+
# compare elements, starting with the elements in this definition
|
84
|
+
left_elements.each do |x|
|
85
|
+
if x.path.include? 'extension'
|
86
|
+
# handle extensions separately
|
87
|
+
left_extensions << x
|
88
|
+
else
|
89
|
+
y = get_element_by_path(x.path, right_elements)
|
90
|
+
compare_element_definitions(x, y, another_definition)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# now compare elements defined in the other definition, if we haven't already looked at them
|
95
|
+
right_elements.each do |y|
|
96
|
+
if y.path.include? 'extension'
|
97
|
+
# handle extensions separately
|
98
|
+
right_extensions << y
|
99
|
+
elsif left_missing.include? y.path
|
100
|
+
x = get_element_by_path(y.path, left_elements)
|
101
|
+
compare_element_definitions(x, y, another_definition)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# finally, compare the extensions.
|
106
|
+
checked_extensions = []
|
107
|
+
left_extensions.each do |x|
|
108
|
+
y = get_extension(x.name, right_extensions)
|
109
|
+
unless y.nil?
|
110
|
+
# both profiles share an extension with the same name
|
111
|
+
checked_extensions << x.name
|
112
|
+
compare_extension_definition(x, y, another_definition)
|
113
|
+
end
|
114
|
+
y = get_extension(x.type[0].profile, right_extensions)
|
115
|
+
next unless !y.nil? && x.name != y.name
|
116
|
+
# both profiles share the same extension definition but with a different name
|
117
|
+
checked_extensions << x.name
|
118
|
+
checked_extensions << y.name
|
119
|
+
compare_element_definitions(x, y, another_definition)
|
120
|
+
end
|
121
|
+
right_extensions.each do |y|
|
122
|
+
next if checked_extensions.include?(y.name)
|
123
|
+
x = get_extension(y.name, left_extensions)
|
124
|
+
unless x.nil?
|
125
|
+
# both profiles share an extension with the same name
|
126
|
+
checked_extensions << y.name
|
127
|
+
compare_extension_definition(x, y, another_definition)
|
128
|
+
end
|
129
|
+
x = get_extension(y.type[0].profile, left_extensions)
|
130
|
+
next unless !x.nil? && x.name != y.name && !checked_extensions.include?(x.name)
|
131
|
+
# both profiles share the same extension definition but with a different name
|
132
|
+
checked_extensions << x.name
|
133
|
+
checked_extensions << y.name
|
134
|
+
compare_element_definitions(x, y, another_definition)
|
135
|
+
end
|
136
|
+
@errors.flatten!
|
137
|
+
@warnings.flatten!
|
138
|
+
@errors.size.zero?
|
139
|
+
end
|
140
|
+
deprecate :is_compatible?, :compatible?
|
141
|
+
|
142
|
+
def get_element_by_path(path, elements = snapshot.element)
|
143
|
+
elements.detect { |element| element.path == path }
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_extension(extension, elements = snapshot.element)
|
147
|
+
elements.each do |element|
|
148
|
+
if element.path.include?('extension') || element.type.map(&:code).include?('Extension')
|
149
|
+
return element if element.name == extension || element.type.map(&:profile).include?(extension)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
# private
|
156
|
+
# name -- name of the profile we're fixing
|
157
|
+
# missing_paths -- list of paths that we're adding
|
158
|
+
# elements -- list of elements currently defined in the profile
|
159
|
+
# base_elements -- list of elements defined in the base resource the profile extends
|
160
|
+
def add_missing_elements(_name, missing_paths, elements, base_elements)
|
161
|
+
variable_paths = elements.map(&:path).grep(/\[x\]/).map { |e| e[0..-4] }
|
162
|
+
variable_paths << base_elements.map(&:path).grep(/\[x\]/).map { |e| e[0..-4] }
|
163
|
+
variable_paths.flatten!.uniq!
|
164
|
+
|
165
|
+
missing_paths.each do |path|
|
166
|
+
# Skip extensions
|
167
|
+
next if path.include? 'extension'
|
168
|
+
|
169
|
+
# Skip the variable paths that end with "[x]"
|
170
|
+
next if variable_paths.any? { |variable| path.starts_with?(variable) }
|
171
|
+
|
172
|
+
elem = get_element_by_path(path, base_elements)
|
173
|
+
unless elem.nil?
|
174
|
+
# _DEEP_ copy
|
175
|
+
elements << FHIR::ElementDefinition.from_fhir_json(elem.to_fhir_json)
|
176
|
+
next
|
177
|
+
end
|
178
|
+
|
179
|
+
x = path.split('.')
|
180
|
+
root = x.first(x.size - 1).join('.')
|
181
|
+
next unless root.include? '.'
|
182
|
+
# get the root element to fill in the details
|
183
|
+
elem = get_element_by_path(root, elements)
|
184
|
+
# get the data type definition to fill in the details
|
185
|
+
# assume missing elements are from first data type (gross)
|
186
|
+
next if elem.type.nil? || elem.type.empty?
|
187
|
+
type_def = FHIR::Definitions.type_definition(elem.type[0].code)
|
188
|
+
next if type_def.nil?
|
189
|
+
type_elements = Array.new(type_def.snapshot.element)
|
190
|
+
# _DEEP_ copy
|
191
|
+
type_elements.map! do |e| # {|e| FHIR::ElementDefinition.from_fhir_json(e.to_fhir_json) }
|
192
|
+
FHIR::ElementDefinition.from_fhir_json(e.to_fhir_json)
|
193
|
+
end
|
194
|
+
# Fix path names
|
195
|
+
type_root = String.new(type_elements[0].path)
|
196
|
+
type_elements.each { |e| e.path.gsub!(type_root, root) }
|
197
|
+
# finally, add the missing element definitions
|
198
|
+
# one by one -- only if they are not already present (i.e. do not override)
|
199
|
+
type_elements.each do |z|
|
200
|
+
y = get_element_by_path(z.path, elements)
|
201
|
+
next unless y.nil?
|
202
|
+
elements << z
|
203
|
+
# else
|
204
|
+
# @warnings << "StructureDefinition #{name} already contains #{z.path}"
|
205
|
+
end
|
206
|
+
elements.uniq!
|
207
|
+
# else
|
208
|
+
# @warnings << "StructureDefinition #{name} missing -- #{path}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# private
|
213
|
+
def compare_extension_definition(x, y, another_definition)
|
214
|
+
x_profiles = x.type.map(&:profile)
|
215
|
+
y_profiles = y.type.map(&:profile)
|
216
|
+
x_only = x_profiles - y_profiles
|
217
|
+
shared = x_profiles - x_only
|
218
|
+
|
219
|
+
if !shared.nil? && shared.size.zero?
|
220
|
+
# same name, but different profiles
|
221
|
+
# maybe the profiles are the same, just with different URLs...
|
222
|
+
# ... so we have to compare them, if we can.
|
223
|
+
@warnings << @finding.warning("#{x.path} (#{x.name})", 'type.profile', 'Different Profiles', x_profiles.to_s, y_profiles.to_s)
|
224
|
+
x_extension = FHIR::Definitions.get_extension_definition(x.type[0].profile)
|
225
|
+
y_extension = FHIR::Definitions.get_extension_definition(y.type[0].profile)
|
226
|
+
if !x_extension.nil? && !y_extension.nil?
|
227
|
+
x_extension.compatible?(y_extension)
|
228
|
+
@errors << x_extension.errors
|
229
|
+
@warnings << x_extension.warnings
|
230
|
+
else
|
231
|
+
@warnings << @finding.warning("#{x.path} (#{x.name})", '', 'Could not find extension definitions to compare.', '', '')
|
232
|
+
end
|
233
|
+
else
|
234
|
+
compare_element_definitions(x, y, another_definition)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# private
|
239
|
+
def compare_element_definitions(x, y, another_definition)
|
240
|
+
return if x.nil? || y.nil? || another_definition.nil?
|
241
|
+
|
242
|
+
# check cardinality
|
243
|
+
x_min = x.min || 0
|
244
|
+
x_max = x.max == '*' ? Float::INFINITY : x.max.to_i
|
245
|
+
y_min = y.min || 0
|
246
|
+
y_max = y.max == '*' ? Float::INFINITY : y.max.to_i
|
247
|
+
|
248
|
+
if x_min.nil? || x.max.nil? || y_min.nil? || y.max.nil?
|
249
|
+
@errors << @finding.error(x.path.to_s, 'min/max', 'Unknown cardinality', "#{x_min}..#{x.max}", "#{y_min}..#{y.max}")
|
250
|
+
elsif (x_min > y_max) || (x_max < y_min)
|
251
|
+
@errors << @finding.error(x.path.to_s, 'min/max', 'Incompatible cardinality', "#{x_min}..#{x.max}", "#{y_min}..#{y.max}")
|
252
|
+
elsif (x_min != y_min) || (x_max != y_max)
|
253
|
+
@warnings << @finding.warning(x.path.to_s, 'min/max', 'Inconsistent cardinality', "#{x_min}..#{x.max}", "#{y_min}..#{y.max}")
|
254
|
+
end
|
255
|
+
|
256
|
+
# check data types
|
257
|
+
x_types = x.type.map(&:code)
|
258
|
+
y_types = y.type.map(&:code)
|
259
|
+
x_only = x_types - y_types
|
260
|
+
y_only = y_types - x_types
|
261
|
+
shared = x_types - x_only
|
262
|
+
|
263
|
+
if !shared.nil? && shared.size.zero? && !x_types.empty? && !y_types.empty? && !x.constraint.empty? && !y.constraint.empty?
|
264
|
+
@errors << @finding.error(x.path.to_s, 'type.code', 'Incompatible data types', x_types.to_s, y_types.to_s)
|
265
|
+
end
|
266
|
+
if !x_only.nil? && !x_only.empty?
|
267
|
+
@warnings << @finding.warning(x.path.to_s, 'type.code', 'Allows additional data types', x_only.to_s, 'not allowed')
|
268
|
+
end
|
269
|
+
if !y_only.nil? && !y_only.empty?
|
270
|
+
@warnings << @finding.warning(x.path.to_s, 'type.code', 'Allows additional data types', 'not allowed', y_only.to_s)
|
271
|
+
end
|
272
|
+
|
273
|
+
# check bindings
|
274
|
+
if x.binding.nil? && !y.binding.nil?
|
275
|
+
val = y.binding.valueSetUri || y.binding.valueSetReference.try(:reference) || y.binding.description
|
276
|
+
@warnings << @finding.warning(x.path.to_s, 'binding', 'Inconsistent binding', '', val)
|
277
|
+
elsif !x.binding.nil? && y.binding.nil?
|
278
|
+
val = x.binding.valueSetUri || x.binding.valueSetReference.try(:reference) || x.binding.description
|
279
|
+
@warnings << @finding.warning(x.path.to_s, 'binding', 'Inconsistent binding', val, '')
|
280
|
+
elsif !x.binding.nil? && !y.binding.nil?
|
281
|
+
x_vs = x.binding.valueSetUri || x.binding.valueSetReference.try(:reference)
|
282
|
+
y_vs = y.binding.valueSetUri || y.binding.valueSetReference.try(:reference)
|
283
|
+
if x_vs != y_vs
|
284
|
+
if x.binding.strength == 'required' || y.binding.strength == 'required'
|
285
|
+
@errors << @finding.error(x.path.to_s, 'binding.strength', 'Incompatible bindings', "#{x.binding.strength} #{x_vs}", "#{y.binding.strength} #{y_vs}")
|
286
|
+
else
|
287
|
+
@warnings << @finding.warning(x.path.to_s, 'binding.strength', 'Inconsistent bindings', "#{x.binding.strength} #{x_vs}", "#{y.binding.strength} #{y_vs}")
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# check default values
|
293
|
+
if x.defaultValue.try(:type) != y.defaultValue.try(:type)
|
294
|
+
@errors << @finding.error(x.path.to_s, 'defaultValue', 'Incompatible default type', x.defaultValue.try(:type).to_s, y.defaultValue.try(:type).to_s)
|
295
|
+
end
|
296
|
+
if x.defaultValue.try(:value) != y.defaultValue.try(:value)
|
297
|
+
@errors << @finding.error(x.path.to_s, 'defaultValue', 'Incompatible default value', x.defaultValue.try(:value).to_s, y.defaultValue.try(:value).to_s)
|
298
|
+
end
|
299
|
+
|
300
|
+
# check meaning when missing
|
301
|
+
if x.meaningWhenMissing != y.meaningWhenMissing
|
302
|
+
@errors << @finding.error(x.path.to_s, 'meaningWhenMissing', 'Inconsistent missing meaning', x.meaningWhenMissing.tr(',', ';').to_s, y.meaningWhenMissing.tr(',', ';').to_s)
|
303
|
+
end
|
304
|
+
|
305
|
+
# check fixed values
|
306
|
+
if x.fixed.try(:type) != y.fixed.try(:type)
|
307
|
+
@errors << @finding.error(x.path.to_s, 'fixed', 'Incompatible fixed type', x.fixed.try(:type).to_s, y.fixed.try(:type).to_s)
|
308
|
+
end
|
309
|
+
if x.fixed != y.fixed
|
310
|
+
xfv = x.fixed.try(:value)
|
311
|
+
xfv = xfv.to_xml.delete(/\n/) if x.fixed.try(:value).methods.include?(:to_xml)
|
312
|
+
yfv = y.fixed.try(:value)
|
313
|
+
yfv = yfv.to_xml.delete(/\n/) if y.fixed.try(:value).methods.include?(:to_xml)
|
314
|
+
@errors << @finding.error(x.path.to_s, 'fixed', 'Incompatible fixed value', xfv.to_s, yfv.to_s)
|
315
|
+
end
|
316
|
+
|
317
|
+
# check min values
|
318
|
+
if x.min.try(:type) != y.min.try(:type)
|
319
|
+
@errors << @finding.error(x.path.to_s, 'min', 'Incompatible min type', x.min.try(:type).to_s, y.min.try(:type).to_s)
|
320
|
+
end
|
321
|
+
if x.min.try(:value) != y.min.try(:value)
|
322
|
+
@errors << @finding.error(x.path.to_s, 'min', 'Incompatible min value', x.min.try(:value).to_s, y.min.try(:value).to_s)
|
323
|
+
end
|
324
|
+
|
325
|
+
# check max values
|
326
|
+
if x.max.try(:type) != y.max.try(:type)
|
327
|
+
@errors << @finding.error(x.path.to_s, 'max', 'Incompatible max type', x.max.try(:type).to_s, y.max.try(:type).to_s)
|
328
|
+
end
|
329
|
+
if x.max.try(:value) != y.max.try(:value)
|
330
|
+
@errors << @finding.error(x.path.to_s, 'max', 'Incompatible max value', x.max.try(:value).to_s, y.max.try(:value).to_s)
|
331
|
+
end
|
332
|
+
|
333
|
+
# check pattern values
|
334
|
+
if x.pattern.try(:type) != y.pattern.try(:type)
|
335
|
+
@errors << @finding.error(x.path.to_s, 'pattern', 'Incompatible pattern type', x.pattern.try(:type).to_s, y.pattern.try(:type).to_s)
|
336
|
+
end
|
337
|
+
if x.pattern.try(:value) != y.pattern.try(:value)
|
338
|
+
@errors << @finding.error(x.path.to_s, 'pattern', 'Incompatible pattern value', x.pattern.try(:value).to_s, y.pattern.try(:value).to_s)
|
339
|
+
end
|
340
|
+
|
341
|
+
# maxLength (for Strings)
|
342
|
+
if x.maxLength != y.maxLength
|
343
|
+
@warnings << @finding.warning(x.path.to_s, 'maxLength', 'Inconsistent maximum length', x.maxLength.to_s, y.maxLength.to_s)
|
344
|
+
end
|
345
|
+
|
346
|
+
# constraints
|
347
|
+
x_constraints = x.constraint.map(&:xpath)
|
348
|
+
y_constraints = y.constraint.map(&:xpath)
|
349
|
+
x_only = x_constraints - y_constraints
|
350
|
+
y_only = y_constraints - x_constraints
|
351
|
+
shared = x_constraints - x_only
|
352
|
+
|
353
|
+
if !shared.nil? && shared.size.zero? && !x.constraint.empty? && !y.constraint.empty?
|
354
|
+
@errors << @finding.error(x.path.to_s, 'constraint.xpath', 'Incompatible constraints', x_constraints.map { |z| z.tr(',', ';') }.join(' && ').to_s, y_constraints.map { |z| z.tr(',', ';') }.join(' && ').to_s)
|
355
|
+
end
|
356
|
+
if !x_only.nil? && !x_only.empty?
|
357
|
+
@errors << @finding.error(x.path.to_s, 'constraint.xpath', 'Additional constraints', x_constraints.map { |z| z.tr(',', ';') }.join(' && ').to_s, '')
|
358
|
+
end
|
359
|
+
if !y_only.nil? && !y_only.empty?
|
360
|
+
@errors << @finding.error(x.path.to_s, 'constraint.xpath', 'Additional constraints', '', y_constraints.map { |z| z.tr(',', ';') }.join(' && ').to_s)
|
361
|
+
end
|
362
|
+
|
363
|
+
# mustSupports
|
364
|
+
if x.mustSupport != y.mustSupport
|
365
|
+
@warnings << @finding.warning(x.path.to_s, 'mustSupport', 'Inconsistent mustSupport', (x.mustSupport || false).to_s, (y.mustSupport || false).to_s)
|
366
|
+
end
|
367
|
+
|
368
|
+
# isModifier
|
369
|
+
return unless x.isModifier != y.isModifier
|
370
|
+
@errors << @finding.error(x.path.to_s, 'isModifier', 'Incompatible isModifier', (x.isModifier || false).to_s, (y.isModifier || false).to_s)
|
371
|
+
end
|
372
|
+
|
373
|
+
private :add_missing_elements, :compare_extension_definition, :compare_element_definitions
|
374
|
+
end
|
375
|
+
end
|
@@ -248,7 +248,7 @@ module FluentPath
|
|
248
248
|
FHIR.logger.debug "DATA: #{tree}"
|
249
249
|
|
250
250
|
# evaluate all the functions at this level
|
251
|
-
functions = [:all, :not, :empty, :exists, :startsWith, :substring, :contains, :in, :distinct, :toInteger, :count]
|
251
|
+
functions = [:all, :not, :empty, :exists, :startsWith, :substring, :contains, :in, :distinct, :toInteger, :count, :length]
|
252
252
|
size = -1
|
253
253
|
while tree.length != size
|
254
254
|
FHIR.logger.debug "FUNC: #{tree}"
|
@@ -273,9 +273,16 @@ module FluentPath
|
|
273
273
|
clean_index(tree, previous_index)
|
274
274
|
when :count
|
275
275
|
tree[index] = 0
|
276
|
-
tree[index] = 1 unless previous_node.nil?
|
276
|
+
tree[index] = 1 unless previous_node == :null || previous_node.nil?
|
277
277
|
tree[index] = previous_node.length if previous_node.is_a?(Array)
|
278
278
|
clean_index(tree, previous_index)
|
279
|
+
when :length
|
280
|
+
if previous_node == :null || previous_node.nil?
|
281
|
+
tree[index] = 0
|
282
|
+
else
|
283
|
+
tree[index] = previous_node.to_s.length
|
284
|
+
end
|
285
|
+
clean_index(tree, previous_index)
|
279
286
|
when :empty
|
280
287
|
tree[index] = (previous_node == :null || previous_node.empty? rescue previous_node.nil?)
|
281
288
|
clean_index(tree, previous_index)
|
@@ -15,7 +15,14 @@ module FluentPath
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def clone
|
18
|
-
clone_tree = @tree.map
|
18
|
+
clone_tree = @tree.map do |x|
|
19
|
+
begin
|
20
|
+
x.clone
|
21
|
+
rescue
|
22
|
+
# TODO: This appears to be dead code
|
23
|
+
x
|
24
|
+
end
|
25
|
+
end
|
19
26
|
FluentPath::Expression.new(clone_tree)
|
20
27
|
end
|
21
28
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module FluentPath
|
2
|
-
@@reserved = ['all', 'not', 'empty', 'exists', 'where', 'select', 'extension', 'startsWith', 'contains', 'in', 'distinct', '=', '!=', '<=', '>=', '<', '>', 'and', 'or', 'xor', '+', '-', '/', '*', 'toInteger', 'implies', 'children', 'first', 'last', 'tail', 'count', 'substring']
|
2
|
+
@@reserved = ['all', 'not', 'empty', 'exists', 'where', 'select', 'extension', 'startsWith', 'contains', 'in', 'distinct', '=', '!=', '<=', '>=', '<', '>', 'and', 'or', 'xor', '+', '-', '/', '*', 'toInteger', 'implies', 'children', 'first', 'last', 'tail', 'count', 'length', 'substring']
|
3
3
|
|
4
4
|
def self.parse(expression)
|
5
5
|
build_tree(tokenize(expression))
|
@@ -7,7 +7,7 @@ module FluentPath
|
|
7
7
|
|
8
8
|
# This method tokenizes the expression into a flat array of tokens
|
9
9
|
def self.tokenize(expression)
|
10
|
-
raw_tokens = expression.gsub('()', '').split(
|
10
|
+
raw_tokens = expression.gsub('()', '').split(%r{(\(|\)|\s|>=|<=|>|<|=|!=|\+|-|\/|\*)})
|
11
11
|
# recreate strings if they were split
|
12
12
|
size = nil
|
13
13
|
while raw_tokens.include?("'") && size != raw_tokens.length
|
data/lib/fhir_models/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fhir_models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Walonoski
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-04-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nokogiri
|
@@ -78,30 +78,30 @@ dependencies:
|
|
78
78
|
name: bundler
|
79
79
|
requirement: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - "
|
81
|
+
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '0'
|
84
84
|
type: :development
|
85
85
|
prerelease: false
|
86
86
|
version_requirements: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - "
|
88
|
+
- - ">="
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '
|
90
|
+
version: '0'
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: rake
|
93
93
|
requirement: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- - "
|
95
|
+
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
97
|
+
version: '0'
|
98
98
|
type: :development
|
99
99
|
prerelease: false
|
100
100
|
version_requirements: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- - "
|
102
|
+
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
104
|
+
version: '0'
|
105
105
|
- !ruby/object:Gem::Dependency
|
106
106
|
name: pry
|
107
107
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +130,20 @@ dependencies:
|
|
130
130
|
- - ">="
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: '0'
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: rspec
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
type: :development
|
141
|
+
prerelease: false
|
142
|
+
version_requirements: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
133
147
|
- !ruby/object:Gem::Dependency
|
134
148
|
name: simplecov
|
135
149
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,16 +176,16 @@ dependencies:
|
|
162
176
|
name: rubocop
|
163
177
|
requirement: !ruby/object:Gem::Requirement
|
164
178
|
requirements:
|
165
|
-
- - "
|
179
|
+
- - ">="
|
166
180
|
- !ruby/object:Gem::Version
|
167
|
-
version: 0
|
181
|
+
version: '0'
|
168
182
|
type: :development
|
169
183
|
prerelease: false
|
170
184
|
version_requirements: !ruby/object:Gem::Requirement
|
171
185
|
requirements:
|
172
|
-
- - "
|
186
|
+
- - ">="
|
173
187
|
- !ruby/object:Gem::Version
|
174
|
-
version: 0
|
188
|
+
version: '0'
|
175
189
|
- !ruby/object:Gem::Dependency
|
176
190
|
name: codeclimate-test-reporter
|
177
191
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,6 +200,34 @@ dependencies:
|
|
186
200
|
- - ">="
|
187
201
|
- !ruby/object:Gem::Version
|
188
202
|
version: '0'
|
203
|
+
- !ruby/object:Gem::Dependency
|
204
|
+
name: guard-rspec
|
205
|
+
requirement: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - ">="
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0'
|
210
|
+
type: :development
|
211
|
+
prerelease: false
|
212
|
+
version_requirements: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - ">="
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
- !ruby/object:Gem::Dependency
|
218
|
+
name: guard-test
|
219
|
+
requirement: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
224
|
+
type: :development
|
225
|
+
prerelease: false
|
226
|
+
version_requirements: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - ">="
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
189
231
|
description: A Gem for handling FHIR models in ruby
|
190
232
|
email:
|
191
233
|
- jwalonoski@mitre.org
|
@@ -195,10 +237,13 @@ extra_rdoc_files: []
|
|
195
237
|
files:
|
196
238
|
- ".codeclimate.yml"
|
197
239
|
- ".gitignore"
|
240
|
+
- ".rspec"
|
198
241
|
- ".rubocop.yml"
|
242
|
+
- ".rubocop_todo.yml"
|
199
243
|
- ".simplecov"
|
200
244
|
- ".travis.yml"
|
201
245
|
- Gemfile
|
246
|
+
- Guardfile
|
202
247
|
- README.md
|
203
248
|
- Rakefile
|
204
249
|
- bin/console
|
@@ -359,6 +404,7 @@ files:
|
|
359
404
|
- lib/fhir_models/definitions/valuesets/v3-codesystems.json
|
360
405
|
- lib/fhir_models/definitions/valuesets/valuesets.json
|
361
406
|
- lib/fhir_models/definitions/version.info
|
407
|
+
- lib/fhir_models/deprecate.rb
|
362
408
|
- lib/fhir_models/examples/json/account-example.json
|
363
409
|
- lib/fhir_models/examples/json/activitydefinition-example.json
|
364
410
|
- lib/fhir_models/examples/json/allergyintolerance-example.json
|
@@ -1317,7 +1363,6 @@ files:
|
|
1317
1363
|
- lib/fhir_models/fhir/resources/MedicationStatement.rb
|
1318
1364
|
- lib/fhir_models/fhir/resources/MessageDefinition.rb
|
1319
1365
|
- lib/fhir_models/fhir/resources/MessageHeader.rb
|
1320
|
-
- lib/fhir_models/fhir/resources/MetadataResource.rb
|
1321
1366
|
- lib/fhir_models/fhir/resources/NamingSystem.rb
|
1322
1367
|
- lib/fhir_models/fhir/resources/NutritionRequest.rb
|
1323
1368
|
- lib/fhir_models/fhir/resources/Observation.rb
|
@@ -1398,7 +1443,9 @@ files:
|
|
1398
1443
|
- lib/fhir_models/fhir/types/Timing.rb
|
1399
1444
|
- lib/fhir_models/fhir/types/TriggerDefinition.rb
|
1400
1445
|
- lib/fhir_models/fhir/types/UsageContext.rb
|
1446
|
+
- lib/fhir_models/fhir_ext/element_definition.rb
|
1401
1447
|
- lib/fhir_models/fhir_ext/structure_definition.rb
|
1448
|
+
- lib/fhir_models/fhir_ext/structure_definition_compare.rb
|
1402
1449
|
- lib/fhir_models/fhir_ext/structure_definition_finding.rb
|
1403
1450
|
- lib/fhir_models/fluentpath/evaluate.rb
|
1404
1451
|
- lib/fhir_models/fluentpath/expression.rb
|