lib/fhir_models/fluentpath/evaluate.rb in fhir_models-1.6.8 vs lib/fhir_models/fluentpath/evaluate.rb in fhir_models-1.8.0

- old
+ new

@@ -1,12 +1,11 @@ module FluentPath - - @@context = Hash.new + @@context = {} @@parent = nil # This is the entry point to using the FluentPath class - def self.evaluate(expression, hash, parent=nil) + def self.evaluate(expression, hash, parent = nil) @@context = hash @@parent = parent tree = FluentPath.parse(expression) FHIR.logger.debug "TREE: #{tree}" compute(tree, hash) @@ -14,40 +13,39 @@ # Get a value from a hash, with some special handling of # self references def self.get(key, hash) return @@context if ['$context', '$resource'].include?(key) - return @@parent if key=='$parent' - return 'http://unitsofmeasure.org' if key=='%ucum' - return 'http://snomed.info/sct' if key=='%sct' - return 'http://loinc.org' if key=='%loinc' + return @@parent if key == '$parent' + return 'http://unitsofmeasure.org' if key == '%ucum' + return 'http://snomed.info/sct' if key == '%sct' + return 'http://loinc.org' if key == '%loinc' return key.gsub!(/\A\'|\'\Z/, '') if key.start_with?("'") && key.end_with?("'") key.gsub!(/\A"|"\Z/, '') # remove quotes around path if they exist if hash.is_a?(Array) response = [] hash.each do |e| - if e.is_a?(Hash) - item = e[key] - if item.is_a?(Array) - item.each{|i| response << i } - else - response << item - end + next unless e.is_a?(Hash) + item = e[key] + if item.is_a?(Array) + item.each { |i| response << i } + else + response << item end end return response end - return :null if !hash.is_a?(Hash) - return hash if hash['resourceType']==key + return :null unless hash.is_a?(Hash) + return hash if hash['resourceType'] == key val = hash[key] if val.nil? # this block is a dangerous hack to get fields of multiple data types # e.g. 'value' instead of 'valueQuantity', or 'onset' instead of 'onsetDateTime' or 'onsetPeriod' - nkey = hash.keys.select{|x|x.start_with?(key)}.first - if !nkey.nil? + nkey = hash.keys.select { |x| x.start_with?(key) }.first + unless nkey.nil? tail = nkey.gsub(key, '') - val = hash[nkey] if (tail[0]==tail[0].capitalize) + val = hash[nkey] if tail[0] == tail[0].capitalize end end val = :null if val.nil? val = "'#{val}'" if val.is_a?(String) && !(val.start_with?("'") && val.end_with?("'")) val @@ -57,42 +55,42 @@ # Everything else is true. def self.convert_to_boolean(value) return false if value.nil? return false if value.is_a?(Array) && value.empty? return false if value.is_a?(Hash) && value.empty? - return false if value==:null - return false if value==false - return true + return false if value == :null + return false if value == false + true end def self.clean_index(tree, index) - tree[index] = nil if !index.nil? + tree[index] = nil unless index.nil? end # evaluate a parsed expression given some context data def self.compute(tree, data) tree = tree.tree if tree.is_a?(FluentPath::Expression) # --------------- OPERATOR PRECEDENCE ------------------ - #01 . (path/function invocation) - #02 [] (indexer) - #03 unary + and - - #04: *, /, div, mod - #05: +, -, - #06: | - #07: >, <, >=, <= - #08: is, as - #09: =, ~, !=, !~ - #10: in, contains - #11: and - #12: xor, or - #13: implies + # 01 . (path/function invocation) + # 02 [] (indexer) + # 03 unary + and - + # 04: *, /, div, mod + # 05: +, -, + # 06: | + # 07: >, <, >=, <= + # 08: is, as + # 09: =, ~, !=, !~ + # 10: in, contains + # 11: and + # 12: xor, or + # 13: implies # evaluate all the data at this level functions = [:where, :select, :extension, :children, :first, :last, :tail] size = -1 substitutions = 1 - while(tree.length!=size || substitutions > 0) + while tree.length != size || substitutions > 0 substitutions = 0 FHIR.logger.debug "DATA: #{tree}" previous_node = nil previous_index = nil size = tree.length @@ -100,13 +98,13 @@ if node.is_a?(String) && !(node.start_with?("'") && node.end_with?("'")) array_index = nil if node.include?('[') && node.end_with?(']') array_index = node[node.index('[')..-1].gsub(/\[|\]/, '') t = get(array_index, data) - t = array_index.to_i if(t.nil? || t==:null) + t = array_index.to_i if t.nil? || t == :null array_index = t - node = node[0..node.index('[')-1] + node = node[0..node.index('[') - 1] end if previous_node.is_a?(Hash) || previous_node.is_a?(Array) tree[index] = get(node, previous_node) clean_index(tree, previous_index) elsif !previous_node.is_a?(FluentPath::Expression) @@ -120,48 +118,43 @@ previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) case node when :where # the previous node should be data (as Array or Hash) # the next node should be a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'Where function requires a block.' end - previous_node = [] if previous_node==:null + previous_node = [] if previous_node == :null if previous_node.is_a?(Array) previous_node.keep_if do |item| sub = compute(block.clone, item) convert_to_boolean(sub) end tree[index] = previous_node clean_index(tree, previous_index) elsif previous_node.is_a?(Hash) sub = compute(block, previous_node) - if convert_to_boolean(sub) - tree[index] = previous_node - clean_index(tree, previous_index) - else - tree[index] = {} - clean_index(tree, previous_index) - end + tree[index] = convert_to_boolean(sub) ? previous_node : {} + clean_index(tree, previous_index) else raise "Where function not applicable to #{previous_node.class}: #{previous_node}" end break when :select # select is equivalent to ruby Array.map! # the previous node should be data (as Array or Hash) # the next node should be a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'Select function requires a block.' end - previous_node = [] if previous_node==:null + previous_node = [] if previous_node == :null if previous_node.is_a?(Array) previous_node.map! do |item| compute(block.clone, item) end tree[index] = previous_node @@ -174,13 +167,13 @@ end break when :extension # the previous node should be a data (as Hash) # the next node optionally is a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'Extension function requires a block.' end if previous_node.is_a?(Hash) FHIR.logger.debug 'Evaling Extension Block....' @@ -190,11 +183,11 @@ begin url = block.tree.first.gsub(/\'|\"/, '') rescue raise 'Extension function requires a single URL as String.' end - ext = exts.select{|x|x['url']==url}.first + ext = exts.select { |x| x['url'] == url }.first tree[index] = ext clean_index(tree, previous_index) else raise "Extension function not applicable to #{exts.class}: #{exts}" end @@ -206,14 +199,14 @@ # if there is a previous node, it should be data (as Hash) # otherwise, use the context as data if previous_node.is_a?(Hash) tree[index] = previous_node.values clean_index(tree, previous_index) - substitutions+=1 + substitutions += 1 elsif data.is_a?(Hash) tree[index] = data.values - substitutions+=1 + substitutions += 1 else raise "Children not applicable to #{previous_node.class}: #{previous_node}" end break when :first @@ -233,11 +226,11 @@ raise "Last function is not applicable to #{previous_node.class}: #{previous_node}" end when :tail # the previous node should be an Array of length > 1 if previous_node.is_a?(Array) - tree[index] = previous_node.last(previous_node.length-1) + tree[index] = previous_node.last(previous_node.length - 1) clean_index(tree, previous_index) else raise "Tail function is not applicable to #{previous_node.class}: #{previous_node}" end end @@ -255,11 +248,11 @@ FHIR.logger.debug "DATA: #{tree}" # evaluate all the functions at this level functions = [:all, :not, :empty, :exists, :startsWith, :substring, :contains, :in, :distinct, :toInteger, :count] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "FUNC: #{tree}" previous_node = data previous_index = nil size = tree.length tree.each_with_index do |node, index| @@ -267,40 +260,39 @@ previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) case node when :all if previous_node.is_a?(Array) result = true - previous_node.each{|item| result = (result && convert_to_boolean(item))} + previous_node.each { |item| result = (result && convert_to_boolean(item)) } tree[index] = result - clean_index(tree, previous_index) else tree[index] = convert_to_boolean(previous_node) - clean_index(tree, previous_index) end + clean_index(tree, previous_index) when :not tree[index] = !convert_to_boolean(previous_node) clean_index(tree, previous_index) when :count tree[index] = 0 - tree[index] = 1 if !previous_node.nil? + tree[index] = 1 unless previous_node.nil? tree[index] = previous_node.length if previous_node.is_a?(Array) clean_index(tree, previous_index) when :empty - tree[index] = (previous_node==:null || previous_node.empty? rescue previous_node.nil?) + tree[index] = (previous_node == :null || previous_node.empty? rescue previous_node.nil?) clean_index(tree, previous_index) when :exists - tree[index] = !previous_node.nil? && previous_node!=:null + tree[index] = !previous_node.nil? && previous_node != :null clean_index(tree, previous_index) when :distinct tree[index] = (previous_node.uniq rescue previous_node) clean_index(tree, previous_index) when :startsWith # the previous node should be a data (as String) # the next node should be a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'StartsWith function requires a block.' end if previous_node.is_a?(String) FHIR.logger.debug 'Evaling StartsWith Block....' @@ -312,41 +304,41 @@ end break when :substring # the previous node should be a data (as String) # the next node should be a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'Substring function requires a block.' end if previous_node.is_a?(String) args = block.tree.first start = 0 length = previous_node.length if args.is_a?(String) && args.include?(',') args = args.split(',') start = args.first.to_i - length = args.last.to_i-1 + length = args.last.to_i - 1 else FHIR.logger.debug 'Evaling Substring Block....' start = compute(block, data) length = previous_node.length - start end - tree[index] = previous_node[start..(start+length)] + tree[index] = previous_node[start..(start + length)] clean_index(tree, previous_index) else raise "Substring function not applicable to #{previous_node.class}: #{previous_node}" end break when :contains # the previous node should be a data (as String) # the next node should be a block or subexpression (as FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) - tree[index+1] = nil + tree[index + 1] = nil else raise 'Contains function requires a block.' end if previous_node.is_a?(String) FHIR.logger.debug 'Evaling Contains Block....' @@ -358,22 +350,22 @@ end break when :in # the previous node should be a data (as String, Number, or Boolean) # the next node should an Array (possibly as a block or subexpression/FluentPath::Expression) - block = tree[index+1] + block = tree[index + 1] if block.is_a?(FluentPath::Expression) FHIR.logger.debug 'Evaling In Block....' - tree[index+1] = compute(block, data) + tree[index + 1] = compute(block, data) end - array = tree[index+1] + array = tree[index + 1] if array.is_a?(Array) - tree[index+1] = nil + tree[index + 1] = nil else raise 'In function requires an array.' end - if previous_node.is_a?(String) || previous_node==true || previous_node==false || previous_node.is_a?(Numeric) + if previous_node.is_a?(String) || previous_node == true || previous_node == false || previous_node.is_a?(Numeric) tree[index] = array.include?(previous_node) rescue false clean_index(tree, previous_index) else raise "In function not applicable to #{previous_node.class}: #{previous_node}" end @@ -401,29 +393,29 @@ end # evaluate all mult/div functions = [:"/", :"*"] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "MATH: #{tree}" previous_node = nil previous_index = nil size = tree.length tree.each_with_index do |node, index| if node.is_a?(Symbol) && functions.include?(node) previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) - tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression) + tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression) left = previous_node - right = tree[index+1] + right = tree[index + 1] case node when :"/" - tree[index] = (left/right) + tree[index] = (left / right) when :"*" - tree[index] = (left*right) + tree[index] = (left * right) end tree[previous_index] = nil - tree[index+1] = nil + tree[index + 1] = nil break end previous_index = index previous_node = node end @@ -432,29 +424,29 @@ FHIR.logger.debug "MATH: #{tree}" # evaluate all add/sub functions = [:"+", :"-"] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "MATH: #{tree}" previous_node = nil previous_index = nil size = tree.length tree.each_with_index do |node, index| if node.is_a?(Symbol) && functions.include?(node) previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) - tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression) + tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression) left = previous_node - right = tree[index+1] + right = tree[index + 1] case node when :"+" - tree[index] = (left+right) + tree[index] = (left + right) when :"-" - tree[index] = (left-right) + tree[index] = (left - right) end tree[previous_index] = nil - tree[index+1] = nil + tree[index + 1] = nil break end previous_index = index previous_node = node end @@ -463,39 +455,39 @@ FHIR.logger.debug "MATH: #{tree}" # evaluate all equality tests functions = [:"=", :"!=", :"<=", :">=", :"<", :">"] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "EQ: #{tree}" previous_node = nil previous_index = nil size = tree.length tree.each_with_index do |node, index| if node.is_a?(Symbol) && functions.include?(node) previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) - tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression) + tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression) left = previous_node - right = tree[index+1] + right = tree[index + 1] case node when :"=" - tree[index] = (left==right) + tree[index] = (left == right) when :"!=" - tree[index] = (left!=right) + tree[index] = (left != right) when :"<=" - tree[index] = (left<=right) + tree[index] = (left <= right) when :">=" - tree[index] = (left>=right) + tree[index] = (left >= right) when :"<" - tree[index] = (left<right) + tree[index] = (left < right) when :">" - tree[index] = (left>right) + tree[index] = (left > right) else raise "Equality operator not implemented: #{node}" end tree[previous_index] = nil - tree[index+1] = nil + tree[index + 1] = nil break end previous_index = index previous_node = node end @@ -504,33 +496,33 @@ FHIR.logger.debug "EQ: #{tree}" # evaluate all logical tests functions = [:and, :or, :xor] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "LOGIC: #{tree}" previous_node = nil previous_index = nil size = tree.length tree.each_with_index do |node, index| if node.is_a?(Symbol) && functions.include?(node) previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) - tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression) + tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression) left = convert_to_boolean(previous_node) - right = convert_to_boolean(tree[index+1]) + right = convert_to_boolean(tree[index + 1]) case node when :and - tree[index] = (left&&right) + tree[index] = (left && right) when :or - tree[index] = (left||right) + tree[index] = (left || right) when :xor - tree[index] = (left^right) + tree[index] = (left ^ right) else raise "Logical operator not implemented: #{node}" end tree[previous_index] = nil - tree[index+1] = nil + tree[index + 1] = nil break end previous_index = index previous_node = node end @@ -538,30 +530,30 @@ end FHIR.logger.debug "LOGIC: #{tree}" functions = [:implies] size = -1 - while(tree.length!=size) + while tree.length != size FHIR.logger.debug "IMPLIES: #{tree}" previous_node = nil previous_index = nil size = tree.length tree.each_with_index do |node, index| if node.is_a?(Symbol) && functions.include?(node) previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression) - tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression) + tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression) case node when :implies tree[index] = false - exists = !previous_node.nil? && previous_node!=:null - implication = convert_to_boolean(tree[index+1]) - tree[index] = true if (exists && (implication || tree[index+1]==false)) + exists = !previous_node.nil? && previous_node != :null + implication = convert_to_boolean(tree[index + 1]) + tree[index] = true if exists && (implication || tree[index + 1] == false) else raise "Logical operator not implemented: #{node}" end tree[previous_index] = nil - tree[index+1] = nil + tree[index + 1] = nil break end previous_index = index previous_node = node end @@ -584,7 +576,6 @@ end FHIR.logger.debug "RETURN: #{tree.first}" tree.first end - end