snfoil 0.5.4 → 0.8.1

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
  SHA256:
3
- metadata.gz: 8691b33751a878d5963ff71517fc31a11e75d2be79fa5816155f62f13d490042
4
- data.tar.gz: c97c4b4f5e17ccb51ba00bfca92e17243c93e591ee1e7967021bedeb8db3d5ce
3
+ metadata.gz: d21f0199087d10df6e8b65aec2e759aaba09ae7435366b861f1f66a7e122f0e3
4
+ data.tar.gz: 8180f9fe7de77979ef2e4e55464d9681ec44feb005e6c007dc17130f333ca902
5
5
  SHA512:
6
- metadata.gz: d40b1751013aa15d36752126f8d0950c38b53c370ec6be45b62a7ebbcfd6744bd17803dd63b3e32dba5659e07c54b5b67e5c594adc07341bd2f156344cd545b3
7
- data.tar.gz: 8c17c0a10e1fa7507596ea19d34a4af9ca3c718e21b14bc200ef55c6f0afa18e2ecd5a999631caa67b0a67bf6eca269cab4671c9cc57252306b59366a1375cc7
6
+ metadata.gz: 89b34c08ac2b0a94962069e10040055800a85196f1b4d0156f943b425750f916c3359992710ee019cf61470965af782250f071aa83079fc82b5c1802ec23195d
7
+ data.tar.gz: 71d2b7a606841a054d06b9268decb625722a801d73ee776c2dacdaecc1a6e8929269d1c1804e9fe173cd4af7aab9e2bbb7354e8a2474307e6440f801309b0347
data/README.md CHANGED
@@ -22,7 +22,520 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+
26
+ ### Major Components
27
+
28
+ #### Model
29
+ #### Policy
30
+ #### Searcher
31
+
32
+ ## Contexts
33
+ Contexts are groupings of common actions that a certain entity can perform.
34
+
35
+ ### Data
36
+
37
+ ### Actions
38
+
39
+ SnFoil Contexts handle basic CRUD through a few different actions
40
+
41
+ <table>
42
+ <thead>
43
+ <th>Action</th>
44
+ <th>Description</th>
45
+ </thead>
46
+ <tbody>
47
+ <tr>
48
+ <td>Build</td>
49
+ <td>
50
+ The action on setting up a model but not persiting.
51
+ <div>
52
+ <i>Author's note:</i> So far I have just been using this so factories in testing follow the same setup logic as a context would.
53
+ </div>
54
+ </td>
55
+ </tr>
56
+ <tr>
57
+ <td>Create</td>
58
+ <td>The action of setting up a model and persisting it.</td>
59
+ </tr>
60
+ <tr>
61
+ <td>Update</td>
62
+ <td>The action of finding a pre-existing model and updating the attributes.</td>
63
+ </tr>
64
+ <tr>
65
+ <td>Destroy</td>
66
+ <td>The action of finding a pre-existing model and destroying it.</td>
67
+ </tr>
68
+ <tr>
69
+ <td>Show</td>
70
+ <td>The action of finding a pre-existing model by an identifier.</td>
71
+ </tr>
72
+ <tr>
73
+ <td>Index</td>
74
+ <td>The action of finding a pre-existing models by using a searcher.</td>
75
+ </tr>
76
+ </tbody>
77
+ </table>
78
+
79
+ ### Methods
80
+ Methods allow users to create inheritable actions that occur in a specific order. Methods will always run before their hook counterpart. Since these are inheritable, you can chain needed actions all the way through the parent heirarchy by using the `super` keyword.
81
+
82
+ <strong>Important Note</strong> Methods <u>always</u> need to return the options hash at the end.
83
+
84
+ <i>Author's opinion:</i> While simplier than hooks, they do not allow for as clean of a composition as hooks.
85
+
86
+ #### Example
87
+
88
+ ```ruby
89
+ # Call the webhooks for third party integrations
90
+ # Commit business logic to internal process
91
+ def after_create_success(**options)
92
+ options = super
93
+
94
+ call_webhook_for_model(options[:object])
95
+ finalize_business_logic(options[:object])
96
+
97
+ options
98
+ end
99
+
100
+ # notify error tracker
101
+ def after_create_error(**options)
102
+ options = super
103
+
104
+ notify_errors(options[:object].errors)
105
+
106
+ options
107
+ end
108
+ ```
109
+
110
+ ### Hooks
111
+ Hooks make it very easy to compose multiple actions that need to occur in a specific order. You can have as many repeated hooks as you would like. This makes defining single responsibility hooks very simple, and they will get called in the order they are defined. The major downside of hooks are that they are currently not inheritable.
112
+
113
+ <strong>Important Note</strong> Hooks <u>always</u> need to return the options hash at the end.
114
+
115
+ #### Example
116
+ Lets take the Method example and make it into hooks instead.
117
+ ```ruby
118
+ # Call the webhooks for third party integrations
119
+ after_create_success do |options|
120
+ call_webhook_for_model(options[:object])
121
+ options
122
+ end
123
+
124
+ # Commit business logic to internal process
125
+ after_create_success do |options|
126
+ finalize_business_logic(options[:object])
127
+ options
128
+ end
129
+
130
+ # notify error tracker
131
+ after_create_error do |options|
132
+ notify_errors(options[:object].errors)
133
+ options
134
+ end
135
+ ```
136
+
137
+ <table>
138
+ <thead>
139
+ <th>Name</th>
140
+ <th>Timing</th>
141
+ <th>Description</th>
142
+ </thead>
143
+ <tbody>
144
+ <tr>
145
+ <td>setup</td>
146
+ <td>-Always at the beginning</td>
147
+ <td>Primarily used for basic setup logic that always needs to occur</td>
148
+ </tr>
149
+ <tr>
150
+ <td>setup_&lt;action&gt;</td>
151
+ <td>-Before the object has been found or created</td>
152
+ <td>Primarily used for basic setup logic that only needs to occur for certain actions</td>
153
+ </tr>
154
+ <tr>
155
+ <td>before_&lt;action&gt;</td>
156
+ <td>
157
+ <div>-After the object has been found or created</div>
158
+ <div>-Before the object has been persisted/altered</div>
159
+ </td>
160
+ <td></td>
161
+ </tr>
162
+ <tr>
163
+ <td>after_&lt;action&gt;_success</td>
164
+ <td>-After the object has been successfully been persisted/altered</td>
165
+ <td></td>
166
+ </tr>
167
+ <tr>
168
+ <td>after_&lt;action&gt;_failure</td>
169
+ <td>-After an error has occured persisting/altering the object</td>
170
+ <td></td>
171
+ <tr>
172
+ <td>after_&lt;action&gt;</td>
173
+ <td>-Always at the end</td>
174
+ <td></td>
175
+ </tr>
176
+ </tr>
177
+ </tbody>
178
+ <table>
179
+
180
+ ### Call Order
181
+
182
+ The call order for actions is extremely important because SnFoil passes the options hash throughout the entire process. So any data you may need down the call order can be added earlier in the stack.
183
+
184
+ <table>
185
+ <thead>
186
+ <tr>
187
+ <th rowspan="2">Action</th>
188
+ <th colspan="2">Order</th>
189
+ </tr>
190
+ <tr>
191
+ <th>Type</th>
192
+ <th>Name</th>
193
+ </tr>
194
+ </thead>
195
+ <tbody>
196
+ <tr>
197
+ <td rowspan="5">Build</td>
198
+ </tr>
199
+ <tr>
200
+ <td>Method</td>
201
+ <td>setup</td>
202
+ </tr>
203
+ <tr>
204
+ <td>Hooks</td>
205
+ <td>setup</td>
206
+ </tr>
207
+ <tr>
208
+ <td>Method</td>
209
+ <td>setup_build</td>
210
+ </tr>
211
+ <tr>
212
+ <td>Hooks</td>
213
+ <td>setup_build</td>
214
+ </tr>
215
+ <tr><td rowspan="25">Create</td></tr>
216
+ <tr>
217
+ <td>Method</td>
218
+ <td>setup</td>
219
+ </tr>
220
+ <tr>
221
+ <td>Hooks</td>
222
+ <td>setup</td>
223
+ </tr>
224
+ <tr>
225
+ <td>Method</td>
226
+ <td>setup_build</td>
227
+ </tr>
228
+ <tr>
229
+ <td>Hooks</td>
230
+ <td>setup_build</td>
231
+ </tr>
232
+ <tr>
233
+ <td>Method</td>
234
+ <td>setup_change</td>
235
+ </tr>
236
+ <tr>
237
+ <td>Hooks</td>
238
+ <td>setup_change</td>
239
+ </tr>
240
+ <tr>
241
+ <td>Method</td>
242
+ <td>setup_create</td>
243
+ </tr>
244
+ <tr>
245
+ <td>Hooks</td>
246
+ <td>setup_create</td>
247
+ </tr>
248
+ <tr>
249
+ <td>Method</td>
250
+ <td>before_change</td>
251
+ </tr>
252
+ <tr>
253
+ <td>Hooks</td>
254
+ <td>before_change</td>
255
+ </tr>
256
+ <tr>
257
+ <td>Method</td>
258
+ <td>before_create</td>
259
+ </tr>
260
+ <tr>
261
+ <td>Hooks</td>
262
+ <td>before_create</td>
263
+ </tr>
264
+ <tr>
265
+ <td>Method</td>
266
+ <td><i>*after_change_success</i</td>
267
+ </tr>
268
+ <tr>
269
+ <td>Hooks</td>
270
+ <td><i>*after_change_success</i</td>
271
+ </tr>
272
+ <tr>
273
+ <td>Method</td>
274
+ <td><i>*after_create_success</i</td>
275
+ </tr>
276
+ <tr>
277
+ <td>Hooks</td>
278
+ <td><i>*after_create_success</i</td>
279
+ </tr>
280
+ <tr>
281
+ <td>Method</td>
282
+ <td><i>*after_change_failure</i</td>
283
+ </tr>
284
+ <tr>
285
+ <td>Hooks</td>
286
+ <td><i>*after_change_failure</i</td>
287
+ </tr>
288
+ <tr>
289
+ <td>Method</td>
290
+ <td><i>*after_create_failure</i</td>
291
+ </tr>
292
+ <tr>
293
+ <td>Hooks</td>
294
+ <td><i>*after_create_failure</i</td>
295
+ </tr>
296
+ <tr>
297
+ <td>Method</td>
298
+ <td>after_change</td>
299
+ </tr>
300
+ <tr>
301
+ <td>Hooks</td>
302
+ <td>after_change</td>
303
+ </tr>
304
+ <tr>
305
+ <td>Method</td>
306
+ <td>after_create</td>
307
+ </tr>
308
+ <tr>
309
+ <td>Hooks</td>
310
+ <td>after_create</td>
311
+ </tr>
312
+ <tr><td rowspan="23">Update</td></tr>
313
+ <tr>
314
+ <td>Method</td>
315
+ <td>setup</td>
316
+ </tr>
317
+ <tr>
318
+ <td>Hooks</td>
319
+ <td>setup</td>
320
+ </tr>
321
+ <tr>
322
+ <td>Method</td>
323
+ <td>setup_change</td>
324
+ </tr>
325
+ <tr>
326
+ <td>Hooks</td>
327
+ <td>setup_change</td>
328
+ </tr>
329
+ <tr>
330
+ <td>Method</td>
331
+ <td>setup_update</td>
332
+ </tr>
333
+ <tr>
334
+ <td>Hooks</td>
335
+ <td>setup_update</td>
336
+ </tr>
337
+ <tr>
338
+ <td>Method</td>
339
+ <td>before_change</td>
340
+ </tr>
341
+ <tr>
342
+ <td>Hooks</td>
343
+ <td>before_change</td>
344
+ </tr>
345
+ <tr>
346
+ <td>Method</td>
347
+ <td>before_update</td>
348
+ </tr>
349
+ <tr>
350
+ <td>Hooks</td>
351
+ <td>before_update</td>
352
+ </tr>
353
+ <tr>
354
+ <td>Method</td>
355
+ <td><i>*after_change_success</i</td>
356
+ </tr>
357
+ <tr>
358
+ <td>Hooks</td>
359
+ <td><i>*after_change_success</i</td>
360
+ </tr>
361
+ <tr>
362
+ <td>Method</td>
363
+ <td><i>*after_update_success</i</td>
364
+ </tr>
365
+ <tr>
366
+ <td>Hooks</td>
367
+ <td><i>*after_update_success</i</td>
368
+ </tr>
369
+ <tr>
370
+ <td>Method</td>
371
+ <td><i>*after_change_failure</i</td>
372
+ </tr>
373
+ <tr>
374
+ <td>Hooks</td>
375
+ <td><i>*after_change_failure</i</td>
376
+ </tr>
377
+ <tr>
378
+ <td>Method</td>
379
+ <td><i>*after_update_failure</i</td>
380
+ </tr>
381
+ <tr>
382
+ <td>Hooks</td>
383
+ <td><i>*after_update_failure</i</td>
384
+ </tr>
385
+ <tr>
386
+ <td>Method</td>
387
+ <td>after_change</td>
388
+ </tr>
389
+ <tr>
390
+ <td>Hooks</td>
391
+ <td>after_change</td>
392
+ </tr>
393
+ <tr>
394
+ <td>Method</td>
395
+ <td>after_update</td>
396
+ </tr>
397
+ <tr>
398
+ <td>Hooks</td>
399
+ <td>after_update</td>
400
+ </tr>
401
+ <tr><td rowspan="23">Destroy</td></tr>
402
+ <tr>
403
+ <td>Method</td>
404
+ <td>setup</td>
405
+ </tr>
406
+ <tr>
407
+ <td>Hooks</td>
408
+ <td>setup</td>
409
+ </tr>
410
+ <tr>
411
+ <td>Method</td>
412
+ <td>setup_change</td>
413
+ </tr>
414
+ <tr>
415
+ <td>Hooks</td>
416
+ <td>setup_change</td>
417
+ </tr>
418
+ <tr>
419
+ <td>Method</td>
420
+ <td>setup_destroy</td>
421
+ </tr>
422
+ <tr>
423
+ <td>Hooks</td>
424
+ <td>setup_destroy</td>
425
+ </tr>
426
+ <tr>
427
+ <td>Method</td>
428
+ <td>before_change</td>
429
+ </tr>
430
+ <tr>
431
+ <td>Hooks</td>
432
+ <td>before_change</td>
433
+ </tr>
434
+ <tr>
435
+ <td>Method</td>
436
+ <td>before_destroy</td>
437
+ </tr>
438
+ <tr>
439
+ <td>Hooks</td>
440
+ <td>before_destroy</td>
441
+ </tr>
442
+ <tr>
443
+ <td>Method</td>
444
+ <td><i>*after_change_success</i</td>
445
+ </tr>
446
+ <tr>
447
+ <td>Hooks</td>
448
+ <td><i>*after_change_success</i</td>
449
+ </tr>
450
+ <tr>
451
+ <td>Method</td>
452
+ <td><i>*after_destroy_success</i</td>
453
+ </tr>
454
+ <tr>
455
+ <td>Hooks</td>
456
+ <td><i>*after_destroy_success</i</td>
457
+ </tr>
458
+ <tr>
459
+ <td>Method</td>
460
+ <td><i>*after_change_failure</i</td>
461
+ </tr>
462
+ <tr>
463
+ <td>Hooks</td>
464
+ <td><i>*after_change_failure</i</td>
465
+ </tr>
466
+ <tr>
467
+ <td>Method</td>
468
+ <td><i>*after_destroy_failure</i</td>
469
+ </tr>
470
+ <tr>
471
+ <td>Hooks</td>
472
+ <td><i>*after_destroy_failure</i</td>
473
+ </tr>
474
+ <tr>
475
+ <td>Method</td>
476
+ <td>after_change</td>
477
+ </tr>
478
+ <tr>
479
+ <td>Hooks</td>
480
+ <td>after_change</td>
481
+ </tr>
482
+ <tr>
483
+ <td>Method</td>
484
+ <td>after_destroy</td>
485
+ </tr>
486
+ <tr>
487
+ <td>Hooks</td>
488
+ <td>after_destroy</td>
489
+ </tr>
490
+ <tr>
491
+ <td rowspan="5">Show</td>
492
+ </tr>
493
+ <tr>
494
+ <td>Method</td>
495
+ <td>setup</td>
496
+ </tr>
497
+ <tr>
498
+ <td>Hooks</td>
499
+ <td>setup</td>
500
+ </tr>
501
+ <tr>
502
+ <td>Method</td>
503
+ <td>setup_show</td>
504
+ </tr>
505
+ <tr>
506
+ <td>Hooks</td>
507
+ <td>setup_show</td>
508
+ </tr>
509
+ <tr>
510
+ <td rowspan="5">Index</td>
511
+ </tr>
512
+ <tr>
513
+ <td>Method</td>
514
+ <td>setup</td>
515
+ </tr>
516
+ <tr>
517
+ <td>Hooks</td>
518
+ <td>setup</td>
519
+ </tr>
520
+ <tr>
521
+ <td>Method</td>
522
+ <td>setup_index</td>
523
+ </tr>
524
+ <tr>
525
+ <td>Hooks</td>
526
+ <td>setup_index</td>
527
+ </tr>
528
+ </tbody>
529
+ <table>
530
+
531
+ <div>
532
+ * only occurs depeding on the success or failure of the action
533
+ </div>
534
+
535
+ ## Policies
536
+
537
+ ## Searchers
538
+
26
539
 
