riak-shim 0.0.12 → 1.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/.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
|
[](http://travis-ci.org/mkb/riak-shim) [](https://gemnasium.com/mkb/riak-shim) [](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
|