levee 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fcd858e4786b09e8a9ba17285b17b0f0b8f1c824
4
- data.tar.gz: b3c1da36324aa5c7a2198f7de69a2e8ae9d38e63
3
+ metadata.gz: 4c7914e86b72b6401588783ca7bae634b4999da7
4
+ data.tar.gz: d9716f4148d31fb9a37fb5d9a3891a5aad3809e5
5
5
  SHA512:
6
- metadata.gz: 4db403789b42ba351f7a66f0bfa57b9e5e094dea2c520e8e254e44a510ccd3c87a45dc9005cefaa0145aab4270d293a4f6368862f7bfa74154d874c60f39e9ad
7
- data.tar.gz: 3136ad1286c7ecc2b8d12546ffd76203407b15ce384927944d16215bd0e1b49a3c2c3922f96b7fec395f75f56ee2ecf73dcb25f0232fafd9335a520758d3042b
6
+ metadata.gz: 69a2eb7d1d755284a542f40410eb49b76a4528423c653648210a89ee90a56e5f163a7c301d66c78cd1e014ee18c2b0759b1d22458b6f7b9762da863304678d88
7
+ data.tar.gz: 08ea0de0d98c6f98645cbe5cf67ef6112a6b3afaa7561745c36741adca7ed4456255871004722c938e692ce826b9f8e30978a2febd573111515d219511dfe7e1
data/README.md CHANGED
@@ -23,9 +23,10 @@ Or install it yourself as:
23
23
  Overview
24
24
  ------------------------
25
25
 
26
- The abstract builder and validator classes are abstract classes for concrete builder classes to inherit from. They will be extracted to a gem once they are imporved and generalized through this project.
26
+ The abstract builder and validator classes are abstract classes for concrete builder classes to inherit from.
27
+ The purpose of the builder object is to create a layer of abstraction between the controller and models in a Rails application.
27
28
 
28
- The purpose of the builder object is to create a layer of abstraction between the controller and models in a Rails application. The builder is particularly useful for receiving complex post and put requests with multiple parameters, but is lightweight enough to use for simple writes when some filtering or parameter combination validation might be useful before writing to the database. Since it wraps the entire write action to mulitple models in a single transaction, any failure in the builder will result in the entire request being rolled back.
29
+ The builder is particularly useful for receiving complex post and put requests with multiple parameters, but is lightweight enough to use for simple writes when some filtering or parameter combination validation might be useful before writing to the database. Since it wraps the entire write action to mulitple models in a single transaction, any failure in the builder will result in the entire request being rolled back.
29
30
 
30
31
 
31
32
  Features
@@ -50,7 +51,7 @@ The TL;DR copy & paste version:
50
51
  Save this somewhere in your app directory. Maybe in a builders folder?
51
52
 
52
53
 
53
- class PostBuilder < BaseBuilder
54
+ class PostBuilder < Levee::Builder
54
55
  #matches the class name
55
56
 
56
57
  #make sure you list all params that are passed in (you can skip id if you want)
@@ -63,7 +64,7 @@ Save this somewhere in your app directory. Maybe in a builders folder?
63
64
  #list as many before_save and after_save callbacks as you want
64
65
  after_save :tweet_post
65
66
 
66
- #choose a validator class to run automatically (must be a legit base validator)
67
+ #choose a validator class to run automatically (must be a legit levee validator class)
67
68
  validator PostParamsValidator
68
69
 
69
70
  #access the model using #object
@@ -142,7 +143,7 @@ Let me say that again. The return value of custom methods does not automatically
142
143
  The id key is the only key in the params hash that does not need to be whitelisted and will not be mapped by default. It can still be overridden if you want to catch the id and do someting with it, or if you want to cue some other action just before the attribute mapping executes.
143
144
 
144
145
 
145
- ###The object Object
146
+ ###The object object
146
147
 
147
148
 
148
149
  As in ActiveModel::Serializers, the builder makes use of the object object within the class. This allows for code that easily reusable between create and update requests. Behind the scenes the base builder uses the id the the params hash to look up the existing object form the inferred model and represents it as object. If no :id parameter exists a new empty object of that class is instantiated and assigned to object. Buidler methods then don't need to know or care about whether object is new or existing and can treat it the same. In the cases where this might be important, you are of course free to dig into object and ask it all sorts of ActiveRecord questions about its state inside your overwriter methods.
