0.0
No commit activity in last 3 years
No release in over 3 years
Store and retrieve Ruby objects without thinking twice about how they are stored. Designed for maximum in flexibility and intuitive interface.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Persistence

http://rubygems.org/gems/persistence

Summary

Persistence layer designed to take advantage of Ruby's object model. The back-end is abstracted so that many different adapters can be created.

Description

Store and retrieve Ruby objects without thinking twice about how they are stored. Designed for maximum in flexibility and intuitive interface.

the idea is to make data storage native to ruby to the OO interface that is already present b/c we already have a schema to describe data so why expect it to be declared again for the DB? so persistence focuses not on mapping but instead on describing how you want your object persistence to behave

Install

Persistence is broken into the core 'persistence' gem and additional adapters. Persistence provides the Ruby interface, adapters provide the storage implementation.

Pre-Beta Warning

This code is very early pre-beta non-production code. It should be near production ready but is offered with no promises.

If you find something broken, please submit a failing test or code snippet and we will work on getting it fixed as soon as possible.

Persistence

  • sudo gem install persistence

Adapters

The persistence gem has a single "Mock" adapter built in. It also has abstract interfaces to assist in creating new adapters.

To actually use Persistence you will want to install a real adapter. Currently, two are available:

Usage

Before Anything Else

Enable a port with an adapter:

adapter_instance = Persistence::Adapter::Mock.new
port_name = :some_port

Persistence.enable_port( port_name, adapter_instance )

The Basics

Enable a class for persistence:

require 'persistence'

class SomeClass
  include Persistence
end

Declare persistent properties:

class SomeClass

  # non-atomic properties will only update when explicitly told to do so via :persist!
  attr_non_atomic_accessor :name, :some_non_atomic_property, :some_other_non_atomic_property

  # atomic properties will update as they change
  attr_atomic_accessor :some_atomic_property, :some_other_atomic_property

end

Assign some data to our object. First, symbols and string (or other basic types):

object = SomeClass.new
object.name = :some_name

# Basic "flat" objects can be stored without declaring anything else
object.some_non_atomic_property = 'some string'

But let's also try out storing nested objects:

# Other objects need to be enabled for persistence:
class SomeOtherClass
  include Persistence
  attr_atomic_accessor :some_atomic_property
end

object.some_other_non_atomic_property = SomeOtherClass.new

Now we want to store the object data for the first time:

# Call :persist! to get our Object Persistence ID and store non-atomic data.
object.persist!

Once we have our Object Persistence ID atomic data will be instantly updated in the storage port as it changes.

At this point we can get a second copy of our object from the storage port:

# Retrieving by Object Persistence ID isn't very useful, but we start with it for the basics
object_copy = SomeClass.persist( object.persistence_id )

# object_copy.should == object
# object_copy.name.should == object.name
# object_copy.some_non_atomic_property.should == object.some_non_atomic_property
# object_copy.some_other_non_atomic_property.should == object.some_other_non_atomic_property
# object_copy.some_atomic_property.should == object.some_atomic_property
# object_copy.some_other_atomic_property.should == object.some_other_atomic_property

Non-atomic properties do not update until :persist! is called:

object.some_non_atomic_property = 'some other string'
# object_copy.some_non_atomic_property.should_not == object.some_non_atomic_property

Atomic properties update immediately (as long as object has a Persistence ID):

object.some_atomic_property = 'some value'
# object_copy.some_atomic_property.should == object.some_atomic_property

object.some_other_atomic_property = SomeOtherClass.new
# object_copy.some_other_atomic_property.should == object.some_other_atomic_property

Indexes

We could retrieve our object simply by its Object Persistence ID (a unique identifier created for each object), but most likely we will want to retrieve the object by way of some arbitrary descriptor (for instance a username).

To do this, we declare an index.

There are three types of indexes on objects: explicit indexes, block indexes, and attribute indexes. There is also a fourth type of index - the bucket index - but it is really just a block index owned by a bucket instead of by an object.

Once we have an indexed key, the object is retrieved in the same way regardless of index type:

# if we already have an index called :name with an indexed value :some_name then we can retrieve the object:
object_copy = SomeClass.persist( :name, :some_name )

Indexes can be unique or permit duplicates. They can be unordered or ordered. Ordered indexes are only partially implemented at this point and will be available soon.

Unique indexes will throw an exception in the event a duplicate would be created.

Explicit Indexes

Explicit indexes require that a key be provided explicitly. Any number of keys can refer to the same object.

class SomeClass
  explicit_index :name
end

object = SomeClass.new
object.name = :some_name
object.some_atomic_property = :some_value
object.persist!( :name, object.name )

object_copy = SomeClass.persist( :name, :some_name )

Block Indexes

Block indexes take a block, which they then run on each object in order to generate index key(s).

class SomeClass
  block_index :name do |object|
    object.name
  end
end

object = SomeClass.new
object.name = :some_name
object.some_atomic_property = :some_value
object.persist!

object_copy = SomeClass.persist( :name, :some_name )

Attribute Indexes

Attribute indexes refer to a getter/setter method on the object. This operates on the same principle as Ruby's attr_accessor.

class SomeClass
  attr_index :name
end

object = SomeClass.new
object.name = :some_name
object.some_atomic_property = :some_value
object.persist!

object_copy = SomeClass.persist( :name, :some_name )

Bucket Indexes

Bucket indexes are block indexes owned by a bucket instead of an object. This permits multiple object types to be stored in the same bucket and to be indexed by the same block.

class SomeClass
  instance_persistence_bucket.create_index( :name ) do |object|
    object.name
  end
end

object = SomeClass.new
object.name = :some_name
object.some_atomic_property = :some_value
object.persist!

object_copy = SomeClass.persist( :name, :some_name )

Outline of General Premises

Storage Ports and Adapters

  • Storage is managed by a "port"
  • Objects are stored in "buckets" owned by the port.
  • Objects are stored by unique Global ID, which can be anything other than a {::Hash}. Currently adapters use {::Integer}s for Global IDs.
  • Objects can be indexed, which allows any object to be used as a reference to the primary storage of the object's properties (by Global ID).
  • Actual storage is provided by an "adapter", which implements the specific terms by which data is stored (ie a database, in files, etc.).

Classes and Instances

  • Persistence-enabled Classes act as controllers for their instances.
  • Data storage takes place through instances.
  • Data retrieval takes place through Class singleton.

Properties

  • Properties are either atomic or non-atomic.
  • Objects can be nested to any degree.

License

(The MIT License)

Copyright (c) 2012, Asher, Ridiculous Power

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.