Up to this point in the Rails Deep Dive series, we’ve focused on digging down into the entrails of the framework, attempting to uncover some of the ways that Rails accomplishes its magic. Going forward, I want to create a Rails 3.1 application, focusing on how I would setup up the application, perform the development, and deploy the application. I think the series will benefit from having a specific goal in mind.
Our application will be called Loccasions. The purpose of Loccasions is to allow users to create Events and Occasions. An Event might be “I cleaned my room” or “It rained” or “A comet sighting”. Events contain Occasions, marking a time and place where the Event occurred. The application will present the occasions on a map, allowing the user to see how often and where an Event occurs. The idea is simple and the use case specific, so creating the app should be a snap (he says, knowing he will hit roadblocks….)
When creating a new Rails application (or any application, really) it’s a good idea to have some user stories to direct the application and ensure we are staying on task. Normally, you would meet with the client and generate the high level user stories together. The key with user stories is to capture just enough detail to start working, avoiding the “analysis paralysis” that can cripple progress. For Loccasions, we will keep the user stories pretty high level, adding more as we go. Our first stories are:
- As an unregistered user, I want to see the home/landing page
- As an administrator, I want to be able to invite users to Loccasions
- As an invited user, I want to be able to create an account
- As a registered user, I want to be able to create Events
- As a registered user, I want to be able to create Occasions
- As a registered user, I want to see Occasions on a map
I think that is a good start.
The next decision concerns the gems we are going to leverage to take care of some of our functional needs. Obviously, Loccasions will need some kind of authentication, and the community has great gems in this area. Probably the most well known authentication gem is Devise written by Jose Valim and the incredible folks at Plataformatec. I think using Devise gives us a well-tested gem and a fantastic community for support.
One of the decisions I have made for Loccasions is how persistence will be handled. Rather than go the standard relational database route, like PostGIS or MySQL, I have chosen MongoDB for our back-end persistence store. First, I think the Event ==> Occasions model makes a good document db model. Second, I am relatively certain that Loccasions will use the spatial functionality that MongoDB provides. Also, if I am being honest, I really want to use MongoDB in a “realish” Rails app and this is opportunity knocking.
The use of MongoDB leads to another area where gems can help. In this case, I looked at MongoMapper and Mongoid and settled on Mongoid because it seems to have slightly better support for the spatial parts of MongoDB, as well as the existence of mongoid_spacial
It’s worth noting that this conclusion is based on a few minutes of looking at both sets of docs, so it could be wrong. However, this is how decisions are made, sometimes, when starting an application. Pick a direction and go. Also, it is highly likely that we’ll run into version issues between gem dependencies. If/When this happens, we may have to either sacrfice a gem or fork it and fix the issue ourselves.
Client Side Stuff
Also, I have become a fan of Haml so I think we’ll use Haml instead of ERB for our view templates.
We will, as much as possible, employ a test-driven approach to creating Loccasions. In essence, this means we will write tests first to drive the design and implementation of the app. With that in mind, we need to select a testing approach, and I have decided on RSpec and Capybara. I like RSpec and think something like Cucumber is a bit more than I need for this series. Also, there is a gem that integraties Mongoid and Rspec (mongoid-rspec) that will simplify our testing.
The test-driven approach extends to the client-side of the application, as well, and using something like Jasmine keeps the specification approach consistent.
I will be creating a Github repository for the Loccasions source. Before you start any development process, you should have a plan for source control. Git makes it criminally easy to get going with SCM, so there is no excuse.
One of the best tools in your Rails toolbelt is the Internet and standing on the shoulders of those that came before. For example, my inspiration for the Devise and Mongoid setup is one of Daniel Kehoe’s fantastic tutorials. I am sure we will be scouring the web for help and resources, and I hope to highlight what we find.
The Starting Line
Alright, I think that is enough planning. Time to stop dipping our toes in the water and jump in up to our necks. Of course, we need MongoDB running locally. Go install MongoDB on your platform….I’ll wait.
We are getting closer. I am using Rails 3.1 RC5 and Ruby 1.9.2. Also, I am using RVM and I strongly recommend you set up RVM and a gemset before continuing.
rvm use 1.9.2@loccasions -- create # will create and switch to loccasions gemset and Ruby 1.9.2
We have a clean gemset, so we need to install a couple of gems before we can get to Rails.
gem install bundler gem install rails --pre # We want 3.1
Remember, we are using MongoDB, so we don’t need any ActiveRecord pieces (-O) (we won’t be using migrations) Also, we are using RSPec, so no need to generate the Test::Unit files (-T).
rails new loccasions -O -T cd loccasions
Now that we finally have an application structure, we need to pull in the aforementioned gems. Open “Gemfile” in your favorite editor (I use vim, b/c it is fantasmic) and make it look like:
source 'http://rubygems.org' gem 'rails', '3.1.0.rc5' gem 'devise', "~> 1.4.2" gem 'mongoid', "~> 2.1.8" gem 'mongoid_spacial', "~> 0.2.13" gem 'haml', '~> 3.1.2' gem 'bson_ext', '~> 1.3.1' gem 'rails-backbone', "~> 0.5.3" # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', "~> 3.1.0.rc" gem 'coffee-rails', "~> 3.1.0.rc" gem 'uglifier' end gem 'jquery-rails' group :test, :development do gem 'rspec-rails', '~> 2.6.1' gem 'mongoid-rspec', '~> 1.4.4' gem 'capybara', '~> 1.0.1' gem 'factory_girl_rails', '~> 1.1.0' gem 'database_cleaner', '~> 0.6.7' gem 'jasmine', '~> 126.96.36.199' end
bundle install and we are ready to attack our first user story. Before we do that, let’s do the initial commit to our git repository and push it up to github. First, edit the .gitignore and make sure it makes sense:
.bundle db/*.sqlite3 log/*.log tmp/ .sass-cache/ *.swp .DS_Store
I added the
.DS_Store lines so that my vim buffers and Mac artifacts don’t get added to the repository.
git add . git commit -m "Initial commit"
Now, add your github remote repository as ‘origin’.
git remote add origin https://firstname.lastname@example.org/ruprict/loccasions.git git push origin master
The minute I did that, I realized I had forgotten to create a .rvmrc file, so let’s do that and push it up as well.
rvm --rvmrc --create ruby-1.9.2-p290@loccasions
cd .. and then
cd loccasions to make the .rvmrc file trusted. It will prompt you to review the file, then type ‘yes’. Finally, add the .rvmrc to git and push it to github.
git add .rvmrc git commit -m "Added .rvmrc" git push origin master
In the next post of the series, we’ll start with the “unregistered user” story, which should lead us to make decisions about how we’ll layout the app. Let me know, in the comments, if you have issues or questions about the setup.