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 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