Skip to content

Commit d867800

Browse files
committed
Merge pull request #65 from cerebris/callback_rework
Callback rework
2 parents 47a89de + fa41aeb commit d867800

7 files changed

Lines changed: 326 additions & 60 deletions

File tree

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,49 @@ class AuthorResource < JSONAPI::Resource
330330
end
331331
```
332332

333+
#### Callbacks
334+
335+
`ActiveSupport::Callbacks` is used to provide callback functionality, so the behavior is very similar to what you may be used to from `ActiveRecord`.
336+
337+
For example, you might use a callback to perform authorization on your resource before an action.
338+
339+
```ruby
340+
class BaseResource < JSONAPI::Resource
341+
before_create :authorize_create
342+
343+
def authorize_create
344+
# ...
345+
end
346+
end
347+
```
348+
349+
The types of supported callbacks are:
350+
- `before`
351+
- `after`
352+
- `around`
353+
354+
##### `JSONAPI::Resource` Callbacks
355+
356+
Callbacks can be defined for the following `JSONAPI::Resource` events:
357+
358+
- `:create`
359+
- `:update`
360+
- `:remove`
361+
- `:save`
362+
- `:create_has_many_link`
363+
- `:replace_has_many_links`
364+
- `:create_has_one_link`
365+
- `:replace_has_one_link`
366+
- `:remove_has_many_link`
367+
- `:remove_has_one_link`
368+
- `:replace_fields`
369+
370+
##### `JSONAPI::OperationsProcessor` Callbacks
371+
372+
Callbacks can also be defined for `JSONAPI::OperationsProcessor` events:
373+
- `:operations`: The set of operations.
374+
- `:operation`: The individual operations.
375+
333376
### Controllers
334377

335378
`JSONAPI::Resources` provides a class, `ResourceController`, that can be used as the base class for your controllers. `ResourceController` supports `index`, `show`, `create`, `update`, and `destroy` methods. Just deriving your controller from `ResourceController` will give you a fully functional controller.

lib/jsonapi/callbacks.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
require 'active_support/callbacks'
2+
3+
module JSONAPI
4+
module Callbacks
5+
6+
def self.included(base)
7+
base.class_eval do
8+
include ActiveSupport::Callbacks
9+
base.extend ClassMethods
10+
end
11+
end
12+
13+
module ClassMethods
14+
def define_jsonapi_resources_callbacks(*callbacks)
15+
options = callbacks.extract_options!
16+
options = {
17+
only: [:before, :around, :after]
18+
}.merge!(options)
19+
20+
types = Array(options.delete(:only))
21+
22+
callbacks.each do |callback|
23+
define_callbacks(callback, options)
24+
25+
types.each do |type|
26+
send("_define_#{type}_callback", self, callback)
27+
end
28+
end
29+
end
30+
31+
private
32+
33+
def _define_before_callback(klass, callback) #:nodoc:
34+
klass.define_singleton_method("before_#{callback}") do |*args, &block|
35+
set_callback(:"#{callback}", :before, *args, &block)
36+
end
37+
end
38+
39+
def _define_around_callback(klass, callback) #:nodoc:
40+
klass.define_singleton_method("around_#{callback}") do |*args, &block|
41+
set_callback(:"#{callback}", :around, *args, &block)
42+
end
43+
end
44+
45+
def _define_after_callback(klass, callback) #:nodoc:
46+
klass.define_singleton_method("after_#{callback}") do |*args, &block|
47+
set_callback(:"#{callback}", :after, *args, &block)
48+
end
49+
end
50+
end
51+
end
52+
end

lib/jsonapi/operation.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ def initialize(resource_klass, values = {})
2222
def apply(context)
2323
resource = @resource_klass.create(context)
2424
resource.replace_fields(@values)
25-
resource.save
2625

2726
return JSONAPI::OperationResult.new(:created, resource)
2827

@@ -64,7 +63,6 @@ def initialize(resource_klass, resource_id, values)
6463
def apply(context)
6564
resource = @resource_klass.find_by_key(@resource_id, context: context)
6665
resource.replace_fields(values)
67-
resource.save
6866

6967
return JSONAPI::OperationResult.new(:ok, resource)
7068
end
@@ -83,7 +81,6 @@ def initialize(resource_klass, resource_id, association_type, key_value)
8381
def apply(context)
8482
resource = @resource_klass.find_by_key(@resource_id, context: context)
8583
resource.create_has_one_link(@association_type, @key_value)
86-
resource.save
8784

8885
return JSONAPI::OperationResult.new(:no_content)
8986
end
@@ -102,7 +99,6 @@ def initialize(resource_klass, resource_id, association_type, key_value)
10299
def apply(context)
103100
resource = @resource_klass.find_by_key(@resource_id, context: context)
104101
resource.replace_has_one_link(@association_type, @key_value)
105-
resource.save
106102

107103
return JSONAPI::OperationResult.new(:no_content)
108104
end
@@ -120,9 +116,7 @@ def initialize(resource_klass, resource_id, association_type, key_values)
120116

121117
def apply(context)
122118
resource = @resource_klass.find_by_key(@resource_id, context: context)
123-
@key_values.each do |value|
124-
resource.create_has_many_link(@association_type, value)
125-
end
119+
resource.create_has_many_links(@association_type, @key_values)
126120

127121
return JSONAPI::OperationResult.new(:no_content)
128122
end
@@ -141,7 +135,6 @@ def initialize(resource_klass, resource_id, association_type, key_values)
141135
def apply(context)
142136
resource = @resource_klass.find_by_key(@resource_id, context: context)
143137
resource.replace_has_many_links(@association_type, @key_values)
144-
resource.save
145138

146139
return JSONAPI::OperationResult.new(:no_content)
147140
end
@@ -180,7 +173,6 @@ def initialize(resource_klass, resource_id, association_type)
180173
def apply(context)
181174
resource = @resource_klass.find_by_key(@resource_id, context: context)
182175
resource.remove_has_one_link(@association_type)
183-
resource.save
184176

185177
return JSONAPI::OperationResult.new(:no_content)
186178
end

lib/jsonapi/operations_processor.rb

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
11
require 'jsonapi/operation_result'
2+
require 'jsonapi/callbacks'
23

34
module JSONAPI
45
class OperationsProcessor
6+
include Callbacks
7+
define_jsonapi_resources_callbacks :operation, :operations
58

69
def process(request)
710
@results = []
8-
@resources = []
9-
10-
context = request.context
11-
12-
transaction {
13-
request.operations.each do |operation|
14-
before_operation(context, operation)
15-
16-
result = operation.apply(context)
17-
18-
after_operation(context, result)
19-
20-
@results.push(result)
21-
if result.has_errors?
22-
rollback
11+
@request = request
12+
@context = request.context
13+
@operations = request.operations
14+
15+
run_callbacks :operations do
16+
transaction do
17+
@operations.each do |operation|
18+
@operation = operation
19+
@result = nil
20+
run_callbacks :operation do
21+
@result = @operation.apply(@context)
22+
@results.push(@result)
23+
if @result.has_errors?
24+
rollback
25+
end
26+
end
2327
end
2428
end
25-
}
29+
end
2630
@results
2731
end
2832

29-
def before_operation(context, operation)
30-
end
31-
32-
def after_operation(context, result)
33-
end
34-
3533
private
3634

3735
# The base OperationsProcessor provides no transaction support

0 commit comments

Comments
 (0)