A cool, clear drink of Ruby object persistence
-
Upload
baccigalupi -
Category
Technology
-
view
4.133 -
download
0
description
Transcript of A cool, clear drink of Ruby object persistence
A cool, clear drink of
Ruby object persistence
Aqua
Kane Baccigalupi RubyConf 09
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
Got unemployment?
Relax with CouchDB
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
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
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
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
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
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
Aqua’s goal:
Focus on objects
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
object.commit!
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
How does Aqua work?
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
Just add Aqua
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
class User aquatic # ...end
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.
Behind the scene
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
user.commit!
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
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
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" }}
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
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
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]" }}
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"} }] } }}
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"} }] } }}
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
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
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" }, } }}
Getting Objects:The basics
Aqua A cool, clear drink of Ruby object persistence
Kane Baccigalupi RubyConf ‘09
User.load( some_id )
user.reload
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
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
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 ???
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)
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
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