Navigation

Tuesday, March 23, 2010

Rails 3 Action Mailer Example

Update: This tutorial has been tested using Rails 3.0.9. Source code is available on Github: https://github.com/kuntoaji/rails_3_contact_form

Create Rails app
rails new my_app
cd my_app
Create mailer
rails g mailer notifier
Create Support Resource
rails g resource support
Remove Support's migration (we don't use database)
rm db/migrate/20100323085113_create_supports.rb
Sending email via Gmail
# config/initializers/mailer.rb
ActionMailer::Base.delivery_method = :smtp

ActionMailer::Base.smtp_settings = {
:enable_starttls_auto => true,
:address => 'smtp.gmail.com',
:port => 587,
:domain => "yourdomain.com",
:user_name => '[email protected]',
:password => 'yourpassword',
:authentication => 'plain'
}
# app/mailer/notifier.rb
class Notifier < ActionMailer::Base

  def support_notification(sender)
    @sender = sender
    mail(:to => "[email protected]",
         :from => sender.email,
         :subject => "New #{sender.support_type}")
 end
end
# app/views/notifier/support_notification.html.erb
hello world!
<%= @sender.content %>
# config/routes.rb
resources :supports, :only => [:new, :create]
# app/controller/supports_controller.rb
class SupportsController < ApplicationController
  def new
    # id is required to deal with form
    @support = Support.new(:id => 1)
  end

  def create
    @support = Support.new(params[:support])
    if @support.save
      redirect_to('/', :notice => "Support was successfully sent.")
    else
      flash[:alert] = "You must fill all fields."
      render 'new'
    end
  end
end
# app/models/support.rb
class Support
  include ActiveModel::Validations

  validates_presence_of :email, :sender_name, :support_type, :content 
  # to deal with form, you must have an id attribute
  attr_accessor :id, :email, :sender_name, :support_type, :content

  def initialize(attributes = {})
    attributes.each do |key, value|
      self.send("#{key}=", value)
    end
    @attributes = attributes
  end

  def read_attribute_for_validation(key)
    @attributes[key]
  end

  def to_key
  end

  def save
    if self.valid?
      Notifier.support_notification(self).deliver!
      return true
    end
    return false
  end
end
# app/views/supports/new.html.erb

Contact Us

<%= render 'form' %>
# app/views/supports/_form.html.erb
<% form_for @support, :url => { :action => "create" }, :html => { :method => :post } do |f| %>
  <p>
    <%= f.label "Name" %>
  </p>
  <p>
    <%= f.text_field :sender_name %>
  </p>
  <p>
    <%= f.label "Email" %>
  </p>
  <p>
    <%= f.text_field :email %>
  </p>
  <p>
    <%= f.label "Type" %>
  </p>
  <p>
    <%= f.select :support_type, options_for_select(["Bug", "Ask", "Idea"]) %>
  </p>
  <p>
    <%= f.label "Details" %>
  </p>
  <p>
    <%= f.text_area :content %>
  </p>
  <p>
    <%= f.submit "Submit" %>
  </p>
<% end %>
Raise Exception When Mailer Can't Send the Email (Development mode)
# config/environments/development.rb
# ...
config.action_mailer.raise_delivery_errors = true
# ...
done! To try this code, open http://localhost:3000/supports/new or try the code via Rails console.
support = Support.new(:email => "[email protected]", :sender_name => "my name", :support_type => "bug", :content => "this is my support")
support.save

