How to Build an Unobtrusive Login System in Rails

How to Build an Unobtrusive Login System in Rails

An unobtrusive login system is one that gets out of the user’s way. It will make your application nicer and more polished. This article will guide you through the process of setting up user logins, then ajaxifying the process by moving the form into a modal box that communicates with the server. Additionally, this article will show you how to create the setup using jQuery and Prototype so you can choose your favorite library. This article assumes that you have experience with Rails and jQuery or Prototype.


You can use Adman65/nettuts for a successful login. Be sure to use bad credentials so you can see how everything works.

What We’re Making

Getting Started

We’re going to start by creating a dummy application that has a public and private page. The root url is the public page. There’s a login link on the public page. If the user logs in successfully, they’re redirected to the private page. If not, they’re redirected back to the login form. The private page shows the user name. We’ll use this as the starting point for ajaxifying the site.

The first step is using the rails command to generate a new application, then install and setup up authlogic.

$ cd into-a-directory
$ rails unobtrusive-login

Add authlogic.

# /config/environment.rb
config.gem 'authlogic'

Now install the gems.

$ sudo gem install gemcutter
$ sudo gem tumble
$ sudo rake gems:install

Next create a user model and add the required authlogic columns to the migration.

$ ./script/generate model User
exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/user.rb
create  test/unit/user_test.rb
create  test/fixtures/users.yml
create  db/migrate
create  db/migrate/20100102082657_create_users.rb

Now, add the columns to the new migration.

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string    :login,               :null => false
      t.string    :crypted_password,    :null => false
      t.string    :password_salt,       :null => false
      t.string    :persistence_token,   :null => false
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

Migrate the database.

$ rake db:migrate

Include authlogic in the user model.

# /app/models/user.rb
class User < ActiveRecord::Base
  acts_as_authentic
end

Now we can create a user. Since this is a demo app, web based functionality for signing up isn’t required. So open up the console and create a user:

$ ./script/console
>> me = User.create(:login => 'Adman65', :password => 'nettuts', :password_confirmation => 'nettuts')

Now we have a user in the system, but we have no way to login or logout. We need to create the models, controllers, and views for this. Authlogic has its own class for tracking logins. We can use the generator for that:

# create the user session
$ ./script/generate UserSession

Next we need to generate the controller that will login/logout users. You can create sessions just like any other resource in Rails.

# create the session controller
$ ./script/generate controller UserSessions

Now set its contents to:

# /app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController  
  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_back_or_default user_path
    else
      render :action => :new
    end
  end
end

It looks exactly the same as a controller that was generated via scaffolding. Now create the users controller which has public and private content. Generate a users controller. Inside the controller we’ll use a before filter to limit access to the private areas. The index action is public and show is private.

# create the users controller
$ ./script/generate controller users

Update its contents:

# /app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_filter :login_required, :only => :show

  def index
  end

  def show
    @user = current_user
  end

  private
  def login_required
    unless current_user
      flash[:error] = 'You must be logged in to view this page.'
      redirect_to new_user_session_path
    end
  end
end

You should notice that current_user is an undefined method at this point. Define these methods in ApplicationController. Open up application_controller.rb and update its contents:

# application controller
class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details

  # From authlogic
  filter_parameter_logging :password, :password_confirmation
  helper_method :current_user_session, :current_user

  private
  def current_user_session
    @current_user_session ||= UserSession.find
  end

  def current_user        
    @current_user ||= current_user_session && current_user_session.user
  end
end

At this point the models and controllers are complete, but views aren’t. We need to create views for a login form and the public and private content. We’ll use the nifty-generators gem to create a basic layout.

$ sudo gem install nifty-generators
$ ./script/generate nifty_layout

Time to create the login form. We’re going to use a partial for this because in the future we’ll use the partial to render just the login form in the modal box. Here’s the code to create the login form. It’s exactly the same as if you were creating a blog post or any other model.

# create the login views
# /app/views/user_sessions/_form.html.erb
<% form_for(@user_session, :url => user_session_path) do |form| %>
  <%= form.error_messages %>

  <p>
    <%= form.label :login %>
    <%= form.text_field :login %>
  </p>

  <p>
    <%= form.label :password %>
    <%= form.password_field :password %>
  </p>

  <%= form.submit 'Login' %>
