Tame Accidental Complexity with Ruby and MongoMapper

Preview:

DESCRIPTION

Gentle introduction to MongoDb with Ruby and MongoMapper

Citation preview

Tame accidental complexityIntroduction to NoSQL with MongoMapper

Giordano Scalzo

I’m not here to talk about performance

I’m not here to talk about scalability

but I’m here to talk about simplicity

Rails has been a first step

Anatomy of a Rails Application

Anatomy of a Rails Application

view

Anatomy of a Rails Application

view controller

Anatomy of a Rails Application

modelview controller

Different languages

modelview controller

html+css

Different languages

modelview controller

html+css oop

Different languages

modelview controller

html+css oop sql

Impedance mismatch

modelview controller

html+css oop sql

We need persistent objects!

We need persistent objects!

class User def initialize(username, password) @username = username @password = password endend

We need persistent objects!

{ username: "giordano", password: "123"}

ActiveRecord tries its best

We need something different

Persistence

Persistence

class User include MongoMapper::Documentend

Persistence

class User include MongoMapper::Documentend

user = User.create({ :username => "giordano", :password => "123"})user.save

Persistence

class User include MongoMapper::Documentend

user = User.create({ :username => "giordano", :password => "123"})user.save

puts User.all.last.to_mongo

Persistence

{ "_id"=>BSON::ObjectId('4d643a274d8ff683dd000001'), "username"=>"giordano", "password"=>"123"}

Types

Types

class User include MongoMapper::Document key :username, String key :password , Stringend

Built-in Types

Array, Binary, Boolean, Date, Float, Hash, Integer, Nil, ObjectId, Set, String, Time

Custom Types

class DowncasedString def self.to_mongo(value) value.nil? ? nil : value.to_s.downcase end def self.from_mongo(value) value.nil? ? nil : value.to_s.downcase endend

Custom Types

class User include MongoMapper::Document key :username, String key :password , String key :email, DowncasedStringend

Custom Types

user = User.newuser.username = "giordano"user.password = "123"user.email = "Giordano.Scalzo@CleanCode.it"

user.save

puts User.all.last.to_mongo

Custom Types

{ "_id"=>BSON::ObjectId('4d6442d94d8ff684d3000001'), "username"=>"giordano", "password"=>"123", "email"=>"giordano.scalzo@cleancode.it"}

Embedded Documents

Embedded Documents

class Task include MongoMapper::EmbeddedDocument key :description, String key :pomodori, Integer key :is_done, Booleanend

Embedded Documents

class User include MongoMapper::Document key :username, String key :password , String key :email, DowncasedString many :tasksend

Embedded Documents

user.tasks << Task.new({ description: 'refactor server', pomodori: 8, is_done: false})

user.tasks << Task.new({ description: 'timer sound', pomodori: 2, is_done: false})

Embedded Documents

{"_id"=>BSON::ObjectId('4d6575e84d8ff692e6000001'), "username"=>"giordano", "password"=>"123", "email"=>"giordano.scalzo@cleancode.it", "tasks"=>[{ "_id"=>BSON::ObjectId('4d6575e84d8ff692e6000002'), "description"=>"refactor server", "pomodori"=>8, "is_done"=>false }, { "_id"=>BSON::ObjectId('4d6575e84d8ff692e6000003'), "description"=>"timer sound", "pomodori"=>2, "is_done"=>false }]}

Embedded Documents

{"_id"=>BSON::ObjectId('4d6575e84d8ff692e6000001'), "username"=>"giordano", "password"=>"123", "email"=>"giordano.scalzo@cleancode.it", "tasks"=>[{ "_id"=>BSON::ObjectId('4d6575e84d8ff692e6000002'), "description"=>"refactor server", "pomodori"=>8, "is_done"=>false }, { "_id"=>BSON::ObjectId('4d6575e84d8ff692e6000003'), "description"=>"timer sound", "pomodori"=>2, "is_done"=>false }]}

Documents

class Task include MongoMapper::Document key :description, String key :pomodori, Integer key :is_done, Booleanend

Documents

p User.all.last.to_mongo

{ "_id"=>BSON::ObjectId('4d657e924d8ff6949c000001'), "username"=>"giordano", "password"=>"123", "email"=>"giordano.scalzo@cleancode.it"}

Documents

p User.all.last.tasks

[#<Task _id: BSON::ObjectId('4d65822b4d8ff69542000002'), description: "refactor server", pomodori: 8, is_done: false, user_id: BSON::ObjectId('4d65822b4d8ff69542000001') >, #<Task _id: BSON::ObjectId('4d65822b4d8ff69542000003'), description: "timer sound", pomodori: 2, is_done: false, user_id: BSON::ObjectId('4d65822b4d8ff69542000001') >]

Validations & Callbacks

Validations & Callbacks

class User include MongoMapper::Document key :username, String, validates_presence_of :username key :password, String validates_presence_of :passwordend

Validations & Callbacks

class User include MongoMapper::Document key :username, String, :required => true key :password, String, :required => trueend

Validations & Callbacks

validates_presence_ofvalidates_length_ofvalidates_format_ofvalidates_numericality_ofvalidates_acceptance_ofvalidates_confirmation_ofvalidates_inclusion_ofvalidates_exclusion_of

Validations & Callbacks

before_save after_save before_create after_create before_update after_update before_validation after_validation before_validation_on_create after_validation_on_create before_validation_on_update after_validation_on_update before_destroy after_destroy validate_on_create validate_on_update validate

Validations & Callbacks

forked in current gem 0.8.6using Rails3 ActiveModel in Rails3 branch just merged

What about querying?

What about querying?

Plucky: ActiveRecord-like language

query = User.where(:last_name.exists => true, :created_at.gte => from_date, :created_at.lt => Time.now) .skip(0).limit(5)

query.all

What about querying?

Plucky: ActiveRecord-like language

query = User.where(:last_name.exists => true, :created_at.gte => from_date, :created_at.lt => Time.now) .skip(0).limit(5)

#<Plucky::Query created_at: { "$gte"=>"1", "$lt"=>2011-02-24 10:54:36 UTC}, last_name: {"$exists"=>true}, limit: 5, skip: 0>

What about querying?

Plucky: ActiveRecord-like language

query = User.where(:last_name.exists => true) .where(:created_at.gte => from_date) .where(:created_at.lt => Time.now) .skip(0).limit(5)

#<Plucky::Query created_at: { "$gte"=>"1", "$lt"=>2011-02-24 10:54:36 UTC}, last_name: {"$exists"=>true}, limit: 5, skip: 0>

What about plugins?

What about plugins?AccessibleAssociationsCachingCallbacksCloneDirtyDocumentDynamic QueryingEmbeddedDocumentEqualityIdentityMapIndexesInspectKeysLogger

ModifiersPaginationPersistenceProtectedQueryingRailsSafeSingle Collection InheritanceScopesSerializationTimestampsUserstampsValidations

Itʼs just a beginning

http://mongoid.org/

@giordanoscalzo

www.slideshare.net/giordano

github.com/gscalzo

giordano.scalzo@cleancode.it