I am building a job application in Rails and I need to attach resumes(cv) that are uploaded in a job application as an email attachment. All files in the application are uploaded to Cloudinary using CarrierWave. Emails are handled by Action Mailer. In ActionMailer, I have
class ApplicantMailer < ApplicationMailer
def applicant_info(applicant)
#applicant = applicant
mail(to: #applicant.job.email, subject: 'Applicant Details')
end
end
In my mailer view template, I have
<p>Hi Recruiter, in this email, you would find the resume of the latest applicant</p>
<p>Resume: <%= cl_image_tag(#applicant.resume, :attachment=>true) %></p>
At the moment, this does not render the cloudinary file as an attachment in the email. I need to be able to render the resume as a readable attachment file.
Something like this should work. I don't think you need that helper
class ApplicantMailer < ApplicationMailer
def applicant_info(applicant)
#applicant = applicant
attachments[ open(applicant.resume) ]
mail(to: #applicant.job.email, subject: 'Applicant Details')
end
end
template
<p>Hi Recruiter, in this email, you would find the resume of the latest applicant</p>
<p>Resume: <%= image_tag #applicant.resume %></p>
I can't seem to find a step by step tutorial on how to integrate the Sendgrid web API in to a Ruby on Rails application. I'm pretty new to this so maybe I'm missing something obvious.
I would like to use the Sendgrid web API instead of the smtp delivery method (mailgun talks about the benefits of the web API over the SMTP method here: https://documentation.mailgun.com/quickstart-sending.html, and I was thinking that Sendgrid would either have the same benefits or I would potentially switch to mailgun later).
After installing the sendgrid gem (https://github.com/sendgrid/sendgrid-ruby), the documentation tells me to "Create a new client with your SendGrid API Key", and that I can do it 2 ways:
require 'sendgrid-ruby'
# As a hash
client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY')
# Or as a block
client = SendGrid::Client.new do |c|
c.api_key = 'YOUR_SENDGRID_APIKEY'
end
Where specifically in my application am I supposed to put this code? Should I put this in my mailer, my application mailer or in the config/environments/production.rb file?
I took a look at this tutorial that walks through how to set up the Mailgun API: https://launchschool.com/blog/handling-emails-in-rails
According to this tutorial it looks like the line client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY') should actually go in to the mailer method itself. See below for the launchschool.com example (presumably replacing the mailgun specific info with the sendgrid info):
class ExampleMailer < ActionMailer::Base
def sample_email(user)
#user = user
mg_client = Mailgun::Client.new ENV['api_key']
message_params = {:from => ENV['gmail_username'],
:to => #user.email,
:subject => 'Sample Mail using Mailgun API',
:text => 'This mail is sent using Mailgun API via mailgun-ruby'}
mg_client.send_message ENV['domain'], message_params
end
end
Additionally, how do I get my mailer method to send a mailer view instead of simple text as outlined in the launchschool example? For example, instead of sending the text 'This mail is sent using...' I would like to send a mailer view (something like account_activation.html.erb).
Finally, I am using Devise in my application, and I would like to have Devise use the web API to send emails (ie password reset, etc). Does this mean I need to create a custom mailer for Devise? If so, how do I do that?
According to Devise (https://github.com/plataformatec/devise/wiki/How-To:-Use-custom-mailer), I should "create a class that extends Devise::Mailer". Does that mean I simply make a file within my mailer folder with the info laid out in the docs? Do I need a separate mailer for Devise or can I have an existing mailer inherit from the Devise mailer? Finally, how do I tell devise to use the sendgrid web api to send emails (instead of the simple smtp method)?
Sorry for the long question, but hopefully others find it useful.
Thanks!
I would create a mailer class to do this
class SendgridWebMailer < ActionMailer::Base
include Sendgrid
def initialize
#client = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']).client
end
def send_some_email(record, token)
mail = Mail.new
// do your mail setup here
mail = Mail.new
mail.from = Email.new(email: YOUR_EMAIL_HERE)
mail.subject = YOUR_SUBJECT
// I personally use sendgrid templates, but if you would like to use html -
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: PATH_TO_TEMPLATE,
layout: nil,
assigns: IF_NEEDED || {}
)
mail.contents = content
personalization = Personalization.new
personalization.to = Email.new(email: EMAIL, name: NAME)
personalization.subject = SUBJECT
mail.personalizations = personalization
#client.mail._('send').post(request_body: mail.to_json)
end
end
Call it using
SendgridWebMailer.send_some_email(record, token).deliver_later
For Devise
class MyDeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
def reset_password_instructions(record, token, opts={})
SendgridWebMailer.send_some_email(record, token).deliver_later
end
end
in config/devise.rb
# Configure the class responsible to send e-mails.
config.mailer = 'MyDeviseMailer'
EDIT:
This method will, unfortunately, only work with remote images.
Okay, so here's how I did it. There may be a better way to do this, but this worked for me.
So with sending an email you would originally send it by doing something like UserMailer.reset_email(user).deliver. So remove the deliver and save it to a variable:
object = UserMailer.reset_email(user)
Deeply nested in this ish is the body of the email. The place where it lies may change with context, so my advice is to return the object to the frontend so that you can dig into it and find it. For me, the body resided here:
object = UserMailer.reset_email(user)
body = object.body.parts.last.body.raw_source
Okay so now you got the raw source. Now to send it, here's a method I created:
def self.sendEmail(from,to,subject,message)
from = Email.new(email: from)
to = Email.new(email: to)
content = Content.new(type: 'text/html', value: message)
mail = Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
response = sg.client.mail._('send').post(request_body: mail.to_json)
puts response.status_code
puts response.body
puts response.headers
end
Call it as such (in my case it's in the user model):
User.sendEmail('noreply#waydope.com',user.email,'Reset Password', body)
Make sure to change the content type from plain to html, or you will just get the raw code. Hope this helps.
Here's a step-by-step guide to help you integrate SendGrid into your RoR's app. Hope it helps!
Create a SendGrid account first at : http://sendgrid.com/
Create a new rails folder (sendgrid_confirmation)
rails new sendgrid_confirmation
Navigate to ‘sendgrid_confirmation’ folder
cd sendgrid_confirmation
Open ‘sendgrid_confirmation’ in your text editor (Sublime)
Create a user model (User is a test model, you can create any other model as per your project’s use)
rails generate scaffold user name email login
rake db:migrate
Include sendgrid-rails gem in your Gemfile
gem 'sendgrid-rails', '~> 2.0'
Run bundle install in your terminal
bundle install
Use secrets.yml to define the SendGrid API credentials: (config/secrets.yml)
production:
sendgrid_username: your-sendgrid-username
sendgrid_password: your-sendgrid-password
(Emails are not sent in development and test environments. So you can define it only for production.)
Generate a Mailer class. Mailer classes function as our controllers for email views.
rails generate mailer UserNotifier
Open app/mailers/user_notifier.rb and add the following mailer action that sends users a sign-up mail
class UserNotifier < ActionMailer::Base
default :from => 'any_from_address#example.com'
# send a signup email to the user, pass in the user object that contains the user's email address
def send_signup_email(user)
#user = user
mail(:to => #user.email,
:subject => 'Thanks for signing up for our amazing app')
end
end
Create a file app/views/User_notifier/send_signup_email.html.erb as follows: (This will create a view that corresponds to our action and outputs HTML for our email)
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Thanks for signing up, <%= #user.name %>!</h1>
<p>Thanks for joining and have a great day! Now sign in and do awesome things!</p>
</body>
</html>
Go to the Users Controller (app/controllers/users_controller.rb) and add a call to UserNotifier.send_signup_email when a user is saved.
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserNotifier.send_signup_email(#user).deliver
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Update your config/environment.rb to point your ActionMailer settings to SendGrid’s servers. Your environment.rb file should look like the following:
# Load the Rails application.
require File.expand_path('../application', __FILE__)
# Initialize the Rails application.
Rails.application.initialize!
ActionMailer::Base.smtp_settings = {
:user_name => ‘your_sendgrid_username’,
:password => 'your_sendgrid_password’,
:domain => ‘your_domain.com',
:address => 'smtp.sendgrid.net',
:port => 587,
:authentication => :plain,
:enable_starttls_auto => true
}
Point your config/routes.rb file to load the index page on load. Add the following in your routes.rb file:
get ‘/’ => ‘users#index’
And that is it! Now when you create a new user, you should receive an email (on the user.email you provided) from any_from_address#example.com. You can change this from:e-mail from app/mailers/user_notifier.rb. Change the default :from address and it should do that. You can add email parameters in the send_signup_mail method inside the same file and add details that you’d like to add. You can also change the message body from app/views/user_notifier/send_signup_email.html.erb to display whatever content you want to.
Here is my mailer:
class MailIt < ActionMailer::Base
def funny(sender)
#sender = sender
attachments['funny.pdf'] = File.read("#{Rails.root}/app/assets/funny.pdf")
mail(:to => "amail#example.com",
:from => "amail#example.com",
:subject => "TESST")
end
end
I get the e-mail with the attached pdf but it is damaged and the file size is only 1/4 of the original file.
if you have images in your pdf, you have to send it as binary.
attachments['funny.pdf'] = File.read("#{Rails.root}/app/assets/funny.pdf", :mode => 'rb')
I did find a solution.
It seems like there is a problem with File.read
I tried this and it worked:
attachments['funny.pdf'] = File.open("#{Rails.root}/app/assets/funny.pdf").read
How to render prawn pdf as attachment in ActionMailer? I use delayed_job and don't understand, how could I render pdf-file in action mailer (not in controller). What format should I use?
You just need to tell Prawn to render the PDF to a string, and then add that as an attachment to the email. See the ActionMailer docs for details on attachments.
Here's an example:
class ReportPdf
def initialize(report)
#report = report
end
def render
doc = Prawn::Document.new
# Draw some stuff...
doc.draw_text #report.title, :at => [100, 100], :size => 32
# Return the PDF, rendered to a string
doc.render
end
end
class MyPdfMailer < ActionMailer::Base
def report(report_id, recipient_email)
report = Report.find(report_id)
report_pdf_view = ReportPdf.new(report)
report_pdf_content = report_pdf_view.render()
attachments['report.pdf'] = {
mime_type: 'application/pdf',
content: report_pdf_content
}
mail(:to => recipient_email, :subject => "Your report is attached")
end
end
I followed the RailsCasts for PRAWN. Taken what has already been said and what I was trying to similarly accomplish, I set the attachment name and then created the PDF.
InvoiceMailer:
def invoice_email(invoice)
#invoice = invoice
#user = #invoice.user
attachments["#{#invoice.id}.pdf"] = InvoicePdf.new(#invoice, view_context).render
mail(:to => #invoice.user.email,
:subject => "Invoice # #{#invoice.id}")
end
My solution:
render_to_string('invoices/show.pdf', :type => :prawn)
PDF was corrupted because I didn't write block for mail function and multi-part email was incorrect.
I have a Rails app that sends a PDF to the user. I am producing this email with the following mailer code:
def new_file_notification
mail(:to => 'test#test.com', :subject => "File for #{Time.now.to_s(:uk)}") do |format|
format.html
format.pdf do
attachments['my_file.pdf'] = File.read(Rails.root.join('tmp', 'my_files', "my_file_#{#my_file.id}.pdf"))
end
end
end
Whilst this produces an email that is fine in Sparrow and the GMail interface, this does not come out well in Apple Mail. I see the attachment, but do not see any of the email HTML, but also have a second blank attachment called "Mail Attachment.pdf"
What am I doing wrong?
I think you should remove format.pdf as doesn't this suggest that PDF is a viable format for the e-mail itself? It should only be .html or .text I think. Your attachment should just go under HTML format.