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

问题描述:

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

我看过的大多数宝石只是阻止你做任何修改。但是我想修改,只是将修改保存到记录副本中。理论上这应该包括Rails中的任何更新方式,例如update_attributessave也应该返回记录的新副本。

+0

你想在你的应用程序中有一种更新历史记录?如果是这样看看paper_trail创业板,https://github.com/airblade/paper_trail – rmagnum2002 2014-12-08 09:06:24

+0

@ rmagnum2002没有不是真的。我想使现有的关联不受影响,这意味着我想保持现有记录不受影响 – lulalala 2014-12-08 09:12:09

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

为了简单起见,我通过只读方式阻止对内置保存功能的访问。

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

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 

我想我可以通过重写保存/保存!/ update_attributes方法方法是比较含蓄。然而,由于这不是正常的行为,我选择了明确的并在此停止。如果任何人有更通用的解决方案,请将其发布。

您可能会利用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

+0

谢谢,但如果解决方案在ActiveModel级别而不是控制器级别实现,则会更好。 – lulalala 2014-12-08 09:19:52

+0

同意,这将是.. :) Might做一些其他的SO用户会分享一个更好的主意。想自己知道。 – rmagnum2002 2014-12-08 09:21:57

我还没有听说过具体的任务宝石,但在早些时候的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对。