a cool, clear drink of ruby object persistence
DESCRIPTION
Introduction to a proof of concept library 'Aqua' that uses CouchDB and other document oriented back ends to transparently persist Ruby objects.TRANSCRIPT
![Page 1: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/1.jpg)
A cool, clear drink of
Ruby object persistence
Aqua
Kane Baccigalupi RubyConf 09
![Page 2: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/2.jpg)
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
![Page 3: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/3.jpg)
Got unemployment?
Relax with CouchDB
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
![Page 4: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/4.jpg)
ORMs are great,
but normalization is expensive.
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
# DataMapper
class Mammal include DataMapper::Resource property :my_id, Serial has n, :legsend
# ActiveRecord
class Bird < ActiveRecord::Base # properties defined by migration has_many, :legsend
![Page 5: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/5.jpg)
CouchDB + Ruby ~= CouchRest
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
# CouchRest
class Reptile < CouchRest::ExtendedDocument use_database MY_DB unique_id :my_id property :legs # a collection!end
![Page 6: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/6.jpg)
CouchRest is different from ORMs because:
• It allows collections• Models are hashes• Instance variables are discarded
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
![Page 7: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/7.jpg)
What defines Ruby object state?
Instance variables @my_variable
Fundamental data Hash key-values, Array values, etc.
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
![Page 8: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/8.jpg)
Database abstractionsfocus on databases
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
# DataMapper
class Mammal include DataMapper::Resource property :my_id, Serial has n, :legsend
# CouchRest
class Reptile < CouchRest::ExtendedDocument use_database MY_DB unique_id :my_id property :legs # a collection!end
# ActiveRecord
class Bird < ActiveRecord::Base # properties defined by schema has_many, :legsend
![Page 9: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/9.jpg)
Aqua’s goal:
Focus on objects
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
object.commit!
![Page 10: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/10.jpg)
Because Ruby is awesome
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class Event < Range attr_accessor :nameend
rubyconf = Event.new(Date.parse('11/19/2009'), Date.parse('11/21/2009'))rubyconf.name = 'RubyConf 09'rubyconf.include?(Date.parse('11/20/2009')) # true
![Page 11: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/11.jpg)
How does Aqua work?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
![Page 12: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/12.jpg)
Just add Aqua
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ...end
![Page 13: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/13.jpg)
Just add Aqua
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ...end
user = User.new# ... More stuff happens to the user
# saving an objectuser.commit! # commit without the ! also works but raises no errors.
![Page 14: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/14.jpg)
Behind the scene
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
user.commit!
![Page 15: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/15.jpg)
Behind the scene
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
{ "class"=>"User", "ivars"=>{ "@username"=>"kane", "@email"=>"[email protected]", "@password"=>"secret" }}
user.commit!
serialization
![Page 16: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/16.jpg)
Behind the scene
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
{ "class"=>"User", "ivars"=>{ "@username"=>"kane", "@email"=>"[email protected]", "@password"=>"secret" }}
user.commit!
serialization
data post
![Page 17: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/17.jpg)
Objects ~= Documents,&& Documents ~= Hashes
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
# YAML for a Ruby User object
--- &id001 !ruby/object:User email: [email protected] password: secret username: kane
# Aqua Serialization for same object
{ "class"=>"User", "ivars"=>{ "@username"=>"kane", "@email"=>"[email protected]", "@password"=>"secret" }}
![Page 18: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/18.jpg)
Sometimes state should not hang around.
There is a method for that.
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic attr_accessor :username, :email, :password hide_attributes :passwordend
![Page 19: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/19.jpg)
Going deeper with embedded objects …
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
address = Address.new # . . . user = User.newuser.addresses = [ address ]
class Address # not an aquatic object, just plain ruby attr_accessor :name, :street, :city, :state, :zipend
![Page 20: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/20.jpg)
Ordinary objectsget serializedinside aquaticobjects
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
{ "class"=>"User", "ivars"=>{ "@addresses"=>{ "class"=>"Array", "init"=>[{ "class"=>"Address", "ivars"=>{ "@city"=>"San Francisco", "@name"=>"work", "@street"=>"P0 Box 58", "@state"=>"94102" } }] }, "@username"=>"kane", "@email"=>"[email protected]" }}
![Page 21: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/21.jpg)
Embedded aquatic objects are saved by reference.
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ... def friends @friends ||= [] endend
user.friends << alex # where alex is another user object
{ "class"=>"User", "ivars"=>{ # ... "@friends"=>{ "class"=>"Array", "init"=>[{ "class"=>"Aqua::Stub", "init"=>{"class"=>"User", "id"=>"32"} }] } }}
![Page 22: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/22.jpg)
Aqua::Stub ~= Lazy Delegate
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
• Gets the object from the db
• Triggered by #method_missing
• Stubs/caches methods
{ "class"=>"User", "ivars"=>{ # ... "@friends"=>{ "class"=>"Array", "init"=>[{ "class"=>"Aqua::Stub", "init"=>{"class"=>"User", "id"=>"32"} }] } }}
![Page 23: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/23.jpg)
Stubbing methods in Aqua::Stub
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
{ "class"=>"User", "ivars"=>{ # ... "@friends"=>{ "class"=>"Array", "init"=>[{ "class"=>"Aqua::Stub", "init"=>{ "class"=>"User", "id"=>"32”, "methods"=>{"username"=>"alex"}, } }] } }}
class User aquatic :embed =>
{ :stub => :username } # ...end
![Page 24: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/24.jpg)
Stubbed behavior
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
user.reload friend = user.friends.first
friend.class # Aqua::Stub
friend.username # ‘alex’# username was cached
friend.email# this triggers the database call # for the friend object
![Page 25: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/25.jpg)
Got Files?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ... attr_accessor :avatarend
user.avatar = my_pic
{ "class"=>"User", "ivars"=>{ # ... "@avatar"=>{
"class"=>"Aqua::FileStub” "init"=>{ "methods"=>{ "content_type"=>"image/png", "content_length"=>{ "class"=>"Fixnum", "init"=>"26551” } } "id"=>"image_attach.png" }, } }}
![Page 26: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/26.jpg)
Getting Objects:The basics
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
User.load( some_id )
user.reload
![Page 27: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/27.jpg)
Indexed Searches
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ... index_on :usernameend
" // javascript map function
function(doc) { if( doc['class'] == 'User' && doc['ivars'] && doc['ivars']['@username'] ){ emit( doc['ivars']['@username'], 1 ); } }"
User.query(:username, ’kane') # returns an array of all users named kane
![Page 28: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/28.jpg)
What’s next?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
More querying ease
• Search on nested variables• Criteria pattern ???• Custom dsl ???• Much coding love
![Page 29: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/29.jpg)
What’s next?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
Mixins
• Validations• Attribute constraints for class, size, etc.• Collections & relationships• ActiveRecord conversion ???
![Page 30: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/30.jpg)
What’s next?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
Serializing Code
• Lambdas and Procs• Singleton methods• Classes (are objects too)
![Page 31: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/31.jpg)
What’s next?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
Repository Layer
• In memory
• Stores objects, not serializations
• commits objects as needed
![Page 32: A cool, clear drink of Ruby object persistence](https://reader033.vdocuments.mx/reader033/viewer/2022061103/540591308d7f729e768b4d25/html5/thumbnails/32.jpg)
More Info
Rdocs: ruby-aqua.org
Get the gem: sudo gem install aqua
Explore the code: github.com/baccigalupi/aqua
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09