David Heinemeier Hansson’s “weblog” Rails tutorial, step by step

July 13, 2007

IMPORTANT NOTE PLEASE READ! – the latest version of Rails, 1.2.5, has some major changes (such as RESTful scaffolding by default) which make this tutorial out of date. RESTful development is a majorly important trend in rails dev, but it might be useful for you to see how ‘traditional’ rails works first (most tutorials use traditional rails, for example). If you’ve installed rails after (approximately) the middle of october 07, you might have 1.2.5 – you can check by typing

gem list

and then scrolling down the list of installed ruby gems to find Rails.

If you want to follow this tutorial (and most others), you probably want to roll back to 1.2.3. The simplest (and probably clumsiest) way to do this is to type

gem uninstall rails
gem install rails -v1.2.3

(there are other ways involving ‘freezing’ rails into your app but they’re beyond the scope of this tutorial)

Most of the tutorials you will see on the net, and books, and even the tutorials and articles on RESTful Rails, are based on pre-1.2.5 rails versions, so if you’re learning rails you should probably stick with 1.2.3. You can still do RESTful dev in 1.2.3, it’s just not the default, and in fact there are some changes in 1.2.5 that make following even RESTful tutorials difficult. So, i’d recommend 1.2.3 to anyone learning rails: leave 1.2.5 till the tutorials have caught up.

END OF IMPORTANT NOTE – read on.

I went to look at this famous rails tutorial by DHH, as the creator of Rails is often referred to, in which he debuts Rails by showing how to make a blog app in under 15 minutes. Watching the video (which was actually very awkward because the quicktime controls were outside of my screen area, so i couldn’t pause it), i was struck by how much i prefer tutorials when they’re written down (though having a movie as well is useful). So, i decided to do a written version of his tutorial, with extra explanation, mainly for my own benefit, even though it’s written as if i’m instructing someone. Here goes. By the way, i’m using windows, while DHH uses a mac in the demo. Rails is pretty much platform-independent but i did find a few minor differences so if you’re on windows then this tutorial may be well suited to you.

MASSIVE DISCLAIMER – I’m a noob myself so some of this so-called teaching may be wrong, or at least not properly explained. If you see any errors please let me know!

Before we start, i’m assuming that you have at least a little (like me!) programming experience in some language, and that you have a basic (like me!) knowledge of ruby. If you don’t know (for example) how a ruby hash or a ruby for loop works then go and read the ruby page on wikipedia, it’s a nice quick introduction. That reminds me – Ruby and Rails are both meant to be capitalised but i’m lazy so will tend not to bother. Just in case anyone gets bothered by this sort of thing, i apologise but i’m not actually sorry.

First, install ruby and rails, and mysql, and a web server. The guys from Onlamp explain how to do this pretty well here. I used their recommendation of InstantRails which does pretty much everything you’ll need except perhaps a mysql front end (which isn’t strictly necessary anyway – see Migrations, later).

So, assuming you’re all set up with the above (you might need to add the ruby/bin folder to your PATH environment variable if you’re using windows), open a command window in your rails working folder (eg InstantRails/rails_apps in my case) and type

rails blog

This creates a framework structure for a rails app called blog. You should see a load of stuff get made. Next, go to the new blog folder (in a new command window) and start your web server. I do this with

mongrel_rails start

Now, your web server should be up and running. Even though we’ve not created a database yet we can still go and see our rails app running, at http://localhost:3000/ in my case (your web server should tell you at what local address it’s running). We see a screen saying “Welcome to rails!” and some other standard stuff that shows us it’s working.

Next, we generate a controller class, called Blog. Do this by typing (at the command line)

ruby script/generate controller Blog

This will make a bunch of new files – it should list them, along with some others that weren’t created because they already existed. Go and open one of them, blog\app\controllers\blog_controller.rb. It contains a single empty class, BlogController, which extends ApplicationController.

Let’s add a new method, “index”. Index is the first page called by any website, and an index method acts as a replacement for the page, which doesn’t exist yet – it will be called when we browse the site.

The method is simply “hello world” – add the following inside the class:

def index
render :text => “Hello world!”
end

Some explanation: “render” is a rails method for rendering web pages. :text is a symbol, which is like a fixed variable name, actually stored as a number (under the hood, though we can view the number as well) for efficiency. Symbols are used in a similar way to variable names, and are denoted by the colon prefix. They have properties of a string as well as the hidden number – printing them simply prints the name minus the colon for example. They’re commonly used as hash keys, and we don’t put them in quotes when we use them. There are various ‘flavours’ of render – for example, we can render a saved html file with render :file => “file path /name”. Some more on render here.

