Adds first extension, coercion, road to 2.0 begins.
This commit is contained in:
parent
1a27b990cf
commit
f7b538fe28
|
@ -7,6 +7,7 @@ GEM
|
|||
remote: http://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.1.2)
|
||||
growl (1.0.3)
|
||||
guard (0.5.1)
|
||||
thor (~> 0.14.6)
|
||||
guard-rspec (0.4.0)
|
||||
|
@ -27,6 +28,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
growl
|
||||
guard
|
||||
guard-rspec
|
||||
hashie!
|
||||
|
|
|
@ -18,4 +18,5 @@ Gem::Specification.new do |gem|
|
|||
gem.add_development_dependency 'rspec', '~> 2.5'
|
||||
gem.add_development_dependency 'guard'
|
||||
gem.add_development_dependency 'guard-rspec'
|
||||
gem.add_development_dependency 'growl'
|
||||
end
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
module Hashie
|
||||
autoload :Clash, 'hashie/clash'
|
||||
autoload :Dash, 'hashie/dash'
|
||||
autoload :Hash, 'hashie/hash'
|
||||
autoload :HashExtensions, 'hashie/hash_extensions'
|
||||
autoload :PrettyInspect, 'hashie/hash_extensions'
|
||||
autoload :Hash, 'hashie/hash'
|
||||
autoload :Trash, 'hashie/trash'
|
||||
autoload :Mash, 'hashie/mash'
|
||||
autoload :Dash, 'hashie/dash'
|
||||
autoload :Clash, 'hashie/clash'
|
||||
autoload :Mash, 'hashie/mash'
|
||||
autoload :PrettyInspect, 'hashie/hash_extensions'
|
||||
autoload :Trash, 'hashie/trash'
|
||||
|
||||
module Extensions
|
||||
autoload :Coercion, 'hashie/extensions/coercion'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
module Hashie
|
||||
module Extensions
|
||||
module Coercion
|
||||
def self.included(base)
|
||||
base.send :extend, ClassMethods
|
||||
base.send :include, InstanceMethods
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def []=(key, value)
|
||||
into = self.class.key_coercion(key) || self.class.value_coercion(value)
|
||||
|
||||
if value && into
|
||||
if into.respond_to?(:coerce)
|
||||
value = into.coerce(value)
|
||||
else
|
||||
value = into.new(value)
|
||||
end
|
||||
end
|
||||
|
||||
super(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# 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 you would like to be coerced.
|
||||
# @param [Class] into the class into which you want the key coerced.
|
||||
#
|
||||
# @example Coerce a "user" subhash into a User object
|
||||
# class Tweet < Hash
|
||||
# include Hashie::Extensions::Coercion
|
||||
# coerce_key :user, User
|
||||
# end
|
||||
def coerce_key(key, into)
|
||||
(@key_coercions ||= {})[key] = into
|
||||
end
|
||||
|
||||
# Returns a hash of any existing key coercions.
|
||||
def key_coercions
|
||||
@key_coercions || {}
|
||||
end
|
||||
|
||||
# Returns the specific key coercion for the specified key,
|
||||
# if one exists.
|
||||
def key_coercion(key)
|
||||
key_coercions[key]
|
||||
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
|
||||
# end
|
||||
def coerce_value(from, into, options = {})
|
||||
options = {:strict => true}.merge(options)
|
||||
|
||||
if options[:strict]
|
||||
(@strict_value_coercions ||= {})[from] = into
|
||||
else
|
||||
while from.superclass && from.superclass != Object
|
||||
(@lenient_value_coercions ||= {})[from] = into
|
||||
from = from.superclass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return all value coercions that have the :strict rule as true.
|
||||
def strict_value_coercions; @strict_value_coercions || {} end
|
||||
# Return all value coercions that have the :strict rule as false.
|
||||
def lenient_value_coercions; @value_coercions || {} end
|
||||
|
||||
# Fetch the value coercion, if any, for the specified object.
|
||||
def value_coercion(value)
|
||||
from = value.class
|
||||
strict_value_coercions[from] || lenient_value_coercions[from]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,3 @@
|
|||
module Hashie
|
||||
VERSION = '1.1.0'
|
||||
VERSION = '2.0.0.beta'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Hashie::Extensions::Coercion do
|
||||
class Initializable
|
||||
def initialize(obj, coerced = false)
|
||||
@coerced = coerced
|
||||
@value = obj.class.to_s
|
||||
end
|
||||
def coerced?; @coerced end
|
||||
attr_reader :value
|
||||
end
|
||||
|
||||
class Coercable < Initializable
|
||||
def self.coerce(obj)
|
||||
new(obj, true)
|
||||
end
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
class ExampleCoercableHash < Hash; include Hashie::Extensions::Coercion end
|
||||
end
|
||||
subject { ExampleCoercableHash }
|
||||
let(:instance){ subject.new }
|
||||
|
||||
describe '.coerce_key' do
|
||||
it { subject.should be_respond_to(:coerce_key) }
|
||||
|
||||
it 'should run through coerce on a specified key' do
|
||||
subject.coerce_key :foo, Coercable
|
||||
|
||||
instance[:foo] = "bar"
|
||||
instance[:foo].should be_coerced
|
||||
end
|
||||
|
||||
it 'should just call #new if no coerce method is available' do
|
||||
subject.coerce_key :foo, Initializable
|
||||
|
||||
instance[:foo] = "bar"
|
||||
instance[:foo].value.should == "String"
|
||||
instance[:foo].should_not be_coerced
|
||||
end
|
||||
end
|
||||
|
||||
describe '.coerce_value' do
|
||||
context 'with :strict => true' do
|
||||
it 'should coerce any value of the exact right class' do
|
||||
subject.coerce_value String, Coercable
|
||||
|
||||
instance[:foo] = "bar"
|
||||
instance[:bar] = "bax"
|
||||
instance[:foo].should be_coerced
|
||||
instance[:bar].should be_coerced
|
||||
end
|
||||
|
||||
it 'should not coerce superclasses' do
|
||||
klass = Class.new(String)
|
||||
subject.coerce_value klass, Coercable
|
||||
|
||||
instance[:foo] = "bar"
|
||||
instance[:foo].should_not be_kind_of(Coercable)
|
||||
instance[:foo] = klass.new
|
||||
instance[:foo].should be_kind_of(Coercable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
Object.send(:remove_const, :ExampleCoercableHash)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue