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.
@@ -36,7 +36,7 @@ module FHIR
36
36
 
37
37
  def fix_name(name)
38
38
  fix = nil
39
- fix = "local_#{name}" if %w(class method resourceType).include?(name)
39
+ fix = "local_#{name}" if %w[class method resourceType].include?(name)
40
40
  fix
41
41
  end
42
42
  end
@@ -6,7 +6,7 @@ module FHIR
6
6
  # templates keeps track of all the templates in context within a given StructureDefinition
7
7
  attr_accessor :templates
8
8
 
9
- def initialize(auto_setup = true)
9
+ def initialize(auto_setup: true)
10
10
  # load the valueset expansions
11
11
  @defn = FHIR::Definitions
12
12
  # templates is an array
@@ -29,7 +29,7 @@ module FHIR
29
29
  def generate_metadata
30
30
  template = FHIR::Boot::Template.new([], true)
31
31
 
32
- primitives = @defn.get_primitive_types
32
+ primitives = @defn.primitive_types
33
33
  hash = {}
34
34
  primitives.each do |p|
35
35
  field = FHIR::Field.new
@@ -52,10 +52,10 @@ module FHIR
52
52
  end
53
53
  template.constants['PRIMITIVES'] = hash
54
54
 
55
- template.constants['TYPES'] = @defn.get_complex_types.map { |t| t['id'] }
55
+ template.constants['TYPES'] = @defn.complex_types.map { |t| t['id'] }
56
56
 
57
57
  # resources
58
- template.constants['RESOURCES'] = @defn.get_resource_definitions.map { |r| r['id'] }
58
+ template.constants['RESOURCES'] = @defn.resource_definitions.map { |r| r['id'] }
59
59
 
60
60
  filename = File.join(@lib, 'fhir', 'metadata.rb')
61
61
  file = File.open(filename, 'w:UTF-8')
@@ -67,13 +67,13 @@ module FHIR
67
67
  folder = File.join @lib, 'fhir', 'types'
68
68
  # complex data types start with an uppercase letter
69
69
  # and we'll filter out profiles on types (for example, Age is a profile on Quantity)
70
- complex_types = @defn.get_complex_types
70
+ complex_types = @defn.complex_types
71
71
  generate_class_files(folder, complex_types)
72
72
  end
73
73
 
74
74
  def generate_resources
75
75
  folder = File.join @lib, 'fhir', 'resources'
76
- generate_class_files(folder, @defn.get_resource_definitions)
76
+ generate_class_files(folder, @defn.resource_definitions)
77
77
  end
78
78
 
79
79
  def generate_class_files(folder = @lib, structure_defs = [])
@@ -81,7 +81,7 @@ module FHIR
81
81
  @templates.clear
82
82
  type_name = structure_def['id']
83
83
  template = generate_class([type_name], structure_def, true)
84
- params = @defn.get_search_parameters(type_name)
84
+ params = @defn.search_parameters(type_name)
85
85
  template.constants['SEARCH_PARAMS'] = params unless params.nil?
86
86
  filename = File.join(folder, "#{type_name}.rb")
87
87
  file = File.open(filename, 'w:UTF-8')
@@ -98,7 +98,7 @@ module FHIR
98
98
 
99
99
  def generate_class(hierarchy, structure_def, top_level = false)
100
100
  type_name = structure_def['id']
101
- constrained_type = structure_def['constrained_type']
101
+ constrained_type = structure_def['type']
102
102
  path_type = type_name
103
103
  path_type = constrained_type if constrained_type
104
104
 
@@ -162,7 +162,7 @@ module FHIR
162
162
  element['type'].select { |t| t['code'] == 'Reference' || t['code'] == 'Extension' }.each do |data_type|
163
163
  profiles << data_type['targetProfile']
164
164
  end
165
- profiles.select! { |p| !p.nil? }
165
+ profiles.reject!(&:nil?)
166
166
  profiles.flatten!
167
167
 
