KEMBAR78
Practical Ruby Projects With Mongo Db | KEY
Practical Ruby
Projects with
Who am I?
Alex Sharp
Amphibious Code Creature at OptimisDev
@ajsharp
alexjsharp.tumblr.com
We’re going to talk about
      two things.
1. Why/how MongoDB is
       practical
2. A few examples of how to
  use MongoDB with Ruby
So what’s all this “practical”
           talk?
Imagine if databases were
           cars...
Any takers for MySQL?
RDBMS (i.e. mysql) =~
In other words...
MySQL is very reliable.
But it may not be a speed
         demon.
Guesses for MongoDB?
MongoDB =~
Mongo is reliable too, but
they’re also go pretty damn
            fast.
The Practicality of
   MongoDB
Simplifying schema design
Objects weren’t made for SQL schemas
Simplifying schema design
Objects weren’t meant to be “mapped” to a SQL
schema
Simplifying schema design
We’re forced to create “relationships” when what we
really want is properties for our objects.
Simplifying schema design
Non-relational databases are better suited for objects
Simplifying schema design
Maps/hashes/associative arrays are arguably among
the best object serialization formats
@alex = Person.new(
  :name => "alex",
  :friends => [Friend.new("Jim"), Friend.new("Bob")]
)
Native ruby object

<Person:0x10017d030 @name="alex",
  @friends=
     [#<Friend:0x10017d0a8 @name="Jim">,
      #<Friend:0x10017d058 @name="Bob">
  ]>
JSON Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
SQL Schema Representation
# in a SQL schema
people:
  - name

friends:
  - name
  - friend_id
Ruby -> JSON -> SQL
       <Person:0x10017d030 @name="alex",
       @friends=
Ruby      [#<Friend:0x10017d0a8 @name="Jim">,
           #<Friend:0x10017d058 @name="Bob">
       ]>

       @alex.to_json
JSON   { name: "alex",
         friends: [{ name: "Jim" }, { name: "Bob" }]
       }


       people:
         - name
SQL
       friends:
         - name
         - friend_id
Ruby -> JSON -> SQL
       <Person:0x10017d030 @name="alex",
       @friends=
Ruby      [#<Friend:0x10017d0a8 @name="Jim">,
           #<Friend:0x10017d058 @name="Bob">
       ]>

       @alex.to_json
JSON   { name: "alex",
         friends: [{ name: "Jim" }, { name: "Bob" }]
       }


       people:
         - name         Feels like we’re having
SQL                     to work too hard here
       friends:
         - name
         - friend_id
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
   In Mongo, friends is an “embedded document”
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
        Great for one-to-many associations
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
              No JOINS required.
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
     This is a good thing for scalability, because
     JOINS limit our ability to horizontally scale.
Mongo Representation
@alex.to_json
{ name: "alex",
  friends: [{ name: "Jim" }, { name: "Bob" }]
}
   But more importantly, this seems more intuitive
          than messing with foreign keys
Ok, so what?
You’re probably thinking...
“Listen GUY, SQL isn’t that bad”
Ok, so what?
True.
Ok, so what?
SQL is actually really, really good at what it was
designed to do.
Ok, so what?
But SQL schemas are designed for storing and
querying data, not necessarily modeling objects.
Ok, so what?
It is called Structured Query Language.
Ok, so what?
And to talk to these schemas in software, we use ORMs.
Ok, so what?
ORM stands for Object Relational Mapper
Ok, so what?
We need ORMs to bridge the gap between SQL and native
objects (in our case, Ruby objects)
Ok, so what?
I don’t want to map my objects to a schema designed for
querying data.
Ok, so what?
I want to store my objects in a datastore that was designed
for storing objects
Ok, so what?
Mongo is great for this b/c it stores objects (documents) as
binary JSON
Ok, so what?
And as we’ve seen, JSON is great for representing native
objects
Ok, so what?
This is important because when I’m writing an application, I
don’t want to accomodate my objects to my datastore.
Ok, so what?
I want something flexible that stays out of my way, but
doesn’t sacrifice performance.
In a Nutshell
 Schema design for humans, not machines

   i.e. document-oriented is awesome

   schema-less is for adults

 Pragmatic balance of performance and functionality

 Speed/performance of mongo is super awesome

 Scalability features are really powerful too
Practical Projects
Three Examples


1.Blogging Application
2.Accounting Application
3.Logging
Blogging Application
Blogging Application
 Much easier to model with Mongo than a relational
 database
Blogging Application
 A post has an author
Blogging Application
 A post has an author
 A post has many tags
Blogging Application
 A post has an author
 A post has many tags
 A post has many comments
Blogging Application
 A post has an author
 A post has many tags
 A post has many comments


Instead of JOINing separate tables,
we can use embedded documents.
require 'mongo'