The use of => in this case refers to the creation of a hash pair, which we pass to the method: the hash key is :text and the value is “Hello World!”. In this case, we’re calling the render method, which can take a hash. It then checks the hash for the existence of a specific key or keys, like :text for example, and if they exist then it gets the value associated with the key and does something with it – in this case print to the browser. The above code would be a little more obvious if the {} around the hash and the () around the method arguments weren’t omitted (like a lot of things in ruby, the syntax is optional). In other words, the line could have been written as

render( {:text => “Hello world!”} ) #passing a hash with one k-v pair as an argument

This strategy, of having a single method that takes a hash and analyses its contents to decide what to do, seems to be the ruby alternative to having lots of overloaded methods that take different arguments, as in Java or C. I guess that because of ruby’s dynamic typing, it’s hard to differentiate methods based on arguments. BTW, the hash must be the last parameter in the list, to allow an arbitrary (including zero) number of key-value pairs to be passed to the method and treated as belonging to the same hash.

Anyway, let’s get on with it. Save blog_controller.rb, go back to the browser and go to http://localhost:3000/blog or your equivalent. The text “Hello world!” should now be there.

As DHH explains, having the controller print to screen isn’t really the right way to do things – rails uses the model-view-controller pattern, and anything to do with displaying should be done in the “view” section of the app. You probably noticed how rails created controllers, models, and views folders in the app folder (the app folder is where we do our work). Remove the hello world line from blog_controller.rb (leave the empty index action for now) Go to the views folder. Rails uses a special kind of file to generate html for the browser to read, called rhtml files. These are basically html in which we can embed chunks of ruby code inside <%= %> brackets: when rails runs the rhtml files are used to make html, with the ruby blocks filled in with the results of the ruby code.

Make a file called index.rhtml in the app/views/blog folder – this is a template. Write “Hello from the template!”, save it, then go back to the browser and refresh. Instead of the ‘index’ action, the browser is now loading the index page (we didn’t have one before), and we see our message. We can go back and delete our index action completely now – the page doesn’t need the action to work.

Now let’s get serious – let’s get some database action in here.

(Postscript – Originally, in this tutorial, i set up the database in the same way that DHH does in his movie, ie by hand using a DB front end such as HeidiSQL. Since then i’ve learnt about migrations, which are a much better way to do it. So, i’ve rewritten this section to use migrations and explain how they work)

If you look in blog\config\database.yml you can see that rails has already set up the config for connecting to a DB called blog_development (we’re just worrying about the dev version of our software for now), on a DB server. I’m using mysql like in the onlamp setup instructions, you should be able to change the details for your own DB if it’s different (enter your username and password for example, if you changed your user name or don’t just have a blank password). But, we don’t have a DB called blog_development yet, so let’s make one.

The simplest way to do this is (in mysql) to type at the command line

mysqladmin -u root create blog_development

(“-u” sets the username, which is “root” in our case – the default mysql username.)

If it went well, it doesn’t tell you anything (oddly), and just returns to the command line after a pause. I use a db front end (such as HeidiSQL) just to double check that things are ok – it’s also useful for quickly looking around the actual data to see if it’s being added properly, what’s in there, etc. But, if it didn’t complain it should be ok.

Using mysqladmin like this is a quick alternative to logging onto the database, making the change, then logging out again, and we only need to send one message to mysql from the command line. Once we’ve created the DB, we’ll do everything else using Rails database migrations. However, if mysqladmin didn’t work for you for some reason, then type

mysql -u root (this should log you into mysql)
create database blog_development;
exit;

Ok, so we’ve got a database, now we need a table. DHH does this by hand, using a DB front end, but we’re going to use a Migration, which is simply a ruby file that sets up the table for us when run with a multi-purpose bit of software that comes with rails, called ‘rake’. I’m not really going to get into rake in this tutorial, but it’s like your Rails development assistant and is generally great.

Anyway, as usual Rails does most of the work for us with the migration. When we create a new model (which we’ll do next), rails makes a migration file to add a corresponding table to the database, which we then go and edit with the details of the fields we want in the table. So, what’s a model anyway?

