jsonapionify 0.0.1.pre → 0.9.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +35 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +0 -2
  5. data/Guardfile +1 -1
  6. data/README.md +13 -8
  7. data/Rakefile +10 -0
  8. data/config.ru +3 -3
  9. data/index.html +1 -0
  10. data/jsonapionify.gemspec +13 -8
  11. data/lib/jsonapionify/api/action.rb +60 -50
  12. data/lib/jsonapionify/api/attribute.rb +13 -2
  13. data/lib/jsonapionify/api/base/app_builder.rb +17 -2
  14. data/lib/jsonapionify/api/base/class_methods.rb +33 -17
  15. data/lib/jsonapionify/api/base/delegation.rb +4 -1
  16. data/lib/jsonapionify/api/base/doc_helper.rb +13 -4
  17. data/lib/jsonapionify/api/base/resource_definitions.rb +13 -2
  18. data/lib/jsonapionify/api/base.rb +22 -6
  19. data/lib/jsonapionify/api/context_delegate.rb +2 -2
  20. data/lib/jsonapionify/api/errors.rb +7 -2
  21. data/lib/jsonapionify/api/errors_object.rb +1 -1
  22. data/lib/jsonapionify/api/header_options.rb +6 -5
  23. data/lib/jsonapionify/api/param_options.rb +49 -7
  24. data/lib/jsonapionify/api/relationship/many.rb +0 -5
  25. data/lib/jsonapionify/api/relationship/one.rb +10 -9
  26. data/lib/jsonapionify/api/relationship.rb +17 -5
  27. data/lib/jsonapionify/api/resource/builders.rb +39 -10
  28. data/lib/jsonapionify/api/resource/class_methods.rb +17 -6
  29. data/lib/jsonapionify/api/resource/defaults/actions.rb +0 -1
  30. data/lib/jsonapionify/api/resource/defaults/errors.rb +11 -11
  31. data/lib/jsonapionify/api/resource/defaults/options.rb +53 -0
  32. data/lib/jsonapionify/api/resource/defaults/params.rb +9 -0
  33. data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +17 -11
  34. data/lib/jsonapionify/api/resource/definitions/actions.rb +51 -45
  35. data/lib/jsonapionify/api/resource/definitions/attributes.rb +2 -2
  36. data/lib/jsonapionify/api/resource/definitions/helpers.rb +18 -0
  37. data/lib/jsonapionify/api/resource/definitions/pagination.rb +183 -53
  38. data/lib/jsonapionify/api/resource/definitions/params.rb +43 -12
  39. data/lib/jsonapionify/api/resource/definitions/request_headers.rb +1 -67
  40. data/lib/jsonapionify/api/resource/definitions/scopes.rb +2 -13
  41. data/lib/jsonapionify/api/resource/definitions/sorting.rb +71 -58
  42. data/lib/jsonapionify/api/resource/error_handling.rb +2 -2
  43. data/lib/jsonapionify/api/resource/includer.rb +6 -0
  44. data/lib/jsonapionify/api/resource.rb +14 -3
  45. data/lib/jsonapionify/api/response.rb +2 -2
  46. data/lib/jsonapionify/api/server/mock_response.rb +2 -2
  47. data/lib/jsonapionify/api/server/request.rb +11 -7
  48. data/lib/jsonapionify/api/server.rb +1 -1
  49. data/lib/jsonapionify/api/sort_field.rb +59 -0
  50. data/lib/jsonapionify/api/sort_field_set.rb +36 -0
  51. data/lib/jsonapionify/callbacks.rb +3 -3
  52. data/lib/jsonapionify/continuation.rb +1 -0
  53. data/lib/jsonapionify/deep_sort_collection.rb +22 -0
  54. data/lib/jsonapionify/documentation/template.erb +196 -77
  55. data/lib/jsonapionify/documentation.rb +9 -9
  56. data/lib/jsonapionify/indented_string.rb +1 -0
  57. data/lib/jsonapionify/inherited_attributes.rb +4 -3
  58. data/lib/jsonapionify/structure/collections/base.rb +2 -1
  59. data/lib/jsonapionify/structure/helpers/errors.rb +1 -1
  60. data/lib/jsonapionify/structure/helpers/object_defaults.rb +2 -1
  61. data/lib/jsonapionify/structure/helpers/validations.rb +2 -1
  62. data/lib/jsonapionify/structure/objects/base.rb +4 -3
  63. data/lib/jsonapionify/structure/objects/top_level.rb +1 -1
  64. data/lib/jsonapionify/types/boolean_type.rb +2 -2
  65. data/lib/jsonapionify/types/date_string_type.rb +1 -1
  66. data/lib/jsonapionify/types/time_string_type.rb +1 -1
  67. data/lib/jsonapionify/version.rb +1 -1
  68. data/lib/jsonapionify.rb +16 -2
  69. metadata +69 -10
  70. data/fixtures/documentation.json +0 -364
  71. data/lib/jsonapionify/api/resource/http.rb +0 -11
  72. data/lib/jsonapionify/enumerable_observer.rb +0 -91
  73. data/lib/jsonapionify/unstrict_proc.rb +0 -28
