shamu 0.0.7 → 0.0.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17d15280c07fb25429f25d595d1f30f7a5509f52
4
- data.tar.gz: cf2f8593461815af79b589332bdc56569bef2de5
3
+ metadata.gz: afa18618f6ec75e4163bbc06c2ac89d60b5729d6
4
+ data.tar.gz: 0d3fc88e85d907868e82a98d8b877c326eacb4b9
5
5
  SHA512:
6
- metadata.gz: 4f954b06458d9728ccdd30636e478ab90e16cd24280a874fbf402c98bad9b59e67c1691895f80ee57643417f783bee7de16613bcdf0875b6cd036f94048798c9
7
- data.tar.gz: 13f497d44a24128bbe7e479547efcb3da461b72cf3d06fb0ec3c2dd8190b73ff0c79937debbae8bbef6c58c47ea45b2ca516ec0275876563d9728eb3620d9275
6
+ metadata.gz: 405509ea9020934875f4d74015c62b4733511b948c81caa617e67c94ffb3512dffd2264a8f8dcfcccd8277a2835eb142c6a84fb6edf090f6ed8dfdbd9aa8becf
7
+ data.tar.gz: af3f05ab6ac199ae75d9716d430ff3270eab6fc156f74a709ad87be0d70d7042fbcc8865cb5f660cdc60d1494657854b76a8c7e6fc10a2c0039ab98402cd6f35
@@ -1,9 +1,9 @@
1
1
  require "api_responder"
2
2
 
3
3
  class ApiController < ApplicationController
4
- include Shamu::Rails::JsonApi
4
+ include Shamu::JsonApi::Rails::Controller
5
5
 
6
6
  self.responder = ::ApiResponder
7
7
 
8
- respond_to :json, :json_api
8
+ respond_to :json_api, :json
9
9
  end
@@ -1,4 +1,4 @@
1
1
  class ApiResponder < ActionController::Responder
2
2
  include Responders::HttpCacheResponder
3
- include Shamu::Rails::JsonApiResponder
3
+ include Shamu::JsonApi::Rails::Responder
4
4
  end
@@ -1,6 +1,6 @@
1
1
  # Base {Shamu::JsonApi::Presenter} that all other presenters should
2
2
  # inherit from.
3
- class ApplicationPresenter < shamu::JsonApi::Presenter
3
+ class ApplicationPresenter < Shamu::JsonApi::Presenter
4
4
  include ::Rails.application.routes.url_helpers
5
5
 
6
6
  # Override default_url_options in config/environments files.
