Ruby ApplicationController function call in ActionMailer - ruby-on-rails

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.

Related

Reason for calling an instance method on a class in Ruby?

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

Rails - URL helpers not working in mailers

I tried:
class MyMailer
def routes
Rails.application.routes.url_helpers
end
def my_mail
#my_route = routes.my_helper
... code omitted
end
Also inside mailer:
include Rails.application.routes.url_helpers
def my_mail
#my_route = my_helper
Also, the simple way, in mailer template:
= link_to 'asd', my_helper
But then when I try to start console I get:
undefined method `my_helper' for #<Module:0x007f9252e39b80> (NoMethodError)
Update
I am using the _url form of the helper, i.e. my_helper_url
For Rails 5, I included the url helpers into the my mailer file:
# mailers/invoice_mailer.rb
include Rails.application.routes.url_helpers
class InvoiceMailer < ApplicationMailer
def send_mail(invoice)
#invoice = invoice
mail(to: #invoice.client.email, subject: "Invoice Test")
end
end
Doing this broke my other routes.
# mailers/invoice_mailer.rb
include Rails.application.routes.url_helpers
Doing this is not the right way, this will break application as routes are reloading and routes will not be available is those are reloading
module SimpleBackend
extend ActiveSupport::Concern
Rails.application.reload_routes!
Right answer is to use *_url and not *_path methods in email templates as explained below in Rails docs.
https://guides.rubyonrails.org/action_mailer_basics.html#generating-urls-in-action-mailer-views
I ran into the same issue but with a Concern, i was unable to use any of the url helpers in my code even using directly Rails.application.routes.url_helpers.administrator_beverages_url i was getting this error:
undefined method `administrator_beverages_url' for #<Module:0x00000002167158> (NoMethodError)
even unable to use the rails console because of this error the only way i found to solve this was to use this code in my Concern
module SimpleBackend
extend ActiveSupport::Concern
Rails.application.reload_routes! #this did the trick
.....
The problem was Rails.application.routes.url_helpers was empty at the moment of the initialization. I don't know the performance implication of using this but for my case this is a small application and i can take the bullet.
In the mailer add
helper :my
or the helper you need
and it will load app/helpers/my_helper.rb & includes MyHelper
Enjoy
The rails route helpers are in Rails.application.routes.url_helpers. You should just be able to put
include Rails.application.routes.url_helpers at the top of your class, though I haven't tested this
I came across this issue while working with a newly added route that seemed to be unavailable in my Mailer. My problem was I needed to restart all the workers, so they would pick up the newly added route. Just leaving this footnote in here in case someone runs into the same issue, it can be tricky to solve if you don't know this is happening.
Please take a look at this blog which explains how you can make use of Rails.application.routes.url_helpers in the right manner.
http://hawkins.io/2012/03/generating_urls_whenever_and_wherever_you_want/
You need to use the my_helper_url

Trying to import my helpers into a custom lib/class and suddenly I need a config?

Real simple.
class Template
def stuff_i_want
stylesheet_link_tag('my_stylesheet')
end
class << self
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
end
end
And this returns..
undefined local variable or method `config' for Template:Class
from /Users/elephanttrip/.rvm/gems/ruby-1.9.3-p385#shasta/gems/actionpack-3.1.12/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb:137:in `stylesheet_link_tag'
From the stylesheet_tag_helpers.rb in Railtie :
def stylesheet_link_tag(*sources)
#stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
#stylesheet_include.include_tag(*sources)
end
Config isn't instantiated in that file anywhere, so I'm assuming its' required from somewhere else.. I have no idea where, or how.
Anyone know how to inject/pass a config into my helper? I've never needed to do this before.
It looks like you're actually including your helpers into Object - and then defining your Template class. I've no idea why it's asking for config, but try putting the includes inside your class definition and see if the problem goes away.
You probably shouldn't be randomly including helpers into things over than your views though - that's not what they're for.
Why not use view_context instead.
So instead of including the helper modules you can do this:
class Template
def stuff_i_want
view_context.stylesheet_link_tag('my_stylesheet')
end
end
This should work fine.
And if you want to include you helpers then use the below code:
class Template
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
def stuff_i_want
stylesheet_link_tag('my_stylesheet')
end
end
Ideally you should not include helpers in ur controllers as they are not intended for that.
Hope that helps

rails, passing in helper into action mailer

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

How to test Rails :helper_methods that live in application.rb?

Here's my situation - I have a helper named LayoutHelper that I use to help me build my layout, and I'm trying to test the following method.
It has a method in LayoutHelper that looks similar to this:
def show_login_form?
return false if #hide_login_form || logged_in?
return true
end
I also have the following in my application.rb:
helper_method :logged_in?
def logged_in?
return #logged_in_user.present?
end
And finally, my test file looks like this:
require File.dirname(__FILE__) + '/../../test_helper'
class LayoutHelperTest < ActionView::TestCase
def test_hide_login_form
assert(show_login_form?)
hide_login_form
assert(!show_login_form?)
end
end
My problem now is that when I run this test, I get this:
NoMethodError: undefined method `logged_in?' for #<LayoutHelperTest:0xb7a7aca8>
app/helpers/layout_helper.rb:17:in `show_login_form?'
layout_helper_test.rb:12:in `test_hide_login_form'
I'm wondering if I'm doing this the correct Rails way, and if so, what I need to change in order to get this test to work.
I have to say, this stumped me at first - but I finally figured out what wasn't quite right.
Helpers are html generators, and should be built in a bubble - not knowing what's going on in sessions, controllers, etc. Your show_login_form? method isn't generating code, it's working with controller methods. It should be in application_controller.rb itself, and tested in controller tests.
Once you move it here, you should have no problem testing. Oh, and this might be a shorter, sweeter way to write the method itself:
def show_login_form?
!(#hide_login_form || logged_in?)
end
I hope this helps. It certainly got me to research helper tests, which I've neglected until now.

Resources