The purpose of the DB is to model and store our domain entities, which we’ll mirror with our software classes, which are called models. The first entity we’re going to model is a post, which consists of a title, some text, and a date. Ultimately we’re going to have a class in our models folder called Post, and one of the many cool things about rails is that if we name things right, then our ruby classes automatically map onto the database structure. The way we name things right is to have the name of a database table being the plural of the class it represents. So, in this case we should call our table “posts”. (Rails is pretty clever with the pluralisation – for example it knows that ‘people’ is the plural of ‘person’. We can override this automatic mapping if we want to , by explicitly saying “this class maps to this table”, but generally we go with the conventions.

(POSTSCRIPT – i’ve since learned about how to override the rails defaults for table and field names, etc. For example if we wanted our Post class to map to a table called blogposts, which had a primary key called bp_id we could have, at the top of the class definition

class Post < ActiveRecord::Base
set_table_name “blogposts”
set_primary_key “bp_id”

Anyway, we’re sticking with the standard names so don’t need to worry about this. More on this sort of thing here)

So, finally, let’s go and make the Post model. Rails makes much more than a class to go with this – it creates all the infrastructure to connect the class and the table in the database, for example. On the command line, navigate into our new “blog” directory, and type

ruby script/generate model Post

(on mac, i believe it’s ./script instead of ruby script)

That should create a few new files, the most interesting for the moment being models/post.rb. Go and open that – we can see that it’s made an empty class, that extends ActiveRecord::Base.

At the bottom of the list of files that were created, we can also see

“create db/migrate/001_create_posts.rb”

Rails has created a migration file to make the db table to go with the Post class. How handy. Go and open it up. It should look like this:

class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
#t.column :name, :string
end
end

def self.down
drop_table :posts
end
end

This migration has two methods – self.up and self.down. self.up is run when the migration is applied, self.down is run when we want to roll our database back to an earlier version, and should basically undo whatever self.up does.

In this case, self.up adds a table, but we also want some fields in the table – we need “id”, a unique id for the database record, ‘title’, and also ‘body’, for the text body of the blog post. As it happens, rails will always add an id field automatically, so we don’t worry about that. All we need to worry about is adding ‘title’, a string, and ‘body’, which is text (text is basically a longer string – “string” creates varchar which is only 255 chars long by default — fine for the title but not the body).

Helpful as ever, rails has put in a comment as an example of the required format: our finished migration should look like this:

class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.column :title, :string
end
end

def self.down
drop_table :posts
end
end

Once you’ve edited the migration as above, save it and go back to the command line. It’s time to use rake. Type

rake db:migrate

and rails runs any db migrations that haven’t already been done (it keeps track of this via the version number, which can be seen in the automatically created db table called schema_info). It should tell you the migration was completed successfully. (Read more about migration here) If you want, go into the db using a front end and have a look at the new table. Note how much SQL we’ve had to write so far – zero lines. This is a pleasant trend that will continue throughout our rails dev.

(POSTSCRIPT: The following paragraph (about scaffold :post) is a recent addition, i mistakenly omitted it in my original writeup, which created a ‘window’ of things not working properly till we got to ruby script/generate scaffold Post Blog further down. Thanks to those who spotted it!

In addition, one of the major changes from rails 1.2.5 onwards is that scaffold has been changed to now use the RESTful approach. I’m not going to get into REST here except to say that it’s a different way of structuring Rails apps and is likely to become the norm. Read about REST elsewhere, there’s plenty of tutorials about. For now though, to follow this tutorial you’d probably better be in rails 1.2.3 or earlier.)

Now, we’re going to get rails to create a ‘scaffold’ for us – the scaffold is a set of default controller methods and view rhtml that allows access to the data stored in the database: in this case, we want a scaffold for the Post objects, allowing us to create new posts, delete, update – the CRUD stuff basically. In a little while we’re going to get these created in a way we can start to muck about and change them (in this tutorial we only get as far as altering the view scaffold: normally you’d alter, or completely do away with and rewrite, the controller scaffold as well), but for now let’s use the under-the-hood rails defaults. We do that by writing, in the BlogController class,

scaffold :post

This is simply a method call, like with render earlier – the scaffold is generated at runtime.

Whenever we change the structure of the model (which we just did by creating Post), the changes aren’t reflected in the app until we restart the web server. So, we need to do that now – just terminate it and restart. Now we’re about to have our first real ‘wow’ moment in rails. Browse to http://localhost:3000/post and we’ve now got an interface for our DB: we can add posts, view posts, and also edit and delete them, all for free, courtesy of rails.

Now, let’s add a body to the blog entries. We do this in the database – just create a new field called ‘body’, make sure it’s of type ‘text’, and then just reload the browser or go to ‘new’. You’ve now got a big text_area to type your blog body into. Just from a change in the DB!

Next, we add a date field in the DB, called “created at”. Refresh. As DHH would say, “whoops!”, that’s just a date field, not a ‘date and time’ field. So, go back to the DB and change the type to DATETIME. Then refresh the browser again – it’s fixed. Nice. Next, go back and move the created_at field above the body in the DB. (if you’re doing this in HeidiSQL you can’t just drag it, you need to alter the field properties and get a scary message saying it might horribly break your DB. Just go for it.) Again, the change is instantly reflected in the new and edit pages in the browser (well, maybe after a refresh anyway).

Next we do some validation code to check that posts have a title. This validation belongs in the model – it’s the model of the post that ‘knows’ it should have a title, not the controller. (remember, the model represents our chosen way of doing things: while there’s no absolute reason why a post should have a title, that’s how we’ve decided they should work. It’s our ‘business logic’.) So, we’re editing the post model, so open models/post.rb. Add the following code inside the class:

validates_presence_of :title

Save, go to make a new post, leave the title blank, and hit ‘create’. It should complain and highlight the title field. Note that we didn’t have to restart the web server – we only do that when we change the structure of the model (ie the database schema). Changing contents of the class like this is ok, but if we link classes together (which we’ll do later) we may need to restart.

Now, it’s time to start customising our app: the scaffold is only there to help us get the building up, after all. We want to be able to get at the rails-generated scaffolding and start mucking about with it. That means that instead of the under the hood scaffold, we want some proper editable code. We generate it by typing, at the command line

ruby script/generate scaffold Post Blog

Rails will ask abut overwriting some files. Press a (“all”) to force the overwite. Now, have a look at blog_controller.rb and see all the actions that have been created. It should also have removed the call to the default scaffolding we added earlier. We’ve now got a bunch of methods.

Before changing the controller methods though, let’s change the look of the blog. Go to views/blog/list.rhtml, which is a template for the list page. Change the heading (h1) at the top to”My wonderful weblog”:

<h1>My wonderful weblog</h1>

Scaffolding was also placed in the view files, making some default display code for us. Now we’re going to chop some of the scaffold-created stuff out and replace it with our own html and ruby code. First, chop out the first table row <tr> section, which is the column titles. Have a look. Next, we make each post title a header (h2), that links to the post. After
<% for post in @posts %>

add
<div>
<h2><%= link_to post.title, :action => ‘show’, :id => post %> </h2>
</div>

Let’s try and break this ruby code (in red) down a bit:

for post in @posts #for every object (called ‘post) in the instance variable @posts (which points to an array of post objects, pointing into the DB), do the following:

link_to post.title, :action => ‘show’, :id => post #equivalent to
link_to(post.title, {:action => ‘show’, :id => post} ) #create a link that is represented by the post title, calls the action called ‘show’ when clicked, and passes the ID of the post to the DB to retrieve the body.

Note that in the first ruby block, there’s no =. This is because we only need <%= on lines that return something. Purely structural code like a for expression only needs <%. Have another look in the browser.

Next, show the post body beneath each title: add this line before the closing div tag:

<p><%= post.body %></p>

It’s easy enough to see that this is literally just displaying the body of the current post (as we cycle through the full list of posts). We can also see that the show/edit/destroy links are now cut off from the posts they correspond to, so let’s get them back in the same div element as the new title and body. (Actually we only need edit and destroy, as we show a post by clicking on the title now, which is more intuitive anyway). We put edit and destroy in their own paragraph, in small text, with the time of creation as well. In addition, since we’ve now got a big continuous list of posts, there’s no need to split them onto pages, so we can delete the previous page/next page lines.

After doing all that stuff, list.rhtml should look like this:

<h1>My wonderful weblog</h1>

<% for post in @posts %>
<div>
<h2><%= link_to post.title, :action => ‘show’, :id => post %> </h2>
<p><%= post.body %></p>
<p><small>
<%= post.created_at.to_s %>
<%= link_to ‘Edit’, :action => ‘edit’, :id => post %>
<%= link_to ‘Destroy’, { :action => ‘destroy’, :id => post }, :confirm => ‘Are you sure?’, :method => :post %>
</small>
</p>
</div>

<% end %>

<%= link_to ‘New post’, :action => ‘new’ %>

Note – i had one problem with DHH’s code: the line where we convert the timedate object into a string, (<%= post.created_at.to_s %>) he was passing it the symbol (:long). This broke it for me, i had to take out long and just call the to_s method with no parameters. Then it was fine. I don’t know what passing :long to the to_s method is supposed to do. My code shown above has the (:long) taken out. If anyone can explain this please add a comment.

Next, DHH shows how to list the posts in reverse order, ie most recent first: simply go to the for block in list.rhtml and change posts to posts.reverse. This demonstrates (i guess) that when we get the rows out of the DB they come into ruby as array elements, where each element is another array (or maybe a hash?).

More polishing: at the moment our body is just plain text: no italics or bold. We add this functionality in via the method textilize, which uses the same rules as the common markup language textile, in which we show bold with *bold text* and italics with _italicised text_. textilize just takes any block of text, which is what we have. Note that this is happening in the view. The database doesn’t know anything about bold or italic text – the view (in list.rhtml in this case) is translating the *s into bold markups. So, where we have post.body at the moment, we pass this as an argument to textilize:

<p><%= textilize(post.body) %></p>

Having said this, rails can’t find the textilize method when i try it: after a quick look on the net, i found that it needs to be added via a ruby gem before use (it’s part of the redcloth gem, which is kind of what packages are called in Ruby). So, if this doesn’t work for you either, just change it back to how it was before. That’s what i did (gems ain’t the focus of this tutorial).

(POSTSCRIPT – It’s actually very easy to add this gem: Just type “gem install redcloth” at the command line and then restart the web server. I have had some problems with other gems, such as rmagick, though, whose windows versions are a bit complex/broken)

Next, we’re going to look at partials, which are blocks of code that can be reused by different files. Open blog_controller.rb. We’re going to change the list action to just find all posts, instead of trying to seperate them into ten per page or whatever:

def list
@posts = Post.find(:all)
end

Then, go back to list.rhtml. Cut out all the contents of the for block, and put them into a new file, called _post.rhtml. This partial is going to be our ‘post displaying’ code. Make sure the name has the underscore, as this is used to denote partials. Now, _post.rhtml is a partial that represents the display for a single post. We can now use this in various places to make sure that a) we have a consistent style and b) we don’t repeat ourselves. “Don’t Repeat Yourself”, or “DRY”, is a good design pattern for any language and is a ruby and rails mantra.

