How to pass variables into Mail body template? - ruby

I am trying to write simple mailer in Sinatra which sends email with params variables.
require 'sinatra'
require 'mail'
class App < Sinatra::Base
post '/test_mailer' do
company = params['Field6']
email = params['Field5']
puts "Company name: #{company}"
puts "Email: #{email}"
mail = Mail.new do
from 'me#mydomain.com'
to 'me#mydomain.com'
subject 'Here is the image you wanted'
text_part do
body "Company Name \n === \n #{company} \n \n Email \n === \n #{email}"
end
end
mail.deliver!
end
end
How to move email template to test_mailer.txt with company and email variables ?

I'm not sure I understand you - you want an separate email template file, right? I'm thinking you could use an erb, or haml template and then do something like the following:
text_part do
body erb(:test_mailer)
end
Your test_mailer.erb file would then contain your email template.
Here shows how something similar is done using pony.

Related

Reading a Gmail Message with ruby-gmail

I am looking for an instance method from the ruby-gmail gem that would allow me to read either:
the body
or
subject
of a Gmail message.
After reviewing the documentation, found here, I couldn't find anything!?
There is a .message instance method found in the Gmail::Message class section; but it only returns, for lack of a better term, email "mumbo-jumbo," for the body.
My attempt:
#!/usr/local/bin/ruby
require 'gmail'
gmail = Gmail.connect('username', 'password')
emails = gmail.inbox.emails(:from => 'someone#mail.com')
emails.each do |email|
email.read
email.message
end
Now:
email.read does not work
email.message returns that, "mumbo-jumbo," mentioned above
Somebody else asked this question on SO but didn't get an answer.
This probably isn't exactly the answer to your question, but I will tell you what I have done in the past. I tried using the ruby-gmail gem but it didn't do what I wanted it to do in terms of reading a message. Or, at least, I couldn't get it to work. Instead I use the built-in Net::IMAP class to log in and get a message.
require 'net/imap'
imap = Net::IMAP.new('imap.gmail.com',993,true)
imap.login('<username>','<password>')
imap.select('INBOX')
subject_id = search_mail(imap, 'SUBJECT', '<mail_subject>')
subject_message = imap.fetch(subject_id,'RFC822')[0].attr['RFC822']
mail = Mail.read_from_string subject_message
body_message = mail.html_part.body
From here your message is stored in body_message and is HTML. If you want the entire email body you will probably need to learn how to use Nokogiri to parse it. If you just want a small bit of the message where you know some of the surrounding characters you can use a regex to find the part you are interested in.
I did find one page associated with the ruby-gmail gem that talks about using ruby-gmail to read a Gmail message. I made a cursory attempt at testing it tonight but apparently Google upped the security on my account and I couldn't get in using irb without tinkering with my Gmail configuration (according to the warning email I received). So I was unable to verify what is stated on that page, but as I mentioned my past attempts were unfruitful whereas Net::IMAP works for me.
EDIT:
I found this, which is pretty cool. You will need to add in
require 'cgi'
to your class.
I was able to implement it in this way. After I have my body_message, call the html2text method from that linked page (which I modified slightly and included below since you have to convert body_message to a string):
plain_text = html2text(body_message)
puts plain_text #Prints nicely formatted plain text to the terminal
Here is the slightly modified method:
def html2text(html)
text = html.to_s.
gsub(/( |\n|\s)+/im, ' ').squeeze(' ').strip.
gsub(/<([^\s]+)[^>]*(src|href)=\s*(.?)([^>\s]*)\3[^>]*>\4<\/\1>/i,
'\4')
links = []
linkregex = /<[^>]*(src|href)=\s*(.?)([^>\s]*)\2[^>]*>\s*/i
while linkregex.match(text)
links << $~[3]
text.sub!(linkregex, "[#{links.size}]")
end
text = CGI.unescapeHTML(
text.
gsub(/<(script|style)[^>]*>.*<\/\1>/im, '').
gsub(/<!--.*-->/m, '').
gsub(/<hr(| [^>]*)>/i, "___\n").
gsub(/<li(| [^>]*)>/i, "\n* ").
gsub(/<blockquote(| [^>]*)>/i, '> ').
gsub(/<(br)(| [^>]*)>/i, "\n").
gsub(/<(\/h[\d]+|p)(| [^>]*)>/i, "\n\n").
gsub(/<[^>]*>/, '')
).lstrip.gsub(/\n[ ]+/, "\n") + "\n"
for i in (0...links.size).to_a
text = text + "\n [#{i+1}] <#{CGI.unescapeHTML(links[i])}>" unless
links[i].nil?
end
links = nil
text
end
You also mentioned in your original question that you got mumbo-jumbo with this step:
email.message *returns mumbo-jumbo*
If the mumbo-jumbo is HTML, you can probably just use your existing code with this html2text method instead of switching over to Net::IMAP as I had discussed when I posted my original answer.
Nevermind, it's:
email.subject
email.body
silly me
ok, so how do I get the body in "readable" text? without all the encoding stuff and html?
Subject, text body and HTML body:
email.subject
if email.message.multipart?
text_body = email.message.text_part.body.decoded
html_body = email.message.html_part.body.decoded
else
# Only multipart messages contain a HTML body
text_body = email.message.body.decoded
html_body = text
end
Attachments:
email.message.attachments.each do |attachment|
path = "/tmp/#{attachment.filename}"
File.write(path, attachment.decoded)
# The MIME type might be useful
content_type = attachment.mime_type
end
require 'gmail'
gmail = Gmail.connect('username', 'password')
emails = gmail.inbox.emails(:from => 'someone#mail.com')
emails.each do |email|
puts email.subject
puts email.text_part.body.decoded
end

