summaryrefslogtreecommitdiff
path: root/lib/hashie/extensions/coercion.rb
diff options
context:
space:
mode:
authorMichael Bleigh <michael@intridea.com>2011-07-28 23:44:50 -0500
committerMichael Bleigh <michael@intridea.com>2011-07-28 23:44:50 -0500
commitf7b538fe288e41494d784f6080a6fa4487594065 (patch)
tree2c635b491710487759766f925cb6fdc5c0496185 /lib/hashie/extensions/coercion.rb
parent1a27b990cfa891dd72dd8c9855473eb0ef7c910b (diff)
downloadhashie-f7b538fe288e41494d784f6080a6fa4487594065.tar.gz
Adds first extension, coercion, road to 2.0 begins.
Diffstat (limited to 'lib/hashie/extensions/coercion.rb')
-rw-r--r--lib/hashie/extensions/coercion.rb101
1 files changed, 101 insertions, 0 deletions
diff --git a/lib/hashie/extensions/coercion.rb b/lib/hashie/extensions/coercion.rb
new file mode 100644
index 0000000..848abe1
--- /dev/null
+++ b/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