Spec-Zone.ru › Ruby on Rails 7.2

module ActiveRecord::Persistence

Наследование Active Record

Публичные методы экземпляра

becomes(klass) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 489
def becomes(klass)
  became = klass.allocate

  became.send(:initialize) do |becoming|
    @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
    becoming.instance_variable_set(:@attributes, @attributes)
    becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
    becoming.instance_variable_set(:@new_record, new_record?)
    becoming.instance_variable_set(:@destroyed, destroyed?)
    becoming.errors.copy!(errors)
  end

  became
end

Возвращает экземпляр указанного klass с атрибутами текущей записи. Это в основном полезно в отношении структур наследования одной таблицы (STI), где вы хотите, чтобы подкласс отображался как суперкласс. Это может использоваться вместе с идентификацией записи в Action Pack, чтобы позволить, скажем, Client < Company сделать что-то вроде render partial: @client.becomes(Company), чтобы отобразить этот экземпляр, используя companies/company partial вместо clients/client.

Примечание: новый экземпляр будет совместно использовать ссылку на те же атрибуты, что и исходный класс. Поэтому значение столбца STI останется прежним. Любое изменение атрибутов в любом из экземпляров повлияет на оба экземпляра. Это включает в себя любую инициализацию атрибутов, выполненную новым экземпляром.

Если вы также хотите изменить столбец STI, используйте becomes! вместо этого.

becomes!(klass) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 510
def becomes!(klass)
  became = becomes(klass)
  sti_type = nil
  if !klass.descends_from_active_record?
    sti_type = klass.sti_name
  end
  became.public_send("#{klass.inheritance_column}=", sti_type)
  became
end

Обёртка вокруг becomes, которая также изменяет значение столбца STI экземпляра. Это особенно полезно, если вы хотите сохранить изменённый класс в вашей базе данных.

Примечание: значение столбца STI старого экземпляра также будет изменено, поскольку оба объекта используют один и тот же набор атрибутов.

