Skip to content

Commit

Permalink
Clean up library by removing dependencies.
Browse files Browse the repository at this point in the history
Allow backend to be instantiated before initializing hashback.
  • Loading branch information
jsl committed Jul 4, 2009
1 parent 4618661 commit 2deaf02
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 94 deletions.
20 changes: 12 additions & 8 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
= HashBack

HashBack is a simple Object-hash mapping (OHM) system for Ruby. It allows for serializable classes to be saved,
retrieved and deleted from any key-value store supported by {Moneta}[http://github.com/wycats/moneta/tree/master]
(a unified interface to key-value systems).
retrieved and deleted from any key-value store. It works particulary well with, but has no dependency on
{Moneta}[http://github.com/wycats/moneta/tree/master] (a unified interface to key-value systems).

== Quick Start

Expand All @@ -21,7 +21,7 @@ UUIDs created by the assaf-uuid gem, so we'll include that as well.
Below we create a simple class that is serializable to HashBack.

class Elephant
HashBack::Resource.setup(self, :uuid, 'Moneta::Memory')
HashBack::Resource.setup(self, :uuid, Moneta::Memory.new)

attr_accessor :uuid, :name

Expand All @@ -46,6 +46,12 @@ When you're sick of Dumbo and want to get rid of him:

Note that at this point the data is still available in the instance variables for Dumbo, but the persisted form of him is gone.

== HashBack::Backend

A lightweight class called HashBack::Backend is included with the distribution of this gem. This class wraps common Hash-like
getter and setter methods with ones that include a namespace. This may be helpful to you if you're persisting many objects
to the same backend data store. Please see the documentation for HashBack::Backend for more information.

== Detailed usage

Generally, HashBack should work with any class that can be serialized. You can decide which key-value storage system
Expand All @@ -57,7 +63,7 @@ follows:
require 'hashback'

class Foo
HashBack::Resource.setup(self, :id, 'Moneta::Memcache', :server => 'localhost:1978')
HashBack::Resource.setup(self, :id, HashBack::Backend.new('Foo', Moneta::Memcache.new(:server => 'localhost:1978')))
end

This initializes a class with a backend storage in a Tokyo Tyrant server. The serialized forms of objects that are
Expand All @@ -77,13 +83,11 @@ together a lightweight implementation with methods for saving and accessing simi
The following features are not present in the current library, but may be useful:

* A system of callbacks
* A system for associating objects, perhaps constrained to objects that have a 1 - 1 mapping (since it's not entirely
intuitive what the structures or algorithms would be for mainting integrity with higher levels of mapping between
objects).
* A system for associating objects, perhaps constrained to objects that have a 1 - 1 mapping

== Feedback

Please write the author if you have any questions or feedbacks about this library.
Please write the author if you have any questions or feedback regarding this library.

== Author

Expand Down
21 changes: 13 additions & 8 deletions hashback.gemspec
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
Gem::Specification.new do |s|
s.name = "hashback"
s.version = "0.0.2.5"
s.version = "0.0.3"
s.date = "2009-05-13"
s.summary = "Generic tool for writing namespaced key-value data to a variety of hash-type systems"

s.summary = "Ruby Object-Hash Mapping system (OHM)"

s.description = <<-EOF
HashBack makes your ruby class persistent by adding methods which will save and retrieve it from a
backend key-value store. Useful when you have objects that should respond to #save and #fetch (as
a class method). Works well with the Moneta gem, which automatically serializes objects before they
are saved and after they are retrieved, but functions with any key-value storage system.
EOF

s.email = "justin@phq.org"
s.homepage = "http://github.com/jsl/hashback"
s.description = "Wrapper around Moneta that facilitates using the key-value store as a backend for applications requiring namespacing"
s.description = "HashBack"
s.has_rdoc = true
s.authors = ["Justin Leitgeb"]
s.files = [
Expand All @@ -32,9 +41,5 @@ Gem::Specification.new do |s|
'--main', 'README.rdoc',
'--line-numbers',
'--inline-source'
]

s.add_dependency("jsl-moneta")
s.add_dependency("activesupport")
s.add_dependency("assaf-uuid")
]
end
2 changes: 2 additions & 0 deletions init.rb
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# In case we're used as a Rails plugin, load the library.

require 'hashback'
1 change: 0 additions & 1 deletion lib/hashback.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require 'moneta'
require 'activesupport'

Dir[File.join(File.dirname(__FILE__), 'hashback', '*.rb')].each do |f|
Expand Down
45 changes: 12 additions & 33 deletions lib/hashback/backend.rb
Original file line number Diff line number Diff line change
@@ -1,54 +1,33 @@
module HashBack

# Delegates methods to Moneta (a unified interface to key-value storage systems) after adding
# a key for proper namespacing. Initializes and loads the proper Moneta class based on input
# options, and delegates setting and retrieval to the Moneta class responsible for storage.
# Proxy class over a key-value storage object which adds a namespace to keys. Useful if you
# have a number of different things that are saving to the same object space. Of course, then
# you may want to consider just opening up new key-value stores. This class may safely be
# ignored if not needed by your application.
class Backend