@@ -0,0 +1,220 @@
1
+ require "rack"
2
+
3
+ module Shamu
4
+ module JsonApi
5
+ module Rails
6
+
7
+ # Add support for writing resources as well-formed JSON API.
8
+ module Controller
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ before_action do
13
+ render json: json_error( "The 'include' parameter is not supported" ), status: :bad_request if params[:include] # rubocop:disable Metrics/LineLength
14
+ request.formats = [ :json_api, :json ]
15
+ end
16
+ end
17
+
18
+ # def process_action( * )
19
+ # # If no format has been specfied, default to json_api
20
+ # request.parameters[:format] ||= "json_api"
21
+ # super
22
+ # end
23
+
24
+ # Builds a well-formed JSON API response for a single resource.
25
+ #
26
+ # @param [Object] resource to present as JSON.
27
+ # @param [Class] presenter {Presenter} class to use when building the
28
+ # response for the given resource. If not given, attempts to find a
29
+ # presenter by calling {Context#find_presenter}.
30
+ # @param (see #json_context)
31
+ # @yield (response) write additional top-level links and meta
32
+ # information.
33
+ # @yieldparam [JsonApi::Response] response
34
+ # @return [JsonApi::Response] the presented JSON response.
35
+ def json_resource( resource, presenter = nil, **context, &block )
36
+ response = build_json_response( context )
37
+ response.resource resource, presenter
38
+ yield response if block_given?
39
+ response.to_json
40
+ end
41
+
42
+ # Builds a well-formed JSON API response for a collection of resources.
43
+ #
44
+ # @param [Enumerable<Object>] resources to present as a JSON array.
45
+ # @param [Class] presenter {Presenter} class to use when building the
46
+ # response for each of the resources. If not given, attempts to find
47
+ # a presenter by calling {Context#find_presenter}
48
+ # @param (see #json_context)
49
+ # @yield (response) write additional top-level links and meta
50
+ # information.
51
+ # @yieldparam [JsonApi::Response] response
52
+ # @return [JsonApi::Response] the presented JSON response.
53
+ def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
54
+ response = build_json_response( context )
55
+ response.collection resources, presenter
56
+ json_paginate_resources response, resources, pagination
57
+ yield response if block_given?
58
+ response.to_json
59
+ end
60
+
61
+ # Write all the validation errors from a record to the response.
62
+ #
63
+ # @param (see Shamu::JsonApi::Response#validation_errors)
64
+ # @yield (builder, attr, message)
65
+ # @yieldparam (see Shamu::JsonApi::Response#validation_errors)
66
+ # @return [JsonApi::Response] the presented JSON response.
67
+ def json_validation_errors( errors, **context, &block )
68
+ response = build_json_response( context )
69
+ response.validation_errors errors, &block
70
+
71
+ response.to_json
72
+ end
73
+
74
+ private
75
+
76
+ # @!visibility public
77
+ #
78
+ # Add page-based pagination links for the resources to the builder.
79
+ #
80
+ # @param [#current_page,#next_page,#previous_page] resources a collection that responds to `#current_page`
81
+ # @param [JsonApi::BaseBuilder] builder to add links to.
82
+ # @param [String] param the name of the key page parameter to adjust
83
+ # @return [void]
84
+ def json_paginate( resources, builder, param: :page )
85
+ page = resources.current_page
86
+
87
+ if resources.respond_to?( :next_page ) ? resources.next_page : true
88
+ builder.link :next, url_for( json_page_parameter( param, :number, page + 1 ) )
89
+ end
90
+
91
+ if resources.respond_to?( :prev_page ) ? resources.prev_page : page > 1
92
+ builder.link :prev, url_for( json_page_parameter( param, :number, page - 1 ) )
93
+ end
94
+ end
95
+
96
+ def json_page_parameter( page_param_name, param, value )
97
+ page_params = params.reverse_merge page_param_name => {}
98
+ page_params[page_param_name][param] = value
99
+
100
+ page_params
101
+ end
102
+
103
+ # @!visibility public
104
+ #
105
+ # Get the pagination request parameters.
106
+ #
107
+ # @param [Symbol] param the request parameter to read pagination
108
+ # options from.
109
+ # @return [Pagination] the pagination state
110
+ def json_pagination( param: :page )
111
+ page_params = params[ param ] || {}
112
+
113
+ Pagination.new( page_params.merge( param: param ) )
114
+ end
115
+
116
+ # @!visibility public
117
+ #
118
+ # Write an error response. See {Shamu::JsonApi::Response#error} for details.
119
+ #
120
+ # @param (see Shamu::JsonApi::Response#error)
121
+ # @yield (builder)
122
+ # @yieldparam [Shamu::JsonApi::ErrorBuilder] builder to customize the
123
+ # error response.
124
+ # @return [JsonApi::Response] the presented JSON response.
125
+ def json_error( error = nil, **context, &block )
126
+ response = build_json_response( context )
127
+
128
+ response.error error do |builder|
129
+ builder.http_status json_http_status_code_from_error( error )
130
+ yield builder if block_given?
131
+ end
132
+
133
+ response.to_json
134
+ end
135
+
136
+ JSON_CONTEXT_KEYWORDS = [ :fields, :namespaces, :presenters ].freeze
137
+
138
+ # @!visibility public
139
+ #
140
+ # Build a {JsonApi::Context} for the current request and controller.
141
+ #
142
+ # @param [Hash<Symbol,Array>] fields to include in the response. If not
143
+ # provided looks for a `fields` request argument and parses that.
144
+ # See {JsonApi::Context#initialize}.
145
+ # @param [Array<String>] namespaces to look for {Presenter presenters}.
146
+ # If not provided automatically adds the controller name and it's
147
+ # namespace.
148
+ #
149
+ # For example in the `Users::AccountController` it will add the
150
+ # `Users::Accounts` and `Users` namespaces.
151
+ #
152
+ # See {JsonApi::Context#find_presenter}.
153
+ # @param [Hash<Class,Class>] presenters a hash that maps resource classes
154
+ # to the presenter class to use when building responses. See
155
+ # {JsonApi::Context#find_presenter}.
156
+ # @return [JsonApi::Context] the builder context honoring any filter
157
+ # parameters sent by the client.
158
+ def json_context( fields: :not_set, namespaces: :not_set, presenters: :not_set )
159
+ Shamu::JsonApi::Context.new fields: fields == :not_set ? json_context_fields : fields,
160
+ namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
161
+ presenters: presenters == :not_set ? json_context_presenters : presenters
162
+ end
163
+
164
+
165
+ def json_context_fields
166
+ params[:fields]
167
+ end
168
+
169
+ def json_context_namespaces
170
+ name = self.class.name.sub /Controller$/, ""
171
+ namespaces = [ name.pluralize ]
172
+ loop do
173
+ name = name.deconstantize
174
+ break if name.blank?
175
+
176
+ namespaces << name
177
+ end
178
+
179
+ namespaces
180
+ end
181
+
182
+ def json_context_presenters
183
+ end
184
+
185
+ def json_paginate_resources( response, resources, pagination )
186
+ pagination = resources.respond_to?( :current_page ) if pagination == :auto
187
+ return unless pagination
188
+
189
+ json_paginate resources, response
190
+ end
191
+
192
+ def json_http_status_code_from_error( error )
193
+ case error
194
+ when ActiveRecord::RecordNotFound then :not_found
195
+ when ActiveRecord::RecordInvalid then :unprocessable_entity
196
+ when /AccessDenied/ then :forbidden
197
+ else
198
+ if error.is_a?( Exception )
199
+ ActionDispatch::ExceptionWrapper.status_code_for_exception( error )
200
+ else
201
+ :bad_request
202
+ end
203
+ end
204
+ end
205
+
206
+ def json_http_status_code_from_request
207
+ case request.method
208
+ when "POST" then :created
209
+ when "HEAD" then :no_content
210
+ else :ok
211
+ end
212
+ end
213
+
214
+ def build_json_response( context )
215
+ Shamu::JsonApi::Response.new( json_context( **context.slice( *JSON_CONTEXT_KEYWORDS ) ) )
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,54 @@
1
+ module Shamu
2
+ module JsonApi
3
+ module Rails
4
+
5
+ # Pagination information gathered from the request.
6
+ class Pagination
7
+ include Attributes
8
+ include Attributes::Assignment
9
+ include Attributes::Validation
10
+
11
+ # ============================================================================
12
+ # @!group Attributes
13
+ #
14
+
15
+ # @!attribute
16
+ # @return [Symbol] the request parameter the pagination was read from. Default `:page`.
17
+ attribute :param, default: :page
18
+
19
+ # @!attribute
20
+ # @return [Integer] the page number.
21
+ attribute :number, coerce: :to_i
22
+
23
+ # @!attribute
24
+ # @return [Integer] the size of each page.
25
+ attribute :size, coerce: :to_i
26
+
27
+ # @!attribute
28
+ # @return [Integer] offset into the list.
29
+ attribute :offset, coerce: :to_i
30
+
31
+ # @!attribute
32
+ # @return [Integer] limit the total number of results.
33
+ attribute :limit, coerce: :to_i
34
+
35
+ # @!attribute
36
+ # @return [String] opaque cursor value
37
+ attribute :cursor, coerce: :to_i
38
+
39
+ #
40
+ # @!endgroup Attributes
41
+
42
+ validate :only_one_kind_of_paging
43
+
44
+ private
45
+
46
+ def only_one_kind_of_paging
47
+ kinds = [ number, offset, cursor ].compact
48
+ errors.add :base, :only_one_kind_of_paging if kinds.count > 2 || ( size && limit )
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ module Shamu
2
+ module JsonApi
3
+ module Rails
4
+
5
+ # Support JSON API responses with the standard rails `#respond_with` method.
6
+ module Responder
7
+
8
+ # Render the response as JSON
9
+ # @return [String]
10
+ def to_json
11
+ if has_errors?
12
+ display_errors
13
+ elsif get?
14
+ display resource
15
+ elsif put? || patch?
16
+ display resource, :location => api_location
17
+ elsif post?
18
+ display resource, :status => :created, :location => api_location
19
+ else
20
+ head :no_content
21
+ end
22
+ end
23
+ alias_method :to_json_api, :to_json
24
+
25
+ protected
26
+
27
+ # @visibility private
28
+ def display( resource, given_options = {} )
29
+ given_options.merge!( options )
30
+
31
+ json =
32
+ if resource.is_a?( Enumerable )
33
+ controller.json_collection resource, **given_options
34
+ else
35
+ controller.json_resource resource, **given_options
36
+ end
37
+
38
+ super json, given_options
39
+ end
40
+
41
+ # @visibility private
42
+ def display_errors
43
+ controller.render format => controller.json_validation_errors( resource_errors ), :status => :unprocessable_entity # rubocop:disable Metrics/LineLength
44
+ end
45
+
46
+ private
47
+
48
+ def validation_resource?( resource )
49
+ resource.respond_to?( :valid? ) && resource.respond_to?( :errors )
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module Shamu
2
+ module JsonApi
3
+ module Rails
4
+ require "shamu/json_api/rails/controller"
5
+ require "shamu/json_api/rails/responder"
6
+ require "shamu/json_api/rails/pagination"
7
+ end
8
+ end
9
+ end
data/lib/shamu/rails.rb CHANGED
@@ -5,8 +5,7 @@ module Shamu
5
5
  require "shamu/rails/entity"
