1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/lib/active_record/attribute.rb
Sean Griffin 3bc314e658 Move writing unknown column exception to null attribute
Making this change revealed several subtle bugs related to models with
no primary key, and anonymous classes. These have been fixed as well,
with regression tests added.
2014-06-26 09:43:35 -06:00

120 lines
2.5 KiB
Ruby

module ActiveRecord
class Attribute # :nodoc:
class << self
def from_database(name, value, type)
FromDatabase.new(name, value, type)
end
def from_user(name, value, type)
FromUser.new(name, value, type)
end
def null(name)
Null.new(name)
end
def uninitialized(name, type)
Uninitialized.new(name, type)
end
end
attr_reader :name, :value_before_type_cast, :type
# This method should not be called directly.
# Use #from_database or #from_user
def initialize(name, value_before_type_cast, type)
@name = name
@value_before_type_cast = value_before_type_cast
@type = type
end
def value
# `defined?` is cheaper than `||=` when we get back falsy values
@value = type_cast(value_before_type_cast) unless defined?(@value)
@value
end
def value_for_database
type.type_cast_for_database(value)
end
def changed_from?(old_value)
type.changed?(old_value, value, value_before_type_cast)
end
def changed_in_place_from?(old_value)
type.changed_in_place?(old_value, value)
end
def with_value_from_user(value)
self.class.from_user(name, value, type)
end
def with_value_from_database(value)
self.class.from_database(name, value, type)
end
def type_cast
raise NotImplementedError
end
def initialized?
true
end
protected
def initialize_dup(other)
if defined?(@value) && @value.duplicable?
@value = @value.dup
end
end
class FromDatabase < Attribute # :nodoc:
def type_cast(value)
type.type_cast_from_database(value)
end
end
class FromUser < Attribute # :nodoc:
def type_cast(value)
type.type_cast_from_user(value)
end
end
class Null < Attribute # :nodoc:
def initialize(name)
super(name, nil, Type::Value.new)
end
def value
nil
end
def with_value_from_database(value)
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
end
alias_method :with_value_from_user, :with_value_from_database
end
class Uninitialized < Attribute # :nodoc:
def initialize(name, type)
super(name, nil, type)
end
def value
if block_given?
yield name
end
end
def value_for_database
end
def initialized?
false
end
end
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
end
end