@@ -7,8 +7,8 @@ module JSONAPIonify::Api
7
7
  end
8
8
 
9
9
  def initialize(request, instance, definitions)
10
- memo = {}
11
- delegate = self
10
+ memo = {}
11
+ delegate = self
12
12
  @definitions = definitions
13
13
 
14
14
  define_singleton_method :request do
@@ -1,6 +1,11 @@
1
1
  module JSONAPIonify::Api
2
2
  module Errors
3
- ResourceNotFound = Class.new StandardError
4
- RelationshipNotFound = Class.new StandardError
3
+ JSONAPIonifyError = Class.new StandardError
4
+ ResourceNotFound = Class.new JSONAPIonifyError
5
+ RelationshipNotFound = Class.new JSONAPIonifyError
6
+ RequestError = Class.new JSONAPIonifyError
7
+ CacheHit = Class.new JSONAPIonifyError
8
+ DoubleCacheError = Class.new JSONAPIonifyError
9
+ InvalidCursor = Class.new JSONAPIonifyError
5
10
  end
6
11
  end
@@ -42,7 +42,7 @@ module JSONAPIonify
42
42
  evaluator.instance_exec(*args, &block) if block
43
43
  end
44
44
  unless ENV['RACK_ENV'] == 'production'
45
- error[:meta] ||= {}
45
+ error[:meta] ||= {}
46
46
  error[:meta][:backtrace] = backtrace
47
47
  end
48
48
  end
@@ -1,12 +1,13 @@
1
1
  module JSONAPIonify::Api
2
2
  class HeaderOptions
3
3
 
4
- attr_reader :name, :actions, :required
4
+ attr_reader :name, :actions, :required, :documented
5
5
 
6
- def initialize(name, actions: nil, required: false)
7
- @name = name
8
- @actions = Array.wrap(actions)
9
- @required = required
6
+ def initialize(name, actions: nil, required: false, documented: true)
7
+ @name = name
8
+ @actions = Array.wrap(actions)
9
+ @required = required
10
+ @documented = documented
10
11
  end
11
12
 
12
13
  end
@@ -1,14 +1,28 @@
1
1
  module JSONAPIonify::Api
2
2
  class ParamOptions
3
+ extend JSONAPIonify::Structure::Helpers::MemberNames
4
+
5
+ def self.reserved?(value)
6
+ %w{sort include}.include? value
7
+ end
8
+
9
+ def self.valid?(value)
10
+ super(value) && value =~ /[^\u0061-\u007A]/
11
+ end
3
12
 
4
13
  def self.hash_to_keypaths(hash)