168
168
  # Calculate fields that have multiple data types
@@ -186,7 +186,7 @@ module FHIR
186
186
  field.max = field.max.to_i
187
187
  field.max = '*' if element['max'] == '*'
188
188
 
189
- if %w(code Coding CodeableConcept).include?(data_type) && element['binding']
189
+ if %w[code Coding CodeableConcept].include?(data_type) && element['binding']
190
190
  field.binding = element['binding']
191
191
  field.binding['uri'] = field.binding['valueSetUri']
192
192
  field.binding['uri'] = field.binding['valueSetReference'] if field.binding['uri'].nil?
@@ -199,7 +199,7 @@ module FHIR
199
199
  codes = @defn.get_codes(field.binding['uri'])
200
200
  field.valid_codes = codes unless codes.nil?
201
201
  FHIR.logger.warn " MISSING EXPANSION -- #{field.path} #{field.min}..#{field.max}: #{field.binding['uri']} (#{field.binding['strength']})" if field.valid_codes.empty? && field.binding['uri'] && !field.binding['uri'].end_with?('bcp47') && !field.binding['uri'].end_with?('bcp13.txt')
202
- elsif %w(Element BackboneElement).include?(data_type)
202
+ elsif %w[Element BackboneElement].include?(data_type)
203
203
  # This is a nested structure or class
204
204
  field.type = "#{hierarchy.join('::')}::#{cap_first(field.name)}"
205
205
  end
@@ -40,9 +40,14 @@ module FHIR
40
40
  next if meta.nil?
41
41
  local_name = key
42
42
  local_name = meta['local_name'] if meta['local_name']
43
- instance_variable_set("@#{local_name}", value) rescue nil
43
+ begin
44
+ instance_variable_set("@#{local_name}", value)
45
+ rescue
46
+ # TODO: this appears to be a dead code branch
47
+ nil
48
+ end
44
49
  # inflate the value if it isn't a primitive
45
- klass = Module.const_get("FHIR::#{meta['type']}") rescue nil
50
+ klass = FHIR::PRIMITIVES.key?(meta['type']) ? nil : FHIR.const_get(meta['type'])
46
51
  if !klass.nil? && !value.nil?
47
52
  # handle array of objects
48
53
  if value.is_a?(Array)
@@ -78,12 +83,20 @@ module FHIR
78
83
 
79
84
  def make_child(child, klass)
80
85
  if child['resourceType']
81
- klass = Module.const_get("FHIR::#{child['resourceType']}") rescue nil
86
+ klass = begin
87
+ FHIR.const_get(child['resourceType'])
88
+ rescue => _exception
89
+ # TODO: this appears to be a dead code branch
90
+ # TODO: should this log / re-raise the exception if encountered instead of silently swallowing it?
91
+ nil
92
+ end
82
93
  end
83
94
  begin
84
95
  obj = klass.new(child)
85
- rescue => e
86
- FHIR.logger.error("Unable to inflate embedded class #{klass}\n#{e.backtrace}")
96
+ rescue => exception
97
+ # TODO: this appears to be a dead code branch
98
+ # TODO: should this re-raise the exception if encountered instead of silently swallowing it?
99
+ FHIR.logger.error("Unable to inflate embedded class #{klass}\n#{exception.backtrace}")
87
100
  end
88
101
  obj
89
102
  end
@@ -16,7 +16,8 @@ module FHIR
16
16
  klass = Module.const_get("FHIR::#{resource_type}")
17
17
  resource = klass.new(hash)
18
18
  rescue => e
19
- FHIR.logger.error("Failed to deserialize JSON:\n#{json}\n#{e.backtrace}")
19
+ FHIR.logger.error("Failed to deserialize JSON:\n#{e.backtrace}")
20
+ FHIR.logger.debug("JSON:\n#{json}")
20
21
  resource = nil
21
22
  end
22
23
  resource
@@ -5,6 +5,7 @@ require 'bcp47'
5
5
 
6
6
  module FHIR
