Leo Lou bio photo

Leo Lou

Autodidacticism. Erlang, elixir, obj-c, ruby.

Github

Create a Rails 4 site with a simple contact us form

a complete tutorial

Contents

This tutorial uses Twitter Bootstrap, bootstrap-sass, SimpleForm, MailForm, and Guard. If you are familiar with Rails basics and bootstrap-sass, you can skip to the Add contact us form section

1. Create a Rails 4 Project

$ rails new project_name
$ cd project_name

Edit Gemfile. Add the following gems.

# Front-end {
gem 'bootstrap-sass', '~> 2.3.2.0'
gem 'haml-rails', '~> 0.4.0'
# }
# Forms, mail {
gem 'mail_form', '~> 1.5.0.rc'
gem 'simple_form', '~> 3.0.0.rc'
# }
# Development (Optional) {
gem 'better_errors', group: :development
gem 'quiet_assets', group: :development
# }
# Development Guard {
gem 'guard-rails', group: :development
gem 'guard-livereload', group: :development
gem 'rack-livereload', group: :development
gem 'guard-bundler', group: :development
# }

See the example Gemfile with extra gems such as pg, bootswatch, heroku, font awesome

Install gems by running

$ bundle install

Setup guard

We use guard to automate gem installations, livereload, rails server restarts, etc.

Add guard definition to your Guardfile by running

$ guard init rails bundler livereload

You should see

08:21:14 - INFO - rails guard added to Guardfile, feel free to edit it
08:21:14 - INFO - bundler guard added to Guardfile, feel free to edit it
08:21:14 - INFO - livereload guard added to Guardfile, feel free to edit it

Booting Rails server with guard

Start Rails server and livereload by running

guard

Visit http://localhost:3000 in your browser. You should see the default Rails welcome page.

Rails 4 welcome page

2. Create a home controller

rails g controller home index

rails generate controller results

Edit config/routes.rb

Replace get "home/index" with root "home#index" So that when you visit http://localhost:3000, the request will be handled by the home controller.

After saving the file, guard should restart your Rails server. Check if there’s any errors in the guard console. Visit http://localhost:3000 and you should see

Home#index Find me in app/views/home/index.html.haml

3. Add styles

Add Bootstrap JavaScript and SCSS

We need some styles! Let’s add Twitter bootstrap.

Add in config/application.rb

config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)

Rename app/assets/stylesheets/application.css to app/assets/stylesheets/application.css.scss

Add in app/assets/stylesheets/application.css.scss

@import "bootstrap";

Add in app/assets/javascript/application.js, before //= require_tree .

//= require bootstrap

The page should now look like

home controller with bootstrap style

Convert erb to Haml

The default Rails uses erb. Now we convert it to haml by html2haml

You can install html2haml as:

$ gem install html2haml

Convert the default layout in erb to haml

cd app/views/layouts
html2haml application.html.erb > application.html.haml

Remove the erb file

rm application.html.erb

Add Bootstrap HTML and navigation bar

Edit app/views/layout/application.html.haml Add between %body and =yield

.navbar.navbar-fixed-top
  .navbar-inner
    .container
      %button.btn.btn-navbar{"data-target" => ".nav-collapse", "data-toggle" => "collapse", :type => "button"}
        %span.icon-bar
        %span.icon-bar
        %span.icon-bar
      %a.brand{:href => root_url}
        brand
      .nav-collapse.collapse
        %ul.nav.pull-right
          %li
            = link_to t('Contact'), "todo"

Because we are using fixed navbar in this example, append app/assets/stylesheets/application.css.scss

body {
  padding-top: $navbarHeight + 20px;
}

Edit app/views/home/index.html.haml

.container
  .row
    .span12
      %h1 Home#index
      %p Find me in app/views/home/index.html.haml

Your page should look like this now.

Style home controller with a navigation bar

4. Add contact us form

Run the simple_form generator

rails g simple_form:install --bootstrap

Controller

rails g controller contacts

Edit config/routes.rb, remove the generated routes for contacts. Add

resources "contacts", only: [:new, :create]

Replace app/controllers/contacts_controller.rb with

class ContactsController < ApplicationController
  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(params[:contact])
    @contact.request = request
    if @contact.deliver
      flash.now[:error] = nil
      flash.now[:notice] = 'Thank you for your message!'
    else
      flash.now[:error] = 'Cannot send message.'
      render :new
    end
  end
end

Model

Create app/models/contact.rb with

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 => "My Contact Form",
      :to => "your_email@example.org",
      :from => %("#{name}" <#{email}>)
    }
  end
end

(Trimmed version of the MailForm example)

Views

Create app/views/contacts/new.html.haml with

.container
  %h1 Contact
  = simple_form_for @contact, :html => {:class => 'form-horizontal' } do |f|
    = f.input :name, :required => true
    = f.input :email, :required => true
    = f.input :message, :as => :text, :required => false, :input_html => {:rows => 10}

    .hidden
      = f.input :nickname, :hint => 'Leave this field blank!'
    .form-actions
      = f.button :submit, 'Send message', :class=> "btn btn-primary"

Create app/views/contacts/create.html.haml with

.container
  %h1 Thank you for your message.
  %p We'll get back to you soon.

Add in app/assets/stylesheets/application.css.scss

.hidden { display: none; }

Edit app/views/layout/application.html.haml, add/edit the menu item

%li
  = link_to t('Contact'), new_contact_path

Add flashes just after the %body

.container
  - flash.each do |name, msg|
    - if msg.is_a?(String)
      %div{:class => "alert alert-#{name == :notice ? "success" : "error"}"}
        %a.close{"data-dismiss" => "alert"}
          &times;
        = content_tag :div, msg, :id => "flash_#{name}"

Results

Open http://localhost:3000/contacts/new in your browser

You should see the working contact us form with server-side validations.

Rails 4 Mail form with validations

For real e-mail delivery, you should set up SMTP in the environment settings.

Bonus / Further readings