5
- mapper = lambda do |hash, ary|
6
- hash.each_with_object(ary) do |(k, v), a|
7
- a << (map = [k.to_sym])
8
- mapper[v, map] if v.is_a?(Hash)
14
+ hash.each_with_object([]) do |(k, v), key_paths|
15
+ pather = lambda do |v, current_path|
16
+ if v.is_a? Hash
17
+ v.each do |k, v|
18
+ pather.call(v, [*current_path, k])
19
+ end
20
+ else
21
+ key_paths << current_path.map(&:to_sym)
22
+ end
9
23
  end
24
+ pather.call(v, [k])
10
25
  end
11
- mapper[hash, []].map(&:flatten)
12
26
  end
13
27
 
14
28
  def self.keypath_to_string(*paths)
@@ -30,14 +44,42 @@ module JSONAPIonify::Api
30
44
  end
31
45
  end
32
46
 
33
- attr_reader :keypath, :actions, :required
47
+ attr_reader :keypath, :actions, :required, :sticky
34
48
 
35
- def initialize(*keys, actions: nil, required: false)
49
+ def initialize(*keys, default: nil, actions: nil, required: false, sticky: false)
36
50
  @keypath = keys
51
+ @sticky = sticky
37
52
  @actions = Array.wrap(actions)
53
+ @default = default.to_s
38
54
  @required = required
39
55
  end
40
56
 
57
+ def with_value(value)
58
+ Hash.new.tap do |hash|
59
+ keypath[0..-2].reduce(hash) do |current_hash, key|
60
+ current_hash[key.to_s] = {}
61
+ end[keypath.last.to_s] = value
62
+ end
63
+ end
64
+
65
+ def default
66
+ with_value @default
67
+ end
68
+
69
+ def extract_value(params)
70
+ keypath.reduce(params) do |p, key|
71
+ p[key.to_s]
72
+ end
73
+ end
74
+
75
+ def has_default?
76
+ @default.present?
77
+ end
78
+
79
+ def default_value?(value)
80
+ @default == value
81
+ end
82
+
41
83
  def string
42
84
  self.class.keypath_to_string(*@keypath)
43
85
  end
@@ -12,7 +12,6 @@ module JSONAPIonify::Api
12
12
  options[:prepend] = 'relationships'
13
13
  define_action(:show, 'GET', **options, &block).response status: 200 do |context|
14
14
  context.response_object[:data] = build_identifier_collection(context.collection)
15
- context.meta[:total_count] = context.collection.count
16
15
  context.response_object.to_json
17
16
  end
18
17
  end
@@ -23,7 +22,6 @@ module JSONAPIonify::Api
23
22
  context.owner_context.reset(:instance)
24
23
  context.reset(:collection)
25
24
  context.response_object[:data] = build_identifier_collection(context.collection)
26
- context.meta[:total_count] = context.collection.count
27
25
  context.response_object.to_json
28
26
  end
29
27
  end
@@ -34,7 +32,6 @@ module JSONAPIonify::Api
34
32
  context.owner_context.reset(:instance)
35
33
  context.reset(:collection)
36
34
  context.response_object[:data] = build_identifier_collection(context.collection)
37
- context.meta[:total_count] = context.collection.count
38
35
  context.response_object.to_json
39
36
  end
40
37
  end
@@ -45,7 +42,6 @@ module JSONAPIonify::Api
45
42
  context.owner_context.reset(:instance)
46
43
  context.reset(:collection)
47
44
  context.response_object[:data] = build_identifier_collection(context.collection)
48
- context.meta[:total_count] = context.collection.count
49
45
  context.response_object.to_json
50
46
  end
51
47
  end
@@ -55,7 +51,6 @@ module JSONAPIonify::Api
55
51
  end
56
52
 
57
53
  show
58
- replace { error_now :forbidden }
59
54
  end
60
55
  end
61
56
  end
@@ -4,8 +4,9 @@ module JSONAPIonify::Api
4
4
  prepend_class do
