Skip to content

Instantly share code, notes, and snippets.

@nileshtrivedi
Forked from brentd/gist:360506
Created November 24, 2011 17:49
Show Gist options
  • Save nileshtrivedi/1391902 to your computer and use it in GitHub Desktop.
Save nileshtrivedi/1391902 to your computer and use it in GitHub Desktop.
HashBuilder allows you to build a Hash in Ruby similar to Builder with some enhancements
# Allows you to build a Hash in a fashion very similar to Builder. Example:
# Fork of https://gist.github.com/360506 by BrentD with some enhancements
#
# HashBuilder.build! do |h|
# h.name "Nilesh"
# h.skill "Ruby"
# h.skill "Rails" # multiple calls of the same method will collect the values in an array
# h.location "Udaipur, India" do # If a block is given, first argument will be set as value for :name
# h.location do
# h.longitude 24.57
# h.latitude 73.69
# end
# end
#
# produces:
#
# {:name=>"Nilesh", :skill=>["Ruby", "Rails"], :location=>{:name=>"Udaipur, India", :location=>{:longitude=>24.57, :latitude=>73.69}}}
class HashBuilder
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^object_id$)/ }
def initialize
@hash = {}
@target = @hash
end
def self.build!
builder = HashBuilder.new
yield builder
builder.to_h
end
def to_h
@hash
end
def inspect
to_h.inspect
end
def attr!(key, value=nil)
if block_given?
parent = @target
@target = {}
@target[:name] = value if value
yield # the block may not be able to override "name" because Class#name exists but takes no argument
parent[key] = @target
@target = parent
else
if @target.keys.include?(key)
@target[key] = [@target[key]].flatten + [value]
else
@target[key] = value
end
end
@hash
end
def method_missing(key, *args, &block)
attr!(key, args.first, &block) # If block_given?, first argument will be taken as the value of :name. Others will be discarded.
end
end
class Hash
unless method_defined?(:build!)
def build!(&block)
::HashBuilder.build!(&block)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment