【问题标题】:Rails before_action for ActionMailer that would use mailer arguments将使用邮件程序参数的 ActionMailer 的 Rails before_action
【发布时间】:2015-04-22 11:09:25
【问题描述】:

假设我有一个发送不同电子邮件的邮件程序,但预计会使用相同的参数调用。我想为所有邮件操作处理这些参数。因此,调用before_action 将读取发送到邮件方法的参数

/mailers/my_mailer.rb
class MyMailer < ApplicationMailer
    before_filter do |c|
      # c.prepare_mail # Will fail, because I need to pass `same_param` arguments
      # # I want to send the original arguments
      # c.prepare_mail(same_param) # How do I get `same_param` here ?
    end

    def action1(same_param)
      # email view is going to use @to, @from, @context    
      method_only_specific_to_action1
    end

    def action2(same_param)
      # email view is going to use @to, @from, @context
      method_only_specific_to_action2
    end

    private
      def prepare_mail(same_params)
        @to = same_params.recipient
        @from = same_params.initiator
        @context = same_params.context
      end
    end

然后在我的控制器/服务中我做某处

MyMailer.actionx(*mailer_params).deliver_now

如何访问before_action 块内的same_param 参数列表?

编辑:

我想重构

/mailers/my_mailer.rb
class MyMailer < ApplicationMailer

    def action1(same_param)
      @to = same_params.recipient
      @from = same_params.initiator
      @context = same_params.context   
      method_only_specific_to_action1
    end

    def action2(same_param)
      @to = same_params.recipient
      @from = same_params.initiator
      @context = same_params.context   
      method_only_specific_to_action2
    end

    def actionx
      ... 
    end
  end

还有这个重构

/mailers/my_mailer.rb
class MyMailer < ApplicationMailer

    def action1(same_param)
      prepare_mail(same_params)   
      method_only_specific_to_action1
    end

    def action2(same_param)
      prepare_mail(same_params)   
      method_only_specific_to_action2
    end

    def actionx
      ... 
    end

    private
      def prepare_mail(same_params)
        @to = same_params.recipient
        @from = same_params.initiator
        @context = same_params.context
      end
    end

感觉不理想(prepare_mail(same_params) 在每个操作中重复)

因此上面的建议

【问题讨论】:

  • 只是一个想法 - 你有没有想过直接使用 net::STMP 发送电子邮件ruby-doc.org/stdlib-2.0.0/libdoc/net/smtp/rdoc/Net/SMTP.html。它将比 Action Mailer 更可定制。
  • 从软件工程的角度来看,ActionMailer是一个适配器,可以配置好几种邮件投递方式(我目前用的是3-4种),恐怕SMTP不全在世界上,所以我想使用ActionMailer。
  • 我认为您正在将逻辑部分从控制器移动到层中。最好在邮件程序类和控制器之间添加一个服务层/模型类来实现这一点,而不是覆盖默认邮件程序来实现你想要的。
  • @spickermann 抱歉忘记继承,现在添加。你可以考虑ApplicationMailer 就像ActionMailer::Base。在我之前的评论中,我只是解释说我不仅仅是通过 SMTP 直接发送邮件,还配置为使用 :file:cache:letter_opener 等由 ActionMailer 很好地包装的传递适配器
  • 您的问题似乎可以通过 Rails 5.1 中的 parameterized mailers 解决(参见 PR

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


【解决方案1】:

解决方案1:

如果您不关心格式,我建议您使用它

MyMailer.generic("actionx", *mailer_params).deliver_now

def generic(actiontype, *mailer_params)
  # custom logic goes here to construct the to, from, etc.,
  # new_options from custom logic
  self.send(actiontype, new_options)
end

下面使用父控制器的method_missing的替代解决方案

把你的逻辑放在那里是不对的,但如果你还想这样做,你可以使用method_missing把你的逻辑放在那里,跳过action1和action2方法。

action_mailer的原始method_missing,可以作为参考:

def method_missing(method_name, *args)
  if action_methods.include?(method_name.to_s)
    MessageDelivery.new(self, method_name, *args)
  else
    super
  end
end

https://github.com/rails/rails/blob/c8a18aaf44a84f1ef0df007aa3f8446752dc327d/actionmailer/lib/action_mailer/base.rb#L561-L567

【讨论】:

  • 问题是有很多 not-really logic 包含填充电子邮件标题(每个操作都相同),但每个单独的 mailer_action 仍然是特定的,我可能想做额外的事情在那里(如附加特定文件等)
  • 查看第一个解决方案。 self.send(actiontype, options)
  • 哦,这确实有道理!抱歉,起初没有意识到它在做什么。
  • @Sairam,你真的尝试过你的第一个建议吗?这很吸引人,但在 Rails 4.2 下,我得到 ActionView::MissingTemplate: Missing template my_mailer/generic with "mailer"
【解决方案2】:

根据 Sairam 的回答,我想到了以下内容,但感觉有点奇怪,不能用 before_action 回调完成吗??

class MyMailer < ApplicationMailer

    # Simulation of before_action callback that would support passing the *args to the callback method
    def self.method_missing(method_name, *args)
      method_name = :"#{method_name.to_s}_headers_prefilled"
      if action_methods.include?(method_name)
        mailer = MyMailer.generic(*args) # The before_action callback that passes *args
        mailer.send(method_name, *args) # The action itself
      else
        super
      end
    end

    def generic(*mailer_params)
      # custom logic goes here to construct the headers to, from, etc.,
    end

    def action1_headers_prefilled(mailer_params)
      # Logic only relevant for action1
    end

我还失去了 before_action 中所有很酷的东西(传递 onlyexcept 数组等)

【讨论】:

    【解决方案3】:

    ActionMailer 使用AbstractController::Callbacks 模块。我试过了,它似乎对我有用。

    代码

    class MyMailer < ApplicationMailer
      def process_action(*args)
        # process the args here
        puts args
        super
      end
    
      def some_mail(*args)
      end
    end
    
    MyMailer.some_mail(1, 2) #=> prints ['some_mail', 1, 2]
    

    The documentation


    更新

    如果您使用的是 Rails 5.1,可以查看ActionMailer::Parameterized

    【讨论】:

    • 你将如何使用 before_action 来做到这一点?我试过了,但是没有args
    • 我也不知道。其实如果你使用的是 Rails 5.1,你可以看看ActionMailer::Parameterized
    • api.rubyonrails.org/v5.1/classes/ActionMailer/… 实际上非常好,可能事情正在发展,但它实际上破坏了大多数现有测试,在撰写本文时,它实际上并没有得到 RSpec 很好的支持,并且将涉及相当多的代码重写位。我们尝试在不同的邮件程序中使用它,但在撰写本文时,它并不像 process_action 那样容易使用
    猜你喜欢
    • 1970-01-01
    • 2013-02-12
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2015-08-23
    相关资源
    最近更新 更多