decrement(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 657
def decrement(attribute, by = 1)
  increment(attribute, -by)
end

Инициализирует attribute в ноль, если nil, и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно на базовом атрибуте, setter не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.

decrement!(attribute, by = 1, touch: nil) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 667
def decrement!(attribute, by = 1, touch: nil)
  increment!(attribute, -by, touch: touch)
end

Обёртка вокруг decrement, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие изменённые атрибуты останутся изменёнными. Validations и обратные вызовы пропускаются. Поддерживает опцию touch из update_counters, см. подробнее. Возвращает self.

delete() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 441
def delete
  _delete_row if persisted?
  @destroyed = true
  @previously_new_record = false
  freeze
end

Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны быть внесены (поскольку они не могут быть сохранены). Возвращает замороженный экземпляр.

Строка просто удаляется с помощью оператора SQL DELETE по первичному ключу записи, и обратные вызовы не выполняются.

Обратите внимание, что это также удалит записи, помеченные как #readonly?.

Чтобы обеспечить выполнение обратных вызовов before_destroy и after_destroy объекта или любых параметров ассоциации :dependent, используйте destroy.

destroy() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 455
def destroy
  _raise_readonly_record_error if readonly?
  destroy_associations
  @_trigger_destroy_callback ||= persisted? && destroy_row > 0
  @destroyed = true
  @previously_new_record = false
  freeze
end

Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны быть внесены (поскольку они не могут быть сохранены).

Существует ряд обратных вызовов, связанных с destroy. Если обратный вызов before_destroy вызывает :abort, действие отменяется, и destroy возвращает false. См. ActiveRecord::Callbacks для получения дополнительной информации.

destroy!() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 471
def destroy!
  destroy || _raise_record_not_destroyed
end

Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны быть внесены (поскольку они не могут быть сохранены).

Существует ряд обратных вызовов, связанных с destroy!. Если обратный вызов before_destroy вызывает :abort, действие отменяется, и destroy! вызывает ActiveRecord::RecordNotDestroyed. См. ActiveRecord::Callbacks для получения дополнительной информации.

destroyed?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 357
def destroyed?
  @destroyed
end

Возвращает true, если этот объект был уничтожен, в противном случае возвращает false.

increment(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 634
def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end

Инициализирует attribute в ноль, если nil, и добавляет значение, переданное как by (по умолчанию 1). Приращение выполняется непосредственно на базовом атрибуте, setter не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.

increment!(attribute, by = 1, touch: nil) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 646
def increment!(attribute, by = 1, touch: nil)
  increment(attribute, by)
  change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
  self.class.update_counters(id, attribute => change, touch: touch)
  public_send(:"clear_#{attribute}_change")
  self
end

Обёртка вокруг increment, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие изменённые атрибуты останутся изменёнными. Validations и обратные вызовы пропускаются. Поддерживает опцию touch из update_counters, см. подробнее. Возвращает self.

new_record?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 340
def new_record?
  @new_record
end

Возвращает true, если этот объект ещё не сохранён – то есть запись для объекта ещё не существует в базе данных; в противном случае возвращает false.

persisted?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 363
def persisted?
  !(@new_record || @destroyed)
end

Возвращает true, если запись сохранена, т. е. это не новая запись и она не была уничтожена, в противном случае возвращает false.

previously_new_record?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 347
def previously_new_record?
  @previously_new_record
end

Возвращает true, если этот объект только что был создан – то есть до последнего обновления или удаления объект не существовал в базе данных, и new_record? вернул бы true.

previously_persisted?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 352
def previously_persisted?
  !new_record? && destroyed?
end

Возвращает true, если этот объект был ранее сохранён, но теперь он был удалён.

reload(options = nil) Show source
# File activerecord/lib/active_record/persistence.rb, line 744
def reload(options = nil)
  self.class.connection_pool.clear_query_cache

  fresh_object = if apply_scoping?(options)
    _find_record((options || {}).merge(all_queries: true))
  else
    self.class.unscoped { _find_record(options) }
  end

  @association_cache = fresh_object.instance_variable_get(:@association_cache)
  @association_cache.each_value { |association| association.owner = self }
  @attributes = fresh_object.instance_variable_get(:@attributes)
  @new_record = false
  @previously_new_record = false
  self
end

Перезагружает запись из базы данных.

Этот метод находит запись по её первичному ключу (который может быть назначен вручную) и изменяет получатель на месте:

account = Account.new
# => #<Account id: nil, email: nil>
account.id = 1
account.reload
# Account Load (1.2ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1  [["id", 1]]
# => #<Account id: 1, email: 'account@example.com'>

Attributes перезагружаются из базы данных, и кеш очищается, в частности кеш ассоциаций и QueryCache.

Если запись больше не существует в базе данных, возникает исключение ActiveRecord::RecordNotFound. В противном случае, в дополнение к изменению на месте, метод возвращает self для удобства.

Необязательный флаг параметра :lock позволяет заблокировать перезагруженную запись:

reload(lock: true) # reload with pessimistic locking

Перезагрузка обычно используется в тестовых наборах для проверки того, что данные действительно записаны в базу данных, или когда какое-то действие изменяет соответствующую строку в базе данных, но не объект в памяти:

assert account.deposit!(25)
assert_equal 25, account.credit        # check it is updated in memory
assert_equal 25, account.reload.credit # check it is also persisted

Другой распространенный случай использования — обработка оптимистичной блокировки:

def with_optimistic_retry
  begin
    yield
  rescue ActiveRecord::StaleObjectError
    begin
      # Reload lock_version in particular.
      reload
    rescue ActiveRecord::RecordNotFound
      # If the record is gone there is nothing to do.
    else
      retry
    end
  end
end
save(**options) Show source
# File activerecord/lib/active_record/persistence.rb, line 392
def save(**options, &block)
  create_or_update(**options, &block)
rescue ActiveRecord::RecordInvalid
  false
end

Сохраняет модель.

Если модель новая, запись создается в базе данных, в противном случае существующая запись обновляется.

По умолчанию save всегда запускает валидацию. Если какая-либо из проверок не пройдена, действие отменяется, и save возвращает false, и запись не будет сохранена. Однако, если вы предоставите validate: false, проверка будет полностью пропущена. См. ActiveRecord::Validations для получения дополнительной информации.

По умолчанию, save также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы предоставите touch: false, эти метки времени не будут обновлены.

Существует ряд обратных вызовов, связанных с save. Если какой-либо из обратных вызовов before_* вызывает исключение :abort, действие отменяется, и save возвращает false. См. ActiveRecord::Callbacks для получения более подробной информации.

Attributes, помеченные как доступные только для чтения, игнорируются, если запись обновляется.

save!(**options) Show source
# File activerecord/lib/active_record/persistence.rb, line 425
def save!(**options, &block)
  create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end

Сохраняет модель.

Если модель новая, запись создается в базе данных, в противном случае существующая запись обновляется.

По умолчанию, save! всегда запускает валидацию. Если какая-либо из проверок не пройдена, возникает исключение ActiveRecord::RecordInvalid, и запись не будет сохранена. Однако, если вы предоставите validate: false, проверка будет полностью пропущена. См. ActiveRecord::Validations для получения дополнительной информации.

По умолчанию, save! также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы предоставите touch: false, эти метки времени не будут обновлены.

Существует ряд обратных вызовов, связанных с save!. Если какой-либо из обратных вызовов before_* вызывает исключение :abort, действие отменяется, и save! вызывает исключение ActiveRecord::RecordNotSaved. См. ActiveRecord::Callbacks для получения более подробной информации.

Attributes, помеченные как доступные только для чтения, игнорируются, если запись обновляется.

Если исключение не вызвано, возвращает true.

toggle(attribute) Show source
# File activerecord/lib/active_record/persistence.rb, line 683
def toggle(attribute)
  self[attribute] = !public_send("#{attribute}?")
  self
end

Присваивает attribute булево значение, противоположное attribute?. Таким образом, если предикат возвращает true, атрибут станет false. Этот метод переключает непосредственно базовое значение без вызова любого сеттера. Возвращает self.

Пример:

user = User.first
user.banned? # => false
user.toggle(:banned)
user.banned? # => true
toggle!(attribute) Show source
# File activerecord/lib/active_record/persistence.rb, line 692
def toggle!(attribute)
  toggle(attribute).update_attribute(attribute, self[attribute])
end

Обертка вокруг toggle, которая сохраняет запись. Этот метод отличается от своей версии без восклицательного знака тем, что он проходит через установщик атрибута. Сохранение не подвергается проверкам валидации. Возвращает true, если запись может быть сохранена.

touch(*names, time: nil) Show source
# File activerecord/lib/active_record/persistence.rb, line 795
def touch(*names, time: nil)
  _raise_record_not_touched_error unless persisted?
  _raise_readonly_record_error if readonly?

  attribute_names = timestamp_attributes_for_update_in_model
  attribute_names = (attribute_names | names).map! do |name|
    name = name.to_s
    name = self.class.attribute_aliases[name] || name
    verify_readonly_attribute(name)
    name
  end

  unless attribute_names.empty?
    affected_rows = _touch_row(attribute_names, time)
    @_trigger_update_callback = affected_rows == 1
  else
    true
  end
end

Сохраняет запись с атрибутами updated_at/on, установленными на текущее время или указанное время. Обратите внимание, что валидация не выполняется, и выполняются только обратные вызовы after_touch, after_commit и after_rollback.

Этому методу могут быть переданы имена атрибутов и необязательный аргумент времени. Если передаются имена атрибутов, они обновляются вместе с атрибутами updated_at/on. Если аргумент времени не передан, по умолчанию используется текущее время.

product.touch                         # updates updated_at/on with current time
product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
product.touch(:designed_at)           # updates the designed_at attribute and updated_at/on
product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes

Если используется вместе с belongs_to, то touch вызовет метод touch на связанном объекте.

class Brake < ActiveRecord::Base
  belongs_to :car, touch: true
end

class Car < ActiveRecord::Base
  belongs_to :corporation, touch: true
end

# triggers @brake.car.touch and @brake.car.corporation.touch
@brake.touch

Обратите внимание, что touch должен использоваться с сохраненным объектом, иначе будет выброшено исключение ActiveRecordError. Например:

ball = Ball.new
ball.touch(:updated_at)   # => raises ActiveRecordError
update(attributes) Show source
# File activerecord/lib/active_record/persistence.rb, line 565
def update(attributes)
  # The following transaction covers any possible database side-effects of the
  # attributes assignment. For example, setting the IDs of a child collection.
  with_transaction_returning_status do
    assign_attributes(attributes)
    save
  end
end

Обновляет атрибуты модели из переданного хэша и сохраняет запись, все это заключено в транзакцию. Если объект недействителен, сохранение завершится неудачей и будет возвращено false.

update!(attributes) Show source
# File activerecord/lib/active_record/persistence.rb, line 576
def update!(attributes)
  # The following transaction covers any possible database side-effects of the
  # attributes assignment. For example, setting the IDs of a child collection.
  with_transaction_returning_status do
    assign_attributes(attributes)
    save!
  end
end

Обновляет свой получатель так же, как update, но вызывает save! вместо save, поэтому возникает исключение, если запись недействительна, и сохранение завершится неудачей.

update_attribute(name, value) Show source
# File activerecord/lib/active_record/persistence.rb, line 532
def update_attribute(name, value)
  name = name.to_s
  verify_readonly_attribute(name)
  public_send("#{name}=", value)

  save(validate: false)
end

Обновляет один атрибут и сохраняет запись. Это особенно полезно для булевых флагов в существующих записях. Также обратите внимание, что

  • Валидация пропускается.

  • Вызываются обратные вызовы.

  • Столбец updated_at/updated_on обновляется, если этот столбец доступен.

  • Обновляет все атрибуты, которые являются измененными в этом объекте.

Этот метод вызывает исключение ActiveRecord::ActiveRecordError, если атрибут помечен как доступный только для чтения.

См. также update_column.

update_attribute!(name, value) Show source
# File activerecord/lib/active_record/persistence.rb, line 554
def update_attribute!(name, value)
  name = name.to_s
  verify_readonly_attribute(name)
  public_send("#{name}=", value)

  save!(validate: false)
end

Обновляет один атрибут и сохраняет запись. Это особенно полезно для булевых флагов в существующих записях. Также обратите внимание, что

  • Валидация пропускается.

  • Вызываются обратные вызовы.

  • Столбец updated_at/updated_on обновляется, если этот столбец доступен.

  • Обновляет все атрибуты, которые являются измененными в этом объекте.

Этот метод вызывает исключение ActiveRecord::ActiveRecordError, если атрибут помечен как доступный только для чтения.

Если какой-либо из обратных вызовов before_* вызывает исключение :abort, действие отменяется, и update_attribute! вызывает исключение ActiveRecord::RecordNotSaved. См. ActiveRecord::Callbacks для получения более подробной информации.

update_column(name, value) Show source
# File activerecord/lib/active_record/persistence.rb, line 586
def update_column(name, value)
  update_columns(name => value)
end

Эквивалентно update_columns(name => value).

update_columns(attributes) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 606
def update_columns(attributes)
  raise ActiveRecordError, "cannot update a new record" if new_record?
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
  _raise_readonly_record_error if readonly?

  attributes = attributes.transform_keys do |key|
    name = key.to_s
    name = self.class.attribute_aliases[name] || name
    verify_readonly_attribute(name) || name
  end

  update_constraints = _query_constraints_hash
  attributes = attributes.each_with_object({}) do |(k, v), h|
    h[k] = @attributes.write_cast_value(k, v)
    clear_attribute_change(k)
  end

  affected_rows = self.class._update_record(
    attributes,
    update_constraints
  )

  affected_rows == 1
end

Обновляет атрибуты напрямую в базе данных, выполняя оператор UPDATE SQL, и устанавливает их в получателе:

user.update_columns(last_request_at: Time.current)

Это самый быстрый способ обновления атрибутов, поскольку он идет напрямую в базу данных, но следует учитывать, что в результате стандартные процедуры обновления полностью проигнорированы. В частности:

  • Проверка не выполняется.

  • Обработчики не вызываются.

  • updated_at/updated_on не обновляются.

  • Однако, атрибуты сериализуются по тем же правилам, что и ActiveRecord::Relation#update_all

Этот метод вызывает исключение ActiveRecord::ActiveRecordError, если он вызван для новых объектов или если хотя бы один из атрибутов помечен как только для чтения.

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

Spec-Zone.ru

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