27
540
  ## Development
28
541
 
data/TODO.md ADDED
@@ -0,0 +1,5 @@
1
+ # TODO
2
+ ## Tests
3
+ ### Contexts
4
+ - Add tests for propagation of :authorize in options aggregate
5
+ - Add tests for Create now using Build
@@ -26,6 +26,10 @@ module SnFoil
26
26
  def attributes=(attributes)
27
27
  __getobj__.attributes = attributes
28
28
  end
29
+
30
+ def is_a?(klass)
31
+ __getobj__.instance_of?(klass)
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'delegate'
4
+
3
5
  module SnFoil
4
6
  module Adapters
5
7
  module ORMs
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'active_support/concern'
4
4
  require_relative './setup_context'
5
- require_relative './change_context'
6
5
 
7
6
  module SnFoil
8
7
  module Contexts
@@ -11,33 +10,58 @@ module SnFoil
11
10
 
12
11
  included do
13
12
  include SetupContext
14
- include ChangeContext
15
13
  end
16
14
 
17
15
  class_methods do
16
+ attr_reader :i_setup_build_hooks
17
+
18
18
  def build(params:, entity: nil, **options)
19
19
  new(entity).build(**options, params: params)
20
20
  end
21
+
22
+ def setup_build(method = nil, **options, &block)
23
+ raise ArgumentError, '#setup_build requires either a method name or a block' if method.nil? && block.nil?
24
+
25
+ (@i_setup_build_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
26
+ end
21
27
  end
22
28
 
23
29
  def setup_build_object(params: {}, object: nil, **options)
24
- SnFoil.logger.info 'Warning: Using build bypasses authorize. It is safer to interact with models through create' unless ENV['ISTEST']
25
- return wrap_object(object) if object
30
+ object = if object
31
+ wrap_object(object)
32
+ else
33
+ klass = options.fetch(:model) { model }
34
+ wrap_object(klass).new
35
+ end
26
36
 
27
- klass = options.fetch(:model) { model }
28
- options.merge! object: wrap_object(klass).new(**params)
37
+ object.attributes = params
38
+ options.merge! object: object
29
39
  end
30
40
 
31
41
  def build(**options)
32
42
  options[:action] = :build
33
- options = setup_build(setup_change(**options))
43
+ options = before_setup_build_object(**options)
34
44
  options = setup_build_object(**options)
45
+ authorize(options[:object], options[:authorize], **options) if options[:authorize]
35
46
  unwrap_object(options[:object])
36
47
  end
37
48
 
38
49
  def setup_build(**options)
39
50
  options
40
51
  end
52
+
53
+ def setup_build_hooks
54
+ self.class.i_setup_build_hooks || []
55
+ end
56
+
57
+ private
58
+
59
+ def before_setup_build_object(**options)
60
+ options = setup(**options)
61
+ options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
62
+ options = setup_build(**options)
63
+ setup_build_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
64
+ end
41
65
  end
42
66
  end
43
67
  end
@@ -15,6 +15,7 @@ module SnFoil
15
15
  class_methods do # rubocop:disable Metrics/BlockLength
16
16
  attr_reader :i_params, :i_setup_change_hooks, :i_before_change_hooks, :i_after_change_hooks,
17
17
  :i_after_change_success_hooks, :i_after_change_failure_hooks
18
+
18
19
  def params(*whitelisted_params)
19
20
  @i_params ||= []
20
21
  @i_params |= whitelisted_params
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/concern'
4
- require_relative './setup_context'
5
4
  require_relative './change_context'
6
5
 
7
6
  module SnFoil
@@ -10,13 +9,16 @@ module SnFoil
10
9
  extend ActiveSupport::Concern
11
10
 
12
11
  included do
13
- include SetupContext
12
+ include BuildContext
14
13
  include ChangeContext
14
+
15
+ alias_method :setup_create_object, :setup_build_object
15
16
  end
16
17
 
17
18
  class_methods do
18
19
  attr_reader :i_setup_create_hooks, :i_before_create_hooks, :i_after_create_hooks,
19
20
  :i_after_create_success_hooks, :i_after_create_failure_hooks
21
+
20
22
  def create(params:, entity: nil, **options)
21
23
  new(entity).create(**options, params: params)
22
24
  end
@@ -52,25 +54,14 @@ module SnFoil
52
54
  end
53
55
  end
54
56
 
55
- def setup_create_object(params: {}, object: nil, **options)
56
- object = if object
57
- wrap_object(object)
58
- else
59
- klass = options.fetch(:model) { model }
60
- wrap_object(klass).new
61
- end
62
-
63
- object.attributes = params
64
- options.merge! object: object
65
- end
66
-
67
57
  def create(**options)
68
58
  options[:action] = :create
59
+ options = before_setup_build_object(**options)
69
60
  options = before_setup_create_object(**options)
70
61
  options = setup_create_object(**options)
71
- authorize(options[:object], :create?, **options)
62
+ authorize(options[:object], options.fetch(:authorize, :create?), **options)
72
63
  options = create_hooks(**options)
73
- unwrap_object(options[:object])
64
+ options[:object]
74
65
  end
75
66
 
76
67
  def setup_create(**options)
@@ -116,18 +107,18 @@ module SnFoil
116
107
  private
117
108
 
118
109
  def before_setup_create_object(**options)
119
- options = setup_create(**options)
120
- options = setup_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
121
110
  options = setup_change(**options)
122
111
  options = setup_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
123
- options = setup(**options)
124
- setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
112
+ options = setup_create(**options)
113
+ setup_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
125
114
  end
126
115
 
127
116
  # This method is private to help protect the order of execution of hooks
128
117
  def create_hooks(options)
129
118
  options = before_create_save(**options)
130
- options = if options[:object].save
119
+ save_successful = options[:object].save
120
+ options.merge!(object: unwrap_object(options[:object]))
121
+ options = if save_successful
131
122
  after_create_save_success(**options)
132
123
  else
133
124
  after_create_save_failure(**options)
@@ -136,31 +127,31 @@ module SnFoil
136
127
  end
137
128
 
138
129
  def before_create_save(**options)
139
- options = before_create(**options)
140
- options = before_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
141
130
  options = before_change(**options)
142
- before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
131
+ options = before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
132
+ options = before_create(**options)
133
+ before_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
143
134
  end
144
135
 
145
136
  def after_create_save(**options)
146
- options = after_create(**options)
147
- options = after_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
148
137
  options = after_change(**options)
149
- after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
138
+ options = after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
139
+ options = after_create(**options)
140
+ after_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
150
141
  end
151
142
 
152
143
  def after_create_save_success(**options)
153
- options = after_create_success(**options)
154
- options = after_create_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
155
144
  options = after_change_success(**options)
156
- after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
145
+ options = after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
146
+ options = after_create_success(**options)
147
+ after_create_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
157
148
  end
158
149
 
159
150
  def after_create_save_failure(**options)
160
- options = after_create_failure(**options)
161
- options = after_create_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
162
151
  options = after_change_failure(**options)
163
- after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
152
+ options = after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
153
+ options = after_create_failure(**options)
154
+ after_create_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
164
155
  end
165
156
  end
166
157
  end
@@ -17,6 +17,7 @@ module SnFoil
17
17
  class_methods do
18
18
  attr_reader :i_setup_destroy_hooks, :i_before_destroy_hooks, :i_after_destroy_hooks,
19
19
  :i_after_destroy_success_hooks, :i_after_destroy_failure_hooks
20
+
20
21
  def destroy(id:, entity: nil, **options)
21
22
  new(entity).destroy(**options, id: id)
22
23
  end
@@ -62,9 +63,9 @@ module SnFoil
62
63
  options[:action] = :destroy
63
64
  options = before_setup_destroy_object(**options)
64
65
  options = setup_destroy_object(**options)
65
- authorize(options[:object], :destroy?, **options)
66
+ authorize(options[:object], options.fetch(:authorize, :destroy?), **options)
66
67
  options = destroy_hooks(**options)
67
- unwrap_object(options[:object])
68
+ options[:object]
68
69
  end
69
70
 
70
71
  def setup_destroy(**options)
@@ -110,18 +111,20 @@ module SnFoil
110
111
  private
111
112
 
112
113
  def before_setup_destroy_object(**options)
113
- options = setup_destroy(**options)
114
- options = setup_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
114
+ options = setup(**options)
115
+ options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
115
116
  options = setup_change(**options)
116
117
  options = setup_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
117
- options = setup(**options)
118
- setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
118
+ options = setup_destroy(**options)
119
+ setup_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
119
120
  end
120
121
 
121
122
  # This method is private to help protect the order of execution of hooks
122
123
  def destroy_hooks(options)
123
124
  options = before_destroy_save(options)
124
- options = if options[:object].destroy
125
+ destroy_successful = options[:object].destroy
126
+ options.merge!(object: unwrap_object(options[:object]))
127
+ options = if destroy_successful
125
128
  after_destroy_save_success(options)
126
129
  else
127
130
  after_destroy_save_failure(options)
@@ -130,31 +133,31 @@ module SnFoil
130
133
  end
131
134
 
132
135
  def before_destroy_save(options)
133
- options = before_destroy(**options)
134
- options = before_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
135
136
  options = before_change(**options)
136
- before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
137
+ options = before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
138
+ options = before_destroy(**options)
139
+ before_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
137
140
  end
138
141
 
139
142
  def after_destroy_save(options)
140
- options = after_destroy(**options)
141
- options = after_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
142
143
  options = after_change(**options)
143
- after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
144
+ options = after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
145
+ options = after_destroy(**options)
146
+ after_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
144
147
  end
145
148
 
146
149
  def after_destroy_save_success(options)
147
- options = after_destroy_success(**options)
148
- options = after_destroy_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
149
150
  options = after_change_success(**options)
150
- after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
151
+ options = after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
152
+ options = after_destroy_success(**options)
153
+ after_destroy_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
151
154
  end
152
155
 
153
156
  def after_destroy_save_failure(options)
154
- options = after_destroy_failure(**options)
155
- options = after_destroy_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
156
157
  options = after_change_failure(**options)
157
- after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
158
+ options = after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
159
+ options = after_destroy_failure(**options)
160
+ after_destroy_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
158
161
  end
159
162
  end
160
163
  end
@@ -54,10 +54,10 @@ module SnFoil
54
54
  private
55
55
 
56
56
  def before_setup_index(**options)
57
- options = setup_index(**options)
58
- options = setup_index_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
59
57
  options = setup(**options)
60
- setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
58
+ options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
59
+ options = setup_index(**options)
60
+ setup_index_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
61
61
  end
62
62
  end
63
63
  end
@@ -43,6 +43,7 @@ module SnFoil
43
43
  end
44
44
 
45
45
  attr_reader :entity
46
+
46
47
  def initialize(entity = nil)
47
48
  @entity = entity
48
49
  end
@@ -51,10 +51,10 @@ module SnFoil
51
51
  private
52
52
 
53
53
  def before_setup_show(**options)
54
- options = setup_show(**options)
55
- options = setup_show_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
56
54
  options = setup(**options)
57
- setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
55
+ options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
56
+ options = setup_show(**options)
57
+ setup_show_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
58
58
  end
59
59
  end
60
60
  end
@@ -17,6 +17,7 @@ module SnFoil
17
17
  class_methods do
18
18
  attr_reader :i_setup_update_hooks, :i_before_update_hooks, :i_after_update_hooks,
19
19
  :i_after_update_success_hooks, :i_after_update_failure_hooks
20
+
20
21
  def update(id:, params:, entity: nil, **options)
21
22
  new(entity).update(**options, id: id, params: params)
22
23
  end
@@ -56,7 +57,8 @@ module SnFoil
56
57
  raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
57
58
 
58
59
  object = wrap_object(object || scope.resolve.find(id))
59
- authorize(object, :update?, **options)
60
+ authorize(object, options.fetch(:authorize, :update?), **options)
61
+
60
62
  object.attributes = params
61
63
  options.merge! object: object
62
64
  end
@@ -65,9 +67,9 @@ module SnFoil
65
67
  options[:action] = :update
66
68
  options = before_setup_update_object(**options)
67
69
  options = setup_update_object(**options)
68
- authorize(options[:object], :update?, **options)
70
+ authorize(options[:object], options.fetch(:authorize, :update?), **options)
69
71
  options = update_hooks(**options)
70
- unwrap_object(options[:object])
72
+ options[:object]
71
73
  end
72
74
 
73
75
  def setup_update(**options)
@@ -113,18 +115,20 @@ module SnFoil
113
115
  private
114
116
 
115
117
  def before_setup_update_object(**options)
116
- options = setup_update(**options)
117
- options = setup_update_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
118
+ options = setup(**options)
119
+ options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
118
120
  options = setup_change(**options)
119
121
  options = setup_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
120
- options = setup(**options)
121
- setup_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
122
+ options = setup_update(**options)
123
+ setup_update_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
122
124
  end
123
125
 
124
126
  # This method is private to help protect the order of execution of hooks
125
127
  def update_hooks(options)
126
128
  options = before_update_save(options)
127
- options = if options[:object].save
129
+ update_successful = options[:object].save
130
+ options.merge!(object: unwrap_object(options[:object]))
131
+ options = if update_successful
128
132
  after_update_save_success(options)
129
133
  else
130
134
  after_update_save_failure(options)
@@ -133,31 +137,31 @@ module SnFoil
133
137
  end
134
138
 
135
139
  def before_update_save(options)
136
- options = before_update(**options)
137
- options = before_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
138
140
  options = before_change(**options)
139
- before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
141
+ options = before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
142
+ options = before_update(**options)
143
+ before_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
140
144
  end
141
145
 
142
146
  def after_update_save(options)
143
- options = after_update(**options)
144
- options = after_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
145
147
  options = after_change(**options)
146
- after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
148
+ options = after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
149
+ options = after_update(**options)
150
+ after_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
147
151
  end
148
152
 
149
153
  def after_update_save_success(options)
150
- options = after_update_success(**options)
151
- options = after_update_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
152
154
  options = after_change_success(**options)
153
- after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
155
+ options = after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
156
+ options = after_update_success(**options)
157
+ after_update_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
154
158
  end
155
159
 
156
160
  def after_update_save_failure(options)
157
- options = after_update_failure(**options)
158
- options = after_update_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
159
161
  options = after_change_failure(**options)
160
- after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
162
+ options = after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
163
+ options = after_update_failure(**options)
164
+ after_update_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
161
165
  end
162
166
  end
163
167
  end
@@ -8,6 +8,7 @@ module SnFoil
8
8
 
9
9
  attr_reader :record, :entity
10
10
  attr_accessor :options
11
+
11
12
  def initialize(entity, record, options = {})
12
13
  @record = record
13
14
  @entity = entity
@@ -7,7 +7,7 @@ module SnFoil
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  class_methods do
10
- attr_reader :i_model, :i_setup, :i_filters, :i_search_step
10
+ attr_reader :i_model, :i_setup, :i_filters, :i_search_step, :i_booleans
11
11
 
12
12
  def model(klass = nil)
13
13
  @i_model = klass
@@ -27,6 +27,11 @@ module SnFoil
27
27
  unless: options[:unless]
28
28
  }
