5 minute project in Rails

August 8, 2009 at 8:43 pm 22 comments

Ok, you’ve installed the Ruby on Rails and you want to run a quick tutorial to see what can be done. Let’s do a simplified Journal application that allows to create users & posts.

1. Create a new rails project by running a rails name_of_the_project command, like this:

rails journal

You will see a bunch of “Created xxx” messages, that will create or copy over a bunch of directories and files.

2. Next, let’s create a Scaffold for our users. Scaffold is a basic structure of Model, View and Controller that allows you to quickly create, edit, show and delete objects. Let’s assume that each user will have a name, a biography and email for now. We will use a generate script in order to automatically prepare all required files for us. Here’s the command:

ruby script/generate scaffold user name:string bio:text email:string

3. So, in the step before we have created a User model, user controller and user views for each of the basic functions of create, edit, show and delete.

The model now consists of three attributes which will be recorded in the database columns, named name, bio and email. In your Rails code you will access those attributes by something like @user.name, @user.bio or @user.email.

However, the generate script does not create the model in the database, it just creates the migration file in the db/migrate folder of your project. You can check out the directory to see that a file create_users.rb (with a timestamp in front) is actually there. If you will need to extend the model later, it can easily be done by running another migration.

Ok, let’s run the migration by typing the command:

rake db:migrate

4. Now, run the server to check that everything works as expected:

ruby script/server

And try out the following address: http://127.0.0.1:3000/users

It should show you an interface to create / edit / delete users

5. Let’s then create a model for our posts. Here’s the same command we used for creating users:

ruby script/generate scaffold post title:string post:text

And, as before, you need to modify the database by running a migration:

rake db:migrate

6. Now, we want to be able to merge these two models together. What we want to say is that a User can write (have) many posts and each post has to be written by a (belong to) user. This is very simply done in the model definitions:

In app/models/user.rb add has_many :posts association

class User < ActiveRecord::Base
 has_many :posts
end
&#91;/sourcecode&#93;

In app/models/post.rb add belongs_to line:
&#91;sourcecode language='ruby'&#93;
class Post < ActiveRecord::Base
 belongs_to :user
end
&#91;/sourcecode&#93;

Now Rails knows that Posts and Users are related and you should be able to get a list of posts of a particular user by simply typing @user.posts and vice versa - an author of the post will be @post.user. This is really cool!

Before it all works, however, you need to provide Rails with a place in the database to store the relationship. It's done by including the id of user to the Post model (you don't need to store / modify anything in the User model). The notation Rails follows is &#91;related_model_name&#93;_id, in our case user_id. And then Rails will be able to do all the wonderful relationships. 

You can use the already used generate script, but this time a command migration will be used:
&#91;sourcecode language='ruby'&#93;
ruby script/generate migration add_user_id_to_post user_id:integer
&#91;/sourcecode&#93;
And run the migration again (you should start to get used to that command by now):
&#91;sourcecode language='ruby'&#93;
rake db:migrate
&#91;/sourcecode&#93;

7. Now it's time to modify our default views to take advantage of the new relationship. First, let's show the author's name of the post. Go to app/views/posts/show.html.erb and add lines 6-9 to it.

&#91;sourcecode language='ruby'&#93;
<p>
  <b>Title:</b>
  <%=h @post.title %>
</p>

<p>
<b>User:</b>
<%= link_to @post.user.name, @post.user %>
</p>

<p>
  <b>Post:</b>
  <%=h @post.post %>
</p>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

Let’s analyze what this command does – link_to @post.user.name, @post.user

First, I already mentioned that you can get the author by accessing @post.user. @post.user is actually an instance of user and you can access any attributes of user, so in order to get the name of the user you write @post.user.name. link_to is a clever method that allows you to create a link to a controller or object. The first parameter is the title of the link, the second is the url. As link_to is a smart method, you can simply pass an instance of a class to it and it will figure out how to link to it.

8. Now, let’s show all posts of a user when you show a user. For that we need to modify the user’s show view (app/views/users/show.html.erb), adding lines 16-23:

<p>
  <b>Name:</b>
  <%=h @user.name %>
</p>

<p>
  <b>Bio:</b>
  <%=h @user.bio %>
</p>

<p>
  <b>Email:</b>
  <%=h @user.email %>
</p>

