diff options
author | Kir Shatrov <shatrov@me.com> | 2015-09-07 21:20:15 +0300 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2015-09-21 10:12:13 -0600 |
commit | 9cc8c6f3730df3d94c81a55be9ee1b7b4ffd29f6 (patch) | |
tree | 68a48d84f3d2e1902d94316f781c4aee60a5f798 /activemodel/lib/active_model/type/helpers | |
parent | b223d729d823dcf3c6915daa2bf48c028718c465 (diff) | |
download | rails-9cc8c6f3730df3d94c81a55be9ee1b7b4ffd29f6.tar.gz rails-9cc8c6f3730df3d94c81a55be9ee1b7b4ffd29f6.tar.bz2 rails-9cc8c6f3730df3d94c81a55be9ee1b7b4ffd29f6.zip |
Move ActiveRecord::Type to ActiveModel
The first step of bringing typecasting to ActiveModel
Diffstat (limited to 'activemodel/lib/active_model/type/helpers')
4 files changed, 148 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb new file mode 100644 index 0000000000..fa1ccd057e --- /dev/null +++ b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb @@ -0,0 +1,27 @@ +module ActiveModel + module Type + module Helpers + class AcceptsMultiparameterTime < Module # :nodoc: + def initialize(defaults: {}) + define_method(:cast) do |value| + if value.is_a?(Hash) + value_from_multiparameter_assignment(value) + else + super(value) + end + end + + define_method(:value_from_multiparameter_assignment) do |values_hash| + defaults.each do |k, v| + values_hash[k] ||= v + end + return unless values_hash[1] && values_hash[2] && values_hash[3] + values = values_hash.sort.map(&:last) + ::Time.send(default_timezone, *values) + end + private :value_from_multiparameter_assignment + end + end + end + end +end diff --git a/activemodel/lib/active_model/type/helpers/mutable.rb b/activemodel/lib/active_model/type/helpers/mutable.rb new file mode 100644 index 0000000000..4dddbe4e5e --- /dev/null +++ b/activemodel/lib/active_model/type/helpers/mutable.rb @@ -0,0 +1,18 @@ +module ActiveModel + module Type + module Helpers + module Mutable # :nodoc: + def cast(value) + deserialize(serialize(value)) + end + + # +raw_old_value+ will be the `_before_type_cast` version of the + # value (likely a string). +new_value+ will be the current, type + # cast value. + def changed_in_place?(raw_old_value, new_value) + raw_old_value != serialize(new_value) + end + end + end + end +end diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb new file mode 100644 index 0000000000..c883010506 --- /dev/null +++ b/activemodel/lib/active_model/type/helpers/numeric.rb @@ -0,0 +1,34 @@ +module ActiveModel + module Type + module Helpers + module Numeric # :nodoc: + def cast(value) + value = case value + when true then 1 + when false then 0 + when ::String then value.presence + else value + end + super(value) + end + + def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc: + super || number_to_non_number?(old_value, new_value_before_type_cast) + end + + private + + def number_to_non_number?(old_value, new_value_before_type_cast) + old_value != nil && non_numeric_string?(new_value_before_type_cast) + end + + def non_numeric_string?(value) + # 'wibble'.to_i will give zero, we want to make sure + # that we aren't marking int zero to string zero as + # changed. + value.to_s !~ /\A-?\d+\.?\d*\z/ + end + end + end + end +end diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb new file mode 100644 index 0000000000..5a899d43ec --- /dev/null +++ b/activemodel/lib/active_model/type/helpers/time_value.rb @@ -0,0 +1,69 @@ +module ActiveModel + module Type + module Helpers + module TimeValue # :nodoc: + def serialize(value) + if precision && value.respond_to?(:usec) + number_of_insignificant_digits = 6 - precision + round_power = 10 ** number_of_insignificant_digits + value = value.change(usec: value.usec / round_power * round_power) + end + + if value.acts_like?(:time) + zone_conversion_method = is_utc? ? :getutc : :getlocal + + if value.respond_to?(zone_conversion_method) + value = value.send(zone_conversion_method) + end + end + + value + end + + def is_utc? + ::Time.zone_default =~ 'UTC' + end + + def default_timezone + ::Time.zone_default + end + + def type_cast_for_schema(value) + "'#{value.to_s(:db)}'" + end + + def user_input_in_time_zone(value) + value.in_time_zone + end + + private + + def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) + # Treat 0000-00-00 00:00:00 as nil. + return if year.nil? || (year == 0 && mon == 0 && mday == 0) + + + if offset + time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil + return unless time + + time -= offset + is_utc? ? time : time.getlocal + else + ::Time.public_send(default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil + end + end + + ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ + + # Doesn't handle time zones. + def fast_string_to_time(string) + if string =~ ISO_DATETIME + microsec = ($7.to_r * 1_000_000).to_i + new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec + end + end + end + end + end +end |