<% end %>

Render the partial in the new view:

# /app/views/user_sessions/new.html.erb
<% title 'Login Please' %>
<%= render :partial => 'form' %>

Create some basic pages for the public and private content. The index action shows public content and show displays private content.

# create the dummy public page
# /app/views/users/index.html.erb
<% title 'Unobtrusive Login' %>

<p>Public Facing Content</p>

<%= link_to 'Login', new_user_session_path %>

And for the private page:

# create the dummy private page
# /app/views/users/show.html.erb
<% title 'Welcome' %>
<h2>Hello <%=h @user.login %></h2>

<%= link_to 'Logout', user_session_path, :method => :delete %>

Delete the file /public/index.html and start the server. You can now log in and logout of the application.

$ ./script/server

Here are some screenshots of the demo application. The first one is the public page.

Public Page

Now the login form

Login Page

And the private page

Private Page

And finally, access denied when you try to visit http://localhost:3000/user

Access Denied

The AJAX Login Process

Before continuing, we need to understand how the server and browser are going to work together to complete this process. We know that we’ll need to use some JavaScript for the modal box and the server to validate logins. Let’s be clear on how this is going to work. The user clicks the login link, then a modal box appears with the login form. The user fills in the form and is either redirected to the private page, or the modal box is refreshed with a new login form. The next question is how do you refresh the modal box or tell the browser what to do after the user submits the form? Rails has respond_to blocks. With respond_to, you can tell the controller to render different content if the user requested XML, HTML, JavaScript, YAML etc. So when the user submits the form, the server can return some JavaScript to execute in the browser. We’ll use this render a new form or a redirect. Before diving any deeper, let’s go over the process in order.

  1. User goes to the public page
  2. User clicks the login link
  3. Modal box appears
  4. User fills in the form
  5. Form is submitted to the server
  6. Server returns JavaScript for execution
  7. Browser executes the JavaScript which either redirects or updates the modal box.

That’s the high level. Here’s the low level implementation.

  1. User visits the public page
  2. The public page has some JavaScript that runs when the DOM is ready that attaches JavaScript to the login link. That javscript does an XMLHTTPRequest (XHR from now on) to the server for some JavaScript. The JavaScript sets the modal box’s content to the form HTML. The JavaScript also does something very important. It binds the form’s submit action to an XHR with POST data to the form’s action. This allows the user to keep filling the login form in inside the modal box.
  3. Modal box now has the form and required JavaScript
  4. User clicks ‘Login’
  5. The submit() function is called which does a POST XHR to the form’s action with its data.
  6. Server either generates the JavaScript for the form or the redirect
  7. Browser receives the JavaScript and executes it. The browser will either update the modal box, or redirect the user through window.location.

Taking a Peak at the AJAX Ready Controller

Let’s take a look at the new structure for the UserSessions controller.

class UserSessionsController < ApplicationController
  layout :choose_layout

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])

    if @user_session.save
      respond_to do |wants|
        wants.html { redirect_to user_path(@user_session.user) }
        wants.js { render :action => :redirect } # JavaScript to do the redirect
      end
    else
      respond_to do |wants|
        wants.html { render :new }
        wants.js # defaults to create.js.erb
      end
    end
  end

  private
  def choose_layout
    (request.xhr?) ? nil : 'application'
  end
end

As you can see the structure is different. Inside the if save, else conditional, respond_to is used to render the correct content. want.xx where xx is a content type. By default Prototype and jQuery request text/JavaScript. This corresponds to wants.js. We’re about ready to get started on the AJAX part. We won’t use any plugins except ones for modal boxes. We’ll use Facebox for jQuery and ModalBox for Prototype.

Prototype

Rails has built in support for Prototype. The Rail’s JavaScript helpers are Ruby functions that generate JavaScript that use Prototype. This technique is known as RJS (Ruby JavaScript). One example is remote_form_for which works like the standard for_for adds some JS bound to onsubmit that submits to the form to its action using its method with its data. I won’t use RJS in this article since I want to demonstrate vanilla JS. I think by using pure JS and eliminating the JS helpers the article will be more approachable by less experienced developers. That being said, you could easily accomplish these steps using RJS/Prototype helpers if you choose.

