Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails before_action for ActionMailer that would use mailer arguments

Suppose I have a mailer that sends different emails, but is expected to be called with the same parameters. I want to process those parameters for all mailer actions. So, calling a before_action that would read the parameters sent to the mailer method

/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

Then in my controller/service I do somewhere

MyMailer.actionx(*mailer_params).deliver_now

How can I access the same_param arguments list inside the before_action block ?

EDIT :

I want to refactor from

/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

And this refactoring

/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

Feels non-optimal (prepare_mail(same_params) duplicated in every action)

Hence what was suggested above

like image 342
Cyril Duchon-Doris Avatar asked Feb 20 '15 17:02

Cyril Duchon-Doris


2 Answers

ActionMailer uses the AbstractController::Callbacks module. I tried it and it seems to work for me.

The code

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


UPDATE

If you're using Rails 5.1, you can have a look at ActionMailer::Parameterized

like image 172
hsluo Avatar answered Oct 09 '22 18:10

hsluo


Solution1:

I would suggest you use this if you do not care about the format

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

alternative solution below using method_missing from the parent controller

Its not right to put your logic there, but if you still want to do it, you can use the method_missing to put your logic there and skip the action1 and action2 methods.

Original method_missing from action_mailer which can be used as a reference:

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

like image 33
Sairam Avatar answered Oct 09 '22 18:10

Sairam