riak-shim 0.0.12 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/FUTURE_WORK.md +28 -0
- data/Gemfile +6 -0
- data/Guardfile +16 -5
- data/README.md +22 -18
- data/lib/riak-shim/persistable.rb +60 -19
- data/lib/riak-shim/store.rb +16 -1
- data/lib/riak-shim/version.rb +1 -1
- data/riak-shim.gemspec +6 -6
- data/spec/persistable_spec.rb +11 -7
- metadata +3 -2
data/.gitignore
CHANGED
data/FUTURE_WORK.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
## The future - mkb
|
3
|
+
|
4
|
+
Riak-shim will follow [semantic versioning](http://semver.org) as best I can
|
5
|
+
manage.
|
6
|
+
|
7
|
+
My plan is to keep tweaking riak-shim to suit my own needs for two internal
|
8
|
+
projects I am working on. The first of those goes into production shortly,
|
9
|
+
so I am declaring a 1.0 release. From here on out, minor version releases
|
10
|
+
might introduce new functionality, but won't break what is already here.
|
11
|
+
|
12
|
+
For the past few weeks riak-shim has been fairly stable and hasn't needed
|
13
|
+
significant changes. I am hopeful that we can avoid incompatable updates for
|
14
|
+
a while.
|
15
|
+
|
16
|
+
The area most likely to change is the handling of secondary indexes since I am
|
17
|
+
unhappy with the current interface.
|
18
|
+
|
19
|
+
## TODOS
|
20
|
+
|
21
|
+
- Examples directory
|
22
|
+
- Keep working on docs
|
23
|
+
- Keep expanding tests
|
24
|
+
- find less horrible way to deal with index names
|
25
|
+
- find less horrible way to deal with key accessor
|
26
|
+
- nice error message when can't connect to db
|
27
|
+
|
28
|
+
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,7 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
|
2
|
+
group :rspec do
|
3
|
+
guard 'rspec', :version => 2, :cli => "--color --format nested" do
|
4
|
+
watch(%r{^spec/(.+)_spec\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
5
|
+
watch(%r{^spec/support/*}) { 'spec' }
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { 'spec' }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
end
|
6
9
|
end
|
7
10
|
|
11
|
+
group :yard do
|
12
|
+
guard 'yard', :port => '8088' do
|
13
|
+
watch(%r{lib/.+\.rb})
|
14
|
+
watch(%r{README.md})
|
15
|
+
watch(%r{FUTURE_WORK.md})
|
16
|
+
watch(%r{LICENSE})
|
17
|
+
end
|
18
|
+
end
|
data/README.md
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# Riak Shim
|
2
2
|
|
3
|
-
A teeny shim between your code and the riak-client gem. Reads database
|
4
|
-
out of config/database.yml and derives bucket names from your
|
5
|
-
appropriate prefix.
|
3
|
+
A teeny shim between your code and the riak-client gem. Reads database
|
4
|
+
configuration out of config/database.yml and derives bucket names from your
|
5
|
+
class names and an appropriate prefix.
|
6
6
|
|
7
|
-
Riak is a database from the good people at Basho. Check it out:
|
7
|
+
Riak is a database from the good people at Basho. Check it out:
|
8
|
+
http://basho.com/products/riak-overview/
|
8
9
|
|
9
10
|
[![Build Status](https://secure.travis-ci.org/mkb/riak-shim.png?branch=master)](http://travis-ci.org/mkb/riak-shim) [![Dependency Status](https://gemnasium.com/mkb/riak-shim.png)](https://gemnasium.com/mkb/riak-shim) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mkb/riak-shim)
|
10
11
|
|
@@ -35,10 +36,14 @@ Create a config/database.yml containing the details of your Riak setup like so:
|
|
35
36
|
<<: *default
|
36
37
|
bucket_prefix: myapp_test_
|
37
38
|
|
39
|
+
production:
|
40
|
+
<<: *default
|
41
|
+
bucket_prefix: myapp_production_
|
42
|
+
|
38
43
|
`bucket_prefix` will be prefixed to each bucket name, allowing you to point
|
39
|
-
multiple applications (or multiple copies of the same application) at a
|
40
|
-
|
41
|
-
|
44
|
+
multiple applications (or multiple copies of the same application) at a single
|
45
|
+
Riak install. During development, this prevents you from stepping on your own
|
46
|
+
toes.
|
42
47
|
|
43
48
|
## Converting a model to use Riak
|
44
49
|
|
@@ -52,7 +57,7 @@ Then, write a #to_hash method which returns a hash representing your object
|
|
52
57
|
|
53
58
|
def to_hash
|
54
59
|
# Return hashified version of your class
|
55
|
-
{ 'foo' =>
|
60
|
+
{ 'foo' => @foo, 'bar' => @bar }
|
56
61
|
end
|
57
62
|
|
58
63
|
You'll use Class#from_hash to create an instance from the hash which was
|
@@ -70,15 +75,16 @@ You can now save instances of your class by calling `#save` and later retrieve
|
|
70
75
|
them from Riak by calling `.for_key`:
|
71
76
|
|
72
77
|
an_instance.save
|
78
|
+
key = an_instance.key
|
73
79
|
retrieved_copy = YourClass.for_key(key)
|
74
80
|
|
75
81
|
### Secondary indexes
|
76
82
|
|
77
83
|
Secondary indexes in Riak allow you to query based on the contents of a
|
78
|
-
particular field. If you'd like to look up your data by the contents
|
79
|
-
|
80
|
-
|
81
|
-
|
84
|
+
particular field. If you'd like to look up your data by the contents of
|
85
|
+
fields, define `#fields_to_index` and return the names of any fields you wish
|
86
|
+
to query on. When you #save an instance of YourClass, riak-shim will populate
|
87
|
+
a secondary index for that field.
|
82
88
|
|
83
89
|
def fields_to_index
|
84
90
|
# Return an Array of hash keys you would like placed into a secondary index.
|
@@ -88,7 +94,7 @@ riak-shim will populate a secondary index for that field.
|
|
88
94
|
The `for_index` method retrieves all records whose value for the given index
|
89
95
|
matches:
|
90
96
|
|
91
|
-
YourClass.for_index(
|
97
|
+
YourClass.for_index('foo_bin', 'some foo')
|
92
98
|
|
93
99
|
...where `index_name` is what you defined in `fields_to_index` plus the suffix
|
94
100
|
"_bin" .
|
@@ -100,15 +106,13 @@ Return value is an Array of instances of your class matching the query.
|
|
100
106
|
|
101
107
|
## Contributing
|
102
108
|
|
109
|
+
For a look at what work is needed (at least according to me), see
|
110
|
+
[FUTURE_WORK.md](https://github.com/mkb/riak-shim/blob/master/FUTURE_WORK.md)
|
111
|
+
|
103
112
|
1. Fork it
|
104
113
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
105
114
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
106
115
|
4. Push to the branch (`git push origin my-new-feature`)
|
107
116
|
5. Create new Pull Request
|
108
117
|
|
109
|
-
## TODOS
|
110
|
-
|
111
|
-
- Examples directory
|
112
|
-
- Keep expanding tests
|
113
|
-
- find less horrible way to deal with index names
|
114
118
|
|
@@ -1,24 +1,34 @@
|
|
1
|
+
|
1
2
|
module Riak
|
2
3
|
module Shim
|
4
|
+
|
5
|
+
# Provides basic persistence features
|
6
|
+
# @attr [String] the unique key for storing the object in Riak
|
3
7
|
module Persistable
|
4
8
|
attr_writer :key
|
5
9
|
|
10
|
+
# @return [String] the unique key for storing the object in Riak
|
6
11
|
def key
|
7
12
|
@key ||= self.class.gen_key
|
8
13
|
end
|
9
14
|
|
15
|
+
# @return [Riak::Bucket] your app's Riak bucket for this class
|
10
16
|
def bucket
|
11
17
|
return self.class.store.bucket(bucket_name)
|
12
18
|
end
|
13
19
|
|
20
|
+
# @return [String] name of the Riak bucket generated from the class name and your configuration
|
14
21
|
def bucket_name
|
15
22
|
self.class.bucket_name
|
16
23
|
end
|
17
24
|
|
25
|
+
# @return [Riak::Shim::Store] the Riak cluster you are connected to
|
18
26
|
def store
|
19
27
|
self.class.store
|
20
28
|
end
|
21
29
|
|
30
|
+
# Persist this object into Riak
|
31
|
+
# @return [Persistable] self
|
22
32
|
def save
|
23
33
|
doc = bucket.get_or_new(key)
|
24
34
|
doc.data = to_hash
|
@@ -27,49 +37,43 @@ module Riak
|
|
27
37
|
self
|
28
38
|
end
|
29
39
|
|
40
|
+
# Remove this object from Riak
|
41
|
+
# @return [Persistable] self
|
30
42
|
def destroy
|
31
43
|
bucket.delete(key)
|
32
|
-
|
33
|
-
|
34
|
-
def set_indexes(indexes)
|
35
|
-
indexes.clear
|
36
|
-
if self.respond_to?(:fields_to_index)
|
37
|
-
fields_to_index.each do |field|
|
38
|
-
index_name = "#{field}_bin"
|
39
|
-
index_value = send(field)
|
40
|
-
indexes[index_name] << index_value
|
41
|
-
end
|
42
|
-
end
|
44
|
+
self
|
43
45
|
end
|
44
46
|
|
45
47
|
module ClassMethods
|
48
|
+
# *WARNING:* This is very slow and is intended only for your tests,
|
49
|
+
# not for production use.
|
50
|
+
#
|
51
|
+
# Remove all instances of this class from Riak.
|
46
52
|
def delete_all
|
47
53
|
bucket.keys.each do |key|
|
48
54
|
bucket.delete(key)
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
58
|
+
# @return [Riak::Shim::Store] the store you are connected to
|
52
59
|
def store
|
53
60
|
@store ||= Store.new
|
54
61
|
end
|
55
62
|
|
63
|
+
# @return [String] name of the Riak bucket generated from the class
|
64
|
+
# name and your configuration
|
56
65
|
def bucket_name
|
57
66
|
myclass = de_camel(self.to_s)
|
58
67
|
"#{store.bucket_prefix}#{myclass}"
|
59
68
|
end
|
60
69
|
|
61
|
-
|
62
|
-
classname.gsub(/::/, '__').
|
63
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
64
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
65
|
-
tr("-", "_").
|
66
|
-
downcase
|
67
|
-
end
|
68
|
-
|
70
|
+
# @return [Riak::Bucket] your app's Riak bucket for this class
|
69
71
|
def bucket
|
70
72
|
return store.bucket(bucket_name)
|
71
73
|
end
|
72
74
|
|
75
|
+
# Look up an instance of your class
|
76
|
+
# @return [Persistable, #nil] an instance corresponding to key
|
73
77
|
def for_key(key)
|
74
78
|
begin
|
75
79
|
raw = bucket.get(key)
|
@@ -82,26 +86,63 @@ module Riak
|
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
89
|
+
# @return [Array<Persistable>] any instances of your class whose
|
90
|
+
# indexed field matches value
|
91
|
+
#
|
92
|
+
# Locate instances by secondary index.
|
93
|
+
#
|
94
|
+
# *NOTE:* This interface is ugly as sin, so expect it to change in a future release.
|
85
95
|
def for_index(index, value)
|
86
96
|
bucket.get_index(index, value).map do |key|
|
87
97
|
for_key(key)
|
88
98
|
end
|
89
99
|
end
|
90
100
|
|
101
|
+
# @return [String] a UUID which we will use as our key
|
91
102
|
def gen_key
|
92
103
|
UUIDTools::UUID.random_create.to_s
|
93
104
|
end
|
94
105
|
|
106
|
+
# @return [Integer] the number of instances of your class stored in
|
107
|
+
# Riak
|
108
|
+
#
|
109
|
+
# *WARNING:* This is very slow and is intended only for your tests,
|
110
|
+
# not for production use.
|
111
|
+
#
|
112
|
+
# Counts instances in the database which really means items in the
|
113
|
+
# bucket.
|
95
114
|
def count
|
96
115
|
counter = 0
|
97
116
|
bucket.keys {|keys| counter += keys.count }
|
98
117
|
return counter
|
99
118
|
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
def de_camel(classname)
|
122
|
+
classname.gsub(/::/, '__').
|
123
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
124
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
125
|
+
tr("-", "_").
|
126
|
+
downcase
|
127
|
+
end
|
100
128
|
end
|
101
129
|
|
130
|
+
protected
|
131
|
+
|
102
132
|
def self.included(base)
|
103
133
|
base.extend(ClassMethods)
|
104
134
|
end
|
135
|
+
|
136
|
+
def set_indexes(indexes)
|
137
|
+
indexes.clear
|
138
|
+
if self.respond_to?(:fields_to_index)
|
139
|
+
fields_to_index.each do |field|
|
140
|
+
index_name = "#{field}_bin"
|
141
|
+
index_value = send(field)
|
142
|
+
indexes[index_name] << index_value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
105
146
|
end
|
106
147
|
end
|
107
148
|
end
|
data/lib/riak-shim/store.rb
CHANGED
@@ -3,13 +3,25 @@ require 'uuidtools'
|
|
3
3
|
|
4
4
|
module Riak
|
5
5
|
module Shim
|
6
|
+
|
7
|
+
# Represents a connection to a particular Riak cluster
|
8
|
+
# @attr [String] config_location The path to our configuration file. Defaults to DEFAULT_CONFIG_LOCATION
|
9
|
+
# @attr [Riak::Client] riak The underlying Riak connection
|
6
10
|
class Store
|
11
|
+
|
7
12
|
attr_writer :config_location
|
8
13
|
attr_writer :riak
|
9
14
|
|
15
|
+
DEFAULT_CONFIG_LOCATION = 'config/database.yml'
|
16
|
+
|
17
|
+
# Thrown when we can't find a configuration which matches the
|
18
|
+
# current RACK_ENV
|
10
19
|
class NoSettingsForCurrentEnvError < StandardError; end
|
20
|
+
|
21
|
+
# Thrown when the RACK_ENV environment variable is not set
|
11
22
|
class RackEnvNotSetError < StandardError; end
|
12
23
|
|
24
|
+
# @return [Hash] the configuration for our current environment
|
13
25
|
def config
|
14
26
|
env = ENV['RACK_ENV'] or raise RackEnvNotSetError.new
|
15
27
|
@config ||= read_config[env]
|
@@ -17,14 +29,16 @@ module Riak
|
|
17
29
|
"RACK_ENV #{ENV['RACK_ENV']} not specified in #{config_location}.")
|
18
30
|
end
|
19
31
|
|
32
|
+
# @return [Hash] the entire config, ie the configuration for each environment
|
20
33
|
def read_config
|
21
34
|
YAML.load_file(config_location)
|
22
35
|
end
|
23
36
|
|
24
37
|
def config_location
|
25
|
-
@config_location ||=
|
38
|
+
@config_location ||= DEFAULT_CONFIG_LOCATION
|
26
39
|
end
|
27
40
|
|
41
|
+
# @return [String] the prefix we will add to the bucket name for each class
|
28
42
|
def bucket_prefix
|
29
43
|
return config['bucket_prefix']
|
30
44
|
end
|
@@ -34,6 +48,7 @@ module Riak
|
|
34
48
|
:nodes => [{:host => config['host'], :http_port => config['http_port']}])
|
35
49
|
end
|
36
50
|
|
51
|
+
# @return [Riak::Bucket] the bucket coresponding to name
|
37
52
|
def bucket(name)
|
38
53
|
riak.bucket(name)
|
39
54
|
end
|
data/lib/riak-shim/version.rb
CHANGED
data/riak-shim.gemspec
CHANGED
@@ -19,12 +19,12 @@ Gem::Specification.new do |gem|
|
|
19
19
|
gem.add_dependency 'excon'
|
20
20
|
gem.add_dependency 'uuidtools', '~>2.1.3'
|
21
21
|
|
22
|
-
gem.add_development_dependency
|
23
|
-
gem.add_development_dependency
|
24
|
-
gem.add_development_dependency
|
25
|
-
gem.add_development_dependency
|
26
|
-
gem.add_development_dependency
|
27
|
-
gem.add_development_dependency
|
22
|
+
gem.add_development_dependency 'rake'
|
23
|
+
gem.add_development_dependency 'rspec'
|
24
|
+
gem.add_development_dependency 'guard'
|
25
|
+
gem.add_development_dependency 'guard-rspec'
|
26
|
+
gem.add_development_dependency 'pry'
|
27
|
+
gem.add_development_dependency 'awesome_print'
|
28
28
|
|
29
29
|
if RUBY_PLATFORM.include? 'darwin'
|
30
30
|
gem.add_development_dependency 'growl'
|
data/spec/persistable_spec.rb
CHANGED
@@ -106,11 +106,11 @@ describe 'persistable' do
|
|
106
106
|
|
107
107
|
context 'with a model which does not need indexes' do
|
108
108
|
it 'does not require #fields_to_index to be defined' do
|
109
|
-
expect { persistable.set_indexes
|
109
|
+
expect { persistable.send(:set_indexes, @indexes) }.to_not raise_error
|
110
110
|
end
|
111
111
|
|
112
112
|
it 'clears preexisting indexes' do
|
113
|
-
persistable.set_indexes
|
113
|
+
persistable.send(:set_indexes, @indexes)
|
114
114
|
@indexes.should be_empty
|
115
115
|
end
|
116
116
|
end
|
@@ -121,7 +121,7 @@ describe 'persistable' do
|
|
121
121
|
indexable.foo = 1
|
122
122
|
indexable.bar = 2
|
123
123
|
indexable.baz = 3
|
124
|
-
indexable.set_indexes
|
124
|
+
indexable.send(:set_indexes, @indexes)
|
125
125
|
end
|
126
126
|
|
127
127
|
after do
|
@@ -147,26 +147,30 @@ describe 'persistable' do
|
|
147
147
|
end
|
148
148
|
|
149
149
|
describe '#de_camel' do
|
150
|
+
def de_camel(camelcase)
|
151
|
+
PersistableExample.send(:de_camel, camelcase)
|
152
|
+
end
|
153
|
+
|
150
154
|
it 'handles a basic camel-case name' do
|
151
155
|
DeCamel.each do |camelcase, lowered|
|
152
|
-
|
156
|
+
de_camel(camelcase).should eq(lowered)
|
153
157
|
end
|
154
158
|
end
|
155
159
|
|
156
160
|
it 'handles runs of capitals appropriately' do
|
157
161
|
DeCamelCapsRuns.each do |camelcase, lowered|
|
158
|
-
|
162
|
+
de_camel(camelcase).should eq(lowered)
|
159
163
|
end
|
160
164
|
end
|
161
165
|
|
162
166
|
it 'turns module separators into slashes' do
|
163
167
|
DeCamelWithModule.each do |camelcase, lowered|
|
164
|
-
|
168
|
+
de_camel(camelcase).should eq(lowered)
|
165
169
|
end
|
166
170
|
end
|
167
171
|
|
168
172
|
it 'does not produce collisions' do
|
169
|
-
|
173
|
+
de_camel('RiakShim').should_not eq de_camel('Riak::Shim')
|
170
174
|
end
|
171
175
|
end
|
172
176
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: riak-shim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: riak-client
|
@@ -196,6 +196,7 @@ extra_rdoc_files: []
|
|
196
196
|
files:
|
197
197
|
- .gitignore
|
198
198
|
- .travis.yml
|
199
|
+
- FUTURE_WORK.md
|
199
200
|
- Gemfile
|
200
201
|
- Guardfile
|
201
202
|
- LICENSE
|