[RUBY] [Rails] ActiveRecord :: Attributes :: ClassMethods

Einführung

Lesen Sie bei Unklarheiten die Rails-Dokumentation sorgfältig durch.

Folgen Sie Rails ActiveRecord :: Attributes :: ClassMethods Documentation.

Wenn Sie kürzlich ein Formularobjekt erstellen, erhalten Sie ein besseres Verständnis für "Attribut", das in mir ein mehrdeutiges Verhalten aufweist.

Es war eine Erkenntnis, dass der Typ von "attr_accessor" angegeben werden konnte.

Überblick

attribute(name, cast_type = Type::Value.new, **options)

Definiert ein Attribut mit einem Modelltyp. Überschreiben Sie vorhandene Attributtypen nach Bedarf. Auf diese Weise können Sie steuern, wie Werte in und aus SQL konvertiert werden, wenn sie einem Modell zugewiesen werden. Es ändert auch das Verhalten des an "ActiveRecord :: Base.where" übergebenen Werts. Auf diese Weise können die meisten Active Record Domänenobjekte verwenden, ohne auf Implementierungsdetails oder Affen-Patches zurückgreifen zu müssen.

name: Der Name der Methode, die die Attributmethode definiert, und die Spalte, die beibehalten werden soll.

cast_type: Ein Symbol wie: string oder: integer oder ein Typobjekt, das für dieses Attribut verwendet wird. Im folgenden Beispiel finden Sie weitere Informationen zum Bereitstellen von benutzerdefinierten Typobjekten.

Möglichkeit

Die folgenden Optionen stehen zur Verfügung.

default: Der Standardwert, der verwendet wird, wenn kein Wert angegeben wird. Wenn diese Option nicht angegeben ist, wird der vorherige Standardwert (?) Verwendet, falls vorhanden, andernfalls ist der Standardwert "Null".

array (nur PostgreSQL): Gibt an, dass der Typ array ist.

range (nur PostgreSQL): Gibt an, dass der Typ range ist.

Durch die Verwendung des Symbols "cast_type" werden zusätzliche Optionen an den Konstruktor des Typobjekts übergeben.

Beispiel

Die von ActiveRecord gefundenen Typen können überschrieben werden.

# 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

In einigen Fällen wird der Standardwert übergeben.

# db/schema.rb
create_table :store_listings, force: true do |t|
  t.decimal :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

Attribute müssen nicht auf Datenbankspalten basieren.

# 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
  }

Wenn Sie eine Option an den Typkonstruktor übergeben

# 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

Versuchen Sie, einen benutzerdefinierten Typ zu erstellen

Der Benutzer kann auch benutzerdefinierte Typen definieren, sofern es sich um im Wertetyp definierte Methoden handelt. Die Methoden "deserialize" und "cast" werden von ihrem eigenen Typobjekt unter Verwendung von Roheingaben aus der Datenbank oder dem Controller aufgerufen. Die erwartete API finden Sie unter "ActiveModel :: Type :: Value". Es wird empfohlen, dass das Typobjekt einen vorhandenen Typ oder "ActiveRecord :: Type :: Value" erbt.

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

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

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

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

Weitere Informationen zu benutzerdefinierten Typen finden Sie in der Dokumentation zu ActiveModel :: Type :: Value. In der Dokumentation zu ActiveRecord :: Type.register finden Sie die eindeutigen Typen, auf die durch Symbole verwiesen wird. Sie können ein Typobjekt auch direkt anstelle eines Symbols übergeben.

Abfrage

Wenn "ActiveRecord :: Base.where" aufgerufen wird, verwendet es den von der Modellklasse definierten Typ und konvertiert ihn in einen SQL-Wert, indem die "serialize" -Methode für ein eigenes Typobjekt aufgerufen wird.

Beispiel

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

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

  # value will be the result of +deserialiez+ 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_bitcoints.amount
  end
end

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

# app/models/product.rb
class Product < ActiveRecord::Base
  currency_converter = ConversionRatesFromTheInternet.new
  attribute :price_in_bitcoins, :money, 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

Dirty Tracking

Der Typ des Attributs kann auch das Verhalten von Dirty Tracking ändern. Die Methoden change? Unddefined_in_pace?Werden von der Klasse ActiveModel :: Dirty aufgerufen. Weitere Informationen finden Sie in den Methoden der Klasse "ActiveModel :: Type :: Value".

# File activerecord/lib/active_record/attributes.rb, line 208
def attribute(name, cast_type = Type::Value.new, **options)
  name = name.to_s
  reload_schema_from_cache

  self.attributes_to_define_after_schema_loads = 
    attributes_to_define_after_schema_loads.merge(
      name => [cast_type, options]
    )
end

define_attribute( name, cast_type, default: NO_DEFAULT_PROVIDED, user_provided_default: true )

Dies ist eine API, die sich unter dem Attribut "Low Level" befindet. Akzeptiert nur Typobjekte und erledigt die Arbeit sofort, anstatt auf das Laden des Schemas zu warten. Sowohl die automatische Schemaerkennung als auch das Attribut "ClassMethods #" rufen dies intern auf. Diese Methode wird vom Ersteller des Plug-Ins zur Verfügung gestellt, aber Sie müssen wahrscheinlich das Attribut "ClassMethods #" in Ihrem Anwendungscode verwenden.

name: Der Name des zu definierenden Attributs. Wird voraussichtlich "String" sein.

cast_type: Das für dieses Attribut verwendete Typobjekt.

default: Der Standardwert, der verwendet wird, wenn kein Wert angegeben wird. Wenn diese Option nicht angegeben ist, wird der vorherige Standardwert (?) Verwendet, falls vorhanden, andernfalls ist der Standardwert "Null". Prozeduren können auch übergeben werden und werden aufgerufen, wenn neue Werte benötigt werden.

user_provided_default: Der Standardwert wird mit der Methode cast oder deserialize umgewandelt.

# File activerecord/lib/actvie_record/attributes.rb, line 236
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

Zusammenfassung

Recommended Posts

[Rails] ActiveRecord :: Attributes :: ClassMethods
Lösen Sie ActiveRecord :: NoDatabaseError auf Rails6 auf
Rails 5 Code Reading Teil 1 ~ ActiveRecord neue Methode ~
[Rails] Verwendung von ActiveRecord :: Bitemporal (BiTemporalDataModel)