diff options
Diffstat (limited to 'activerecord/lib')
20 files changed, 82 insertions, 85 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index c5f7bcae7d..306588ac66 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -57,7 +57,7 @@ module ActiveRecord def ids_writer(ids) pk_column = reflection.primary_key_column ids = Array(ids).reject { |id| id.blank? } - ids.map! { |i| pk_column.type_cast(i) } + ids.map! { |i| pk_column.type_cast_from_user(i) } replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids)) end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 954128064d..f6e08991f7 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -89,7 +89,7 @@ module ActiveRecord end def through_scope_attributes - scope.where_values_hash(through_association.reflection.name.to_s) + scope.where_values_hash(through_association.reflection.name.to_s).except!(through_association.reflection.foreign_key) end def save_through_record(record) diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 35659766d3..fbb4551b22 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -144,7 +144,7 @@ module ActiveRecord column_aliases = aliases.column_aliases join_root result_set.each { |row_hash| - primary_id = type_caster.type_cast row_hash[primary_key] + primary_id = type_caster.type_cast_from_database row_hash[primary_key] parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases) construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases) } diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index ae3785638a..a354cd7503 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -123,9 +123,9 @@ module ActiveRecord } if self.class.cache_attribute?(name) - @attributes[name] = column.type_cast(value) + @attributes[name] = column.type_cast_from_database(value) else - column.type_cast value + column.type_cast_from_database value end } end 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 c1c3987cf5..abad949ef4 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,21 +1,22 @@ module ActiveRecord module AttributeMethods module TimeZoneConversion - class Type # :nodoc: - delegate :type, :type_cast_for_database, to: :@column - - def initialize(column) - @column = column + class Type < SimpleDelegator # :nodoc: + def type_cast_from_database(value) + convert_time_to_time_zone(super) end - def type_cast(value) - value = @column.type_cast(value) - convert_value_to_time_zone(value) + def type_cast_from_user(value) + if value.is_a?(Array) + value.map { |v| type_cast_from_user(v) } + elsif value.respond_to?(:in_time_zone) + value.in_time_zone + end end - def convert_value_to_time_zone(value) + def convert_time_to_time_zone(value) if value.is_a?(Array) - value.map { |v| convert_value_to_time_zone(v) } + value.map { |v| convert_time_to_time_zone(v) } elsif value.acts_like?(:time) value.in_time_zone else @@ -35,26 +36,6 @@ module ActiveRecord end module ClassMethods - protected - # Defined for all +datetime+ attributes when +time_zone_aware_attributes+ are enabled. - # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone. - def define_method_attribute=(attr_name) - if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) - method_body, line = <<-EOV, __LINE__ + 1 - def #{attr_name}=(time) - time_with_zone = convert_value_to_time_zone(time) - previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name}) - write_attribute(:#{attr_name}, time) - #{attr_name}_will_change! if previous_time != time_with_zone - @attributes["#{attr_name}"] = time_with_zone - end - EOV - generated_attribute_methods.module_eval(method_body, __FILE__, line) - else - super - end - end - private def create_time_zone_conversion_attribute?(name, column) time_zone_aware_attributes && @@ -62,16 +43,6 @@ module ActiveRecord (:datetime == column.type) end end - - private - - def convert_value_to_time_zone(value) - if value.is_a?(Array) - value.map { |v| convert_value_to_time_zone(v) } - elsif value.respond_to?(:in_time_zone) - value.in_time_zone - end - end end end end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 5203b30462..b72a6219b0 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -70,7 +70,7 @@ module ActiveRecord attr_name = attr_name.to_s attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key @attributes.delete(attr_name) - column = column_for_attribute(attr_name) + column = type_for_attribute(attr_name) unless has_attribute?(attr_name) || self.class.columns_hash.key?(attr_name) raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" @@ -80,13 +80,11 @@ module ActiveRecord # so we don't attempt to typecast multiple times. if column.binary? @attributes[attr_name] = value + elsif should_type_cast + @attributes[attr_name] = column.type_cast_from_user(value) end - if should_type_cast - @raw_attributes[attr_name] = column.type_cast_for_write(value) - else - @raw_attributes[attr_name] = value - end + @raw_attributes[attr_name] = value end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e8c067f758..e4d0abb8ef 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -311,8 +311,8 @@ module ActiveRecord #:nodoc: include Locking::Optimistic include Locking::Pessimistic include AttributeMethods - include Timestamp include Callbacks + include Timestamp include Associations include ActiveModel::SecurePassword include AutosaveAssociation diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 23434df1fe..72c6990ba5 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -17,7 +17,7 @@ module ActiveRecord delegate :type, :precision, :scale, :limit, :klass, :accessor, :text?, :number?, :binary?, :serialized?, :changed?, - :type_cast, :type_cast_for_write, :type_cast_for_database, + :type_cast_from_user, :type_cast_from_database, :type_cast_for_database, :type_cast_for_schema, to: :cast_type @@ -52,7 +52,7 @@ module ActiveRecord end def default - @default ||= type_cast(@original_default) + @default ||= type_cast_from_database(@original_default) end def with_type(type) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb index 971f5eed7e..666d1cf6e3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -108,7 +108,7 @@ module ActiveRecord if ::Array === value value.map {|item| type_cast_array(oid, item)} else - oid.type_cast value + oid.type_cast_from_database value end 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 a65ca83f77..88de816d4f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb @@ -7,12 +7,16 @@ module ActiveRecord :hstore end - def type_cast_for_write(value) - ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value) + def type_cast_from_user(value) + type_cast_from_database(type_cast_for_database(value)) + end + + def type_cast_from_database(value) + ConnectionAdapters::PostgreSQLColumn.string_to_hstore(value) end - def cast_value(value) - ConnectionAdapters::PostgreSQLColumn.string_to_hstore value + def type_cast_for_database(value) + ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value) end def accessor 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 c87422fe32..b4fed1bcab 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb @@ -7,12 +7,16 @@ module ActiveRecord :json end - def type_cast_for_write(value) - ConnectionAdapters::PostgreSQLColumn.json_to_string(value) + def type_cast_from_user(value) + type_cast_from_database(type_cast_for_database(value)) + end + + def type_cast_from_database(value) + ConnectionAdapters::PostgreSQLColumn.string_to_json(value) end - def cast_value(value) - ConnectionAdapters::PostgreSQLColumn.string_to_json value + def type_cast_for_database(value) + ConnectionAdapters::PostgreSQLColumn.json_to_string(value) end def accessor 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 a0d8a94c74..c289ba8980 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb @@ -29,7 +29,7 @@ module ActiveRecord end def type_cast_single(value) - infinity?(value) ? value : @subtype.type_cast(value) + infinity?(value) ? value : @subtype.type_cast_from_database(value) end def cast_value(value) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 283ca81f94..71b05cdbae 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -348,14 +348,14 @@ module ActiveRecord if supports_extensions? res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", 'SCHEMA' - res.column_types['enabled'].type_cast res.rows.first.first + res.column_types['enabled'].type_cast_from_database res.rows.first.first end end def extensions if supports_extensions? res = exec_query "SELECT extname from pg_extension", "SCHEMA" - res.rows.map { |r| res.column_types['extname'].type_cast r.first } + res.rows.map { |r| res.column_types['extname'].type_cast_from_database r.first } else super end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index baf2b5fbf8..f96f77f696 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -51,6 +51,8 @@ module ActiveRecord self.pluralize_table_names = true self.inheritance_column = 'type' + + delegate :type_for_attribute, to: :class end module ClassMethods @@ -221,6 +223,10 @@ module ActiveRecord @column_types ||= decorate_columns(columns_hash.dup) end + def type_for_attribute(attr_name) # :nodoc: + column_types.fetch(attr_name) { column_for_attribute(attr_name) } + end + def decorate_columns(columns_hash) # :nodoc: return if columns_hash.empty? @@ -245,7 +251,7 @@ module ActiveRecord # are the default values suitable for use in `@raw_attriubtes` def raw_column_defaults # :nodoc: @raw_column_defauts ||= Hash[column_defaults.map { |name, default| - [name, columns_hash[name].type_cast_for_write(default)] + [name, columns_hash[name].type_cast_for_database(default)] }] end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 1242f49e28..8a2a06f2ca 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) - Type::Boolean.new.type_cast(hash['_destroy']) + Type::Boolean.new.type_cast_from_user(hash['_destroy']) end # Determines if a new record should be rejected by checking diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 11ab1b4595..38970a66ae 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -179,7 +179,7 @@ module ActiveRecord result = result.map do |attributes| values = attributes.values - columns.zip(values).map { |column, value| column.type_cast value } + columns.zip(values).map { |column, value| column.type_cast_from_database value } end columns.one? ? result.map!(&:first) : result end @@ -379,7 +379,7 @@ module ActiveRecord end def type_cast_using_column(value, column) - column ? column.type_cast(value) : value + column ? column.type_cast_from_database(value) : value end # TODO: refactor to allow non-string `select_values` (eg. Arel nodes). diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 293189fa69..ae53f66d7a 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -31,7 +31,7 @@ module ActiveRecord class Result include Enumerable - IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc: + IDENTITY_TYPE = Type::Value.new # :nodoc: attr_reader :columns, :rows, :column_types diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb index bc93f6e1bf..3bf29b5026 100644 --- a/activerecord/lib/active_record/type/binary.rb +++ b/activerecord/lib/active_record/type/binary.rb @@ -9,7 +9,16 @@ module ActiveRecord true end + def type_cast(value) + if value.is_a?(Data) + value.to_s + else + super + end + end + def type_cast_for_database(value) + return if value.nil? Data.new(super) end diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index 0866383de9..9144028576 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -9,7 +9,7 @@ module ActiveRecord super(subtype) end - def type_cast(value) + def type_cast_from_database(value) if is_default_value?(value) value else @@ -17,15 +17,17 @@ module ActiveRecord end end - def type_cast_for_write(value) + def type_cast_from_user(value) + type_cast_from_database(type_cast_for_database(value)) + end + + def type_cast_for_database(value) return if value.nil? unless is_default_value?(value) super coder.dump(value) end end - alias type_cast_for_database type_cast_for_write - def serialized? true end diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 1c41b28646..efcdab1c0e 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -16,15 +16,16 @@ module ActiveRecord # must 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? + def type_cast_from_database(value) + type_cast(value) + end + + def type_cast_from_user(value) + type_cast(value) end def type_cast_for_database(value) - type_cast_for_write(value) + value end def type_cast_for_schema(value) @@ -50,10 +51,6 @@ module ActiveRecord def klass # :nodoc: end - def type_cast_for_write(value) # :nodoc: - value - end - # +old_value+ will always be type-cast. # +new_value+ will come straight from the database # or from assignment, so it could be anything. Types @@ -64,6 +61,12 @@ module ActiveRecord end private + # 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) # :api: public + cast_value(value) unless value.nil? + end # Responsible for casting values from external sources to the appropriate # type. Called by `type_cast` for all values except `nil`. |