Erb from database and image_tag - ruby-on-rails

Just stumbled upon a problem and to the moment, cannot solve it. So here's the setting:
I have an ERB template fetched from the database and rendered to html
Class MyController < ApplicationController
include AssetTagHelper
...
def Show
template=Page.find(...) # <%=image_tag('Test.png')%>
#html=ERB.new(template).result(binding)
end
...
Now the problem is image_tag 'src' resolves into '/images/Test.png', when normally it should resolve to '/assets/Test.png'. So I looked into the rails source of AssetTagHelper which led me to AssetUrlHelper and the following call chain: image_path => asset_path => compute_asset_path. And compute_asset_path legitimately states it should actually resolve to /images/Test.png...
What am I missing here? How can I make the image tag work and give me 'assets/Test.png'?
Thanks in advance for all replies!

Just for the record - while debugging figured out that normally compute_asset_path is overriden in sprockets-rails-2.0.1/lib/sprockets/rails/helper.rb
Solved the issue by moving #html=ERB.new(template).result(binding) from controller to view. Hope this helps somebody ))

Like example I show how to create ERB from database for Mailer class. For another classes all same.
Completed mailer to create email templates from database:
class UserMailer < ActionMailer::Base
# included helper
include ActionView::Helpers::NumberHelper
include ActionView::Helpers::TextHelper
# another helpers...
# included helper
def mailer(from, to, subject, path, name)
mail( from: from, to: to, subject: subject, template_path: path, template_name: name )
end
def get_template(template_name)
#erb = EmailTemplate.where(name: template_name, mailer_name: UserMailer.to_s.underscore).first rescue ''
#template_content_html = ERB.new(#erb.template_html).result(binding).html_safe rescue ''
#template_content_text = ERB.new(#erb.template_text).result(binding).html_safe rescue ''
end
def test(user_id)
from = 'from#mail.com'
recipients = 'to#mail.com'
subject = "test"
template_path = "user_mailer"
get_template(__method__) #def
template_name = "general"
mailer(from, recipients, subject, template_path, template_name)
end
end
for include helper in Mailer you can use construction like this:
include ActionView::Helpers::NumberHelper
works perfect from rails 3.2.13. earlier not tried.

Related

Rails 4.1 Mailer Previews and Devise custom emails