<p>
  <b>Posts:</b>
  
  <% @user.posts.each do |post| %>
    Post: <%= link_to post.title, post %><br/>
  <% end %>
  <%= link_to "Create new post", new_post_path %> 
</p>

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

Ok, this one took 4 non-HTML lines, let’s go one by one:

@user.posts.each do |post| — we access the array of user’s posts and iterate through each of them by using iterator each. each is the easiest way to go through all the object in an array. The iteration passes control to block and puts each element one by one of array in local variable post.

link_to post.title, post — this is almost the same as we did for post’s user earlier.

end – this is to say to Rails that it’s the end of the iteration block

link_to “Create new post”, new_post_path — By this line we allow a new post to be created. First parameter is the string for a name of the link, second – is again a cool way to link to action of a controller [action]_[controller]_path

9. Finally, when we create a new Post we need to be able to select who is the author of the post by selecting the author from the list of available authors. In app/views/posts/new.html.erb before the lines <p> <%= f.submit ‘Create’ %> insert the new field:

  <p>
    <%= f.label :user_id %><br />
    <%= f.collection_select(:user_id, User.find(:all), :id, :name, {:prompt => true}) %> </p>
  </p>

The first line simply creates a text label of the field. The second creates a drop down list of all users in your system. The most important are the first 2 parameters – :user_id, which tells the form which variable to store the result of selection in and User.find(:all), which provides a list of all users to the drop-down.

10. Now save all changes and run the application at http://127.0.0.1:3000/users

This is an impressive application given that it took you only 5 minutes, 15 lines of code and couple of command-line commands. Of course, you will need to extend it with a proper user authentication and you will want to modify models and views to make the app useful, but it’s a great start.

Entry filed under: Uncategorized. Tags: .

Ruby on Rails screencast / course Yet more advice on learning Ruby & Rails