38 comments:

  1. I had to add
    gem 'mail'
    to my Gemfile then run
    bundle install
    ReplyDelete
  2. undefined method `to_key' for #1}>
    ReplyDelete
  3. I'm guessing this is deprecated? I've been working at this for a couple hours and there are quite a few reasons why this does not work anymore.
    ReplyDelete
  4. @depassion & @anonymous: Sorry, this tutorial used Rails 3 Beta 1. Not tested yet for Rails 3 final version.
    ReplyDelete
  5. Tested on Rails 3.0.0 final version.
    ReplyDelete
  6. Its a great tutorial, and I've followed it several times over the last few months, but there are a couple of bugs in the code that I feel I should point out.

    In the section titled "Remove Support's migration (we don't use database)" you advise to run "rm db/migrate/20100323085113_create_posts.rb" - which will totally break everything because this code removes the Posts migration.... You probably meant for the user to run "rm db/migrate/XXXXXXXXXXX_create_supports.rb" .... :)

    In the section titled "Sending email via Gmail" you provide some code to be pasted into application.rb, but there is a trailing comma at the end of the hash and this will cause mayhem too. I would personally advise sticking that code into a mailer.rb intiliazer file to keep the application file clean, but at the very least, I would remove the trailing comma from the hash....

    Anyway, thanks for the tut, as I said, I've followed it a few times now
    ReplyDelete
  7. Thanks for you correction and advise Stephen. I'll update it.
    ReplyDelete
  8. I tried this but got an error:
    NoMethodError: undefined method `support_notification' for Notifier:Class

    Any thoughts?

    I am using rails 3.0.3
    ReplyDelete
  9. stalkert, it's no method error, so you must define support_notification method on Notifier class.

    the code on this tutorial works fine with rails 3.0.3
    ReplyDelete
  10. thanks, it works well with 3.0.3
    ReplyDelete
  11. Cant get it working, having this error: http://pastie.org/1437694

    everything is same as showed here, Please can anybody help!
    ReplyDelete
  12. Great tutorial! Many thanks. What do I need to do if I want to use a database to store supports?
    ReplyDelete
  13. Having the almost the same issue as soend https://gist.github.com/823565 error is there if anyone can help please let me know thank you.
    ReplyDelete
  14. Excellent write-up, it is walks through all the steps of the ActionMailer setup and useful for helping get started.
    ReplyDelete
  15. Thanks for this article. Very helpful.
    ReplyDelete
  16. Hi there. I've followed your great tutorial. For some reason, it isn't sending the email for me. The server log is showing that the email has been sent, but I'm not getting an email in my account when testing from localhost. Is there a setting I need to change? I'm sure I have the email address correct (it is an account managed by gmail). I can get other email to this account. Thanks!
    ReplyDelete
  17. Julie

    Try opening config/environments/test.rb and change this line:

    config.action_mailer.delivery_method = :test

    to:

    config.action_mailer.delivery_method = :smtp

    This should allow you to send real emails with rails 3.0.5 assuming you followed it closely. I just got the tutorial working 100% with the latest updates.

    If you leave the line as-is it stores outgoing emails in an array, which could still be viewed if you didn't want to send real emails.
    ReplyDelete
  18. Works great, Thank you! Tested on Rails 3.0.3
    ReplyDelete
  19. For some inexplicable reason, I could not get the controller to include the model without explicitly requiring it. I thought this was automagic?
    ReplyDelete
  20. uninitialized constant Support::Notifier

    app/models/support.rb:25:in `save'
    app/controllers/supports_controller.rb:8:in `create'

    ...any ideas?

    Thanks!
    ReplyDelete
  21. I'm having the same exact same problem as anonymous posted above:

    uninitialized constant Support::Notifier

    app/models/support.rb:25:in `save'
    app/controllers/supports_controller.rb:10:in `create'

    How did other people deal with this?
    ReplyDelete
  22. @bludragon, anonymous, & apeiron: what rails version do you use?
    ReplyDelete
  23. I had the same error: uninitialized constant Support::Notifier. Restarted the server and it worked.
    ReplyDelete
  24. Has anyone had any problems with not finding a table? I used Contact, rather than support and am getting the following error; Could not find table 'contacts' from my form?
    ReplyDelete
  25. Hi ReddoBowen

    Probably because you kept the line :
    "class Contact < ActiveRecord::Base" inside the app/models/contact.rb file. Because an ActiveRecord inherited class will automaticly search for a table ;).

    You should delete the " < ActiveRecord::Base" part so that you have those two lines instead :

    class Contact
    include ActiveModel::Validations

    Hope it helps :)
    ReplyDelete
  26. I changed it from support to contact, and although I believe the code is all correct, it will not render the form partial for the contact controller.

    What could be causing this?
    ReplyDelete
  27. @Mike, its just missing a = after the initial <%, as in

    <%= form_for @support, :url => { :action => "create" }, :html => { :method => :post } do |f| %>
    ReplyDelete
  28. Hi, I was just doing this great tutorial and as for the command prompt it's working and sending me the email. Problem is, I never receive any emails. Any suggestions why that could happen? I already did the changing suggestet some comments above:

    Try opening config/environments/test.rb and change this line:

    config.action_mailer.delivery_method = :test

    to:

    config.action_mailer.delivery_method = :smtp

    and I also got rid of the mistake that was pointed out just two comments earlier some time ago. So it's not giving me any mistake massages, it says it's working and sending the mail... I really don't have any idear anymore, why I still don't get the emails.

    Maybe someone here has an idear?
    ReplyDelete
  29. @Ithil: Have you checked the spam inbox?
    ReplyDelete
  30. Yes, the mails don't go into my spam inbox either. I had the same smtp settings in my PHP form and there everithings working fine and I receive the emails...
    ReplyDelete
  31. @Ithil: Could you provide your mailer configuration and log file to me?
    ReplyDelete
  32. I don't get any errors..It shows the success flash message but i dont get any mail from the contact form. I am using Rails 3.0.7

    Any suggestions. I followed all the above steps
    ReplyDelete
  33. maybe your username or password is incorrect. try to change deliver to deliver! to debug mailer:

    Notifier.support_notification(self).deliver

    to

    Notifier.support_notification(self).deliver!
    ReplyDelete
  34. If I try to visit /supports/new I get the message:

    Table 'appname_development.supports' doesn't exist

    -> I deleted the migration file but in some way rails thinks he needs the db table for this? any ideas?
    ReplyDelete
  35. In order to pass the text you should also delete the fixture support.yml
    ReplyDelete
  36. Great tutorial! I went to implement it, and hit a bug after I hit the 'submit' button on a test email:

    NoMethodError in SupportsController#create

    You have a nil object when you didn't expect it!
    You might have expected an instance of ActiveRecord::Base.
    The error occurred while evaluating nil.[]

    app/models/support.rb:16:in `read_attribute_for_validation'
    app/models/support.rb:23:in `save'
    app/controllers/supports_controller.rb:9:in `create'
    ReplyDelete
  37. @dietcoupon:
    change
    class Support < ActiveRecord::Base

    to

    class Support
    ReplyDelete