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=ifRubyVersion.new(RUBY_VERSION)>=RubyVersion.new('2.4.0'){Numeric=>[Integer,Float,Complex,Rational]}else{Integer=>[Fixnum,Bignum],Numeric=>[Fixnum,Bignum,Float,Complex,Rational]}enddefself.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)unlessvalue.nil?||into.nil?beginvalue=self.class.fetch_coercion(into).call(value)rescueNoMethodError,TypeError=>eraiseCoercionError,"Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{e.message}"endendset_value_without_coercion(key,value)enddefcustom_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}selfendendmoduleClassMethodsattr_writer:key_coercionsprotected:key_coercions=# 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)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!=Objectlenient_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@lenient_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]enddeffetch_coercion(type)returntypeiftype.is_a?Proccoercion_cache[type]enddefcoercion_cache@coercion_cache||=::Hash.newdo|hash,type|hash[type]=build_coercion(type)endenddefbuild_coercion(type)iftype.is_a?Enumerableiftype.class<=::Hashtype,key_type,value_type=type.class,*type.firstbuild_hash_coercion(type,key_type,value_type)else# Enumerable but not Hash: Array, Setvalue_type=type.firsttype=type.classbuild_container_coercion(type,value_type)endelsifCORE_TYPES.key?typebuild_core_type_coercion(type)elsiftype.respond_to?:coercelambdado|value|returnvalueifvalue.is_a?typetype.coerce(value)endelsiftype.respond_to?:newlambdado|value|returnvalueifvalue.is_a?typetype.new(value)endelsefailTypeError,"#{type} is not a coercable type"endenddefbuild_hash_coercion(type,key_type,value_type)key_coerce=fetch_coercion(key_type)value_coerce=fetch_coercion(value_type)lambdado|value|type[value.map{|k,v|[key_coerce.call(k),value_coerce.call(v)]}]endenddefbuild_container_coercion(type,value_type)value_coerce=fetch_coercion(value_type)lambdado|value|type.new(value.map{|v|value_coerce.call(v)})endenddefbuild_core_type_coercion(type)name=CORE_TYPES[type]lambdado|value|returnvalueifvalue.is_a?typereturnvalue.send(name)endenddefinherited(klass)superklass.key_coercions=key_coercions.dupendendendendend