The first place we need to use it is the place we just chopped it out of! Go back to list.rhtml and remove what’s left of the for loop completely. Instead, we have a single call to the render method. As with our “hello world” code earlier we pass render a hash, but in this case the hash has two elements: one has the key :partial, and points to the partial in question, simply called ‘post’ (it knows to add the underscore when looking for a partial), and the other has the key :collection, which points to @posts.reverse. So we’re saying “do the partial for every item in that collection”.

So, list.rhtml now looks like this:

<h1>My wonderful weblog</h1>

<%= render :partial => “post”, :collection => @posts.reverse %>

<%= link_to ‘New post’, :action => ‘new’ %>

Now refresh the list page – it should look exactly the same. Of course, partials are only worth doing if we use them in more than one place, so lets do that now. In the browser, click on a item title to go to the ‘show’ page. It still has the old default looking style: we’re going to fix it to use the partial as well. Go to show.rhtml, delete the for block from there and replace it with a line that’s almost the same as the one we used to replace the for block in list.rhtml. The only difference is that instead of displaying a whole collection of posts, we just want to show one, the current one, ie @post. (we don’t reverse it because it’s a single post – reversing it makes no sense). So we change :collection to :object, and @posts to @post –

<%= render :partial => “post”, :object => @post %>

Now refresh the show page in the browser – it should have the new template.