5
5
  rel = self.rel
6
6
  remove_action :list, :create
7
+
7
8
  class << self
8
- undef_method :list, :create
9
+ undef_method :list
9
10
  end
10
11
 
11
12
  define_singleton_method(:show) do |**options, &block|
@@ -16,15 +17,15 @@ module JSONAPIonify::Api
16
17
  end
17
18
  end
18
19
 
19
- define_singleton_method(:replace) do |**options, &block|
20
- options[:prepend] = 'relationships'
21
- define_action(:replace, 'PATCH', '', nil, :resource_identifier, **options, &block).response status: 200 do |context|
22
- context.owner_context.reset(:instance)
23
- context.reset(:instance)
24
- context.response_object[:data] = build_resource_identifier(context.instance)
25
- context.response_object.to_json
26
- end
20
+ define_singleton_method(:replace) do |**options, &block|
21
+ options[:prepend] = 'relationships'
22
+ define_action(:replace, 'PATCH', '', nil, :resource_identifier, **options, &block).response status: 200 do |context|
23
+ context.owner_context.reset(:instance)
24
+ context.reset(:instance)
25
+ context.response_object[:data] = build_resource_identifier(context.instance)
26
+ context.response_object.to_json
27
27
  end
28
+ end
28
29
 
29
30
  context :instance do |context|
30
31
  context.owner_context.instance.send(rel.name)
@@ -30,6 +30,10 @@ module JSONAPIonify::Api
30
30
  owner_context_proc.call(context.request)
31
31
  end
32
32
 
33
+ context(:owner) do |context|
34
+ context.owner_context.instance
35
+ end
36
+
33
37
  context(:id) do
34
38
  nil
35
39
  end
@@ -43,9 +47,17 @@ module JSONAPIonify::Api
43
47
  end
44
48
 
45
49
  define_singleton_method(:build_links) do |base_url|
50
+ build_url = ->(*paths) {
51
+ URI.parse(base_url).tap do |uri|
52
+ uri.path = File.join uri.path, *paths
53
+ params = sticky_params(Rack::Utils.parse_nested_query(uri.query))
54
+ uri.query = params.to_param if params.present?
55
+ end.to_s
56
+ }
57
+
46
58
  JSONAPIonify::Structure::Maps::RelationshipLinks.new(
47
- self: File.join(base_url, 'relationships', rel.name.to_s),
48
- related: File.join(base_url, rel.name.to_s)
59
+ self: build_url['relationships', rel.name.to_s],
60
+ related: build_url[rel.name.to_s]
49
61
  )
50
62
  end
51
63
  end
@@ -59,10 +71,10 @@ module JSONAPIonify::Api
59
71
  @resource = resource || name
60
72
  end
61
73
 
62
- def documentation_object(base_url)
74
+ def documentation_object
63
75
  OpenStruct.new(
64
- name: name,
65
- actions: resource_class.actions.map { |a| a.documentation_object resource_class, base_url, name.to_s, false }
76
+ name: name,
77
+ resource: resource_class.type
66
78
  )
67
79
  end
68
80
 
@@ -4,18 +4,26 @@ module JSONAPIonify::Api
4
4
 
5
5
  module ClassMethods
6
6
  def build_resource(request, instance, fields: api.fields, relationships: true, links: true)
7
+ relationships = false if JSONAPIonify::FALSEY_STRINGS.include? request.params['include-relationships']
7
8
  return nil unless instance
8
9
  resource_url = build_url(request, instance)
9
10
  id = build_id(instance)
10
11
  JSONAPIonify::Structure::Objects::Resource.new.tap do |resource|