6
6
  require "shamu/rails/controller"
7
7
  require "shamu/rails/features"
8
- require "shamu/rails/json_api"
9
- require "shamu/rails/json_api_responder"
10
8
  require "shamu/rails/railtie"
9
+ require "shamu/json_api/rails"
11
10
  end
12
11
  end
data/lib/shamu/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Shamu
3
3
  # The primary version number
4
- VERSION_NUMBER = "0.0.7".freeze
4
+ VERSION_NUMBER = "0.0.8".freeze
5
5
 
6
6
  # Version suffix such as 'beta' or 'alpha'
7
7
  VERSION_SUFFIX = "".freeze
@@ -8,7 +8,7 @@ module JsonApiControllerSpec
8
8
  end
9
9
 
10
10
  class ResourcesController < ActionController::Base
11
- include Shamu::Rails::JsonApi
11
+ include Shamu::JsonApi::Rails::Controller
12
12
  end
13
13
 
14
14
  module Resources
@@ -78,12 +78,21 @@ describe JsonApiControllerSpec::ResourcesController, type: :controller do
78
78
  end
79
79
 
80
80
  subject do
81
- get :index, format: :json
81
+ get :index
82
82
  JSON.parse( response.body )
83
83
  end
84
84
 
85
85
  it { is_expected.to include "data" => kind_of( Array ) }