Next – adding comments. Comments are a whole new type of object, so we’re going to have to add the model and add a corresponding table to the DB. As before, we’ll make the model, edit the migration created, and run the migration to make the table. So, let’s do the model. At the command line, in the main blog folder, type

ruby script/generate model Comment

Among other things, that will have created a new model file, comment.rb. (this will map automatically onto a table called “comments” in the DB but we’ll get to that in a bit). Open comment.rb.

Next we model the relationship between our classes: a comment belongs to a particular post instance, and we inform rails of this by writing

belongs_to :post

inside the Comment class definition. This relationship is two way – a post can have many comments. So, we need to modify the Post class definition as well, with

has_many :comments

Some nice self documenting code there.

Ok, now we need to sort out the DB table. As before, rails made us a migration, called 002_create_comments.rb

Now, the comment table is a bit more interesting than the post table. It has a foreign key field – basically, a field that links the comment to a record in another table. In this case, we want to link the comment to a post – the post the comment was left for. So, the foreign key field should be called post_id. This is a simple way to tell rails how to link the comment to the post – a comment with post_id = 23 goes with the post whose id = 23. Nice and simple. If we follow this naming convention Rails knows, without being told, how to link the tables together. This is the database component of the linking that we set up when we wrote “post has_many :comments” and “comment belongs_to :post” in the model classes.

Apart from id and post_id, all we need in a comment is ‘body’ – the body of text left by the commenter. (In a more fully featured app we’d want other things as well, such as the date and time of creation, and the person who created it. But for us, now, a comment is simply a chunk of text attached to a blog post).

So, the migration, 002_create_comments.rb, should look like this:

