« Why Our Little NoSQL App Matters | Main | The Software That Links It All »

NoSQL on the Cloud: Our First Application

Banner Cloud formation on the Matterhorn

We're ready to get started making NoSQL apps on "the cloud", so let's review our cloud base image, log into it, and get going!   We'll use "describe-instances" to get its ID, and then use SSH to log in.

So far, so good.   Now that we're in our cloud application we can install Mongo (or any other NoSQL data store), create our data store, and front-end it with a new application.


First, we've got some building and utility programs to install.   From the command line we enter:

$ apt-get -y install tcsh git-core scons g++

$ apt-get -y install libpcre++-dev libboost-dev libreadline-dev xulrunner-1.9.1-dev
$ apt-get -y install libboost-program-options-dev libboost-thread-dev libboost-filesystem-dev libboost-date-time-dev

Next, we'll import the 10gen signing key (10Gen is the company that makes mongodb)

$ apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10

And we'll add the 10gen repo to the apt sources.list

$ sudo vi /etc/apt/sources.list

add the following line for ubuntu 10.10:

deb http://downloads.mongodb.org/distros/ubuntu 10.10 10gen

save and exit.

Finally, we'll install mongo-db.   Linux has a lot of nice installation utilites, and a series of apt-get install commands makes this easy.

$ apt-get -y install mongodb

Then check the installation

$ mongo
MongoDB shell version: 1.4.4
url: test
connecting to: test
type "exit" to exit
type "help" for help


The important thing here in creating our application in Rails is to avoid loading ActiveRecord, which we won't need or use with Mongo as our data store. One way to do this is with the --skip-activerecord switch.  We'll call this application mongomapper, because it should give us a nice quick overview of Mongo and the mongo_mapper gem that gives us active-record-like access in Mongo.   You can create your app skeleton like so:

$ cd /var/www/apps
$ rails new mongomapper --skip-active-record

$ echo "rvm --create use 'ruby-1.8.7-p302@example_app'" > ~/projects/example_app/.rvmrc

Ruby 1.9.2 appears to be about 2X faster than the old standby 1.8.7, but lots of older code isn't 1.9-compatible, so for now we'll stick with Ruby 1.8.7. We'll edit our Gemfile next, to give it the gems we'll need for this Mongo demo. We edit the Gemfile to bundle your application's dependencies, and the ability to manage gem versions is one of the terrific advances that Rails3 makes over Rails2.   Here's how we edit the Gemfile -- add the following code for the gems and dependencies we'll see with our Mongo front-end application:  

require 'rubygems'

require 'mongo'

source 'http://gemcutter.org'

gem "rails" 
gem "mongo_mapper"

gem "ruby-debug"

Next we'll add our test environment. We won't use this for this quick demo, but these are terrific tools that we'll want available for all of our projects. Add the following as well to our Gemfile:

group :development, :test do
gem "rspec-rails", ">= 2.0.0"

gem "cucumber-rails", ">= 0.3.2"

gem "webrat", ">= 0.7.2"


RSpec and Cucumber are probably the most popular test packages for Rails applications, and it's great to have them at-the-ready here.   I actually prefer "capybara" for Rails integration testing, but I haven't worked with it enough yet to make it part of my standard app install package.   Now we're ready to create and install our bundle of ruby gems specific to this application.   "Bundle install" I've found to be my answer to most unplanned startup problems with Rails3 apps -- a good bundle install here and we're rolling.

$ bundle install

Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.


The last thing we need to do is to create an initializer in our Rails app to connect to MongoDB. Create a Ruby file in config/initializers. We could call it any name we like; but here let's stick to convention and call it config/initializers/mongo.rb:

MongoMapper.connection = Mongo::Connection.new('localhost', 27017) 
MongoMapper.database = "#myapp-#{Rails.env}"
    if defined?(PhusionPassenger) 
        PhusionPassenger.on_event(:starting_worker_process) do |forked|
        MongoMapper.connection.connect_to_master if forked

I've created a simple Rails app for accessing Mongo, the files of which are shown below.   We have Mongo installed and ready, so if we fire Mongo up we can then fire up our Rails app and work with our NoSQL data store.  We'll make sure Mongo has a place to store its data, and then lauch Mongo with the run command:

$ mkdir -p /data/db     #the -p option creates a /data directory if there isn't one yet as well

$ mongod run &

We can also bring up a browser, and enter: http://ec2-174-129-81-163.compute-1.amazonaws.com:28017/ and see the Admin screen for our MongoDB data store in all its glory!   (Note:  I've done a bit more bookkeeping with nginx, thin, and dynamic DNS that I'll cover in my next posting)
Even better, we can now start our Rails test application, and play with our Mongo store in the cloud.   We've created the application in the conventional /var/www/apps/mongomapper, so from that directory we can start our application with:

$ rails s thin  #Note -- I've got nginx fronting thin on port 80, but either way start Rails here...

We can then enter our URL in a web browser, and we can see our Rails/Mongo/Amazon cloud application:

http://ec2-174-129-87-241.compute-1.amazonaws.com/notes/  # OR, with dyndns:  http://jkr-blog-dyndns.org

And this command takes us to our Mongomapper application home page:

We can then click on "Add Notes" and add a note that will be stored in MongoDB:

And when we click on the Save button, our new record is saved, like so:

We can now go back to our home page and we'll see our first note added to the list.   We can add more notes, edit them and delete them in a simple little introduction to Rails' and Mongo's simplicity, and maybe indication of a hint of the power of the Amazon (or similar) cloud, Rails, and Mongo.   

This is, admittedly, still just a little toy app, but we can create it's bigger brothers and sisters with ease -- just by adding more data and then focusing our work on the analytics that follow the data store.  This is just a beginning, but if we can do simple things quickly, impossible thing should take only a bit longer!

More to follow: