rrrmatey 0.1.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.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +16 -0
  3. data/README.md +249 -0
  4. data/RELEASE_NOTES.md +26 -0
  5. data/Rakefile +39 -0
  6. data/lib/rrrmatey.rb +8 -0
  7. data/lib/rrrmatey/crud_controller.rb +97 -0
  8. data/lib/rrrmatey/discrete_result.rb +31 -0
  9. data/lib/rrrmatey/errors.rb +11 -0
  10. data/lib/rrrmatey/retryable.rb +28 -0
  11. data/lib/rrrmatey/string_model/connection_methods.rb +47 -0
  12. data/lib/rrrmatey/string_model/consumer_adapter_methods.rb +9 -0
  13. data/lib/rrrmatey/string_model/delete_methods.rb +12 -0
  14. data/lib/rrrmatey/string_model/field_definition_methods.rb +101 -0
  15. data/lib/rrrmatey/string_model/find_methods.rb +85 -0
  16. data/lib/rrrmatey/string_model/index_methods.rb +90 -0
  17. data/lib/rrrmatey/string_model/namespaced_key_methods.rb +21 -0
  18. data/lib/rrrmatey/string_model/string_model.rb +92 -0
  19. data/lib/rrrmatey/type_coercion.rb +61 -0
  20. data/lib/rrrmatey/version.rb +3 -0
  21. data/spec/rrrmatey/crud_controller/crud_controller_spec.rb +258 -0
  22. data/spec/rrrmatey/crud_controller/model_methods_spec.rb +26 -0
  23. data/spec/rrrmatey/discrete_result_spec.rb +104 -0
  24. data/spec/rrrmatey/retryable_spec.rb +95 -0
  25. data/spec/rrrmatey/string_model/connection_methods_spec.rb +64 -0
  26. data/spec/rrrmatey/string_model/consumer_adapter_methods_spec.rb +43 -0
  27. data/spec/rrrmatey/string_model/delete_methods_spec.rb +36 -0
  28. data/spec/rrrmatey/string_model/field_definition_methods_spec.rb +51 -0
  29. data/spec/rrrmatey/string_model/find_methods_spec.rb +147 -0
  30. data/spec/rrrmatey/string_model/index_methods_spec.rb +231 -0
  31. data/spec/rrrmatey/string_model/namespaced_key_methods_spec.rb +34 -0
  32. data/spec/rrrmatey/string_model/string_model_spec.rb +208 -0
  33. data/spec/spec_helper.rb +16 -0
  34. metadata +148 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b26fa620ebb2d28f3d3ae5dbd19274f076e5c810
