Rails Plugins — Making Website Development Easy!

1
5201
Website development is easy using Rails plugins

Website development is easy using Rails plugins

This article is a tutorial on how to use Rails plugins for pagination, authentication and file attachments. It is aimed at readers with a basic knowledge of Ruby on Rails programming.

A plugin is a wonderful way to package a frequently implemented solution to a common problem and reuse the code. In case you had to build a blogging application, you could use a blog plugin, an authentication plugin, an image-upload plugin, a comments plugin and a ratings plugin. Plugins make it super simple to create an application, and save a lot of effort, which in turn, allows you to focus on the user interaction, security and testing.

In this article, we will discuss some of the plugins that made our life easy while building Foodlets.in, a visual guide for restaurant food.

For the purpose of this article, let’s build a basic events-management application on Rails version 2.3.8. This app would have the Event and the User models. We shall add in features like user authentication, Facebook Connect, pagination for listing events, image attachment and the ability to use jQuery in place of the default libraries. For your reference, we have made the source code of this app available on GitHub.

To create a new Rails app, issue the following command:

rails events
cd events

Let’s use a scaffold to get started:

script/generate scaffold Event title:string description:string start_date:date end_date:date user_id:integer

And here’s another scaffold for the User model:

script/generate scaffold user login:string email:string persistence_token:string crypted_password:string password_salt:string

Paperclip plugin

This plugin adds an image to the event, and shows a thumbnail. It helps in handling file attachments and their storage on the server’s filesystem. It allows uploaded files to be handled like regular model fields/properties. It also includes methods to validate file size, file type, etc. Paperclip proves to be excellent while handling image attachments. It uses ImageMagick to resize images and generate thumbnails.

Installation

Install the plugin from the online git repository:

script/plugin install https://github.com/thoughtbot/paperclip.git

This will install the paperclip plugin under events/vendor/plugins.

Usage

Let us add an image attachment to our Event model:

script/generate paperclip event image

Paperclip comes with a generator, and this command will create a migration named TIMESTAMP_add_attachments_image_to_event.rb in the events/db/migrate/ directory. This migration will add the following fields to the Event model:

  • image_file_name
  • image_content_type
  • image_file_size
  • image_updated_at

Run the migration, as follows:

rake db:migrate

We need to specify in the Event model that it uses an image attachment. The code to be added to the model is:

# events/app/models/event.rb
has_attached_file :image,
:styles => {:thumbnail => "100x100#", :large => "500x500#"}

The styles parameter mentions that we need a thumbnail of size 100×100 pixels, and a large image of size 500×500 pixels. thumbnail and large are labels, and could be words of the user’s choice. Let’s add in a few validations:

# events/app/models/event.rb
validates_attachment_size :image, :less_than => 5.megabytes,
             :message => "Image should be present and less than 5 MB in size"
validates_attachment_content_type :image,
            :content_type => ['image/jpeg','image/png','image/jpg'],
            :message => "Attachment should be a jpg, jpeg or a png"

The first validation will verify that the image size is less than 5 MB, and the second will verify that only JPEG and PNG images are stored.

Changes to views

To facilitate the upload of the image, we need to make the “new event” and “edit event” forms multi-part. Replace the following code…:

# events/app/views/events/new.html.erb and events/app/views/events/edit.html.erb
<% form_for(@event) do |f| %>

……with:

# events/app/views/events/new.html.erb and events/app/views/events/edit.html.erb
<% form_for(@event, :html => { :multipart => true } ) do |f| %>

Then add the file field given below:

# events/app/views/events/new.html.erb and events/app/views/events/edit.html.erb
<%= f.file_field :image %>

To display the image thumbnail in the show and index views, you should add the following helper:

# events/app/views/events/show.html.erb and events/app/views/events/index.html.erb
<%= image_tag @event.image.url(:thumbnail) %>

To learn more, check the Paperclip wiki.

Will_Paginate plugin

This plugin will allow us to implement pagination, i.e., to break a long list of data items into numbered pages. We shall restrict listings to five events per page in the Event index view.

Installation

Install will_paginate from the online git repository:

script/plugin install git://github.com/mislav/will_paginate.git

This will install the will_paginate plugin under events/vendor/plugins.

Usage

In the index method of EventsController (events/app/controllers/events_controller.rb), replace the following code…:

 @events = Event.all

…with:

