vuderacha-activekv 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ test:
2
+ default:
3
+ driver: memory
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
+