86
- it { is_expected.to include "links" => hash_including( "next" ) }
86
+ it { is_expected.to include "links" => include( "next" => match( /page.*number/ ) ) }
87
+ end
88
+
89
+ describe "#json_pagination" do
90
+ it "parses pagination parameters" do
91
+ controller.params[:page] = { number: 3 }
92
+ pagination = controller.send :json_pagination
93
+
94
+ expect( pagination.number ).to eq 3
95
+ end
87
96
  end
88
97
 
89
98
  it "writes an error" do
@@ -0,0 +1,11 @@
1
+ require "rails_helper"
2
+
3
+ describe Shamu::JsonApi::Rails::Pagination do
4
+ it "retains nil value if not set" do
5
+ expect( Shamu::JsonApi::Rails::Pagination.new.number ).to be_nil
6
+ end
7
+
8
+ it "only allows one kind of paging" do
9
+ expect( Shamu::JsonApi::Rails::Pagination.new( size: 1, limit: 1 ) ).not_to be_valid
10
+ end
11
+ end
@@ -8,13 +8,13 @@ module JsonApiResponderSpec
8
8
  end
9
9
 
10
10
  class Responder < ActionController::Responder
11
- include Shamu::Rails::JsonApiResponder
11
+ include Shamu::JsonApi::Rails::Responder
12
12
  end
13
13
 
14
14
  class ResourcesController < ActionController::Base
