0

I am actually creating a newsletter massmailling software from which I can create a new mailling list on the fly, upload a template, and then send an email to the suscribers from that list doing something like this:

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

(Note that the delay method is because I use sidekiq for my background jobs)

I have tried to override the method_missing from ActionMailer::Base inside the NewsletterMailer class to handle this logic, but it just doesn't seem to be executed. I just receive a NoMethodError saying "undefined method `testing_newsletter' for NewsletterMailer:Class".

I looked up the ActionMailer::Base#method_missing source code and I see that it is already executing some logic so we can be able to do Mailer.name_of_the_email.deliver without calling new. Also, this method is protected.

But, is there a way I can still send an email from a method that is not hardcoded inside my NewsletterMailer class? Is there a way to add methods dynamically to an ActionMailer controller?

Thanks a lot.

2 Answers2

1

If you've defined it as def method_missing ..., you have created an instance method, but your code indicates you are sending the dynamic message to the class itself. You need to define self.method_missing on NewsletterMailer if you want it to execute as you've written it.

That being said, I would recommend against this design for your particular case. The idea I would keep in mind is that you have behavior - the methods and actions that all newsletters have in common - and data, which describes the particulars of any given list and/or its newsletter. In most cases, these should remain separate and distinct, with your code describing behavior, and a database holding your data. It makes little sense to define a new method for each mailing list, when you could just have a method send_newsletter that takes the list as an argument.

Zach Kemp
  • 11,736
  • 1
  • 32
  • 46
  • Thanks a lot. At first i though about the same design you described but i though it was breaking too much the way ActionMailer was made, because even if it is pretty much the same behavior the template is not the same and the philosophy of ActionMailer::Base was to load the template based on the method name. Because of this i though every newsletter needed their own method. But i found out i could specify the template_name inside the mail method. Knowing this, I will refactor everything and use a single method to send all newsletters. – Mathieu Dumont Aug 06 '13 at 18:33
0

You can use class_eval to define new methods on the fly:

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 
BroiSatse
  • 44,031
  • 8
  • 61
  • 86