Adding Prototype to the application is very easy. When you use the rails command, it creates the Prototype and scriptaculous files in /public/JavaScripts. Including them is easy. Open up /app/views/layouts/application.erb and add this line inside the head tag:

<%= JavaScript_include_tag :defaults %>

JavaScript_include_tag creates script tags for default files in /public/JavaScripts, most importantly prototype.js, effects.js, and application.js. effects.js is scriptaculous. application.js is a file you can use to keep application specific JS. Now we need a modal box plugin. We’re going to use this. Its a very nice modal box plugin inspired by OSX. The source is hosted on GitHub, so you’ll have to clone and move the files in your project directory. For example:

$ cd code
$ git clone git://github.com/okonet/modalbox.git
$ cd modalbox
# move the files in the correct directories.
# move modalbox.css into /public/stylesheets
# move modalbox.js into /public/JavaScripts
# move spinner.gif into /public/images

Now include the stylesheets and JavaScript in your application.

  <%= stylesheet_link_tag ‘application’ %>
  <%= stylesheet_link_tag ‘modalbox’ %> 
  <%= JavaScript_include_tag :defaults %>
  <%= JavaScript_include_tag ‘modalbox’%>

Now let’s get our login link to open a modalbox. In order to do this we need to add some JavaScript that runs when the DOM is ready that attaches the modalbox to our link. When the user clicks the login link, the browser will do a GET to /user_sessions/new which contains the login form. The login link uses the #login-link selector. Update the login link to use the new id in /app/views/users/index.html.erb. Modify the link_to function like this:

<%= link_to 'Login', new_user_session_path, :id => 'login-link' %>

That gives us a#login-link. Now for the JavaScript to attach a modalbox. Add this JS in /public/JavaScripts/application.js

document.observe('dom:loaded', function() {
    $('login-link').observe('click', function(event) {
        event.stop();
        Modalbox.show(this.href,
            {title: 'Login', 
            width: 500}
        );
    });
})

There’s some simple JS for when the user clicks the link a modal box opens up with the link’s href. Refer to the modalbox documentation if you’d like more customization. Here’s a screenshot:

Initial Modalbox

Notice that inside the modal box looks very similar to our standard page. Rails is using our application layout for all HTML responses. Since our XHR’s want HTML fragments, it make sense to render without layouts. Refer back to the example controller. I introduced a method for determining the layout. Add that to UserSessionsController to disable layout for XHR’s.

class UserSessionsController < ApplicationController  
  layout :choose_layout

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_to user_path
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = "Logout successful!"
    redirect_to root_path
  end

  private
  def choose_layout
    (request.xhr?) ? nil : 'application'
  end
end

Refresh the page and click the link you should get something like this:

Without Layout

Fill in the form and see what happens. If you fill in the from with bad info, you’re redirected outside the modal box. If you login correctly you’re redirected normally. According the requirements the user should be able to fill out the form over and over again inside the modal box until they login correctly. How can we accomplish this? As described before we need to use AJAX to submit data to the server, then use JavaScript to update the modal box with the form or do a redirection. We know that the modalbox does a GET for HTML. After displaying the initial modalbox, we need to write JS that makes the form submits itself AJAX style. This allows the form to submit itself inside the modal box. Simply adding this code after the modal box is called won’t work because the XHR might not have finished. We need to use Modalbox’s afterLoad callback. Here’s the new code:

document.observe('dom:loaded', function() {
    $('login-link').observe('click', function(event) {
        event.stop();
        Modalbox.show(this.href,
            {title: 'Login', 
            width: 500,
            afterLoad: function() {
                $('new_user_session').observe('submit', function(event) {
                    event.stop();
                    this.request();
                })
            }}
        );      
    });
})

Form#request is a convenience method for serializing and submitting the form via an Ajax.Request to the URL of the form’s action attribute—which is exactly what we want. Now you can fill in the form inside the modal without it closing. The client side is now complete. What about the server side? The client is submitting a POST wanting JS back. The server needs to decide to either return JavaScript to update the form or render a redirect. In the UserSessionsController we’ll use respond_to to handle the JS request and a conditional to return the correct JS. Let’s begin by handling the failed login case. The server needs to return JS that updates the form, and tells the new form to submit over ajax. We’ll place this template in /app/views/users_sessions/create.js.erb. Here’s the structure for the new create action:

