moduleHashieclassCoercionError<StandardError;endmoduleExtensionsmoduleCoercionCORE_TYPES={Integer=>:to_i,Float=>:to_f,Complex=>:to_c,Rational=>:to_r,String=>:to_s,Symbol=>:to_sym}ABSTRACT_CORE_TYPES={Integer=>[Fixnum,Bignum],Numeric=>[Fixnum,Bignum,Float,Complex,Rational]}defself.included(base)base.send:include,InstanceMethodsbase.extendClassMethods# NOTE: we wanna make sure we first define set_value_with_coercion before extendingbase.send:alias_method,:set_value_without_coercion,:[]=unlessbase.method_defined?(:set_value_without_coercion)base.send:alias_method,:[]=,:set_value_with_coercionendmoduleInstanceMethodsdefset_value_with_coercion(key,value)into=self.class.key_coercion(key)||self.class.value_coercion(value)returnset_value_without_coercion(key,value)ifvalue.nil?||into.nil?beginreturnset_value_without_coercion(key,coerce_or_init(into).call(value))unlessinto.is_a?(Enumerable)ifinto.class<=::Hashkey_coerce=coerce_or_init(into.flatten[0])value_coerce=coerce_or_init(into.flatten[-1])value=into.class[value.map{|k,v|[key_coerce.call(k),value_coerce.call(v)]}]else# Enumerable but not Hash: Array, Setvalue_coerce=coerce_or_init(into.first)value=into.class.new(value.map{|v|value_coerce.call(v)})endrescueNoMethodError,TypeError=>eraiseCoercionError,"Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{e.message}"endset_value_without_coercion(key,value)enddefcoerce_or_init(type)returntypeiftype.is_a?ProcifCORE_TYPES.key?(type)lambdado|v|returnvifv.is_a?typereturnv.send(CORE_TYPES[type])endelsiftype.respond_to?(:coerce)lambdado|v|returnvifv.is_a?typetype.coerce(v)endelsiftype.respond_to?(:new)lambdado|v|returnvifv.is_a?typetype.new(v)endelsefailTypeError,"#{type} is not a coercable type"endendprivate:coerce_or_initdefcustom_writer(key,value,_convert=true)self[key]=valueenddefreplace(other_hash)(keys-other_hash.keys).each{|key|delete(key)}other_hash.each{|key,value|self[key]=value}selfendendmoduleClassMethods# Set up a coercion rule such that any time the specified# key is set it will be coerced into the specified class.# Coercion will occur by first attempting to call Class.coerce# and then by calling Class.new with the value as an argument# in either case.## @param [Object] key the key or array of keys you would like to be coerced.# @param [Class] into the class into which you want the key(s) coerced.## @example Coerce a "user" subhash into a User object# class Tweet < Hash# include Hashie::Extensions::Coercion# coerce_key :user, User# enddefcoerce_key(*attrs)@key_coercions||={}into=attrs.popattrs.each{|key|@key_coercions[key]=into}endalias_method:coerce_keys,:coerce_key# Returns a hash of any existing key coercions.defkey_coercions@key_coercions||{}end# Returns the specific key coercion for the specified key,# if one exists.defkey_coercion(key)key_coercions[key.to_sym]end# Set up a coercion rule such that any time a value of the# specified type is set it will be coerced into the specified# class.## @param [Class] from the type you would like coerced.# @param [Class] into the class into which you would like the value coerced.# @option options [Boolean] :strict (true) whether use exact source class only or include ancestors## @example Coerce all hashes into this special type of hash# class SpecialHash < Hash# include Hashie::Extensions::Coercion# coerce_value Hash, SpecialHash## def initialize(hash = {})# super# hash.each_pair do |k,v|# self[k] = v# end# end# enddefcoerce_value(from,into,options={})options={strict: true}.merge(options)ifABSTRACT_CORE_TYPES.key?fromABSTRACT_CORE_TYPES[from].eachdo|type|coerce_valuetype,into,optionsendendifoptions[:strict](@strict_value_coercions||={})[from]=intoelsewhilefrom.superclass&&from.superclass!=Object(@lenient_value_coercions||={})[from]=intofrom=from.superclassendendend# Return all value coercions that have the :strict rule as true.defstrict_value_coercions@strict_value_coercions||{}end# Return all value coercions that have the :strict rule as false.deflenient_value_coercions@value_coercions||{}end# Fetch the value coercion, if any, for the specified object.defvalue_coercion(value)from=value.classstrict_value_coercions[from]||lenient_value_coercions[from]endendendendend