aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/type/value.rb
blob: 7f41d525e18e90c56ca0730c72544d9cdf0889eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
module ActiveRecord
  module Type
    class Value
      attr_reader :precision, :scale, :limit

      def initialize(precision: nil, limit: nil, scale: nil)
        @precision = precision
        @scale = scale
        @limit = limit
      end

      def type; end # :nodoc:

      # 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
      # +type_cast+ and +cast_value+
      #
      # +value+ The raw input, as provided from the database
      def type_cast_from_database(value)
        type_cast(value)
      end

      # Type casts a value from user input (e.g. from a setter). This value may
      # be a string from the form builder, or a ruby object passed to a setter.
      # There is currently no way to differentiate between which source it came
      # from.
      #
      # The return value of this method will be returned from
      # +ActiveRecord::AttributeMethods::Read#read_attribute+. See also:
      # +type_cast+ and +cast_value+
      #
      # +value+ The raw input, as provided to the attribute setter.
      def type_cast_from_user(value)
        type_cast(value)
      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)
        value
      end

      # Type cast a value for schema dumping. This method is private, as we are
      # hoping to remove it entirely.
      def type_cast_for_schema(value) # :nodoc:
        value.inspect
      end

      # These predicates are not documented, as I need to look further into
      # their use, and see if they can be removed entirely.
      def text? # :nodoc:
        false
      end

      def number? # :nodoc:
        false
      end

      def binary? # :nodoc:
        false
      end

      def klass # :nodoc:
      end

      # Determines whether a value has changed for dirty checking. +old_value+
      # and +new_value+ will always be type-cast. Types should not need to
      # override this method.
      def changed?(old_value, new_value, _new_value_before_type_cast)
        old_value != new_value
      end

      # Determines whether the mutable value has been modified since it was
      # read. Returns +false+ by default. If your type returns an object
      # which could be mutated, you should override this method. You will need
      # to either:
      #
      # - pass +new_value+ to +type_cast_for_database+ and compare it to
      #   +raw_old_value+
      #
      # or
      #
      # - pass +raw_old_value+ to +type_cast_from_database+ and compare it to
      #   +new_value+
      #
      # +raw_old_value+ The original value, before being passed to
      # +type_cast_from_database+.
      #
      # +new_value+ The current value, after type casting.
      def changed_in_place?(raw_old_value, new_value)
        false
      end

      def ==(other)
        self.class == other.class &&
          precision == other.precision &&
          scale == other.scale &&
          limit == other.limit
      end

      private

      # Convenience method. If you don't need separate behavior for
      # +type_cast_from_database+ and +type_cast_from_user+, you can override
      # this method instead. The default behavior of both methods is to call
      # this one. See also +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
      # +type_cast_from_database+ and +type_cast_from_user+ for all values
      # except +nil+.
      def cast_value(value) # :doc:
        value
      end
    end
  end
end