jsonapionify 0.11.11 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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