@@ -183,7 +184,7 @@ Moar Features
183
184
 
184
185
  You can list macro-style callbacks inside the class just like in your ActiveRecord models.
185
186
 
186
- class PostBuilder
187
+ class PostBuilder < Levee::Builder
187
188
 
188
189
  attributes :title,
189
190
  :content,
@@ -214,7 +215,7 @@ Just be sure you define the methods you list somewhere within the class, otherwi
214
215
  The builder implements #delayed_saved(object) to be used inside a builder class. This is a shortcut for calling save! on any object as part of the after save callbacks. It is useful for when you are creating nested objects inside a builder and you want to be sure that the parent object is saved first, as is the case with associations where you need to be sure the parent has all attributes in place to pass validattion before the child forces a save. Note that delayed_save callbacks fire before the rest of the after_save callbacks.
215
216
 
216
217
 
217
- class PostBuilder
218
+ class PostBuilder < Levee::Builder
218
219
 
219
220
  attributes :title,
220
221
  :content,
@@ -264,7 +265,7 @@ You can make a validator class to be used with your builder. It's a good place t
264
265
  Make the class like this. You can put it anywhere in the app directory, but it feels very at home in a folder called validations. It doesn't use class inference so you can call it whatever
265
266
 
266
267
 
267
- class UltraSweetValidator < BaseValidator
268
+ class UltraSweetValidator < Levee::Validator
268
269
 
269
270
  validations :first_method,
270
271
  :other_method
@@ -296,7 +297,7 @@ When you find something you don't like, you can either use the super userful #ad
296
297
  Call the validator inside the builder by listing the class name
297
298
 
298
299
 
299
- class PostBuilder < BaseBuilder
300
+ class PostBuilder < Levee::Builder
300
301
 
301
302
  attributes :title,
302
303
  :content,
@@ -315,10 +316,6 @@ Don't bother validating simple params in your validator that map straight onto t
315
316
 
316
317
 
317
318
 
318
-
319
-
320
- TODO: Write usage instructions here
321
-
322
319
  ## Contributing
323
320
 
