Skip to content

Commit 855466a

Browse files
committed
Merge branch 'master' into provide_fields_to_operation
Conflicts: lib/jsonapi/operation.rb
2 parents e5cf559 + 4015be1 commit 855466a

24 files changed

Lines changed: 1125 additions & 1264 deletions

.travis.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ sudo: false
33
env:
44
- "RAILS_VERSION=4.1.0"
55
- "RAILS_VERSION=4.2.6"
6-
- "RAILS_VERSION=5.0.0.beta3"
6+
- "RAILS_VERSION=5.0.0.rc1"
77
rvm:
8-
- 2.0
98
- 2.1
109
- 2.2.4
1110
- 2.3.0
1211
matrix:
1312
exclude:
1413
- rvm: 2.0
15-
env: "RAILS_VERSION=5.0.0.beta3"
14+
env: "RAILS_VERSION=5.0.0.rc1"
1615
- rvm: 2.1
17-
env: "RAILS_VERSION=5.0.0.beta3"
16+
env: "RAILS_VERSION=5.0.0.rc1"

README.md

Lines changed: 79 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ backed by ActiveRecord models or by custom objects.
3636
* [Error Codes] (#error-codes)
3737
* [Handling Exceptions] (#handling-exceptions)
3838
* [Action Callbacks] (#action-callbacks)
39+
* [Operation Processors] (#operation-processors)
3940
* [Serializer] (#serializer)
4041
* [Serializer options] (#serializer-options)
4142
* [Formatting] (#formatting)
@@ -1117,68 +1118,31 @@ Callbacks can be defined for the following `JSONAPI::Resource` events:
11171118
- `:remove_to_one_link`
11181119
- `:replace_fields`
11191120
1120-
##### `JSONAPI::OperationsProcessor` Callbacks
1121+
##### `JSONAPI::Processor` Callbacks
11211122
1122-
Callbacks can also be defined for `JSONAPI::OperationsProcessor` events:
1123-
- `:operations`: The set of operations.
1123+
Callbacks can also be defined for `JSONAPI::Processor` events:
11241124
- `:operation`: Any individual operation.
1125-
- `:find_operation`: A `find_operation`.
1126-
- `:show_operation`: A `show_operation`.
1127-
- `:show_relationship_operation`: A `show_relationship_operation`.
1128-
- `:show_related_resource_operation`: A `show_related_resource_operation`.
1129-
- `:show_related_resources_operation`: A `show_related_resources_operation`.
1130-
- `:create_resource_operation`: A `create_resource_operation`.
1131-
- `:remove_resource_operation`: A `remove_resource_operation`.
1132-
- `:replace_fields_operation`: A `replace_fields_operation`.
1133-
- `:replace_to_one_relationship_operation`: A `replace_to_one_relationship_operation`.
1134-
- `:create_to_many_relationship_operation`: A `create_to_many_relationship_operation`.
1135-
- `:replace_to_many_relationship_operation`: A `replace_to_many_relationship_operation`.
1136-
- `:remove_to_many_relationship_operation`: A `remove_to_many_relationship_operation`.
1137-
- `:remove_to_one_relationship_operation`: A `remove_to_one_relationship_operation`.
1138-
1139-
The operation callbacks have access to two meta data hashes, `@operations_meta` and `@operation_meta`, two links hashes,
1140-
`@operations_links` and `@operation_links`, the full list of `@operations`, each individual `@operation` and the
1141-
`@result` variables.
1142-
1143-
##### Custom `OperationsProcessor` Example to Return total_count in Meta
1144-
1145-
Note: this can also be accomplished with the `top_level_meta_include_record_count` option, and in most cases that will
1146-
be the better option.
1147-
1148-
To return the total record count of a find operation in the meta data of a find operation you can create a custom
1149-
OperationsProcessor. For example:
1150-
1151-
```ruby
1152-
# lib/jsonapi/counting_active_record_operations_processor.rb
1153-
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
1154-
after_find_operation do
1155-
@operation_meta[:total_records] = @operation.record_count
1156-
end
1157-
end
1158-
```
1159-
1160-
Set the configuration option `operations_processor` to use the new `CountingActiveRecordOperationsProcessor` by
1161-
specifying the snake cased name of the class (without the `OperationsProcessor`).
1162-
1163-
```ruby
1164-
require 'jsonapi/counting_active_record_operations_processor'
1165-
1166-
JSONAPI.configure do |config|
1167-
config.operations_processor = :counting_active_record
1168-
end
1169-
```
1170-
1171-
To use a specific `OperationsProcessor` in a `ResourceController`, override the `create_operations_processor` method:
1172-
1173-
```ruby
1174-
def create_operations_processor
1175-
CountingActiveRecordOperationsProcessor.new
1176-
end
1177-
```
1178-
1179-
The callback code will be called after each find. It will use the same options as the find operation, without the
1180-
pagination, to collect the record count. This is stored in the `operation_meta`, which will be returned in the top level
1181-
meta element.
1125+
- `:find`: A `find` operation is being processed.
1126+
- `:show`: A `show` operation is being processed.
1127+
- `:show_relationship`: A `show_relationship` operation is being processed.
1128+
- `:show_related_resource`: A `show_related_resource` operation is being processed.
1129+
- `:show_related_resources`: A `show_related_resources` operation is being processed.
1130+
- `:create_resource`: A `create_resource` operation is being processed.
1131+
- `:remove_resource`: A `remove_resource` operation is being processed.
1132+
- `:replace_fields`: A `replace_fields` operation is being processed.
1133+
- `:replace_to_one_relationship`: A `replace_to_one_relationship` operation is being processed.
1134+
- `:create_to_many_relationship`: A `create_to_many_relationship` operation is being processed.
1135+
- `:replace_to_many_relationship`: A `replace_to_many_relationship` operation is being processed.
1136+
- `:remove_to_many_relationship`: A `remove_to_many_relationship` operation is being processed.
1137+
- `:remove_to_one_relationship`: A `remove_to_one_relationship` operation is being processed.
1138+
1139+
See [Operation Processors] (#operation-processors) for details on using OperationPprocessors
1140+
1141+
##### `JSONAPI::OperationsProcessor` Callbacks (a removed feature)
1142+
1143+
Note: The `JSONAPI::OperationsProcessor` has been removed and replaced with the `JSONAPI::OperationDispatcher`
1144+
and `Processor` classes per resource. The callbacks have been renamed and moved to the
1145+
`Processor`s, with the exception of the `operations` callback which is now on the controller.
11821146
11831147
### Controllers
11841148
@@ -1202,6 +1166,7 @@ end
12021166
Of course you are free to extend this as needed and override action handlers or other methods.
12031167
12041168
A jsonapi-controller generator is avaliable
1169+
12051170
```
12061171
rails generate jsonapi:controller contact
12071172
```
@@ -1417,6 +1382,56 @@ class UsersController < JSONAPI::ResourceController
14171382
end
14181383
```
14191384

1385+
### Operation Processors
1386+
1387+
Operation Processors are called to perform the operation(s) that make up a request. The controller (through the `OperationDispatcher`), creates an `OperatorProcessor` to handle each operation. The processor is created based on the resource name, including the namespace. If a processor does not exist for a resource (namespace matters) the default operation processor is used instead. The default processor can be changed by a configuration setting.
1388+
1389+
Defining a custom `Processor` allows for custom callback handling of each operation type for each resource type. For example:
1390+
1391+
```ruby
1392+
class Api::V4::BookProcessor < JSONAPI::Processor
1393+
after_find do
1394+
unless @result.is_a?(JSONAPI::ErrorsOperationResult)
1395+
@result.meta[:total_records_found] = @result.record_count
1396+
end
1397+
end
1398+
end
1399+
```
1400+
1401+
This simple example uses a callback to update the result's meta property with the total count of records (a redundant
1402+
feature only for example purposes), if there wasn't an error in the operation. It is also possible to override the
1403+
`find` method as well if a different behavior is needed, for example:
1404+
1405+
```ruby
1406+
class Api::V4::BookProcessor < JSONAPI::Processor
1407+
def find
1408+
filters = params[:filters]
1409+
include_directives = params[:include_directives]
1410+
sort_criteria = params.fetch(:sort_criteria, [])
1411+
paginator = params[:paginator]
1412+
1413+
verified_filters = resource_klass.verify_filters(filters, context)
1414+
resource_records = resource_klass.find(verified_filters,
1415+
context: context,
1416+
include_directives: include_directives,
1417+
sort_criteria: sort_criteria,
1418+
paginator: paginator)
1419+
1420+
page_options = {}
1421+
# Overriding the default record count logic to always include it in the meta
1422+
#if (JSONAPI.configuration.top_level_meta_include_record_count ||
1423+
# (paginator && paginator.class.requires_record_count))
1424+
page_options[:record_count] = resource_klass.find_count(verified_filters,
1425+
context: context,
1426+
include_directives: include_directives)
1427+
#end
1428+
end
1429+
```
1430+
1431+
Note: The authors of this gem expect the most common uses cases to be handled using the callbacks. It is likely that the
1432+
internal functionality of the operation processing methods will change, at least for several revisions. Effort will be
1433+
made to call this out in release notes. You have been warned.
1434+
14201435
### Serializer
14211436

14221437
The `ResourceSerializer` can be used to serialize a resource into JSON API compliant JSON. `ResourceSerializer` must be
@@ -1837,8 +1852,7 @@ JR has a few configuration options. Some have already been mentioned above. To s
18371852
initializer and add the options you wish to set. All options have defaults, so you only need to set the options that
18381853
are different. The default options are shown below.
18391854
1840-
If using custom classes (such as the CountingActiveRecordOperationsProcessor, or a CustomPaginator),
1841-
be sure to require them at the top of the initializer before usage.
1855+
If using custom classes (such as a CustomPaginator), be sure to require them at the top of the initializer before usage.
18421856
18431857
```ruby
18441858
JSONAPI.configure do |config|
@@ -1848,8 +1862,9 @@ JSONAPI.configure do |config|
18481862
#:underscored_route, :camelized_route, :dasherized_route, or custom
18491863
config.route_format = :dasherized_route
18501864
1851-
#:basic, :active_record, or custom
1852-
config.operations_processor = :active_record
1865+
# Default Processor, used if a resource specific one is not defined.
1866+
# Must be a class
1867+
config.default_processor_klass = JSONAPI::Processor
18531868
18541869
#:integer, :uuid, :string, or custom (provide a proc)
18551870
config.resource_key_type = :integer

lib/jsonapi-resources.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
require 'jsonapi/exceptions'
1414
require 'jsonapi/error'
1515
require 'jsonapi/error_codes'
16-
require 'jsonapi/request'
17-
require 'jsonapi/operations_processor'
18-
require 'jsonapi/active_record_operations_processor'
16+
require 'jsonapi/request_parser'
17+
require 'jsonapi/operation_dispatcher'
18+
require 'jsonapi/processor'
1919
require 'jsonapi/relationship'
2020
require 'jsonapi/include_directives'
2121
require 'jsonapi/operation_result'

lib/jsonapi/active_record_operations_processor.rb

Lines changed: 0 additions & 35 deletions
This file was deleted.

lib/jsonapi/acts_as_resource_controller.rb

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ module ActsAsResourceController
77

88
def self.included(base)
99
base.extend ClassMethods
10+
base.include Callbacks
1011
base.before_action :ensure_correct_media_type, only: [:create, :update, :create_relationship, :update_relationship]
1112
base.before_action :ensure_valid_accept_media_type
1213
base.cattr_reader :server_error_callbacks
14+
base.define_jsonapi_resources_callbacks :process_operations
1315
end
1416

1517
def index
@@ -57,23 +59,44 @@ def get_related_resources
5759
end
5860

5961
def process_request
60-
@request = JSONAPI::Request.new(params, context: context,
61-
key_formatter: key_formatter,
62-
server_error_callbacks: (self.class.server_error_callbacks || []))
62+
@request = JSONAPI::RequestParser.new(params, context: context,
63+
key_formatter: key_formatter,
64+
server_error_callbacks: (self.class.server_error_callbacks || []))
6365
unless @request.errors.empty?
6466
render_errors(@request.errors)
6567
else
66-
operation_results = create_operations_processor.process(@request)
67-
render_results(operation_results)
68+
process_operations
69+
render_results(@operation_results)
6870
end
6971

7072
rescue => e
7173
handle_exceptions(e)
7274
end
7375

74-
# set the operations processor in the configuration or override this to use another operations processor
75-
def create_operations_processor
76-
JSONAPI.configuration.operations_processor.new
76+
def process_operations
77+
run_callbacks :process_operations do
78+
@operation_results = operation_dispatcher.process(@request.operations)
79+
end
80+
end
81+
82+
def transaction
83+
lambda { |&block|
84+
ActiveRecord::Base.transaction do
85+
block.yield
86+
end
87+
}
88+
end
89+
90+
def rollback
91+
lambda {
92+
fail ActiveRecord::Rollback
93+
}
94+
end
95+
96+
def operation_dispatcher
97+
@operation_dispatcher ||= JSONAPI::OperationDispatcher.new(transaction: transaction,
98+
rollback: rollback,
99+
server_error_callbacks: @request.server_error_callbacks)
77100
end
78101

79102
private

lib/jsonapi/configuration.rb

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
require 'jsonapi/formatter'
2-
require 'jsonapi/operations_processor'
3-
require 'jsonapi/active_record_operations_processor'
2+
require 'jsonapi/processor'
43
require 'concurrent'
54

65
module JSONAPI
@@ -9,13 +8,13 @@ class Configuration
98
:resource_key_type,
109
:route_format,
1110
:raise_if_parameters_not_allowed,
12-
:operations_processor,
1311
:allow_include,
1412
:allow_sort,
1513
:allow_filter,
1614
:default_paginator,
1715
:default_page_size,
1816
:maximum_page_size,
17+
:default_processor_klass,
1918
:use_text_errors,
2019
:top_level_links_include_pagination,
2120
:top_level_meta_include_record_count,
@@ -34,9 +33,6 @@ def initialize
3433
#:underscored_route, :camelized_route, :dasherized_route, or custom
3534
self.route_format = :dasherized_route
3635

37-
#:basic, :active_record, or custom
38-
self.operations_processor = :active_record
39-
4036
#:integer, :uuid, :string, or custom (provide a proc)
4137
self.resource_key_type = :integer
4238

@@ -80,6 +76,10 @@ def initialize
8076
self.always_include_to_one_linkage_data = false
8177
self.always_include_to_many_linkage_data = false
8278

79+
# The default Operation Processor to use if one is not defined specifically
80+
# for a Resource.
81+
self.default_processor_klass = JSONAPI::Processor
82+
8383
# Formatter Caching
8484
# Set to false to disable caching of string operations on keys and links.
8585
self.cache_formatters = true
@@ -144,15 +144,14 @@ def route_formatter
144144
return formatter
145145
end
146146

147-
def operations_processor=(operations_processor)
148-
@operations_processor_name = operations_processor
149-
@operations_processor = JSONAPI::OperationsProcessor.operations_processor_for(@operations_processor_name)
150-
end
151-
152147
def exception_class_whitelisted?(e)
153148
@exception_class_whitelist.flatten.any? { |k| e.class.ancestors.include?(k) }
154149
end
155150

151+
def default_processor_klass=(default_processor_klass)
152+
@default_processor_klass = default_processor_klass
153+
end
154+
156155
attr_writer :allow_include, :allow_sort, :allow_filter
157156

158157
attr_writer :default_paginator

0 commit comments

Comments
 (0)