ruby mail gem - set charset in deliver block

I'm using the mail gem to send E-Mail with UTF-8 content using this code
Mail.defaults do
...
end
Mail.deliver do
from "user#example.com"
to "otheruser#example.com"
subject "Mäbülö..."
body "Märchenbücher lösen Leseschwächen."
end
This works, but gives the warning
Non US-ASCII detected and no charset defined.
Defaulting to UTF-8, set your own if this is incorrect.
Now after much trying around, consulting mail gem's generated documentation as well as source code, I'm still unable to set the charset. There is a method charset= in Message.rb, but when I add a call to charset, like so:
Mail.deliver do
from "user#example.com"
to "otheruser#example.com"
charset "UTF-8"
subject "Mäbülö..."
body "Märchenbücher lösen Leseschwächen."
end
I get this ArgumentError:
/usr/local/lib/ruby/gems/1.9.1/gems/mail-2.4.4/lib/mail/message.rb:1423:in `charset': wrong number of arguments (1 for 0) (ArgumentError)
How can I set the charset within the deliver block?
mail.charset() returns the current charset, it does not allow to set one and does not take any argument.
To do so you need to use mail.charset = ...
It's actually possible inside the block with:
Mail.deliver do
from "user#example.com"
to "otheruser#example.com"
subject "Mäbülö..."
body "Märchenbücher lösen Leseschwächen."
charset = "UTF-8"
end
It's also possible without a block:
mail = Mail.new
mail.charset = 'UTF-8'
mail.content_transfer_encoding = '8bit'
mail.from = ...
mail.to = ...
mail.subject = ...
mail.text_part do
body ...
end
mail.html_part do
content_type 'text/html; charset=UTF-8'
body ...
end
mail.deliver!
you need to set the encoding also for the individual parts. Answer by maxdec shows it. Ensure that you do this for the text_part also.
This works for me.
mail = Mail.deliver do
charset='UTF-8'
content_transfer_encoding="8bit"
require 'pry';binding.pry
to 'xxx#xxx.yy'
from 'yyy#yyy.ss'
subject "Tet with äöüß"
text_part do
content_type "text/plain; charset=utf-8"
body <<-EOF
this is a test with äöüß
EOF
end
end
mail.deliver!
I use mail (2.7.1), neither charset nor content_transfer_encoding work for me.
charset='UTF-8'
content_transfer_encoding="8bit"
The following works for me!
content_type "text/plain; charset=utf-8"

Email body missing in Gmail when I add inline attachments

My UserMailer has an export method which sends an email to an address. This email has a CSV attachment and some inline PNG attachments. Here's the action which sends the email:
# user - the user to send the email to.
# csv_files - an array of hashes which represent CSV files to attach to the email.
# export_type - the name of the model being exported (either "customer" or "invoice").
#
# Examples
#
# UserMailer.export_email(current_user, files, "customer")
#
# Returns an email instance.
def export_email(user, csv_files, export_type)
#user = user
from_addy = %|'Exports' <exports#example.com>|
subject = "Your #{export_type} export files."
csv_files.each do |file|
# file[:content] will just be a big CSV string.
opts = { mime_type: 'text/plain', content: file[:content] }
attachments[file[:name]] = opts
end
# export_email_images is an array of hashes of image information defined elsewhere
# in the UserMailer class. These images are used in the email body.
# If I comment out this line and remove the images from my email body
# then everything works fine.
export_email_images.each { |opts| attachments.inline[opts[:name]] = opts }
# I have app/views/user_mailer/customer_export_email view files in both
# .html.erb and .text.erb formats.
mail(to: user.email, subject: subject, from: from_addy,
template_name: "#{export_type}_export_email")
end
However, when I send the email to Gmail in production, the body is missing:
Removing the inline attachments fixes the problem and the email body renders correctly (sans inline images obviously). What could be wrong here?

Mail gem determine whether plaintext or html

I'm using the mail gem from https://github.com/mikel/mail
I use it to parse raw mail data: e.g
require 'mail'
maildata = Mail.new(body) #where body is the raw text of an email message
#from there I can see info such as
p maildata.body.decoded #displays the decoded email body
p maildata.from #shows who the email is from
How would I figure out whether the email is plaintext or html is there a built in way to do this?
You could look at maildata.content_type:
maildata.content_type
#=> "text/plain; charset=us-ascii"
If it's a multipart e-mail, you could have both plain text and HTML. You could then look at the parts array to see which content types it includes:
maildata.content_type
#=> "multipart/alternative; boundary=\"--==_mimepart_4f848491e618f_7e4b6c1f3849940\"; charset=utf-8"
maildata.parts.collect { |part| part.content_type }
#=> ["text/plain; charset=utf-8", "text/html; charset=utf-8"]
mail = Mail.new do
text_part do
body 'test'
end
html_part do
content_type 'text/html; charset=utf-8'
body '<b>test</b>'
end
end
mail.multipart? # true
puts mail.html_part.decoded # "<b>test</b>"
puts mail.text_part # "test"

Testing ActionMailer multipart emails(text and html version) with RSpec

I'm currently testing my mailers with RSpec, but I've started setting up multipart emails as described in the Rails Guides here: http://guides.rubyonrails.org/action_mailer_basics.html#sending-multipart-emails
I have both mailer templates in text and html formats, but it looks like my tests are only checking the HTML portion. Is there a way to check the text template separately?
Is it only checking the HTML view because it's first in the default order?
To supplement, nilmethod's excellent answer, you can clean up your specs by testing both text and html versions using a shared example group:
spec_helper.rb
def get_message_part (mail, content_type)
mail.body.parts.find { |p| p.content_type.match content_type }.body.raw_source
end
shared_examples_for "multipart email" do
it "generates a multipart message (plain text and html)" do
mail.body.parts.length.should eq(2)
mail.body.parts.collect(&:content_type).should == ["text/plain; charset=UTF-8", "text/html; charset=UTF-8"]
end
end
your_email_spec.rb
let(:mail) { YourMailer.action }
shared_examples_for "your email content" do
it "has some content" do
part.should include("the content")
end
end
it_behaves_like "multipart email"
describe "text version" do
it_behaves_like "your email content" do
let(:part) { get_message_part(mail, /plain/) }
end
end
describe "html version" do
it_behaves_like "your email content" do
let(:part) { get_message_part(mail, /html/) }
end
end
This can be tested with regular expressions.
Finding things in the HTML portion (use #should after this to match):
mail.body.parts.find {|p| p.content_type.match /html/}.body.raw_source
Finding things in the plain text portion (use #should after this to match):
mail.body.parts.find {|p| p.content_type.match /plain/}.body.raw_source
Checking that it is, indeed, generating a multipart message:
it "generates a multipart message (plain text and html)" do
mail.body.parts.length.should == 2
mail.body.parts.collect(&:content_type).should == ["text/html; charset=UTF-8", "text/plain; charset=UTF-8"]
end
To make things even simpler, you can use
message.text_part and
message.html_part
to find the respective parts. This works even for structured multipart/alternative messages with attachments. (Tested on Ruby 1.9.3 with Rails 3.0.14.)
These methods employ some kind of heuristic to find the respective message parts, so if your message has multiple text parts (e.g. as Apple Mail creates them) it might fail to do the "right thing".
This would change the above method to
def body_should_match_regex(mail, regex)
if mail.multipart?
["text", "html"].each do |part|
mail.send("#{part}_part").body.raw_source.should match(regex)
end
else
mail.body.raw_source.should match(regex)
end
end
which works for both plaintext (non-multipart) messages and multipart messages and tests all message bodies against a specific regular expression.
Now, any volunteers to make a "real" RSpec matcher out of this? :) Something like
#mail.bodies_should_match /foobar/
would be a lot nicer ...
If your email has attachments the text and html parts will end be placed in a multipart/alternative part. This is noted on under Sending Emails with Attachments in the Rails 3 Guide.
To handle this, I first simplified the get_message_part method above to:
def get_message_part(mail, content_type)
mail.body.parts.find { |p| p.content_type.match content_type }
end
Then in my test:
multipart = get_message_part(email, /multipart/)
html = get_message_part(multipart, /html/)
html_body = html.body.raw_source
assert_match 'some string', html_body
I have done this way, I found it simpler since the content of both emails is gonna be similar except styles and markup.
context 'When there are no devices' do
it 'makes sure both HTML and text version emails are sent' do
expect(mail.body.parts.count).to eq(2)
# You can even make sure the types of the part are `html` and `text`
end
it 'does not list any lockboxes to be removed in both types emails' do
mail.body.parts.each do |part|
expect(part.body).to include('No devices to remove')
end
end
end

Resources