29
29
  end
30
+
31
+ def booleans(*fields)
32
+ @i_booleans ||= []
33
+ @i_booleans |= fields.map(&:to_sym)
34
+ end
30
35
  end
31
36
 
32
37
  def model
@@ -34,6 +39,7 @@ module SnFoil
34
39
  end
35
40
 
36
41
  attr_reader :scope
42
+
37
43
  def initialize(scope: nil)
38
44
  @scope = scope || model.all
39
45
  end
@@ -55,6 +61,10 @@ module SnFoil
55
61
  self.class.i_filters || []
56
62
  end
57
63
 
64
+ def booleans
65
+ self.class.i_booleans || []
66
+ end
67
+
58
68
  private
59
69
 
60
70
  def apply_setup(filtered_scope, params)
@@ -90,15 +100,24 @@ module SnFoil
90
100
 
91
101
  def transform_params_booleans(params)
92
102
  params.map do |key, value|
93
- value = if value == 'true'
94
- true
95
- elsif value == 'false'
96
- false
103
+ value = if booleans.include?(key.to_sym)
104
+ value_to_boolean(value)
97
105
  else
98
106
  value
99
107
  end
100
108
  [key, value]
101
109
  end.to_h
102
110
  end
111
+
112
+ def value_to_boolean(value)
113
+ string_val = value.to_s
114
+ if value == true || %w[true 1].include?(string_val)
115
+ true
116
+ elsif value == false || %w[false 0].include?(string_val)
117
+ false
118
+ else
119
+ value
120
+ end
121
+ end
103
122
  end
