I'm trying so send an email like this
mail(:to => #user.email, :from => from_email, :subject => t('orders.invoice.email_subject'), :content_type => "text/html") do |format|
format.html { render partial: "notifier/follow_up_email.#{I18n.locale}.html.erb", locals: {storefront: storefront, order: order} }
end
Unfortunately, rails is only looking for follow_up_email.en.html and not follow_up_email.en.html.erb - What am I doing wrong?
render partial: ... should not contain file extensions such as en.html.erb since they are automagically added.
http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html
But why do you use partials for mails, couldn't you create a proper view?
UPDATE
It's all described in the guide on http://guides.rubyonrails.org/v3.2.16/action_mailer_basics.html
app/mailers/user_mailer.rb:
class UserMailer < ActionMailer::Base
default from: "no-reply#memyselfandi.com"
def notification(user, storefront, order)
#user, #storefront, #order = user, storefront, order
I18n.locale = #user.locale # see note below
mail to: #user.email, subject: I18n.t('orders.invoice.email_subject')
end
end
app/views/user_mailer/notification.en.html.erb:
<html><body>
This is a mail for <%= #user.email %> yadda yadda. Use #storefront and #order here.
</body></html>
app/views/user_mailer/notification.de.html.erb:
<html><body>
Das ist ein mail für <%= #user.email %> bla bla.
</body></html>
The line I18n.locale = #user.locale is only necessary if you want to send async mails e.g. when triggering rake tasks from a cronjob.
Related
Beginner rails error I'm trying to send emails out to ALL current users when an article is updated.
I have sendgrid and devise set up with my app and am able to get the mailer to work through rails console. But, for some reason, I receive an undefined method email for #<User::ActiveRecord_Relation:0x007f8685aebec0> when updating an article.
ArticleNotificationMailer
class ArticleNotificationMailer < ApplicationMailer
default from: 'you#example.com'
def new_article(user, article)
#user = user
#article = article
mail(
to: #user.email,
subject: "New update to article #{article.title}"
)
end
end
new_article.html.erb
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-type" />
</head>
<body>
<h1>New article on website "<%= #article.title %>"</h1>
<p>
<%= #article.body %>
</p>
<p>
<%= link_to "View Comment on site", article_url(#article, anchor: "updates=#{#article.id}") %>
</p>
</body>
</html>
ArticleController
I'm using ArticleNotificationMailer.new_article(#user, #article).deliver
def update
respond_to do |format|
if #article.update(article_params)
ArticleNotificationMailer.new_article(#user, #article).deliver
format.html { redirect_to #article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: #article }
else
format.html { render :edit }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
Error Message
NoMethodError in ArticlesController#update
undefined method `email' for #<User::ActiveRecord_Relation:0x007f8685aebec0>
mail(
to: #user.email,
subject: "New post to articles #{article.title}"
)
end
Rails Console
>> u = User.last
>> a = Article.first
>> ActionNotificationMailer.new_article(u, a).deliver_now
ArticleNotificationMailer.new_article(#user, #article).deliver
Seems like #user was initialized by User.where() in your controller. User.where returns an instance of User::ActiveRecord_Relation which is in fact rails-enhanced array. And errors comes up when you are trying to call email on this array.
Just use User.find if you need to find only one record.
Try passing in the id of the elements.
class ArticleNotificationMailer < ApplicationMailer
default from: 'you#example.com'
def new_article(user_id, article_id)
#user = User.where(id: user_id)
#article = Article.where(id: article_id)
mail(
to: #user.email,
subject: "New update to article #{article.title}"
)
end
end
In your console
>> u = User.last
>> a = Article.first
>> ActionNotificationMailer.new_article(u.id, a.id).deliver_now
I figured out how to fix this.
I added the code below to article.rb and added #article.send_notifications! to my update controller.
def send_notifications!
user = User.all
user.each do |user|
ArticleNotificationMailer.new_article(user, self).deliver_now
end
end
I have application in which I can create invoice, render it to pdf and send to the customer. I have two action in my mailer:
class InvoiceMailer < ActionMailer::Base
default from: "from#example.com"
def send_invoice_reminder(invoice)
#invoice = invoice
attach_invoice
mail :subject => "Invoice reminder", :to => invoice.customer.email
end
def send_invoice(invoice)
#invoice = invoice
attach_invoice
mail :subject => "Your Invoice", :to => invoice.customer.email
end
protected
def attach_invoice
attachments["invoice.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'admin/invoices/show.pdf.erb')
)
end
end
Now I want to send this through Sidkiq workers. But I have question. Whether I need two sidekiq workers:
one to send invoice email
second to send reminder
or maybe one worker will be enough?
I think you could use one worker for it, because you have mostly the same job for these two tasks
and It can looks like:
class InvoiceMailer < ActionMailer::Base
default from: "from#example.com"
def send_invoice(invoice, subject)
#invoice = invoice
attachments["invoice.pdf"] = pdf
mail subject: subject, to: invoice.customer.email
end
private
def pdf
WickedPdf.new.pdf_from_string render_to_string(
pdf: "invoice",
template: 'admin/invoices/show.pdf.erb'
)
end
end
class InvoceEmailSender
include Sidekiq::Worker
def perform(invoice, subject)
InvoiceMailer.send_invoice(invoice, subject).deliver
end
end
InvoiceEmailSender.perform_async invoice, 'Your Invoice'
InvoiceEmailSender.perform_async invoice, 'Invoice Reminder'
I am trying to generate mails with rails action mailer, but it seems like it is not doing everything completely right:
Here's where I create my mail:
class UserNotifier < ActionMailer::Base
default from: 'mail#mail.com'
def forgot_password(user)
setup_email(user)
#subject += "Password restoration"
#url = url_for :controller => 'user_session', :action => 'reset_password',
:id => user.pw_reset_code, :only_path => true
end
protected
def setup_email(user)
#subject = "[MY APP] "
#sent_on = Time.now
#user = user
#content_type = "text/html"
mail to: user.email
end
end
And here's the html.erb that contains the mail.
<%= #user.name %>,
<br/><br/>
Restore password:
<%= #url %>
However, the subject of the mail is not what I said it should be.
But most importantly, #url is nil in the html.erb, however it is generated correctly in forgot_password, I tested it.
Why is it nil? And what can I do to render the URL?
Mails are sent when you call method deliver on ActionMailer::Base object.
Your method forgot_password does not return this object.
the line mail(to: .....) should be the last one in the forgot_password routine
Run rails console and type this to see what returns
ActionMailer::Base::mail(to: 'example.com', subject: 'hello')
then call #deliver on it.
The line
mail to: user.email
Is actually causing your email to send.
Because you are calling this in setup_email, you are actually sending the email AND THEN trying to change the subject and URL after it is sent.
If you ignore the setup method that you have for a second, this is what your code would look like in the order you currently have it:
#subject = "[MY APP] "
#sent_on = Time.now
#user = user
#content_type = "text/html"
mail to: user.email << MAIL IS SENT HERE
#subject += "Password restoration"
#url = url_for :controller => 'user_session', :action => 'reset_password',
:id => user.pw_reset_code, :only_path => true
Update your code to call the mail command last, for example:
def forgot_password(user)
setup_email(user)
#subject += "Password restoration"
#url = url_for :controller => 'user_session', :action => 'reset_password',
:id => user.pw_reset_code, :only_path => true
mail to: user.email
end
protected
def setup_email(user)
#subject = "[MY APP] "
#sent_on = Time.now
#user = user
#content_type = "text/html"
end
I am attempting to create beta invitations using the structure from railscasts episode 124, updated for rails 3.2.8.
Currently, the invitation email gets sent, but does not contain the url (which includes the invitation token) for users to follow to sign up because the instance variable I am creating in ActionMailer (#invitation_link) is nil in the view. Inspecting #invitation_link in the ActionMailer controller shows that it is pointing to the correct url, but it is nil in the view.
I have also checked out the following questions and none of the solutions have worked for me:
How do you use an instance variable with mailer in Ruby on Rails?
https://stackoverflow.com/questions/5831038/unable-to-access-instance-variable-in-mailer-view
Actionmailer instance variable problem Ruby on Rails
ActionMailer pass local variables to the erb template
Relevant code snippets below:
invitations_controller.rb
class InvitationsController < ApplicationController
def new
#invitation = Invitation.new
end
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user
if #invitation.save
if signed_in?
InvitationMailer.invitation(#invitation).deliver
flash[:notice] = "Thank you, invitation sent."
redirect_to current_user
else
flash[:notice] = "Thank you, we will notify when we are ready."
redirect_to root_url
end
else
render :action => 'new'
end
end
end
in invitation_mailer.rb file
class InvitationMailer < ActionMailer::Base
default from: "holler#thesite.com", content_type: "text/html"
def invitation(invitation)
mail to: invitation.recipient_email, subject: "Invitation"
#invitation_link = invited_url(invitation.token)
invitation.update_attribute(:sent_at, Time.now)
end
end
views/invitation_mailer/invitation.text.erb
You are invited to join the site!
<%= #invitation_link %> # INSTANCE VARIABLE THAT IS NIL IN VIEW
routes.rb (only showing relevant line)
match '/invited/:invitation_token', to: 'users#new_invitee', as: 'invited'
try this way
This is your InvitationMailer
def invitation(invitation)
#invitation = invitation
mail(:to => #invitation.recipient_email, :subject => "Invitation")
end
now, in your InvitationsController
if signed_in?
#invitation.update_attribute(:sent_at, Time.now)
InvitationMailer.invitation(#invitation).deliver
...
else
...
end
now, views/invitation_mailer/invitation.text.erb
You are invited to join the site!
<%= invited_url(#invitation.token) %> # INSTANCE VARIABLE THAT IS NIL IN VIEW
try this...
#invitation_link = invited_url(invitation.token, :host => "localhost:3000")
Our Rails (3.0.14) application has user profiles where users can choose whether they want to receive HTML formatted emails. To keep things DRY, I would like to only setup one set of templates for all mails (HTML) and then use my own String extension dehtml (basically strip_tags with some formatting modifications) on the text/plain part. Also, I would like to keep the mailer code DRY.
So far, our mailer methods look like this:
def signup_confirmation(user)
#user = user
mail(:to => #user.email, :subject => ..., ...)
end
1. DRY templates: If possible, I would like to avoid having to create 200 additional mail templates, and autocreate the text/plain part from the HTML template. This is the basic idea (dehtml is my own String extension):
def signup_confirmation(user)
#user = user
mail(:to => #user.email, :subject => ..., ...) do |format|
format.html
format.text { render(:file => 'signup_notification.html').dehtml }
end
end
However, this fails with a 'missing template' error. How do I tell Rails to use the HTML template in both cases? I tried appending :formats => :html and :handler => :html but this didn't help.
I don't have a solution here right now. Any ideas?
2: DRY mailer methods:
Since our users should be able to decide whether they want to have HTML or not, the above method will look something like
def signup_confirmation(user)
#user = user
attachments.inline["email-header.jpg"] = File.read(...) if #user.wants_html
mail(:to => #user.email, :subject => ..., ...) do |format|
format.html if #user.wants_html
format.text { render(:file => 'signup_notification.html').dehtml }
end
end
Altogether, this triples the LOC in each method. I would like to DRY this up (since it will have to be inserted into at least 200 mailer methods) as far as possible. One idea would be to write my own mail method (let's call it mymail) as something like
def mymail(user, p={})
attachments.inline["email-header.jpg"] = File.read(...) if user.wants_html
mail(p) do |format|
format.html if user.wants_html
format.text
end
end
(ignoring the above text template problem for now) and then change each call to mail to mymail, as in
def signup_confirmation(user)
#user = user
attachments.inline["email-header.jpg"] = File.read(...) if #user.wants_html
mymail(user, { :to => #user.email, :subject => ..., ... })
end
This works. But is it good practice? Where do I best put mymail - in a helper?
Any insights and recommendations welcome!
I did something very similar to your first solution a long time ago. I don't really remember why it hat to be the way it is, but this is working for me:
def my_mail
mail(:to => #user.email ...) do |format|
format.text { convert_html_to_plain(__method__) } # first text
format.html # then html
end
end
def convert_html_to_plain(method)
old_formats = self.formats
self.formats = ["html"]
rendered = render "#{method}", :layout => false
self.formats = old_formats
# strip tags, reformat, etc. from rendered
rendered << render(:partial => "plaintext_footer", :locals => {:user => #user}, :formats => [:text] )
end