15
- include Shamu::Rails::JsonApi
15
+ include Shamu::JsonApi::Rails::Controller
16
16
 
17
- respond_to :json, :json_api
17
+ respond_to :json_api, :json
18
18
  self.responder = Responder
19
19
 
20
20
  def json_api_responder_spec_resource_url( * )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shamu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Alexander
@@ -286,6 +286,10 @@ files:
286
286
  - lib/shamu/json_api/error.rb
287
287
  - lib/shamu/json_api/error_builder.rb
288
288
  - lib/shamu/json_api/presenter.rb
289
+ - lib/shamu/json_api/rails.rb
290
+ - lib/shamu/json_api/rails/controller.rb
291
+ - lib/shamu/json_api/rails/pagination.rb
292
+ - lib/shamu/json_api/rails/responder.rb
289
293
  - lib/shamu/json_api/relationship_builder.rb
290
294
  - lib/shamu/json_api/resource_builder.rb
291
295
  - lib/shamu/json_api/response.rb
@@ -301,8 +305,6 @@ files:
301
305
  - lib/shamu/rails/controller.rb
302
306
  - lib/shamu/rails/entity.rb
303
307
  - lib/shamu/rails/features.rb
304
- - lib/shamu/rails/json_api.rb
305
- - lib/shamu/rails/json_api_responder.rb
306
308
  - lib/shamu/rails/railtie.rb
307
309
  - lib/shamu/rspec.rb
308
310
  - lib/shamu/rspec/matchers.rb
@@ -389,6 +391,9 @@ files:
389
391
  - spec/lib/shamu/json_api/common_builder_spec.rb
390
392
  - spec/lib/shamu/json_api/context_spec.rb
391
393
  - spec/lib/shamu/json_api/error_builder_spec.rb
394
+ - spec/lib/shamu/json_api/rails/controller_spec.rb
395
+ - spec/lib/shamu/json_api/rails/pagination_spec.rb
396
+ - spec/lib/shamu/json_api/rails/responder_spec.rb
392
397
  - spec/lib/shamu/json_api/relationship_builder_spec.rb
393
398
  - spec/lib/shamu/json_api/resource_builder_spec.rb
394
399
  - spec/lib/shamu/json_api/response_spec.rb
@@ -400,8 +405,6 @@ files:
400
405
  - spec/lib/shamu/rails/entity_spec.rb
401
406
  - spec/lib/shamu/rails/features.yml
402
407
  - spec/lib/shamu/rails/features_spec.rb
403
- - spec/lib/shamu/rails/json_api_responder_spec.rb
404
- - spec/lib/shamu/rails/json_api_spec.rb
405
408
  - spec/lib/shamu/security/active_record_policy_spec.rb
406
409
  - spec/lib/shamu/security/hashed_value_spec.rb
407
410
  - spec/lib/shamu/security/policy_refinement_spec.rb
@@ -501,6 +504,9 @@ test_files:
501
504
  - spec/lib/shamu/json_api/common_builder_spec.rb
502
505
  - spec/lib/shamu/json_api/context_spec.rb
503
506
  - spec/lib/shamu/json_api/error_builder_spec.rb
507
+ - spec/lib/shamu/json_api/rails/controller_spec.rb
508
+ - spec/lib/shamu/json_api/rails/pagination_spec.rb
509
+ - spec/lib/shamu/json_api/rails/responder_spec.rb
504
510
  - spec/lib/shamu/json_api/relationship_builder_spec.rb
505
511
  - spec/lib/shamu/json_api/resource_builder_spec.rb
506
512
  - spec/lib/shamu/json_api/response_spec.rb
@@ -512,8 +518,6 @@ test_files:
512
518
  - spec/lib/shamu/rails/entity_spec.rb
513
519
  - spec/lib/shamu/rails/features.yml
514
520
  - spec/lib/shamu/rails/features_spec.rb
515
- - spec/lib/shamu/rails/json_api_responder_spec.rb
516
- - spec/lib/shamu/rails/json_api_spec.rb
517
521
  - spec/lib/shamu/security/active_record_policy_spec.rb
518
522
  - spec/lib/shamu/security/hashed_value_spec.rb
519
523
  - spec/lib/shamu/security/policy_refinement_spec.rb