conn = Mongo::Connection.new.db('bloggery')
posts   = conn.collection('posts')
authors = conn.collection('authors')
# returns a Mongo::ObjectID object
alex = authors.save :name => "Alex"
post = posts.save(
  :title      => 'Post title',
  :body       => 'Massive potification...',
  :tags       => ['laruby', 'omg', 'lolcats'],
  :comments    => [
     { :name => "Loudmouth McGee",
       :email => 'loud@mouth.edu',
       :body => "Something really ranty..."
     }
  ],
  :author_id => alex
)
# returns a Mongo::ObjectID object
alex = authors.save :name => "Alex"
post = posts.save(
  :title      => 'Post title',
  :body       => 'Massive potification...',
  :tags       => ['laruby', 'omg', 'lolcats'],
  :comments    => [
     { :name => "Loudmouth McGee",
       :email => 'loud@mouth.edu',
       :body => "Something really ranty..."
     }
  ],
                    Joins not necessary. Sweet.
  :author_id => alex
)
Logging with Capped
Collections
Capped collections
  Fixed-sized, limited operation, auto age-out
  collections (kinda like memcached)
  Fixed insertion order
  Super fast (faster than normal writes)
  Ideal for logging and caching
This is awesome.
Now we have logs we can
  query (and analyze)
Can also be used for
troubleshooting when things
         go wrong
Bunyan
  Thin ruby layer around a
  Mongo capped collection
require 'bunyan'

Bunyan::Logger.configure do |c|
  c.database   'my_bunyan_db'
  c.collection 'development_log'
  # == 100.megabytes if using rails
  c.size       104857600
end

Bunyan::Logger.save(
  :request_method => 'get',
  :status_code => 200
)
A simple accounting
application (maybe)
The object model
           ledger


               *
         transactions


               *
           entries
The object model

                  #       Credits                      Debits



Transaction   {   1
                      { :account => “Cash”,
                        :amount => 100.00 }
                                                { :account => “Notes Pay.”,
                                                     :amount => 100.00 }


                                  Ledger Entries
Transaction   {   2   { :account => “A/R”,
                        :amount => 25.00 }
                                              { :account => “Gross Revenue”,
                                                     :amount => 25.00 }
Object model summary
Each ledger transaction belongs to a ledger.
Each ledger transaction has two ledger entries which
must balance.
Object Model with ActiveRecord
@credit_entry = LedgerEntry.new :account => "Cash",
  :amount => 100.00, :type => "credit"
@debit_entry = LedgerEntry.new :account => "Notes Pay.",
  :amount => 100.00, :type => "debit"


@ledger_transaction = LedgerTransaction.new
  :ledger_id => 1,
  :ledger_entries => [@credit_entry, @debit_entry]
Object Model with Mongo
@ledger_transaction = LedgerTransaction.new :ledger_id => 1,
  :ledger_entries => [
    { :account => 'Cash',       :type => "credit", :amount => 100.00 },
    { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 }
  ]
Object Model with Mongo
@ledger_transaction = LedgerTransaction.new :ledger_id => 1,
  :ledger_entries => [
    { :account => 'Cash',       :type => "credit", :amount => 100.00 },
    { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 }
  ]


 This is the perfect case for embedded documents.
Object Model with Mongo
@ledger_transaction = LedgerTransaction.new :ledger_id => 1,
  :ledger_entries => [
    { :account => 'Cash',       :type => "credit", :amount => 100.00 },
    { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 }
  ]


We would never have a ledger entry w/o a transaction.
Using mongo w/ Ruby

Ruby mongo driver
MongoMapper
MongoID
Many other ORM/ODM’s under active development
MongoMapper
MongoMapper
• MongoDB “ORM” developed by John Nunemaker
MongoMapper
• MongoDB “ORM” developed by John Nunemaker
 • author of HttpParty
MongoMapper
• MongoDB “ORM” developed by John Nunemaker
  • author of HttpParty
• Very similar syntax to DataMapper
MongoMapper
• MongoDB “ORM” developed by John Nunemaker
  • author of HttpParty
• Very similar syntax to DataMapper
  • Declarative rather than inheritance-based
MongoMapper
• MongoDB “ORM” developed by John Nunemaker
  • author of HttpParty
• Very similar syntax to DataMapper
  • Declarative rather than inheritance-based
• Very easy to drop into rails
MongoMapper
class Post
  include MongoMapper::Document

 belongs_to :author, :class_name => "User"

 key :title,     String, :required => true
 key :body,      String
 key :author_id, Integer, :required => true
 key :published_at, Time
 key :published,    Boolean, :default => false
 timestamps!

  many :tags
end


class Tag
  include MongoMapper::EmbeddedDocument

  key :name, String, :required => true
end
Questions?
Thanks!

Practical Ruby Projects With Mongo Db