11
- resource[:id] = id
12
- resource[:type] = type
13
- resource[:attributes] = fields[type.to_sym].each_with_object(JSONAPIonify::Structure::Objects::Attributes.new) do |member, attributes|
12
+ resource[:id] = id
13
+ resource[:type] = type
14
+
15
+ resource[:attributes] = fields[type.to_sym].each_with_object(JSONAPIonify::Structure::Objects::Attributes.new) do |member, attributes|
14
16
  attributes[member.to_sym] = instance.public_send(member)
15
17
  end
16
- resource[:links] = JSONAPIonify::Structure::Objects::Links.new(
18
+
19
+ resource[:links] = JSONAPIonify::Structure::Objects::Links.new(
17
20
  self: resource_url
18
21
  ) if links
22
+
23
+ resource[:meta] = {
24
+ cursor: build_cursor_from_instance(request, instance)
25
+ }
26
+
19
27
  resource[:relationships] = relationship_definitions.each_with_object(JSONAPIonify::Structure::Maps::Relationships.new) do |rel, hash|
20
28
  hash[rel.name] = build_relationship(request, instance, rel.name)
21
29
  end if relationships
@@ -29,12 +37,28 @@ module JSONAPIonify::Api
29
37
  )
30
38
  end
31
39
 
32
- def build_collection(request, collection, fields: api.fields, relationships: false)
40
+ def build_collection(request, collection, fields: api.fields)
41
+ relationships = JSONAPIonify::TRUTHY_STRINGS.include? request.params['include-relationships']
33
42
  collection.each_with_object(JSONAPIonify::Structure::Collections::Resources.new) do |instance, resources|
34
43
  resources << build_resource(request, instance, fields: fields, relationships: relationships)
35
44
  end
36
45
  end
37
46
 
47
+ def build_cursor_from_instance(request, instance)
48
+ sort_string = request.params['sort']
49
+ sort_fields = sort_fields_from_sort_string(sort_string)
50
+ attrs_with_values = sort_fields.each_with_object({}) do |field, hash|
51
+ hash[field.name] = instance.send(field.name)
52
+ end
53
+ Base64.urlsafe_encode64(JSON.dump(
54
+ {
55
+ t: type,
56
+ s: sort_string,
57
+ a: attrs_with_values
58
+ }
59
+ ))
60
+ end
61
+
38
62
  def build_identifier_collection(collection)
39
63
  collection.each_with_object(JSONAPIonify::Structure::Collections::ResourceIdentifiers.new) do |instance, resource_identifiers|
40
64
  resource_identifiers << build_resource_identifier(instance)
@@ -61,11 +85,16 @@ module JSONAPIonify::Api
61
85
  end
62
86
 
63
87
  def build_url(request, instance = nil)
64
- if instance
65
- File.join(request.root_url, type, build_id(instance))
66
- else
67
- File.join(request.root_url, type)
68
- end
88
+ URI.parse(request.root_url).tap do |uri|
89
+ uri.path =
90
+ if instance
91
+ File.join(uri.path, type, build_id(instance))
92
+ else
93
+ File.join(request.root_url, type)
94
+ end
95
+ sticky_params = self.sticky_params(request.params)
96
+ uri.query = sticky_params.to_param if sticky_params.present?
97
+ end.to_s
69
98
  end
70
99
 
71
100
  def build_id(instance)
@@ -36,23 +36,34 @@ module JSONAPIonify::Api
36
36
  nil
37
37
  end
38
38
 
39
+ def eager_load
40
+ relationships.map(&:name).each do |name|
41
+ relationship name
42
+ end
43
+ end
44
+
39
45
  def get_url(base)
40
- File.join base, type.to_s
46
+ URI.parse(base).tap do |uri|
47
+ uri.path = File.join(uri.path, type)
48
+ params = sticky_params(Rack::Utils.parse_nested_query(uri.query))
49
+ uri.query = params.to_param if params.present?
50
+ end.to_s
41
51
  end
42
52
 
43
- def actions_in_order
53
+ def documented_actions_in_order
44
54
  indexes = %i{list create read update delete add replace remove}
