Formtastic forms in Rails for Beginners

You will build a lot of forms in Rails and Formtastic is a gem that will help speed up your form creation. This enables you to have great forms with a lot of extras in the same amount of time it takes to create a basic from in Rails.

Formtastic code is very similar to the standard Rails form code though it will generate more relevant HTML that can be used to make your forms more useable.

This article will go over getting started with Formtastic in Rails as a beginner, covering the basic types of form elements. Formtastic's fantastic readme is essential reading, though I hope I can ease beginners into the process of getting started.

Prerequisites

You will need a basic understanding of model validations and Active record associations which has been covered in previous articles.

You should have a solid understanding of how to build forms in Rails. You can always review my Rails forms for Beginners article to help bring you up to speed.

The Setup

All the examples in the article will be built and tested using the following versions:
  • Rails: 4.1.8
  • Ruby 2.1.5
  • Formtastic 3.1.2
  • MiniTest 5.4.3

Installing Formtastic

The standard: add the gem to your Gemfile and run bundle to install it

gem 'formtastic', '~> 3.1'

Run the Formtastic generator, to install the Formtastic template into your app. This is a one time only step.

rails generate formtastic:install

Formtastic comes with some default css styles that are great for starting out.

Add the following lines to: app/assets/stylesheets/application.css

  *= require formtastic
  *= require my_formtastic_changes

You will need to generate a blank app/assets/stylesheets/my_formtastic_changes.css file. You can do this on Mac/Linux with the bash command:

touch app/assets/stylesheets/my_formtastic_changes.css

See the Formtastic Readme if you require IE6/7 support for further steps.

Generate a Scaffold

To start using Formtastic, you just have to generate your standard Rails scaffold using the exact same syntax as before. Here is an example with a Article model.

rails generate scaffold Article title:string body:text

The generated file app/views/posts/_form.html.erb will look a little different to before.

<%= semantic_form_for @article do |f| %>
  <%= f.inputs do %>
    <%= f.input :title %>
    <%= f.input :body %>
  <% end %>

  <%= f.actions do %>
    <%= f.action :submit, :as => :input %>
  <% end %>
<% end %>

This is how a standard Rails scaffold form looks. Formtastic might look like less, but it does so much more.

<%= form_for(@article) do |f| %>
  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@article.errors.count, "error") %> 
       prohibited this article from being saved:</h2>

      <ul>
      <% @article.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :body %><br>
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Lets look at the generated HTML

Formtastic HTML

<form accept-charset="UTF-8" action="/articles" class="formtastic article" 
id="new_article" method="post" novalidate="novalidate">
<div style="display:none"><input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="N/eg6LfW/w=" />
</div>
<fieldset class="inputs"><ol>
  <li class="string input optional stringish" id="article_title_input">
    <label class="label" for="article_title">Title</label>
    <input id="article_title" maxlength="255" name="article[title]" type="text" />
  </li>
  <li class="text input optional" id="article_body_input">
    <label class="label" for="article_body">Body</label>
    <textarea id="article_body" name="article[body]" rows="20"> </textarea>
  </li>
</ol></fieldset>
<fieldset class="actions"><ol>
  <li class="action input_action " id="article_submit_action">
    <input name="commit" type="submit" value="Create Article" />
  </li>
</ol></fieldset>
</form>

As you can see, everything is wrapped in fieldset, ol and li tags with id and label tags for each input field. This gives Formtastic a lot of powerful ways to use css for styling which we will see later.

Rails scaffold HTML

<form accept-charset="UTF-8" action="/articles" class="new_article" id="new_article" method="post">
  <div style="display:none">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="/1iuG++UmXpoeu55MMKwozGA=" />
  </div>
  <div class="field">
    <label for="article_title">Title</label><br>
    <input id="article_title" name="article[title]" type="text" />
  </div>
  <div class="field">
    <label for="article_body">Body</label><br>
    <textarea id="article_body" name="article[body]"> </textarea>
  </div>
  <div class="actions">
    <input name="commit" type="submit" value="Create Article" />
  </div>
</form>
As you can see very similar HTML. Formtastic has a few extras to give you greater flexibility.

How does it look?

Formtastic Form

Rails Form

As you can see, the Formtastic form aligns the elements to make for a smoother look.

NOTE: Formtastic forms will grow/shrink to fill the whole width of the screen. Generally you will want to put the form in a div with a fixed width.

Hints and Errors

Hints

