Reddit is a social news site (or a social links site, or a social funny pictures site, depending on how you want to use it). People sign up, submit links and the other users either vote them up or down. Links that get voted up enough appear on the front page, where they're seen by millions of people. The development of Reddit is quite interesting, it's open source and you can check out the code (written in Python) on their github page. However, most of the challenges in developing and running Reddit come from making sure it doesn't collapse under the crush of its millions of visitors. You can create your own version in Ruby on Rails Rails quite easily.
At its core, Reddit has users, links, votes and comments. Users submit links and comments, and either can be voted on. So, right there, we've identified our models. We'll be looking at relationships like a user has_many links, and links belong_to users. There are a few more complex relationships like comments belong_to both users and links, and the votes are a many to many relationship. But none of that really matters right now, all we need to do at the moment is to get the very core of Reddit online: submitting links.
Links themselves are very simple. They belong to a user and have a title and URL. So, before we scaffold the links and get a (very) basic Reddit clone up and running, we need to create a user class. Since we'll be doing so much more with the user in later parts, we'll skip this step for the most part and generate a "dummy" user class with nothing but a user ID. We'll manually generate a user using the Rails console and automatically attribute all links to the first user in the database. And as for the links, we'll start off with a straight up scaffold.
Generating the Project and Getting Ready
To start off, we'll generate a new project. If you don't know how to generate a new project, see the previous tutorial on creating a blog in Rails, it covers this in more depth. But for now, just run the command rails new railsit (a portmanteau of Rails and Reddit). Once it does its thing, we'll set up git.
Git is source code control software. If you're totally unfamiliar with Git, see our series of Using Git with Ruby. While such pieces of software were once seen as only something necessary for working in a group, it's extremely useful for working by yourself as well, so we'll start using it here. Change to your new Rails project directory and run the command git init, then git add . and git commit -am 'Initial commit'. This will create the git repository in the .git directory, stage the files and then commit them all to the repository.
$ rails new railsit create create README.rdoc create Rakefile create config.ru … $ cd railsit $ git init Initialized empty Git repository in /home/user/tmp/.git/ $ git add . $ git commit -am 'Initial import'
Create the Dummy User Model
We'll want a model to get things up and running, so we'll generate a User model. To begin with, you don't usually generate models without any controllers or views, but it serves our purpose here. We'll then go into the console and create a user to use for now. But first, we'll start a new git branch.
I'm a big fan of the git branch-work-merge workflow. For every action, even small actions, create a git branch. Then do your work, and when you're finished, merge the branch back to the bit master branch. This separates things nicely and gives you an easy out if everything goes sideways. I often find myself doing things completely wrong and the easiest way I have to undo all the work I did is to go back to the master branch and delete my working branch. So let's create a new branch by checking out the create_new_user branch. If you then run git branch command, you'll see that we're not on the master branch anymore.
$ git checkout -b create_dummy_user Switched to a new branch 'create_dummy_user' $ git branch * create_dummy_user master
OK, we'll generate the new user model with a rather standard rails generate model User command. No fields for now, all it needs is a user ID, which is automatically generated by Rails. We'll then fire up rails console to create a new user. A new user can be created by running u = User.new, but don't forget to run u.save, or else the user won't be saved to the database.
$ rails generate model User invoke active_record create db/migrate/20121129183304_create_users.rb create app/models/user.rb invoke test_unit create test/unit/user_test.rb create test/fixtures/users.yml
Take a quick look at the migration file generated in db/migrate and the model in app/models/user.rb. They're mostly empty, pretty much the timestamps in the migration and nothing in the model. Good, this is what we wanted (for now).
$ cat db/migrate/20121129183304_create_users.rb class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.timestamps end end end $ cat app/models/user.rb class User < ActiveRecord::Base # attr_accessible :title, :body end
Since this is our first model, we need to create the database first, then run the migration. This is done with rake db:create and rake db:migrate.
$ rake db:create $ rake db:migrate == CreateUsers: migrating ==================================================== -- create_table(:users) -> 0.0006s == CreateUsers: migrated (0.0007s) ===========================================
Next, fire up the Rails console and create a new user. Make sure you save it to the database before exiting. If you've never used the Rails console before, it's just an IRB prompt with your Rails project already loaded. You can use it to create and inspect data using ActiveRecord, or run any Ruby statements having to do with your Rails project interactively. It's extremely useful.
$ rails console Loading development environment (Rails 3.2.8) irb(main):001:0> u = User.new => #
irb(main):002:0> u.save (0.1ms) begin transaction SQL (23.8ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Thu, 29 Nov 2012 18:37:37 UTC +00:00], ["updated_at", Thu, 29 Nov 2012 18:37:37 UTC +00:00]] (8.8ms) commit transaction => true irb(main):003:0>
OK, this little task is done, let's merge the branch to master and delete the working branch (or leave it, but once it's a success I don't like to have to hanging around). So first add all the new files we created with git add ., commit the changes with a git commit.
$ git add . $ git commit -am 'Created dummy user model'
Next we'll switch back to the master branch, merge the working branch and delete it.
$ git checkout master Switched to branch 'master' $ git merge create_dummy_user Updating 49f86b6..08eb01a Fast-forward Gemfile.lock | 112 +++++++++++++++++++++++++++++ app/models/user.rb | 3 + ... $ git branch -D create_dummy_user Deleted branch create_dummy_user (was 08eb01a). $ git branch * master
Great, we're ready for the next part. In the next part, we'll scaffold the Post model, controller and views, and start looking at ActiveRecord relations in the Rails console. After that, we'll jump into users using Devise, a popular authentication and user management gem. That alone gets us most of the way to a full Reddit clone in Ruby on Rails, once you get down to it, most things are easy in Rails, at least on the surface.