require 'rubygems'
require 'facet/hash/stringify_keys'
require 'facet/symbol/to_proc'
require 'pp'

class Hash
def recursively(&block)
yield inject({}) { |hash, (key, value)|
if value.is_a?(Hash)
hash[key] = value.recursively(&block)
else
hash[key] = value
end
hash
}
end

def recursively!(&block)
replace(recursively(&block))
end

def old_recursively(&block)
inject({}) do |hash, (key, value)|
if value.is_a?(Hash)
hash[key] = value.old_recursively(&block)
else
hash[key] = value
end
yield hash
end
end
end

class Array
def recursively(&block)
map do |item|
if item.is_a?(self.class)
item.recursively(&block)
else
yield item
end
end
end

def recursively!(&block)
replace(recursively(&block))
end

def new_recursively(&block)
yield map { |item|
if item.is_a?(self.class)
item.new_recursively(&block)
else
item
end
}
end
end

# Array Example

nested = [ 'a', 'b', [ 'c', 'd', [ 'e', 'f' ] ], 'g', [ 'h' ] ]

puts "=== New Array#recursively"
nested.new_recursively do |x|
pp x
end
puts "=== End"

puts "=== New nested.recursively(&:upcase)"
# this doesn't work
begin
pp nested.new_recursively(&:upcase)
rescue => e
puts e
end
puts "=== End"

puts "=== Old Array#recursively"
nested.recursively do |x|
pp x
end
puts "=== End"

puts "=== Old nested.recursively(&:upcase)"
pp nested.recursively(&:upcase)
puts "=== End"

# Hash Example

hash = {
:a => 1,
:b => 2,
:c => {
:ca => 1,
:cb => 2,
:cc => 3,
:cd => {
:cda => 1,
:cdb => 2
},
:ce => 5
},
:d => {
:da => 1,
:db => 2
}
}

puts "=== New Hash#recursively"
hash.recursively do |x|
pp x
end
puts "=== End"

puts "=== New hash.recursively(&:stringify_keys)"
pp hash.recursively(&:stringify_keys)
puts "=== End"

puts "=== Old Hash#recursively"
# much more chaotic
hash.old_recursively do |x|
pp x
x # you have to return this for some reason
end
puts "=== End"

puts "=== Old hash.recursively(&:stringify_keys)"
pp hash.old_recursively(&:stringify_keys)
puts "=== End"