Adding a hint to your inputs is as simple as adding a hint value to the input

  <%= f.inputs do %>
    <%= f.input :title, hint: 'Make it a good one' %>
    <%= f.input :body %>
  <% end %>

Here is how the hint looks

Labels

You can easily change the label for an input, as human friendly names don't always match up to table column names.

  <%= f.inputs do %>
    <%= f.input :title, label: 'Article Title', hint: 'Make it a good one' %>
    <%= f.input :body %>
  <% end %>

Here is how the custom label looks

Errors

If a field is required, Formtastic will automatcially add an * next to the name and will display an error below the input field if it is blank.

class Article < ActiveRecord::Base
  validates_presence_of :title
end

Formtastic will automatically display errors next to the input.

NOTE: Formtastic will ONLY do this for all error messages that relate to the input fields in the form. If you have a generic message or message that applies to an input field that isn't displayed in the form, then you need to add in code to handle that message. The error reporting code in the standard Rails scaffold will work.

Custom HTML attributes

You can customize all your input fields HTML attributes using the input_html hash option.

<%= f.input :title, label: 'Article Title', hint: 'Make it short', input_html: { size: 10 } %>
<%= f.input :body, input_html: { rows: 10, cols: 25 } %>

What about Relationships?

Formtastic automatically handles relationships between models for you.

# generate the Article scaffold with a category
rails generate scaffold Article title:string category:references body:text author:string

# if you already have the Article model, here is the migration
rails generate migration AddCategoryToArticle category:references author:string
# add the relationship to the Article model
belongs_to :category

# update strong parameters in the Article Controller
def article_params
  params.require(:article).permit(:title, :body, :category_id, :author)
end

# generate the Category model, we are adding in a cat_type for further classification
rails generate model Category name:string cat_type:integer
# create categories in the Rails console
Category.create(name: 'Technology', cat_type: 1)
Category.create(name: 'Lifestyle', cat_type: 2)
Category.create(name: 'Business', cat_type: 1)
Category.create(name: 'Entertainment', cat_type: 2)

Formtastic will automatically reference the relationship if you reference it by the relationship name category and not by the table column name category_id

<%= f.inputs do %>
  <%= f.input :title %>
  <%= f.input :category %>
  <%= f.input :body %>
<% end %>

Here is how it will look

Further Select Tweaks

Once again you can customize this further with a few options.

Remove the blank entry

<%= f.input :category, include_blank: false %>

Use a custom drop down list

<%= f.input :category, collection: Category.where(cat_type: 1) %>

Merged all into one

<%= f.input :category, collection: Category.where(cat_type: 1), include_blank: false %>

What about fully custom selects in a string field?

You can specify any field to use a select box and use a Model collection or a custom hash/array for the data.

<%= f.input :author, as: :select, collection: ['Pam','Steve', 'Dave', 'Meg'] %>

NOTE: You can combine all the Formtastic options together to get your input fields just the way you want them.

See the Formtastic site for extensive examples of all the possibilities.

One last one: Checkboxes

So to round out our checklist, lets add a checkbox! For this example, lets add tags

# generate the Tag and Tagging models
rails generate model Tag name:string
rails generate model Tagging article:references tag:references

# Add the tag relationship to the Article model
class Article < ActiveRecord::Base
  has_many :taggings
  has_many :tags, through: :taggings # notice the use of the plural model name
end

# update strong parameters in the Article Controller
def article_params
  params.require(:article).permit(:title, :body, :category_id, :author, { :tag_ids => [] })
end

# create tags
Tag.create(name: 'Fun')
Tag.create(name: 'Scary')
Tag.create(name: 'Sad')
Tag.create(name: 'Excited')

Now the Formtastic Form Code

<%= f.inputs do %>
  <%= f.input :title, label: 'Article Title', hint: 'Make it a good one', input_html: { size: 10 } %>
  <%= f.input :category %>
  <%= f.input :body, input_html: { rows: 10, cols: 25 } %>
  <%= f.input :tags, as: :check_boxes %>
  <%= f.input :author, as: :select, collection: ['Pam','Steve', 'Dave', 'Meg'] %>
<% end %>
As you can see, its as simple as specifying the tags relationship should be displayed as check boxes. Formtastic understands the has_many :tags, through: :taggings relationship and automatically handles things for you.

Lots of Formtastic Inputs