@events = Event.paginate(:per_page => 5, :page => params[:page],
:order => 'updated_at DESC')

Here, the parameters are as follows:

:per_page: number of records on a page.
:page: the number of the page to be fetched; the default is 1.
:order: to decide on the ascending or descending order on a field.

Other parameters like :conditions, :include, etc, can also be used. For example, to list all events starting today, or in the future, you could add in a condition:

@events = Event.paginate(:per_page => 5, :page => params[:page],
 :order => 'DATE(start_date) asc', :conditions => ['start_date >= ?', Date.today])

In the index view, add the following code wherever you want the pagination links to appear:

# events/app/views/events/index.html.erb
<%= will_paginate %>

For further reading, check will_paginate documentation.

Authlogic plugin

Authlogic is an easy-to-use but powerful authentication plugin for Rails. In laymen terms, it gives you user registration, sign-in (with a “Remember me” option), sign-out and user account activation. Our rule is that only authenticated users can create an event.

Installation

Run the command to install the plugin:

script/plugin install git://github.com/binarylogic/authlogic.git

Adding user authentication to the application

The first thing that you need to do is associate the authentication functionality with the User model:

#user.rb
acts_as_authentic do |c|
 c.require_password_confirmation = false
end

In this tutorial, we are going to focus on user authentication only, and will create a new user using the create operation provided by the scaffold. We need to make one change to the view for new user — the form should capture only email, login and password fields. Now, you must have noticed that our User model doesn’t have a password field, so how can we use it in the new form? That’s because the password is encrypted before it is stored — Authlogic takes the value of your password, encrypts it and stores it in the crypted_password field.

Once you associate a model with Authlogic, by default it adds a set of validations for the fields. Most of the fields are optional, but password confirmation is required by default; if you don’t need it, then set it to false. You can read more about the other fields at the Authlogic ActsAsAuthentic documentation.

Create the UserSession model:

script/generate session user_session

Now create the UserSessionsController. Remember that the sign-in will not require the user to be signed in, while sign-out can happen only if the user is already signed in.

# events/app/controllers/user_sessions_controller.rb
before_filter :require_no_user, :only => [:new, :create]
before_filter :require_user, :only => :destroy

Add the following code to the new and create methods for the sign-in:

# events/app/controllers/user_sessions_controller.rb
 # new
  @user_session = UserSession.new

 # create
  @user_session = UserSession.new(params[:user_session])
   if @user_session.save
     flash[:notice] = "You have signed in successfully!"
     redirect_to root_url
   else
     render :action => :new
   end

For the sign-out, simply destroy the user session:

# app/controllers/user_sessions_controller.rb
 # destroy
   current_user_session.destroy
   flash[:notice] = "You have signed out successfully!"
   redirect_to root_url

In the view for the sign-in, the form should look like what follows:

# app/views/user_sessions/new.html.erb
<% form_for @user_session, :url => user_session_path do |f| %>
     # fields for username, password, remember_me
<% end %>

Next, wire up the routes:

# config/routes.rb
map.resource :user_session
map.root :controller => "events_controller", :action => "index"

You then need to add some code to the Application Controller for persisting the session.

Declare the helper methods current_user_session and current_user, which can be used in other controllers where the session object or the current user object is required:

# app/controllers/application.rb
helper_method :current_user_session, :current_user

We don’t want the password to be written to the log files!

# app/controllers/application.rb
filter_parameter_logging :password

Besides the helper methods, you need to add two authentication methods to the Application Controller:

require_user:  to be used in controller actions that require signed-in users
require_no_user: to be used in controller actions that require non-signed-in users

# app/controllers/application.rb
# current_user_session (for retrieving the current session)
     return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find

#  current_user (for retrieving the current user)
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user

#  require_user
     unless current_user
       flash[:notice] = "Please sign in to access this page"
       redirect_to root_url
       return false
end

#  require_no_user
     # check if it is the current_user

That’s it! You are ready to use authentication in the application now! Next, ensure that only signed-in users can create new events. Add the following code to the start of the new method in the Events Controller:

# events/app/controllers/events_controller.rb
# new
      if current_user.nil?
       flash[:notice] = "You must be logged in to access this page"
       redirect_to (new_user_session_url)
       return
     end

Try to create a new event without signing in — it should take you to the sign-in page, /user_sessions/new. For sign-out, use: /user_sessions/destroy.

Read more about Authlogic here.

Omniauth plugin

