jsonapionify 0.11.11 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +3 -0
  3. data/examples/example_api.rb +80 -0
  4. data/jsonapionify.gemspec +1 -0
  5. data/lib/jsonapionify/api/action/documentation.rb +2 -2
  6. data/lib/jsonapionify/api/attribute.rb +3 -2
  7. data/lib/jsonapionify/api/context.rb +8 -3
  8. data/lib/jsonapionify/api/context_delegate.rb +6 -0
  9. data/lib/jsonapionify/api/relationship.rb +5 -10
  10. data/lib/jsonapionify/api/relationship/many.rb +46 -14
  11. data/lib/jsonapionify/api/relationship/one.rb +31 -8
  12. data/lib/jsonapionify/api/resource.rb +3 -6
  13. data/lib/jsonapionify/api/resource/builders/fields_builder.rb +2 -2
  14. data/lib/jsonapionify/api/resource/builders/relationship_builder.rb +3 -2
  15. data/lib/jsonapionify/api/resource/builders/relationships_builder.rb +3 -2
  16. data/lib/jsonapionify/api/resource/caching.rb +1 -4
  17. data/lib/jsonapionify/api/resource/callbacks.rb +60 -0
  18. data/lib/jsonapionify/api/resource/caller.rb +8 -7
  19. data/lib/jsonapionify/api/resource/defaults/actions.rb +15 -12
  20. data/lib/jsonapionify/api/resource/defaults/hooks.rb +10 -57
  21. data/lib/jsonapionify/api/resource/defaults/options.rb +3 -8
  22. data/lib/jsonapionify/api/resource/defaults/params.rb +0 -2
  23. data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +24 -29
  24. data/lib/jsonapionify/api/resource/defaults/response_contexts.rb +11 -18
  25. data/lib/jsonapionify/api/resource/definitions/actions.rb +38 -22
  26. data/lib/jsonapionify/api/resource/definitions/attributes.rb +2 -2
  27. data/lib/jsonapionify/api/resource/definitions/helpers.rb +1 -1
  28. data/lib/jsonapionify/api/resource/definitions/includes.rb +16 -0
  29. data/lib/jsonapionify/api/resource/definitions/pagination.rb +7 -11
  30. data/lib/jsonapionify/api/resource/definitions/params.rb +19 -26
  31. data/lib/jsonapionify/api/resource/definitions/relationships.rb +3 -3
  32. data/lib/jsonapionify/api/resource/definitions/request_headers.rb +14 -16
  33. data/lib/jsonapionify/api/resource/definitions/response_headers.rb +2 -1
  34. data/lib/jsonapionify/api/resource/definitions/scopes.rb +17 -6
  35. data/lib/jsonapionify/api/resource/definitions/sorting.rb +8 -7
  36. data/lib/jsonapionify/api/resource/error_handling.rb +3 -2
  37. data/lib/jsonapionify/api/resource/exec.rb +3 -1
  38. data/lib/jsonapionify/api/resource/includer.rb +20 -51
  39. data/lib/jsonapionify/api/response.rb +3 -1
  40. data/lib/jsonapionify/callbacks.rb +10 -7
  41. data/lib/jsonapionify/destructured_proc.rb +27 -0
  42. data/lib/jsonapionify/structure/collections/base.rb +20 -8
  43. data/lib/jsonapionify/structure/objects/base.rb +11 -2
  44. data/lib/jsonapionify/structure/objects/resource_identifier.rb +9 -14
  45. data/lib/jsonapionify/version.rb +1 -1
  46. metadata +20 -2
@@ -3,35 +3,52 @@ require 'active_support/core_ext/array/wrap'
3
3
 
4
4
  module JSONAPIonify::Api
5
5
  module Resource::Definitions::Actions
6
+ using JSONAPIonify::DestructuredProc
6
7
  ActionNotFound = Class.new StandardError
7
8
 
