mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
2eb547a4d9
Many of the methods defined in `AttributeMethods::Serialization` can be refactored onto this type as well, but this is a reasonable small step. Removes the `Type` class, and the need for `decorate_columns` to handle serialized types.
106 lines
3.4 KiB
Ruby
106 lines
3.4 KiB
Ruby
module ActiveRecord
|
|
module Properties # :nodoc:
|
|
extend ActiveSupport::Concern
|
|
|
|
Type = ActiveRecord::Type
|
|
|
|
included do
|
|
class_attribute :user_provided_columns, instance_accessor: false # :internal
|
|
self.user_provided_columns = {}
|
|
end
|
|
|
|
module ClassMethods
|
|
# Defines or overrides a property on this model. This allows customization of
|
|
# Active Record's type casting behavior, as well as adding support for user defined
|
|
# types.
|
|
#
|
|
# ==== Examples
|
|
#
|
|
# The type detected by Active Record can be overriden.
|
|
#
|
|
# # db/schema.rb
|
|
# create_table :store_listings, force: true do |t|
|
|
# t.decimal :price_in_cents
|
|
# end
|
|
#
|
|
# # app/models/store_listing.rb
|
|
# class StoreListing < ActiveRecord::Base
|
|
# end
|
|
#
|
|
# store_listing = StoreListing.new(price_in_cents: '10.1')
|
|
#
|
|
# # before
|
|
# store_listing.price_in_cents # => BigDecimal.new(10.1)
|
|
#
|
|
# class StoreListing < ActiveRecord::Base
|
|
# property :price_in_cents, Type::Integer.new
|
|
# end
|
|
#
|
|
# # after
|
|
# store_listing.price_in_cents # => 10
|
|
#
|
|
# Users may also define their own custom types, as long as they respond to the methods
|
|
# defined on the value type. The `type_cast` method on your type object will be called
|
|
# with values both from the database, and from your controllers. See
|
|
# `ActiveRecord::Properties::Type::Value` for the expected API. It is recommended that your
|
|
# type objects inherit from an existing type, or the base value type.
|
|
#
|
|
# class MoneyType < ActiveRecord::Type::Integer
|
|
# def type_cast(value)
|
|
# if value.include?('$')
|
|
# price_in_dollars = value.gsub(/\$/, '').to_f
|
|
# price_in_dollars * 100
|
|
# else
|
|
# value.to_i
|
|
# end
|
|
# end
|
|
# end
|
|
#
|
|
# class StoreListing < ActiveRecord::Base
|
|
# property :price_in_cents, MoneyType.new
|
|
# end
|
|
#
|
|
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
|
# store_listing.price_in_cents # => 1000
|
|
def property(name, cast_type)
|
|
name = name.to_s
|
|
clear_properties_cache
|
|
# Assign a new hash to ensure that subclasses do not share a hash
|
|
self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, nil, cast_type))
|
|
end
|
|
|
|
# Returns an array of column objects for the table associated with this class.
|
|
def columns
|
|
@columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name))
|
|
end
|
|
|
|
# Returns a hash of column objects for the table associated with this class.
|
|
def columns_hash
|
|
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
|
end
|
|
|
|
def reset_column_information # :nodoc:
|
|
super
|
|
clear_properties_cache
|
|
end
|
|
|
|
private
|
|
|
|
def add_user_provided_columns(schema_columns)
|
|
existing_columns = schema_columns.map do |column|
|
|
user_provided_columns[column.name] || column
|
|
end
|
|
|
|
existing_column_names = existing_columns.map(&:name)
|
|
new_columns = user_provided_columns.except(*existing_column_names).values
|
|
|
|
existing_columns + new_columns
|
|
end
|
|
|
|
def clear_properties_cache
|
|
@columns = nil
|
|
@columns_hash = nil
|
|
end
|
|
end
|
|
end
|
|
end
|