Can't get Rails 3 ActionMailer to accept an attachment - ruby-on-rails-3

I am migrating an Rails 2.3.14 application to Rails 3.0. In it, a mailer sends a message with an attachment. Using the code below, this worked without issue in 2.3.x.
def notification(material, recipient, path_to_file)
enctype = "base64"
#recipients = recipient.email
#from = material.person.email
#reply_to = material.person.email
#subject = "New or updated materials: " + material.name
#sent_on = Time.now
#content_type = "multipart/mixed"
#headers['sender'] = material.person.email
part :content_type => "text/plain",
:body => render_message('notification',
:material => material,
:url => material.full_url_to_material)
attachment :content_type => "application" + "/" + material.file_type,
:body => File.read(path_to_file),
:filename => File.basename(material.file),
:transfer_encoding => enctype,
:charset => "utf-8" if !!material.send_as_attachment
end
Reading through the Rails 3.0 ActionMailer instructions, I have modified the method to the following:
def notification(material, recipient, path_to_file)
#material = material
#url = material.full_url_to_material
attachments[material.file_file_name] = File.open(path_to_file, 'rb'){|f| f.read} if material.send_as_attachment?
headers['sender'] = material.person.email
mail(:to => recipient.email,
:subject => "New or updated materials: " + material.name,
:reply_to => material.person.email,
:from => material.person.email)
end
MaterialMailer#notification is called when a material is created. I have the following spec to test this:
it "will include the materials as an attachement with the the send_as_attachment field is set to 1" do
it = Material.create(#materials_hash.merge(:send_notification => "1", :send_as_attachment => "1"))
email = ActionMailer::Base.deliveries[0]
email.body.should =~ Regexp.new("Name of the posted material: " + it.name )
email.has_attachments?.should be_true
end
As I mentioned, this worked fine in 2.3. Now, if I set the send_as_attachment flag to one, I get the following error, referencing the email.body.should line:
1) Material will include the materials as an attachement with the the send_as_attachment field is set to 1
Failure/Error: email.body.should =~ Regexp.new("Name of the posted material: " + it.name )
expected: /Name of the posted material: My material/
got: (using =~)
Diff:
## -1,2 +1 ##
-/Name of the posted material: My material/
If I change the spec and set the send_as_attachment to 0, I get the following error, referencing the has_attachments? line:
1) Material will include the materials as an attachement with the the send_as_attachment field is set to 1
Failure/Error: email.has_attachments?.should be_true
expected false to be true
So including the attachment is somehow breaking the email.
I have tried other methods for attaching the material:
attachments[material.file_file_name] = {:mime_type => "application" + "/" + material.file_content_type,
:content => File.read(material.file.path),
:charset => "utf-8"}
I have tried hardcoding the file paths to known files. But no luck.
Any where else I should look?