@@ -1,192 +0,0 @@
1
- require "rack"
2
-
3
- module Shamu
4
- module Rails
5
-
6
- # Add support for writing resources as well-formed JSON API.
7
- module JsonApi
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- before_action do
12
- render json: json_error( "The 'include' parameter is not supported" ), status: :bad_request if params[:include] # rubocop:disable Metrics/LineLength
13
- end
14
- end
15
-
16
- def process_action( * )
17
- # If no format has been specfied, default to json_api
18
- request.parameters[:format] ||= "json_api"
19
- super
20
- end
21
-
22
- # Builds a well-formed JSON API response for a single resource.
23
- #
24
- # @param [Object] resource to present as JSON.
25
- # @param [Class] presenter {Presenter} class to use when building the
26
- # response for the given resource. If not given, attempts to find a
27
- # presenter by calling {Context#find_presenter}.
28
- # @param (see #json_context)
29
- # @yield (response) write additional top-level links and meta
30
- # information.
31
- # @yieldparam [JsonApi::Response] response
32
- # @return [JsonApi::Response] the presented JSON response.
33
- def json_resource( resource, presenter = nil, **context, &block )
34
- response = build_json_response( context )
35
- response.resource resource, presenter
36
- yield response if block_given?
37
- response.to_json
38
- end
39
-
40
- # Builds a well-formed JSON API response for a collection of resources.
41
- #
42
- # @param [Enumerable<Object>] resources to present as a JSON array.
43
- # @param [Class] presenter {Presenter} class to use when building the
44
- # response for each of the resources. If not given, attempts to find
45
- # a presenter by calling {Context#find_presenter}
46
- # @param (see #json_context)
47
- # @yield (response) write additional top-level links and meta
48
- # information.
49
- # @yieldparam [JsonApi::Response] response
50
- # @return [JsonApi::Response] the presented JSON response.
51
- def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
52
- response = build_json_response( context )
53
- response.collection resources, presenter
54
- json_paginate_resources response, resources, pagination
55
- yield response if block_given?
56
- response.to_json
57
- end
58
-
59
- # Add page-based pagination links for the resources to the builder.
60
- #
61
- # @param [#current_page,#next_page,#previous_page] resources a collection that responds to `#current_page`
62
- # @param [JsonApi::BaseBuilder] builder to add links to.
63
- # @param [String] param the name of the page parameter to adjust for
64
- # @return [void]
65
- def json_paginate( resources, builder, param: "page[number]" )
66
- page = resources.current_page
67
-
68
- if resources.respond_to?( :next_page ) ? resources.next_page : true
69
- builder.link :next, url_for( params.reverse_merge( param => resources.current_page + 1 ) )
70
- end
71
-
72
- if resources.respond_to?( :prev_page ) ? resources.prev_page : page > 1
73
- builder.link :prev, url_for( params.reverse_merge( param => resources.current_page - 1 ) )
74
- end
75
- end
76
-
77
- # Write an error response. See {Shamu::JsonApi::Response#error} for details.
78
- #
79
- # @param (see Shamu::JsonApi::Response#error)
80
- # @yield (builder)
81
- # @yieldparam [Shamu::JsonApi::ErrorBuilder] builder to customize the
82
- # error response.
83
- # @return [JsonApi::Response] the presented JSON response.
84
- def json_error( error = nil, **context, &block )
85
- response = build_json_response( context )
86
-
87
- response.error error do |builder|
88
- builder.http_status json_http_status_code_from_error( error )
89
- yield builder if block_given?
90
- end
91
-
92
- response.to_json
93
- end
94
-
95
- # Write all the validation errors from a record to the response.
96
- #
97
- # @param (see Shamu::JsonApi::Response#validation_errors)
98
- # @yield (builder, attr, message)
99
- # @yieldparam (see Shamu::JsonApi::Response#validation_errors)
100
- # @return [JsonApi::Response] the presented JSON response.
101
- def json_validation_errors( errors, **context, &block )
102
- response = build_json_response( context )
103
- response.validation_errors errors, &block
104
-
105
- response.to_json
106
- end
107
-
108
- JSON_CONTEXT_KEYWORDS = [ :fields, :namespaces, :presenters ].freeze
109
-
110
- # @!visibility public
111
- #
112
- # Build a {JsonApi::Context} for the current request and controller.
113
- #
114
- # @param [Hash<Symbol,Array>] fields to include in the response. If not
115
- # provided looks for a `fields` request argument and parses that.
116
- # See {JsonApi::Context#initialize}.
117
- # @param [Array<String>] namespaces to look for {Presenter presenters}.
118
- # If not provided automatically adds the controller name and it's
119
- # namespace.
120
- #
121
- # For example in the `Users::AccountController` it will add the
122
- # `Users::Accounts` and `Users` namespaces.
123
- #
124
- # See {JsonApi::Context#find_presenter}.
125
- # @param [Hash<Class,Class>] presenters a hash that maps resource classes
126
- # to the presenter class to use when building responses. See
127
- # {JsonApi::Context#find_presenter}.
128
- # @return [JsonApi::Context] the builder context honoring any filter
129
- # parameters sent by the client.
130
- def json_context( fields: :not_set, namespaces: :not_set, presenters: :not_set )
131
- Shamu::JsonApi::Context.new fields: fields == :not_set ? json_context_fields : fields,
132
- namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
133
- presenters: presenters == :not_set ? json_context_presenters : presenters
134
- end
135
-
136
- private
137
-
138
- def json_context_fields
139
- params[:fields]
140
- end
141
-
142
- def json_context_namespaces
143
- name = self.class.name.sub /Controller$/, ""
144
- namespaces = [ name.pluralize ]
145
- loop do
146
- name = name.deconstantize
147
- break if name.blank?
148
-
149
- namespaces << name
150
- end
151
-
152
- namespaces
153
- end
154
-
155
- def json_context_presenters
156
- end
157
-
158
- def json_paginate_resources( response, resources, pagination )
159
- pagination = resources.respond_to?( :current_page ) if pagination == :auto
160
- return unless pagination
161
-
162
- json_paginate resources, response
163
- end
164
-
165
- def json_http_status_code_from_error( error )
166
- case error
167
- when ActiveRecord::RecordNotFound then :not_found
168
- when ActiveRecord::RecordInvalid then :unprocessable_entity
169
- when /AccessDenied/ then :forbidden
170
- else
171
- if error.is_a?( Exception )
172
- ActionDispatch::ExceptionWrapper.status_code_for_exception( error )
173
- else
174
- :bad_request
175
- end
176
- end
177
- end
178
-
179
- def json_http_status_code_from_request
180
- case request.method
181
- when "POST" then :created
182
- when "HEAD" then :no_content
183
- else :ok
184
- end
185
- end
186
-
187
- def build_json_response( context )
188
- Shamu::JsonApi::Response.new( json_context( **context.slice( *JSON_CONTEXT_KEYWORDS ) ) )
189
- end
190
- end
191
- end
192
- end
@@ -1,53 +0,0 @@
1
- module Shamu
2
- module Rails
3
-
4
- # Support JSON API responses with the standard rails `#respond_with` method.
5
- module JsonApiResponder
6
-
7
- # Render the response as JSON
8
- # @return [String]
9
- def to_json
10
- if has_errors?
11
- display_errors
12
- elsif get?
13
- display resource
14
- elsif put? || patch?
15
- display resource, :location => api_location
16
- elsif post?
17
- display resource, :status => :created, :location => api_location
18
- else
19
- head :no_content
20
- end
21
- end
22
- alias_method :to_json_api, :to_json
23
-
24
- protected
25
-
26
- # @visibility private
27
- def display( resource, given_options = {} )
28
- given_options.merge!( options )
29
-
30
- json =
31
- if resource.is_a?( Enumerable )
32
- controller.json_collection resource, **given_options
33
- else
34
- controller.json_resource resource, **given_options
35
- end
36
-
37
- super json, given_options
38
- end
39
-
40
- # @visibility private
41
- def display_errors
42
- controller.render format => controller.json_validation_errors( resource_errors ), :status => :unprocessable_entity # rubocop:disable Metrics/LineLength
43
- end
44
-
45
- private
46
-
47
- def validation_resource?( resource )
48
- resource.respond_to?( :valid? ) && resource.respond_to?( :errors )
49
- end
50
-
51
- end
52
- end
53
- end