HybridDB Logo

Starter Tutorial

A hybrid Object Relational database

  1. No SQL
  2. No need for migrations
  3. Uses MySQL for indexing speed (other db adapters are trivial to implement, postgres and sqlite in progress)
  4. True Ruby objects

Download

TESTS

Number of tests within the test folder (you’ll need to set your connection to a mysql server to run them)

cd test ruby all_tests.rb

TUTORIALS

The hybrid_db download contains a tutorials sub-folder with example code to follow along with in articles on this site. Navigate to the tutorials sub-folder in your command shell (bash, dos cmd.exe) and launch irb with the relevant tutorial file e.g.

irb -r tutorial1.rb

Basic Usage

(listing tutorial1.rb)


    require 'lib/hybriddb'
    #Connect to the specified database and create the (minimal set of) tables required for operation if not present, database must exist
    HybridDB::Connection.set_adapter(MySQLAdapter.new({:host=> 'myserver', :user => 'username', :password => 'password', :database => 'hybriddb_dev' }))

    #create a model by inheriting from HybridObject
    class BlogPost < HybridObject 
        attr_accessor :title, :content, :viewable_range
    end

Then fire up irb

paul:~/dev/HybridDB/tutorial$ irb -r tutorial1.rb


    irb> BlogPost.find(:all)
    => []
    irb> b = BlogPost.new
    => #<BlogPost:0xb79af8c8>
    irb> b.title = "My First Post" 
    => "My First Post" 
    irb> b.content = "Lorem ipsum doler sit amet adapissthere...." 
    => "Lorem ipsum doler sit amet adapissthere...." 
    irb> b.viewable_range = Date.new(2008,1,1)..Date.new(2008,7,11)
    => #<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>
    irb> b.save
    => true

The save call saves the object to the database, no schema involved, you can see it in YAML format if you look in the hybrid_objects table in mysql


    irb> b
    => #<BlogPost:0xb79af8c8 @viewable_range=#<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>, @hybrid_id="216", @hybrid_version=1, @hybrid_saved=true, @content="Lorem ipsum doler sit amet adapissthere....", @hybrid_size=169, @title="My First Post">
    irb> b.hybrid_id
    => 216
    irb> b.hybrid_version
    => 1
    b.hybrid_size
    => 169