def create
  @user_session = UserSession.new(params[:user_session])
  if @user_session.save
    flash[:notice] = "Login successful!"
    redirect_to user_path
  else
    respond_to do |wants|
      wants.html { render :new }
      wants.js # create.js.erb
    end
  end
end

Now let’s fill in create.js.erb:

$('MB_content').update("<%= escape_JavaScript(render :partial => 'form') %>");
Modalbox.resizeToContent();
$('new_user_session').observe('submit', function(event) {
    event.stop();
    this.request();
});

First we update the content to include the new form. Then we resize the modal box. Next we ajaxify the form just as before. Voilla, you can fill in the form as many times as you want.

Bad Info
Updated Form

Next we need to handle the redirection case. Create a new file in /app/views/users_sessions/redirect.js.erb:

  window.location=”<%= user_path %>”;

Now, update the create action to handle the redirection process:

def create
  @user_session = UserSession.new(params[:user_session])
  if @user_session.save
    respond_to do |wants|
      wants.html do 
        flash[:notice] = "Login successful!"
        redirect_to user_path
      end

      wants.js { render :redirect }
    end
  else
    respond_to do |wants|
      wants.html { render :new }
      wants.js # create.js.erb
    end
  end
end

And that’s it! Now try login with correct credentials and you’re redirected to the private page. For further learning, try to add a spinner and notification telling the user the form is submitting or they’re being redirect. The application still works if the user has JavaScript disabled too.

jQuery

Since I’ve already covered the Prototype process, so I won’t go into the same detail as before. Instead, I will move quickly describing the alternate JavaScript to add to the application. The jQuery vesion will have the exact same structure as the Prototype version. All we need to change is what’s in application.js, create.js.erb, and the JavaScript/css includes.

First thing we need to do is download jQuery and Facebox. Move jQuery into /public/JavaScripts as jquery.js. For facebox move the images into /public/images/, stylesheets into /public/stylesheets, and finally the JS into /public/JavaScripts. Now update /app/views/layouts/application.html.erb to reflect the changes:

<head>
  <title><%= h(yield(:title) || "Untitled") %></title>
  <%= stylesheet_link_tag 'facebox' %>
  <%= stylesheet_link_tag 'application' %>    
  <%= JavaScript_include_tag 'jquery' %>
  <%= JavaScript_include_tag 'facebox' %>
  <%= JavaScript_include_tag 'application' %>
</head>

Facebox comes with a default stylesheet which assumes you have your images in /facebox. You’ll need to update these selectors in facebox.css like so:

#facebox .b {
  background:url(/images/b.png);
}

#facebox .tl {
  background:url(/images/tl.png);
}

#facebox .tr {
  background:url(/images/tr.png);
}

#facebox .bl {
  background:url(/images/bl.png);
}

#facebox .br {
  background:url(/images/br.png);
}

Now we attach facebox to the login link. Open up /public/JavaScripts/application.js and use this:

$(document).ready(function() {
    $('#login-link').facebox({
        loadingImage : '/images/loading.gif',
    closeImage   : '/images/closelabel.gif',
    });
});

I override the default settings for the images to reflect the new image path. Start the sever and head over to the index page. You should see a nice facebox with the login form:

Facebox

Next thing we have to do is set the form to submit itself via AJAX. Just like before, we’ll have to use callbacks to execute code after the modal box is ready. We’ll use jQuery’s post method for the XHR request. Facebox has an after reveal hook we can use. application.js:

$(document).ready(function() {
    $('#login-link').facebox({
        loadingImage : '/images/loading.gif',
        closeImage   : '/images/closelabel.gif',
    });

    $(document).bind('reveal.facebox', function() {
        $('#new_user_session').submit(function() {
            $.post(this.action, $(this).serialize(), null, "script");
            return false;
        });
    });
});

Updating create.js.erb should be easy enough. We have to update the facebox’s contents and re-ajaxify the form. Here’s the code:

$('#facebox .content').html("<%= escape_JavaScript(render :partial => 'form') %>");
$('#new_user_session').submit(function() {
    $.post(this.action, $(this).serialize(), null, "script");
    return false;
});

And that’s it! Here’s the final product:

Logging In
Bad Login
Redirected

Downloading the Code

