Skip to content

Commit

Permalink
Merge pull request #65 from cerebris/callback_rework
Browse files Browse the repository at this point in the history
Callback rework
  • Loading branch information
dgeb committed Jan 23, 2015
2 parents 47a89de + fa41aeb commit d867800
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 60 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,49 @@ class AuthorResource < JSONAPI::Resource
end
```

#### Callbacks

`ActiveSupport::Callbacks` is used to provide callback functionality, so the behavior is very similar to what you may be used to from `ActiveRecord`.

For example, you might use a callback to perform authorization on your resource before an action.

```ruby
class BaseResource < JSONAPI::Resource
before_create :authorize_create

def authorize_create
# ...
end
end
```

The types of supported callbacks are:
- `before`
- `after`
- `around`

##### `JSONAPI::Resource` Callbacks

Callbacks can be defined for the following `JSONAPI::Resource` events:

- `:create`
- `:update`
- `:remove`
- `:save`
- `:create_has_many_link`
- `:replace_has_many_links`
- `:create_has_one_link`
- `:replace_has_one_link`
- `:remove_has_many_link`
- `:remove_has_one_link`
- `:replace_fields`

##### `JSONAPI::OperationsProcessor` Callbacks

Callbacks can also be defined for `JSONAPI::OperationsProcessor` events:
- `:operations`: The set of operations.
- `:operation`: The individual operations.

### Controllers

`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.
Expand Down
52 changes: 52 additions & 0 deletions lib/jsonapi/callbacks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'active_support/callbacks'

module JSONAPI
module Callbacks

def self.included(base)
base.class_eval do
include ActiveSupport::Callbacks
base.extend ClassMethods
end
end

module ClassMethods
def define_jsonapi_resources_callbacks(*callbacks)
options = callbacks.extract_options!
options = {
only: [:before, :around, :after]
}.merge!(options)

types = Array(options.delete(:only))

callbacks.each do |callback|
define_callbacks(callback, options)

types.each do |type|
send("_define_#{type}_callback", self, callback)
end
end
end

private

def _define_before_callback(klass, callback) #:nodoc:
klass.define_singleton_method("before_#{callback}") do |*args, &block|
set_callback(:"#{callback}", :before, *args, &block)
end
end

def _define_around_callback(klass, callback) #:nodoc:
klass.define_singleton_method("around_#{callback}") do |*args, &block|
set_callback(:"#{callback}", :around, *args, &block)
end
end

def _define_after_callback(klass, callback) #:nodoc:
klass.define_singleton_method("after_#{callback}") do |*args, &block|
set_callback(:"#{callback}", :after, *args, &block)
end
end
end
end
end
10 changes: 1 addition & 9 deletions lib/jsonapi/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def initialize(resource_klass, values = {})
def apply(context)
resource = @resource_klass.create(context)
resource.replace_fields(@values)
resource.save

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

Expand Down Expand Up @@ -64,7 +63,6 @@ def initialize(resource_klass, resource_id, values)
def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
resource.replace_fields(values)
resource.save

return JSONAPI::OperationResult.new(:ok, resource)
end
Expand All @@ -83,7 +81,6 @@ def initialize(resource_klass, resource_id, association_type, key_value)
def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
resource.create_has_one_link(@association_type, @key_value)
resource.save

return JSONAPI::OperationResult.new(:no_content)
end
Expand All @@ -102,7 +99,6 @@ def initialize(resource_klass, resource_id, association_type, key_value)
def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
resource.replace_has_one_link(@association_type, @key_value)
resource.save

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

def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
@key_values.each do |value|
resource.create_has_many_link(@association_type, value)
end
resource.create_has_many_links(@association_type, @key_values)

return JSONAPI::OperationResult.new(:no_content)
end
Expand All @@ -141,7 +135,6 @@ def initialize(resource_klass, resource_id, association_type, key_values)
def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
resource.replace_has_many_links(@association_type, @key_values)
resource.save

return JSONAPI::OperationResult.new(:no_content)
end
Expand Down Expand Up @@ -180,7 +173,6 @@ def initialize(resource_klass, resource_id, association_type)
def apply(context)
resource = @resource_klass.find_by_key(@resource_id, context: context)
resource.remove_has_one_link(@association_type)
resource.save

return JSONAPI::OperationResult.new(:no_content)
end
Expand Down
42 changes: 20 additions & 22 deletions lib/jsonapi/operations_processor.rb
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
require 'jsonapi/operation_result'
require 'jsonapi/callbacks'

module JSONAPI
class OperationsProcessor
include Callbacks
define_jsonapi_resources_callbacks :operation, :operations

def process(request)
@results = []
@resources = []

context = request.context

transaction {
request.operations.each do |operation|
before_operation(context, operation)

result = operation.apply(context)

after_operation(context, result)

@results.push(result)
if result.has_errors?
rollback
@request = request
@context = request.context
@operations = request.operations

run_callbacks :operations do
transaction do
@operations.each do |operation|
@operation = operation
@result = nil
run_callbacks :operation do
@result = @operation.apply(@context)
@results.push(@result)
if @result.has_errors?
rollback
end
end
end
end
}
end
@results
end

def before_operation(context, operation)
end

def after_operation(context, result)
end

private

# The base OperationsProcessor provides no transaction support
Expand Down
Loading

0 comments on commit d867800

Please sign in to comment.