You can see that we have added instance variables to the object containing the hybrid_id, hybrid_version and hybrid_size


    irb> BlogPost.find(:all)
    => [#<BlogPost:0xb7b8a990 @viewable_range=#<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>, @hybrid_id=216, @hybrid_version=2, @hybrid_saved=true, @content="Lorem ipsum doler sit amet adapissthere....", @hybrid_size=169, @title="My First Post">]
    irb> BlogPost.find(216)
    => #<BlogPost:0xb7b6c490 @viewable_range=#<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>, @hybrid_id=216, @hybrid_version=2, @hybrid_saved=true, @content="Lorem ipsum doler sit amet adapissthere....", @hybrid_size=169, @title="My First Post">        
    irb> BlogPost.find(:first)
    => #<BlogPost:0xb7b6c490 @viewable_range=#<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>, @hybrid_id=216, @hybrid_version=2, @hybrid_saved=true, @content="Lorem ipsum doler sit amet adapissthere....", @hybrid_size=169, @title="My First Post">
    irb> BlogPost.find(:first).content
    => "Lorem ipsum doler sit amet adapissthere...." 
    irb> BlogPost.find(:first).viewable_range
    => #<Date: 4908933/2,0,2299161>..#<Date: 4909317/2,0,2299161>
    irb> b.delete
    => true
    irb> BlogPost.find(:all)
    => []
    irb>exit

For basic usage see the basic usage page

Hybrid Objects Contained within Hybrid Objects(....Man!)

(listing tutorial2.rb)


    require '../lib/hybriddb'
    HybridDB::Connection.set_adapter(MySQLAdapter.new({:host=> 'myservername', :user => 'sqluser', :password => 'password', :database => 'hybriddb' }))
    #add 2 hybrid models
    class Parent < HybridObject 
        attr_accessor :name, :age, :child
    end
    class Child < HybridObject 
        attr_accessor :name, :age, :parent
    end

This tutorial shows how we can have Hybrid objects as properties of other Hybrid objects, and that they can be found individually, without navigating an object graph.

start irb and load the tutorial

paul:~/dev/HybridDB/tutorial$ irb -r tutorial2.rb


    irb> p = Parent.new
    => #<Parent:0xb7aac208>
    irb> p.name = "Pappy" 
    => "Pappy" 
    irb> p.age = 30
    => 30
    irb> c = Child.new
    => #<Child:0xb7aa6678>
    irb> c.name = "Lil babee" 
    => "Lil babee" 
    irb> c.age = 0.5
    => 0.5

We have a parent and a child, lets link them.


    irb> p.child = c
    => #<Child:0xb7aa6678 @name="Lil babee", @age=0.5>
    irb> c.parent = p
    => #<Parent:0xb7aac208 @child=#<Child:0xb7aa6678 @name="Lil babee", @age=0.5, @parent=#<Parent:0xb7aac208 ...>>, @name="Pappy", @age=30>
    irb> p.save
    => true

We have called save on the parent. This also saves any contained Hybrid objects.


    irb> p
    => #<Parent:0xb7aac208 @child=#<Child:0xb7aa6678 @name="Lil babee", @hybrid_id="764", @age=0.5, @hybrid_version=1, @hybrid_saved=true, @hybrid_size=119, @parent=#<Parent:0xb7aac208 ...>>, @name="Pappy", @hybrid_id="763", @age=30, @hybrid_version=1, @hybrid_saved=true, @hybrid_size=112>
    irb> c
    => #<Child:0xb7aa6678 @name="Lil babee", @hybrid_id="764", @age=0.5, @hybrid_version=1, @hybrid_saved=true, @hybrid_size=119, @parent=#<Parent:0xb7aac208 @child=#<Child:0xb7aa6678 ...>, @name="Pappy", @hybrid_id="763", @age=30, @hybrid_version=1, @hybrid_saved=true, @hybrid_size=112>>
    irb> p.child.hybrid_id
    => "764" 
    irb> p.hybrid_id
    => "763" 

Both objects have been saved, and have their hybrid characteristics. Both can be loaded using the find class method. We’ll clear the variables.


    irb> p=nil
    => nil
    irb> c=nil
    => nil

Find the parent again by it’s hybrid_id


    irb> p=Parent.find(763)
    => #<Parent:0xb7a901e8 @child=#<Stub Child:764 *Unloaded*>, @name="Pappy", @hybrid_id=763, @age=30, @hybrid_version=2, @hybrid_saved=true, @hybrid_size=112>
    irb> p.child
    => #<Stub Child:764 *Unloaded*>

The child shows up as an unloaded Child stub, confusing, but it acts just like a child and will load on any access.

Note: I made stub objects appear when inspecting (instead of the actual contents) to get around the problem of a recursive display of a parent with a child, whose parent was a child whose parent….. making irb meltdown

Lets query it’s properties.


    irb> p.child.name
    => "Lil babee" 
    irb> p.child.parent.name
    => "Pappy" 
    irb> p=nil
    => nil

We can also load the child object and get to the parent from there.


    irb> c=Child.find(:first)
    => #<Child:0xb7a81634 @name="Lil babee", @hybrid_id=764, @age=0.5, @hybrid_version=2, @hybrid_saved=true, @hybrid_size=119, @parent=#<Stub Parent:763 *Unloaded*>>
    irb> c.parent.name
    => "Pappy" 
    irb> c.parent
    => #<Stub Parent:763 *Loaded*>

Lets change the parents name through the child.


    irb> c.parent.name = "Pappy O Daniels" 
    => "Pappy O Daniels" 
    irb> c.parent.save
    => true
    irb> p=Parent.find(:first)
    => #<Parent:0xb7a6f574 @child=#<Stub Child:764 *Unloaded*>, @name="Pappy O Daniels", @hybrid_id=764, @age=30, @hybrid_version=3, @hybrid_saved=true, @hybrid_size=121>
    irb> p.name
    => "Pappy O Daniels" 

For an explanation of how hybrid objects are stored and linked to other objects see The Concept

Indexing

(listing tutorial3.rb)


    require '../lib/hybriddb'
    HybridDB::Connection.set_adapter(MySQLAdapter.new({:host=> 'myservername', :user => 'sqluser', :password => 'password', :database => 'hybriddb' }))
    #declare a model and index a property
    class User < HybridObject 
        indexes :name
        attr_accessor :name, :department, :is_admin
    end

This tutorial demonstrates the indexing capabilities of HybridDB. The above model has an index built on it’s name property, users will be able to be found by their name quickly using the database servers indexes.

load the file in irb

paul:~/dev/HybridDB/tutorial$ irb -r tutorial3.rb


    irb> u = User.new
    => #<User:0xb7a8d09c>
    irb> u.name = "Larry Luser" 
    => "Larry Luser" 
    irb> u.department = "Prestidigitation" 
    => "Prestidigitation" 
    irb> u.is_admin = true
    => true
    irb> u.save
    => true
    irb> u=nil
    => nil

Lets find the user again, by their (indexed) name. This will use the DB servers indexing ability to find only matches we are interested in, no looping through objects.


    irb> u = User.find(:first, :conditions => {:name => "Larry Luser"})
    => #<User:0xb7ae8910 @hybrid_id=765, @department="Prestidigitation", @hybrid_version=2, @hybrid_saved=true, @hybrid_size=76, @name="Larry Luser", @is_admin=true>
    irb> u.name
    => "Larry Luser" 

We can still find the object by it’s hybrid_id


    irb> u.hybrid_id
    => 765
    irb> u = User.find(765)
    => #<User:0xb7ae3230 @hybrid_id=765, @department="Prestidigitation", @hybrid_version=2, @hybrid_saved=true, @hybrid_size=76, @name="Larry Luser", @is_admin=true>

Collections

(listing tutorial4.rb)


    require '../lib/hybriddb'
    HybridDB::Connection.set_adapter(MySQLAdapter.new({:host=> 'myservername', :user => 'sqluser', :password => 'password', :database => 'hybriddb' }))
    #declare some models
    class BlogPost < HybridObject 
        indexes :title 
        has_many :comments
        attr_accessor :title, :content
    end
    class BlogComment < HybridObject
        indexes :author
        attr_accessor :author, :content
    end

The has_many class method, adds an array-like property to a class, this property can have items (hybrid and non-hybrid) added to, and removed from it without saving the whole object graph. Links to objects are saved in the hybrid_relations table on the server

load the file in irb

paul:~/dev/HybridDB/tutorial$ irb -r tutorial4.rb


    irb> b=BlogPost.new
    => #<BlogPost:0xb7b36408>
    irb> b.comments
    => nil
    irb> b.title = "Post One" 
    => "Post One" 
    irb> b.content = "Blah blah blah" 
    => "Blah blah blah" 
    irb> b.save
    => true
    irb> b.comments
    => #<HybridCollection:0xb7b2c764 @property=:comments, @owner_id="766", @owner_class="BlogPost">

We have saved the object, it’s (indexed) collection is now accessable. Lets append comments to our post.


    irb> comment1 = BlogComment.new
    => #<BlogComment:0xb7b2a52c>
    irb> comment1.author="Playa1" 
    => "Playa1" 
    irb> comment1.content = "First Post!" 
    => "First Post!" 
    irb> b.comments << comment1
    => [#<BlogComment:0xb7b2a52c @hybrid_version=1, @hybrid_saved=true, @hybrid_size=47, @content="First Post!", @hybrid_id="767", @author="Playa1">]

We appended the comment to the collection, this will extended the object to be hybrid savable if required, and saved it along with indexed links in the hybrid_relations table Lets add another


    irb> comment2 = BlogComment.new
    => #<BlogComment:0xb7b21134>
    irb> comment2.author="Troll-ey" 
    => "Troll-ey" 
    irb> comment2.content = "I hatez Micro$oft, alt-os FTW!" 
    => "I hatez Micro$oft, alt-os FTW!" 
    irb> b.comments << comment2
    => [#<BlogComment:0xb7b21134 @hybrid_version=1, @hybrid_saved=true, @hybrid_size=68, @content="I hatez Micro$oft, alt-os FTW!", @hybrid_id="768", @author="Troll-ey">]

We can query the length. Note: if the parent object has just been read, and the collection is not loaded, this will simply query the DB for the number of collection items.


    irb> b.comments.length
    => 2

Pass a block with each.


    irb> b.comments.each{|c| puts c.content}
    First Post!
    I hatez Micro$oft, alt-os FTW!
    => [#<BlogComment:0xb7b13b24 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=47, @content="First Post!", @hybrid_id=767, @author="Playa1">, #<BlogComment:0xb7b13930 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=68, @content="I hatez Micro$oft, alt-os FTW!", @hybrid_id=768, @author="Troll-ey">]

The collection can be accessed like an array


    irb> b.comments.first
    => #<BlogComment:0xb7b13b24 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=47, @content="First Post!", @hybrid_id=767, @author="Playa1">
    irb> b.comments[1]
    => #<BlogComment:0xb7b13930 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=68, @content="I hatez Micro$oft, alt-os FTW!", @hybrid_id=768, @author="Troll-ey">

The appended items are hybrid objects in their own right


    irb> b.comments[1].hybrid_id
    => 768
    irb> BlogComment.find(768)
    => #<BlogComment:0xb7b09a5c @hybrid_version=2, @hybrid_saved=true, @hybrid_size=68, @content="I hatez Micro$oft, alt-os FTW!", @hybrid_id=768, @author="Troll-ey">
    irb> BlogComment.find(:all)
    => [#<BlogComment:0xb7b073c4 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=47, @content="First Post!", @hybrid_id=767, @author="Playa1">, #<BlogComment:0xb7b071d0 @hybrid_version=2, @hybrid_saved=true, @hybrid_size=68, @content="I hatez Micro$oft, alt-os FTW!", @hybrid_id=768, @author="Troll-ey">]