324
321
  1. Fork it ( https://github.com/[my-github-username]/levee/fork )
data/levee-0.0.2.gem ADDED
Binary file
data/lib/levee/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Levee
2
2
  extend self
3
3
 
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
 
6
6
  def gem_description
7
7
  %Q(The purpose of the builder object is to create a layer of abstraction between the controller and models in a Rails application. The builder is particularly useful for receiving complex post and put requests with multiple parameters, but is lightweight enough to use for simple writes when some filtering or parameter combination validation might be useful before writing to the database. Since it wraps the entire write action to mulitple models in a single transaction, any failure in the builder will result in the entire request being rolled back.)
data/lib/levee.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require "levee/version"
2
2
  require "levee/builder"
3
+ require "levee/validator"
3
4
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: levee
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Martinson
@@ -70,9 +70,7 @@ files:
70
70
  - LICENSE.txt
71
71
  - README.md
72
72
  - Rakefile
73
- - builder/base_builder_spec.rb
74
- - builder/builder_helper.rb
75
- - levee-0.0.1.gem
73
+ - levee-0.0.2.gem
76
74
  - levee.gemspec
77
75
  - lib/levee.rb
78
76
  - lib/levee/builder.rb
@@ -1,311 +0,0 @@
1
- require 'rails_helper'
2
- require_relative './builder_helper.rb'
3
-
4
- #Base builder is an abstract class. A test class is made in /spec/support/test_builder.rb that defines
5
- #a concrete child class.
6
-
7
- describe 'BaseBuidler' do
8
- let(:demo_class) { DemoTest }
9
- let(:params) { {name: 'test params',
10
- content: 'none'} }
11
- subject { DemoTestBuilder.new(params) }
12
-
13
- describe 'initialization' do
14
- it 'sets requires_save to true' do
15
- expect(subject.requires_save).to be
16
- end
17
- end
18
-
19
- describe '#build' do
20
- let(:object_class) { double(find_by: nil) }
21
- before do
22
- allow(object_class).to receive(:new) { demo_class.new }
23
- allow(subject).to receive(:object_class) { object_class }
24
- allow(subject).to receive(:assign_parameters_in_transaction)
25
- end
26
-
27
- context 'when the parameters are a top level array' do
28
- it 'calls #assign_parameters_in_transaction' do
29
- subject.params = []
30
- expect(subject).to receive(:assign_parameters_in_transaction)
31
- subject.build
32
- end
33
- end
34
-
35
- context 'when the parameters are an object that contains an id' do
36
- before { subject.params[:id] = 4 }
37
-
38
- it 'class #find_by on #object class with params[:id]' do
39
- expect(object_class).to receive(:find_by).with({id: subject.params[:id]})
40
- subject.build
41
- end
42
- end
43
-
44
- context 'when the parameters are an object that does not contain an id' do
45
-
46
- it 'calls #object_class' do
47
- expect(subject).to receive(:object_class)
48
- subject.build
49
- end
50
-
51
- it 'assigns a newly instantiated object of object_class to builder#object' do
52
- allow(subject).to receive(:assign_parameters_in_transaction)
53
- subject.build
54
- expect(subject.object).to be_a demo_class
55
- end
56
- end
57
- end
58
-
59
- describe 'build_nested' do
60
- before do
61
- allow(subject).to receive(:build)
62
- end
63
-
64
- it 'sets #requires_save to false' do
65
- expect(subject).to receive(:requires_save=).with(false)
66
- subject.build_nested
67
- end
68
-
69
- it 'calls #build' do
70
- expect(subject).to receive(:build)
71
- subject.build_nested
72
- end
73
- end
74
-
75
- describe 'update(object_id:, acting_user=nil' do
76
- context 'when the object can\'t be found' do
77
- it 'returns an errors hash with the error status 404' do
78
- object_class = double(find_by_id: nil)
79
- allow(subject).to receive(:object_class) {object_class}
80
- result = subject.update(object_id: 0)
81
- expect(result[:error_status]).to eq(404)
82
- end
83
- end
84
-
85
- context 'when an object is found' do
86
- before do
87
- object_class = double(find_by_id: :found_object)
88
- allow(subject).to receive(:object_class) {object_class}
89
- end
90
-
91
- it 'assigns the found object to the builder\'s object attribute' do
92
- expect(subject).to receive(:object=).with(:found_object)
93
- subject.update(object_id: 0)
94
- end
95
-
96
- it 'calls #assign_parameters_in_transaction' do
97
- expect(subject).to receive(:assign_parameters_in_transaction)
98
- subject.update(object_id: 0)
99
- end
100
- end
101
- end
102
-
103
- context 'when the params have a root node' do
104
- it 'throws an exception' do
105
- expect { DemoTestBuilder.new({demo_test: 'gg'}) }.to raise_error
106
- end
107
- end
108
-
109
- describe '#permitted_attributes' do
110
- context 'when the attributes method is called in the class definition with attribute symbols as parameter' do
111
- it 'those attribute symbols are available in #permitted_attributes' do
112
- expect(DemoTestBuilder.new({}).permitted_attributes).to include :name
113
- expect(DemoTestBuilder.new({}).permitted_attributes).to include :content
114
- end
115
- end
116
-
117
- context 'when params are passed that are not permitted at class definition' do
118
- it 'does not include those in the #permitted_attributes' do
119
- expect(DemoTestBuilder.new({}).permitted_attributes).to_not include :author
120
- end
121
- end
122
- end
123
-
124
- describe 'matching param keys to attributes' do
125
- let(:unpermitted_params) { {name: 'test params',
126
- content: 'none',
127
- author: 'Jim'} }
128
- it 'saves the object' do
129
- builder = NoMethodsBuilder.new params
130
- builder.build
131
- expect(builder.object.saved).to be
132
- end
133
-
134
- it 'does not save the object if a param key is included that is not listed as a permitted attribute' do
135
- builder = NoMethodsBuilder.new unpermitted_params
136
- builder.build
137
- expect(builder.object.saved).to_not be
138
- end
139
-
140
- context 'when there are no custom methods defined that match param keys' do
141
- specify "For each param key that is included in permitted attributes, the value of the param is assigned to the object\'s attribute matching the key name" do
142
- builder = NoMethodsBuilder.new params
143
- builder.build
144
- expect(builder.object.name).to eq(params[:name])
145
- expect(builder.object.content).to eq(params[:content])
146
- expect(builder.object.author).to_not be
147
- end
148
- end
149
-
150
- context 'when there are custom methods defined that match the name of attributes' do
151
- specify 'those attributes are not automatically assigned' do
152
- builder = MethodsBuilder.new params
153
- builder.build
154
- expect(builder.object.name).to_not be
155
- expect(builder.object.content).to_not be
156
- expect(builder.object.author).to_not be
157
- end
158
- end
159
- end
160
-
161
- describe '#top_level_array' do
162
- let(:array_params) { [params,params] }
163
- subject { DemoTestBuilder.new(array_params) }
164
-
165
- context 'when the params contain an array of two objects' do
166
- it 'returns two objects' do
167
- result = subject.send(:top_level_array)
168
- expect(result[0]).to be_a DemoTest
169
- expect(result[1]).to be_a DemoTest
170
- end
171
-
172
- it 'does not save the objects' do
173
- result = subject.send(:top_level_array)
174
- expect(result[0].saved).to_not be
175
- end
176
- end
177
- end
178
-
179
- describe 'delayed_save!(object)' do
180
- it 'adds the object to the #nested_objects_to_save array' do
181
- expect(subject.nested_objects_to_save).to be_a Array
182
- subject.send(:delayed_save!, :nested_object)
183
- expect(subject.nested_objects_to_save).to include :nested_object
184
- end
185
-
186
- it 'does not add an duplicate object' do
187
- expect(subject.nested_objects_to_save.length).to be 0
188
- subject.send(:delayed_save!, 'nested_object')
189
- subject.send(:delayed_save!, 'nested_object')
190
- expect(subject.nested_objects_to_save.length).to be 1
191
- end
192
- end
193
-
194
- describe 'callbacks' do
195
- context 'when the before_save method is used in the class definition' do
196
- it 'adds the method symbols passed to the #before_save_callbacks array' do
197
- expect(subject.send(:before_save_callbacks)).to match_array [:before_one, :before_two]
198
- end
199
- end
200
-
201
- context 'when the after_save method is used in the class definition' do
202
- it 'adds the method symbols passed to the #after_save_callbacks array' do
203
- expect(subject.send(:after_save_callbacks)).to match_array [:after_one]
204
- end
205
- end
206
- end
207
-
208
- describe 'building the object' do
209
- context '#assign_parameters_in_transaction' do
210
-
211
- context 'when there is a validator assigned' do
212
- it 'calls #validate_parameters on the validator' do
213
- subject.build
214
- expect(subject.validator.validated).to be
215
- end
216
- end
217
-
218
- context 'when the validator returns errors' do
219
- let(:error) { error = {status: 400, code: 'invalid_request_error'} }
220
- before { allow(subject.validator).to receive(:errors) { [error] } }
221
-
222
- it 'the method returns early, not calling the following methods' do
223
- expect(subject).to_not receive(:top_level_array)
224
- end
225
-
226
- it 'returns an errors hash' do
227
- expect(subject.build[:errors]).to be
228
- end
229
- end
230
-
231
- it '#calls top_level_array' do
232
- expect(subject).to receive(:top_level_array).at_least(:once)
233
- subject.build
234
- end
235
-
236
- it 'calls #call_setter_for_each_param_key' do
237
- expect(subject).to receive(:assign_parameters_in_transaction).at_least(:once)
238
- subject.build
239
- end
240
-
241
- context 'when there are no errors' do
242
- context 'when #requires_save is truthy' do
243
- it 'saves the object' do
244
- subject.build
245
- expect(subject.object.saved).to be
246
- end
247
-
248
- it 'calls save on all #nested_objects_to_save' do
249
- nest1 = double
250
- nest2 = double
251
- expect(nest1).to receive(:save!)
252
- expect(nest2).to receive(:save!)
253
- subject.nested_objects_to_save = [ nest1, nest2 ]
254
- subject.build
255
- end
256
-
257
- it 'calls the #before_save_callbacks before the object is saved' do
258
- subject.build
259
- expect(subject.object.before_save_called_with_unsaved_object).to be
260
- end
261
-
262
- it 'calls @after_save_callbacks after object is saved' do
263
- subject.build
264
- expect(subject.object.after_save_called_with_saved_object).to be
265
- end
266
-
267
- it 'calls callback blocks after object is saved' do
268
- subject = DemoTestBuilder.new(params) { |object| object.block_callback_called_after_save = true if object.saved }
269
- subject.build
270
- expect(subject.object.block_callback_called_after_save).to be
271
- end
272
- end
273
-
274
- context 'when #requires_save is falsey' do
275
- it 'does not save the object' do
276
- subject.build_nested
277
- expect(subject.object.saved).to_not be
278
- end
279
- end
280
- end
281
-
282
- context 'when there are errors' do
283
-
284
- end
285
- end
286
- end
287
-
288
- describe 'self.validator' do
289
- context 'when called with a validator class name' do
290
- it 'adds that validator to the #validator method' do
291
- expect(subject.validator).to be_a DemoParamsValidator
292
- end
293
-
294
- it 'raises an exception if the class given is not a BaseParamsValidator' do
295
-
296
- end
297
- end
298
- end
299
-
300
- describe '#validator' do
301
- it 'returns a BaseParamsValidator' do
302
- expect(subject.validator).to be_a BaseParamsValidator
303
- end
304
-
305
- it 'returns an object that responds to #params and returns some data' do
306
- expect(subject.validator.params).to be
307
- end
308
- end
309
- end
310
-
311
-
@@ -1,95 +0,0 @@
1
- require_relative "../../app/application_services/base_builder"
2
- require_relative "../../app/application_services/base_params_validator"
3
-
4
- class DemoParamsValidator < BaseParamsValidator
5
-
6
- attr_accessor :validated
7
-
8
- def validate_params
9
- @validated = true
10
- self
11
- end
12
-
13
- def errors
14
- []
15
- end
16
-
17
- end
18
-
19
- class DemoTestBuilder < BaseBuilder
20
- attributes :name, :content
21
- before_save :before_one, :before_two
22
- after_save :after_one
23
- validator DemoParamsValidator
24
-
25
- def before_one
26
- object.before_save_called_with_unsaved_object = true unless object.saved
27
- end
28
-
29
- def before_two; end
30
-
31
- def after_one
32
- object.after_save_called_with_saved_object = true if object.saved
33
- end
34
- end
35
-
36
- class NoMethodsBuilder < BaseBuilder
37
- attributes :name, :content
38
- end
39
-
40
- class MethodsBuilder < BaseBuilder
41
- attributes :name, :content
42
- before_save :before_method
43
- after_save :after_method
44
- validator DemoParamsValidator
45
-
46
- def name(data); end
47
-
48
- def content(data); end
49
-
50
- def before_method
51
- object.before_method_called = true
52
- end
53
-
54
- def after_method
55
- object.after_method_called = true
56
- end
57
- end
58
-
59
- class DemoTest
60
- attr_accessor :name, :content, :author
61
- attr_reader :saved, :updated
62
- attr_accessor :before_method_called, :after_method_called
63
- attr_accessor :before_save_called_with_unsaved_object
64
- attr_accessor :after_save_called_with_saved_object
65
- attr_accessor :block_callback_called_after_save
66
-
67
- def save!
68
- @saved = true
69
- end
70
-
71
- def update!
72
- @updated = true
73
- end
74
-
75
- def self.find_by(argument)
76
- nil
77
- end
78
-
79
- def persisted?
80
- false
81
- end
82
- end
83
-
84
- class PlainValidator; end
85
-
86
- class BadValidatorBuilder < BaseBuilder
87
- validator PlainValidator
88
- end
89
-
90
-
91
-
92
- #Used so that the buidlers with different names can all use the DemoTest class
93
- NoMethods = DemoTest
94
- Methods = DemoTest
95
- BadValidator = DemoTest
data/levee-0.0.1.gem DELETED
Binary file