From 728fa69839d2c3b839391f9077896c06df80bddf Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 27 May 2014 10:59:22 -0700 Subject: Move types to the top level `ActiveRecord` namespace `ActiveRecord::ConnectionAdapters::Type::Value` => `ActiveRecord::Type::Value` --- .../connection_adapters/abstract_adapter.rb | 2 +- .../connection_adapters/mysql_adapter.rb | 2 +- .../lib/active_record/connection_adapters/type.rb | 25 --------- .../connection_adapters/type/binary.rb | 19 ------- .../connection_adapters/type/boolean.rb | 21 -------- .../active_record/connection_adapters/type/date.rb | 44 ---------------- .../connection_adapters/type/date_time.rb | 35 ------------- .../connection_adapters/type/decimal.rb | 27 ---------- .../type/decimal_without_scale.rb | 13 ----- .../connection_adapters/type/float.rb | 25 --------- .../type/hash_lookup_type_map.rb | 21 -------- .../connection_adapters/type/integer.rb | 29 ---------- .../connection_adapters/type/numeric.rb | 20 ------- .../connection_adapters/type/string.rb | 29 ---------- .../active_record/connection_adapters/type/text.rb | 13 ----- .../active_record/connection_adapters/type/time.rb | 28 ---------- .../connection_adapters/type/time_value.rb | 36 ------------- .../connection_adapters/type/type_map.rb | 50 ------------------ .../connection_adapters/type/value.rb | 61 ---------------------- .../lib/active_record/nested_attributes.rb | 2 +- activerecord/lib/active_record/properties.rb | 2 +- activerecord/lib/active_record/type.rb | 18 +++++++ activerecord/lib/active_record/type/binary.rb | 17 ++++++ activerecord/lib/active_record/type/boolean.rb | 19 +++++++ activerecord/lib/active_record/type/date.rb | 42 +++++++++++++++ activerecord/lib/active_record/type/date_time.rb | 33 ++++++++++++ activerecord/lib/active_record/type/decimal.rb | 25 +++++++++ .../active_record/type/decimal_without_scale.rb | 11 ++++ activerecord/lib/active_record/type/float.rb | 23 ++++++++ .../lib/active_record/type/hash_lookup_type_map.rb | 19 +++++++ activerecord/lib/active_record/type/integer.rb | 27 ++++++++++ activerecord/lib/active_record/type/numeric.rb | 18 +++++++ activerecord/lib/active_record/type/string.rb | 27 ++++++++++ activerecord/lib/active_record/type/text.rb | 11 ++++ activerecord/lib/active_record/type/time.rb | 26 +++++++++ activerecord/lib/active_record/type/time_value.rb | 34 ++++++++++++ activerecord/lib/active_record/type/type_map.rb | 48 +++++++++++++++++ activerecord/lib/active_record/type/value.rb | 59 +++++++++++++++++++++ 38 files changed, 461 insertions(+), 500 deletions(-) delete mode 100644 activerecord/lib/active_record/connection_adapters/type.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/binary.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/boolean.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/date.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/date_time.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/decimal.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/float.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/integer.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/numeric.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/string.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/text.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/time.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/time_value.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/type_map.rb delete mode 100644 activerecord/lib/active_record/connection_adapters/type/value.rb create mode 100644 activerecord/lib/active_record/type.rb create mode 100644 activerecord/lib/active_record/type/binary.rb create mode 100644 activerecord/lib/active_record/type/boolean.rb create mode 100644 activerecord/lib/active_record/type/date.rb create mode 100644 activerecord/lib/active_record/type/date_time.rb create mode 100644 activerecord/lib/active_record/type/decimal.rb create mode 100644 activerecord/lib/active_record/type/decimal_without_scale.rb create mode 100644 activerecord/lib/active_record/type/float.rb create mode 100644 activerecord/lib/active_record/type/hash_lookup_type_map.rb create mode 100644 activerecord/lib/active_record/type/integer.rb create mode 100644 activerecord/lib/active_record/type/numeric.rb create mode 100644 activerecord/lib/active_record/type/string.rb create mode 100644 activerecord/lib/active_record/type/text.rb create mode 100644 activerecord/lib/active_record/type/time.rb create mode 100644 activerecord/lib/active_record/type/time_value.rb create mode 100644 activerecord/lib/active_record/type/type_map.rb create mode 100644 activerecord/lib/active_record/type/value.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 6ecd4efdc8..0dba0c7688 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -1,9 +1,9 @@ require 'date' require 'bigdecimal' require 'bigdecimal/util' +require 'active_record/type' require 'active_support/core_ext/benchmark' require 'active_record/connection_adapters/schema_cache' -require 'active_record/connection_adapters/type' require 'active_record/connection_adapters/abstract/schema_dumper' require 'active_record/connection_adapters/abstract/schema_creation' require 'monitor' diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index aa8a91ed39..909bba8c7d 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -298,7 +298,7 @@ module ActiveRecord end class << self - TYPES = ConnectionAdapters::Type::HashLookupTypeMap.new # :nodoc: + TYPES = Type::HashLookupTypeMap.new # :nodoc: delegate :register_type, :alias_type, to: :TYPES diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb deleted file mode 100644 index bab7a3ff7e..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'active_record/connection_adapters/type/numeric' -require 'active_record/connection_adapters/type/time_value' -require 'active_record/connection_adapters/type/value' - -require 'active_record/connection_adapters/type/binary' -require 'active_record/connection_adapters/type/boolean' -require 'active_record/connection_adapters/type/date' -require 'active_record/connection_adapters/type/date_time' -require 'active_record/connection_adapters/type/decimal' -require 'active_record/connection_adapters/type/decimal_without_scale' -require 'active_record/connection_adapters/type/float' -require 'active_record/connection_adapters/type/integer' -require 'active_record/connection_adapters/type/string' -require 'active_record/connection_adapters/type/text' -require 'active_record/connection_adapters/type/time' - -require 'active_record/connection_adapters/type/type_map' -require 'active_record/connection_adapters/type/hash_lookup_type_map' - -module ActiveRecord - module ConnectionAdapters - module Type # :nodoc: - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb deleted file mode 100644 index 4b2d1a66e0..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/binary.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Binary < Value # :nodoc: - def type - :binary - end - - def binary? - true - end - - def klass - ::String - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb deleted file mode 100644 index 2337bdd563..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/boolean.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Boolean < Value # :nodoc: - def type - :boolean - end - - private - - def cast_value(value) - if value == '' - nil - else - Column::TRUE_VALUES.include?(value) - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb deleted file mode 100644 index 1e7205fd0b..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/date.rb +++ /dev/null @@ -1,44 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Date < Value # :nodoc: - def type - :date - end - - def klass - ::Date - end - - private - - def cast_value(value) - if value.is_a?(::String) - return if value.empty? - fast_string_to_date(value) || fallback_string_to_date(value) - elsif value.respond_to?(:to_date) - value.to_date - else - value - end - end - - def fast_string_to_date(string) - if string =~ Column::Format::ISO_DATE - new_date $1.to_i, $2.to_i, $3.to_i - end - end - - def fallback_string_to_date(string) - new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday)) - end - - def new_date(year, mon, mday) - if year && year != 0 - ::Date.new(year, mon, mday) rescue nil - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb deleted file mode 100644 index c34f4c5a53..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/date_time.rb +++ /dev/null @@ -1,35 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class DateTime < Value # :nodoc: - include TimeValue - - def type - :datetime - end - - private - - def cast_value(string) - return string unless string.is_a?(::String) - return if string.empty? - - fast_string_to_time(string) || fallback_string_to_time(string) - end - - # '0.123456' -> 123456 - # '1.123456' -> 123456 - def microseconds(time) - time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0 - end - - def fallback_string_to_time(string) - time_hash = ::Date._parse(string) - time_hash[:sec_fraction] = microseconds(time_hash) - - new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)) - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb deleted file mode 100644 index ac5af4b963..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/decimal.rb +++ /dev/null @@ -1,27 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Decimal < Value # :nodoc: - include Numeric - - def type - :decimal - end - - def klass - ::BigDecimal - end - - private - - def cast_value(value) - if value.respond_to?(:to_d) - value.to_d - else - value.to_s.to_d - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb b/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb deleted file mode 100644 index e58c6e198d..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'active_record/connection_adapters/type/integer' - -module ActiveRecord - module ConnectionAdapters - module Type - class DecimalWithoutScale < Integer # :nodoc: - def type - :decimal - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb deleted file mode 100644 index 28111e9d8e..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/float.rb +++ /dev/null @@ -1,25 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Float < Value # :nodoc: - include Numeric - - def type - :float - end - - def klass - ::Float - end - - alias type_cast_for_database type_cast - - private - - def cast_value(value) - value.to_f - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb deleted file mode 100644 index bb1abc77ff..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class HashLookupTypeMap < TypeMap # :nodoc: - delegate :key?, to: :@mapping - - def lookup(type, *args) - @mapping.fetch(type, proc { default_value }).call(type, *args) - end - - def fetch(type, *args, &block) - @mapping.fetch(type, block).call(type, *args) - end - - def alias_type(type, alias_type) - register_type(type) { |_, *args| lookup(alias_type, *args) } - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb deleted file mode 100644 index 8e6a509b5b..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/integer.rb +++ /dev/null @@ -1,29 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Integer < Value # :nodoc: - include Numeric - - def type - :integer - end - - def klass - ::Fixnum - end - - alias type_cast_for_database type_cast - - private - - def cast_value(value) - case value - when true then 1 - when false then 0 - else value.to_i rescue nil - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/numeric.rb b/activerecord/lib/active_record/connection_adapters/type/numeric.rb deleted file mode 100644 index a3379831cb..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/numeric.rb +++ /dev/null @@ -1,20 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - module Numeric # :nodoc: - def number? - true - end - - def type_cast_for_write(value) - case value - when true then 1 - when false then 0 - when ::String then value.presence - else super - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb deleted file mode 100644 index 55f0e1ee1c..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/string.rb +++ /dev/null @@ -1,29 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class String < Value # :nodoc: - def type - :string - end - - def text? - true - end - - def klass - ::String - end - - private - - def cast_value(value) - case value - when true then "1" - when false then "0" - else value.to_s - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb deleted file mode 100644 index ee5842a3fc..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/text.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'active_record/connection_adapters/type/string' - -module ActiveRecord - module ConnectionAdapters - module Type - class Text < String # :nodoc: - def type - :text - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb deleted file mode 100644 index bc331b0fa7..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/time.rb +++ /dev/null @@ -1,28 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Time < Value - include TimeValue - - def type - :time - end - - private - - def cast_value(value) - return value unless value.is_a?(::String) - return if value.empty? - - dummy_time_value = "2000-01-01 #{value}" - - fast_string_to_time(dummy_time_value) || begin - time_hash = ::Date._parse(dummy_time_value) - return if time_hash[:hour].nil? - new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)) - end - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/time_value.rb b/activerecord/lib/active_record/connection_adapters/type/time_value.rb deleted file mode 100644 index e9ca4adeda..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/time_value.rb +++ /dev/null @@ -1,36 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - module TimeValue # :nodoc: - def klass - ::Time - 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 - Base.default_timezone == :utc ? time : time.getlocal - else - ::Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil - end - end - - # Doesn't handle time zones. - def fast_string_to_time(string) - if string =~ Column::Format::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 diff --git a/activerecord/lib/active_record/connection_adapters/type/type_map.rb b/activerecord/lib/active_record/connection_adapters/type/type_map.rb deleted file mode 100644 index 48b8b51417..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/type_map.rb +++ /dev/null @@ -1,50 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class TypeMap # :nodoc: - def initialize - @mapping = {} - end - - def lookup(lookup_key, *args) - matching_pair = @mapping.reverse_each.detect do |key, _| - key === lookup_key - end - - if matching_pair - matching_pair.last.call(lookup_key, *args) - else - default_value - end - end - - def register_type(key, value = nil, &block) - raise ::ArgumentError unless value || block - - if block - @mapping[key] = block - else - @mapping[key] = proc { value } - end - end - - def alias_type(key, target_key) - register_type(key) do |sql_type, *args| - metadata = sql_type[/\(.*\)/, 0] - lookup("#{target_key}#{metadata}", *args) - end - end - - def clear - @mapping.clear - end - - private - - def default_value - @default_value ||= Value.new - end - end - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb deleted file mode 100644 index 415ef0aee9..0000000000 --- a/activerecord/lib/active_record/connection_adapters/type/value.rb +++ /dev/null @@ -1,61 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Type - class Value # :nodoc: - attr_reader :precision, :scale, :limit - - # Valid options are +precision+, +scale+, and +limit+. - # They are only used when dumping schema. - def initialize(options = {}) - options.assert_valid_keys(:precision, :scale, :limit) - @precision = options[:precision] - @scale = options[:scale] - @limit = options[:limit] - end - - # The simplified that this object represents. Subclasses - # should override this method. - def type; end - - # Takes an input from the database, or from attribute setters, - # and casts it to a type appropriate for this object. This method - # should not be overriden by subclasses. Instead, override `cast_value`. - def type_cast(value) - cast_value(value) unless value.nil? - end - - def type_cast_for_write(value) - value - end - - def type_cast_for_database(value) - type_cast_for_write(value) - end - - def text? - false - end - - def number? - false - end - - def binary? - false - end - - def klass - ::Object - end - - private - - # Responsible for casting values from external sources to the appropriate - # type. Called by `type_cast` for all values except `nil`. - def cast_value(value) # :api: public - value - end - end - end - end -end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 7dc7169a02..1242f49e28 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -516,7 +516,7 @@ module ActiveRecord # Determines if a hash contains a truthy _destroy key. def has_destroy_flag?(hash) - ConnectionAdapters::Type::Boolean.new.type_cast(hash['_destroy']) + Type::Boolean.new.type_cast(hash['_destroy']) end # Determines if a new record should be rejected by checking diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb index a25c1cec58..417afe6109 100644 --- a/activerecord/lib/active_record/properties.rb +++ b/activerecord/lib/active_record/properties.rb @@ -2,7 +2,7 @@ module ActiveRecord module Properties # :nodoc: extend ActiveSupport::Concern - Type = ConnectionAdapters::Type + Type = ActiveRecord::Type module ClassMethods # Defines or overrides a property on this model. This allows customization of diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb new file mode 100644 index 0000000000..2c26477201 --- /dev/null +++ b/activerecord/lib/active_record/type.rb @@ -0,0 +1,18 @@ +require 'active_record/type/numeric' +require 'active_record/type/time_value' +require 'active_record/type/value' + +require 'active_record/type/binary' +require 'active_record/type/boolean' +require 'active_record/type/date' +require 'active_record/type/date_time' +require 'active_record/type/decimal' +require 'active_record/type/decimal_without_scale' +require 'active_record/type/float' +require 'active_record/type/integer' +require 'active_record/type/string' +require 'active_record/type/text' +require 'active_record/type/time' + +require 'active_record/type/type_map' +require 'active_record/type/hash_lookup_type_map' diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb new file mode 100644 index 0000000000..e34b7bb268 --- /dev/null +++ b/activerecord/lib/active_record/type/binary.rb @@ -0,0 +1,17 @@ +module ActiveRecord + module Type + class Binary < Value # :nodoc: + def type + :binary + end + + def binary? + true + end + + def klass + ::String + end + end + end +end diff --git a/activerecord/lib/active_record/type/boolean.rb b/activerecord/lib/active_record/type/boolean.rb new file mode 100644 index 0000000000..06dd17ed28 --- /dev/null +++ b/activerecord/lib/active_record/type/boolean.rb @@ -0,0 +1,19 @@ +module ActiveRecord + module Type + class Boolean < Value # :nodoc: + def type + :boolean + end + + private + + def cast_value(value) + if value == '' + nil + else + ConnectionAdapters::Column::TRUE_VALUES.include?(value) + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/date.rb b/activerecord/lib/active_record/type/date.rb new file mode 100644 index 0000000000..45c69460ef --- /dev/null +++ b/activerecord/lib/active_record/type/date.rb @@ -0,0 +1,42 @@ +module ActiveRecord + module Type + class Date < Value # :nodoc: + def type + :date + end + + def klass + ::Date + end + + private + + def cast_value(value) + if value.is_a?(::String) + return if value.empty? + fast_string_to_date(value) || fallback_string_to_date(value) + elsif value.respond_to?(:to_date) + value.to_date + else + value + end + end + + def fast_string_to_date(string) + if string =~ ConnectionAdapters::Column::Format::ISO_DATE + new_date $1.to_i, $2.to_i, $3.to_i + end + end + + def fallback_string_to_date(string) + new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday)) + end + + def new_date(year, mon, mday) + if year && year != 0 + ::Date.new(year, mon, mday) rescue nil + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/date_time.rb b/activerecord/lib/active_record/type/date_time.rb new file mode 100644 index 0000000000..560d63c101 --- /dev/null +++ b/activerecord/lib/active_record/type/date_time.rb @@ -0,0 +1,33 @@ +module ActiveRecord + module Type + class DateTime < Value # :nodoc: + include TimeValue + + def type + :datetime + end + + private + + def cast_value(string) + return string unless string.is_a?(::String) + return if string.empty? + + fast_string_to_time(string) || fallback_string_to_time(string) + end + + # '0.123456' -> 123456 + # '1.123456' -> 123456 + def microseconds(time) + time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0 + end + + def fallback_string_to_time(string) + time_hash = ::Date._parse(string) + time_hash[:sec_fraction] = microseconds(time_hash) + + new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)) + end + end + end +end diff --git a/activerecord/lib/active_record/type/decimal.rb b/activerecord/lib/active_record/type/decimal.rb new file mode 100644 index 0000000000..1c0147a797 --- /dev/null +++ b/activerecord/lib/active_record/type/decimal.rb @@ -0,0 +1,25 @@ +module ActiveRecord + module Type + class Decimal < Value # :nodoc: + include Numeric + + def type + :decimal + end + + def klass + ::BigDecimal + end + + private + + def cast_value(value) + if value.respond_to?(:to_d) + value.to_d + else + value.to_s.to_d + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb new file mode 100644 index 0000000000..cabdcecdd7 --- /dev/null +++ b/activerecord/lib/active_record/type/decimal_without_scale.rb @@ -0,0 +1,11 @@ +require 'active_record/type/integer' + +module ActiveRecord + module Type + class DecimalWithoutScale < Integer # :nodoc: + def type + :decimal + end + end + end +end diff --git a/activerecord/lib/active_record/type/float.rb b/activerecord/lib/active_record/type/float.rb new file mode 100644 index 0000000000..dc50dae328 --- /dev/null +++ b/activerecord/lib/active_record/type/float.rb @@ -0,0 +1,23 @@ +module ActiveRecord + module Type + class Float < Value # :nodoc: + include Numeric + + def type + :float + end + + def klass + ::Float + end + + alias type_cast_for_database type_cast + + private + + def cast_value(value) + value.to_f + end + end + end +end diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb new file mode 100644 index 0000000000..bf92680268 --- /dev/null +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -0,0 +1,19 @@ +module ActiveRecord + module Type + class HashLookupTypeMap < TypeMap # :nodoc: + delegate :key?, to: :@mapping + + def lookup(type, *args) + @mapping.fetch(type, proc { default_value }).call(type, *args) + end + + def fetch(type, *args, &block) + @mapping.fetch(type, block).call(type, *args) + end + + def alias_type(type, alias_type) + register_type(type) { |_, *args| lookup(alias_type, *args) } + end + end + end +end diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb new file mode 100644 index 0000000000..1e2147dec9 --- /dev/null +++ b/activerecord/lib/active_record/type/integer.rb @@ -0,0 +1,27 @@ +module ActiveRecord + module Type + class Integer < Value # :nodoc: + include Numeric + + def type + :integer + end + + def klass + ::Fixnum + end + + alias type_cast_for_database type_cast + + private + + def cast_value(value) + case value + when true then 1 + when false then 0 + else value.to_i rescue nil + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/numeric.rb b/activerecord/lib/active_record/type/numeric.rb new file mode 100644 index 0000000000..464d631d80 --- /dev/null +++ b/activerecord/lib/active_record/type/numeric.rb @@ -0,0 +1,18 @@ +module ActiveRecord + module Type + module Numeric # :nodoc: + def number? + true + end + + def type_cast_for_write(value) + case value + when true then 1 + when false then 0 + when ::String then value.presence + else super + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb new file mode 100644 index 0000000000..b3f6ca7691 --- /dev/null +++ b/activerecord/lib/active_record/type/string.rb @@ -0,0 +1,27 @@ +module ActiveRecord + module Type + class String < Value # :nodoc: + def type + :string + end + + def text? + true + end + + def klass + ::String + end + + private + + def cast_value(value) + case value + when true then "1" + when false then "0" + else value.to_s + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/text.rb b/activerecord/lib/active_record/type/text.rb new file mode 100644 index 0000000000..26f980f060 --- /dev/null +++ b/activerecord/lib/active_record/type/text.rb @@ -0,0 +1,11 @@ +require 'active_record/type/string' + +module ActiveRecord + module Type + class Text < String # :nodoc: + def type + :text + end + end + end +end diff --git a/activerecord/lib/active_record/type/time.rb b/activerecord/lib/active_record/type/time.rb new file mode 100644 index 0000000000..41f7d97f0c --- /dev/null +++ b/activerecord/lib/active_record/type/time.rb @@ -0,0 +1,26 @@ +module ActiveRecord + module Type + class Time < Value # :nodoc: + include TimeValue + + def type + :time + end + + private + + def cast_value(value) + return value unless value.is_a?(::String) + return if value.empty? + + dummy_time_value = "2000-01-01 #{value}" + + fast_string_to_time(dummy_time_value) || begin + time_hash = ::Date._parse(dummy_time_value) + return if time_hash[:hour].nil? + new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)) + end + end + end + end +end diff --git a/activerecord/lib/active_record/type/time_value.rb b/activerecord/lib/active_record/type/time_value.rb new file mode 100644 index 0000000000..6cc19b6379 --- /dev/null +++ b/activerecord/lib/active_record/type/time_value.rb @@ -0,0 +1,34 @@ +module ActiveRecord + module Type + module TimeValue # :nodoc: + def klass + ::Time + 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 + Base.default_timezone == :utc ? time : time.getlocal + else + ::Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil + end + end + + # Doesn't handle time zones. + def fast_string_to_time(string) + if string =~ ConnectionAdapters::Column::Format::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 diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb new file mode 100644 index 0000000000..88c5f9c497 --- /dev/null +++ b/activerecord/lib/active_record/type/type_map.rb @@ -0,0 +1,48 @@ +module ActiveRecord + module Type + class TypeMap # :nodoc: + def initialize + @mapping = {} + end + + def lookup(lookup_key, *args) + matching_pair = @mapping.reverse_each.detect do |key, _| + key === lookup_key + end + + if matching_pair + matching_pair.last.call(lookup_key, *args) + else + default_value + end + end + + def register_type(key, value = nil, &block) + raise ::ArgumentError unless value || block + + if block + @mapping[key] = block + else + @mapping[key] = proc { value } + end + end + + def alias_type(key, target_key) + register_type(key) do |sql_type, *args| + metadata = sql_type[/\(.*\)/, 0] + lookup("#{target_key}#{metadata}", *args) + end + end + + def clear + @mapping.clear + end + + private + + def default_value + @default_value ||= Value.new + end + end + end +end diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb new file mode 100644 index 0000000000..72d27197d5 --- /dev/null +++ b/activerecord/lib/active_record/type/value.rb @@ -0,0 +1,59 @@ +module ActiveRecord + module Type + class Value # :nodoc: + attr_reader :precision, :scale, :limit + + # Valid options are +precision+, +scale+, and +limit+. + # They are only used when dumping schema. + def initialize(options = {}) + options.assert_valid_keys(:precision, :scale, :limit) + @precision = options[:precision] + @scale = options[:scale] + @limit = options[:limit] + end + + # The simplified that this object represents. Subclasses + # should override this method. + def type; end + + # Takes an input from the database, or from attribute setters, + # and casts it to a type appropriate for this object. This method + # should not be overriden by subclasses. Instead, override `cast_value`. + def type_cast(value) + cast_value(value) unless value.nil? + end + + def type_cast_for_write(value) + value + end + + def type_cast_for_database(value) + type_cast_for_write(value) + end + + def text? + false + end + + def number? + false + end + + def binary? + false + end + + def klass + ::Object + end + + private + + # Responsible for casting values from external sources to the appropriate + # type. Called by `type_cast` for all values except `nil`. + def cast_value(value) # :api: public + value + end + end + end +end -- cgit v1.2.3