【问题标题】:Difference in rails 4 and rails 5 same code acting differentlyrails 4 和 rails 5 的区别相同的代码行为不同
【发布时间】:2018-07-24 22:45:46
【问题描述】:

我的 Rails 4 中有这个课程,工作得非常好:

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

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

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

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

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

我的问题是为什么它在 rails 5 上表现得如此?为什么assign_attributes 会被触发?

【问题讨论】:

  • assign_attributes 应该在做什么?
  • save 不是序列化程序上可用的方法,这是类名的拼写错误吗?
  • @Schwern 显然被用于更新端点来分配属性,我不是此代码的作者,我正在尝试处理到 rails 5 的转换
  • @trueinViso save 不是拼写错误,是的,它没有覆盖原始方法,它正在做一些特定于应用程序的事情。

标签: ruby-on-rails ruby-on-rails-4 ruby-on-rails-5


【解决方案1】:

在 Rails 4 中,assign_attributes method 是 ActiveRecord 的一部分,在 Rails 5 中,它是 ActiveModel 的一部分。这个变化在Rails5 CHANGELOG 中提到,每个人在升级 Rails 之前都会阅读它。

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

但是,该项目位于state of chaos(为大方),因此很难追溯来源:

对 0.10.x 维护的更改:

  • 0.10.x 版本已成为庞大的维护版本。我们曾希望在 1.0 版本中实现它,但很明显这不是 将要发生。几乎没有来自 0.8、0.9 或 早期的 0.10 仍在 AMS 上工作。我们会继续维护 0.10.x 在 0-10-stable 分支上,但维护者不会积极开发它。
    • 我们可以选择制作一个 0.11.x ( 0-11-stable) 基于 0-10-stable 的版本,它只是删除了 弃用。

AMS 发生了什么:

  • 自从 AMS 早在 Rails 3.2 中出现以来,它就发生了很多变化,并且出现了许多新库,并且 JSON:API 规范已达到 1.0。
  • 如果要发布 AMS 的 1.0 版本,则需要以使用 ActiveJob 的方式来解决序列化的一般需求 与不同的工人。
  • 下一个主要版本正在开发中。我们从简单开始,至少在一开始就避免了 AMS 中的所有并发症 版本,尤其是所有来自猜测的隐含行为 序列化器,到关联的序列化器,到序列化 类型等。
  • ...

那是六个月前的事了,GitHub 上已经没有有用的代码了,我觉得这个项目已经被遗弃了。

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

  • jsonapi-rb 是一个高性能和模块化的 JSON:API-only 实现。它周围有一个充满活力的社区,他们制作了 JSON:API Suite 等项目。
  • fast_jsonapi 是一个闪电般快速的 JSON:API 序列化器,用于 Netflix 团队的 Ruby 对象。

【讨论】:

  • 同意 ActiveModelSerializers 非常可疑。出于某种原因,他们已经从master 中删除了所有代码,并且只将其放在分支中。奇怪的。虽然ActiveModelSerializers doesn't define assign_attributes, ActiveModelSerializers::Model does include ActiveModel::Model.
  • @Schwern “ActiveModelSerializers::Model 确实包含 ActiveModel::Model”部分几乎可以肯定是意外 assign_attributes 覆盖的来源。在 Rails4 版本中,assign_attributes 仅来自 OP 的类,因此除非 OP 的代码调用,否则没有人会调用它;现在assign_attributes 是ActiveModel 的一部分,AM 将调用它。因此,建议在逐步淘汰整个 AMS 宝石时使用不同的名称(对我来说,这闻起来像一具腐烂的尸体)。查看Method#owner 将是快速验证(前提是已安装 AMS)。
【解决方案2】:

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

导轨 4

允许您通过传入属性的哈希值来设置所有属性,其中键与属性名称匹配(再次与列名称匹配)。

轨道 5

允许您通过传入属性的哈希值来设置所有属性,其中键与属性名称匹配。

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

此外,如果您已覆盖 assign_attributes,那么它继续执行记录在案的操作很重要。单独设置每个属性和一次性设置它们之间应该没有功能差异。看起来您已经编写了自己的批量分配代码,而不是调用 super,并添加了自己的额外代码以将属性镜像到另一个对象。

还有一个问题是 ActiveModelSerializers::Model 可能如何干扰。


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

def foo=(value)
    ...
end

或者使用before_validation等各种ActiveRecord::Callbacks

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

delegate :this, :that, to: :some_custom_variable

【讨论】:

  • 我认为真正的问题是 assign_attribute 曾经在 ActiveRecord 中,所以它最终不会被 ActiveModel 调用,现在该方法是 ActiveModel 的一部分,所以它们有一个意外覆盖。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-27
  • 1970-01-01
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 2015-10-31
  • 1970-01-01
相关资源
最近更新 更多