Есть ли чистый API для сброса переменных экземпляра в 'reload' в ActiveRecord?

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

user = User.first
user.email #=> "[email protected]"
user.email = "[email protected]"
user.email #=> "[email protected]"
user.reload
user.email #=> "[email protected]"

Но если я добавлю настраиваемый атрибут, единственный способ, которым я обнаружил, что он действует одинаково, выглядит следующим образом:

class User < ActiveRecord::Base
  attr_accessor :user_agent

  def reload
    super
    self.user_agent = nil
    self
  end
end

Мой вопрос в том, есть ли какой-нибудь API, чтобы обновлять атрибуты без базы данных-столбца reset при перезагрузке? Что-то вроде:

class User < ActiveRecord::Base
  # this
  reloadable_attr_accessor :user_agent
  # or this
  def user_agent
    @user_agent
  end

  def user_agent=(value)
    set_instance_var_that_resets_on_reload("@user_agent", value)
  end
end

Это где-то существует в Rails?

Ответ 1

ActiveRecord не предоставляет способ сделать это, он может действовать только на атрибуты модели.

Говоря, я думаю, что более элегантным способом сделать это было бы перебирать иголки и устанавливать их в любое удобное для вас время:

class User < ActiveRecord::Base
  def reload(options = nil)
    super
    self.instance_variables.each do |ivar|
      next if ivar == '@attributes'
      self.instance_variable_set(ivar, nil)      
    end
  end
 end

Обратите внимание, что мы пропускаем @attributes, потому что AR заботится об этом, когда вы перезагружаете атрибуты.

Ответ 2

Погрешность Жан-До немного. Он не разбивает стандартные экземпляры экземпляра и отношения.

after_initialize do 
  @default_instance_variables = instance_variables
end

def reload(options = nil)
  super
  self.instance_variables.each do |ivar|
    if ivar == :'@default_instance_variables' || 
      @default_instance_variables.include?(ivar)
      next 
    end
    remove_instance_variable(ivar)
  end
  self
end

Ответ 3

Я принял gayavat ответ и переработал его в мой файл test_helper.rb, потому что я не хотел переопределять обычный метод #reload.

class ActiveRecord::Base
  attr_accessor :default_instance_variables
  after_initialize do 
    @default_instance_variables = instance_variables
  end
end
def reload_ivars(obj)
  obj.reload
  obj.instance_variables.each do |ivar|
    if ivar == :'@default_instance_variables' || 
     obj.default_instance_variables.include?(ivar)
      next 
    end
    obj.send(:remove_instance_variable, ivar)
  end
end

Когда мне нужно перезагрузить что-то в тесте, я просто вызываю reload_ivars(object).