module ActiveRecord::Attributes::ClassMethods

def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)

methods in ActiveModel::Type::Value for more details.
will be called from ActiveModel::Dirty. See the documentation for those
tracking is performed. The methods +changed?+ and +changed_in_place?+
The type of an attribute is given the opportunity to change how dirty

==== Dirty Tracking

# => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
Product.where(price_in_bitcoins: Money.new(5, "GBP"))

# => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
Product.where(price_in_bitcoins: Money.new(5, "USD"))

end
attribute :price_in_bitcoins, :money, currency_converter: currency_converter
currency_converter = ConversionRatesFromTheInternet.new
class Product < ActiveRecord::Base
# app/models/product.rb

ActiveRecord::Type.register(:money, MoneyType)
# config/initializers/types.rb

end
end
value_in_bitcoins.amount
value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
def serialize(value)
# this case.
# +cast+. Assumed to be an instance of +Money+ in
# value will be the result of +deserialize+ or

end
@currency_converter = currency_converter
def initialize(currency_converter:)
class MoneyType < ActiveRecord::Type::Value

end
class Money < Struct.new(:amount, :currency)

calling +serialize+ on your type object. For example:
use the type defined by the model class to convert the value to SQL,
When {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where] is called, it will

==== \Querying

also pass a type object directly, in place of a symbol.
to be referenced by a symbol, see ActiveRecord::Type.register. You can
ActiveModel::Type::Value. For more details on registering your types
For more details on creating custom types, see the documentation for

store_listing.price_in_cents # => 1000
store_listing = StoreListing.new(price_in_cents: '$10.00')

end
attribute :price_in_cents, :money
class StoreListing < ActiveRecord::Base
# app/models/store_listing.rb

ActiveRecord::Type.register(:money, MoneyType)
# config/initializers/types.rb

end
end
end
super
else
super(price_in_dollars * 100)
price_in_dollars = value.gsub(/\$/, '').to_f
if !value.kind_of?(Numeric) && value.include?('$')
def cast(value)
class MoneyType < ActiveRecord::Type::Integer

existing type, or from ActiveRecord::Type::Value
expected API. It is recommended that your type objects inherit from an
database or from your controllers. See ActiveModel::Type::Value for the
+cast+ will be called on your type object, with raw input from the
to the methods defined on the value type. The method +deserialize+ or
Users may also define their own custom types, as long as they respond

==== Creating Custom Types

# => Error: 65537 is out of range for the limit of two bytes
MyModel.create(small_int: 65537)

end
attribute :small_int, :integer, limit: 2
class MyModel < ActiveRecord::Base
# app/models/my_model.rb

Passing options to the type constructor

}
my_float_range: 1.0..3.5
my_int_array: [1, 2, 3],
my_string: "string",
{
# =>
model.attributes
)
my_float_range: "[1,3.5]",
my_int_array: ["1", "2", "3"],
my_string: "string",
model = MyModel.new(

end
attribute :my_float_range, :float, range: true
attribute :my_int_array, :integer, array: true
attribute :my_string, :string
class MyModel < ActiveRecord::Base
# app/models/my_model.rb

\Attributes do not need to be backed by a database column.

Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600

end
attribute :my_default_proc, :datetime, default: -> { Time.now }
class Product < ActiveRecord::Base

StoreListing.new.my_string # => "new default"

end
attribute :my_string, :string, default: "new default"
class StoreListing < ActiveRecord::Base
# app/models/store_listing.rb

StoreListing.new.my_string # => "original default"

end
t.string :my_string, default: "original default"
create_table :store_listings, force: true do |t|
# db/schema.rb

A default can also be provided.

store_listing.price_in_cents # => 10
# after

end
attribute :price_in_cents, :integer
class StoreListing < ActiveRecord::Base

store_listing.price_in_cents # => BigDecimal(10.1)
# before

store_listing = StoreListing.new(price_in_cents: '10.1')

end
class StoreListing < ActiveRecord::Base
# app/models/store_listing.rb

end
t.decimal :price_in_cents
create_table :store_listings, force: true do |t|
# db/schema.rb

The type detected by Active Record can be overridden.

==== Examples

constructor of the type object.
When using a symbol for +cast_type+, extra options are forwarded to the

examples below).
+range+ (PostgreSQL only) specifies that the type should be a range (see the

examples below).
+array+ (PostgreSQL only) specifies that the type should be an array (see the

Otherwise, the default will be +nil+.
is not passed, the previous default value (if any) will be used.
+default+ The default value to use when no value is provided. If this option

The following options are accepted:

==== Options

information about providing custom type objects.
to be used for this attribute. See the examples below for more
+cast_type+ A symbol such as +:string+ or +:integer+, or a type object

column which this will persist to.
+name+ The name of the methods to define attribute methods for, and the

rely on implementation details or monkey patching.
your domain objects across much of Active Record, without having to
{ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]. This will let you use
changes the behavior of values passed to
values are converted to and from SQL when assigned to a model. It also
type of existing attributes if needed. This allows control over how
Defines an attribute with a type on this model. It will override the
def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
  name = name.to_s
  name = attribute_aliases[name] || name
  reload_schema_from_cache
  case cast_type
  when Symbol
    cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
  when nil
    if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
      default = prev_default if default == NO_DEFAULT_PROVIDED
    else
      prev_cast_type = -> subtype { subtype }
    end
    cast_type = if block_given?
      -> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
    else
      prev_cast_type
    end
  end
  self.attributes_to_define_after_schema_loads =
    attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
end

def define_attribute(

+cast+ or +deserialize+.
+user_provided_default+ Whether the default value should be cast using

will be called once each time a new value is needed.
Otherwise, the default will be +nil+. A proc can also be passed, and
is not passed, the previous default value (if any) will be used.
+default+ The default value to use when no value is provided. If this option

+cast_type+ The type object to use for this attribute.

+name+ The name of the attribute being defined. Expected to be a +String+.

should probably use ClassMethods#attribute.
is provided so it can be used by plugin authors, application code
ClassMethods#attribute both call this under the hood. While this method
waiting for the schema to load. Automatic schema detection and
accepts type objects, and will do its work immediately instead of
This is the low level API which sits beneath +attribute+. It only
def define_attribute(
  name,
  cast_type,
  default: NO_DEFAULT_PROVIDED,
  user_provided_default: true
)
  attribute_types[name] = cast_type
  define_default_attribute(name, default, cast_type, from_user: user_provided_default)
end

def define_default_attribute(name, value, type, from_user:)

def define_default_attribute(name, value, type, from_user:)
  if value == NO_DEFAULT_PROVIDED
    default_attribute = _default_attributes[name].with_type(type)
  elsif from_user
    default_attribute = ActiveModel::Attribute::UserProvidedDefault.new(
      name,
      value,
      type,
      _default_attributes.fetch(name.to_s) { nil },
    )
  else
    default_attribute = ActiveModel::Attribute.from_database(name, value, type)
  end
  _default_attributes[name] = default_attribute
end

def load_schema! # :nodoc:

:nodoc:
def load_schema! # :nodoc:
  super
  attributes_to_define_after_schema_loads.each do |name, (cast_type, default)|
    cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
    define_attribute(name, cast_type, default: default)
  end
end