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.
@@ -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__))