7
7
  class Model
8
+ extend FHIR::Deprecate
8
9
  def initialize(hash = {})
9
10
  from_hash(hash)
10
11
  self.class::METADATA.each do |key, value|
@@ -14,11 +15,11 @@ module FHIR
14
15
  end
15
16
  end
16
17
 
17
- def method_missing(method, *args, &block)
18
+ def method_missing(method, *_args, &_block)
18
19
  if defined?(self.class::MULTIPLE_TYPES) && self.class::MULTIPLE_TYPES[method.to_s]
19
20
  self.class::MULTIPLE_TYPES[method.to_s].each do |type|
20
21
  type[0] = type[0].upcase
21
- value = self.method("#{method}#{type}").call
22
+ value = send("#{method}#{type}".to_sym)
22
23
  return value unless value.nil?
23
24
  end
24
25
  return nil
@@ -41,7 +42,7 @@ module FHIR
41
42
  return ext.first.value.nil? ? ext.first : ext.first.value
42
43
  end
43
44
  end
44
- super(method, *args, &block)
45
+ raise NoMethodError.new("undefined method `#{method}' for #{inspect}", method)
45
46
  end
46
47
 
47
48
  def to_reference
@@ -87,9 +88,10 @@ module FHIR
87
88
  end
88
89
  end
89
90
 
90
- def is_valid?
91
+ def valid?
91
92
  validate.empty?
92
93
  end
94
+ deprecate :is_valid?, :valid?
93
95
 
94
96
  def validate(contained = nil)
95
97
  validate_profile(self.class::METADATA, contained)
@@ -125,7 +127,11 @@ module FHIR
125
127
  end
126
128
  end # metadata.each
127
129
  # check multiple types
128
- multiple_types = self.class::MULTIPLE_TYPES rescue {}
130
+ multiple_types = begin
131
+ self.class::MULTIPLE_TYPES
132
+ rescue
133
+ {}
134
+ end
129
135
  multiple_types.each do |prefix, suffixes|
130
136
  present = []
131
137
  suffixes.each do |suffix|
@@ -137,10 +143,11 @@ module FHIR
137
143
  end
138
144
  errors[prefix] = ["#{prefix}[x]: more than one type present."] if present.length > 1
139
145
  # remove errors for suffixes that are not present
146
+ next unless present.length == 1
140
147
  suffixes.each do |suffix|
141
148
  typename = "#{prefix}#{suffix[0].upcase}#{suffix[1..-1]}"
142
149
  errors.delete(typename) unless present.include?(typename)
143
- end if present.length==1
150
+ end
144
151
  end
145
152
  errors.keep_if { |_k, v| (v && !v.empty?) }
146
153
  end
@@ -195,7 +202,7 @@ module FHIR
195
202
  match = (v =~ Regexp.new(primitive_meta['regex']))
196
203
  errors[field] << "#{meta['path']}: #{v} does not match #{datatype} regex" if match.nil?
197
204
  else
198
- errors[field] << "#{meta['path']}: #{v} is not a valid #{datatype}" unless is_primitive?(datatype, v)
205
+ errors[field] << "#{meta['path']}: #{v} is not a valid #{datatype}" unless FHIR.primitive?(datatype: datatype, value: v)
199
206
  end
200
207
  end
201
208
  # check binding
@@ -226,12 +233,13 @@ module FHIR
226
233
 
227
234
  def validate_reference_type(ref, meta, contained_here, errors)
228
235
  return unless ref.reference && meta['type_profiles']
236
+ return if ref.reference.start_with?('urn:uuid:', 'urn:oid:')
229
237
  matches_one_profile = false
230
238
  meta['type_profiles'].each do |p|
231
239
  basetype = p.split('/').last
232
240
  matches_one_profile = true if ref.reference.include?(basetype)
233
241
  # check profiled resources
234
- profile_basetype = FHIR::Definitions.get_basetype(p)
242
+ profile_basetype = FHIR::Definitions.basetype(p)
235
243
  matches_one_profile = true if profile_basetype && ref.reference.include?(profile_basetype)