Per Christopher's suggestion, here is the mailer and spec code that I used to get it to work:
def notification(material, recipient, path_to_file)
#material = material
#url = material.full_url_to_material
attachments[material.file_file_name] = File.open(material.file.path, 'rb'){|f| f.read} if material.send_as_attachment?
headers['sender'] = material.person.email
mail(:to => recipient.email,
:subject => message_subject("New or updated materials: " + material.name),
:reply_to => material.person.email,
:from => material.person.email)
end
it "will include the materials as an attachment with the the send_as_attachment field is set to 1" do
it = Material.create(#materials_hash.merge(:send_notification => "1", :send_as_attachment => "1"))
Delayed::Worker.new(:quiet => true).work_off
email = ActionMailer::Base.deliveries[0]
email.parts.each.select{ |email| email.body =~ Regexp.new("Name of the posted material: " + it.name )}.should_not be_empty
email.has_attachments?.should be_true
end
In the spec, I had to check the body of each of the parts of the email, as it was not consistent on whether the attachment was the first or second part.

Related

Rails mailer sending Empty Body on EMail

I have following on config/application.rb
config.action_mailer.default_url_options = { :host => 'xxxxx.com' }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.mandrillapp.com",
:port => 587,
:domain => 'xxxxx.com',
:user_name => 'xxx.xxxxx#gmail.com',
:password => 'xxxxxxxxxxxxxxxx',
:authentication => 'plain',
:enable_starttls_auto => true }
And in app/mailers/welcome_mailer.rb
def welcome_email(user)
#user = user
#lang=I18n.locale
if #user.email.present?
begin
headers = {
:subject => welcome_email_subject(#user).to_s,
:from => ADMINISTRATIVE_EMAIL,
:to => user.email
}
mail(headers)
rescue Exception => e
abort
end
end
end
I have a template on /app/views/welcome_mailer/welcome_email.html.erb
I am using this mailer action for sending welcome emails along with confirmation link by using devise.For that I have done the following on /config/initializers/welcome_mailers.rb
module Devise::Models::Confirmable
def send_on_create_confirmation_instructions
if self.email
WelcomeMailer.welcome_email(self).deliver
end
end
def send_reset_password_instructions
generate_reset_password_token! if should_generate_reset_token?
WelcomeMailer.generate_password(self).deliver
end
end
Even though the development I have used same smtp configurations I am getting empty body for the mail sent on production and the same working fine in development(local).By the way my production environment is Amazon EC2. Initally 15 days before I have got the same issue and I solved by changing the smtp account.Now it is not happening in any order.Suggest with your feedback or comments.
You can overwrite the template route in the mailer config:
class WelcomeMailer < ActionMailer::Base
...
self.template_root = "#{RAILS_ROOT}/app/views/mailers" # Rails.root in 3.x
...
end

rails actionmailer error with 'part' method

I'm upgrading rails 2.3.2 app ot rails 3.
Have unknown error with sending email message in MailerFormError.
MailerFormError is my model: class MailerFormError < ActionMailer::Base
At 1st I have error with 'deliver_send' method (undefined method `deliver_sent' for MailerFormError:Class),
I change it to 'send'. Now I have this:
NoMethodError in LeadsController#create
undefined method `part' for #
My code in controller:
#msg = {}
#msg["errors"] = #lead.errors
#msg["params"] = params
#MailerFormError.deliver_sent(#msg)
MailerFormError.sent(#msg)
This is my class with sending method:
def sent(msg, sent_at = Time.now)
#subject = ("Ошибка при заполнении формы").force_encoding('iso-8859-1').encode('utf-8')
#recipients = 'mymail#gmail.com'
#from = 'mymail#gmail.com'
#sent_on = sent_at
#headers = {}
part( :content_type => "multipart/alternative" ) do |p|
p.part :content_type => "text/plain",
:body => render_message("sent.plain.erb", :msg=>msg )
end
end
1) for Rails 3, to send your notification in your controller , you have to write this :
MailerFormError.sent(#msg).deliver
2) And you have to rewrite your 'sent' method in the Rails 3 way :
def sent(msg, sent_at = Time.now)
...
mail(:to => '...', :from => '...', :subject => '...') do |format|
format.html
format.text
end
...
end
You can also create the text version and html in your view directory app/views/mail_form_error : sent.text.erb and sent.html.erb

Rails 3 - Paperclip - Email Attachments

Attempting to email a Paperclip attachment in Rails 3.
Invoice model:
class Invoice < ActiveRecord::Base
has_attached_file :attachment,
:url => "http://server/app/attachments/:id/:style/:basename.:extension",
:path => ":rails_root/public/attachments/:id/:style/:basename.:extension"
validates_attachment_presence :attachment
validates_attachment_size :attachment, :less_than => 5.megabytes
end
account_mailer:
def email_approver(invoice)
#subject = 'Invoice Approval Request'
#body["invoice"] = invoice
#attachment "application/octet-stream" do |a|
#a.body = File.read(invoice.attachment.to_file.path)
#a.filename = invoice.attachment_file_name
#end
#recipients = [invoice.approver_email, invoice.alternate_approver_email].compact
#from = "ADMIN"
#sent_on = Time.now
#headers = {}
end
error:
NoMethodError (undefined method `filename=' for #<Mail::Part:0x00000002566dd0>):
Any ideas?
Thanks!
Did you tries something like this
attachments[invoice.attachment_file_name] = File.read(invoice.attachment.to_file.path)
?
It's an old question, but here's my working solution:
attachments[mymodel.attachment.filename] = File.read(mymodel.attachment.path)

activerecord not destroying

I have the following code which is working for the most part. However I do get error msg's that it can't save because the mail address already exists. How is that possible when I do a destroy_all first?
My guess: there are multiple rows with the same mail address (that is true) and somehow it doesn't seem to destroy those which are inserted with the procedure below.
Is there a way to force it to also take those into account?
namespace :db do
desc "load vdc members from google CSV"
task :load_csv_data => :environment do
require 'csv'
CSV.foreach("data.csv",{:headers=>:first_row}) do |row|
User.destroy_all(:email => row[7])
user = User.new(
:password => "temppwd",
:name => row[1],
:first_name => row[2],
:birth_date => row[3],
:street => row[4],
:zipcode => row[5],
:town => row[6],
:email => row[7],
:certificate_id => Certificate.find_by_name(row[9]).id,
:external_certificates => row[11],
:education_level => row[12],
:phone_number => row[15]
)
if user.save
puts "saved " + row[1] + " " + row[2]
else
puts "Could not save " + row[1] + " " + row[2] + " due to "
puts user.errors.full_messages.first if user.errors.any?
end
end
end
end

Keep getting A sender (Return-Path, Sender or From) required to send a message

class SupportMailer < ActionMailer::Base
default :from => "email1#gmail.com"
def welcome_email(ticket)
case ticket.game
when "gameone"
#ticket = ticket
headers["Reply-to"] = "email1+#{ticket.token}#gmail.com"
headers["Return-Path"] = "email1+#{ticket.token}#gmail.com"
mail(:from => "email1#gmail.com", :to => ticket.email, :subject => "Welcome to 1 Support Ticket")
when "gametwo"
#ticket = ticket
headers["Reply-to"] = "email2+#{ticket.token}#gmail.com"
headers["Return-Path"] = "email2+#{ticket.token}#gmail.com"
mail(:from => "email2#gmail.com", :to => ticket.email, :subject => "Welcome to 2 Support Ticket")
when "gamethree"
#ticket = ticket
headers["Reply-to"] = "email3+#{ticket.token}#gmail.com"
header["Return-Path"] = "email3+#{ticket.token}#gmail.com"
mail(:from => "email3#gmail.com", :to => ticket.email, :subject => "Welcome to 3 Support Ticket")
end
end
end
I've set my default :from, so I don't get why I keep getting this message, I'm also trying to set it via headers to no avail.
here are my settings
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "gmail.com",
:user_name => "emailx#gmail.com",
:password => "password",
:authentication => "plain",
:enable_starttls_auto => true
}
I just call it like so, SupportMailer.support_response(#message).deliver
How do I fix this?
I notice you have no default case for the case statement. If you never end up calling the "mail" method inside your methods in the Mailer class, you'll get that error. Try moving your case statement out to where you call SupportMailer, maybe have methods for each case. That way you never call the SupportMailer unless you've already determined the correct ticket game.