class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.column :body, :text
t.column :post_id, :integer
end
end

def self.down
drop_table :comments
end
end

Save this, and type

rake db:migrate

at the command line. It should do migration number 2 but not number 1. Since we’ve made a major change to the database schema by adding a table, we need to restart the server to get rails to pick it up. Stop the server with Ctrl&C and type

mongrel_rails start

to restart it again. Adding a new table to the DB is pretty much the only time we need to restart the server like this – normally in rails we just make our changes and refresh the browser.

At the moment, we want to see how comments are displayed, but we don’t have an interface to add comments to our blog app. So, as a shortcut, we’re going to create one in the database to use as an example/test. So, go to the data page of the database (if you’re using heidisql you might need to refresh the table view first) and write some entries into the fields: use an id of 1 and make sure you use a post_id that exists in the posts table. Swap between table and data view to make sure your data got saved ok.

Ok, now we’re going to work out how to display the comments. The main (perhaps only) place for comments to appear is on the ‘show’ page, so let’s look at show.rhtml. After the post, and the edit/back links, we’re going to list all the comments. This is another for loop, along the lines of “for every comment, show the comment”. Because of the conventions we followed, rails lets us get at all the comments belonging to a post simply by writing @post.comments. We then make a for loop out of this as follows, showing the comment body for each post, inside the loop.

<% for comment in @post.comments %>
<%= comment.body %>
<% end %>

That’s worked, but the comment is simply listed on the same row as edit/back, which looks bad. So let’s add just a little html formatting: a “comments” heading before we list the comments, and a line break (<hr /> ) after every comment. So, show.rhtml now looks like this:

<%= render :partial => “post”, :object => @post %>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @post %> |
<%= link_to ‘Back’, :action => ‘list’ %>

<h2>Comments</h2>

<% for comment in @post.comments %>
<%= comment.body %>
<hr />
<% end %>

Next, lets let the user add comments. We’re going to do this with some form components that we write straight into show.rhtml. The code we’re going to write is pretty much the same as already exists in _form.rhtml and edit.rhtml, in the views/blog folder. We call form_tag with a hash consisting of the key “:action” and the value “comment”, and the key “:id” and the value @post. In other words, when we submit, we’re making a new comment, and also passing through the id of the post we’re looking at when we write the comment, so the DB knows which post we’re commenting on.

Then we’ve just got a text area which goes into body and a submit button. Finally we close the form with an html end tag. So, the following goes at the bottom of show.rhtml:

<%= form_tag :action => “comment”, :id => @post %>
<%= text_area “comment”, “body” %><br/>
<%= submit_tag “Comment!” %>
</form>

OK, refresh the show page in the browser, and you should see a new text box and a submit button, labelled “Comment!”. Enter a comment and hit the “Comment!” button. Whoops! (sorry, couldn’t resist it). Rails doesn’t yet know what to do with this thing we just created, and we get an error message.

To fix this, we need to make an action called “comment”. This makes sense considering above that we told the form tag that we were calling an :action called “comment”. So, we do this in the controller. Remember the rules of MVC – the model is our ‘business logic”, ie what holds what, what connects to what, etc. The view handles everything to do with displaying the data to the user. The controller handles user actions, and making a new comment is a user action (the interaction happens, ie the form is displayed, in the view, but as soon as the user submits, it’s the controller’s job to handle the action).

(Postscript – i’ve since discovered the ‘skinny controller/fat model’ principle, which says that as much functionality as possible that relates to an object should be moved out of the controller into the model class for that object. The controller, then, should really just act like a manager – telling things what to do but letting them actually do the work. Under this principle, the below code is actually fine, since we’re doing about as little as we could possibly do anyway. But if the process of creating a new comment was any more complicated, then we should really call a method in the Comment class, from the controller, to do it, then, again back in the controller, decide what to do next, eg redirect to the show page for the commented-upon article. So remember – the controller’s just a manager, and it should be a lazy one at that.)

So, open blog_controller.rb. This is perhaps the most complicated bit of ruby coding we’ve done yet, so i’ll go through step by step. If you see any errors in my explanation please let me know.

Before we start, a brief diversion to talk about parameter passing in rails, and ‘params’. ‘params’ is the default way of passing through parameters to a method, and it’s a hash. That means it contains hash keys, which as we’ve seen consist of a key and a value. Usually, the key is a symbol, ie starts with a colon. We can pass any (and as many) keys we want through, but the method is normally going to be look for some specific key or keys, denoted by name, which is a symbol like i said. When we pass the key through we don’t have to explicitly put it in params, but we need to look in params for it at the other end, ie inside the method, which we do in the standard square-bracketed hash-accessing way.

