fhir_models 1.8.2 → 1.8.3
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.
- 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
|