require'hashie/hash'moduleHashie# Mash allows you to create pseudo-objects that have method-like# accessors for hash keys. This is useful for such implementations# as an API-accessing library that wants to fake robust objects# without the overhead of actually doing so. Think of it as OpenStruct# with some additional goodies.## A Mash will look at the methods you pass it and perform operations# based on the following rules:## * No punctuation: Returns the value of the hash for that key, or nil if none exists.# * Assignment (<tt>=</tt>): Sets the attribute of the given method name.# * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.# * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.## == Basic Example## mash = Mash.new# mash.name? # => false# mash.name = "Bob"# mash.name # => "Bob"# mash.name? # => true## == Hash Conversion Example## hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}# mash = Mash.new(hash)# mash.a.b # => 23# mash.a.d.e # => "abc"# mash.f.first.g # => 44# mash.f.last # => 12## == Bang Example## mash = Mash.new# mash.author # => nil# mash.author! # => <Mash>## mash = Mash.new# mash.author!.name = "Michael Bleigh"# mash.author # => <Mash name="Michael Bleigh">#classMash<Hashie::HashincludeHashie::PrettyInspectalias_method:to_s,:inspect# If you pass in an existing hash, it will# convert it to a Mash including recursively# descending into arrays and hashes, converting# them as well.definitialize(source_hash=nil,default=nil,&blk)deep_update(source_hash)ifsource_hashdefault?super(default):super(&blk)endclass<<selfalias[]newdefsubkey_classselfendenddefid#:nodoc:key?("id")?self["id"]:superenddeftype#:nodoc:key?("type")?self["type"]:superendalias_method:regular_reader,:[]alias_method:regular_writer,:[]=# Retrieves an attribute set in the Mash. Will convert# any key passed in to a string before retrieving.def[](key)value=regular_reader(convert_key(key))yieldvalueifblock_given?valueend# Sets an attribute in the Mash. Key will be converted to# a string before it is set, and Hashes will be converted# into Mashes for nesting purposes.def[]=(key,value)#:nodoc:regular_writer(convert_key(key),convert_value(value))end# This is the bang method reader, it will return a new Mash# if there isn't a value already assigned to the key requested.definitializing_reader(key)ck=convert_key(key)regular_writer(ck,self.class.new)unlesskey?(ck)regular_reader(ck)enddefdelete(key)super(convert_key(key))endalias_method:regular_dup,:dup# Duplicates the current mash as a new mash.defdupself.class.new(self,self.default)enddefkey?(key)super(convert_key(key))endalias_method:has_key?,:key?alias_method:include?,:key?alias_method:member?,:key?# Performs a deep_update on a duplicate of the# current mash.defdeep_merge(other_hash)dup.deep_update(other_hash)endalias_method:merge,:deep_merge# Recursively merges this mash with the passed# in hash, merging each hash in the hierarchy.defdeep_update(other_hash)other_hash.each_pairdo|k,v|key=convert_key(k)ifregular_reader(key).is_a?(Mash)andv.is_a?(::Hash)regular_reader(key).deep_update(v)elseregular_writer(key,convert_value(v,true))endendselfendalias_method:deep_merge!,:deep_updatealias_method:update,:deep_updatealias_method:merge!,:update# Performs a shallow_update on a duplicate of the current mashdefshallow_merge(other_hash)dup.shallow_update(other_hash)end# Merges (non-recursively) the hash from the argument,# changing the receiving hashdefshallow_update(other_hash)other_hash.each_pairdo|k,v|regular_writer(convert_key(k),convert_value(v,true))endselfend# Will return true if the Mash has had a key# set in addition to normal respond_to? functionality.defrespond_to?(method_name,include_private=false)returntrueifkey?(method_name)superenddefmethod_missing(method_name,*args,&blk)returnself.[](method_name,&blk)ifkey?(method_name)match=method_name.to_s.match(/(.*?)([?=!]?)$/)casematch[2]when"="self[match[1]]=args.firstwhen"?"!!self[match[1]]when"!"initializing_reader(match[1])elsedefault(method_name,*args,&blk)endendprotecteddefconvert_key(key)#:nodoc:key.to_senddefconvert_value(val,duping=false)#:nodoc:casevalwhenself.classval.dupwhen::Hashval=val.dupifdupingself.class.subkey_class.new.merge(val)whenArrayval.collect{|e|convert_value(e)}elsevalendendendend