104
123
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SnFoil
4
- VERSION = '0.5.4'
4
+ VERSION = '0.8.1'
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snfoil
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Howes
8
8
  - Danny Murphy
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-01 00:00:00.000000000 Z
12
+ date: 2021-06-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -129,29 +129,29 @@ dependencies:
129
129
  requirements:
130
130
  - - "~>"
131
131
  - !ruby/object:Gem::Version
132
- version: 0.76.0
132
+ version: '1.14'
133
133
  type: :development
134
134
  prerelease: false
135
135
  version_requirements: !ruby/object:Gem::Requirement
136
136
  requirements:
137
137
  - - "~>"
138
138
  - !ruby/object:Gem::Version
139
- version: 0.76.0
139
+ version: '1.14'
140
140
  - !ruby/object:Gem::Dependency
141
141
  name: rubocop-rspec
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - "~>"
145
145
  - !ruby/object:Gem::Version
146
- version: 1.36.0
146
+ version: '2.3'
147
147
  type: :development
148
148
  prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
150
150
  requirements:
151
151
  - - "~>"
152
152
  - !ruby/object:Gem::Version
153
- version: 1.36.0
154
- description:
153
+ version: '2.3'
154
+ description:
155
155
  email:
156
156
  - howeszy@gmail.com
157
157
  - dmurph24@gmail.com
