lib/jsonpath/parser.rb in jsonpath-1.0.0 vs lib/jsonpath/parser.rb in jsonpath-1.0.1

- old
+ new

@@ -12,43 +12,43 @@ end # parse will parse an expression in the following way. # Split the expression up into an array of legs for && and || operators. # Parse this array into a map for which the keys are the parsed legs - # of the split. This map is then used to replace the expression with their + #  of the split. This map is then used to replace the expression with their # corresponding boolean or numeric value. This might look something like this: # ((false || false) && (false || true)) - # Once this string is assembled... we proceed to evaluate from left to right. - # The above string is broken down like this: + #  Once this string is assembled... we proceed to evaluate from left to right. + #  The above string is broken down like this: # (false && (false || true)) # (false && true) - # false + #  false def parse(exp) exps = exp.split(/(&&)|(\|\|)/) construct_expression_map(exps) - @_expr_map.each {|k, v| exp.sub!(k, "#{v}")} + @_expr_map.each { |k, v| exp.sub!(k, v.to_s) } raise ArgumentError, "unmatched parenthesis in expression: #{exp}" unless check_parenthesis_count(exp) - while (exp.include?("(")) - exp = parse_parentheses(exp) - end + + exp = parse_parentheses(exp) while exp.include?('(') bool_or_exp(exp) end # Construct a map for which the keys are the expressions - # and the values are the corresponding parsed results. + #  and the values are the corresponding parsed results. # Exp.: # {"(@['author'] =~ /herman|lukyanenko/i)"=>0} # {"@['isTrue']"=>true} def construct_expression_map(exps) - exps.each_with_index do |item, index| + exps.each_with_index do |item, _index| next if item == '&&' || item == '||' + item = item.strip.gsub(/\)*$/, '').gsub(/^\(*/, '') @_expr_map[item] = parse_exp(item) end end - # using a scanner break down the individual expressions and determine if + #  using a scanner break down the individual expressions and determine if # there is a match in the JSON for it or not. def parse_exp(exp) exp = exp.sub(/@/, '').gsub(/^\(/, '').gsub(/\)$/, '').tr('"', '\'').strip scanner = StringScanner.new(exp) elements = [] @@ -98,83 +98,84 @@ # @TODO: Remove this once JsonPath no longer supports ruby versions below 2.3 def dig(keys, hash) return nil unless hash.is_a? Hash return nil unless hash.key?(keys.first) return hash.fetch(keys.first) if keys.size == 1 + prev = keys.shift dig(keys, hash.fetch(prev)) end - # This will break down a parenthesis from the left to the right - # and replace the given expression with it's returned value. + #  This will break down a parenthesis from the left to the right + #  and replace the given expression with it's returned value. # It does this in order to make it easy to eliminate groups # one-by-one. def parse_parentheses(str) opening_index = 0 closing_index = 0 - (0..str.length-1).step(1) do |i| - if str[i] == '(' - opening_index = i - end + (0..str.length - 1).step(1) do |i| + opening_index = i if str[i] == '(' if str[i] == ')' closing_index = i break end end - to_parse = str[opening_index+1..closing_index-1] + to_parse = str[opening_index + 1..closing_index - 1] - # handle cases like (true && true || false && true) in + #  handle cases like (true && true || false && true) in # one giant parenthesis. top = to_parse.split(/(&&)|(\|\|)/) - top = top.map{|t| t.strip} + top = top.map(&:strip) res = bool_or_exp(top.shift) top.each_with_index do |item, index| case item when '&&' res &&= top[index + 1] when '||' res ||= top[index + 1] end end - # if we are at the last item, the opening index will be 0 + #  if we are at the last item, the opening index will be 0 # and the closing index will be the last index. To avoid # off-by-one errors we simply return the result at that point. - if closing_index+1 >= str.length && opening_index == 0 - return "#{res}" + if closing_index + 1 >= str.length && opening_index == 0 + return res.to_s else - return "#{str[0..opening_index-1]}#{res}#{str[closing_index+1..str.length]}" + return "#{str[0..opening_index - 1]}#{res}#{str[closing_index + 1..str.length]}" end end - # This is convoluted and I should probably refactor it somehow. - # The map that is created will contain strings since essentially I'm + #  This is convoluted and I should probably refactor it somehow. + #  The map that is created will contain strings since essentially I'm # constructing a string like `true || true && false`. # With eval the need for this would disappear but never the less, here - # it is. The fact is that the results can be either boolean, or a number + #  it is. The fact is that the results can be either boolean, or a number # in case there is only indexing happening like give me the 3rd item... or # it also can be nil in case of regexes or things that aren't found. # Hence, I have to be clever here to see what kind of variable I need to # provide back. def bool_or_exp(b) - if "#{b}" == 'true' + if b.to_s == 'true' return true - elsif "#{b}" == 'false' + elsif b.to_s == 'false' return false - elsif "#{b}" == "" + elsif b.to_s == '' return nil end + b = Float(b) rescue b b end # this simply makes sure that we aren't getting into the whole - # parenthesis parsing business without knowing that every parenthesis + #  parenthesis parsing business without knowing that every parenthesis # has its pair. def check_parenthesis_count(exp) - return true unless exp.include?("(") + return true unless exp.include?('(') + depth = 0 exp.chars.each do |c| if c == '(' depth += 1 elsif c == ')'