require 'bigdecimal' require 'bigdecimal/util' module DataMapper module Adapters module Sql # Coersion is a mixin that allows for coercing database values to Ruby Types. # # DESIGN: Probably should handle the opposite scenario here too. I believe that's # currently in DataMapper::Database, which is obviously not a very good spot for # it. module Coersion class CoersionError < StandardError end TRUE_ALIASES = ['true'.freeze, 'TRUE'.freeze, '1'.freeze, 1] FALSE_ALIASES = [nil, '0'.freeze, 0] def self.included(base) base.const_set('TRUE_ALIASES', TRUE_ALIASES.dup) base.const_set('FALSE_ALIASES', FALSE_ALIASES.dup) end def type_cast_boolean(raw_value) return nil if raw_value.nil? || (raw_value.respond_to?(:empty?) && raw_value.empty?) case raw_value when TrueClass, FalseClass then raw_value when *self::class::TRUE_ALIASES then true when *self::class::FALSE_ALIASES then false else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a boolean") end end def type_cast_string(raw_value) return nil if raw_value.blank? # type-cast values should be immutable for memory conservation raw_value end def type_cast_text(raw_value) return nil if raw_value.blank? # type-cast values should be immutable for memory conservation raw_value end def type_cast_class(raw_value) return nil if raw_value.blank? Object::recursive_const_get(raw_value) end def type_cast_integer(raw_value) return nil if raw_value.blank? raw_value.to_i rescue ArgumentError nil end def type_cast_decimal(raw_value) return nil if raw_value.blank? raw_value.to_d rescue ArgumentError nil end def type_cast_float(raw_value) return nil if raw_value.blank? case raw_value when Float then raw_value when Numeric, String then raw_value.to_f else CoersionError.new("Can't type-cast #{raw_value.inspect} to a float") end end def type_cast_datetime(raw_value) return nil if raw_value.blank? case raw_value when "0000-00-00 00:00:00" then nil when DateTime then raw_value when Date then DateTime.new(raw_value) rescue nil when String then DateTime::parse(raw_value) rescue nil else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a datetime") end end def type_cast_date(raw_value) return nil if raw_value.blank? case raw_value when Date then raw_value when DateTime, Time then Date::civil(raw_value.year, raw_value.month, raw_value.day) when String then Date::parse(raw_value) else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a date") end end def type_cast_object(raw_value) return nil if raw_value.blank? begin YAML.load(raw_value) rescue raise CoersionError.new("Can't type-cast #{raw_value.inspect} to an object") end end def type_cast_value(type, raw_value) return nil if raw_value.blank? case type when :string then type_cast_string(raw_value) when :text then type_cast_text(raw_value) when :boolean then type_cast_boolean(raw_value) when :class then type_cast_class(raw_value) when :integer then type_cast_integer(raw_value) when :decimal then type_cast_decimal(raw_value) when :float then type_cast_float(raw_value) when :datetime then type_cast_datetime(raw_value) when :date then type_cast_date(raw_value) when :object then type_cast_object(raw_value) else if respond_to?("type_cast_#{type}") send("type_cast_#{type}", raw_value) else raise "Don't know how to type-cast #{{ type => raw_value }.inspect }" end end end end # module Coersion end end end