Getting Started with Rails 3: Part 2

Tweet

Welcome to the second part of our two-part tutorial. Last time, we installed Ruby on Rails, generated an application for shortening URLs and created the Model for it.

Now we’re going to create the Controller for our application, write the View and see if we can get the application to work. You might want to just quickly review where we were up to in Part 1, and then push on.

Creating Your First Controller

Now that we’ve created our model (with Rails taking care of persistence and validation) it’s time to write a Controller. The Controller part of your applications handles taking the input (for example, data from the URL or the request body in the case of POST and PUT), and then interacting with the Model, as well as setting up data for the view portion.

RESTful Controllers

By default, Rails emphasizes a “RESTful” approach; that is to say, it uses the given HTTP verb (for example, GET, POST, PUT, DELETE) and well-organized urls to represent resources, leading to a general structure where code goes for operations on an object. It provides tools to handle basic CRUD (Create, Read, Update, Delete) actions on your data. In Rails terminology, the actions are typically:

  • index—An action that returns a listing of all resources; for example GET /urls will hit the index action on the URLs Controller, listing the known urls.
  • show—An action that performs a read operation for a single resource; for example GET /urls/1 will hit the show on the URLs Controller, showing the details of the URL with id 1.
  • new—The action to show a new object form, in our case, the new URL form; for example GET /urls/new will hit the new action on the URLs Controller and show the new URL form.
  • create—A post action to take the form data from the new action and to attempt to create a record; for example POST /urls will hit the create action on the URLs Controller, typically with some associated form data.
  • edit—Will show the form to edit a specific resource; for example GET /urls/1/edit will hit the edit action on the URLs Controller and show a form to edit the URL with id 1.
  • update—Will attempt to update the given resource; for example PUT /urls/1 will hit the update action on the URLs Controller and attempt to update the URL with id 1.
  • destroy—Will attempt to destroy a given resource; for example DELETE /urls/1 will hit the destroy action on the URLs Controller and attempt to destroy the URL with id 1.

Generating Our Controller

In our example, we’re going to cut it back from a full CRUD Controller and implement three basic actions: new, create, and show. When the user hits the new action, it will render a new URL form. This will in turn hit the create action when we submit the form, and attempt to create the URL object.

Finally, when the user hits the show action, we will redirect them to the stored URL.

So, use the “Command Prompt with Ruby and Rails” option from the Start Menu to open the command prompt and change to the directory where you stored your code. We’ll generate a new URL Controller with a single new action by running:

rails generate controller urls new

The reason we only passed in the new action (instead of new, create, and show) is because Rails automatically generates a dummy view for each action included in the generator call. In this case, we only want a dummy view for the new action, so we exclude the others.

Running the command should have created an app/controllers/urls_controller.rb file and an app/views/urls/new.HTML.erb file, as well as a few test files. For the moment, open up the Controller file and you’ll see something similar to:

class UrlsController < ApplicationController
  def new
  end

end

And, if you open config/routes.rb, you should also see it added a dummy route for us:

get "urls/new"

To start, while still in config/routes.rb, replace the above line with:

resources :urls, :only => [:show, :new, :create]

Save the file, switch back to the command prompt, and run rake routes. This will print out a list of all our routes. As you can see, Rails automatically set up three urls for us—GET /urls/new, GET /urls/:id, and POST /urls pointing to the new, show, and create actions respectively.

