Tame accidental complexity Introduction 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 view controller model
Different languages html+css view controller model
Different languages html+css oop view controller model
Different languages html+css oop sql view controller model
Impedance mismatch html+css oop sql view controller model
Origin of Sql SQL From Wikipedia, the free encyclopedia SQL (...), is a database computer language designed for managing data in relational database management systems (RDBMS)
Origin of Sql SQL From Wikipedia, the free encyclopedia SQL (...), is a database computer language designed for managing data in relational database management systems (RDBMS)
We need persistent objects!
We need persistent objects! class User def initialize(username, password) @username = username @password = password end end
We need persistent objects! { username: "giordano", password: "123" }
ActiveRecord tries its best
We need something different
Persistence
Persistence class User include MongoMapper::Document end
Persistence class User include MongoMapper::Document end user = User.create({ :username => "giordano", :password => "123" }) user.save
Persistence class User include MongoMapper::Document end 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 , String end
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 end end
Custom Types class User include MongoMapper::Document key :username, String key :password , String key :email, DowncasedString end
Custom Types user = User.new user.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, Boolean end
Embedded Documents class User include MongoMapper::Document key :username, String key :password , String key :email, DowncasedString many :tasks end
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, Boolean end
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 :password end
Validations & Callbacks class User include MongoMapper::Document key :username, String, :required => true key :password, String, :required => true end
Validations & Callbacks validates_presence_of validates_length_of validates_format_of validates_numericality_of validates_acceptance_of validates_confirmation_of validates_inclusion_of validates_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.6 using Rails3 ActiveModel in Rails3 branch just merged
What about querying?
What about querying? query = User.where(:last_name.exists => true, :created_at.gte => from_date, :created_at.lt => Time.now) Plucky: ActiveRecord-like language .skip(0).limit(5) query.all
What about querying? query = User.where(:last_name.exists => true, :created_at.gte => from_date, :created_at.lt => Time.now) Plucky: ActiveRecord-like language .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? query = User.where(:last_name.exists => true) .where(:created_at.gte => from_date) .where(:created_at.lt => Time.now) Plucky: ActiveRecord-like language .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? Accessible Modifiers Associations Pagination Caching Persistence Callbacks Protected Clone Querying Dirty Rails Document Safe Dynamic Querying Single EmbeddedDocument Collection Equality Inheritance IdentityMap Scopes Indexes Serialization Inspect Timestamps Keys Userstamps Logger Validations
Itʼs just a beginning
http://mongomapper.com/documentation/
http://mongoid.org/
giordano.scalzo@cleancode.it @giordanoscalzo www.slideshare.net/giordano github.com/gscalzo

Tame Accidental Complexity with Ruby and MongoMapper