Formtastic will choose an input based on the column type, though you can manually specify the type as show above. Here is a short list taken from the Formtastic site:

  • :select – a select menu. Default for ActiveRecord associations: belongs_to, has_many, and has_and_belongs_to_many.
  • :check_boxes – a set of check_box inputs. Alternative to :select for ActiveRecord-associations: has_many, and has_and_belongs_to_many.
  • :radio – a set of radio inputs. Alternative to :select for ActiveRecord-associations: belongs_to.
  • :password – a password input. Default for column types: :string with name matching "password".
  • :text – a textarea. Default for column types: :text.
  • :date_select – a date select. Default for column types: :date.
  • :datetime_select – a date and time select. Default for column types: :datetime and :timestamp.
  • :time_select – a time select. Default for column types: :time.
  • :boolean – a checkbox. Default for column types: :boolean.
  • :string – a text field. Default for column types: :string.
  • :number – a text field (just like string). Default for column types: :integer, :float, and :decimal.
  • :file – a file field. Default for file-attachment attributes

Formtastic can handle just about everything you throw at it, even nested forms. Be sure to check the Formtastic site

What next?

Install Formtastic and see how much it speeds up your form development.

Make sure you visit the The official Formtastic site and view the readme to see what is there.

Don't forget, you can use your own custom CSS to completely control the look and feel of your form. Formtastic generates great HTML for you to hook into.

Resources

The official Formtastic site - Required reading, one of the best readme documents out there.

Formtastic Wiki - Even more details and guides for Formtastic.

Rails forms for Beginners

Forms are second only to normal views for how your users interact with your application. Designing easy to use forms that make it easy for your users to enter their data into your app can make a big difference in how 'hard' your users view your application.

Here I will go over creating forms for your models in Rails. In a future article I will cover using a gem called formtastic to help speed up form creation.

Prerequisites

You will need a basic understanding of model validations and Active record associations which has been covered in previous articles.

The Setup

All the examples in the article will be built and tested using the following versions:
  • Rails: 4.1.7
  • Ruby 2.1.4
  • MiniTest 5.4.3

To help with the testing side of things, you can read my article on Rails Automated Testing Setup for Beginners to help you get setup with automated testing if you haven't already.

The data models

So we can have a lot of examples, lets build a classic Blog post model and add enough fields so we can have an example of each type of input:

  • Title - standard input
  • Body - Text input
  • Category - Select list
  • Status - Radio Buttons
  • Tags - Check Boxes

Now normally you would either have a category (belongs_to association) or tag (has_many association). For our demonstration purposes we have both.

Generate our models

First we generate our models so Rails will create all our files.

# use a scaffold for post as this will generate our initial form view.
rails generate scaffold Post title:string body:text category_id:integer status_id:integer
rails generate model Category name:string
rails generate model Status name:string
rails generate model Tag name:string
rails generate model Tagging post_id:integer tag_id:integer
Don't forget to migrate the database with: rake db:migrate

Now add relationships to the models

class Post < ActiveRecord::Base
  belongs_to :category
  belongs_to :status
  has_many :taggings
  has_many :tags, through: :taggings # notice the use of the plural model name
end
class Category < ActiveRecord::Base
  has_many :posts
end
class Status < ActiveRecord::Base
  has_many :posts
end
class Tagging < ActiveRecord::Base
  belongs_to :post  # foreign key - post_id
  belongs_to :tag   # foreign key - tag_id
end
class Tag < ActiveRecord::Base
  has_many :taggings
  has_many :posts, through: :taggings # notice the use of the plural model name
end

Next some seed data to get started

These commands need to be run in a rails console

# create categories
Category.create(name: 'Technology')
Category.create(name: 'Lifestyle')
Category.create(name: 'Business')
Category.create(name: 'Entertainment')
# create status
Status.create(name: 'just created')
Status.create(name: 'review')
Status.create(name: 'published')
# create tags
Tag.create(name: 'Fun')
Tag.create(name: 'Scary')
Tag.create(name: 'Sad')
Tag.create(name: 'Excited')

Creating a Standard form

Thanks to the Rails scaffold command, you already have a form for the Post model.

app/views/posts/new.html.erb will look like:

<h1>New post</h1>

<%= render 'form' %>

<%= link_to 'Back', posts_path %>

It renders the form separately so that both the new and edit views can use the same form.

app/views/posts/_form.html.erb will look like:

