Skip to content

Instantly share code, notes, and snippets.

@chsh
Created May 29, 2011 16:29
Show Gist options
  • Save chsh/997918 to your computer and use it in GitHub Desktop.
Save chsh/997918 to your computer and use it in GitHub Desktop.
Add Hash#path method to lookup deep structured hash value.
# pathable_hash.rb
# Add Hash#path method to lookup deep stuructured hash value.
# e.g)
# h0 = {a: {b: {c: 1}}}
# h0.path '/a/b/c' => 1
# h0.path 'a/b/c' => 1 # heading slash has no meaning.
# h1 = {a: {b: [1,2,{c: 3}]}
# h1.path '/a/b[2]/c' => 3 # [\d] means picking up array item.
# path syntax is inspired by xpath, but not same. :-)
module GenericSupport
module Pathable
module ClassMethods
def path(hash, *pathes)
target = hash
pathes.map! do |p|
p.to_s.split(/[\/\.]+/)
end
pathes.flatten.each do |element|
next if (element == nil || element == '')
key, index = parse_element__(element)
target = target[key] || target[key.to_s]
return nil unless target
if index
raise "target=#{target.inspect} is not array. but specified index value." unless target.is_a?(Array)
target = target[index]
return nil unless target
end
end
target
end
private
def parse_element__(elm_string)
if elm_string =~ /^(.+)\[(\d+)\]$/
[$1.to_sym, $2.to_i]
elsif elm_string =~ /^(.+)_(\d+)$/
[$1.to_sym, $2.to_i]
else
[elm_string.to_sym, nil]
end
end
end
module InstanceMethods
def path(*pathes)
self.class.path(self, *pathes)
end
end
end
end
module GenericSupport
module Hash
def self.included(base)
base.send(:extend, GenericSupport::Pathable::ClassMethods)
base.send(:include, GenericSupport::Pathable::InstanceMethods)
end
end
end
Hash.send(:include, GenericSupport::Hash)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment