diff --git a/README.md b/README.md index c052ad2..b4729b0 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,6 @@ enum.any?{ |c| c == 'red' } # => true ``` -You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor. - ### More examples For more usage examples and variations on paths, please visit the tests. There are some more complex ones as well. diff --git a/lib/jsonpath.rb b/lib/jsonpath.rb index 9166b63..d25c2e8 100644 --- a/lib/jsonpath.rb +++ b/lib/jsonpath.rb @@ -3,6 +3,7 @@ require 'jsonpath/proxy' require 'jsonpath/enumerable' require 'jsonpath/version' +require 'jsonpath/parser' # JsonPath: initializes the class with a given JsonPath and parses that path # into a token array. diff --git a/lib/jsonpath/enumerable.rb b/lib/jsonpath/enumerable.rb index 7a1f490..7a1bd80 100644 --- a/lib/jsonpath/enumerable.rb +++ b/lib/jsonpath/enumerable.rb @@ -1,19 +1,12 @@ class JsonPath class Enumerable include ::Enumerable - attr_reader :allow_eval - alias_method :allow_eval?, :allow_eval def initialize(path, object, mode, options = nil) @path = path.path @object = object @mode = mode @options = options - @allow_eval = if @options && @options.key?(:allow_eval) - @options[:allow_eval] - else - true - end end def each(context = @object, key = nil, pos = 0, &blk) @@ -27,10 +20,6 @@ def each(context = @object, key = nil, pos = 0, &blk) each(context, key, pos + 1, &blk) if node == @object when /^\[(.*)\]$/ handle_wildecard(node, expr, context, key, pos, &blk) - else - if pos == (@path.size - 1) && node && allow_eval? - yield_value(blk, context, key) if instance_eval("node #{@path[pos]}") - end end if pos > 0 && @path[pos - 1] == '..' || (@path[pos - 1] == '*' && @path[pos] != '..') @@ -75,11 +64,12 @@ def handle_wildecard(node, expr, context, key, pos, &blk) end def handle_question_mark(sub_path, node, pos, &blk) - raise 'Cannot use ?(...) unless eval is enabled' unless allow_eval? case node when Array node.size.times do |index| @_current_node = node[index] + # exps = sub_path[1, sub_path.size - 1] + # if @_current_node.send("[#{exps.gsub(/@/, '@_current_node')}]") if process_function_or_literal(sub_path[1, sub_path.size - 1]) each(@_current_node, nil, pos + 1, &blk) end @@ -113,40 +103,22 @@ def yield_value(blk, context, key) def process_function_or_literal(exp, default = nil) return default if exp.nil? || exp.empty? return Integer(exp) if exp[0] != '(' - return nil unless allow_eval? && @_current_node + return nil unless @_current_node identifiers = /@?((?=][<>=]?\s+?/) + operator = t + elsif t = scanner.scan(/(\s+)?'?(\w+)?[.,]?(\w+)?'?(\s+)?/) # @TODO: At this point I should trim somewhere... + operand = t.delete("'").strip + elsif t = scanner.scan(/.*/) + raise "Could not process symbol: #{t}" + end + end + el = dig(elements, @_current_node) + return false unless el + return true if operator.nil? && el + operand = operand.to_f if operand.to_i.to_s == operand || operand.to_f.to_s == operand + el.send(operator.strip, operand) + end + + private + + # @TODO: Remove this once JsonPath no longer supports ruby versions below 2.3 + def dig(keys, 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 + end +end diff --git a/lib/jsonpath/version.rb b/lib/jsonpath/version.rb index cac7361..848d18e 100644 --- a/lib/jsonpath/version.rb +++ b/lib/jsonpath/version.rb @@ -1,3 +1,3 @@ class JsonPath - VERSION = '0.7.2'.freeze + VERSION = '0.8.2'.freeze end diff --git a/test/test_jsonpath.rb b/test/test_jsonpath.rb index 7f9ad0c..09e717a 100644 --- a/test/test_jsonpath.rb +++ b/test/test_jsonpath.rb @@ -80,8 +80,8 @@ def test_eval_with_floating_point assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23.0 && @['price'] > 9.0)]").on(@object) end - def test_no_eval - assert_equal [], JsonPath.new('$..book[(@.length-2)]', allow_eval: false).on(@object) + def test_eval_with_floating_point + assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] == 13.0)]").on(@object) end def test_paths_with_underscores @@ -109,7 +109,7 @@ def test_use_first end def test_counting - assert_equal 54, JsonPath.new('$..*').on(@object).to_a.size + assert_equal 57, JsonPath.new('$..*').on(@object).to_a.size end def test_space_in_path @@ -255,6 +255,10 @@ def test_support_underscore_in_member_names JsonPath.new("$.store._links").on(@object) end + # def test_filter_support_include + # #assert_equal true, JsonPath.new("$.store.book[(@.tags == 'asdf3')]").on(@object) + # assert_equal true, JsonPath.new("$.store.book..tags[?(@ == 'asdf')]").on(@object) + # end def example_object { 'store' => { @@ -262,7 +266,8 @@ def example_object { 'category' => 'reference', 'author' => 'Nigel Rees', 'title' => 'Sayings of the Century', - 'price' => 9 }, + 'price' => 9, + 'tags' => ['asdf', 'asdf2']}, { 'category' => 'fiction', 'author' => 'Evelyn Waugh', 'title' => 'Sword of Honour',