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