rails 4 和 rails 5 相同代码的区别

Difference in rails 4 and rails 5 same code acting differently

我的 rails 4 中有这个 class,工作得很好 :

class Rule < ActiveModelSerializers::Model
  # Required by ActiveModelSerializers in order to seralize this object
  # @return [Hash] with all the attributes accessible for serialization
  def attributes
   ...some attributes...
  end

  def initialize(args = {})
    super
    @some_custom_variable = ...something...
  end
  # This method is relevant
  def assign_attributes(args)
    args.each { |k, v| instance_variable_set("@#{k}", v) }
    some_custom_variable.assign_attributes(args)
  end
end

此 class 是从控制器创建操作中调用的,如下所示:

rule = Rule.new(permitted_params.symbolize_keys)
rule.save

我遇到的问题是 assign_attributes 在执行控制器代码时被调用(我知道这一点是因为我在里面放置了断点),这只是发生在 rails 5.

相同的代码,控制器中相同的参数(由相同的测试生成)表现不同。使用 rails 5,代码永远不会在 assign_attributes 方法中结束。

我的问题是为什么 rails 5 会这样?为什么 assign_attributes 会被触发?

根据Rails 4 and Rails 5 docs两者的说法,Rails本身是否会使用assign_attributes没有任何意义。只是它是一种可用于一次分配一堆属性的方法。

Rails 4

Allows you to set all the attributes by passing in a hash of attributes with keys matching the attribute names (which again matches the column names).

Rails 5

Allows you to set all the attributes by passing in a hash of attributes with keys matching the attribute names.

Rule.new 调用或不调用 Rule#assign_attributes 是实现的一个怪癖。没有记录 new 会调用 assign_attributes 所以你不能依赖 Rails 什么时候会调用它。

此外,如果您覆盖了 assign_attributes,请务必继续按照记录的方式执行操作。单独设置每个属性和一次设置所有属性之间应该没有功能上的区别。看来您没有调用 super 而是编写了自己的批量分配代码,并添加了自己的额外代码以将属性镜像到另一个对象。

还有一个问题是 ActiveModelSerializers::Model 可能会产生干扰。


如果您想在设置特定参数时采取行动,请改写关联的 attribute= 方法。

def foo=(value)
    ...
end

或使用各种ActiveRecord::Callbacks,例如before_validation

或者因为您似乎正在尝试委托质量设置属性,请考虑是否改为 delegation is the answer

delegate :this, :that, to: :some_custom_variable

在Rails4中,assign_attributes method was part of ActiveRecord, in Rails 5 it is part of ActiveModel. This change is mentioned in the Rails5 CHANGELOG升级前大家看的Rails。

据推测,ActiveModelSerializers::Model 通过 ActiveModel 中的某些内容包含一个 assign_attributes 方法,而 ActiveModel 中的某些内容认为它正在调用 AM 的 assign_attributes 而不是您的意外覆盖。

然而,该项目处于state of chaos(大方)所以很难追溯来源:

Changes to 0.10.x maintenance:

  • The 0.10.x version has become a huge maintenance version. We had hoped to get it in shape for a 1.0 release, but it is clear that isn't going to happen. Almost none of the maintainers from 0.8, 0.9, or earlier 0.10 are still working on AMS. We'll continue to maintain 0.10.x on the 0-10-stable branch, but maintainers won't otherwise be actively developing on it.
    • We may choose to make a 0.11.x ( 0-11-stable) release based on 0-10-stable that just removes the deprecations.

What's happening to AMS:

  • There's been a lot of churn around AMS since it began back in Rails 3.2 and a lot of new libraries are around and the JSON:API spec has reached 1.0.
  • If there is to be a 1.0 release of AMS, it will need to address the general needs of serialization in much the way ActiveJob can be used with different workers.
  • The next major release is in development. We're starting simple and avoiding, at least at the outset, all the complications in AMS version, especially all the implicit behavior from guessing the serializer, to the association's serializer, to the serialization type, etc.
  • ...

那是六个月前的事了,GitHub 上已经没有有用的代码了,这个项目对我来说似乎被遗弃了。

短期解决方法是重命名您的 assign_attributes 方法。一个长期的解决办法是完全替换 ActiveModelSerializers,最好用正在积极维护的东西。 AMS 自述文件甚至提供 some alternatives:

  • jsonapi-rb is a highly performant and modular JSON:API-only implementation. There's a vibrant community around it that has produced projects such as JSON:API Suite.
  • fast_jsonapi is a lightning fast JSON:API serializer for Ruby Objects from the team of Netflix.