236
244
  end
237
245
  matches_one_profile = true if meta['type_profiles'].include?('http://hl7.org/fhir/StructureDefinition/Resource')
@@ -250,37 +258,11 @@ module FHIR
250
258
  errors << "#{meta['path']}: incorrect Reference type, expected #{meta['type_profiles'].map { |x| x.split('/').last }.join('|')}" unless matches_one_profile
251
259
  end
252
260
 
253
- def is_primitive?(datatype, value)
254
- # Remaining data types: handle special cases before checking type StructureDefinitions
255
- case datatype.downcase
256
- when 'boolean'
257
- value == true || value == false || value.downcase == 'true' || value.downcase == 'false'
258
- when 'code'
259
- value.is_a?(String) && value.size >= 1 && value.size == value.rstrip.size
260
- when 'string', 'markdown'
261
- value.is_a?(String)
262
- when 'xhtml'
263
- fragment = Nokogiri::HTML::DocumentFragment.parse(value)
264
- value.is_a?(String) && fragment.errors.size.zero?
265
- when 'base64binary'
266
- regex = /[^0-9\+\/\=A-Za-z\r\n ]/
267
- value.is_a?(String) && (regex =~ value).nil?
268
- when 'uri'
269
- !URI.parse(value).nil? rescue false
270
- when 'instant'
271
- regex = /\A[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))))\Z/
272
- value.is_a?(String) && !(regex =~ value).nil?
273
- when 'integer', 'unsignedint'
274
- (!Integer(value).nil? rescue false)
275
- when 'positiveint'
276
- (!Integer(value).nil? rescue false) && (Integer(value) >= 0)
277
- when 'decimal'
278
- (!Float(value).nil? rescue false)
279
- else
280
- FHIR.logger.warn "Unable to check #{value} for datatype #{datatype}"
281
- false
282
- end
261
+ def primitive?(datatype, value)
262
+ FHIR.logger.warn("prefer using FHIR.primitive? Called from #{caller.first}")
263
+ FHIR.primitive?(datatype: datatype, value: value)
283
264
  end
265
+ deprecate :is_primitive?, :primitive?
284
266
 
285
267
  def check_binding(uri, value)
286
268
  valid = false
@@ -298,6 +280,6 @@ module FHIR
298
280
  valid
299
281
  end
300
282
 
301
- private :validate_reference_type, :is_primitive?, :check_binding, :validate_field
283
+ private :validate_reference_type, :check_binding, :validate_field
302
284
  end
303
285
  end
@@ -11,7 +11,7 @@ module FHIR
11
11
 
12
12
  # Remove entries that do not interest us: CompartmentDefinitions, OperationDefinitions, Conformance statements
13
13
  hash['entry'].select! do |entry|
14
- %w(StructureDefinition ValueSet CodeSystem SearchParameter).include? entry['resource']['resourceType']
14
+ %w[StructureDefinition ValueSet CodeSystem SearchParameter].include? entry['resource']['resourceType']
15
15
  end
16
16
 
17
17
  # Remove unnecessary elements from the hash
@@ -34,37 +34,25 @@ module FHIR
34
34
 
35
35
  def self.pre_process_structuredefinition(hash)
36
36
  # Remove large HTML narratives and unused content
37
- %w(text publisher contact description requirements mapping).each { |key| hash.delete(key) }
37
+ %w[text publisher contact description requirements mapping].each { |key| hash.delete(key) }
38
38
 
39
- # Remove unused descriptions within the snapshot elements
40
- if hash['snapshot']
41
- hash['snapshot']['element'].each do |element|
42
- %w(short definition comments requirements alias mapping).each { |key| element.delete(key) }
43
- end
44
- end
45
- # Remove unused descriptions within the differential elements
46
- if hash['differential']
47
- hash['differential']['element'].each do |element|
48
- %w(short definition comments requirements alias mapping).each { |key| element.delete(key) }
39
+ # Remove unused descriptions within the snapshot and differential elements
40
+ %w[snapshot differential].each do |key|
41
+ next unless hash[key]
42
+ hash[key]['element'].each do |element|
43
+ %w[short definition comments requirements alias mapping].each { |subkey| element.delete(subkey) }
49
44
  end
