attribute.rb 3.6 KB
Newer Older
1 2 3
module ActiveRecord
  class Attribute # :nodoc:
    class << self
4 5
      def from_database(name, value, type)
        FromDatabase.new(name, value, type)
6 7
      end

8 9
      def from_user(name, value, type)
        FromUser.new(name, value, type)
10
      end
11

12 13 14 15
      def with_cast_value(name, value, type)
        WithCastValue.new(name, value, type)
      end

16 17 18 19 20 21
      def null(name)
        Null.new(name)
      end

      def uninitialized(name, type)
        Uninitialized.new(name, type)
22
      end
23 24
    end

25
    attr_reader :name, :value_before_type_cast, :type
26 27 28

    # This method should not be called directly.
    # Use #from_database or #from_user
29 30
    def initialize(name, value_before_type_cast, type)
      @name = name
31 32 33 34 35 36
      @value_before_type_cast = value_before_type_cast
      @type = type
    end

    def value
      # `defined?` is cheaper than `||=` when we get back falsy values
37
      @value = original_value unless defined?(@value)
38 39 40
      @value
    end

41 42 43 44
    def original_value
      type_cast(value_before_type_cast)
    end

45
    def value_for_database
46
      type.serialize(value)
47 48
    end

49 50 51 52 53
    def changed_from?(old_value)
      type.changed?(old_value, value, value_before_type_cast)
    end

    def changed_in_place_from?(old_value)
54
      has_been_read? && type.changed_in_place?(old_value, value)
55 56
    end

57
    def with_value_from_user(value)
58
      self.class.from_user(name, value, type)
59 60 61
    end

    def with_value_from_database(value)
62
      self.class.from_database(name, value, type)
63 64
    end

65 66 67 68
    def with_cast_value(value)
      self.class.with_cast_value(name, value, type)
    end

69 70 71 72
    def with_type(type)
      self.class.new(name, value_before_type_cast, type)
    end

73
    def type_cast(*)
74 75 76
      raise NotImplementedError
    end

77 78 79 80
    def initialized?
      true
    end

81 82 83 84
    def came_from_user?
      false
    end

85 86 87 88
    def has_been_read?
      defined?(@value)
    end

89 90 91 92 93 94
    def ==(other)
      self.class == other.class &&
        name == other.name &&
        value_before_type_cast == other.value_before_type_cast &&
        type == other.type
    end
S
Sean Griffin 已提交
95 96 97 98 99
    alias eql? ==

    def hash
      [self.class, name, value_before_type_cast, type].hash
    end
100

101 102 103 104 105 106 107 108
    protected

    def initialize_dup(other)
      if defined?(@value) && @value.duplicable?
        @value = @value.dup
      end
    end

109
    class FromDatabase < Attribute # :nodoc:
110
      def type_cast(value)
111
        type.deserialize(value)
112 113 114
      end
    end

115
    class FromUser < Attribute # :nodoc:
116
      def type_cast(value)
S
Sean Griffin 已提交
117
        type.cast(value)
118
      end
119 120 121 122

      def came_from_user?
        true
      end
123
    end
124

125 126 127 128 129 130 131 132 133 134
    class WithCastValue < Attribute # :nodoc:
      def type_cast(value)
        value
      end

      def changed_in_place_from?(old_value)
        false
      end
    end

135 136 137
    class Null < Attribute # :nodoc:
      def initialize(name)
        super(name, nil, Type::Value.new)
138
      end
139

140 141
      def value
        nil
142
      end
143

144 145 146 147
      def with_type(type)
        self.class.with_cast_value(name, nil, type)
      end

148 149 150 151
      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
152 153 154
    end

    class Uninitialized < Attribute # :nodoc:
155 156
      def initialize(name, type)
        super(name, nil, type)
157 158 159
      end

      def value
160 161 162 163 164 165
        if block_given?
          yield name
        end
      end

      def value_for_database
166 167 168 169
      end

      def initialized?
        false
170 171
      end
    end
172
    private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
173 174
  end
end