保存更改时克隆自己的不可变记录



我想要一个模型,如果我对其进行更改并保存,它将另存为新记录。这就像不可变字符串的想法。我想知道是否有涵盖此领域的现有 gem/教程/最佳实践?

我看过的大多数宝石只是阻止你进行任何修改。但是我想修改,只是将修改保存到记录的副本中。从理论上讲,这应该涵盖 Rails 中的任何更新方式,例如 update_attributes ,并且save也应该返回新的记录副本。

我还没有听说过特定的任务宝石,但在早期的 Rails 中,有只读记录,并且尝试更新该记录被例外地引用。在现代版本的 Rails 中,这种记录已被弃用,并且没有可以为关系设置只读属性。但是,您可以创建一个拒绝更新记录的模型,但在控制器中,您可以捕获该特定异常,并尝试基于拒绝的记录创建新记录,如下所示:

class ReadonlyModelException < ActiveRecord::ActiveRecordError; end
class ReadonlyModel < ActiveRecord::Base
   before_update { |_| raise ReadonlyModelException }
end
class User < ReadonlyModel; end

在控制器中,只需捕获异常并更改行为。

def update
   user = User.where(id: params[:id]).first
   user.update_attributes(name: "New name") # => ReadonlyModelException
rescue ReadonlyModelException
   redirect :create
end

注意:但在我看来,更好的方法是不要对特定对型号/控制器使用 #update 操作,而只对#new/#create

您可能可以使用 rails changed?方法。基于User模型的示例:

def update
  @object = YourClass.find(params[:id])
  if @object.changed?
    # Create new object
  else
    # Action if no change has been made
  end
end

changed? 方法将检查模型的任何属性是否已更改,您希望指定要使用的确切属性object.attributename_changed?

[10] pry(main)> u = User.last
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."type" IN ('User', 'AnonymousUser', 'DeletedUser') ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 5, login: "Carson725", firstname: "Hillard", lastname: "VonRueden">
[11] pry(main)> u.lastname = 'Someother'
=> "Sergiu"
[12] pry(main)> u.changed?
=> true
[13] pry(main)> u.firstname_changed?
=> false
[14] pry(main)> u.lastname_changed?
=> true
[15] pry(main)> 

红宝石 2.1.5p273/导轨 3.2.21

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

所以最后我把以下内容放到我的模型中。

为了简单起见,我通过将内置保存函数设置为只读来阻止对内置保存函数的访问。

然后我有一个保存修改的方法。将保存副本,但会更新对该副本的选定引用。例如,当我更新价格对象时,我还使用该价格更新现有产品以指向新的价格记录。虽然订单仍将引用旧记录。

  def readonly?
    persisted?
  end
  def save_as_clone
      return self if ! changed?
      copy = self.dup
      ActiveRecord::Base.transaction do
        copy.save!
        if copy.persisted?
          # Update existing references
          self.products.update_all(price_id:copy.id)
        end
      end
      copy
  end
  def destroy
    # remove mapping only but do not destroy self
  end
  def delete
    # remove mapping only but do not destroy self
  end

我想我可以通过覆盖 save/save!/update_attributes 方法来使其更加隐式。但是,由于这不是正常行为,我选择明确并停在这里。如果有人有更通用的解决方案,也请发布。

最新更新