50
45
  end
51
46
  end
52
47
 
53
48
  def self.pre_process_valueset(hash)
54
49
  # Remove large HTML narratives and unused content
55
- %w(meta text publisher contact description requirements).each { |key| hash.delete(key) }
56
-
57
- if hash['compose'] && hash['compose']['include']
58
- hash['compose']['include'].each do |element|
59
- next unless element['concept']
60
- element['concept'].each do |concept|
61
- concept.delete('designation')
62
- end
63
- end
64
- end
50
+ %w[meta text publisher contact description requirements].each { |key| hash.delete(key) }
65
51
 
66
- if hash['compose'] && hash['compose']['exclude']
67
- hash['compose']['exclude'].each do |element|
52
+ return unless hash['compose']
53
+ %w[include exclude].each do |key|
54
+ next unless hash['compose'][key]
55
+ hash['compose'][key].each do |element|
68
56
  next unless element['concept']
69
57
  element['concept'].each do |concept|
70
58
  concept.delete('designation')
@@ -75,27 +63,24 @@ module FHIR
75
63
 
76
64
  def self.pre_process_codesystem(hash)
77
65
  # Remove large HTML narratives and unused content
78
- %w(meta text publisher contact description requirements).each { |key| hash.delete(key) }
79
-
80
- if hash['concept']
81
- hash['concept'].each do |concept|
82
- pre_process_codesystem_concept(concept)
83
- end
66
+ %w[meta text publisher contact description requirements].each { |key| hash.delete(key) }
67
+ return unless hash['concept']
68
+ hash['concept'].each do |concept|
69
+ pre_process_codesystem_concept(concept)
84
70
  end
85
71
  end
86
72
 
87
73
  def self.pre_process_codesystem_concept(hash)
88
- %w(extension definition designation).each { |key| hash.delete(key) }
89
- if hash['concept']
90
- hash['concept'].each do |concept|
91
- pre_process_codesystem_concept(concept)
92
- end
74
+ %w[extension definition designation].each { |key| hash.delete(key) }
75
+ return unless hash['concept']
76
+ hash['concept'].each do |concept|
77
+ pre_process_codesystem_concept(concept)
93
78
  end
94
79
  end
95
80
 
96
81
  def self.pre_process_searchparam(hash)
97
82
  # Remove large HTML narratives and unused content
98
- %w(id url name date publisher contact description xpathUsage).each { |key| hash.delete(key) }
83
+ %w[id url name date publisher contact description xpathUsage].each { |key| hash.delete(key) }
99
84
  end
100
85
 
101
86
  def self.remove_fhir_comments(hash)
@@ -2,6 +2,7 @@ module FHIR
2
2
  module Boot
3
3
  class Template
4
4
  include FHIR::Hashable
5
+ extend FHIR::Deprecate
5
6
 
6
7
  attr_accessor :name
7
8
  attr_accessor :hierarchy
@@ -21,7 +22,7 @@ module FHIR
21
22
  @top_level = top_level
22
23
  end
23
24
 
24
- def get_metadata
25
+ def metadata
25
26
  metadata = {}
26
27
  @fields.each do |field|
27
28
  if metadata.keys.include?(field.name)
@@ -41,6 +42,7 @@ module FHIR
41
42
  end
42
43
  metadata
43
44
  end
45
+ deprecate :get_metadata, :metadata
44
46
 
45
47
  def to_s(offset = 0)
46
48
  # create an array of Strings, one per line
@@ -66,7 +68,6 @@ module FHIR
66
68
  s << ''
67
69
 
68
70
  # add mandatory METADATA constant
