diff options
Diffstat (limited to 'activerecord/lib')
45 files changed, 162 insertions, 139 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 260ef71e64..a146d78a5a 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1386,7 +1386,7 @@ module ActiveRecord # has_one :last_comment, -> { order 'posted_on' }, class_name: "Comment" # has_one :project_manager, -> { where role: 'project_manager' }, class_name: "Person" # has_one :attachment, as: :attachable - # has_one :boss, readonly: :true + # has_one :boss, -> { readonly } # has_one :club, through: :membership # has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable # has_one :credit_card, required: true @@ -1514,7 +1514,7 @@ module ActiveRecord # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count }, # class_name: "Coupon", foreign_key: "coupon_id" # belongs_to :attachable, polymorphic: true - # belongs_to :project, readonly: true + # belongs_to :project, -> { readonly } # belongs_to :post, counter_cache: true # belongs_to :comment, touch: true # belongs_to :company, touch: :employees_last_updated_at diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 33e4516bd1..eea847cfa3 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -63,7 +63,7 @@ module ActiveRecord def ids_writer(ids) pk_type = reflection.primary_key_type ids = Array(ids).reject(&:blank?) - ids.map! { |i| pk_type.type_cast_from_user(i) } + ids.map! { |i| pk_type.cast(i) } replace(klass.find(ids).index_by(&:id).values_at(*ids)) end diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb index 91886f1324..73dd3fa041 100644 --- a/activerecord/lib/active_record/attribute.rb +++ b/activerecord/lib/active_record/attribute.rb @@ -43,7 +43,7 @@ module ActiveRecord end def value_for_database - type.type_cast_for_database(value) + type.serialize(value) end def changed_from?(old_value) @@ -108,13 +108,13 @@ module ActiveRecord class FromDatabase < Attribute # :nodoc: def type_cast(value) - type.type_cast_from_database(value) + type.deserialize(value) end end class FromUser < Attribute # :nodoc: def type_cast(value) - type.type_cast_from_user(value) + type.cast(value) end def came_from_user? diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 90c36e4b02..f9beb43e4b 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -2,13 +2,13 @@ module ActiveRecord module AttributeMethods module TimeZoneConversion class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc: - def type_cast_from_database(value) + def deserialize(value) convert_time_to_time_zone(super) end - def type_cast_from_user(value) + def cast(value) if value.is_a?(Array) - value.map { |v| type_cast_from_user(v) } + value.map { |v| cast(v) } elsif value.is_a?(Hash) set_time_zone_without_conversion(super) elsif value.respond_to?(:in_time_zone) diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index 87d4bc8578..c8979a60d7 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -102,14 +102,14 @@ module ActiveRecord # # Users may also define their own custom types, as long as they respond # to the methods defined on the value type. The method - # +type_cast_from_database+ or +type_cast_from_user+ will be called on + # +deserialize+ or +cast+ will be called on # your type object, with raw input from the database or from your # controllers. See ActiveRecord::Type::Value for the expected API. It is # recommended that your type objects inherit from an existing type, or # from ActiveRecord::Type::Value # # class MoneyType < ActiveRecord::Type::Integer - # def type_cast_from_user(value) + # def cast(value) # if value.include?('$') # price_in_dollars = value.gsub(/\$/, '').to_f # super(price_in_dollars * 100) @@ -119,21 +119,27 @@ module ActiveRecord # end # end # + # # config/initializers/types.rb + # ActiveRecord::Type.register(:money, MoneyType) + # + # # /app/models/store_listing.rb # class StoreListing < ActiveRecord::Base - # attribute :price_in_cents, MoneyType.new + # attribute :price_in_cents, :money # end # # store_listing = StoreListing.new(price_in_cents: '$10.00') # store_listing.price_in_cents # => 1000 # # For more details on creating custom types, see the documentation for - # ActiveRecord::Type::Value. + # ActiveRecord::Type::Value. For more details on registering your types + # to be referenced by a symbol, see ActiveRecord::Type.register. You can + # also pass a type object directly, in place of a symbol. # # ==== Querying # # When ActiveRecord::QueryMethods#where is called, it will # use the type defined by the model class to convert the value to SQL, - # calling +type_cast_for_database+ on your type object. For example: + # calling +serialize+ on your type object. For example: # # class Money < Struct.new(:amount, :currency) # end @@ -143,18 +149,20 @@ module ActiveRecord # @currency_converter = currency_converter # end # - # # value will be the result of +type_cast_from_database+ or - # # +type_cast_from_user+. Assumed to be in instance of +Money+ in + # # value will be the result of +deserialize+ or + # # +cast+. Assumed to be in instance of +Money+ in # # this case. - # def type_cast_for_database(value) + # def serialize(value) # value_in_bitcoins = @currency_converter.convert_to_bitcoins(value) # value_in_bitcoins.amount # end # end # + # ActiveRecord::Type.register(:money, MoneyType) + # # class Product < ActiveRecord::Base # currency_converter = ConversionRatesFromTheInternet.new - # attribute :price_in_bitcoins, MoneyType.new(currency_converter) + # attribute :price_in_bitcoins, :money, currency_converter # end # # Product.where(price_in_bitcoins: Money.new(5, "USD")) @@ -195,7 +203,7 @@ module ActiveRecord # Otherwise, the default will be +nil+. # # +user_provided_default+ Whether the default value should be cast using - # +type_cast_from_user+ or +type_cast_from_database+. + # +cast+ or +deserialize+. def define_attribute( name, cast_type, diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 1ac909da2e..d2840b9498 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -52,7 +52,7 @@ module ActiveRecord def type_cast_from_column(column, value) # :nodoc: if column type = lookup_cast_type_from_column(column) - type.type_cast_for_database(value) + type.serialize(value) else value end @@ -103,7 +103,7 @@ module ActiveRecord end def quote_default_expression(value, column) #:nodoc: - value = lookup_cast_type(column.sql_type).type_cast_for_database(value) + value = lookup_cast_type(column.sql_type).serialize(value) quote(value) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index 932aaf7aa7..af7ef7cbaa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -47,7 +47,7 @@ module ActiveRecord def schema_default(column) type = lookup_cast_type_from_column(column) - default = type.type_cast_from_database(column.default) + default = type.deserialize(column.default) unless default.nil? type.type_cast_for_schema(default) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 0438c95bd7..503b09d1fc 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -851,12 +851,6 @@ module ActiveRecord raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified" end - elsif [:datetime, :time].include?(type) && precision ||= native[:precision] - if (0..6) === precision - column_type_sql << "(#{precision})" - else - raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6") - end elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit]) column_type_sql << "(#{limit})" end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c307189980..ee11c0efa4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -245,11 +245,6 @@ module ActiveRecord false end - # Does this adapter support datetime with precision? - def supports_datetime_with_precision? - false - end - # This is meant to be implemented by the adapters that support extensions def disable_extension(name) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 84bfab43bb..0a1c66be1e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -251,10 +251,6 @@ module ActiveRecord version[0] >= 5 end - def supports_datetime_with_precision? - (version[0] == 5 && version[1] >= 6) || version[0] >= 6 - end - def native_database_types NATIVE_DATABASE_TYPES end @@ -627,6 +623,13 @@ module ActiveRecord when 0x1000000..0xffffffff; 'longtext' else raise(ActiveRecordError, "No text type has character length #{limit}") end + when 'datetime' + return super unless precision + + case precision + when 0..6; "datetime(#{precision})" + else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.") + end else super end @@ -923,7 +926,7 @@ module ActiveRecord end class MysqlString < Type::String # :nodoc: - def type_cast_for_database(value) + def serialize(value) case value when true then "1" when false then "0" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index 2608a2abab..fb4e0de2a8 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -25,22 +25,22 @@ module ActiveRecord @delimiter = delimiter end - def type_cast_from_database(value) + def deserialize(value) if value.is_a?(::String) - type_cast_array(parse_pg_array(value), :type_cast_from_database) + type_cast_array(parse_pg_array(value), :deserialize) else super end end - def type_cast_from_user(value) + def cast(value) if value.is_a?(::String) value = parse_pg_array(value) end - type_cast_array(value, :type_cast_from_user) + type_cast_array(value, :cast) end - def type_cast_for_database(value) + def serialize(value) if value.is_a?(::Array) cast_value_for_database(value) else @@ -69,7 +69,7 @@ module ActiveRecord casted_values = value.map { |item| cast_value_for_database(item) } "{#{casted_values.join(delimiter)}}" else - quote_and_escape(subtype.type_cast_for_database(value)) + quote_and_escape(subtype.serialize(value)) end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb index 1dbb40ca1d..ea0fa2517f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb @@ -7,7 +7,7 @@ module ActiveRecord :bit end - def type_cast(value) + def cast(value) if ::String === value case value when /^0x/i @@ -20,7 +20,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) Data.new(super) if value end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb index 6bd1b8ecae..8f9d6e7f9b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb @@ -3,7 +3,7 @@ module ActiveRecord module PostgreSQL module OID # :nodoc: class Bytea < Type::Binary # :nodoc: - def type_cast_from_database(value) + def deserialize(value) return if value.nil? return value.to_s if value.is_a?(Type::Binary::Data) PGconn.unescape_bytea(super) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb index 222f10fa8f..eeccb09bdf 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb @@ -18,7 +18,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) if IPAddr === value "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}" else diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb index 77d5038efd..b3b610a5f6 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb @@ -7,7 +7,7 @@ module ActiveRecord :enum end - def type_cast(value) + def cast(value) value.to_s end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb index b46e50c865..9270fc9f21 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb @@ -9,7 +9,7 @@ module ActiveRecord :hstore end - def type_cast_from_database(value) + def deserialize(value) if value.is_a?(::String) ::Hash[value.scan(HstorePair).map { |k, v| v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1') @@ -21,7 +21,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) if value.is_a?(::Hash) value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ') else diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb index 13dd037314..8e1256baad 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb @@ -9,19 +9,19 @@ module ActiveRecord :json end - def type_cast_from_database(value) + def deserialize(value) if value.is_a?(::String) ::ActiveSupport::JSON.decode(value) rescue nil else - super + value end end - def type_cast_for_database(value) + def serialize(value) if value.is_a?(::Array) || value.is_a?(::Hash) ::ActiveSupport::JSON.encode(value) else - super + value end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb index 380c50fc14..afc9383f91 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb @@ -13,7 +13,7 @@ module ActiveRecord # the comparison here. Therefore, we need to parse and re-dump the # raw value here to ensure the insignificant whitespaces are # consistent with our encoder's output. - raw_old_value = type_cast_for_database(type_cast_from_database(raw_old_value)) + raw_old_value = serialize(deserialize(raw_old_value)) super(raw_old_value, new_value) end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb index 4084725ed7..bf565bcf47 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb @@ -9,13 +9,13 @@ module ActiveRecord :point end - def type_cast(value) + def cast(value) case value when ::String if value[0] == '(' && value[-1] == ')' value = value[1...-1] end - type_cast(value.split(',')) + cast(value.split(',')) when ::Array value.map { |v| Float(v) } else @@ -23,7 +23,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) if value.is_a?(::Array) "(#{number_for_point(value[0])},#{number_for_point(value[1])})" else diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb index 9d3633d109..fc201f8fb9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb @@ -30,7 +30,7 @@ module ActiveRecord ::Range.new(from, to, extracted[:exclude_end]) end - def type_cast_for_database(value) + def serialize(value) if value.is_a?(::Range) from = type_cast_single_for_database(value.begin) to = type_cast_single_for_database(value.end) @@ -49,11 +49,11 @@ module ActiveRecord private def type_cast_single(value) - infinity?(value) ? value : @subtype.type_cast_from_database(value) + infinity?(value) ? value : @subtype.deserialize(value) end def type_cast_single_for_database(value) - infinity?(value) ? '' : @subtype.type_cast_for_database(value) + infinity?(value) ? '' : @subtype.serialize(value) end def extract_bounds(value) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb index 97b4fd3d08..5e839228e9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb @@ -5,13 +5,13 @@ module ActiveRecord class Uuid < Type::Value # :nodoc: ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x - alias_method :type_cast_for_database, :type_cast_from_database + alias_method :serialize, :deserialize def type :uuid end - def type_cast(value) + def cast(value) value.to_s[ACCEPTABLE_UUID, 0] end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/vector.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/vector.rb index de4187b028..b26e876b54 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/vector.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/vector.rb @@ -16,7 +16,7 @@ module ActiveRecord # FIXME: this should probably split on +delim+ and use +subtype+ # to cast the values. Unfortunately, the current Rails behavior # is to just return the string. - def type_cast(value) + def cast(value) value end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb index 334af7c598..d40d837cee 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb @@ -7,7 +7,7 @@ module ActiveRecord :xml end - def type_cast_for_database(value) + def serialize(value) return unless value Data.new(super) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 503dda60f4..81fde18f64 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -534,6 +534,13 @@ module ActiveRecord when 5..8; 'bigint' else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end + when 'datetime' + return super unless precision + + case precision + when 0..6; "timestamp(#{precision})" + else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6") + end else super end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6d25b53b21..5a887ea529 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -180,10 +180,6 @@ module ActiveRecord true end - def supports_datetime_with_precision? - true - end - def index_algorithms { concurrently: 'CONCURRENTLY' } end @@ -481,6 +477,7 @@ module ActiveRecord register_class_with_limit m, 'varbit', OID::BitVarying m.alias_type 'timestamptz', 'timestamp' m.register_type 'date', Type::Date.new + m.register_type 'time', Type::Time.new m.register_type 'money', OID::Money.new m.register_type 'bytea', OID::Bytea.new @@ -506,8 +503,10 @@ module ActiveRecord m.alias_type 'lseg', 'varchar' m.alias_type 'box', 'varchar' - register_class_with_precision m, 'time', Type::Time - register_class_with_precision m, 'timestamp', OID::DateTime + m.register_type 'timestamp' do |_, _, sql_type| + precision = extract_precision(sql_type) + OID::DateTime.new(precision: precision) + end m.register_type 'numeric' do |_, fmod, sql_type| precision = extract_precision(sql_type) diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 470e0b5d29..ea88983917 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -93,7 +93,7 @@ module ActiveRecord @mapping = mapping end - def type_cast_from_user(value) + def cast(value) return if value.blank? if mapping.has_key?(value) @@ -105,12 +105,12 @@ module ActiveRecord end end - def type_cast_from_database(value) + def deserialize(value) return if value.nil? mapping.key(value.to_i) end - def type_cast_for_database(value) + def serialize(value) mapping.fetch(value, value) end diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index c5b10fcddf..a58d8355aa 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -185,7 +185,7 @@ module ActiveRecord end class LockingType < DelegateClass(Type::Value) # :nodoc: - def type_cast_from_database(value) + def deserialize(value) # `nil` *should* be changed to 0 super.to_i end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 117a128579..084ef397a8 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -521,7 +521,7 @@ module ActiveRecord # Determines if a hash contains a truthy _destroy key. def has_destroy_flag?(hash) - Type::Boolean.new.type_cast_from_user(hash['_destroy']) + Type::Boolean.new.cast(hash['_destroy']) end # Determines if a new record should be rejected by checking diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index a543341149..e07580a563 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -27,15 +27,15 @@ module ActiveRecord # # ==== Options # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000. - # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value. + # * <tt>:begin_at</tt> - Specifies the primary key value to start from, inclusive of the value. # * <tt>:end_at</tt> - Specifies the primary key value to end at, inclusive of the value. # This is especially useful if you want multiple workers dealing with # the same processing queue. You can make worker 1 handle all the records # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond - # (by setting the +:start+ and +:end_at+ option on each worker). + # (by setting the +:begin_at+ and +:end_at+ option on each worker). # # # Let's process for a batch of 2000 records, skipping the first 2000 rows - # Person.find_each(start: 2000, batch_size: 2000) do |person| + # Person.find_each(begin_at: 2000, batch_size: 2000) do |person| # person.party_all_night! # end # @@ -46,15 +46,22 @@ module ActiveRecord # # NOTE: You can't set the limit either, that's used to control # the batch sizes. - def find_each(start: nil, end_at: nil, batch_size: 1000) + def find_each(begin_at: nil, end_at: nil, batch_size: 1000, start: nil) + if start + begin_at = start + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing `start` value to find_each is deprecated, and will be removed in Rails 5.1. + Please pass `begin_at` instead. + MSG + end if block_given? - find_in_batches(start: start, end_at: end_at, batch_size: batch_size) do |records| + find_in_batches(begin_at: begin_at, end_at: end_at, batch_size: batch_size) do |records| records.each { |record| yield record } end else - enum_for(:find_each, start: start, end_at: end_at, batch_size: batch_size) do + enum_for(:find_each, begin_at: begin_at, end_at: end_at, batch_size: batch_size) do relation = self - apply_limits(relation, start, end_at).size + apply_limits(relation, begin_at, end_at).size end end end @@ -79,15 +86,15 @@ module ActiveRecord # # ==== Options # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000. - # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value. + # * <tt>:begin_at</tt> - Specifies the primary key value to start from, inclusive of the value. # * <tt>:end_at</tt> - Specifies the primary key value to end at, inclusive of the value. # This is especially useful if you want multiple workers dealing with # the same processing queue. You can make worker 1 handle all the records # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond - # (by setting the +:start+ and +:end_at+ option on each worker). + # (by setting the +:begin_at+ and +:end_at+ option on each worker). # # # Let's process the next 2000 records - # Person.find_in_batches(start: 2000, batch_size: 2000) do |group| + # Person.find_in_batches(begin_at: 2000, batch_size: 2000) do |group| # group.each { |person| person.party_all_night! } # end # @@ -98,12 +105,19 @@ module ActiveRecord # # NOTE: You can't set the limit either, that's used to control # the batch sizes. - def find_in_batches(start: nil, end_at: nil, batch_size: 1000) - relation = self + def find_in_batches(begin_at: nil, end_at: nil, batch_size: 1000, start: nil) + if start + begin_at = start + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing `start` value to find_in_batches is deprecated, and will be removed in Rails 5.1. + Please pass `begin_at` instead. + MSG + end + relation = self unless block_given? - return to_enum(:find_in_batches, start: start, end_at: end_at, batch_size: batch_size) do - total = apply_limits(relation, start, end_at).size + return to_enum(:find_in_batches, begin_at: begin_at, end_at: end_at, batch_size: batch_size) do + total = apply_limits(relation, begin_at, end_at).size (total - 1).div(batch_size) + 1 end end @@ -113,7 +127,7 @@ module ActiveRecord end relation = relation.reorder(batch_order).limit(batch_size) - relation = apply_limits(relation, start, end_at) + relation = apply_limits(relation, begin_at, end_at) records = relation.to_a while records.any? @@ -131,8 +145,8 @@ module ActiveRecord private - def apply_limits(relation, start, end_at) - relation = relation.where(table[primary_key].gteq(start)) if start + def apply_limits(relation, begin_at, end_at) + relation = relation.where(table[primary_key].gteq(begin_at)) if begin_at relation = relation.where(table[primary_key].lteq(end_at)) if end_at relation end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 63e0d2fc21..4a4de86d48 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -353,9 +353,9 @@ module ActiveRecord def type_cast_calculated_value(value, type, operation = nil) case operation when 'count' then value.to_i - when 'sum' then type.type_cast_from_database(value || 0) + when 'sum' then type.deserialize(value || 0) when 'average' then value.respond_to?(:to_d) ? value.to_d : value - else type.type_cast_from_database(value) + else type.deserialize(value) end end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 3a3e65ef32..500c478e65 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -81,7 +81,7 @@ module ActiveRecord def cast_values(type_overrides = {}) # :nodoc: types = columns.map { |name| column_type(name, type_overrides) } result = rows.map do |values| - types.zip(values).map { |type, value| type.type_cast_from_database(value) } + types.zip(values).map { |type, value| type.deserialize(value) } end columns.one? ? result.map!(&:first) : result diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 313e767dcb..c7f55ebaa1 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -75,7 +75,7 @@ module ActiveRecord def sanitize_sql_hash_for_assignment(attrs, table) c = connection attrs.map do |attr, value| - value = type_for_attribute(attr.to_s).type_cast_for_database(value) + value = type_for_attribute(attr.to_s).serialize(value) "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}" end.join(', ') end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index cddd56a20d..2c0cda69d0 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -26,10 +26,21 @@ module ActiveRecord class << self attr_accessor :registry # :nodoc: + delegate :add_modifier, to: :registry - delegate :register, :add_modifier, to: :registry + # Add a new type to the registry, allowing it to be referenced as a + # symbol by ActiveRecord::Attributes::ClassMethods#attribute. If your + # type is only meant to be used with a specific database adapter, you can + # do so by passing +adapter: :postgresql+. If your type has the same + # name as a native type for the current adapter, an exception will be + # raised unless you specify an +:override+ option. +override: true+ will + # cause your type to be used instead of the native type. +override: + # false+ will cause the native type to be used over yours if one exists. + def register(type_name, klass = nil, **options, &block) + registry.register(type_name, klass, **options, &block) + end - def lookup(*args, adapter: current_adapter_name, **kwargs) + def lookup(*args, adapter: current_adapter_name, **kwargs) # :nodoc: registry.lookup(*args, adapter: adapter, **kwargs) end diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb index 005a48ef0d..0baf8c63ad 100644 --- a/activerecord/lib/active_record/type/binary.rb +++ b/activerecord/lib/active_record/type/binary.rb @@ -9,7 +9,7 @@ module ActiveRecord true end - def type_cast(value) + def cast(value) if value.is_a?(Data) value.to_s else @@ -17,13 +17,13 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) return if value.nil? Data.new(super) end def changed_in_place?(raw_old_value, value) - old_value = type_cast_from_database(raw_old_value) + old_value = deserialize(raw_old_value) old_value != value end diff --git a/activerecord/lib/active_record/type/date_time.rb b/activerecord/lib/active_record/type/date_time.rb index a25f2521bb..05d2af3808 100644 --- a/activerecord/lib/active_record/type/date_time.rb +++ b/activerecord/lib/active_record/type/date_time.rb @@ -10,7 +10,7 @@ module ActiveRecord :datetime end - def type_cast_for_database(value) + def serialize(value) if precision && value.respond_to?(:usec) number_of_insignificant_digits = 6 - precision round_power = 10 ** number_of_insignificant_digits diff --git a/activerecord/lib/active_record/type/float.rb b/activerecord/lib/active_record/type/float.rb index b3928242b7..d88482b85d 100644 --- a/activerecord/lib/active_record/type/float.rb +++ b/activerecord/lib/active_record/type/float.rb @@ -7,7 +7,7 @@ module ActiveRecord :float end - alias type_cast_for_database type_cast + alias serialize cast private diff --git a/activerecord/lib/active_record/type/helpers/accepts_multiparameter_time.rb b/activerecord/lib/active_record/type/helpers/accepts_multiparameter_time.rb index 640943c5e9..be571fc1c7 100644 --- a/activerecord/lib/active_record/type/helpers/accepts_multiparameter_time.rb +++ b/activerecord/lib/active_record/type/helpers/accepts_multiparameter_time.rb @@ -3,7 +3,7 @@ module ActiveRecord module Helpers class AcceptsMultiparameterTime < Module # :nodoc: def initialize(defaults: {}) - define_method(:type_cast_from_user) do |value| + define_method(:cast) do |value| if value.is_a?(Hash) value_from_multiparameter_assignment(value) else diff --git a/activerecord/lib/active_record/type/helpers/mutable.rb b/activerecord/lib/active_record/type/helpers/mutable.rb index dc37f4a885..88a9099277 100644 --- a/activerecord/lib/active_record/type/helpers/mutable.rb +++ b/activerecord/lib/active_record/type/helpers/mutable.rb @@ -2,15 +2,15 @@ module ActiveRecord module Type module Helpers module Mutable # :nodoc: - def type_cast_from_user(value) - type_cast_from_database(type_cast_for_database(value)) + 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 != type_cast_for_database(new_value) + raw_old_value != serialize(new_value) end end end diff --git a/activerecord/lib/active_record/type/helpers/numeric.rb b/activerecord/lib/active_record/type/helpers/numeric.rb index b0d4f03117..a755a02a59 100644 --- a/activerecord/lib/active_record/type/helpers/numeric.rb +++ b/activerecord/lib/active_record/type/helpers/numeric.rb @@ -2,7 +2,7 @@ module ActiveRecord module Type module Helpers module Numeric # :nodoc: - def type_cast(value) + def cast(value) value = case value when true then 1 when false then 0 diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index 2ab2402dfd..2a1b04ac7f 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -16,13 +16,13 @@ module ActiveRecord :integer end - def type_cast_from_database(value) + def deserialize(value) return if value.nil? value.to_i end - def type_cast_for_database(value) - result = type_cast(value) + def serialize(value) + result = cast(value) if result ensure_in_range(result) end diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index 6c6c520048..732029c723 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -11,7 +11,7 @@ module ActiveRecord super(subtype) end - def type_cast_from_database(value) + def deserialize(value) if default_value?(value) value else @@ -19,7 +19,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) return if value.nil? unless default_value?(value) super coder.dump(value) @@ -28,7 +28,7 @@ module ActiveRecord def changed_in_place?(raw_old_value, value) return false if value.nil? - subtype.changed_in_place?(raw_old_value, type_cast_for_database(value)) + subtype.changed_in_place?(raw_old_value, serialize(value)) end def accessor diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb index fbc0af2c5a..2662b7e874 100644 --- a/activerecord/lib/active_record/type/string.rb +++ b/activerecord/lib/active_record/type/string.rb @@ -11,7 +11,7 @@ module ActiveRecord end end - def type_cast_for_database(value) + def serialize(value) case value when ::Numeric, ActiveSupport::Duration then value.to_s when ::String then ::String.new(value) diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 7338920f3b..fc3ef5e83b 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -14,12 +14,12 @@ module ActiveRecord # Convert a value from database input to the appropriate ruby type. The # return value of this method will be returned from - # ActiveRecord::AttributeMethods::Read#read_attribute. See also - # Value#type_cast and Value#cast_value. + # ActiveRecord::AttributeMethods::Read#read_attribute. The default + # implementation just calls Value#cast. # # +value+ The raw input, as provided from the database. - def type_cast_from_database(value) - type_cast(value) + def deserialize(value) + cast(value) end # Type casts a value from user input (e.g. from a setter). This value may @@ -29,18 +29,18 @@ module ActiveRecord # # The return value of this method will be returned from # ActiveRecord::AttributeMethods::Read#read_attribute. See also: - # Value#type_cast and Value#cast_value. + # Value#cast_value. # # +value+ The raw input, as provided to the attribute setter. - def type_cast_from_user(value) - type_cast(value) + def cast(value) + cast_value(value) unless value.nil? end # Cast a value from the ruby type to a type that the database knows how # to understand. The returned value from this method should be a # +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or # +nil+. - def type_cast_for_database(value) + def serialize(value) value end @@ -68,16 +68,16 @@ module ActiveRecord # which could be mutated, you should override this method. You will need # to either: # - # - pass +new_value+ to Value#type_cast_for_database and compare it to + # - pass +new_value+ to Value#serialize and compare it to # +raw_old_value+ # # or # - # - pass +raw_old_value+ to Value#type_cast_from_database and compare it to + # - pass +raw_old_value+ to Value#deserialize and compare it to # +new_value+ # # +raw_old_value+ The original value, before being passed to - # +type_cast_from_database+. + # +deserialize+. # # +new_value+ The current value, after type casting. def changed_in_place?(raw_old_value, new_value) @@ -93,16 +93,8 @@ module ActiveRecord private - # Convenience method. If you don't need separate behavior for - # Value#type_cast_from_database and Value#type_cast_from_user, you can override - # this method instead. The default behavior of both methods is to call - # this one. See also Value#cast_value. - def type_cast(value) # :doc: - cast_value(value) unless value.nil? - end - # Convenience method for types which do not need separate type casting - # behavior for user and database inputs. Called by Value#type_cast for + # behavior for user and database inputs. Called by Value#cast for # values except +nil+. def cast_value(value) # :doc: value diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb index 03c9e8ff83..4b1941351c 100644 --- a/activerecord/lib/active_record/type_caster/map.rb +++ b/activerecord/lib/active_record/type_caster/map.rb @@ -8,7 +8,7 @@ module ActiveRecord def type_cast_for_database(attr_name, value) return value if value.is_a?(Arel::Nodes::BindParam) type = types.type_for_attribute(attr_name.to_s) - type.type_cast_for_database(value) + type.serialize(value) end protected diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index a766d77e88..9be4b10a55 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -61,7 +61,7 @@ module ActiveRecord column = klass.columns_hash[attribute_name] cast_type = klass.type_for_attribute(attribute_name) - value = cast_type.type_cast_for_database(value) + value = cast_type.serialize(value) value = klass.connection.type_cast(value) if value.is_a?(String) && column.limit value = value.to_s[0, column.limit] |