我应该把多个模型中使用的自定义回调函数放在哪里?

4
假设我有两个模型拥有相同的回调函数:
class Entry < ActiveRecord::Base
    belongs_to :patient
    validates :text, presence: true
    after_validation :normalizeDate

    def normalizeDate
      self.created_at = return_DateTime(self.created_at)
    end
end

class Post < ActiveRecord::Base
    after_validation :normalizeDate

    def normalizeDate
      self.created_at = return_DateTime(self.created_at)
    end
end

我应该把共享回调代码放在哪里?谢谢

 def normalizeDate
   self.created_at = return_DateTime(self.created_at)
 end

1
这不是验证代码,而是回调函数。 - Marek Lipka
抱歉!我会修改我的问题。 - John Smith
1
@sytycs 干就是干,我的朋友 :) 两次相同的代码已经是重复了,想象一下几个月后你不得不维护你的代码,却忘记更新其中之一的场景? - Benj
@BenjaminSinclaire 我不同意。如果在两个月后我决定改变我的日期规范化方式,我可能会考虑代码中发生这种情况的2个地方。我只是不喜欢过早地进行重构。参见:https://dev59.com/43E95IYBdhLWcg3wd9tK - wpp
@BenjaminSinclaire 哈哈可能是吧 :) 感谢你的好态度。 - wpp
显示剩余2条评论
3个回答

8

Marek的回答不错, 但Rails的方式是:

module NormalizeDateModule
  extend ActiveSupport::Concern

  included do
    after_validation :normalize_date
  end

  def normalize_date
    self.created_at = return_DateTime(created_at)
  end
end

这里有文档

(而且你有一个专门用于它的文件夹:models/concerns)


好的,我忘记了“Concern”。 - Marek Lipka
我该如何在我的模型中调用它? - John Smith
@JohnSmith 与我的解决方案相同。 - Marek Lipka
为了测试你的解决方案,我将 self.created_at = self.created_at + 2.days 放入了 def normalize_date 中。但是我总是得到错误:undefined method +' for nil:NilClass,因为 self.created` 似乎没有被定义!我做错了什么?谢谢。 - John Smith
1
@JohnSmith created_at 在对象创建之前是 nil。替换为:self.created_at = (created_at || Time.now) + 2.days - apneadiving

3

您可以定义自己的模块:

module NormalizeDateModule
  def self.included(base)
    base.class_eval do
      after_validation :normalize_date
    end
  end

  def normalize_date
    self.created_at = return_DateTime(created_at)
  end
end

并在您想要实现此行为的每个类中包含它:

class Entry < ActiveRecord::Base
  include NormalizeDateModule
  # ...
end

我不确定我的代码是否没有错误(我没有测试过),请将其视为一个示例。


1
如果您将上述代码制作为模块并包含在内,它将能够正常工作,而不是使用“class NormalizeDateModule”。请参考:https://dev59.com/P3I-5IYBdhLWcg3wu7Pv,该链接也使用了上述代码。用户已将其标记为有效答案。 - SreekanthGS
当然,我是指“模块”,但我写成了“类”。:) 谢谢。 - Marek Lipka
1
@JohnSmith 是的,我认为models文件夹最适合这个。而且,它应该按照你写的那样命名。 - Marek Lipka
1
@JohnSmith 不应该,因为它旨在在许多模型中重复使用,所以应该放在单独的文件中。 - Marek Lipka
为了测试你的解决方案,我将 self.created_at = self.created_at + 2.days 放入了 def normalize_date 中。但是我总是得到错误:undefined method +' for nil:NilClass,因为 self.created` 似乎没有被定义!我做错了什么?谢谢。 - John Smith
显示剩余4条评论

2
Rails 4的做法是使用ActiveSupport::Concern
文件models/concerns/date_normalizer.rb
module Concerns
  module DateNormalizer
    extend ActiveSupport::Concern

    included do |base|
      base.after_validation :normalize_date
    end

    def normalize_date
      self.created_at = return_DateTime(self.created_at)
    end
  end
end

文件 model/entry.rb

class Entry < ActiveRecord::Base
  include Concerns::DateNormalizer

  belongs_to :patient
  validates :text, presence: true
end

文件 models/post.rb

class Post < ActiveRecord::Base
  include Concerns::DateNormalizer
end

注意:我已经为您将normalizeDate重命名为normalize_date

到目前为止最好的答案!我会去看看! - John Smith
1
抱歉,我有一个习惯,就是拥有几个关注目录,最好从一开始就使用Rails默认的。 - Benj
1
这是另一个问题:我假设您正在对新记录进行测试,并且对于新记录,在验证时(因为您的回调函数是after_validation),Rails尚未设置created_at字段。 - Benj
2
你出现这个错误的事实意味着你的回调函数是正确工作的(因为错误来自于回调函数中的代码)。 - Benj
1
请确认此答案并发布另一个问题,否则会变得混乱。 - Benj
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接