【问题标题】:Metaprogramming in ActionMailer::BaseActionMailer::Base 中的元编程
【发布时间】:2013-08-06 16:59:37
【问题描述】:

我实际上正在创建一个时事通讯群发软件,我可以通过它即时创建一个新的邮件列表,上传一个模板,然后从该列表中向订阅者发送一封电子邮件,执行如下操作:

@suscribers.each do |suscriber|
    NewsletterMailer.delay.send("#{@list.name}_newsletter", suscriber, @newsletter)
end

(注意延迟方法是因为我的后台作业使用sidekiq)

我试图覆盖 NewsletterMailer 类中 ActionMailer::Base 中的 method_missing 来处理这个逻辑,但它似乎没有被执行。我刚刚收到一个 NoMethodError 说“未定义的方法 `testing_newsletter' for NewsletterMailer:Class”。

我查看了 ActionMailer::Base#method_missing 源代码,发现它已经在执行一些逻辑,因此我们可以在不调用 new 的情况下执行 Mailer.name_of_the_email.deliver。此外,此方法受到保护。

但是,我是否仍然可以通过我的 NewsletterMailer 类中未硬编码的方法发送电子邮件?有没有办法将方法动态添加到 ActionMailer 控制器?

非常感谢。

【问题讨论】:

    标签: ruby-on-rails ruby metaprogramming actionmailer method-missing


    【解决方案1】:

    如果您将其定义为def method_missing ...,则您已经创建了一个实例方法,但您的代码表明您正在向类本身发送动态消息。如果您希望它按照您编写的方式执行,您需要在 NewsletterMailer 上定义 self.method_missing

    话虽如此,对于您的特定情况,我建议您不要使用这种设计。我要记住的想法是,您有行为——所有时事通讯共有的方法和行动——和数据,它描述了任何给定列表的细节和/或其时事通讯。在大多数情况下,这些应该保持独立和不同,您的代码描述行为,并且数据库保存您的数据。为每个邮件列表定义一个新方法几乎没有意义,因为您可以只使用一个将列表作为参数的方法send_newsletter

    【讨论】:

    • 非常感谢。起初我虽然与您描述的设计相同,但我认为它破坏了 ActionMailer 的制作方式太多,因为即使它的行为几乎相同,模板也不相同,ActionMailer::Base 的理念是根据方法名称加载模板。正因为如此,我虽然每个时事通讯都需要自己的方法。但我发现我可以在邮件方法中指定 template_name。知道了这一点,我将重构所有内容并使用单一方法发送所有时事通讯。
    【解决方案2】:

    您可以使用 class_eval 动态定义新方法:

    class SomeClass
    end
    
    [:foo, :bar].each do |name|
      SomeClass.class_eval <<COMMAND
        def self.#{name}
          puts "Hello from #{name}"
        end
    COMMAND
    end
    
    SomeClass.foo
    SomeClass.bar 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-25
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 2018-12-05
      • 2012-10-10
      • 2013-02-21
      • 2012-02-01
      相关资源
      最近更新 更多