69
- metadata = get_metadata
70
71
  @constants['METADATA'] = metadata unless metadata.empty?
71
72
 
72
73
  # add constants
@@ -94,18 +95,13 @@ module FHIR
94
95
  end
95
96
 
96
97
  # calculate the longest field name for whitespace layout
97
- max_name_size = 0
98
- @fields.each do |f|
99
- name = f.local_name || f.name
100
- max_name_size = name.length if name.length > max_name_size
101
- end
102
- max_name_size += 1
98
+ max_name_size = (@fields.map { |field| field.local_name || field.name }.map(&:length).max || 0) + 1
103
99
 
104
100
  # declare attributes
105
101
  @fields.each do |field|
106
102
  s << "#{space}attr_accessor :"
107
103
  local_name = field.local_name || field.name
108
- s[-1] << ("%-#{max_name_size}s" % local_name.to_s)
104
+ s[-1] << format("%-#{max_name_size}s", local_name)
109
105
  # add comment after field declaration
110
106
  s[-1] << "# #{field.min}-#{field.max} "
111
107
  s[-1] << '[ ' if field.max.to_i > 1 || field.max == '*'
@@ -1,6 +1,7 @@
1
1
  require 'nokogiri'
2
2
  module FHIR
3
3
  module Xml
4
+ extend FHIR::Deprecate
4
5
  #
5
6
  # This module includes methods to serialize or deserialize FHIR resources to and from XML.
6
7
  #
@@ -32,7 +33,7 @@ module FHIR
32
33
  end
33
34
 
34
35
  hash.each do |key, value|
35
- next if %w(extension modifierExtension).include?(name) && key == 'url'
36
+ next if %w[extension modifierExtension].include?(name) && key == 'url'
36
37
  next if key == 'id' && !FHIR::RESOURCES.include?(name)
37
38
  if value.is_a?(Hash)
38
39
  node.add_child(hash_to_xml_node(key, value, doc))
@@ -61,7 +62,7 @@ module FHIR
61
62
  node.add_child(child)
62
63
  end
63
64
  end
64
- node.set_attribute('url', hash['url']) if %w(extension modifierExtension).include?(name)
65
+ node.set_attribute('url', hash['url']) if %w[extension modifierExtension].include?(name)
65
66
  node.set_attribute('id', hash['id']) if hash['id'] && !FHIR::RESOURCES.include?(name)
66
67
  node
67
68
  end
@@ -78,7 +79,8 @@ module FHIR
78
79
  klass = Module.const_get("FHIR::#{resource_type}")
79
80
  resource = klass.new(hash)
80
81
  rescue => e
81
- FHIR.logger.error("Failed to deserialize XML:\n#{xml}\n#{e.backtrace}")
82
+ FHIR.logger.error("Failed to deserialize XML:\n#{e.backtrace}")
83
+ FHIR.logger.debug("XML:\n#{xml}")
82
84
  resource = nil
83
85
  end
84
86
  resource
@@ -106,7 +108,7 @@ module FHIR
106
108
  end
107
109
  end
108
110
  end
109
- hash['url'] = node.get_attribute('url') if %w(extension modifierExtension).include?(node.name)
111
+ hash['url'] = node.get_attribute('url') if %w[extension modifierExtension].include?(node.name)
110
112
  hash['id'] = node.get_attribute('id') if node.get_attribute('id') # Testscript fixture ids (applies to any BackboneElement)
111
113
  hash['resourceType'] = node.name if FHIR::RESOURCES.include?(node.name)
112
114
 
@@ -122,9 +124,10 @@ module FHIR
122
124
  end
123
125
  end
124
126
 
125
- def self.is_valid?(xml)
127
+ def self.valid?(xml)
126
128
  validate(xml).empty?
127
129
  end
130
+ deprecate :is_valid?, :valid?
128
131
 
129
132
  def self.validate(xml)
130
133
  defns = File.expand_path '../definitions/schema', File.dirname(File.absolute_path(__FILE__))