<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:
      </h2>

      <ul>
      <% @post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :body %><br>
    <%= f.text_area :body %>
  </div>
  <div class="field">
    <%= f.label :category_id %><br>
    <%= f.number_field :category_id %>
  </div>
  <div class="field">
    <%= f.label :status_id %><br>
    <%= f.number_field :status_id %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Lets break it down into the core parts and go through one by one.

Form For

<%= form_for(@post) do |f| %>
  <!-- ... the form objects -->
<% end %>

The form_for(@post) tags tell rails that this form is for a @post object. This will tell rails what action to use when the submit button is pressed. You can pass a few different options, though for now just leave it as is and it will work.

Error Messages

  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:
      </h2>
      <ul>
      <% @post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

Next is the error message code, if you have any validations in your model and the form fails to save, then Rails will populate the .errors method and this will display them to the user.

Once again, Rails has a lot of sensible defaults and its best to leave this alone for now.

Text Field

The most common field for a form, matches up to a string column. This is your standard single row input.

  <%= f.label :title %><br>
  <%= f.text_field :title %>
### Generated output ###
  <label for="post_title">Title</label><br>
  <input id="post_title" name="post[title]" type="text" />
Notice that the input ID is generated by model_column. This gives you a unique ID to use for javascript and css.

Common customizations are to set the size of the field and css class:

  <%= f.label :title %><br>
  <%= f.text_field :title, size: 80, class: 'input' %>
### Generated output ###
  <label for="post_title">Title</label><br>
  <input type="text" size="80" name="post[title]" id="post_title" class="input"> 

Text Area

Next up is text area which matches up to text fields in your model.

  <%= f.label :body %><br>
  <%= f.text_area :body %>
### Generated output ###
  <label for="post_body">Body</label><br>
  <textarea name="post[body]" id="post_body"></textarea>

Common customizations are to set the columns and rows of the area and css class:

  <%= f.label :body %><br>
  <%= f.text_area :body, cols: 80, rows: 5, class: 'input' %>
### Generated output ###
  <label for="post_body">Body</label><br>
  <textarea rows="5" name="post[body]" id="post_body" cols="80" class="input"></textarea> 

Select List

When you have a relationship with another model, it is easiest to present this as a select list. The Rails scaffold will select a number_field, though you can easily replace that with a select field.

  <%= f.label :category_id %><br>
  <%= f.select :category_id, 
    options_from_collection_for_select(Category.all, :id, :name, @post.category_id) %>
### Generated output ###
  <label for="post_category_id">Category</label><br>
  <select id="post_category_id" name="post[category_id]">
    <option value="1">Technology</option>
    <option value="2">Lifestyle</option>
    <option value="3">Business</option>
    <option value="4">Entertainment</option>
  </select> 

All the heavy lifting is done by the options_from_collection_for_select helper.
It takes 4 arguments:

  • collection - this is the collection of data that you want the user to choose from. Generally best to generate it in the controller and reference it here.
  • value_method - the method to use on a member of the collection to get the value for the select list. Generally :id
  • text_method - the method to use on a member of the collection to get the display text for the select list.
  • selected = nil - this will set a value to 'checked', needed for when the form is being edited or re displayed on error so that it selects the already selected category, needs to match the value. In our case it is @post.category_id

Rails does all the heavy lifting with just a single line. Make sure you have the 4th value or you won't get the correct item selected when displaying the form. It defaults to nil if empty and will cause your users issues.

Radio Buttons

Radio buttons are done similarly in that a helper collection_radio_buttons will do all the heavy lifting.

  <%= f.label :status_id %><br>
  <%= collection_radio_buttons(:post, :status_id, Status.all, :id, :name) %>
### Generated output ###
  <label for="post_status_id">Status</label><br>
  <input id="post_status_id_1" name="post[status_id]" value="1" type="radio">
  <label for="post_status_id_1">just created</label>
  <input id="post_status_id_2" name="post[status_id]" value="2" type="radio">
  <label for="post_status_id_2">review</label>
  <input id="post_status_id_3" name="post[status_id]" value="3" type="radio">
  <label for="post_status_id_3">published</label>

It takes 5 arguments with optional options and html_options

  • object - this is the object that we are referencing, in our case @post.
  • method - the method to use on the object to get the current selection, needed when doing an edit or re displaying the form.
  • collection - this is the collection of data that you want the user to choose from. Generally best to generate it in the controller and reference it here.
  • value_method - the method to use on a member of the collection to get the value for the select list. Generally :id
  • text_method - the method to use on a member of the collection to get the display text for the select list.