If we start the server (using rails server) and hit the URL for the new action in the browser (http://localhost:3000/urls/new), you should now see a placeholder page similar to the image below.

fig2

Writing Our Controller Logic

In this case, our Controller logic is going to be very simple:

  • When the user hits the new page, it will set the @shortened_url instance variable to a new, unsaved URL object (we use @shortened_url as our instance variable instead of @url, as the latter is used internally by Rails).
  • When the user submits the new form, we should attempt to create a new URL. If it is valid and saves, we’ll set a message telling the user the URL of their shortened link, and then we’ll redirect back to the Shorten page again.
  • If the URL is invalid, we’ll re-render the new form, this time showing the errors.
  • When the user hits the show action, we’ll redirect them to the URL.

So, with appcontrollersurls_controller.rb now open in your editor, replace all of code with:

class UrlsController < ApplicationController
  def new
    @shortened_url = Url.new
  end

  def create
    @shortened_url = Url.new(params[:url])
    if @shortened_url.save
      flash[:shortened_id] = @shortened_url.id
      redirect_to new_url_url
    else
      render :action => "new"
    end
  end

  def show
    @shortened_url = Url.find(params[:id])
    redirect_to @shortened_url.url
  end

end

Save the file. One important point to note here is that our choice of Model name—Url—has led to potentially confusing routes (for example new_url_url). To understand what this means exactly, we just need to remember the output of rake routes. In there, we saw a route with the name new_url (among others). Rails automatically provides us with the new_url_url method (if our resources call in config/routes.rb had :posts instead of :urls, it would be new_post_url instead) as well as the _path equivalent; for example new_url_path. The difference between the two is that _url variants include the host, port, and protocol, whereas the _path variants include only the path and query string.

In the above Controller code, you may have noticed the line containing:

flash[:shortened_id] = @shortened_url.id

If you’ve used a language like PHP before where you have a session for storing data, you’ll be pleased to know Rails also supports a session for sharing data between requests. On top of this, it also has a second feature—the flash—which is a session that automatically expires entries. When we store data in the flash (like the id of our shortened URL above), it will be removed after it has been accessed. This is particular useful for tasks like passing error and status messages around (as opposed to in the URL), as they’ll show once, and will disappear on subsequent pageloads.

With all of that done, we now need to make one minor adjustment: we want to make it so if  users hits the root page of our application (for example http://localhost:3000/), they’re automatically redirected to the new URL form. In this case, it’s a fairly simple matter of doing two things:

  1. Remove the public/index.html file. This is just the default “Welcome to Rails” page, but when present it overrides our root, showing every user the “Welcome to Rails” page.
  2. In the config/routes.rb file, add a new line below our resources call.

The line we add should look like this:

root :to => redirect('/urls/new')

This tells Rails that when we hit the root URL (the equivalent of get '/' in our routes file), it should automatically redirect to the new URL action.

To try it out, go back to the terminal, restart the Rails server if you closed it, and then point your browser to http://localhost:3000/. If all went as expected, your browser should have automatically been redirected to http://localhost:3000/urls/new.

Writing Your View

With the Controller and Model for Shorty ready to use, it’s time to add the final layer: a View.

The View is the part that is actually shown to the user. By default, our Views are written in ERb: embedded ruby. ERb lets us embed bits of ruby into our HTML to generate dynamic pages. For example, to show the value of the URL attribute on a URL object, we’d use something like this in our View:

<%= @shortened_url.url %>

Then, at runtime, Rails will interpret the ruby portion to generate our HTML. Since our application is very simple, we’re only going to have to edit two views: the app/views/urls/new.html.erb file and our layout, app/views/layouts/application.html.haml.

Editing Your Layout

Layouts are the Rails way of defining the general structure or layout of the page. While the new.html.erb file is used for the HTML specific to our action (that is,for example the new action on another Controller will use another template), layouts are typically reused across several or all Actions. They are typically used to set up the general page structure; for example headers, menus, footers, and the like, while the page-specific stuff goes in your normal View.

In this case, our default application.html.erb looks like:

<!DOCTYPE html>
<html>
<head>
  <title>Shorty</title>
  <%= stylesheet_link_tag :all %>
  <%= javascript_include_tag :defaults %>
  <%= csrf_meta_tag %>
</head>
<body>

<%= yield %>

</body>
</html>

This is a very basic HTML5 page that does nothing but render the action view inside the body tag. The only aspect we’re going to change on this page (to make it reusable when we extend the application in the future), is when we shorten a link, we get a link to it at the top of the page. So, below the <body> opening tag and above the line with yield on it, add the following:

<% if flash[:shortened_id].present? %>
  <p class='shortened-link'>
    The shortened url is available <%= link_to 'here',
url_url(flash[:shortened_id]) %>.
    (Right click and copy link to share it).
  </p>
<% end %>

The link_to in the code is simply a call to what is known as a helper method; in this case, something Rails defines for us that generates the HTML for a link with the given text (‘here’ in this example) pointing to the given URL (in this case, something like http://localhost:3000/urls/1) when a URL has been shortened. If no :shortened_id value is present in the flash, nothing will be shown, but when one is present it will output a link to it inside a paragraph tag.

Editing Your Form

With the layout updated to show our link post shortening, we now need to set up our form. First, open up app/views/urls/new.html.erb in your editor. By default, it should look like:

<h1>Urls#new</h1>
<p>Find me in app/views/urls/new.html.erb</p>

Delete that and replace it with:


<h1>Add a new URL</h1>
<%= form_for @shortened_url do |form| %>

  <p>
    <%= form.label :url, "Your URL:" %>
    <%= form.text_field :url %>
  </p>

  <% if @shortened_url.errors[:url].any? %>
    <p class='error-messages'>
      The given url <%= @shortened_url.errors[:url].to_sentence %>.
    </p>
  <% end %>

  <p class='buttons'>
    <%= form.submit "Shorten my URL" %>
  </p>

<% end %>

When run, this code will generate a form tag (with the correct Action and Method) with a bunch of other HTML inside of it. Inside here, it’ll generate a paragraph containing a label and text input for our URL field. In this case, we’re using a specific class of helpers via the form_for method. These are classes provided by Rails to make it easier to build up complex forms by handling generating fields and such.

Next, we check if there are any errors on the URL field of the shortened URL object, and generate a paragraph tag with the class error-messages when there are. If the object doesn’t have any errors, it won’t output the HTML at all for this portion.

Lastly, we generate a submit tag with the text “Shorten my URL,” finishing off our form.

Testing It Out

Going back to the console and starting the Rails server again (if it’s been stopped), reload http://localhost:3000/urls/new in your browser and you should see the following.

fig3

If you click on Shorten my URL without any URLs entered into the Your URL field, you will receive the following error page.

fig4

If you enter a valid URL into the Your URL field and click Shorten my URL (for example http://google.com/), you’ll see the following image.

fig5

As a bonus, by using a form builder, Rails provides a lot of extras. For instance, it automatically sets the value of the text field so that when we go to submit it the second time (after an error), it will insert a special token to prevent cross-site request forgery. In general, it makes it very easy to build up forms.

Wrapping It All Up

As you’ve seen, with very little code we’ve been able to build a rudimentary URL shortener (whether the URLs are actually shorter depends on your domain and the input URL, but it’s the idea in general that matters). We’ve only just scratched the surface of what you can do with Rails, but we have managed to cover a decent amount of Rails in a short period of time.

Of course, the application we built is not without flaws. For example, whilst we validate that a URL is actually given, we don’t validate that it is a URL (and we blindly redirect to that value); hence, this isn’t the sort of code you’d actually deploy. Likewise, the workflow itself is very rudimentary.

If you’re keen on what you’ve done so far, you now have a good starting place to learn more about Ruby on Rails. For example, you might go about adding better url validation, add a way to list all urls shortened, or even better, use the routes and a custom Action that works more like existing URL shorteners. For bonus points, you could switch to an alternative form builder like formtastic,which will generate even better in-depth HTML for us (removing the need to do tasks like printing the error messages manually).

There is a wealth of documentation out there for Rails beginners, some better than others, and some of it requiring payment. I strongly suggest you start with the following resources:

  • The Official Rails Guides—free, lots of relevant information, and a great place to start.
  • The Ruby on Rails API—an API reference for the methods available from Rails.
  • Railscasts—a lot (250 and counting) of free, short screencasts on performing different tasks in Rails.
  • PeepCode—paid but professionally made screencasts on topics. Longer than Railscasts and generally more in depth.
  • Rails Tutorial—a free book (with paid downloadable versions) that serves as an in-depth guide to Rails.

I hope you enjoyed gaining a brief feel for the Rails way of doing things.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • noob

    Thanks for the tutorial. I am lost here. Is this method :shortened_id defined anywhere. That is, is there a method defined anywhere that does the actual shortening of the url. I can’t seem to find that in both part 1 and part 2 and that leaves me confused. thanks again.

    • Darcy Laycock

      flash[:shortened_id] doesn’t call a shortened_id method – In ruby, :shortened_id is what is called a Symbol (think of it like a String, but special – They’re usually used to refer to fixed values e.g. where you want something as an internal identifier like the key to a Hash / Associative Array without having to use a string).
      In this case, our lines use the flash method which returns a Hash-like object (that is to say, “flash[:shortened_id]” will access the :shortened_key on the flash object, “flash[:shortened_id] = ‘Some Value'” will set the value associated with the :shortened_id key.
      For a bit more detail about Hashes and Symbols in Ruby, Check out the “The Ruby Language” chapter and the “Ruby.new” chapter in Programming Ruby at http://ruby-doc.org/docs/ProgrammingRuby/ – It’s an older version of the book (You can buy a print or ebook up to date copy of it online for reasonably cheap) for a better explanation of what these do.

    • Anonymous

      The shortening is actually mapping http://localhost:3000/url/1 to http://myreallylongdomain.com or whatever url was entered. In production, maybe you own abc.com which would be pretty short: http://abc.com/url/1

      Symbols are also faster than a string in that they don’t have the methods that strings do.