So, for example, if we wanted to write a method called ‘hello’ which takes a name and returns “hello name!”, then the typical rails way would be

def hello
#in ruby the last evaluated statement is returned by default
#so we can omit ‘return’ and just have the statement

“hello #{params[:name]}!”
end

Now, the person using this method needs to know to use the symbol “:name”. If they try to call it with hello(“max”) then it won’t work. So, the name of the symbol presents a bit of inflexibility and requires a bit of knowledge of the method. Generally, though, naming conventions make it easy to remember the names of the symbols and in lots of other ways it’s great: for example, the order in which you pass the parameters in a given hash is not important – a big plus. Anyway, to call the hello method we would say

hello :name => “max”

BTW, in case you were wondering, #{} is a ruby mechanism: the contents of the brackets is evaluated and then converted to a string. They’re commonly used inside a string as an alternative to the

puts “blah ” + var1.to_s + ” blah blah ” + var2.to_s

javaesque way of doing things – we’d instead write the above as

puts “blah #{var1} blah blah #{var2}”

Anyway, that’s enough diversions into ruby. Let’s get on with our rails app!

In BlogController.rb, make a new action called comment. The first line inside the action is this:

Post.find(params[:id]).comments.create(params[:comment])

Breakdown:

Post.find(params[:id])
This is going to use the class (ie static) method find, using the :id passed through from the form (remember the second key in the hash?).

.comments
Then (all on the same line), we take that post and access the comments array associated with it.

.create(params[:comment])
In that array, we create a new comment, passing through :comment (which is the comment we just created). This is a lot of work for one line – in this respect Ruby can be a bit scary at first, like your first driving lesson.

The next line stores (not sends!) a message to the user that their comment worked.

flash[:notice] = “Added your comment.”

‘flash’ is a temporary store in rails. We can put stuff in there, and if it’s not used before anything else happens, it’s deleted. The next time the view displays anything it will check if there’s anything in the notice field and if there is, display it. This is an example of controller-view separation: the controller doesn’t directly display anything: instead it’s stuck a postit on the view’s desk.

Next line:

redirect_to :action => “show”, :id => params[:id] #equivalent to
redirect_to ( { :action => “show”, :id => params[:id] } )

By now we should be getting used to this hash-passing syntax. Without being familiar with the code of the redirect_to method, we can imagine a general logic of:
redirect to what? An action.
Which one? “show”
Do you want to pass anything along to that? yes please, the :id, so we know which post to show.
And a value for that please? the :id of the post we’re currently looking at, that was passed through to us in params from the thing that called this method.

In other words, “call show for the current post”.

(POSTSCRIPT – just an aside, but the url in a rails app is kind of a translation of the redirect_to action: after the main domain name (or localhost:3000 or whatever), we have the name of the controller we want, then the name of the action, then the id to pass to the action. so

localhost:3000/blog/show/4

is equivalent to

redirect_to :controller => “blog”, :action => “show”, :id => 4

(if we don’t specify a controller, btw, it defaults to the current controller)

That means that your choice of controller for any action will affect the url that the user sees – so if you want to have a nice bunch of intuitive urls, you might need to think about reorganising your actions between your controllers, or making a new controller for some actions, or maybe just renaming your controllers or your actions. At the very least, think about your controller and action names before you name them – they’ll show up in your url, so don’t call them foo and bar! Having said all that, there might be a way to change the url that is displayed to be something other than the controller/action names. Please comment if you know any more about this!)

Here’s the complete comment action for blog_controller.rb

def comment
Post.find(params[:id]).comments.create(params[:comment])
flash[:notice] = ‘Added your comment.”
redirect_to :action => “show”, :id => params[:id]
end

Once you’ve added those (and saved), refresh the browser and add some comments. If you get an error message about it missing an ID, go back to the list screen, select the post again, then add the comments. It should work now.

That’s our blog app! Nice work.

In the video, DHH goes on to talk about unit testing and the rails ‘console’, but if you want more practise building stuff in rails, try finishing the onlamp tutorial i referred to earlier: Rolling with Ruby on Rails Revisited. Bill Walton and Curt Hibbs’ app is a little more complicated, but the ruby code should make more sense after working through the one we just did. If you want an introduction to unit testing in Rails (and you should, since there’s no compiler safety net in Ruby, and unit testing is definitely becoming the ‘way to code’) then watch the rest of the movie, which briefly introduces rails testing (as you might be expecting by now, rails sets up a load of tests for you automatically). If you want to really learn about tests though, look at some of the links at the bottom of this post.