Once again Rails does it all with a single line.

Check Boxes

For our example we need to manually add the tags to the form as it is a many to many relationship. Once again a Rails helper will do the heavy lifting for us.
This time though, we need to modify the strong paramters in the controller to allow the tags.

    <%= f.label :tags %><br>
    <%= collection_check_boxes(:post, :tag_ids, Tag.all, :id, :name) %>
### Generated output ###
  <label for="post_tags">Tags</label><br>
  <input id="post_tag_ids_1" name="post[tag_ids][]" value="1" type="checkbox">
  <label for="post_tag_ids_1">Fun</label>
  <input id="post_tag_ids_2" name="post[tag_ids][]" value="2" type="checkbox">
  <label for="post_tag_ids_2">Scary</label>
  <input id="post_tag_ids_3" name="post[tag_ids][]" value="3" type="checkbox">
  <label for="post_tag_ids_3">Sad</label>
  <input id="post_tag_ids_4" name="post[tag_ids][]" value="4" type="checkbox">
  <label for="post_tag_ids_4">Excited</label>
  <input name="post[tag_ids][]" value="" type="hidden">

The collection_check_boxes helper works exactly the same as the previous collection_radio_buttons helper. This time though, the helper is a plural due to the many to many relationship.

Now the controllers strong parameters need to be modified to allow the tags through as they weren't put there by the Rails scaffold generator.

Before:

def post_params
  params.require(:post).permit(:title, :body, :category_id, :status_id)
end

After:

def post_params
  params.require(:post).permit(:title, :body, :category_id, :status_id, { :tag_ids => [] })
end

The addition of { :tag_ids => [] } tells the controller to permit an array of tag_ids through. Rails will then do the rest.

Now you can do fancy forms

Now you know how to do all the common types of forms that your users will require to input their data.

My rule of thumb: Try not to ask your users to fill out complicated forms, keep it as simple as possible and only collect the minimum that you need. Use good defaults if at all possible.

Resources

RailsGuides for Form Helpers - Covers using forms with and without a model in Rails.

Form Helpers API Doc - More details and examples on form helpers.

Form Options Helpers API Doc

Rails Automated Testing Setup for Beginners - My article on how to setup MiniTest for easy automated testing.


Active Record Associations for Beginners

Relationships between the data in your models are defined using associations in Rails. Here is a brief overview of the associations I use the most and how to implement them in your Rails applications.

The Setup

All the examples in the article will be built and tested using the following versions:
  • Rails: 4.1.5
  • Ruby 2.1.2
  • MiniTest 5.4.1

To help with the testing side of things, you can read my article on Rails Automated Testing Setup for Beginners to help you get setup with automated testing if you haven't already.

Basic Principles of associations

When specifying an association between 2 models it is best to put the association on both sides of the Model. This allows your models to become self documenting so that you know what type of association your model has with other models.

Rails associations refer to the plural or singular of a model, depending on the type of association. It is important to note what you should be using and where you should be using it, as associations and queries will fail if the wrong one is used.

Typically only one Model/Table has a field that references the other model in the association. This model is considered the child model with the other model the parent.

One to Many

This is probably the most common association you will see when starting out. And Rails makes it easy with just 2 lines of code.

A simple example is a blog post that has comments. The Post model is the parent and the Comment model is the child.

So your Post model would have many comments, and your comments would belong to the post. You set this up in Rails as follows:

class Post < ActiveRecord::Base
  # no foreign key - no special column needed
  has_many :comments
end

class Comment < ActiveRecord::Base
  # foreign key - requires a database column: post_id of type: integer
  belongs_to :post     
end

The comments table would have a post_id field to reference the Post model that it belongs to. The foreign key field in the child table is the singular of the Parent model followed by _id.

How to query your One to Many records

With a One to Many setup, typically you may want to show all the children belonging to a parent in the same page. To reduce multiple database query's it is recommended to include the child table in the query so Rails does a database join query and only makes a single query to the database to get all the records (parent and children). You don't need to know how a join query works right now, Rails will take care of that for you.

Here is the simple (wrong) way to query a Post model and then show its comments.

post = Post.find(params[:id])
post.comments.each do |comment|
  # each comment will generate a database query
end
Not the ideal way as lots of comments will generate a lot of database queries.

By using the includes method, Rails looks at your associations and uses a single database query to get all the data. Post and comments are now together.