4
+ data.tar.gz: 6a10068e527b7cb91e516fc0b6e6929aa649e857
5
+ SHA512:
6
+ metadata.gz: 6ebdc1b2f0341a663d3c579f808e5b83a7866c25922b8df1b427a19e3d8c065aa02bc9f9c146be69be7229e691cc1e7d570c44b3e9ddf783f27c46680162cb7d
7
+ data.tar.gz: e57d5187f5cb638f2db3ab7810d495a7e4ac72ff6cb9f8766f450540fa43cda5509ed5b2b5f631127bc89d4819c1d73f4eb692b68323878fda4ac90aac4e0e5b
data/LICENSE.md ADDED
@@ -0,0 +1,16 @@
1
+ Copyright 2015-2016 James Gorlick and Basho Technologies, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ All of the files in this project are under the project-wide license
16
+ unless they are otherwise marked.
data/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # RRRMatey
2
+
3
+ `rrrmatey` is an ODM (Object-Document-Mapper) framework for use with Basho Data
4
+ Platform (BDP) Cache Proxy and Riak KV.
5
+
6
+ CRUD operations are encapsulated at the controller- and model-level for relatively
7
+ smooth mixin for use in a `rails` or leaner `ruby` application framework.
8
+
9
+ BDP Cache Proxy provides the majority of the underlying interface to the data
10
+ layer. Redis acts as the cache layer while Riak provides availability with
11
+ partition tolerance. Riak Search is also used to provide the Index operation (the
12
+ silent I in CRUD).
13
+
14
+ ## Dependencies
15
+
16
+ * Ruby
17
+ * 1.9.3, 2.0, 2.1, and 2.2 are supported. JRuby in 1.9 and 2.0 modes are also
18
+ supported.
19
+ * BDP Cache Proxy
20
+ * Depends on write-around and read-through caching, so BDP EE 1.1.0+ .
21
+ * Riak KV
22
+ * Depends on basic KV and Riak Search, so Riak KV v.2.0.0+ . Ensure Riak Search
23
+ is enabled.
24
+
25
+ ### Development Dependencies
26
+ Development dependencies are handled with bundler. Install bundler
27
+ (`gem install bundler`), if not present.
28
+
29
+ ```shell
30
+ bundle install
31
+ ```
32
+
33
+ ### Tests
34
+ RSpec is generally run without coverage analysis.
35
+
36
+ ```shell
37
+ rake spec
38
+ ```
39
+
40
+ To enable coverage analysis, run with the 'CI' environment variable set.
41
+
42
+ ```shell
43
+ CI=1 rake spec
44
+ ```
45
+
46
+ ## Use Cases
47
+
48
+ ### Configuring Connections
49
+
50
+ Since `rrrmatey` uses BDP Cache Proxy which speaks a subset of the Redis protocol,
51
+ a `redis` client should be configured with one or more host entries corresponding
52
+ to the BDP Cache Proxies.
53
+
54
+ Similarly, the `riak` client is used for Riak Search.
55
+
56
+ In both cases, the use of `connection_pool` is recommended.
57
+
58
+ The following example captures the essential elements of configuration. However,
59
+ for a Rails application, the connection details should be in environment-specific
60
+ files to avoid leaking production credentials.
61
+
62
+ ```ruby
63
+ require 'rrrmatey'
64
+ require 'connection_pool'
65
+
66
+ RRRMatey::StringModel.cache_proxy = RRRMatey::Retryable.new(
67
+ ConnectionPool.new(:size => 5, :timeout => 5) {
68
+ Redis.new(:host => '127.0.0.1', :port => 22122)
69
+ }
70
+ )
71
+
72
+ RRRMatey::StringModel.riak = RRRMatey::Retryable.new(
73
+ ConnectionPool.new(:size => 5, :timeout => 5) {
74
+ Riak::Client.new(:nodes => [
75
+ {:host => '127.0.0.1', :pb_port => 10017},
76
+ {:host => '127.0.0.1', :pb_port => 10027},
77
+ {:host => '127.0.0.1', :pb_port => 10037},
78
+ {:host => '127.0.0.1', :pb_port => 10047},
79
+ {:host => '127.0.0.1', :pb_port => 10057},
80
+ ])
81
+ }
82
+ )
83
+ ```
84
+
85
+ ### Developing Models with Relations
86
+
87
+ To provide a shipworthy example, the models for Pirate and Vessel including a
88
+ relation for aboard follows:
89
+
90
+ ```ruby
91
+ class Pirate
92
+ include ::RRRMatey::StringModel
93
+ field :name, :type => :string
94
+ field :created, :type => :date, :default => Date.new(1970, 1, 1)
95
+ field :aboard, :type => :string
96
+
97
+ def valid?
98
+ valid_name? &&
99
+ valid_created?
100
+ end
101
+
102
+ private
103
+
104
+ def valid_name?
105
+ !name.blank?
106
+ end
107
+
108
+ def valid_created?
109
+ !created.nil? && created < Date.today - 14 * 365.25
110
+ end
111
+ end
112
+
113
+ class Vessel
114
+ include ::RRRMatey::StringModel
115
+ field :name, :type => :string
116
+
117
+ def valid?
118
+ valid_name?
119
+ end
120
+
121
+ def boardings(offset, limit)
122
+ Pirate.list_by(offset, limit, :aboard_s => vessel_id)
123
+ end
124
+
125
+ private
126
+
127
+ def valid_name?
128
+ !name.blank?
129
+ end
130
+ end
131
+ ```
132
+
133
+ #### Specialiing the Model Connection
134
+ The module-level `riak` and `cache_proxy` connections may be overriden on the
135
+ Model-level.
136
+
137
+ #### Opinionated, but Open
138
+ The StringModel is opinionted, providing reasonable defaults for the following:
139
+ * item_name
140
+ * default: the class name in snake case format (underscored)
141
+ * purpose: the element wrapper for json or xml content
142
+ * index_name
143
+ * default: the item_name
144
+ * purpose: the Riak Search index name
145
+ * note: the index is created automatically
146
+
147
+ Several Rails "built-in" methods such as blank? and underscore are used if
148
+ present or otherwise are monkey-patched in Rails fashion. This is mostly okey,
149
+ but Rails inflections, ie Person => People, are not re-implemented within
150
+ RRRMatey. For use in a Rails application, existing inflections will be used.
151
+ To be 100% covered, if a Model is known to be an abnormal case, requiring
152
+ inflection, specialize the item_name on the Model.
153
+
154
+ ### Developing an API Controller
155
+
156
+ Using the Pirate and Vessel models and mixing in the CrudController requires
157
+ only definition of non-CRUD methods, ie "A Pirate boards a Vessel":
158
+
159
+ ```ruby
160
+ class PiratesController < ApplicationController
161
+ include RRRMatey::CrudController
162
+
163
+ def board
164
+ vessel_id = params['vessel_id']
165
+ return respond_bad_request if vessel_id.blank?
166
+ pirate_id = params['pirate_id']
167
+ return respond_bad_request if pirate_id.blank?
168
+
169
+ v = Vessel.get(vessel_id)
170
+ return respond_not_found if v.nil?
171
+
172
+ p = Pirate.get(pirate_id)
173
+ return respond_not_found if p.nil?
174
+ p.aboard = v.id
175
+ p.save
176
+ respond_ok(p)
177
+ end
178
+ end
179
+
180
+ class VesselsController < ApplicationController
181
+ include RRRMatey::CrudController
182
+
183
+ def boardings
184
+ vessel_id = params['vessel_id']
185
+ return respond_bad_request if vessel_id.blank?
186
+ offset = params['offset'] || 0
187
+ limit = params['limit'] || 20
188
+ vessel = Vessel.new(vessel_id)
189
+ pirates_aboard = vessel.boardings(offset, limit)
190
+ respond_ok(pirates_aboard)
191
+ end
192
+ end
193
+ ```
194
+
195
+ #### Strong Parameters
196
+ By mixing in the CrudController functionality, existing Rails strong_parameters
197
+ best practice is still available within a Rails context and should be used.
198
+
199
+ ## How to Contribute
200
+
201
+ * Fork the project on Github. If you have already forked, use `git pull --rebase`
202
+ to reapply your changes on top of the mainline. Example:
203
+
204
+ ```shell
205
+ git checkout master
206
+ git pull --rebase basho master
207
+ ```
208
+
209
+ * Create a topic branch. If you've already created a topic branch, rebase it on
210
+ top of changes from the mainline "master" branch. Examples:
211
+ * New branch:
212
+
213
+ ```shell
214
+ git checkout -b topic
215
+ ```
216
+
217
+ * Existing branch:
218
+
219
+ ```shell
220
+ git rebase master
221
+ ```
222
+
223
+ * Write an RSpec example or set of examples that demonstrate the necessity and
224
+ validity of your changes. **Patches without specs will most often be ignored.
225
+ Just do it, you'll thank me later. Documenation patches need no specs, of course.
226
+
227
+ * Make your feature addition or bug fix. Make your specs and stories pass (green).
228
+
229
+ * Run the suite using multiruby or rvm or rbenv to ensure cross-version
230
+ compatibility.
231
+
232
+ * Cleanup any trailing whitespace in your code and generally follow the coding
233
+ style of existing code.
234
+
235
+ * Send a pull request to the upstream repositoty.
236
+
237
+ ## License & Copyright
238
+ Copyright ©2015-2016 James Gorlick and Basho Technologies, Inc.
239
+
240
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
241
+ this file except in compliance with the License. You may obtain a copy of the
242
+ License at
243
+
244
+ http://www.apache.org/licenses/LICENSE-2.0
245
+
246
+ Unless required by applicable law or agreed to in writing, software distributed
247
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
248
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
249
+ specific language governing permissions and limitations under the License.
data/RELEASE_NOTES.md ADDED
@@ -0,0 +1,26 @@
1
+ # RRRMatey Release Notes
2
+
3
+ ## 0.1.0 Release - 2016-01-04
4
+
5
+ Version 0.1.0 is released as a reference for how to use the BDP Cache Proxy
6
+ and Riak KV to implement an API.
7
+
8
+ ### New Features:
9
+ * StringModel mixin integrating BDP Cache Proxy and Riak Search to support all
10
+ CRUD operations.
11
+ * CrudController mixin supporting all CRUD operations, requiring only non-CRUD
12
+ operations to be added.
13
+
14
+ ### Significant Known Issues
15
+
16
+ Several Rails "built-in" methods such as blank? and underscore are used if
17
+ present or otherwise are monkey-patched in Rails fashion. This is mostly okey,
18
+ but Rails inflections, ie Person => People, are not re-implemented within
19
+ RRRMatey. For use in a Rails application, existing inflections will be used.
20
+ To be 100% covered, if a Model is known to be an abnormal case, requiring
21
+ inflection, specialize the item_name on the Model.
22
+
23
+ The Update method on CrudController does not operate in a PATCHy way, merging the
24
+ existing values from the underlying data store with the values provided via the PUT
25
+ method. Implementing PATCH seemed to conflate understanding of the use of BDP
26
+ Cache Proxy and Riak Search.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'rake'
5
+ require 'rspec/core/rake_task'
6
+
7
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
8
+ require 'rrrmatey/version'
9
+
10
+ task :gem => :build
11
+ task :build do
12
+ system 'gem build rrrmatey.gemspec'
13
+ end
14
+
15
+ task :install => :build do
16
+ system "sudo gem install rrrmatey-#{RRRMatey::VERSION}.gem"
17
+ end
18
+
19
+ task :uninstall do
20
+ system 'sudo gem uninstall rrrmatey'
21
+ end
22
+
23
+ task :release => :build do
24
+ system "git tag -a v#{RRRMatey::VERSION} -m 'Tagging #{RRRMatey::VERSION}'"
25
+ system 'git push --tags'
26
+ system "gem push rrrmatey-#{RRRMatey::VERSION}.gem"
27
+ system "rm rrrmatey-#{RRRMatey::VERSION}.gem"
28
+ end
29
+
30
+ RSpec::Core::RakeTask.new('spec') do |spec|
31
+ spec.pattern = "spec/**/*_spec.rb"
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new('spec:progress') do |spec|
35
+ spec.rspec_opts = %w(--format progress)
36
+ spec.pattern = "spec/**/*_spec.rb"
37
+ end
38
+
39
+ task :default => :spec
data/lib/rrrmatey.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'redis'
2
+ require 'riak'
3
+ require_relative 'rrrmatey/type_coercion.rb'
4
+ require_relative 'rrrmatey/errors.rb'
5
+ require_relative 'rrrmatey/retryable.rb'
6
+ require_relative 'rrrmatey/discrete_result.rb'
7
+ require_relative 'rrrmatey/string_model/string_model.rb'
8
+ require_relative 'rrrmatey/crud_controller.rb'
@@ -0,0 +1,97 @@
1
+ module RRRMatey
2
+ module CrudController
3
+ def self.included(base)
4
+ base.extend(ModelMethods)
5
+ end
6
+
7
+ module ModelMethods
8
+ def model(*model_klass_n_stuff)
9
+ if model_klass_n_stuff.blank?
10
+ model_klass
11
+ else
12
+ @model_klass = model_klass_n_stuff[0]
13
+ end
14
+ end
15
+
16
+ def model_klass
17
+ @model_klass ||= opinionated_model
18
+ end
19
+
20
+ private
21
+
22
+ def opinionated_model
23
+ # remove sController from Controller klass name
24
+ self.name[0..-12].constantize
25
+ end
26
+ end
27
+
28
+ def index
29
+ offset = params[:offset] || 0
30
+ limit = params[:limit] || 20
31
+ items = self.class.model_klass.list(offset, limit)
32
+ respond_ok(items)
33
+ end
34
+
35
+ def show
36
+ id = params[:id]
37
+ if id.blank?
38
+ return respond_bad_request
39
+ end
40
+ item = self.class.model_klass.get(id)
41
+ if item.nil?
42
+ return respond_not_found
43
+ end
44
+ respond_ok(item)
45
+ end
46
+
47
+ def destroy
48
+ id = params[:id]
49
+ if id.blank?
50
+ return respond_bad_request
51
+ end
52
+ self.class.model_klass.delete(id)
53
+ respond_ok(nil)
54
+ end
55
+
56
+ def update
57
+ # TODO: get and merge with params if a patch-y update is desired
58
+ item = self.class.model_klass.from_params(params)
59
+ begin
60
+ item.save
61
+ rescue RRRMatey::InvalidModelError
62
+ return respond_bad_request
63
+ rescue StandardError => e
64
+ return respond_internal_server_error(e)
65
+ end
66
+ respond_ok(nil)
67
+ end
68
+
69
+ def respond_bad_request
70
+ respond_to do |format|
71
+ format.json { render :json => nil, :status => 400 }
72
+ format.xml { render :xml => nil, :status => 400 }
73
+ end
74
+ end
75
+
76
+ def respond_not_found
77
+ respond_to do |format|
78
+ format.json { render :json => 'Not Found', :status => 404 }
79
+ format.xml { render :xml => 'Not Found', :status => 404 }
80
+ end
81
+ end
82
+
83
+ def respond_internal_server_error(e)
84
+ respond_to do |format|
85
+ format.json { render :json => { :mesage => e.message }, :status => 500 }
86
+ format.xml { render :xml => { :message => e.message }, :status => 500 }
87
+ end
88
+ end
89
+
90
+ def respond_ok(item)
91
+ respond_to do |format|
92
+ format.json { render :json => item }
93
+ format.xml { render :xml => item }
94
+ end
95
+ end
96
+ end
97
+ end