8
- INSTANCE_RESPONSE = proc do |context|
9
- builder =
10
- context.respond_to?(:builder) ? context.builder : nil
11
- context.response_object[:data] =
12
- build_resource(
13
- context: context,
14
- instance: context.instance,
15
- &builder
16
- )
17
- context.response_object.to_json
9
+ DEFAULT_SAVE_COMMIT = proc do |instance:, request_attributes:, request_relationships:|
10
+ # Assign the attributes
11
+ request_attributes.each do |key, value|
12
+ instance.send "#{key}=", value
13
+ end
14
+
15
+ # Assign the relationships
16
+ request_relationships.each do |key, value|
17
+ instance.send "#{key}=", value
18
+ end
19
+
20
+ # Save the instance
21
+ instance.save if instance.respond_to? :save
22
+ end
23
+
24
+ DEFAULT_DELETE_COMMIT = proc do |instance:|
25
+ instance.respond_to?(:destroy) ? instance.destroy : instance.respond_to?(:delete) ? instance.delete : nil
18
26
  end
19
27
 
20
- COLLECTION_RESPONSE = proc do |context|
21
- builder = context.respond_to?(:builder) ? context.builder : nil
22
- context.response_object[:data] = build_resource_collection(
28
+ INSTANCE_RESPONSE = proc do |context, instance:, response_object:, builder: nil|
29
+ response_object[:data] = build_resource(context: context, instance: instance, &builder)
30
+ response_object.to_json
31
+ end
32
+
33
+ CREATE_RESPONSE = proc do |context, instance:, response_object:, builder: nil, response_headers:|
34
+ instance_exec(context, instance: instance, response_object: response_object, builder: builder, &INSTANCE_RESPONSE).tap do
35
+ response_headers['Location'] = response_object[:data][:links][:self]
36
+ end
37
+ end
38
+
39
+ COLLECTION_RESPONSE = proc do |context, response_collection:, links:, response_object:, builder: nil, nested_request: false|
40
+ response_object[:data] = build_resource_collection(
23
41
  context: context,
24
- collection: context.response_collection,
25
- include_cursors: (context.links.keys & [:first, :last, :next, :prev]).present?,
42
+ collection: response_collection,
43
+ include_cursors: (links.keys & [:first, :last, :next, :prev]).present?,
26
44
  &builder
27
45
  )
28
- context.response_object.to_json
46
+ response_object.to_json unless nested_request
29
47
  end
30
48
 
31
49
  def self.extended(klass)
32
50
  klass.class_eval do
33
51
  extend JSONAPIonify::InheritedAttributes
34
- include JSONAPIonify::Callbacks
35
52
  define_callbacks(
36
53
  :request,
37
54
  :exception,
@@ -57,12 +74,9 @@ module JSONAPIonify::Api
57
74
  end
58
75
 
59
76
  def create(**options, &block)
77
+ block ||= DEFAULT_SAVE_COMMIT
60
78
  define_action(:create, 'POST', '', **options, cacheable: false, example_input: :resource, &block).tap do |action|
61
- action.response(status: 201) do |context|
62
- instance_exec(context, &INSTANCE_RESPONSE).tap do
63
- response_headers['Location'] = context.response_object[:data][:links][:self]
64
- end
65
- end
79
+ action.response(status: 201, &CREATE_RESPONSE)
66
80
  end
67
81
  end
68
82
 
@@ -73,12 +87,14 @@ module JSONAPIonify::Api
73
87
  end
74
88
 
75
89
  def update(**options, &block)
90
+ block ||= DEFAULT_SAVE_COMMIT
76
91
  define_action(:update, 'PATCH', '/:id', **options, cacheable: false, example_input: :resource, &block).tap do |action|
77
92
  action.response(status: 200, &INSTANCE_RESPONSE)
78
93
  end
79
94
  end
80
95
 
81
96
  def delete(**options, &block)
97
+ block ||= DEFAULT_DELETE_COMMIT
82
98
  define_action(:delete, 'DELETE', '/:id', **options, cacheable: false, &block).tap do |action|
83
99
  action.response(status: 204)
84
100
  end
@@ -8,8 +8,8 @@ module JSONAPIonify::Api
8
8
  inherited_array_attribute :attributes
9
9
  delegate :id_attribute, :attributes, to: :class
10
10
 
11
- context(:fields, readonly: true) do |context|
12
- context.params['fields']&.map do |type, fields|
11
+ context(:fields, readonly: true) do |params:|
12
+ params['fields']&.map do |type, fields|
13
13
  [type, fields.split(',')]
14
14
  end&.to_h
15
15
  end
@@ -20,7 +20,7 @@ module JSONAPIonify::Api
20
20
  end
21
21
 
22
22
  def on_exception(&block)
23
- before_exception &block
23
+ before_exception(&block)
24
24
  end
25
25
 
26
26
  end
@@ -0,0 +1,16 @@
1
+ module JSONAPIonify::Api
2
+ module Resource::Definitions::Includes
3
+
4
+ def self.extended(klass)
5
+ klass.class_eval do
6
+ extend JSONAPIonify::InheritedAttributes
7
+ inherited_hash_attribute :include_definitions
8
+ end
9
+ end
10
+
11
+ def includable(relationship, &block)
12
+ include_definitions[relationship.to_sym] = block
13
+ end
14
+
15
+ end
16
+ end
@@ -99,29 +99,25 @@ module JSONAPIonify::Api
99
99
  param :page, :before, actions: %i{list}
100
100
  param :page, :first, actions: %i{list}
101
101
  param :page, :last, actions: %i{list}
102
- context :paginated_collection, readonly: true do |context|
103
- if context.root_request?
104
- collection = context.sorted_collection
102
+ context :paginated_collection, readonly: true do |context, nested_request: false, sorted_collection:, collection:, request:, params:, links:|
103
+ if !nested_request
104
+ collection = sorted_collection
105
105
  _, block = pagination_strategies.to_a.reverse.to_h.find do |mod, _|
106
- Object.const_defined?(mod, false) && context.collection.class <= Object.const_get(mod, false)
106
+ Object.const_defined?(mod, false) && collection.class <= Object.const_get(mod, false)
107
107
  end
108
108
 
109
- links_delegate = PaginationLinksDelegate.new(
110
- context.request.url,
111
- context.params,
112
- context.links
113
- )
109
+ links_delegate = PaginationLinksDelegate.new(request.url, params, links)
114
110
 
115
111
  instance_exec(
116
112
  collection,
117
- context.request.params['page'] || {},
113
+ request.params['page'] || {},
118
114
  links_delegate,
119
115
  per,
120
116
  context,
121
117
  &block
122
118
  )
123
119
  else
124
- context.collection
120
+ collection
125
121
  end
126
122
  end
127
123
  end
@@ -6,47 +6,40 @@ module JSONAPIonify::Api
6
6
  extend JSONAPIonify::InheritedAttributes
7
7
  inherited_hash_attribute :param_definitions
8
8
 
9
- context(:params, readonly: true, persisted: true) do |context|
10
- should_error = false
11
-
12
- params = self.class.param_definitions.select do |_, v|
9
+ context(:params, readonly: true, persisted: true) do |request:, action_name:|
10
+ defined_params = self.class.param_definitions.select do |_, v|
13
11
  v.actions.blank? || v.actions.include?(action_name)
14
12
  end
13
+ param_defaults = defined_params.values.select(&:has_default?).map(&:default)
14
+ [*param_defaults, request.params].reduce(:deep_merge)
15
+ end
15
16
 
16
- context.request.params.replace(
17
- [*params.values.select(&:has_default?).map(&:default), context.request.params].reduce(:deep_merge)
18
- )
17
+ # Validate params before request
18
+ before do |params:, action_name:, request:, nested_request: false|
19
+ defined_params = self.class.param_definitions.select do |_, v|
20
+ v.actions.blank? || v.actions.include?(action_name)
21
+ end
19
22
 
20
- required_params = params.select do |_, v|
23
+ required_params = defined_params.select do |_, v|
21
24
  v.required
22
25
  end
23
26
 
24
27
  # Check for validity
25
- context.request.params.each do |k, v|
28
+ params.each do |k, v|
26
29
  keypath = ParamOptions.hash_to_keypaths(k => v)[0]
27
30
  reserved = ParamOptions.reserved?(k)
28
- allowed = params.keys.include? keypath
31
+ allowed = defined_params.keys.include? keypath
29
32
  valid = ParamOptions.valid?(k) || v.is_a?(Hash)
30
- unless reserved || (allowed && valid) || !context.root_request?
31
- should_error = true
33
+ unless reserved || (allowed && valid) || nested_request
32
34
  error :parameter_invalid, ParamOptions.keypath_to_string(*keypath)
33
35
  end
34
- end unless context.request.options?
36
+ end unless request.options?
35
37
 
36
38
  # Check for requirement
37
- missing_params =
38
- ParamOptions.missing_parameters(
39
- context.request.params,
40
- required_params.values.map(&:keypath)
41
- )
42
- if context.root_request? && missing_params.present?
43
- error :parameters_missing, missing_params
44
- end
45
-
46
- halt if should_error
47
-
48
- # Return the params
49
- context.request.params
39
+ missing_params = ParamOptions.missing_parameters(
40
+ request.params, required_params.values.map(&:keypath)
41
+ )
42
+ error :parameters_missing, missing_params if !nested_request && missing_params.present?
50
43
  end
51
44
 
52
45
  end
@@ -8,7 +8,7 @@ module JSONAPIonify::Api
8
8
  end
9
9
  end
10
10
 
11
- def relates_to_many(name, count_attribute: false, count_hidden: false, **opts, &block)
11
+ def relates_to_many(name, count_attribute: false, count_hidden: :list, **opts, &block)
12
12
  define_relationship(name, Relationship::Many, **opts, &block).tap do
13
13
  define_relationship_counter(
14
14
  name,
@@ -23,8 +23,8 @@ module JSONAPIonify::Api
23
23
  define_relationship(name, Relationship::One, **opts, &block)
24
24
  end
25
25
 
26
- def define_relationship_counter(rel_name, name, hidden = false)
27
- attribute name.to_sym, types.Integer, "The number of #{rel_name}.", hidden: hidden, write: false do |_, instance, context|
26
+ def define_relationship_counter(rel_name, name, hidden)
27
+ attribute name.to_sym, types.Integer, "The number of #{rel_name}.", hidden: hidden do |_, instance, context|
28
28
  rel = context.resource.class.relationship(rel_name)
29
29
  rel_context = rel.new(
30
30
  request: context.request,
@@ -6,28 +6,26 @@ module JSONAPIonify::Api
6
6
  extend JSONAPIonify::InheritedAttributes
7
7
  inherited_hash_attribute :request_header_definitions
8
8
 
9
- context(:request_headers, persisted: true, readonly: true) do |context|
10
- should_error = false
9
+ # Define context
10
+ context(:request_headers, persisted: true, readonly: true) do |request:|
11
+ request.headers
12
+ end
11
13
 
12
- # Check for validity
13
- headers = self.class.request_header_definitions.select do |_, v|
14
+ # Validate headers before request
15
+ before do |request_headers:, action_name:, nested_request: false|
16
+ # Gather Definitions
17
+ defined_headers = self.class.request_header_definitions.select do |_, v|
14
18
  v.actions.blank? || v.actions.include?(action_name)
15
19
  end
16
- required_headers = headers.select do |_, v|
17
- v.required
18
- end
19
20
 
20
- missing_keys =
21
- required_headers.keys.map(&:downcase) -
22
- context.request.headers.keys.map(&:downcase)
23
- if context.root_request? && missing_keys.present?
24
- should_error = true
25
- error :headers_missing, missing_keys
21
+ # Gather required Headers
22
+ required_headers = defined_headers.select do |_, v|
23
+ v.required
26
24
  end
27
25
 
28
- halt if should_error
29
-
30
- context.request.headers
26
+ # Gather Missing Keys
27
+ missing_keys = required_headers.keys.map(&:downcase) - request_headers.keys.map(&:downcase)
28
+ error :headers_missing, missing_keys if !nested_request && missing_keys.present?
31
29
  end
32
30
  end
33
31
  end
@@ -1,5 +1,6 @@
1
1
  module JSONAPIonify::Api
2
2
  module Resource::Definitions::ResponseHeaders
3
+ using JSONAPIonify::DestructuredProc
3
4
 
4
5
  def self.extended(klass)
5
6
  klass.class_eval do
@@ -8,7 +9,7 @@ module JSONAPIonify::Api
8
9
 
9
10
  context(:response_headers, persisted: true) do |context|
10
11
  self.class.response_header_definitions.each_with_object({}) do |(name, block), headers|
11
- headers[name.to_s] = instance_exec(context, &block)
12
+ headers[name.to_s] = instance_exec(context, &block.destructure)
12
13
  end
13
14
  end
14
15
  end
@@ -16,14 +16,25 @@ module JSONAPIonify::Api
16
16
  define_singleton_method(:find_instance) do |id|
17
17
  instance_exec(current_scope, id, OpenStruct.new, &block)
18
18
  end
19
- context :instance, persisted: true do |context|
20
- instance_exec(context.scope, context.id, context, &block)
19
+ context :instance, persisted: true do |context, scope:, id:|
20
+ instance_exec(scope, id, context, &block)
21
21
  end
22
22
  end
23
23
 
24
24
  def collection(&block)
25
- context :collection do |context|
26
- Object.new.instance_exec(context.scope, context, &block)
25
+ context :collection do |context, scope:, includes:|
26
+ collection = Object.new.instance_exec(scope, context, &block)
27
+
28
+ # Compute includes manipulations
29
+ self.class.include_definitions.select do |relationship, _|
30
+ includes.keys.include? relationship.to_s
31
+ end.reduce(collection) do |lv, (name, include_block)|
32
+ lv.instance_exec(lv, includes[name.to_s], &include_block)
33
+ end
34
+
35
+ # TODO: Compute field manipulations
36
+
37
+ # TODO: Compute param manipulations
27
38
  end
28
39
  end
29
40
 
@@ -31,8 +42,8 @@ module JSONAPIonify::Api
31
42
  define_singleton_method(:build_instance) do
32
43
  Object.new.instance_exec(current_scope, &block)
33
44
  end
34
- context :new_instance, persisted: true, readonly: true do |context|
35
- Object.new.instance_exec(context.scope, context, &block)
45
+ context :new_instance, persisted: true, readonly: true do |context, scope:|
46
+ Object.new.instance_exec(scope, context, &block)
36
47
  end
37
48
  end
38
49
  end
@@ -3,6 +3,7 @@ require 'set'
3
3
  module JSONAPIonify::Api
4
4
  module Resource::Definitions::Sorting
5
5
  using JSONAPIonify::DeepSortCollection
6
+ using JSONAPIonify::DestructuredProc
6
7
 
7
8
  def self.extended(klass)
8
9
  klass.class_eval do
@@ -10,20 +11,20 @@ module JSONAPIonify::Api
10
11
  delegate :sort_fields_from_sort_string, to: :class
11
12
 
12
13
  # Define Contexts
13
- context :sorted_collection, readonly: true do |context|
14
- if context.root_request?
14
+ context :sorted_collection, readonly: true do |context, collection:, sort_params:, nested_request: false|
15
+ if !nested_request
15
16
  _, block = sorting_strategies.to_a.reverse.to_h.find do |mod, _|
16
- Object.const_defined?(mod, false) && context.collection.class <= Object.const_get(mod, false)
17
+ Object.const_defined?(mod, false) && collection.class <= Object.const_get(mod, false)
17
18
  end
18
19
  context.reset(:sort_params)
19
- instance_exec(context.collection, context.sort_params, context, &block)
20
+ instance_exec(collection, sort_params, context, &block.destructure)
20
21
  else
21
- context.collection
22
+ collection
22
23
  end
23
24
  end
24
25
 
25
- context(:sort_params, readonly: true, persisted: true) do |context|
26
- sort_fields_from_sort_string(context.params['sort'])
26
+ context(:sort_params, readonly: true, persisted: true) do |params:|
27
+ sort_fields_from_sort_string(params['sort'])
27
28
  end
28
29
 
29
30
  define_sorting_strategy('Object') do |collection|
@@ -41,7 +41,7 @@ module JSONAPIonify::Api
41
41
  )
42
42
  }
43
43
  if callbacks
44
- run_callbacks :exception, exception, context, &evaluate
44
+ run_callbacks :exception, exception, &evaluate
45
45
  else
46
46
  evaluate.call
47
47
  end
@@ -95,7 +95,8 @@ module JSONAPIonify::Api
95
95
  errors.evaluate(
96
96
  *args,
97
97
  error_block: lookup_error(name),
98
- runtime_block: block
98
+ runtime_block: block,
99
+ backtrace: caller
99
100
  )
100
101
  end
101
102
 
@@ -1,11 +1,13 @@
1
1
  module JSONAPIonify::Api
2
2
  module Resource::Exec
3
+ using JSONAPIonify::DestructuredProc
4
+
3
5
  def halt
4
6
  # Don't Halt
5
7
  end
6
8
 
7
9
  def exec(&block)
8
- instance_exec @__context, &block
10
+ instance_exec @__context, &block.destructure
9
11
  end
10
12
  end
11
13
  end
@@ -2,26 +2,16 @@ require 'concurrent'
2
2
 
3
3
  module JSONAPIonify::Api
4
4
  module Resource::Includer
5
+ include JSONAPIonify::Structure
5
6
  extend ActiveSupport::Concern
6
7
 
7
8
  included do
8
- before :list, :create, :read, :update do |context|
9
- supports_includes = context.root_request? && context.includes.present?
10
- is_active_record = defined?(ActiveRecord) && context.scope.is_a?(Class) && context.scope < ActiveRecord::Base
11
- if supports_includes && is_active_record
12
- valid_includes = context.includes.select do |k, v|
13
- context.scope._reflect_on_association(k)
14
- end.to_h
15
- context.scope = context.scope.includes valid_includes
16
- end
17
- end
18
-
19
9
  after :commit_list do |context|
20
10
  if context.includes.present?
21
11
  included =
22
- Concurrent::Array.new(context.response_collection).map do |instance|
12
+ context.response_collection.map do |instance|
23
13
  fetch_included(context, owner: instance)
24
- end.reduce(:+)
14
+ end.reduce(:|)
25
15
  append_included(context, included)
26
16
  end
27
17
  end
@@ -35,10 +25,6 @@ module JSONAPIonify::Api
35
25
  end
36
26
  end
37
27
 
38
- context :root_request?, readonly: true do
39
- true
40
- end
41
-
42
28
  context :includes do |context|
43
29
  Resource::Includer.includes_to_hashes context.params['include']
44
30
  end
@@ -46,53 +32,36 @@ module JSONAPIonify::Api
46
32
 
47
33
  def append_included(context, included)
48
34
  if included.present?
49
- context.response_object[:included] = included
50
- context.response_object[:included].tap(&:uniq!).reject! do |r|
51
- Array.wrap(context.response_object[:included]).include?(r) ||
52
- Array.wrap(context.response_object[:data]).include?(r)
53
- end
35
+ context.response_object[:included] |= included
54
36
  end
55
37
  end
56
38
 
57
39
  def fetch_included(context, **overrides)
58
- collection = JSONAPIonify::Structure::Collections::IncludedResources.new
59
- context.includes.each_with_object(collection) do |(name, _),|
60
- res = self.class.relationship(name)
61
- if res.rel.includable?
62
- overrides = overrides.merge includes: context.includes[name],
63
- errors: context.errors,
64
- root_request?: false
65
- *, body =
66
- case res.rel
67
- when Relationship::One
68
- res.call_action(:read, context.request, **overrides)
69
- when Relationship::Many
70
- res.call_action(:list, context.request, **overrides)
71
- end
72
- collection.concat expand_body(body)
40
+ context.includes.reduce(Collections::IncludedResources.new) do |lv, (name, _)|
41
+ if self.class.include_definitions.keys.include?(name.to_sym)
42
+ response_object = JSONAPIonify.new_object
43
+ res = self.class.relationship(name)
44
+ overrides = overrides.merge includes: context.includes[name],
45
+ errors: context.errors,
46
+ root_request?: false,
47
+ action_name: context.action_name,
48
+ response_object: response_object
49
+ action_name = res.rel.is_a?(Relationship::One) ? :read : :list
50
+ res.call_action(action_name, context.request, context_overrides: overrides)
51
+ lv | (Array.wrap(response_object[:data]).compact + response_object[:included])
73
52
  else
74
53
  error :relationship_not_includable, res.rel.name
54
+ lv
75
55
  end
76
- end.uniq
56
+ end
77
57
  end
78
58
 
79
59
  def self.includes_to_hashes(path)
80
- path.to_s.split(',').each_with_object({}) do |path, obj|
81
- rel, *sub_path = path.split('.')
60
+ path.to_s.split(',').each_with_object({}) do |p, obj|
61
+ rel, *sub_path = p.split('.')
82
62
  obj[rel] = includes_to_hashes(sub_path.join('.'))
83
63
  end
84
64
  end
85
65
 
86
- def expand_body(body)
87
- case body
88
- when Rack::BodyProxy
89
- json = JSONAPIonify.parse(body.body.join)
90
- Array.wrap(json[:data]) + (json[:included] || [])
91
- when Array
92
- json = JSONAPIonify.parse(body.join)
93
- Array.wrap(json[:data]) + (json[:included] || [])
94
- end
95
- end
96
-
97
66
  end
98
67
  end