@@ -162,6 +162,7 @@ files:
162
162
  - CODE_OF_CONDUCT.md
163
163
  - README.md
164
164
  - Rakefile
165
+ - TODO.md
165
166
  - lib/sn_foil.rb
166
167
  - lib/sn_foil/adapters/orms/active_record.rb
167
168
  - lib/sn_foil/adapters/orms/base_adapter.rb
@@ -177,11 +178,11 @@ files:
177
178
  - lib/sn_foil/policy.rb
178
179
  - lib/sn_foil/searcher.rb
179
180
  - lib/sn_foil/version.rb
180
- homepage: https://github.com/howeszy/snfoil
181
+ homepage: https://github.com/limited-effort/snfoil
181
182
  licenses:
182
183
  - MIT
183
184
  metadata: {}
184
- post_install_message:
185
+ post_install_message:
185
186
  rdoc_options: []
186
187
  require_paths:
187
188
  - lib
@@ -189,15 +190,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
189
190
  requirements:
190
191
  - - ">="
191
192
  - !ruby/object:Gem::Version
192
- version: '0'
193
+ version: 2.5.0
193
194
  required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  requirements:
195
196
  - - ">="
196
197
  - !ruby/object:Gem::Version
197
198
  version: '0'
198
199
  requirements: []
199
- rubygems_version: 3.0.3
200
- signing_key:
200
+ rubygems_version: 3.1.4
201
+ signing_key:
201
202
  specification_version: 4
202
203
  summary: A boilerplate gem for providing basic contexts
203
204
  test_files: []