22 Comments Add your own

  • […] you should be dying to do something useful in Rails. As a very basic example you can follow this 5 minute Rails tutorial. Possibly related posts: (automatically generated)My Ruby on Rails dev environmentRails books in […]

    Reply
  • 2. Gaby  |  August 30, 2009 at 11:34 am

    Just wanted to say THANK YOU!! I found your tutorial of how to install Ruby on Rails for Windows fabulous (I didn’t need to do the fix of 4.1, by the way) and just now I’ve completed this 5-minute project. I can’t wait to dig my teeth into RoR some more.

    One question though. When I want to make an app live (not just on 127.0.0.1/whatever), what documents do I need to upload? And how is the whatever determined? I have a feeling it is linked to certain variables (and maybe the Scaffold), but I’m confused on the connection.

    Thanks again on a great resource. I’m glad you’ve started using it again. I know what it’s like, I need to finish a blog entry of my own, heh.

    Reply
    • 3. allaboutruby  |  August 30, 2009 at 3:15 pm

      Hi Gaby –

      1 – You will need to move almost all of your full project directory, not only the app subdirectory.

      2 – /whatever is by default the name of your controller. However, if you want to be able to just go to 127.0.0.1:3000 and automatically call the “whatver” controller, you need to go to config/routes.rb and define a root route like this –
      map.root :controller => “whatever”

      Reply
      • 4. Gaby  |  August 30, 2009 at 9:57 pm

        Almost all? What do I leave out?

        Thank you for your quick response!

      • 5. allaboutruby  |  September 2, 2009 at 10:14 pm

        Gaby – it depends on your situation, but most probably you won’t need the old db migrations (migration don’t migrate data) nor development log or docs in production. Everything else can go there.

  • 6. Sarah  |  October 21, 2009 at 12:18 am

    It took a lot more time for me but that was a lot of fun.

    Framework looks amazing and programming looks like playing a toy.

    I think when I’ll go deeper into this… when I’ll know more of ruby… then it will become harder.

    The more I know, The less I know.

    Is creating app like Twitter (let’s say like Twitter on the beggining of existence) in RoR a extremely hard task?

    Reply
  • 7. Simer  |  January 8, 2010 at 12:35 pm

    Great tutorial. its very simple and easy way to learn rails

    Reply
  • 8. Adam Noveskey  |  March 11, 2010 at 3:17 am

    Love the tutorials! Thanks once again!

    I am having a bit of trouble with this one though. I have gone through and checked my code, but I can’t seem to make it work. I can add new users, but when it comes to submitting a new post I get this (c&p directly from my browser window):

    undefined method `posts’ for nil:NilClass

    Extracted source (around line #19):

    16:
    17: Posts:
    18:
    19:
    20: Post:
    21:
    22:

    I have tried starting and stopping the server; db:migrate, etc. Am I missing something?

    Reply
    • 9. halkazzar  |  May 11, 2010 at 12:16 pm

      Hi Adam,

      do you see this at http://127.0.0.1:3000/posts ?

      I had similar problem, but it was just a syntax error. Check for all syntax. I missed “equal” sign after opening the tag
      <% link_to

      should’ve been

      <%= link_to

      Reply
      • 10. Pritesh Desai  |  July 21, 2011 at 11:33 pm

        Nope. Syntax seems to be correct… still not working

  • 11. Ahmed  |  April 23, 2010 at 3:53 pm

    THANK YOU SOOOOOO MUCH!!! 🙂

    I’ve been trying to install RoR and get it work for almost 3 weeks, and i wasn’t able to do.

    Lots of resources online about that mater but yours is very helpful, easy and well done.

    Keep up the great work, and looking forward for more posts.

    Reply
  • 12. Marvin  |  May 25, 2010 at 7:14 am

    Ok, I don’t know what I’m doing wrong, but RadRails starts complaining immediately about the colon in my has_many statement in the User model as soon as I finished typing it. When I got done with everything and ran it, I got an “SyntaxError in UsersController#index” error about that line.

    What am I missing?

    Reply
    • 13. Marvin  |  May 25, 2010 at 7:32 am

      Nevermind, I got it. My colons were one space off. I had “has_many: posts” instead of “has_many :posts”. Back in the saddle. 🙂

      Reply
  • 14. Jim  |  November 4, 2010 at 12:32 am

    Great tutorial you have here! Would be nice if you can link to another good tutorial for quickly building the authentication on top (and determining which user is logged in, etc…)

    Oh, one typo you had in step #9: You actually meant the “_form.html.erb” file but you actually said the “app/views/posts/new.html.erb” file. 🙂

    Reply
  • […] can continue with a more detailed tutorial here, but hopefully this has provided proof of concept.  Note that this tutorial references older […]

    Reply
  • 16. Satkar  |  February 9, 2012 at 7:01 am

    hi ,
    I am trying to create the model and Controller using the command
    ruby script/generate scaffold Item Manage
    where Item is the model name and Manage is the controller name
    I have used rails 2.3.5 version with ruby 1.8.7

    I am getting the error as
    NOTE: Gem::SourceIndex#each is deprecated with no replacement. It will be removed on or after 2011-11-01.
    Gem::SourceIndex#each called from /home/satkar/.rvm/gems/ruby-1.8.7-p352/gems/rails-2.3.5/lib/rails/vendor_gem_source_index.rb:124.
    exists app/models/
    exists app/controllers/
    exists app/helpers/
    exists app/views/items
    exists app/views/layouts/
    exists test/functional/
    exists test/unit/
    exists test/unit/helpers/
    exists public/stylesheets/
    wrong number of arguments (1 for 2)

    Reply
  • 17. paulywill  |  December 6, 2012 at 6:36 pm

    How can this example be altered to allow the SQL database to capture both the user ID and the user name for each post?

    If you were to export the database it would have nothing but user ID numbers.

    Just thinking of applying to an application but struggling how a user selecting 1 user can fill out two columns, “user_id” and “user_name” for each post record.

    Reply
  • 18. withintheruins14  |  February 2, 2013 at 10:33 pm

    i’ve managed to get it working but i’d like to know how to understand the rest of this line

    true}) %>

    Reply
  • 20. chanakya devraj  |  February 28, 2013 at 5:09 pm

    ActiveModel::MassAssignmentSecurity::Error in PostsController#create i am geting this error .. what should i do now.. could you please guide me

    Reply
  • 21. Dan Voell  |  June 11, 2013 at 3:30 pm

    Nice tutorial, don’t forget to add :user_id to your attributes accessible in your posts_controller.

    Reply
    • 22. Dan Voell  |  June 11, 2013 at 3:32 pm

      Also, This code –

      true}) %>

      should go into _form vs. new.

      Reply

Leave a comment

Trackback this post  |  Subscribe to the comments via RSS Feed


Starting to learn Rails?

Kindle

Get Kindle - the best e-book reader, that I personally use, and the only one that you can read on the beach - very useful: Kindle Wireless Reading Device (6" Display, Global Wireless, Latest Generation)