From 9fa1d29e4feafb4dd712082cf8a27918216740b0 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 4 Jan 2022 02:36:49 -0800 Subject: [PATCH] duck-type values responding to #to_hash or #to_ary rather than checking class --- lib/jsonpath/dig.rb | 16 +++++++--------- lib/jsonpath/enumerable.rb | 26 +++++++++++++------------- lib/jsonpath/parser.rb | 4 ++-- lib/jsonpath/proxy.rb | 2 +- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/jsonpath/dig.rb b/lib/jsonpath/dig.rb index 7a130042..0a25b96f 100644 --- a/lib/jsonpath/dig.rb +++ b/lib/jsonpath/dig.rb @@ -20,11 +20,10 @@ def dig_as_hash(context, keys) # Dig the value of k on context. def dig_one(context, k) - case context - when Hash - context[@options[:use_symbols] ? k.to_sym : k] - when Array - context[k.to_i] + if context.respond_to?(:to_hash) + context.to_hash[@options[:use_symbols] ? k.to_sym : k] + elsif context.respond_to?(:to_ary) + context.to_ary[k.to_i] else if context.respond_to?(:dig) context.dig(k) @@ -37,12 +36,11 @@ def dig_one(context, k) # Yields the block if context has a diggable # value for k def yield_if_diggable(context, k, &blk) - case context - when Array + if context.respond_to?(:to_ary) nil - when Hash + elsif context.respond_to?(:to_hash) k = @options[:use_symbols] ? k.to_sym : k - return yield if context.key?(k) || @options[:default_path_leaf_to_null] + return yield if context.to_hash.key?(k) || @options[:default_path_leaf_to_null] else if context.respond_to?(:dig) digged = dig_one(context, k) diff --git a/lib/jsonpath/enumerable.rb b/lib/jsonpath/enumerable.rb index 7a6f2709..76e0b765 100644 --- a/lib/jsonpath/enumerable.rb +++ b/lib/jsonpath/enumerable.rb @@ -31,9 +31,10 @@ def each(context = @object, key = nil, pos = 0, &blk) end if pos > 0 && @path[pos - 1] == '..' || (@path[pos - 1] == '*' && @path[pos] != '..') - case node - when Hash then node.each { |k, _| each(node, k, pos, &blk) } - when Array then node.each_with_index { |_, i| each(node, i, pos, &blk) } + if node.respond_to?(:to_hash) + node.to_hash.each { |k, _| each(node, k, pos, &blk) } + elsif node.respond_to?(:to_ary) + node.to_ary.each_with_index { |_, i| each(node, i, pos, &blk) } end end end @@ -41,11 +42,10 @@ def each(context = @object, key = nil, pos = 0, &blk) private def filter_context(context, keys) - case context - when Hash + if context.respond_to?(:to_hash) dig_as_hash(context, keys) - when Array - context.each_with_object([]) do |c, memo| + elsif context.respond_to?(:to_ary) + context.to_ary.each_with_object([]) do |c, memo| memo << dig_as_hash(c, keys) end end @@ -62,7 +62,7 @@ def handle_wildecard(node, expr, _context, _key, pos, &blk) when '?' handle_question_mark(sub_path, node, pos, &blk) else - next if node.is_a?(Array) && node.empty? + next if node.respond_to?(:to_ary) && node.empty? next if node.nil? # when default_path_leaf_to_null is true array_args = sub_path.split(':') @@ -103,15 +103,15 @@ def ensure_exclusive_end_index(value) end def handle_question_mark(sub_path, node, pos, &blk) - case node - when Array - node.size.times do |index| - @_current_node = node[index] + if node.respond_to?(:to_ary) + node_ary = node.to_ary + node_ary.size.times do |index| + @_current_node = node_ary[index] if process_function_or_literal(sub_path[1, sub_path.size - 1]) each(@_current_node, nil, pos + 1, &blk) end end - when Hash + elsif node.respond_to?(:to_hash) if process_function_or_literal(sub_path[1, sub_path.size - 1]) each(@_current_node, nil, pos + 1, &blk) end diff --git a/lib/jsonpath/parser.rb b/lib/jsonpath/parser.rb index 79feb8c9..475f53f6 100644 --- a/lib/jsonpath/parser.rb +++ b/lib/jsonpath/parser.rb @@ -58,7 +58,7 @@ def parse_exp(exp) next if i.empty? index = Integer(i[0]) - raise ArgumentError, 'Node does not appear to be an array.' unless @_current_node.is_a?(Array) + raise ArgumentError, 'Node does not appear to be an array.' unless @_current_node.respond_to?(:to_ary) raise ArgumentError, "Index out of bounds for nested array. Index: #{index}" if @_current_node.size < index @_current_node = @_current_node[index] @@ -93,7 +93,7 @@ def parse_exp(exp) el = if elements.empty? @_current_node - elsif @_current_node.is_a?(Hash) + elsif @_current_node.respond_to?(:to_hash) dig(@_current_node, *elements) else elements.inject(@_current_node, &:__send__) diff --git a/lib/jsonpath/proxy.rb b/lib/jsonpath/proxy.rb index 3d11a8b3..8bc34b4b 100644 --- a/lib/jsonpath/proxy.rb +++ b/lib/jsonpath/proxy.rb @@ -52,7 +52,7 @@ def _delete(obj, path) def _remove(obj) obj.each do |o| - if o.is_a?(Hash) || o.is_a?(Array) + if o.respond_to?(:to_hash) || o.respond_to?(:to_ary) _remove(o) o.delete({}) end