# notice how the includes use the plural of the child.
post = Post.includes(:comments).find(params[:id])

post.comments.each do |comment|
  # each comment will NOT generate a database query
  # this will make things faster
end
When generating your Parent object, include uses the plural of the child model.

One Model, Multiple Associations

Now say our Post model from above belonged to a category. To reduce duplication of category names, you would create a Category model and add associations to your Post models.

Here Category is the parent and Post is the child.

class Category < ActiveRecord::Base
  # no foreign key - no special column needed
  # uses plural of child model
  has_many :posts
end

class Post < ActiveRecord::Base
  # foreign key - requires a database column: category_id of type: integer
  belongs_to :category

  # comment association remains unchanged
  has_many :comments
end

# Comment model is unchanged.
class Comment < ActiveRecord::Base
  # foreign key - requires a database column: post_id of type: integer
  belongs_to :post     
end

This association plays out exactly the same as before with the same rules regarding database queries.

A category page that lists all the posts in that category:

category = Category.includes(:posts).find(params[:id])

category.posts.each do |post|
  # each post will NOT generate a database query
  # puts post.title
end

What the above shows is that your models can have multiple associations without any issues in Rails.

One to One

So what about models with a one to one relationship? Simple, just use has_one instead of has_many on the Parent model

class Customer < ActiveRecord::Base
  # no foreign key - no special column needed
  # uses singular of child model
  has_one :account
end

class Account < ActiveRecord::Base
  # foreign key - requires a database column: customer_id of type: integer
  belongs_to :customer
end

It works just the same, just make sure to use the singular of the child as their is only one.

customer = Customer.includes(:account).find(params[:id])

customer.account

Many to many

Continuing with our blog post theme, what about posts that have tags? In this case it would be a many to many relationship as there are many posts that have many tags. You might also want to find the posts that have a certain tag.

To use a many to many association you will use the has many through association which requires a third model/table to track the association between the two models. This table only needs 2 columns that are foreign keys for each model. For our example lets call this through model Tagging

Here is how we generate the tagging and tag models:

rails generate model Tagging post_id:integer tag_id:integer
rails generate model Tag name:string

Notice how only the through model needs foreign keys.

# the through model
class Tagging < ActiveRecord::Base
  belongs_to :post  # foreign key - programmer_id
  belongs_to :tag   # foreign key - project_id
end

class Tag < ActiveRecord::Base
  has_many :taggings
  has_many :posts, through: :taggings # notice the use of the plural model name
end

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, through: :taggings # notice the use of the plural model name
end

Access the tags

Now we can access the tags the same way we did with a many to one.

Show a post with all its tags