OK, that’s it for this tutorial, i hope it’s been as educational reading this as it was for me to write it. Please mail me with any errors or suggestions. Thanks for reading!

Links (in no particular order)

13 Responses to “David Heinemeier Hansson’s “weblog” Rails tutorial, step by step”

  1. Fei Says:

    Perhaps I managed to miss it, but I think you forgot to mention that you need to create a Post controller after making the Post model in order for the auto-link to the database to work… other than that, nice to have DHH’s video in writing, because his video was way too fast for me :) 15-min setup is actually half-a-day setup for a nub like me!

  2. nubyonrails Says:

    Hi Fei! You made the first non-bot-generated comment on my blog, thanks very much for the feedback! You’re right, i think only DHH could do this in 15 minutes :)

    Your comment made me check through the tutorial again (which was cool as i added a couple of postscripts about migrations and gems) but as it happens i believe you’re mistaken about the controller – there is no Post controller, everything is done in BlogController in this tutorial(it would probably make more *sense* to have a post controller do most of the post displaying stuff though).

    The autolink to the database, as far as i know, depends only on the models, not the controllers – eg that a post “has_many :comments” etc, though of course we can also access the db in controller methods. Let me know if i’m wrong though, i’ve learned a lot in the last month but still consider myself a nub as well.

    Thanks again for the comment.

  3. Tommy Brunn Says:

    First of all, I believe that you may have made a typo when you are generating the blogcontroller. Shouldn’t it be “rails script/generate controller blog” instead of “ruby script/generate controller blog”?
    Anyway, for some reason I can’t generate the blogcontroller. Creating the framework for blog (“rails blog”) works just fine, but the controller just won’t work. When I write “rails script/generate controller blog” in the terminal I can see the output, and how files and folders are created, but when I check my project, the only thing I see is application.rb (in my apps folder), when I should have blog.rb or something like that. When I try your method of writing “ruby script/generate controller blog” I just get “nevon@S3RV3R:~$ ruby script/generate controller blog
    ruby: Is a directory – script/generate (Errno::EISDIR)”

    Anyway, I hope you know what I’m doing wrong.
    It would be nice if you could also email me your reply, in case I forget to check this post again.

  4. Tommy Brunn Says:

    Nevermind, I’m a fucking moron, that’s all. I forgot to cd to my project xD Thanks anyway

  5. nubyonrails Says:

    haha no worries, i was going to say that it’s definitely ruby script/generate rather than rails script/generate, for me at least. I just tried the rails script/generate in an old test project and it didn’t make any new files. I’m using windows, in case that’s relevant.

    Thanks for reading :)

    max

  6. peter Says:

    Found what Fei was missing. You had forgotten to add the ‘scaffold :post’ to blog_controller.rb

    I was stuck there and decided to watch the screencast to see the source.

    Peter

  7. nubyonrails Says:

    You’re absolutely right! Thanks very much for pointing that out – i guess i got confused because he properly generates the scaffold a couple of minutes later, which automatically deletes the scaffold :post line from the controller at the same time that it puts in all the methods. But yeah, in between those two sections it would be broken.

    I’ve updated the tutorial with an explanation about scaffolding. Thanks again.

  8. hemant Says:

    hi..it was a nice tutorial for a newbe.
    do u have such tutorials for rspec in rails.

    cheers
    hemant sonker

  9. nubyonrails Says:

    I know next to nothing about rspec i’m afraid.

  10. superbnerb Says:

    Love the tute, dude. I’m wondering if you could spell out your database portions to include setting up your db files in rails, now that you know you don’t have to use heidi or phpadmin. I’m ‘stuck’ on this whole foreigntable_id thing… If you wrote out what your 001_posts.rb file looked like, then we could ‘see’ what you mean.

    Others obviously didn’t have a problem with it, and i’m glad you put up the tutorial, but it’s just could me and other rails noobs that may need it.
    thanks.

  11. nubyonrails Says:

    Thanks superbnerb, i’ve done as you suggested, using migrations to set up the tables, and also a brief explanation about foreign keys in the comments table.

    I also added a note about the latest version of rails, 1.2.5, which has some pretty radical changes which will mean that this (and most others) tutorial won’t work. Use rails 1.2.3 or earlier to follow this tutorial!


  12. […] David Heinemeier Hansson’s “weblog” Rails tutorial, step by step « nuby on rails A text version of the original DHH “create a blog in 30 mins” Rails demo. (tags: dhh ruby rubyonrails rails tutorial tutorials) […]

  13. Alexwebmaster Says:

    Hello webmaster
    I would like to share with you a link to your site
    write me here preonrelt@mail.ru


Leave a reply to nubyonrails Cancel reply