Upload
alex-sharp
View
10.003
Download
0
Embed Size (px)
Citation preview
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
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
Simplifying schema design
@alex = Person.new( :name => "alex", :friends => [Friend.new("Jim"), Friend.new("Bob")])
<Person:0x10017d030 @name="alex", @friends= [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob">]>
Native ruby object
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
JSON Representation
SQL Schema Representation# in a SQL schemapeople: - name friends: - name - friend_id
Ruby -> JSON -> SQL
people: - name friends: - name - friend_id
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
<Person:0x10017d030 @name="alex", @friends= [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob">]>
Ruby
JSON
SQL
Ruby -> JSON -> SQL
people: - name friends: - name - friend_id
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
<Person:0x10017d030 @name="alex", @friends= [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob">]>
Ruby
JSON
SQLFeels like we’re having to work too hard here
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
In Mongo, friends is an “embedded document”
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
Great for one-to-many associations
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
No JOINS required.
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
This is a good thing for scalability, because JOINS limit our ability to horizontally scale.
@alex.to_json{ name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
Mongo Representation
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.
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
In a Nutshell
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 objectalex = authors.save :name => "Alex"post = posts.save( :title => 'Post title', :body => 'Massive potification...', :tags => ['laruby', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => '[email protected]', :body => "Something really ranty..." } ], :author_id => alex)
# returns a Mongo::ObjectID objectalex = authors.save :name => "Alex"post = posts.save( :title => 'Post title', :body => 'Massive potification...', :tags => ['laruby', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => '[email protected]', :body => "Something really ranty..." } ], :author_id => alex)
Joins not necessary. Sweet.
Logging with 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
Capped collections
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
Text
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
1 { :account => “Cash”, :amount => 100.00 }
{ :account => “Notes Pay.”, :amount => 100.00 }
2 { :account => “A/R”, :amount => 25.00 }
{ :account => “Gross Revenue”, :amount => 25.00 }
Ledger Entries
{Transaction {
Transaction
Object model summary
Each ledger transaction belongs to a ledger.
Each ledger transaction has two ledger entries which must balance.
@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 ActiveRecord
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
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 :tagsend
MongoMapper
class Tag include MongoMapper::EmbeddedDocument
key :name, String, :required => trueend
Questions?
Thanks!