Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adds first extension, coercion, road to 2.0 begins.

  • Loading branch information...
commit f7b538fe288e41494d784f6080a6fa4487594065 1 parent 1a27b99
@mbleigh mbleigh authored
View
2  Gemfile.lock
@@ -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!
View
1  hashie.gemspec
@@ -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
View
16 lib/hashie.rb
@@ -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
View
101 lib/hashie/extensions/coercion.rb
@@ -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
View
2  lib/hashie/version.rb
@@ -1,3 +1,3 @@
module Hashie
- VERSION = '1.1.0'
+ VERSION = '2.0.0.beta'
end
View
70 spec/hashie/extensions/coercion_spec.rb
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.