i was wondering how do you make a helper file's functions available to an action mailer? i have an action mailer called UserMailer and a helper called sessions_helper.rb. how do i make the methods available to UserMailer? ive tried 'include' but it gives the following error
the method im trying to get is "current_user" and i receive the error
undefined local variable or method `cookies'
im using rails 3.2.1
thanks
You can helpers in your mailers like this,
class Notifier < ActionMailer::Base
add_template_helper(ApplicationHelper)
#...
end
As far as current_user go, I don't think mailers have any concept of cookies as ActionController does. As a better design choice I'd keep my mailer independent of the current_user. Mailers are not concerned with who the current_user is ( similar to models ).
For that matters, mailers are not even concerned with who the user is, they are concerned with "email, subject, and body".
So when calling mailer methods, you can pass them the user object ( it can be of the current_user or any body else ) so that they can get the email , generate the subject and the body.
I have had to add a file to config/initializers with this:
class ActionMailer::Base
helper MiscHelper
helper ExtraMailHelper
end
I guess you would add lines for other helpers as needed, e.g.:
class ActionMailer::Base
helper MiscHelper
helper ExtraMailHelper
helper SessionsHelper
end
If you want to include specific helpers in specific mailers you have, you can use this.
class RegistrationMailer < ActionMailer::Base
include MyOwnHelper
def method
end
end
Related
I would like to know is there any specific reason for doing this or is this a silly mistake done by someone (or is it something else that I am not understanding).
class SomeMailer < ActionMailer::Base
def first_method(user)
mail(to: user.email, subject: "Testing")
end
end
This method is called at some other place in the code as follows
SomeMailer.first_method(user).deliver
ActionMailer::Base classes are weird... Yes, you do indeed call instance methods on the class - which obviously won't work for 'normal' classes!
But there's some meta-programming magic under the hood:
module ActionMailer
class Base < AbstractController::Base
def method_missing(method_name, *args) # :nodoc:
if action_methods.include?(method_name.to_s)
MessageDelivery.new(self, method_name, *args)
else
super
end
end
end
end
If you look through the rails documentation, you'll see that calling instance methods on the class is, strangely, the normal thing to do for mailers.
This is how rails is intended to work.
It is also mention in rails guides that
You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
Rails do the internal processing by invoking method_missin.
Basically, any action method defined in mailer class will be intercepted by method_missing and will return an instance of MessageDelivery, otherwise it runs the default implementation. And where do action methods come from? ActionMailer::Base inherits from AbstractController::Base, so it works exactly the same as for controllers - it returns a set of public instance methods of a given class.
Rails itself encourage this behavior. For more information, you can refer this link
I will try to answer this since I have come across similar situations myself while working on existing code.
The instance methods like this in a class help when you do call backs on a class. For example, if you want to perform some action on a object that was created from that class.
Say you have another class User and you want to send an email to a user immediately after creating a new user. In that case you can call this method on that object by doing
after_save :method_name
Given we have a custom header set in application.rb as
config.action_mailer.default_options = {
'X-MyCustomHeader' => "Default_value" }
I would like to be able to call a method, such as remove_custom_headerfrom inside any mailer and it have the header removed, or at least set to nil.
Because its a custom header (starting with X-) ActionMailer will allow more than one to be created rather than reseting it as it would with standard headers.
What is the best way to define a method that can overide it?
The issue i have is that is you call headers['X-MyCustomHeader'] = nilinside the mailer method it will not override it, it simply creates a second header where the entry is nil, leaving the original from application.rb on the email.
The only way i can find to override the default set there is to call default 'X-MyCustomHeader' => nil inside the ApplicationMailer (or in any inheriting mailers class'), like below, but not in the method.
class ApplicatioMailer < ActionMailer::Base
helper :application
default 'X-MyCustomHeader' => nil
end
but i would like to call this on a method basis, not in the mailer class itself as the app has many many mailer classes and im looking to disable this for one or 2 methods in some of the Mailers.
My current solution is:
class ApplicatioMailer < ActionMailer::Base
helper :application
default 'X-MyCustomHeader' => Proc.new { #is_disabled ? nil : Rails.config.action_mailer.default_header }
def remove_custom_header
#is_disabled = true
end
end
and this seems to work as you are using the default call from ActionMailer to override. Now i have tested this and it does work with multiple calls, so the value of #is_disableddoes not seem to persist between mailer calls. I dont understand how the class var works here, in a SuperClass of the mailer that calls it and when no new object of the class is created, but it seems to be null every new call to the ApplicationMailer, so for me it works. However, is this a good solution? Have i missed something obvious? I dont feel comfterable using a class var in this situation, but i can think of another way!
Thanks in advance for any help!
EDIT:: For example, i would like to call in my mailers like so..
class MyMailer < ApplicatioMailer
def mail_method_one(email)
# my call
remove_custom_header
mail from: a.format, subject: email.subject
end
end
And if my method above of setting the class var is valid and sane (which i doubt somehow) can someone explain why as i would be interested how it works and will have to justify it to my lead dev! :)
Contrary to what it looks like, #is_disabled is not a class variable, it's an instance variable. Just as the method you defined is defined as an instance class
Note that you're doing...
def mail_method_one(email)
...not...
def self.mail_method_one(email)
You can confirm this yourself by stopping execution using the pry gem or some other inspection tool...
def mail_method_one(email)
binding.pry
And if you examine self you'll see you're in an instance of the mailer.
You do call it as a class method, but that's because action mailer uses method_missing to test if the missing class method is actually an instance method and creates a new instance of MessageDelivery passing the class name, the method name and the arguments, and subsequently calls the method on an instance of the mailer class.
Here's the code where it does that...
def method_missing(method_name, *args)
if action_methods.include?(method_name.to_s)
MessageDelivery.new(self, method_name, *args)
else
super
end
end
You can examine the code here...
https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb
I'm sending out emails to users and I need to call a function I have in my ApplicationController. Seems simple enough, but I cannot for the life of me find any documentation on how to to this. I can access ApplicationHelper functions just fine, but that's not doing me much good for what I need it to do... Anyone got any light they can shed on the subject?
So not sure if this will help anybody out since this was a highly project specific case, but here's what I did to work through this:
Move functions required for the mailer to the ApplicationHelper.
Add include ApplicationHelper to the Mailer class and my ApplicationController.
For the logic where I needed if !provider_signed_in? I updated to if defined?(provider_signed_in?) && !provider_signed_in? so that it wouldn't return an undefined method error... The logic that I needed if the provider was signed in was irrelevant for the mailer (since a provider would never get this email), so it didn't matter there even though it does matter everywhere else I'm using it on the site.
Thanks Rahul for helping me think through this.
there is a method called helper which you can use in your mailer to inlude your helper in your mailer..
# mailer_helper.rb
module MailerHelper
def your_method
# do whatever you want to do here.
end
end
# in your mailer file
class WelcomeMailer < ActionMailer::Base
helper MailerHelper
.....
end
and that't it, now you can use the methods in the views also.
I am wondering what is a clean and conventional way for setting instance variables in Mailers? Currently, I have re-defined the initialize method in Mailer and subsequently overwrite certain instance variables when needed in any mailers that inherit from Mailer.
class Mailer < ActionMailer::Base
attr_reader :ivar
def initialize
super
#ivar = :blah
...
end
end
This only seems weird to me because new is a private method for mailers. For example, if I were to try to retrieve these in the rails console, I need to do the following:
mailer = Mailer.send(:new)
mailer.ivar
I have also considered adding them to the default hash like so:
class Mailer < ActionMailer::Base
default ivar: :blah,
...
end
The only problem being that I need to create a method like this to retrieve the ivars:
def default_getter(ivar)
self.class.default[ivar]
end
Neither way seems particularly clean to me. I've considered using class variables, but I'm wondering if someone could suggest a cleaner way. Thanks.
Just a little bit late...
You can use before_action callbacks
http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-callbacks
I have trying to access helper methods from a rails 3 mailer in order to access the current user for the session.
I put the helper :application in my mailer class, which seems to work, except the methods defined therein are not available to my mailer (i get undefined errors). Does anyone know how this is supposed to work?
Here's my class:
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
helper :application
end
Thanks,
Sean
To enable you to access application helpers from the ActionMailer views, try adding this:
add_template_helper(ApplicationHelper)
To your ActionMailer (just under your default :from line).
Use helper ApplicationHelper
class NotificationsMailer < ActionMailer::Base
default from: "Community Point <Team#CommunityPoint.ca>"
helper ApplicationHelper
helper NotificationMailerHelper
# ...other code...
NOTE: These helper methods are only available to the Views. They are not available in the mailer class (NotificationMailer in my example).
If you need them in the actual mailer class, use include ApplicationHelper, like so:
class NotificationMailer < ActionMailer::Base
include ApplicationHelper
# ... the rest of your mailer class.
end
From this other SO question.
This is a very old question, but I don't see the full answer, so I will try as I didn't find another resource.
It depends on what you are doing with the methods that have been defined in the helper module. If they are class methods, and everything that's not called on a specific instance seem to be a class methods for 3.2.13, you need to use
extend ApplicationHelper
if an instance methods
include ApplicationHelper
and if you want to use them in a mailer view
helper ApplicationHelper
You could try mixing in the required helper module:
class CommentMailer < ActionMailer::Base
include ApplicationHelper
end
Josh Pinter's answer is correct, but I discovered that it is not necessary.
What is necessary is to name the helper correctly.
NotificationMailerHelper is correct. NotificationMailersHelper (note the s) is not correct.
The class and filename of the helper must match and be correctly spelled.
Rails 3.2.2
include ActionView::Helpers::TextHelper worked for me in the Mailer controller (.rb file). This allowed me to use the pluralize helper in a Mailer controller action (helpers worked fine from the get go in Mailer views). None of the other answers worked, at least not on Rails 4.2
If you want to call helper method from ActionMailer you need to include helper (module) in Mailer file as, if Helper module name is “UserHelper”, then need to write following in Mailer file
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
add_template_helper(UserHelper)
end
Or
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
include UserHelper
end
Hope this is helpful.
The single method version of promoting a method to being a helper that is available in ApplicationController also works in ActionMailer:
class ApplicationMailer < ActionMailer::Base
helper_method :marketing_host
def marketing_host
"marketing.yoursite.com"
end
end
from there you can call marketing_host from any of your mailer views
I'm not sure exactly what you are doing here, but when I want to access current_user from a mailer, I make a mailer method that I pass the user to as an argument:
class CommentMailer < ActionMailer::Base
default :from => "Andre Fournier <andre#gfournier.com>"
def blog_comment(user)
#recipients = user.email
#from = "andre#gfournier.com"
#sent_on = Time.now
#timestamp = Time.now
#user = user
end
end
With the above, #user, as well as all the other instance variables, are accessible from inside the mailer views ./views/comment_mailer/blog_comment.html.erb and ./views/comment_mailer/blog_comment.text.erb
Separately, you can make a helper called
comment_mailer_helper.rb
and put into that helper any methods that you want to be available to your mailer's views. This seems to me more like what you might want, regarding helpers, because helpers are designed to help views, whereas a mailer is analogous to a controller.
None of the *_path helpers are accessible by default inside of an email. It is necessary instead to use the *_url form of the wanted helper. So, for instance, instead of using user_path(#user) it is necessary to use user_url(#user).
See at Action Mailer basics.
A hackish means of achieving what I wanted is to store the objects I need (current_user.name + current_user.email) in thread attributes, like so: Thread.current[:name] = current_user.name. Then in my mailer I just assigned new instance variables to those values stored in the thread: #name = Thread.current[:name]. This works, but it won't work if using something like delayed job.