aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/attributes/typecasting.rb
diff options
context:
space:
mode:
authorEric Chapweske <eric@chapweske.com>2009-10-17 12:37:15 -0500
committerJoshua Peek <josh@joshpeek.com>2009-10-17 12:37:15 -0500
commitf936a1f100e75082081e782e5cceb272885c2df7 (patch)
tree6c5091faa38f15765b3be153141b81d693b02d18 /activerecord/lib/active_record/attributes/typecasting.rb
parente13d232150921cdf0ec3d713caefa628d235152e (diff)
downloadrails-f936a1f100e75082081e782e5cceb272885c2df7.tar.gz
rails-f936a1f100e75082081e782e5cceb272885c2df7.tar.bz2
rails-f936a1f100e75082081e782e5cceb272885c2df7.zip
Refactoring attributes/types [#3348 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
Diffstat (limited to 'activerecord/lib/active_record/attributes/typecasting.rb')
-rw-r--r--activerecord/lib/active_record/attributes/typecasting.rb111
1 files changed, 111 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/attributes/typecasting.rb b/activerecord/lib/active_record/attributes/typecasting.rb
new file mode 100644
index 0000000000..de36a297eb
--- /dev/null
+++ b/activerecord/lib/active_record/attributes/typecasting.rb
@@ -0,0 +1,111 @@
+module ActiveRecord
+ module Attributes
+ module Typecasting
+ # Typecasts values during access based on their key mapping to a Type.
+ #
+ # Example:
+ # class Attributes < Hash
+ # include Typecasting
+ # end
+ #
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ # attributes['comments_count'] = '5'
+ #
+ # attributes['comments_count']
+ # => 5
+ #
+ # To support keys not mapped to a typecaster, add a default to types.
+ # attributes.types.default = Type::Unknown
+ # attributes['age'] = '25'
+ # attributes['age']
+ # => '25'
+ #
+ # A valid type supports #cast, #precast, #boolean, and #appendable? methods.
+ #
+ def [](key)
+ value = super(key)
+ typecast_read(key, value)
+ end
+
+ def []=(key, value)
+ super(key, typecast_write(key, value))
+ end
+
+ def to_h
+ hash = {}
+ hash.merge!(self)
+ hash
+ end
+
+ # Provides a duplicate with typecasting disabled.
+ #
+ # Example:
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ # attributes['comments_count'] = '5'
+ #
+ # attributes.without_typecast['comments_count']
+ # => '5'
+ #
+ def without_typecast
+ dup.without_typecast!
+ end
+
+ def without_typecast!
+ types.clear
+ self
+ end
+
+ def typecast!
+ keys.each { |key| self[key] = self[key] }
+ self
+ end
+
+ # Check if key has a value that typecasts to true.
+ #
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ #
+ # attributes['comments_count'] = 0
+ # attributes.has?('comments_count')
+ # => false
+ #
+ # attributes['comments_count'] = 1
+ # attributes.has?('comments_count')
+ # => true
+ #
+ def has?(key)
+ value = self[key]
+ boolean_typecast(key, value)
+ end
+
+ def types
+ @types ||= {}
+ end
+
+ protected
+
+ def types=(other_types)
+ @types = other_types
+ end
+
+ def boolean_typecast(key, value)
+ value ? types[key].boolean(value) : false
+ end
+
+ def typecast_read(key, value)
+ type = types[key]
+ value = type.cast(value)
+ self[key] = value if type.appendable? && !frozen?
+
+ value
+ end
+
+ def typecast_write(key, value)
+ types[key].precast(value)
+ end
+
+ end
+ end
+end