post = Post.includes(:tags).find(params[:id)
post.tags.each do |tag|
  # puts tag.name
end

Show a tag with all associated posts

tag = Tag.includes(:posts).find(params[:id])
tag.posts.each do |post|
  # puts post.title
end

Tree Relationship

Last we have a tree relationship. A good example of his is the classic folder hierarchy. Folders can have many sub folders and one parent folder. In this case the folder model needs to have associations with itself.

Here is the model being generated with the parent_folder_id column

rails generate model Folder name:string parent_folder_id:integer

Here are the associations

class Folder < ActiveRecord::Base
  belongs_to :parent_folder, class_name: Folder
  has_many :child_folders, class_name: Folder, foreign_key: "parent_folder_id"
end

The belongs_to and has_many work exactly the same as before, though this time with the class_name field and foreign_key field, you are building self joins within the same model so you can have a tree relationship.

The query works the same as before:

parent_folder = Folder.includes(:child_folders).first

parent_folder.child_folders.each do |child_folder|
  # puts child_folder
end

What next?

As you have seen, Rails makes it very easy to create associations between your models.
We have just scratched the surface of what you can do with associations, be sure to check the RailsGuides for Active Record Associations as it dives deeper into associations in Rails.

In the future when creating your Rails applications, start thinking about the relationships/associations between your data models before you create them to make sure that you are leveraging Rails associations to help make your job easier.

Resources

RailsGuides for Active Record Associations - A great resource that goes in depth on Active Record Callbacks.

Active Record Associations API Doc - More details and examples on callbacks.

Parameterize - API reference for the parameterize method in rails.

Try Method - API doc for the Try method

Rails Automated Testing Setup for Beginners - My article on how to setup MiniTest for easy automated testing.


Rails Validations for Beginners

Validations in Rails allow you to make sure that your users are entering data in a format that the applications expects and can use. Here is a pretty simple beginners guide on how to get started using validations in your application.
Using validations allows you to keep a watchful eye on data that is input into your application and to make sure that it is useable by the application.

Doing the data validations within Rails allows you to customize how errors are presented to the user. As the validations occur before a database INSERT or UPDATE command, this can protect your user from potentially bad mistakes.

Doing your validations at the model level ensures that no matter how the data is entered, the model will perform the final validations. Doing controller or view validations should not be relied upon as multiple controllers can access a single model.

The Setup

All the examples in the article will be built and tested using the following versions:
  • Rails: 4.1.5
  • Ruby 2.1.2
  • MiniTest 5.4.1

To help with the testing side of things, you can read my article on Rails Automated Testing Setup for Beginners to help you get setup with automated testing if you haven't already.

Standard Validations

Rails offers a lot of ways to validate your models, though here I will cover what I use on a regular basis and think of as 'Standard'.

Presence

My go to validation and probably the most basic validation, is to check that a field is actually there. Rails does this with the presence validation.

# single column check
class Post < ActiveRecord::Base
  validates :title, presence: true
end
# multiple column check
class Post < ActiveRecord::Base
  validates_presence_of :title, :body, :description
end
As you can see, a single line is all you need to make sure a field has some data in it.

Is it unique?

The next most common validation you are likely to use is the unique validation. This will check to make sure data is unique to that Model and you can even specify criteria by which to compare.

class Post < ActiveRecord::Base
  # regular uniqueness check
  validates :title, uniqueness: true
  # do a check that ignores case
  validates :title, uniqueness: { case_sensitive: false }
  # will only compare against other posts with the same post_type_id
  validates :title, uniqueness: { scope: :post_type_id }
end

Is it a Number?

You can check that a field is in a number format with the numericality validation.

The check by itself will validate the numbers 12 and 12.5 so to force it to check for integer only numbers you replace true with the hash { only_integer: true }

Finally, by doing this check it is also doing a presence check, so to ONLY check that the field is a number when there is data in it, add allow_blank: true to the end. This will only check the validation if the field is not blank and can be used on almost all validations.

class Post < ActiveRecord::Base
  # will validate both 12 and 12.5
  validates :review_score, numericality: true
  # will only validate 12
  validates :review_score, numericality: { only_integer: true }
  # will only validate 12 and only if the field is NOT blank. 
  validates :review_score, numericality: { only_integer: true }, allow_blank: true
end
The last line is the one I typically use most.

Displaying Error Messages

There are a lot of ways to build forms in Rails, though for our example, the standard Rails scaffold way of displaying error messages is as follows:

  # assuming @post is the form variable 
  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> 
        prohibited this post from being saved:</h2>

      <ul>
      <% @post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

What this does is to go through your @post object and check for errors and then display any error messages.

Rails has a default error message for each type of validation and these can be customized (see below).

Here are the standard error messages, the error is displayed as 'column_name' 'message'

  • presence - "can't be blank"
  • uniqueness - “has already been taken”
  • numericality - "is not a number"

Some examples:

validates :title, presence: true
# error message: Title can't be blank
validates :title, uniqueness: true
# error message: Title has already been taken
validates :review_score, numericality: true
# error message: Review score is not a number

Custom Error Messages

Built in errors not working for you? Easy, just add a custom message to your validations.

validates :title, presence: { message: 'should have some words in it' }
# error message: Title should have some words in it
validates :title, uniqueness: { message: 'was already used. Try something else' }
# error message: Title was already used. Try something else
validates :review_score, numericality: { message: 'should be a number' }
# error message: Review score should be a number

Testing your Validations

Rails provides a simple way to test your validations with the .valid? method.

By creating the object with or without the correct parameters you can easily check it is passing all validations with the .valid? check.

This .valid? works for all types of validations and is an easy way to test that your validations are working as designed.

Based on model

 validates_presence_of :title, :body

require 'test_helper'

class PostTest < ActiveSupport::TestCase
  test "validations are correct" do
    post = Post.new
    # post should not be valid as it is missing the title and body
    assert_not post.valid?
    post = Post.new(title: 'Post title')
    # post should not be valid as it is missing the body
    assert_not post.valid?
    post = Post.new(title: 'Post title', body: 'Post Body')
    assert post.valid?
  end
end

As you can see, its just a matter of providing missing, bad or good data to a model and then testing if it is valid. It is a good idea to make sure you are testing your validations with at least this basic test to make sure that they are working.

What next

We have only just scratched the surface of what you can do with validations. There is a lot you can do to help your users enter in valid data beyond what we have covered.

Read the Rails Guide for Active Record Validations to start diving deeper into validations.

Resources

RailsGuides for Validations - The Rails Guides page on Active Record Validations.

ActiveRecord Validations ClassMethods API Doc

Rails Automated Testing Setup for Beginners - My article on how to setup MiniTest for easy automated testing.


Rails Functional Testing Controllers for Beginners - Part 2

This article is continuing on from Part 1 and dives further into writing functional tests for your controllers.

Here are the common tests that I use when writing functional tests for my Rails controllers.

First make sure you have some form of automated testing setup as per my previous article. This will allow your tests to automatically run whenever you modify your test or model file. This will save a lot of time. If you have notifications setup, even better, you can just wait for the alert to pop up.

NOTE: The following versions were used for this setup:

  • Rails: 4.1.5
  • Ruby 2.1.2
  • MiniTest 5.4.0

Testing for Divs in Views

Generally you will probably have views that will be showing different content on the same page depending on what actions are taken. In this case its great to be able to check for the presence of unique ID's that should or should NOT be there.

The trick is to use unique ID tags for your divs in your views so you can easily check for them in your tests. Here is an example of the ID tag.

<div id="product_admin">
Admin Content goes here.
</div>
<div id="product">
Regular Content goes here.
</div>

This is also great for testing to make sure that admin content only appears to an admin and not a regular user.

Here is how to do the functional test:

test "should get show as regular user" do
  # this is based off devise, this is a regular user
  sign_in users(:regular) 
  get :index, product_id: 1
  assert_response :success
  # regular users can't see the admin panel
  assert_select "#product_admin", count: 0
  # regular users can see the admin panel
  assert_select "#product", count: 1
end
test "should get show as admin user" do
  # this is based off devise, this is an admin user.
  sign_in users(:admin) 
  get :index, company_id: 1
  assert_response :success
  # admin users can see both panels
  assert_select "#product_admin", count: 1
  assert_select "#product", count: 1
end
As you can see, this is a simple little test that can be very handy to verify the presence of certain elements on your page.

Simple AJAX Javascript testing

While javascript testing is a whole chapter by itself, there is a basic test that you can do to ensure that your javascript views are at least being sent to the browser without any errors.

test "update product via javascript" do
  # use xhr to simulate a ajax call
  xhr :get, :product_update, format: :js, product_id: '77'
  # if the controller responds with a javascript file the response will be a success
  assert_response :success
end
As you can see, an effective way to test that your .js views are responding correctly.

Testing the actual content of views

If you are going to do a refactor of the logic in your application views, then you can check the actual rendered content of views is still correct using assert_select.

Here is the view:

# app/views/products/show.html.erb
<div id="post_content">
  <p>
    <strong>Name:</strong>
    <%= @product.title %>
  </p>

  <p>
    <strong>Description:</strong>
    <%= @product.description %>
  </p>
</div>

Here is the test of the view contents:

test "should show content of product page correctly" do
  # do a standard get of the product
  get :show, id: products(:one)
  assert_response :success
  #check that the content matches
  assert_select '#product_content', html: '<p>
    <strong>Name:</strong>
    MyString
  </p>

  <p>
    <strong>Description:</strong>
    MyText
  </p>'
Now you can refactor your view/controller and be assured that the final output is unmodified from what it was originally.

Where to start

If you already have a Rails application working and don't have any tests, its easy to get started. The basic scaffold tests for your controllers are a great way to get started and you can slowly build up from there.

Your own tests

As you can see, it is easy to get started writing tests that you can rely on to ensure your application is working. You can slowly build up your test suite over time.

Hopefully this has helped you to get more confident with writing tests that you can rely on.

Resources

RailsGuides for Testing - The Rails Guides guide to testing applications.

Assert Select Documentation - For a deeper dive into assert select and what it can do.

Rails Automated Testing Setup for Beginners - The first article on how to setup automated testing in Rails.

Rails Unit Testing with MiniTest for Beginners - Getting started testing your Rails application with Unit testing using MiniTest. Also includes fixtures.

Rails Functional Testing Controllers for Beginners - Part 1 - The previous article on how to setup functional controller tests in Rails.