You can get the code here. There are branches for each library so you can check out the Prototype or jQuery versions. Any questions, comments, concerns? Thanks again for reading!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Kyle

    I love rails! Keep up the great tuts.

  • http://brandensilva.com Branden Silva

    Neat effect with the slide down Adam. Nice to see some rail tutorials up on here.

  • Josue Abreu

    Great Work! I wish nettus would publish a lot more rails tutorials :(

  • BILL

    more rails tutorials please

  • Max

    HEy,

    who else wants a “Ruby on Rails from Scratch Video Series” by Jeffrey Way?
    Please do a follow up comment to support me!!

    thx for the rails tut!

    • Kyle

      2nd that

    • http://www.jeffrey-way.com Jeffrey Way

      I’d love it too….but not qualified unfortunately. :)

      • http://twitter.com/maxberndt Max

        do reply anyway so that Jeffrey can see how popular that would be.

        Sorry about that Jeff ;-)

      • http://blog.jackbliss.co.uk Jackbliss

        Dernit! Ah well, can we convince someone else to do it? This is a little advanced for me, but rails seems great.

      • http://seowisedesigns.com Yheng

        Who knows? Jeff has a lot of surprises for us I think ;)

      • http://www.broadcastingadam.com Adam Hawkins

        I could do it :)

      • http://www.broadcastingadam.com Adam Hawkins

        I could do it :) Email me if you want too.

      • http://twitter.com/maxberndt Max

        @Adam: even so I’d prefer one by Jeffrey as his teaching style at screencasts is unbeaten when it comes to easy understanding new stuff, one by you would be great as well. Please don’t take me wrong and don’t be offended, but we all know how popular Jeff’s beginner series are (ie. jQuery for Absolute Beginners).

        Just like Yheng sad: Maybe Jeff will surprise us :-). But if we can’t convince him, somebody else should definitely do such a “Ruby on Rails from Scratch Video Series”.

  • mnkd

    great stuff, Adam. keep ‘em coming!

  • http://www.portlandonrails.com Jason Schmidt

    Now that’s what I call a Tut! Nice job!

  • http://www.infoquestit.com Web designing company Qatar

    Hi Buddy!!!

    Thanks for give rails info … thanks many for solved my prob.. meet again…

  • http://spotdex.com/ David Moreen

    I’ve been wanting this for so long, thanks a bunch!

  • http://www.jordanwalker.net/index.php Jordan Walker

    That is a great write up about Rails and Ruby, I am not to familiar with this bit of technology.

  • http://www.compucraft.net Kris

    Great Job Adam! Your tuts are done very well.

  • Some1

    The link to source actually forwards to the demo page. Can this be fixed, pls?

    Thanks for the nice tut!

  • Gustavo

    Is it only me or TAB doesnt work on the login form?

    • http://twitter.com/maxberndt Max

      same with me

  • http://www.battlechapters.com/ Coops

    Good work…

    I have used a few different plugins/gems for authentication but authlogic is on my list of things to try.

    Thanks.

    And i agree with everyone above. more rails tuts and screencasts…

    Coops

  • http://bloggerzbible.blogspot.com/ Bloggerzbible

    Thanks its a really useful tool

  • stephen murdoch

    awesome! but there are some typos in the code above (note that the code in the github repo doesn’t contain any typos):

    1) When it comes to generating the user_session model, you instruct the reader to type the following:

    ./script/generate UserSession

    This code will not work. You probably meant the following:

    ./script/generate session user_session

    2) There are two problems with the initial version of the UserSessionController.rb file:

    i) the create method calls ‘redirect_back_or_default user_path’ – I think that’s meant be ‘redirect_to user_path’

    ii) when you first generate the UserSessionController.rb file, you forget to add a destroy method. This in itself is not a big deal, but you do go on to advise the user that they will be able to login and logout once they start up the server for the first time – this of course is impossible without that destroy action…

    3) Are you using some kind of grammar or spell checker? Because just about every instance of the word ‘javascript’ to appears as ‘JavaScript’ – thus the ‘javascript_include_tag’ and ‘escape_javascript’ methods are corrupted and the errors on the user session don’t show up in the facebox or modalbox

    4) Finally, the following code the prototype section, will not work when copy+pasted:

    If you look closely at the code you have written, the apostrophe’s are of the form that ruby doesn’t seem to like. I’ve often found this to be a problem when copying code from wordpress and other such blogging platforms… I’d just change those apostrophe’s so that they are the good ones.

    The same thing occurs when you instruct the reader to add the following to redirect.js.erb:

    window.location=””;

    Again the wrong kind of apostrophe is used.

    I realise that people shouldn’t really rely on copy+paste but yeah, noobs are bound to do it…. The copy_to_clipboard utility doesn’t appear to copy more than one line of any code block to the clipboard too…. But that’s something I’ve experienced on a number of other tuts on this site so not your concern….

    Anyway, I’m not grumbling, I think it was an absolutely excellent tutorial and will be recommending it to my friends and fellow coders. Keep up the good work.

  • stephen murdoch

    Whoops, resubmitting point 4 without the angled brackets….

    4) Finally, the following code the prototype section, will not work when copy+pasted:

    = stylesheet_link_tag ‘application’
    = stylesheet_link_tag ‘modalbox’
    = JavaScript_include_tag :defaults
    = JavaScript_include_tag ‘modalbox’

    If you look closely at the code you have written, the apostrophe’s are of the form that ruby doesn’t seem to like. I’ve often found this to be a problem when copying code from wordpress and other such blogging platforms… I’d just change those apostrophe’s so that they are the good ones.

    The same thing occurs when you instruct the reader to add the following to redirect.js.erb:

    window.location=” = user_path ”;

    Again the wrong kind of apostrophe is used.

  • http://igotjosh.com Josh

    Is it me? Or this is not working at all…. He did not set routes for any of the controllers so in the end the application goes nowhere…

  • anton

    Great tutorial. Many thanks.

    Quick question regarding layouts – the create.js.erb doesn’t seem to reuse the layout from new.html.erb, so any styling is lost if there is an error on login. Any tips on how to get this working?

    Cheers

  • James

    Good tutorial but it is now out of date. Please update so that it works with current versions of rails.

  • http://www.danielg.dk Daniel

    I actually have to agree with Anton on this one, where are the routes?…. Cant get it working because the routes havent been set…

    This tutorial is not 100%…

  • sam

    hi. can I actually place this in wordpress.? Please do reply asap. ! thanks a lot! :)

  • Ryan Isaac

    Thanks for the tutorial, I got the modalbox portion working, but I’m getting hung up on the facebox portion. First question would be how do you attach the facebox attribute to the link?

  • Ryan Isaac

    Can anyone answer this, after getting the modalbox portion to work just fine for a new user link in my model, I wanted to do the same on the same view for the edit link. Everything works as planned when duplicating the efforts made previously, except when there is an error in the form. I can’t seem to get it to stay in the modalbox to edit, even after doing the same things as I did to make it work for adding a new user. Any thoughts on what this might be?

  • Will

    Does not work.
    I am a complete Rails newbie (although a programming veteran), and since most tutorials out there seem to assume you already know how to use Rails, I am trying to learn by example. So bear with me if this mistake is basic.
    First off, apparently (as of Oct 2011) the “gem tumble” command is deprecated. It simply says that “gemcutter is the official source now” or something like that – it doesn’t say what command you SHOULD use, and I don’t know what the “tumble” command DOES (I presume it’s an update of some sort), so maybe skipping this is the problem.
    Anyways, when I run the “sudo rake gems:install” command, I get an error: “undefined method ‘map’ for nil:NilClass”

    I did get it to work one time – the first time – when I followed this tutorial rather haphazardly and out of order.

    If there’s an easy fix for this, e-mail replies would be much appreciated. I’m having a great deal of difficulty finding any example DIY Rails applications that actually work, and an even harder time figuring out what’s going on with this thing. If I could just get a few applications (like this one) to work, and play with them, I’m sure I’d understand :)

  • Giselle Hernandez

    Thanks for the tutorial! The source files lead to a 404 page, however.

  • Giselle Hernandez

    Thought this was a good tutorial until I saw the comments and saw that the Sources Files lead to a 404 page. Not gonna bother trying it out now. Thanks for saving some headaches, commenters!

  • kyle

    Figured it out! I just used request.referer and that worked perfectly. BOOYA.

    Thanks again for the great tutorial!