Omniauth is a Rails plugin that enables external authentication for your application. It allows people to use Facebook Connect. Some other supported external providers are Twitter and Foursquare via OAuth, Google Apps via OpenID and Flickr.

Installation

Install the plugin from the repository at github:

script/plugin install git://github.com/intridea/omniauth.git

Adding Facebook authentication to the Events application

For Rails v2.3.8, you need to create an initialiser for Omniauth, and specify a strategy for each external provider:

# events/config/initializers/omniauth.rb
#Facebook
ActionController::Dispatcher.middleware.use OmniAuth::Strategies::Facebook, '<APP_ID>', '<APP_SECRET>'

Please note that you have to register your app with Facebook in order to get the API key. Also, you need to ensure that you provided the correct settings for your Facebook App:

Site URL = http://yoursitename.com
Site Domain = yoursitename.com

Next, add a “Connect with Facebook” button to the home page (you can get the Facebook button image from the Facebook Developers website):

# app/views/events/index.html.rb
<a href="/auth/facebook"><img src="/images/fb-small-button.png" alt="Connect  with Facebook"/></a>

This link takes you to /auth/facebook as required by Omniauth, after which Omniauth redirects you to Facebook, which then takes care of authenticating you, and returns a response message on the callback URL /auth/facebook/callback. This callback URL needs to be configured in routes for all providers:

# events/config/routes.rb
map.connect '/auth/:provider/callback', :controller => 'user_sessions', :action => 'social_session_create'

The response that you get from Facebook is an OAuth response, and will contain a UID field which denotes the user’s unique OAuth ID at Facebook and a provider field (set to facebook), besides other fields like name, email, etc. Let’s create a new model to store the key fields, along with an additional user_id field to map it to your User model:

script/generate model authorization provider:string uid:string user_id:integer

This will generate authorization.rb, and you need to associate it with your User model by adding a has_one association as follows:

# events/app/models/authorization.rb
has_one :user, :primary_key => "user_id", :foreign_key => "id"

For external authentication, handle the session creation differently in the UserSessionsController. Use a new method, create_social_session:

# events/app/controllers/user_sessions_controller.rb
# create_social_session
   # get the auth response from the request
      @auth_response = request.env['rack.auth']
   # Check if the user has an existing social profile
   # (in the Authorization model - match on uid,provider)
      @auth = Authorization.find_from_hash(@auth_response)
   # if the user doesn't have a social profile then nil is returned
   if @auth.nil?
     # Create a new user or add an authorization to an existing user
     @auth = Authorization.create_from_hash(@auth_response)
   end
   # Log-in the authorizing user with remember me as false
   @user_session = UserSession.create(@auth.user, false)
   if @user_session.save
     flash[:notice] = "Successfully logged in."
     redirect_to root_url
   else
     flash[:notice] = "Login failed!"
     render :action => 'new'
   end

We will not discuss authorisation methods, as they are simple CRUD methods on either the Authorization model or the User model. The gist of this code is that you need to extract and process the response from Facebook.

That’s it! You now have Facebook Connect for your application. Who would have thought that it could be so simple? Thanks to the people at Intridea who came up with this wonderful plugin! Read more about Omniauth here.

JRails plugin

Rails, by default, uses the Prototype JavaScript library. When a new Rails project is created, the following files will be present in the events/public/javascripts/ directory:

  • prototype.js
  • effects.js
  • dragdrop.js
  • controls.js

The last three are script.aculo.us libraries. If you wish to use jQuery instead, don’t worry, jRails comes to the rescue. This plugin lets you use your favourite JS library, jQuery, instead of Prototype. It includes the jQuery equivalent (jquery.js and jquery-ui.js) of the above four files in your app. jRails will allow your app to use the Rails JavaScript helpers by callingjQuery functions.

Installation

Install the plugin from github:

script/plugin install https://github.com/aaronchi/jrails.git

Usage

Adding the following helper to the view will include jquery.js and jquery-ui.js in the HTML:

# e.g. events/app/views/events/new.html.erb
<%= javascript_include_tag :defaults %>

Now you can include any jQuery library and call the functions through the available Rails helpers, or pure JavaScript code. To know more about jRails check out its wiki.

Well, the next time you think of a feature for your Rails app, just Google around, there could be an existing plugin for it! It’ll save you a lot of time…

1 COMMENT

LEAVE A REPLY

Please enter your comment!
Please enter your name here