A voting extension from scratch for Rails 3 (Part 1 – Overview and setup)

This blog series is about creating a voting extension for Ruby On Rails. There are already some others voting extensions available, but none is currently up-to-date 1 or ActiveRecord compatible 2. So let’s build one from scratch. It is also (or maybe even more) a blueprint of current best practices on how to build those “acts_as_something” like Rails 3 extensions.

The full and working source code inclusive documentation can be fetched from the GitHub make_voteable repository or from RubyGems (gem install make_voteable).

Here are some features that come into my mind what such a voting extension should fulfill:

  • Allow up and down votes
  • Be voteable agnostic (everything can be voted on)
  • Be voter agnostic (everything can vote)
  • Only one vote per voter on one voteable
  • Full access to voting info (when / what / who)
  • Good performance (optimized model structure and queries)
  • Support for Rails 3 and ActiveRecord … not /dev/null
  • Easy to setup (migration generators)
  • Fully tested (RSpec)
  • Easy distribution (Github and RubyGems)
  • Free and open license (MIT License)

For the gem name I chose make_voteable as the acts_as is already occupied by the older above mentioned gems. I also think it is a nice “namespace” that is not yet so worn out like the “acts_as” one. I also didn’t like the extension to be on steroids.

We will setup our gem the new “Rails 3 way” and use Bundler to help get us started. From thereon we will edit our **.gemspec* file ourself (we don’t need a tool for tool). You can learn more about the gem creation process using bundler on the official Bundler Rubygems site or a recent Railscasts screencast.

Enough talk, make sure you have Bundler installed (gem install bundler) and type the following command into your console.

bundle gem make_voteable

This will create the base structure for our new gem in a make_voteable directory.

make_voteable
|-- Gemfile
|-- Rakefile
|-- .gitignore
|-- make_voteable.gemspec
|-- lib
    |-- make_voteable.rb
    |-- make_voteable
        |-- version.rb

Let’s edit the make_voteable.gemspec file now. This file contains all the meta data of the extension (like the extension name, authors, summary and so on). It also contains the dependencies for development and in normal usage. When you have already used Bundler in a Rails application then you are maybe familiar with the Gemfile. This file contains the gem dependencies when using Rails. By using Bundler to create our gem base structure, a Gemfile was also created. In contrast to a Rails application, the Gemfile when developing a gem does only contain some kind of link to get the dependencies from the gemspec file. So we just need to put our gem dependencies in the gemspec file. We also use twiddle wakka (~>) to specify the dependent versions. That way we benefit from new patches and minor versions, but don’t run into the danger that new major version of a gem dependency would break our gem.

The only non development dependency we have is ActiveRecord. We also add RSpec 2 for testing and Database Cleaner to clean our database before every test run (unfortunately we can’t use the transactional fixtures feature for that when developing a gem). We also use SQLite as our test database, as it is simple to install, by default requires no setup and is embeddable (does not need a separate started server instance). We also add Bunder as development dependency.

# make_voteable.gemspec
...
s.add_dependency "activerecord", "~> 3.0.0"
s.add_development_dependency "bundler", "~> 1.0.0"
s.add_development_dependency "rspec", "~> 2.0.0"
s.add_development_dependency "database_cleaner", "0.6.7.RC"
s.add_development_dependency "sqlite3-ruby", "~> 1.3.0"
...

The full content (with all other meta information) of the gemspec can be viewed here. Note that you don’t have to explicitly specify the files (s.files) of your gem. This is handled for you by fetching them from Git (make sure you have Git installed). Also the version (s.version) is not directly specified in the gemspec. Instead if fetches the current version from the version file make_voteable/lib/make_voteable/version.rb (which was also created by Bundler).

Save your edited gemspec and install all those provided dependencies by using Bundler.

bundle install

This is the end of the first part. In the next one we will setup our testing environment (you know … test first).