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 CHANGED
@@ -10,3 +10,6 @@ pkg
10
10
  spec/reports
11
11
  tmp
12
12
  vendor/bundle
13
+ .yardoc
14
+ _yardoc
15
+ doc/
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
@@ -6,3 +6,9 @@ platforms :jruby do
6
6
  gem 'json-jruby'
7
7
  end
8
8
 
9
+ platforms :mri_19 do
10
+ gem 'guard-yard'
11
+ gem 'yard'
12
+ gem 'redcarpet'
13
+ end
14
+
data/Guardfile CHANGED
@@ -1,7 +1,18 @@
1
- guard 'rspec', :version => 2, :cli => "--color --format nested" do
2
- watch(%r{^spec/(.+)_spec\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
3
- watch(%r{^spec/support/*}) { 'spec' }
4
- watch(%r{^lib/(.+)\.rb$}) { 'spec' }
5
- watch('spec/spec_helper.rb') { "spec" }
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 configuration
4
- out of config/database.yml and derives bucket names from your class names and an
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: http://basho.com/products/riak-overview/
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
- single Riak install. During development, this prevents you from stepping on
41
- your own toes.
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' => self.foo, 'bar' => self.bar }
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
- of fields, define `#fields_to_index` and return the names of
80
- any fields you wish to query on. When you #save an instance of YourClass,
81
- riak-shim will populate a secondary index for that field.
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(index_name, value)
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
- end
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
- def de_camel(classname)
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
@@ -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 ||= 'config/database.yml'
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
@@ -1,5 +1,5 @@
1
1
  module Riak
2
2
  module Shim
3
- VERSION = "0.0.12"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
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 "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"
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'
@@ -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(@indexes) }.to_not raise_error
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(@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(@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
- PersistableExample.de_camel(camelcase).should eq(lowered)
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
- PersistableExample.de_camel(camelcase).should eq(lowered)
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
- PersistableExample.de_camel(camelcase).should eq(lowered)
168
+ de_camel(camelcase).should eq(lowered)
165
169
  end
166
170
  end
167
171
 
168
172
  it 'does not produce collisions' do
169
- PersistableExample.de_camel('RiakShim').should_not eq PersistableExample.de_camel('Riak::Shim')
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.12
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-08-21 00:00:00.000000000 Z
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