def initialize(namespace, moneta_klass, options = { })
# Backend accepts a namespace which will be added to all keys on retrieval and
# setting, and a backend that responds to Hash-like methods. Moneta classes
# see the Moneta gem) work well as the backends to this class.
def initialize(namespace, backend)
@namespace = namespace
@options = options
@moneta = initialize_moneta_klass(moneta_klass)
@backend = backend
end

def [](key)
@moneta[key_name_for(key)]
@backend[key_name_for(key)]
end

def []=(key, value)
@moneta[key_name_for(key)] = value
@backend[key_name_for(key)] = value
end

def delete(key)
@moneta.delete(key_name_for(key))
@backend.delete(key_name_for(key))
end

private

def initialize_moneta_klass(klass)
require_moneta_library_for(klass)
load_moneta_klass(klass)
end

def require_moneta_library_for(klass)
require_klass(klass.to_s.gsub(/::/, '/').downcase)
end

def load_moneta_klass(klass)
klass_const = klass.respond_to?(:constantize) ? klass.constantize : klass
moneta = klass_const.new(@options)

# The options hash would have messed up default Hash initialization to return an empty hash
# when the key was not found. Revert this case by setting the default to nil if the object
# responds to this method.
moneta.default = nil if moneta.respond_to?(:default)
moneta
end

def require_klass(klass)
require klass
end

def key_name_for(key)
[ @namespace, key ].join('-')
end
Expand Down
27 changes: 14 additions & 13 deletions lib/hashback/resource.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
module HashBack

# HashBack::Resource is an Object-Hash Mapping (OHM) tool for Ruby. It is able to map Ruby objects
# to any of the backends supported by Moneta, a unified interface to key-value storage systems.
# HashBack::Resource is an Object-Hash Mapping (OHM) tool for Ruby. It uses a Hash-like object
# as the persistent resource, which can be given on HashBack::Resource initialization.
class Resource

# Configures the persistent backend for this object. Configuration options:
#
# * +source+ - the class to be persisted
# * +key_method+ - a symbol representing the method that will return a unique identifier
# for this object when called on the instance
# * +moneta_klass+ - a String representation or class constant of the Moneta class used to store this object
# * +moneta_options+ - an (optional) hash which is passed directly to the moneta backend for configuration
def self.setup(source, key_method_sym, moneta_klass, moneta_options = {})
source.__send__(:class_variable_set, :@@_backend, HashBack::Backend.new(source.to_s, moneta_klass, moneta_options))
# * +backend+ - a Hash-like Object (Moneta works well) for persisting this resource.
def self.setup(source, key_method_sym, backend)
source.__send__(:class_variable_set, :@@_backend, backend)
source.__send__(:class_variable_set, :@@_key_method_sym, key_method_sym)

source.__send__(:include, InstanceMethods)
Expand All @@ -23,25 +22,27 @@ module InstanceMethods

# Saves the serialized form of this object to the configured backend store.
def save
_hashback_backend[_hashback_id_key] = self
hashback_backend[_hashback_id_key] = self
end

# Destroy the persisted copy of this object.
def destroy
_hashback_backend.delete(_hashback_id_key)
hashback_backend.delete(_hashback_id_key)
end

## Methods we try to hide, because we're just sneaky like that.

def _hashback_backend
# Convenience method for accessing the backend without having to get to the
# obscurely-named class variable in which it's stored.
def hashback_backend
self.class.__send__(:class_variable_get, :@@_backend)
end


## Methods we try to hide, because we're just sneaky like that.

def _hashback_id_key
self.__send__(self.class.__send__(:class_variable_get, :@@_key_method_sym))
end

private :_hashback_backend, :_hashback_id_key
private :_hashback_id_key
end

module ClassMethods
Expand Down
32 changes: 2 additions & 30 deletions spec/hashback/backend_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
before do
@mock_moneta = mock('moneta')
@mock_moneta.stubs(:keys).returns(['keyname'])
@moneta_klass = "Moneta::Memory"
@b = HashBack::Backend.new('foo', @moneta_klass, { })
@b.instance_variable_set(:@moneta, @mock_moneta)
@b = HashBack::Backend.new('foo', @mock_moneta)
@b.stubs(:key_name_for).returns('keyname')
end

Expand All @@ -31,31 +29,5 @@
@b.delete('foo')
end
end
end

describe "#initialize_moneta_klass" do
it "should call require_moneta_library_for and load_moneta_klass" do
b = HashBack::Backend.new('foo', @moneta_klass, { })
b.expects(:require_moneta_library_for).with(@moneta_klass)
b.expects(:load_moneta_klass).with(@moneta_klass)
b.__send__(:initialize_moneta_klass, @moneta_klass)
end
end

describe "#require_moneta_library_for" do
it "should require the class given" do
@b.expects(:require_klass).with('moneta/memory')
@b.__send__(:require_moneta_library_for, "Moneta::Memory")
end
end

describe "#load_moneta_klass" do
it "should load the klass without error" do
require 'moneta/memory'

lambda {
@b.__send__(:load_moneta_klass, "Moneta::Memory")
}.should_not raise_error
end
end
end
end
2 changes: 1 addition & 1 deletion spec/hashback/resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'uuid'

class Orange
HashBack::Resource.setup(self, :uuid, 'Moneta::Memory', { })
HashBack::Resource.setup(self, :uuid, { })

attr_accessor :uuid

Expand Down

0 comments on commit 2deaf02

Please sign in to comment.