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

时间:2014-12-08 08:59:26

标签: ruby-on-rails gem

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

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

3 个答案:

答案 0 :(得分:0)

您可能可以使用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)> 

ruby​​ 2.1.5p273 / Rails 3.2.21

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

答案 1 :(得分:0)

我还没有听说过具体的任务宝石,但在早期的Rails中只有readonly记录,并且更新的尝试被异常更新。在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对。

答案 2 :(得分:0)

所以最后我将以下内容放入我的模型中。

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

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

  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方法使其更加隐含。然而,由于这不是正常的行为,我选择明确并停在这里。如果有人有更一般的解决方案,请发布。