aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/type/helpers
diff options
context:
space:
mode:
authorKir Shatrov <shatrov@me.com>2015-09-07 21:20:15 +0300
committerSean Griffin <sean@seantheprogrammer.com>2015-09-21 10:12:13 -0600
commit9cc8c6f3730df3d94c81a55be9ee1b7b4ffd29f6 (patch)
tree68a48d84f3d2e1902d94316f781c4aee60a5f798 /activemodel/lib/active_model/type/helpers
parentb223d729d823dcf3c6915daa2bf48c028718c465 (diff)
downloadrails-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')
-rw-r--r--activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb27
-rw-r--r--activemodel/lib/active_model/type/helpers/mutable.rb18
-rw-r--r--activemodel/lib/active_model/type/helpers/numeric.rb34
-rw-r--r--activemodel/lib/active_model/type/helpers/time_value.rb69
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