Spec-Zone.ru › Ruby on Rails 8.1

module ActiveRecord::Attributes::ClassMethods

Атрибуты Active Record

Открытые методы экземпляра

attribute(name, cast_type = nil, **options) Показать исходный код
# File activerecord/lib/active_record/attributes.rb, line 14
      

Определяет атрибут с типом для этой модели. При необходимости он переопределит тип существующих атрибутов. Это позволяет контролировать, как значения преобразуются в SQL и обратно при присваивании модели. Это также изменяет поведение значений, передаваемых в ActiveRecord::Base.where. Благодаря этому вы сможете использовать объекты предметной области во многих частях Active Record, не полагаясь на детали реализации и не прибегая к monkey patching.

Параметры

name

Имя методов, для которых нужно определить методы атрибутов, и столбца, в который будет сохраняться значение.

cast_type

Символ, например :string или :integer, либо объект типа, который будет использоваться для этого атрибута. Если этот параметр не передан, будет использоваться ранее определённый тип (если он есть). В противном случае типом будет ActiveModel::Type::Value. Дополнительные сведения о передаче пользовательских объектов типов приведены в примерах ниже.

Параметры

:default

Значение по умолчанию, используемое, если значение не задано. Если этот параметр не передан, будет использоваться ранее определённое значение по умолчанию (если оно есть) из суперкласса или схемы. В противном случае значением по умолчанию будет nil.

:array

(Только для PostgreSQL) Указывает, что тип должен быть массивом. См. примеры ниже.

:range

(Только для PostgreSQL) Указывает, что тип должен быть диапазоном. См. примеры ниже.

При использовании символа для cast_type дополнительные параметры передаются конструктору объекта типа.

Примеры

Тип, определяемый Active Record, можно переопределить.

# 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(10.1)

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

# after
store_listing.price_in_cents # => 10

Также можно указать значение по умолчанию.

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

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

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

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

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

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

Атрибуты не обязательно должны соответствовать столбцу базы данных.

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

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

Передача параметров конструктору типа

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

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

Создание пользовательских типов

Пользователи также могут определять собственные пользовательские типы, если те реализуют методы, определённые для типа значения. Для объекта типа будет вызван метод deserialize или cast с исходными данными из базы данных или контроллеров. Ожидаемый API описан в ActiveModel::Type::Value. Рекомендуется, чтобы объекты типов наследовались от существующего типа или от ActiveRecord::Type::Value

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

# config/initializers/types.rb
ActiveRecord::Type.register(:price, PriceType)

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

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

Дополнительные сведения о создании пользовательских типов см. в документации по ActiveModel::Type::Value. Дополнительные сведения о регистрации типов, на которые можно ссылаться с помощью символа, см. в ActiveRecord::Type.register. Вместо символа также можно передать непосредственно объект типа.

Запросы

При вызове ActiveRecord::Base.where для преобразования значения в SQL будет использоваться тип, определённый классом модели; у объекта типа будет вызван метод serialize. Например:

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

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

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

# config/initializers/types.rb
ActiveRecord::Type.register(:price, PriceType)

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

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

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

Отслеживание изменений

Тип атрибута может влиять на отслеживание изменений. Методы changed? и changed_in_place? вызываются из ActiveModel::Dirty. Дополнительные сведения см. в документации по этим методам в ActiveModel::Type::Value.

define_attribute ( name, cast_type, default: NO_DEFAULT_PROVIDED, user_provided_default: true ) Показать исходный код
# File activerecord/lib/active_record/attributes.rb, line 243
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

Этот API принимает только объекты типов и выполняет свою работу немедленно, не дожидаясь загрузки схемы. Этот метод предоставлен для авторов плагинов, однако в коде приложения, вероятно, следует использовать ClassMethods#attribute.

Параметры

name

Имя определяемого атрибута. Ожидается, что это будет String.

cast_type

Объект типа, используемый для этого атрибута.

default

Значение по умолчанию, используемое, если значение не задано. Если этот параметр не передан, будет использоваться предыдущее значение по умолчанию (если оно есть). В противном случае значением по умолчанию будет nil. Также можно передать proc, который будет вызываться каждый раз, когда требуется новое значение.

user_provided_default

Определяет, следует ли приводить значение по умолчанию с помощью cast или deserialize.

type_for_attribute(attribute_name, &block) Показать исходный код
# File activerecord/lib/active_record/attributes.rb, line 269
      

См. ActiveModel::Attributes::ClassMethods#type_for_attribute.

При вызове этого метода будет выполнен доступ к базе данных и при необходимости загружена схема модели.

Защищённые методы экземпляра

reload_schema_from_cache (*) Показать исходный код
# File activerecord/lib/active_record/attributes.rb, line 281
def reload_schema_from_cache(*)
  reset_default_attributes!
  super
end
Вызывает метод суперкласса

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API