Mongoid in the
Real World


Toronto Ruby Brigade           Kevin Faustino
December 14, 2010                         @kfaustino
                       http://adventuresincoding.com
Thanks to our sponsors
What is Mongoid?
Object Document
    Mapper
Using Mongoid
Everyday Mongoid
   Integrations
class Post
end
class Post
  include Mongoid::Document
end
Types
class Post
  include Mongoid::Document
  include Mongoid::Timestamps

  field   :title
  field   :content
  field   :published_on, :type => DateTime
  field   :slug
end
Valid Types



Array, BigDecimal, Boolean, Date, DateTime, Float, Hash,
Integer, String, Symbol, Time
Associations
class Person
  include Mongoid::Document
  field :first_name
  field :last_name
  embeds_one :address
end

class Address
  include Mongoid::Document
  field :street
  field :city
  embedded_in :person, :inverse_of => :address
end
{
    "_id" : ObjectId("4d06eaa46c50a1031a000001"),
    "address" : {
       "_id" : ObjectId("4d06eade6c50a1031a000002"),
       "street" : "123 Liberty St.",
       "city" : "Toronto"
    },
    "first_name" : "Kevin",
    "last_name" : "Faustino"
}
class Blog
  include Mongoid::Document

  references_many :posts
end

class Post
  include Mongoid::Document

  referenced_in :blog
end
{
  "_id" : ObjectId("4c9f54866c50a10a75000003"),
  "blog_id" : ObjectId("4ca531c46c50a12d1b000002"),
  "content" : "What is Faraday? ...",
  "featured_image_filename" : "faraday.jpg",
  "slug" : "building-modular-http-client-code-with-
faraday",
  "title" : "Building modular HTTP client code with
Faraday",
}
Validations
ActiveModel Validators + More
       validates_acceptance_of
       validates_associated
       validates_confirmation_of
       validates_exclusion_of
       validates_format_of
       validates_inclusion_of
       validates_length_of
       validates_numericality_of
       validates_presence_of
       validates_uniqueness_of
Indexing
Defining Indexes
# unique
index :ssn, :unique => true

# Compound indexes
index([
     [ :first_name, Mongo::ASCENDING ],
     [ :last_name, Mongo::ASCENDING ]
])

# Foreign Key indexes
referenced_in :post, :inverse_of
=> :comments, :index => true
Using Mongoid
Everyday Mongoid
   Integrations
Document Limit
4 MB
Testing
RSpec Integration

# in spec/spec_helper.rb
RSpec.configure do |config|

  config.mock_with :rspec

  config.before :each do
    Mongoid.master.collections.select {|c| c.name !~ /
system/ }.each(&:drop)
  end

end
Shoulda Matchers?
Use Remarkable


# in spec/spec_helper.rb
require 'remarkable/active_model'
require 'remarkable/mongoid'
Mongoid Matchers
have_field
reference_one
reference_many
be_referenced_in
embed_one
embed_many
be_embedded_in
validate_uniqueness_of
validate_association
All ActiveModel validations from Remarkable::ActiveModel
Cucumber Integration


# features/support/hooks.rb
Before do |scenario|
  Mongoid.master.collections.select {|c| c.name !~ /system/ }.each
(&:drop)
end
Factory Girl
Works out of the box
FactoryGirl.define do

  factory :post do
    title 'My first blog post'
    author 'Kevin Faustino'
    content 'Hello World'
  end

end
Factories with Cucumber


# features/support/env.rb
require 'factory_girl'
require 'factory_girl/step_definitions'
Monkey Patched Step Definition

# features/step_definitions/mongoid_steps.rb
Given /^an? (.+) exists with an? (.+) of "([^"]*)"$/
do |model, field, value|
  factory_name = model.gsub(' ', '_')
  Factory factory_name, field => value
end
Example Feature Usage


Given the following posts exist:
  | title                        | excerpt          |
  | 10 greatest Batman Villians | We countdown ... |
  | The Death of Batman          | Batman falls ... |
Caching in Rails
Create a Cache-Key
module Cacheable

  def cache_key
    if new_record?
      "#{collection_name}/#{id}/new"
    elsif respond_to?(:updated_at)
      "#{collection_name}/#{id}-#{updated_at.to_i}"
    else
      "#{collection_name}/#{id}"
    end
  end

end
Safe Operations
post = Post.safely(:w => 2).create(
  :title => 'We will receive verification'
)
post.safely(:w => 2).destroy
Safely Options


:w - A client can block until a write
operation has been replicated to N servers

:fsync - force the database to fsync all files
before returning
Enslave
Query from your Slaves

# Query specific
Post.where(:title => 'Toronto Ruby').enslave

# Document specific
class Post
  include Mongoid::Document
  enslave
end
Dropping Down
When you need more
performance, drop down
      to the driver
collection = mongo-ruby-driver


# Add a post id only if it does not already exist on
# the set
collection.update({'_id' => id}, {'$addToSet' =>
{'post_ids' => post.id}})
Using Mongoid
Everyday Mongoid
   Integrations
Full Text-Search
Use Solr

@solr = RSolr.connect :url => "http://127.0.0.1:8983/solr"
@solr.add( {
  :id => post.id,
  :text => post.content,
  :published_on => post.published_on.strftime("%Y-%m-%dT%H:%M:
00Z")
  }, { :add_attributes => { :commitWithin => 10000 } })
Photo Uploads
Not with paperclip!




      X
Agnostic
Document Example
class Post
  include Mongoid::Document
  include Mongoid::Timestamps

  field   :title
  field   :author
  field   :content
  field   :slug

  referenced_in :blog

  mount_uploader :featured_image, ImageUploader
end
Authentication
Use Devise

class User
  include Mongoid::Document
  devise :database_authenticatable,
    :rememberable,
    :trackable,
    :validatable

end
Coming Soon
Many to Many support
without dropping down
     to the driver
Thank you!

Mongoid in the real world