45
- actions.sort_by { |action| indexes.index(action.name) || indexes.length }
55
+ documented_actions.sort_by { |action, _| indexes.index(action.name) || indexes.length }
46
56
  end
47
57
 
48
58
  def documentation_object(base_url)
49
- url = File.join(base_url, type)
50
59
  OpenStruct.new(
51
60
  name: type,
52
61
  description: JSONAPIonify::Documentation.render_markdown(@description || ''),
53
- relationships: relationships.map { |r| r.documentation_object url },
62
+ relationships: relationships.map { |r| r.documentation_object },
54
63
  attributes: attributes.map(&:documentation_object),
55
- actions: actions_in_order.map { |a| a.documentation_object self, base_url, type, true }
64
+ actions: documented_actions_in_order.map do |action, base, args|
65
+ action.documentation_object File.join(base_url, base), *args
66
+ end
56
67
  )
57
68
  end
58
69
 
@@ -4,7 +4,6 @@ module JSONAPIonify::Api
4
4
 
5
5
  included do
6
6
  before(:create) { |context| context.instance = context.new_instance }
7
- list
8
7
  read
9
8
  end
10
9
  end
@@ -8,7 +8,7 @@ module JSONAPIonify::Api
8
8
  rescue_from JSONAPIonify::Structure::ValidationError, error: :jsonapi_validation_error
9
9
  rescue_from Oj::ParseError, error: :json_parse_error
10
10
 
11
- Rack::Utils::SYMBOL_TO_STATUS_CODE.each do |symbol, code|
11
+ Rack::Utils::SYMBOL_TO_STATUS_CODE.reject { |_, v| v < 400 }.each do |symbol, code|
12
12
  message = Rack::Utils::HTTP_STATUS_CODES[code]
13
13
  error symbol do
14
14
  title message
@@ -51,7 +51,7 @@ module JSONAPIonify::Api
51
51
 
52
52
  error :include_parameter_invalid do
53
53
  parameter 'sort'
54
- title 'Sort parameter is invalid'
54
+ title 'Include parameter is invalid'
55
55
  status '400'
56
56
  end
57
57
 
@@ -61,10 +61,10 @@ module JSONAPIonify::Api
61
61
  status '400'
62
62
  end
63
63
 
64
- error :parameter_not_permitted do |param|
64
+ error :parameter_invalid do |param|
65
65
  parameter param
66
- title 'Parameter Not Permitted'
67
- detail "parameter not permitted: #{param}"
66
+ title 'Parameter Invalid'
67
+ detail "parameter invalid: #{param}"
68
68
  status '400'
69
69
  end
70
70
 
@@ -74,18 +74,18 @@ module JSONAPIonify::Api
74
74
  status '400'
75
75
  end
76
76
 
77
- error :header_not_permitted do |header|
78
- title 'Header Not Permitted'
79
- detail "header not permitted: #{header}"
80
- status '400'
81
- end
82
-
83
77
  error :sort_parameter_invalid do
84
78
  parameter 'sort'
85
79
  title 'Sort parameter is invalid'
86
80
  status '400'
87
81
  end
88
82
 
83
+ error :page_parameter_invalid do |*paths|
84
+ parameter ParamOptions.keypath_to_string(*paths)
85
+ title 'Page parameter invalid'
86
+ status '400'
87
+ end
88
+
89
89
  error :request_object_invalid do |context, request_object|
90
90
  context.errors.set request_object.errors.as_collection
91
91
  end
