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.
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.
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.
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
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.
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.
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