Getting Started with Rails 3: Part 2
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.
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 /urlswill 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/1will 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/newwill 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 /urlswill 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/editwill 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/1will 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/1will 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:
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
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:
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/:id, and
POST /urls pointing to the
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.
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_urlinstance variable to a new, unsaved URL object (we use
@shortened_urlas 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.
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
: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:
- Remove the
public/index.htmlfile. This is just the default “Welcome to Rails” page, but when present it overrides our root, showing every user the “Welcome to Rails” page.
- In the
config/routes.rbfile, 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
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,
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:
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 %>
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.
If you click on Shorten my URL without any URLs entered into the Your URL field, you will receive the following error page.
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.
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.