12.28.05
rubyGreg: Learning Rails
The time has finally come for me to jump on the Rails-wagon and learn Ruby on Rails. I will document here my efforts to port the fabulous Gregarius feed aggregator to Ruby. I admit this is not a tutorial for the complete novices, and definitely of no use to a seasoned Rails developer, unless they enjoy a good laugh. In particular, I am assuming that you have glanced at the standard tutorials here and here, as well as Amy Hoy’s wonderful list of articles on her site. I am sure there are other worthwhile tutorials out there, and you can probably find them documented at the wiki.
So to recap, I am assuming that you have Ruby and Rails installed and you have two terminal windows open, as well as your favorite editor ready to work on any file in the directory we are about to create, and of course your favorite browser open, so that we can view our results. Also keep in mind that whatever is presented here is my view on things, and it might very well be incomplete/inaccurate. Please feel free to correct me. This might also be an opportune moment to have a look at the Gregarius source code, to have some idea what we are trying to replicate. Of course for now we’ll only do some small bits of the overall monster.
Ready? Alright then, on with the show!
First, let’s create the basic structure for our website, the first thing that Rails does for us for free. So in one of our terminal windows we’ll move to the location we want our new website created. In my case, that would be ~/Desktop/RoR/Rails, but of course your milage may vary. Now, to create the website. This just means running the command rails rubyGreg. As you might have guessed, rubyGreg is the name for our port. If you have followed the aforementioned tutorials, you know that what has just happened is that a huge collection of files has just been created for you for free, all living in a subdirectory called rubyGreg. Let’s cd to that directory, and see what subdirectories are there.
app. That sounds important. We’ll be spending a lot of time here.components. Hm, I have no idea what this is for, so I’ll ignore it for the time being.config. We’ll probably need to customize something here, at the very least tell Rails what database we are using.db. Hm, no idea here either, sounds like it should have to do something with the database. We’ll see if we need that.doc. Ok, that’s obviously here so that we can document our exploits. Seems to have a README inside of it.lib. Sounds reasonable. We’ll see…log. Good, logging is always good to have. Let’s see what Rails logs by itself.public. I learned from Amy what that is for: javascript, css, images, standard error pages etc.script. That you hopefully already know about. This contains some scripts to automate lots of things. We’ll use it a lot in a moment.test. This is to prepare automated tests for our app. I’ve never written tests before, time to start. It already seems to have some stuff in there.vendor. I think I’ll ignore that for now.
Ok, that’s a lot of stuff, but we’ll ignore most of it. The place to start is a database to play with. I’ll use a test gregarius database that I have around just for this reason. You can get it here. I assume here that you know how to set up a database and give the right permissions to a user. Keep in mind that if you are using mysql, you will need to have an account with a password of the new type, and not the old type. Once you have the database going, you need to edit the file config/database.yml accordingly. Good, we are done!
Ok I lied, we’re not done yet, now the fun part begins. Time to talk about the app directory, and the MVC design pattern. I will actually assume you don’t know what MVC is, and will attempt to explain. MVC is a standard design pattern for the general structure of an application. The initials stand for Model-View-Controller.
- The Model is a class that is responsible for the data of our application. This is in charge of getting the data from the database and saving/deleting/creating it. So it is the first level of abstraction on top of the database, so that our program doesn’t have to talk to the database directly.
- The View is a class that is responsible for the presentation of the data to the user. Think of it as the usual mixed HTML/PHP files you often deal with to send the data back to the client.
- The Controller is the class that coordinates the other two classes. This is the heart of the application.
In a typical run of the program, the controller will first figure out what the client has asked for. Then, it will ask the model for the appropriate data. Then it will process this data, if it needs processing, and then it will send the data to the view for display. This is a very important point. When we build our app we need to keep this in mind. We’ll see an example of how this works in a minute. Now, on to create our controllers, models, and views.
Before we do that, we need to do one more thing, and that is to check that things have been set up correctly. Luckily for us, one of the things that the rails command we executed above created for us was a mini server to use for testing things. And this is where our second terminal window will be used (I’m sure you were dying to find out what that was for, eh?). So go ahead and switch to that window, and move to the directory rubyGreg that we just created. Now run script/server. You should see some nice messages about WEBrick running, along with a web-address, http://0.0.0.0:3000 in my case. If you point your browser to this address, you will actually see our app running! Yes, I know it doesn’t feel much like our app yet, what did you expect without writing a single line of code? But it is running!
Alright, now finally we’ll create our controllers, views etc. Let’s move to the other terminal window, which is also at the rubyGreg directory. Our aggregator basically deals with two things, items and channels. So we’ll create two controllers and models to go with that. Run script/generate controller Item. You will notice this created the following files:
app/views/item. The views subdirectory is the one where all the views will reside. It will be filled with.rhtlmfiles. It will also contain one subdirectory for each controller. Here theitemsubdirectory is associated to theItemcontroller we created above.app/controllers/item_controller.rb. The controllers subdirectory will of course contain the controllers. These will be ruby code files, that we will be working a lot with.test/functional/item_controller_test.rb. This is a file to add the tests for the controller. I’ll worry about those a bit later.app/helpers/item_helper.rb. As I learned from Amy, the helpers are little methods to do stuff for you, and this file is to add our own helpers. Something to worry about later too.
Ok, so not too much happened for now. We just got a controller and a view for our items. Now we need a model. Go to the terminal window again, and run script/generate model Item. This created the following files:
app/models/item.rb. This is the file that does the modeling work for us. It is magic in many ways, as we’ll see.test/unit/item_test.rb. For the unit tests for the model, obviously.test/fixtures/items.yml. Hm, this is again part of the testing package. I’ll investigate all that at a later post.
Ok, now we need to do the same for the channels, so let’s run the commands script/generate controller Channel and script/generate model Channel. And now, we are ready to do some magic. Let’s start by a simple task: Create a page that contains a list of all the channels, probably appropriately called index. To do that, the first thing to do is add an index method to the channels controller. This will be the file app/controllers/channel_controller.rb, but from now on I will not mention full paths like that. In this method, all the controller has to do is ask the channel model for all channels, and store those in some controller variable so that the view can get hold of them. Remember, the key idea is that the view and the model are only allowed to communicate with the controller, and not each other. So the code to be added between the class ... line and the final end line would be just:
def index
@channels = Channel.find_all
end
So let’s explain this for a second. The def index line defines a new function with name index. The code for the function is everything enclosed between that line and the corresponding end. The line in the middle tells the model Channel to execute the method find_all, which just returns an array of all channels. Then this is stored in the instance/controller variable @channels. All instance variables need to start with the symbol @.
Now we need to arrange for the view of this table of data. This will be done by creating a file titled index.rhtml, and placing it in the views/channel directory. We’ll need to change this file later, namely in two paragraphs, but for now let’s just add the following there:
<html>
<body>
<h1>All Channels</h1>
<ul>
<% @channels.each do |channel| %>
<li><%= channel.title %></li>
<% end %>
</ul>
</body>
</html>
To see what this would produce, point your browser at http://0.0.0.0:3000/channel/, or perhaps different numbers depending on what WEBrick told you it would understand. If all has gone well, you should see something like this:

Let’s take a closer look at the above code though. You will notice the typical idea of mixing html markup with code, except with a tiny twist: There are two ways to insert code: <% Code here %> or <%= Code here %>. As I understand it, the former just executes that code, without inserting anything in, while the latter inserts at that point in the html the value returned by the last operation in the code. (Ok, maybe there is a similar trick in PHP, if so, I admit ignorance). The important lines above are of course the three lines between <ul> and </ul>. The first one looks at the @channels array, and for each element of the array executes the block starting with do and ending with end. Each time through the block, the element is captured by the local channel variable, which is what the |channel| in the first row indicates. For each of those channels, we ask the channel to return us its title and this is then placed in the html output enclosed in li tags.
Well, that’s probably not very exciting, is it? We would probably like to have it so that each channel title is actually a link, and clicking on the link loads up a list of articles. For this we will use the Rails command link_to, which creates such a link. You can read more about it in the built in documentation, which in my installation is at file:///usr/lib/ruby/gems/1.8/doc/actionpack-1.11.1/rdoc/index.html. The thing to decide is whether the responsibility for showing a list of items from a particular channel should be the responsibility of the channels controller or the items controller. For now I will put it in the channels controller. All one has to do is change the line:
<li><%= channel.title %></li>
to:
<li><%= link_to channel.title, {:action => "list_items", :id => "#{channel.id}"} %></li>
So what happened here? link_to is a method that will create a link. The rest of the stuff in this line is arguments to that method. One could also have written it more traditionally as:
<li><%= link_to(channel.title, {:action => "list_items", :id => "#{channel.id}"}) %></li>
Fortunately, Ruby is very lax about this short of thing, it has a very minimalist syntax. That’s also the reason we don’t need to put semicolons anywhere, though we could if we wanted to. The first argument is of course the text of the link. The thing in the braces is a hash table, which associates to :action and :id the values "list_items" and "#{channel.id}" respectively.
What is :action you ask? The colon in front indicates that this is to stand for the symbol for the method action. This tells Rails what method to call from our controller, in this case the method called action. :id is just some extra information that the controller will be able to use in order to find out what channel’s items to display. The "#{channel.id}" part tells Ruby to execute the ruby code channel.id, and then store the result in the string. Just to give you another example, "#{3+4}" will result in "7", while "#{a=2;a+2}" will return "4".
How does this turn into a URL for the link? I have no clue, but that’s what link_to takes care of for us. Now try it out. Reload the page on your browser and try to click one of the channels. You will probably see:

