A cool, clear drink of Ruby object persistence

32
A cool, clear drink of Ruby object persistence Aqua Kane Baccigalupi RubyConf 09

description

Introduction to a proof of concept library 'Aqua' that uses CouchDB and other document oriented back ends to transparently persist Ruby objects.

Transcript of A cool, clear drink of Ruby object persistence

Page 1: A cool, clear drink of  Ruby object persistence

A cool, clear drink of

Ruby object persistence

Aqua

Kane Baccigalupi RubyConf 09

Page 2: A cool, clear drink of  Ruby object persistence

Aqua A cool, clear drink of Ruby object persistence

Kane Baccigalupi RubyConf ‘09

Page 3: A cool, clear drink of  Ruby object persistence

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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