This is a quick walk through on how to:
- make a contact form in rails 6,
- test it locally, and
- move it into production using heroku and the MailGun addon
This uses the free heroku and mailgun plans. If you get stuck on any part, check the full code here.
Create a new rails app (we don't need a database for this, so skip that option if you prefer)
rails new contactforminrails6 --database=postgresql
cd
into your newly create rails application with cd contactforminrails6
.
Add the mail_form gem to Gemfile
and bundle install
to install it.
# Gemfile
gem 'mail_form'
If you used the database
option when creating the rails app, run rake db:create db:migrate
to create and migrate it. If you didn't use that option, ignore this step.
Let's add the new
and create
routes. After doing so, your routes.rb
file should look like this:
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :contacts, only: [:new, :create]
end
Generate a new controller with
rails g controller contacts
Add this to app/controllers/contacts_controller.rb
:
def new
@contact = Contact.new
end
def create
@contact = Contact.new(params[:contact])
@contact.request = request
if @contact.deliver
flash.now[:success] = 'Message sent!'
else
flash.now[:error] = 'Could not send message'
render :new
end
end
Create a file called app/models/contact.rb
and place the following in it (changing "[email protected]"
to the address you wish for the form to send to):
class Contact < MailForm::Base
attribute :name, validate: true
attribute :email, validate: /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
attribute :message
attribute :nickname, captcha: true
# Declare the e-mail headers. It accepts anything the mail method
# in ActionMailer accepts.
def headers
{
:subject => "Contact Form Inquiry",
:to => "[email protected]",
:from => %("#{name}" <#{email}>)
}
end
end
In the above, I haven't validated the presence of a message
, but if you prefer to, simply add validate: true
in the same way it's been done for the :name
field.
You may have noticed the nickname
field. That's a bit odd, isn't it? It's from the mail_form gem docs, and it's purpose is to trick bots! We'll make that a field hidden in the view, so no human would submit to it, so if we receive a value in that field, we'll conclude that it's a bot, and ignore the request!
Let's create our contact form in app/views/contacts/new.html.erb
:
<div class="container">
<h1>Contact Form</h1>
<%= form_for @contact do |f| %>
<div class="col-md-6">
<%= f.label :name %></br>
<%= f.text_field :name, required: true, class: "contact-form-text-area" %></br>
<%= f.label :email %></br>
<%= f.text_field :email, required: true, class: "contact-form-text-area" %></br>
<%= f.label :message %></br>
<%= f.text_area :message, rows: 8, cols: 40, required: true, class: "contact-form-text-area",
placeholder: "Send me a message"%></br>
<div class= "d-none">
<%= f.label :nickname %>
<%= f.text_field :nickname, :hint => 'Leave this field blank!' %>
</div>
<%= f.submit 'Send Message', class: 'btn btn-primary' %>
</div>
<% end %>
</div>
and a simple confirmation message in app/views/contacts/create.html.erb
<div class="container">
<h1>Contact Form</h1>
<h3>Thank you for your message!</h3>
</div>
lastly, some informative flash messages, by adding this to app/views/layouts/application.html.erb
between <body>
and <%= yield %>
:
<div class="container">
<br>
<% flash.each do |key, message| %>
<p class="alert alert-<%= key %>"><%= message %></p>
<% end %>
</div>
The only critical thing to do is ensure that the nickname field isn't displayed to humans. You can simply delete it from your form view if preferred. However, another way to do this is to use some CSS to hide it. Here, I'll add bootstrap to the rails app and use the d-none
class we gave that form field will hide it for us!
Run this to install bootstrap bin/yarn add bootstrap jquery
Then add this to app/javascript/packs/application.js
:
window.$ = window.jQuery = require("jquery");
import "bootstrap/dist/js/bootstrap.bundle.js"
Lastly, add this to app/assets/stylesheets/application.scss
. If that file doesn't exist, you can rename app/assets/stylesheets/application.css
. Then add this:
//= require bootstrap/dist/css/bootstrap
Restart your rails server.
Note: if you prefer not to use bootstrap, that's okay, simply add a CSS class (e.g. 'hidden') to the nickname field in the form, and define it in app/assets/stylesheets/application.scss
like so:
.hidden {
display: none;
}
At this point, we should have a fully functional contact form in our local environment. You can confirm this by submitting the form (leaving the nickname field blank), and checking the server logs to ensure the mail is sent.
I like to do a little more testing locally, just to ensure everything works as expected. For this, I use mailcatcher. Install it with gem install mailcatcher
.
Add these to config/environments/development.rb
:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { :address => '127.0.0.1', :port => 1025 }
config.action_mailer.raise_delivery_errors = false
Now another terminal window and run the simple command mailcatcher
to start it, then pasting this http://127.0.0.1:1080/ into your browser to show a virtual inbox with all your outgoing mail! Note: you'll need to restart your rails server for this to take effect.
Try submitting your contact form again to see the outbound mail appear in mailcatcher. Once that works, it's time to take this functional form on to the web!
If you don't already have the heroku CLI, you'll need to install and login to the heroku CLI.
Create a new heroku app with heroku create <name of new app>
. I used the same name as the rails app e.g. heroku create contactforminrails6
, but you don't have to. Check the console output to confirm that heroku created your new app, it will give you the app URL, e.g. https://contactforminrails6.herokuapp.com.
First we must move the app code to GitHub. In the browser, head to GitHub, and create a new repository.
In the terminal, cd
into the root directory of your app and run:
git init
git add .
git commit -m "First commit to github repo"
git remote add origin https://github.com/username/name_of_new_repository.git
git push -u origin master
Be sure to replace https://github.com/username/neme_of_new_repository.git
in the above with the URL of your new repository.
Now your rails app will be on GitHub, but not yet on heroku. But pushing it to heroku is easy:
git push heroku master
You should see console output indicating it's building. Wait a few minutes while heroku builds and deploys your app.
Once your app is build and it has deployed, you can visit it at the URL heroku provided you when you created it. E.g. https://contactforminrails6.herokuapp.com/contacts/new. You now have a working app on heroku! If you see a 'page not found' message, be sure you appended /contacts/new
on the URL (the root URL doesn't have routes defined for it yet, so it's expected that there should not be any page found for it).
We still need to configure the mailer to work in production. To do this, we'll use the heroku Mailgun addon. Simply run this:
heroku addons:create mailgun:starter
And configure our app to use Mailgun in production by adding the following inside config/environments/production.rb
:
ActionMailer::Base.smtp_settings = {
:port => ENV['MAILGUN_SMTP_PORT'],
:address => ENV['MAILGUN_SMTP_SERVER'],
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => 'yourapp.heroku.com', # UPDATE THIS VALUE WITH YOUR OWN APP
:authentication => :plain,
}
ActionMailer::Base.delivery_method = :smtp
In the above, remember to change yourapp.heroku.com
to the name of your app, leave everything else as-is.
Now commit this to git and push to heroku with:
git add .
git commit -m "Updating production mailer settings to use MailGun"
git push
git push heroku master
That will push your updated code to GitHub and then build it on heroku.
Once that's done, head to your conctact form on your heroku app. Mine's at https://contactforminrails6.herokuapp.com/contacts/new and test your form!
You won't receive any mail just yet. If you check your heroku logs with heroku logs -n 500
, you'll see something like:
Net::SMTPUnknownError (could not get 3xx (421: 421 Domain sandbox8f9918227df9s7b6s8b9s73503.mailgun.org is not allowed to send: Sandbox subdomains are for test purposes only. Please add your own domain or add the address to authorized recipients in Account Settings.
This is easy to fix. In the browser, go to https://dashboard.heroku.com/apps/contactforminrails6 (replacing contactforminrails6
with the name of your app). Simply click on the Mailgun icon - doing so will activate your Mailgun addon:
Give your contact form a try, wait a few seconds and check your inbox!
Congratulations - you just built a fully functional contact form in rails 6 and deployed it to production!
- The completed form can be found here
- Full code for this tutorial can be found here
- This video tutorial is very useful
- As is this tutorial
- This tutorial uses an outdated method (sending via Gmail), but may still be useful.
When I made the controller, I chose to name it
ContactController
believing it would not make a difference. But I don't see how it leads to this particular error.