rrrmatey 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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