mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
3bc314e658
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.
120 lines
2.5 KiB
Ruby
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
|