Why did this happen? Well, simple enough, as the error message says: We have not implemented a list_items action yet. Before we do that, we need to change our database a little bit, namely the names of the tables. Rails uses a set of conventions for table and column names that helps it find things without us explicitly telling it about them. Now, if you look at the SQL schema for our database, you’ll notice that the table for the items is called item. Rails uses the convention that the model name should be singular and capitalized, while the corresponding table name should be plural and not capitalized, i.e. Item -> items, Channel -> channels and so on. There are two ways to fix that. The one is to change the table name. The other is to go to the file app/models/item.rb and add the line set_table_name "item". I will opt for the first option.
The second change that needs to take place is the column named cid in the items table. This is a foreign key corresponding to the channel id, and so the convention that Rails imposes on this is that it should be named channel_id. Once again there are two ways to fix it, the simpler one being renaming it to channel_id. I will mention the second one right now, along with the changes in the model that need to be made. Basically, we need to tell our items that they belong to channels, and our channels that they have items. So in the Item model file, add the line:
belongs_to :channel
or if you did not change the column name for cid, add instead the line:
belongs_to :channel, :foreign_key => "cid"
Also, in the Channel model file, add the line:
has_many :items
Now we’ve told our models all they need to know about each other. Let’s implement list_items now. In the channels controller, add the following method:
def list_items
@channel = Channel.find(@params['id'])
@items = Item.find_all_by_channel_id(@params['id'])
end
Also in the app/views/channel directory, create a new file called list_items.rhtml, with code:
<html>
<body>
<h1><%= @channel.title %></h1>
<ul>
<% @items.each do |item| %>
<li><%= link_to(item.title, {:action => "details", :id => "#{item.id}", :controller => "item"}) %></li>
<% end %>
</ul>
<%= link_to("See all items from this feed.", {:action => "list_details", :controller => "item", :channel_id => "#{@channel.id}"}) %>
</body>
</html>
This is not exactly how these appear in Gregarius but for now I’m just trying to see how some things work. We’ve seen most of this code before. The only thing that’s new is the , :controller => "item" bit. That’s there because currently we are in the channels controller, but we would like this action to be executed from the items controller. We just created two new actions for the items controller, so we should implement them, as well as the corresponding views. So add the following code in the items controller:
def details
@item = Item.find(@params['id'])
end
def list_details
@channel = Channel.find(@params['channel_id'])
@items = Item.find_all_by_channel_id(@params['channel_id'])
end
Also create files app/views/item/details.rhtml with code:
<html>
<body>
<h1>Item details</h1>
<div><h2><%= @item.title %></h2>
<p>Posted by: <%= @item.author %> on <%= if @item.pubdate.nil? then @item.added else @item.pubdate end %></p>
<p><%= @item.description %></p>
</div>
</body>
</html>
and app/views/item/details.rhtml with code:
<html>
<body>
<h1><%= @channel.title %></h1>
<% for item in @items do %>
<div><h2><%= item.title %></h2>
<p>Posted by: <%= item.author %> on <%= if item.pubdate.nil? then item.added else item.pubdate end %></p>
<p><%= item.description %></p>
</div>
<% end %>
</body>
</html>
Ok, this will be enough for now. Next time I’ll deal with using layouts to get rid of a lot of the common glue code in all of the forms above, and try to get the options menu in, and finally make it start looking more like gregarius.
Later
Marco said,
December 29, 2005 at 2:00 am
Tu quoque, Brute, fili mi!
Seriously though, it’s quite interesting to see how the same application logic is implemented in a totally different paradigm. I wish you best of luck if you intend to go the whole way!
Sameer said,
December 29, 2005 at 2:25 pm
oh nice!! can we see a demo? I think dreamhost has ruby on rails.
Oh btw, the links in this post need to be fixed….
Haris said,
December 29, 2005 at 10:20 pm
Thanks Sameer, links should be fixed now.
As per Sameer’s request, here it is running:
http://rubygreg.skiadas.dcostanet.net/channel/index
Doesn’t look pretty yet, and can do very little at the moment, but what did you expect with 34 lines of code?
Rich said,
January 6, 2006 at 6:52 am
Haris, the wiki link in the first paragraph does not resolve. The following is displayed:
Not Found
The requested URL /afterthought/2005/12/28/rubygreg-learning-rails/wiki.rubyonrails.com/rails was not found on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
Haris said,
January 6, 2006 at 7:42 am
Thanks Rich, good catch. It seems Markdown and I don’t communicate very well when it comes to links. It should be fixed now.