vuderacha-activekv 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +34 -0
- data/Rakefile +66 -0
- data/lib/activekv/base.rb +150 -0
- data/lib/activekv.rb +14 -0
- data/spec/base.rb +115 -0
- data/spec/simple-config.yml +3 -0
- metadata +69 -0
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= ActiveKV: Active Key/Value objects
|
2
|
+
ActiveKV provides a wrapper around Moneta to allow for active objects that can be
|
3
|
+
searched and persisted into configured key value stores. Values are serialised JSON.
|
4
|
+
|
5
|
+
== Getting Started
|
6
|
+
Create an ActiveKV configuration file:
|
7
|
+
-- config/kv.yml
|
8
|
+
development:
|
9
|
+
default:
|
10
|
+
driver: memory
|
11
|
+
test:
|
12
|
+
default:
|
13
|
+
driver: memory
|
14
|
+
|
15
|
+
Initialise the ActiveKV infrastructure:
|
16
|
+
require 'activekv'
|
17
|
+
ActiveKV::Base.configure('config/kv.yml')
|
18
|
+
|
19
|
+
Create your ActiveKV object:
|
20
|
+
class MyPersistentObj < ActiveKV::Base
|
21
|
+
key :name
|
22
|
+
attr_accessor :x
|
23
|
+
end
|
24
|
+
|
25
|
+
Store your object:
|
26
|
+
o = MyPersistentObj.new
|
27
|
+
o.name = "asdasd"
|
28
|
+
o.x = 12
|
29
|
+
o.save!
|
30
|
+
|
31
|
+
Load your object:
|
32
|
+
r = MyPersistentObj.find("asdasd")
|
33
|
+
p r.name
|
34
|
+
p r.x
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc "Run all examples"
|
5
|
+
Spec::Rake::SpecTask.new('examples') do |t|
|
6
|
+
t.spec_opts = ["-cfs"]
|
7
|
+
t.spec_files = FileList['spec/**/*.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Print specdocs"
|
11
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
12
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
13
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Generate RCov code coverage report"
|
17
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
18
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
19
|
+
t.rcov = true
|
20
|
+
t.rcov_opts = ['--exclude', 'examples']
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
######################################################
|
26
|
+
|
27
|
+
require 'rake'
|
28
|
+
require 'rake/testtask'
|
29
|
+
require 'rake/clean'
|
30
|
+
require 'rake/gempackagetask'
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
require 'fileutils'
|
33
|
+
require 'jeweler'
|
34
|
+
include FileUtils
|
35
|
+
# $:.push File.join(File.dirname(__FILE__), 'lib')
|
36
|
+
|
37
|
+
begin
|
38
|
+
require 'jeweler'
|
39
|
+
Jeweler::Tasks.new do |s|
|
40
|
+
s.name = "activekv"
|
41
|
+
s.summary = "Ruby Active Key/Value Objects."
|
42
|
+
s.description = "ActiveKV provides ActiveRecord style data storage for Key/Value stores."
|
43
|
+
s.author = "Paul Jones"
|
44
|
+
s.email = "pauljones23@gmail.com"
|
45
|
+
s.homepage = "http://github.com/vuderacha/activekv/"
|
46
|
+
|
47
|
+
s.files = %w(Rakefile README.rdoc) + Dir.glob("{bin,lib,spec}/**/*")
|
48
|
+
s.require_path = "lib"
|
49
|
+
s.add_dependency('activesupport', '>= 2.2.2')
|
50
|
+
end
|
51
|
+
rescue LoadError
|
52
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
53
|
+
end
|
54
|
+
|
55
|
+
task :test => [ :spec ]
|
56
|
+
|
57
|
+
Rake::RDocTask.new do |t|
|
58
|
+
t.rdoc_dir = 'rdoc'
|
59
|
+
t.title = "ActiveKV -- Active Key/Value Objects"
|
60
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
61
|
+
t.options << '--charset' << 'utf-8'
|
62
|
+
t.rdoc_files.include('README.rdoc')
|
63
|
+
t.rdoc_files.include('lib/activekv/*.rb')
|
64
|
+
end
|
65
|
+
|
66
|
+
CLEAN.include [ 'build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log', 'pkg', 'lib/*.bundle', '*.gem', '.config' ]
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ActiveKV
|
2
|
+
class Base
|
3
|
+
# Default the key property and configured state to empty states
|
4
|
+
@@configured = nil, false
|
5
|
+
|
6
|
+
def initialize(config = {})
|
7
|
+
apply_hash!(config)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Configures the ActiveKV support with the given configuration file
|
11
|
+
def self.configure(config_file)
|
12
|
+
# Determine our environment
|
13
|
+
env = if defined? RACK_ENV then RACK_ENV else :development end
|
14
|
+
|
15
|
+
# Load the configuration, and find the appropriate section
|
16
|
+
config = YAML::load_file(config_file)
|
17
|
+
env_config = config[env.to_s]
|
18
|
+
if env_config.nil?
|
19
|
+
raise "No configuration found for environment #{env}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Work through each store
|
23
|
+
@@stores = {}
|
24
|
+
@@default_store = nil
|
25
|
+
env_config.each do |store_name, store_config|
|
26
|
+
@@stores[store_name] = load_store(store_name, store_config)
|
27
|
+
@@default_store = @@stores[store_name] if @@default_store.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Remember that we're configured
|
31
|
+
@@configured = true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Prepares a configuration store based on the given configuration
|
35
|
+
def self.load_store(name, config)
|
36
|
+
raise "No driver specified in configuration #{store_name}" if config['driver'].nil?
|
37
|
+
|
38
|
+
# Try to create a store driver
|
39
|
+
driver_name = config.delete 'driver'
|
40
|
+
require "moneta/#{driver_name}"
|
41
|
+
class_name = Moneta.constants.find { |c| c.to_s.downcase == driver_name.gsub(/_/, "").downcase }
|
42
|
+
raise "No implementation found for driver #{config['driver']}" if class_name.nil?
|
43
|
+
driver_class = Moneta.const_get(class_name)
|
44
|
+
driver_class.new transform_hash(config)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Requests that the ActiveKV forget its configuration
|
48
|
+
def self.unconfigure!
|
49
|
+
@@configured = false
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self # Class Methods
|
53
|
+
# Sets the property that will be used to work out the key for the item
|
54
|
+
def key(key_prop = nil)
|
55
|
+
if key_prop.nil?
|
56
|
+
return nil if not defined? @key_prop
|
57
|
+
return @key_prop
|
58
|
+
end
|
59
|
+
|
60
|
+
@key_prop = key_prop
|
61
|
+
attr_accessor key_prop
|
62
|
+
end
|
63
|
+
|
64
|
+
# Changes the pattern used to build keys
|
65
|
+
def key_pattern(pattern = nil)
|
66
|
+
if pattern.nil?
|
67
|
+
return ":class/:key" if not defined? @key_pattern or @key_pattern.nil?
|
68
|
+
return @key_pattern
|
69
|
+
end
|
70
|
+
|
71
|
+
@key_pattern = pattern
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sets the store that is used for instances of this class
|
75
|
+
def store(name)
|
76
|
+
@store = name
|
77
|
+
end
|
78
|
+
|
79
|
+
# Finds a record with the given key
|
80
|
+
def find(key)
|
81
|
+
# Create the key and lookup the value
|
82
|
+
key_val = create_key key
|
83
|
+
found_val = store_instance[key_val]
|
84
|
+
return nil if found_val.nil? or found_val == {}
|
85
|
+
# Check if result is {}, because the memory implementation returns this as a default value
|
86
|
+
# when the real value isn't found
|
87
|
+
|
88
|
+
# Decode the found value, then apply each property in the JSON to the object
|
89
|
+
vals = ActiveSupport::JSON.decode(found_val)
|
90
|
+
result = new
|
91
|
+
result.apply_hash!(vals)
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
# Creates a store key based on the pattern and provided key
|
96
|
+
def create_key(key)
|
97
|
+
key_pattern.gsub(':class', self.name).gsub(':key', key)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieves an instance of the store that is used for this class
|
101
|
+
def store_instance
|
102
|
+
if defined? @store and defined? @@stores[@store] then
|
103
|
+
@@stores[@store]
|
104
|
+
else
|
105
|
+
@@default_store
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Saves the record to the appropriate datastore
|
111
|
+
def save!
|
112
|
+
# Ensure that we have everything we need
|
113
|
+
raise NotConfiguredError unless @@configured
|
114
|
+
raise NoKeySpecifiedError if self.class.key.nil?
|
115
|
+
raise NilKeyError if send(self.class.key).nil?
|
116
|
+
|
117
|
+
# Transform ourselves to json, and get our key
|
118
|
+
new_val = to_json
|
119
|
+
key_val = self.class.create_key send(self.class.key)
|
120
|
+
|
121
|
+
# Retrieve the appropriate store, and save
|
122
|
+
self.class.store_instance[key_val] = new_val
|
123
|
+
end
|
124
|
+
|
125
|
+
def apply_hash!(props)
|
126
|
+
props.each do |k, v|
|
127
|
+
send("#{k}=", v)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Transforms a hash so it also indexes by symbol
|
132
|
+
def self.transform_hash h
|
133
|
+
result = {}
|
134
|
+
h.each do |k,v|
|
135
|
+
result_v = if v.is_a? Hash then transform_hash(v) else v end
|
136
|
+
|
137
|
+
result[k] = result_v
|
138
|
+
result[k.to_sym] = result_v
|
139
|
+
end
|
140
|
+
result
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class NotConfiguredError < Exception
|
145
|
+
end
|
146
|
+
class NilKeyError < Exception
|
147
|
+
end
|
148
|
+
class NoKeySpecifiedError < Exception
|
149
|
+
end
|
150
|
+
end
|
data/lib/activekv.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
require 'moneta'
|
4
|
+
rescue LoadError
|
5
|
+
puts "You need the moneta gem to use ActiveKV"
|
6
|
+
exit
|
7
|
+
end
|
8
|
+
begin
|
9
|
+
require 'active_support'
|
10
|
+
rescue LoadError
|
11
|
+
puts "You need the ActiveSupport gem to use ActiveKV"
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
require 'activekv/base'
|
data/spec/base.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'activekv'
|
3
|
+
|
4
|
+
RACK_ENV = :test unless defined? RACK_ENV
|
5
|
+
|
6
|
+
describe "ActiveKV::Base" do
|
7
|
+
before do
|
8
|
+
ActiveKV::Base.unconfigure!
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should be inheritable" do
|
12
|
+
class InheritanceTest < ActiveKV::Base
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should allow a configuration file to be provided" do
|
17
|
+
ActiveKV::Base.configure File.join(File.dirname(__FILE__), 'simple-config.yml')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should fail to save an object if not configured" do
|
21
|
+
class Unconfigured < ActiveKV::Base
|
22
|
+
end
|
23
|
+
|
24
|
+
u = Unconfigured.new
|
25
|
+
begin
|
26
|
+
u.save!
|
27
|
+
raise "Should not have allowed an instance to be saved when not configured"
|
28
|
+
rescue ActiveKV::NotConfiguredError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should make the key available on the class" do
|
33
|
+
class WithKeyA < ActiveKV::Base
|
34
|
+
key :a
|
35
|
+
end
|
36
|
+
class WithKeyB < ActiveKV::Base
|
37
|
+
key :b
|
38
|
+
end
|
39
|
+
|
40
|
+
WithKeyA.key.should == :a
|
41
|
+
WithKeyB.key.should == :b
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "ActiveKV::Base when configured" do
|
46
|
+
before do
|
47
|
+
ActiveKV::Base.unconfigure!
|
48
|
+
ActiveKV::Base.configure File.join(File.dirname(__FILE__), 'simple-config.yml')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should fail to save an object if no key is specified" do
|
52
|
+
class NoKey < ActiveKV::Base
|
53
|
+
end
|
54
|
+
|
55
|
+
u = NoKey.new
|
56
|
+
begin
|
57
|
+
u.save!
|
58
|
+
raise "Should not have allowed an instance to be saved when it does not specify a key"
|
59
|
+
rescue ActiveKV::NoKeySpecifiedError
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should fail to save an object if key is nil" do
|
64
|
+
class WithKey < ActiveKV::Base
|
65
|
+
key :name
|
66
|
+
end
|
67
|
+
|
68
|
+
u = WithKey.new
|
69
|
+
begin
|
70
|
+
u.save!
|
71
|
+
raise "Should not have allowed an instance to be saved when key value is nil"
|
72
|
+
rescue ActiveKV::NilKeyError
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow an object to be saved if configured and a key is specified" do
|
77
|
+
class WithKey < ActiveKV::Base
|
78
|
+
key :name
|
79
|
+
end
|
80
|
+
|
81
|
+
u = WithKey.new
|
82
|
+
u.name = 'TestName'
|
83
|
+
u.save!
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not find an object that has not been created" do
|
87
|
+
class NotFound < ActiveKV::Base
|
88
|
+
end
|
89
|
+
|
90
|
+
NotFound.find('missing').should be_nil
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should find an object that has previously been saved" do
|
94
|
+
class Found < ActiveKV::Base
|
95
|
+
key :name
|
96
|
+
end
|
97
|
+
|
98
|
+
f = Found.new
|
99
|
+
f.name = 'myname'
|
100
|
+
f.save!
|
101
|
+
|
102
|
+
Found.find('myname').name.should == 'myname'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should allow an object to be created with a hash of parameters" do
|
106
|
+
class WithSomeParams < ActiveKV::Base
|
107
|
+
key :name
|
108
|
+
attr_accessor :another_prop
|
109
|
+
end
|
110
|
+
|
111
|
+
p = WithSomeParams.new :name => 'asr', :another_prop => 'world'
|
112
|
+
p.name.should == 'asr'
|
113
|
+
p.another_prop.should == 'world'
|
114
|
+
end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vuderacha-activekv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Jones
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-22 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.2.2
|
24
|
+
version:
|
25
|
+
description: ActiveKV provides ActiveRecord style data storage for Key/Value stores.
|
26
|
+
email: pauljones23@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
files:
|
34
|
+
- Rakefile
|
35
|
+
- README.rdoc
|
36
|
+
- lib/activekv
|
37
|
+
- lib/activekv/base.rb
|
38
|
+
- lib/activekv.rb
|
39
|
+
- spec/base.rb
|
40
|
+
- spec/simple-config.yml
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/vuderacha/activekv/
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --inline-source
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.2.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 2
|
67
|
+
summary: Ruby Active Key/Value Objects.
|
68
|
+
test_files: []
|
69
|
+
|