I have a brand new Rails 4.1.1 app where I'm customizing the Devise emails. I want to have them displayed on the new Rails email preview feature so I did the following:
1) Added the following snippet to my config/development.rb file:
config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
2) Created my custom Devise email UserMailer in app/mailers/user_mailer.rb:
class UserMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
layout "notifications_mailer"
end
3) Changed config/initializers/devise.rb to contain the following snippet:
config.mailer = 'UserMailer'
4) Added the class UserMailerPreview to lib/mailer_previews with the following content:
class UserMailerPreview < ActionMailer::Preview
def confirmation_instructions
UserMailer.confirmation_instructions(User.first, {})
end
def reset_password_instructions
UserMailer.reset_password_instructions(User.first, {})
end
def unlock_instructions
UserMailer.unlock_instructions(User.first, {})
end
end
So far, so good. Looks like I've done everything right. But then I try to see the preview for the confirmation_instructions email at the /rails/mailers/user_mailer/confirmation_instructions route and I get the following error:
undefined method `confirmation_url' for #<#<Class:0x007fa02ab808e0>:0x007fa030fb7e80>
the code for my confirmation_url.html.erb template looks like this:
<%= t("notifications.texts.greeting") + #user.display_name %>,
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #token) %></p>
What am I doing wrong? I guess it is something wrong with the way I call the confirmation_url method. Anyone can help me here?
For those looking to preview Devise emails without using custom mailers, (but still custom emails) this is what I did:
Configure your app for email previewing.
Set up the Devise Mailer Preview class
a. Rails ~> 4.1
# mailer/previews/devise_mailer_preview.rb
class Devise::MailerPreview < ActionMailer::Preview
def confirmation_instructions
Devise::Mailer.confirmation_instructions(User.first, "faketoken")
end
def reset_password_instructions
Devise::Mailer.reset_password_instructions(User.first, "faketoken")
end
...
end
b. Rails ~> 5.0
class DeviseMailerPreview < ActionMailer::Preview
... # same setup as Rails 4 above
Restart the server
Using Rails 5 and found the syntax slightly different from #steel's excellent answer, with the use of double "::" being the difference:
# Preview all emails at http://localhost:3000/rails/mailers/devise_mailer
class DeviseMailerPreview < ActionMailer::Preview
def reset_password_instructions
Devise::Mailer.reset_password_instructions(User.first, "faketoken")
end
end
You will get undefined method for the devise url helpers when you have forgotten to include the necessary devise modules in your model. e.g. if your model is User and you want the confirmation_url, you must ensure that the :confirmable module is included:
devise <...snipped...>, :confirmable
Be aware that if this module is not currently loaded that your application most likely does not use e.g. confirmations!
Including the :confirmable module might then lock out all your users. See https://github.com/plataformatec/devise/wiki/How-To:-Add-:confirmable-to-Users
I found this question because I was trying to figure out how to preview Devise emails myself. I copied your code almost exactly and it works fine for me.
The only thing I did differently was to remove the line layout "notifications_mailer" from UserMailer - when I included it I got an error message Missing template layouts/notifications_mailer. What happens if you remove it?
The line Devise::Controllers::UrlHelpers definitely includes the method confirmation_url (you can see this for yourself by opening up the Rails console and running include Devise::Controllers::UrlHelpers then confirmation_url, so I suspect the problem is that your previews aren't being rendered by UserMailer at all. I'm not sure why, but the line layout "notifications_mailer" might be the culprit.
Do you have a class called NotificationsMailer? Does including Devise::Controllers::UrlHelpers in there solve your problem?

rails activerecord concern, including url helpers

Im trying to create an before_save callback for certain models that will add links and formatting to text and that gets saved in a special field. It won't let me include the URL helpers in the callback.
Here's my code:
module SocialText
extend ActiveSupport::Concern
included do
before_save :action_before_save
end
def action_before_save
self.body_formatted = htmlizeBody(self.body)
end
def htmlizeBody(body)
include Rails.application.routes.url_helpers
include ActionView::Helpers
#replace all \ns with <br>
body = body.gsub(/\n/, ' <br/> ')
words = body.split(/\s/)
words.map! do |word|
if word.first == '#'
username = extractUsernameFromAtSyntax word
user = User.find_by! username: username
if not user.nil?
link_to(word, profile_path(user.username))
else
word
end
else
word
end
end
words.join " "
end
def extractUsernameFromAtSyntax(username)
matchData = username.match(/#(\w+)(['.,]\w*)?/)
if not matchData.nil?
matchData[1]
else
username
end
end
end
I'm getting:
NoMethodError (undefined method `include`)
How do I get the helper? is there a better way to do this?
In your htmlizeBody function:
include Rails.application.routes.url_helpers
include ActionView::Helpers
This is in the wrong scope, moving it underneath extend ActiveSupport::Concern will resolve your error.
Another question you could ask yourself is why you need to use view helpers at the concern level?
include Rails.application.routes.url_helpers is usually used when modifying the default url host options (typically when you need to interface with an external API). In this case, it would make sense to use it in the /lib directory.
See this SO post and this post on using url helpers for more info
include operates on a class instance object and you call it like an instance method.
You should take that include part outside of your methods.
Consider using require at your module scope.

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

Access helpers from mailer?

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.

How to use my view helpers in my ActionMailer views?

I want to use the methods I defined in app/helpers/annotations_helper.rb in my ReportMailer views (app/views/report_mailer/usage_report.text.html.erb). How do I do this?
Based on this guide it seems like the add_template_helper(helper_module) method might do what I want, but I can't figure out how to use it.
(BTW, is there a reason you get access to a different set of helpers in mailer views? This is pretty annoying.)
In the mailer class that you are using to manage your emails:
class ReportMailer < ActionMailer::Base
add_template_helper(AnnotationsHelper)
...
end
In Rails 3, just use the helper method at the top of your ActionMailer class:
helper :mail # loads app/helpers/mail_helper.rb & includes MailHelper
I just passed in a block, since I only need it in the one Mailer:
helper do
def host_url_for(url_path)
root_url.chop + url_path
end
end
(be sure to set config.action_mailer.default_url_options.)
(and if you use url_for, be sure to pass in :only_path => false)
For all mailers in Rails 3 (setting "application" helper):
# config/application.rb:
...
config.to_prepare do
ActionMailer::Base.helper "application"
end
(This is an old question but Rails has evolved so I'm sharing what works for me in Rails 5.2.)
Typically you might want to use a custom view helper in rendering the subject line of an email as well as the HTML. In the case where the view helper is in app/helpers/application_helper.rb as follows:
module ApplicationHelper
def mydate(time, timezone)
time.in_time_zone(timezone).strftime("%A %-d %B %Y")
end
end
I can create a dynamic email subject line and template which both use the helper but I need to tell Rails to use the ApplicationHelper explicitly in apps/mailer/user_mailer.rb in two different ways, as you can see in the second and third lines here:
class UserMailer < ApplicationMailer
include ApplicationHelper # This enables me to use mydate in the subject line
helper :application # This enables me to use mydate in the email template (party_thanks.html.erb)
def party_thanks
#party = params[:party]
mail(to: 'user#domain.com',
subject: "Thanks for coming on #{mydate(#party.created_at, #party.timezone)}")
end
end
I should mention that these two lines work just as well so choose one or the other:
helper :application
add_template_helper(ApplicationHelper)
FWIW, the email template at app/views/user_mailer/party_thanks.html.erb looks like this:
<p>
Thanks for coming on <%= mydate(#party.created_at, #party.timezone) %>
</p>
And the app/controller/party_controller.rb controller looks like this
class PartyController < ApplicationController
...
def create
...
UserMailer.with(party: #party).party_thanks.deliver_later
...
end
end
I have to agree with OP (#Tom Lehman) and #gabeodess that this all feels quite convoluted given https://guides.rubyonrails.org/action_mailer_basics.html#using-action-mailer-helpers so perhaps I am missing something...
For Ruby on Rails 4, I had to do 2 things:
(1) As Duke already said, if the helper you want to add is UsersHelper for example, then add
helper :users
to the derived ActionMailer class (e.g. app/mailers/user_mailer.rb)
(2) After that, I got a new error:
ActionView::Template::Error (Missing host to link to! Please provide the :host
parameter, set default_url_options[:host], or set :only_path to true)
To fix this, add the line
config.action_mailer.default_url_options = { :host => 'localhost' }
to each of the config/environments/*.rb files. For config/environments/production.rb, replace localhost with a more appropriate host for the production helper-generated urls.
Q: For #2, why does the mail view need this information, and the regular views do not?
A: Because the regular views don't need to know the host, since all generated links are served from the host they link to. Links that show up in emails are not served from the same host (unless you are linking to hotmail.com or gmail.com, etc.)
You can just add in your mailer
helper :application
or whatever helper you need
This is what I did in rails 6
class ApplicationMailer < ActionMailer::Base
default from: 'community#example.com'
layout 'mailer'
# Add whatever helper you want
helper :application
end
in my case for Rails4, i do like this:
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
add_template_helper ApplicationHelper
...
end
and
# app/mailers/user_mailer.rb
class AccountMailer < ApplicationMailer
def some_method(x, y)
end
end
so that you do not have to specify add_template_helper everywhere.

Resources