@@ -0,0 +1,53 @@
1
+ module JSONAPIonify::Api
2
+ module Resource::Defaults::Options
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ id :id
6
+ scope { raise NotImplementedError, 'scope not implemented' }
7
+ collection { raise NotImplementedError, 'collection not implemented' }
8
+ instance { raise NotImplementedError, 'instance not implemented' }
9
+ new_instance { raise NotImplementedError, 'new instance not implemented' }
10
+
11
+ context :scope_defined? do |context|
12
+ begin
13
+ !!context.scope
14
+ rescue NotImplementedError
15
+ false
16
+ end
17
+ end
18
+
19
+ context :collection_defined? do |context|
20
+ begin
21
+ !!context.collection
22
+ rescue NotImplementedError
23
+ false
24
+ end
25
+ end
26
+
27
+ context :instance_defined? do |context|
28
+ begin
29
+ !!context.instance
30
+ rescue NotImplementedError
31
+ false
32
+ end
33
+ end
34
+
35
+ context :new_instance_defined? do |context|
36
+ begin
37
+ !!context.new_instance
38
+ rescue NotImplementedError
39
+ false
40
+ end
41
+ end
42
+
43
+ before do |context|
44
+ context.request_headers # pull request_headers so they verify
45
+ end
46
+
47
+ before do |context|
48
+ context.params # pull params so they verify
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module JSONAPIonify::Api
2
+ module Resource::Defaults::Params
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ param :'include-relationships'
7
+ end
8
+ end
9
+ end
@@ -18,20 +18,22 @@ module JSONAPIonify::Api
18
18
  end
19
19
 
20
20
  context(:request_attributes, readonly: true) do |context|
21
- request_object = context.request_object
21
+ request_object = context.request_object
22
+
23
+ # Validate Request Object
24
+ request_object.validate
25
+ error_now(:request_object_invalid, context, request_object) if request_object.errors.present?
26
+
22
27
  request_attributes = context.request_data.fetch(:attributes) do
23
28
  error_now :attributes_missing
24
29
  end
25
30
  request_attributes.tap do |attributes|
31
+ # Check Permitted Attributes
26
32
  writable_attributes = context.request_resource.attributes.select(&:write?)
27
- required_attributes = writable_attributes.select(&:required?).map(&:name)
28
- optional_attributes = writable_attributes.select(&:optional?).map(&:name)
29
- if (extra_attributes = attributes.keys - (optional_attributes + required_attributes)).present?
33
+ if (extra_attributes = attributes.keys - writable_attributes.map(&:name)).present?
30
34
  extra_attributes.each { |attr| error :attribute_not_permitted, attr }
31
- raise error_exception
35
+ raise Errors::RequestError
32
36
  end
33
- request_object.validate
34
- error_now(:request_object_invalid, context, request_object) if request_object.errors.present?
35
37
  end.to_hash
36
38
  end
37
39
 
@@ -41,11 +43,11 @@ module JSONAPIonify::Api
41
43
  instances = data.map.each_with_index do |item, i|
42
44
  begin
43
45
  find_instance item, pointer: "data/#{i}"
44
- rescue error_exception
46
+ rescue Errors::RequestError
45
47
  should_error = true
46
48
  end
47
49
  end
48
- raise error_exception if should_error
50
+ raise Errors::RequestError if should_error
49
51
  instances
50
52
  end
51
53
 
@@ -63,6 +65,10 @@ module JSONAPIonify::Api
63
65
  error_now(:data_missing)
64
66
  }
65
67
  end
68
+
69
+ context(:authentication, readonly: true) do
70
+ OpenStruct.new
71
+ end
66
72
  end
67
73
 
68
74
  def find_instance(item, pointer:)
@@ -75,7 +81,7 @@ module JSONAPIonify::Api
75
81
  self.detail "could not find resource: `#{item[:type]}` with id: #{item[:id]}"
76
82
  end
77
83
  end
78
- raise error_exception if should_error
84
+ raise Errors::RequestError if should_error
79
85
  instance
80
86
  end
81
87
 
@@ -88,7 +94,7 @@ module JSONAPIonify::Api
88
94
  self.detail "could not find resource: `#{item[:type]}`"
89
95
  end
90
96
  end
91
- raise error_exception if should_error
97
+ raise Errors::RequestError if should_error
92
98
  resource
93
99
  end
94
100