mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
4010a9ddc6
Nothing is directly using the columns for the default values anymore. This step helps us get closer not not mutating the columns hash.
139 lines
4.6 KiB
Ruby
139 lines
4.6 KiB
Ruby
module ActiveRecord
|
|
module Attributes # :nodoc:
|
|
extend ActiveSupport::Concern
|
|
|
|
Type = ActiveRecord::Type
|
|
|
|
included do
|
|
class_attribute :user_provided_columns, instance_accessor: false # :internal:
|
|
class_attribute :user_provided_defaults, instance_accessor: false # :internal:
|
|
self.user_provided_columns = {}
|
|
self.user_provided_defaults = {}
|
|
end
|
|
|
|
module ClassMethods # :nodoc:
|
|
# Defines or overrides a attribute on this model. This allows customization of
|
|
# Active Record's type casting behavior, as well as adding support for user defined
|
|
# types.
|
|
#
|
|
# +name+ The name of the methods to define attribute methods for, and the column which
|
|
# this will persist to.
|
|
#
|
|
# +cast_type+ A type object that contains information about how to type cast the value.
|
|
# See the examples section for more information.
|
|
#
|
|
# ==== Options
|
|
# The options hash accepts the following options:
|
|
#
|
|
# +default+ is the default value that the column should use on a new record.
|
|
#
|
|
# ==== Examples
|
|
#
|
|
# The type detected by Active Record can be overridden.
|
|
#
|
|
# # 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
|
|
# attribute :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::Attributes::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
|
|
# attribute :price_in_cents, MoneyType.new
|
|
# end
|
|
#
|
|
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
|
# store_listing.price_in_cents # => 1000
|
|
def attribute(name, cast_type, options = {})
|
|
name = name.to_s
|
|
clear_caches_calculated_from_columns
|
|
# Assign a new hash to ensure that subclasses do not share a hash
|
|
self.user_provided_columns = user_provided_columns.merge(name => cast_type)
|
|
|
|
if options.key?(:default)
|
|
self.user_provided_defaults = user_provided_defaults.merge(name => options[:default])
|
|
end
|
|
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_caches_calculated_from_columns
|
|
end
|
|
|
|
private
|
|
|
|
def add_user_provided_columns(schema_columns)
|
|
existing_columns = schema_columns.map do |column|
|
|
new_type = user_provided_columns[column.name]
|
|
if new_type
|
|
column.with_type(new_type)
|
|
else
|
|
column
|
|
end
|
|
end
|
|
|
|
existing_column_names = existing_columns.map(&:name)
|
|
new_columns = user_provided_columns.except(*existing_column_names).map do |(name, type)|
|
|
connection.new_column(name, nil, type)
|
|
end
|
|
|
|
existing_columns + new_columns
|
|
end
|
|
|
|
def clear_caches_calculated_from_columns
|
|
@attributes_builder = nil
|
|
@column_names = nil
|
|
@column_types = nil
|
|
@columns = nil
|
|
@columns_hash = nil
|
|
@content_columns = nil
|
|
@default_attributes = nil
|
|
end
|
|
|
|
def raw_default_values
|
|
super.merge(user_provided_defaults)
|
|
end
|
|
end
|
|
end
|
|
end
|