Rails Deep Dive: Application Setup, Loccasions

Glenn Goodrich
Ruby Editor
This entry is part 5 of 15 in the series Loccasions

Loccasions

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….)

User Stories

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.

Gems

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

I am relatively sure that we’ll use a decent amount of javascript in Loccasions. My initial vision sees each Event page as a Single Page Application, allowing the user to create Occasions and add them to the map. The map and list of Occasions will stay in sync, which means we have two “views with our view”. This vision pushes me toward a client-side framework, and my current favorite is Backbone.js. The rails-backbone gem simplifies using Backbone with Rails, so we’ll put that gem on the list as well. (Note: There are two Backbone gems that are very closely named, ensure you are using codebrew’s gem)

Also, I have become a fan of Haml so I think we’ll use Haml instead of ERB for our view templates.

Testing

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.

Source Control

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.

Other Resources

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.

Done? Hopefully you have a default MongoDB setup ready to go. The last thing to do before we generate the application is to create a github repository for our app (Mine is here.)

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', '~> 1.0.2.1'
end

A quick 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 *.swp and .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://ruprict@github.com/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

Now, 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.

Loccasions

<< Rails Intro, Deep Dive: App GenerationRails Deep Dive: Loccasions, Home Page >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.methack.it/devblog mattia

    Hi Glenn, thanks for your article… you inspired me on backbone! but, i’ve another question: i’m quite new to the mongoDB world, but recently i’ve heard his name from everywhere. According to your experience, which are the pros of this technology?

    • http://www.ruprict.net/ Glenn Goodrich

      Well, I am relatively new to the MongoDB/NoSQL world myself, but I’ll try to go through a couple of the pros/cons. In this case, I foresee the Events encompassing the Occasions, meaning, when I ask for an event, I want the occasions to come with it. The document-subdocument schema that MongoDB offers fits this well (IMO). Also, we the no schema (meaning, in the Rails case, no migrations) is attractive when you may need to change the schema later. Finally, the native JSON storage that MongoDB uses makes working with the data a bit more intuitive. However, there are certainly many cases where an RDBMS would be the right tool, like if you need transactions. The idea is to use the right tool/db for the job.

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi Mattia,

      I found this blog post on Heroku very informative about nosql:
      http://blog.heroku.com/archives/2010/7/20/nosql/

      DAZ

  • http://kkerley.com Kyle

    I was having a heck of a time getting this to work at first once I got to the “bundle install” portion. I copy/pasted your Gemfile code above for the sake of saving time and I kept getting errors every time I ran it. Turns out, it was because of this line:

    gem ‘bson_ext’, ‘~%gt; 1.3.1′

    the %gt; is supposed to be >

    Once I changed that, everything worked brilliantly. Great article…I’m very much looking forward to the upcoming parts! Thanks for taking the time to do this!

    • http://www.ruprict.net/ Glenn Goodrich

      Hey Kyle,

      Thanks for finding that…had a rougy ‘%’ where I needed a ‘&’….fixed now.

      Glenn

  • Walter Souto

    Hi Glenn,

    Nice work! Thank you very much.

    I noted that you didn’t mencion ‘git init’ before ‘git add .’, of course we all know about that, but, maybe you want to put it there for completeness only.

    Walter.

  • Tim

    Hi Glenn,

    I have a question about the gem listings in the Gemfile, which has been bugging me for a while now. How do you know what version numbers to put against each gem? I thought the purpose of Bundler was to work out all the dependencies for you, so that you don’t need to specify the versions explicitly. I see lots of rails tutorials that take the approach you have, so it obviously seems like a good idea, but I wouldn’t know where to start working out which version numbers I should put in. Can you put me straight?

    Tim

    • http://www.ruprict.net/ Glenn Goodrich

      Hey Tim,

      The short answer is, I look at the latest on rubygems.org for each gem I want and start there. If Bundler then whines about a dependency, I’ll change the version to whatever the dependency needs (had to do this with bson_ext, for example). The twitta-waka (or whatever it’s called… the ~> thing) allows your app some flexibility without jumping to a breaking version (presuming the gem authors are following semantic versioning).

      That make sense?

      • Tim

        Yes, I think it does, thanks. Let me check, just to make sure:

        — the twitta-waka allows bundler to upgrade to a higher minor version, but not a higher major version. If you don’t put the twitta-waka in, then you risk your gems not working if one of your gems has a major release, you run ‘bundle install’, and the new gem stops working with one or more of your other gems or rails code (for whatever reason – it’s just more likely to happen on a major version upgrade than a minor version upgrade).

        — to find the right gem version you look for the current highest version number on rubygems.org for each gem

        — you then plug the version numbers into your Gemfile, run ‘bundle install’ and if bundler doesn’t complain, you’re good to go. But if it does, you read the error messages and adjust accordingly (some googling may be necessary to find the right version I guess).

        Or, do you just run ‘bundle install’ without any version numbers and then list the gems that have been installed to find their version numbers and plug those into the gemfile?

        And even if it’s not called a twitta-waka, I believe the world would be a better place if it were.

        Tim

        • http://www.ruprict.net/ Glenn Goodrich

          :)

          I would never recommend putting gems without versions into the Gemfile. Also, when you look on rubygems.org, each gem should list its dependencies, including the required versions. So, if you start with, say, Rails 3.1.0 final, for example, you can check each gem to ensure it works with Rails 3.1.0. In practice, however, I generally start with rubygems.org, get my versions there, and cruise until I hit an issue.

          Hope this helps…

          • Tim

            Yes, I get the process now, thanks. But I’m still a bit confused about what Bundler does if despite checking dependencies across all the gems in your Gemfile you could still get dependency conflicts. I did try and google it and the best I could find was this quote from Yehuda Katz:

            “After bundling, always check your Gemfile.lock into version control. If you do this, you do not need to specify exact versions of gems in your Gemfile.”
            [from: http://yehudakatz.com/2011/05/30/gem-versioning-and-bundler-doing-it-right/

            That seems to suggest I can just write, for example:
            gem ‘rspec-rails’

            rather than:
            gem ‘rspec-rails’, ‘~> 2.6.1′

            unless I specifically I _want_ to use version 2.6.1 for whatever reason. If I understand Bundler’s purpose correctly, it _should_ resolve dependencies across all gems in the Gemfile (and it too gets its dependency information from the gem’s spec file on Rubygems.org as I understand it). So are you saying that if:

            1) you _want_ to install not less than version x.y.z for a specific gem, and
            2) you want to install new versions of that gem when they are released, and
            3) a new gem version is released, and
            4) you happen to run ‘bundle install’
            then to avoid dependency issues caused by the gem upgrade, you make sure you restrict the new version to a minor version upgrade using the twitta-waka.

            But if that is true, then wouldn’t a strategy of putting gems without version numbers into the Gemfile, running ‘bundle install’ and then filling in the version numbers from the Gemfile.lock after work?

            Sorry for being a bit slow here. There’s obviously something about the way Bundler/gems work that I’m not understanding. And I guess I just want to find the lazy option and throw gems into the Gemfile without caring too much about hunting down version numbers!

            Tim

          • http://www.ruprict.net/ Glenn Goodrich

            Hey Tim,

            I don’t think you are being slow, I think i am being lazy. The post you link from ykatz lists the right way to do things. He, after all, has probably forgotten more about how to setup a ruby/rails app than I’ll ever know. According to him, you can rely on Bundler to manage your dependencies without putting versions into your Gemfile. As updates occur, running bundle will try to update your app or prompt you when conflicts occur. So, in short, I think you’ve answered your own question. In fact, I don’t even think you need to go back and fill in version numbers from your Gemfile, provided you add the Gemfile.lock to your repository. BTW, Heroku asks you to do just that to ensure the versions of the gems heroku uses matches your dev environment.

            Thanks for keeping at this…

  • http://jeremyricketts.com Jeremy Ricketts

    This article really cleared up a few things for me. Well, this whole series really. Anyway, THANK YOU.