Module: Shamu::JsonApi::Rails::Controller

Extended by:
ActiveSupport::Concern
Included in:
ApiController
Defined in:
lib/shamu/json_api/rails/controller.rb

Overview

Add support for writing resources as well-formed JSON API.

Constant Summary collapse

ID_PATTERN =

Pattern to identify request params that hold 'ids'

/\A(id|.+_id)\z/

Instance Method Summary collapse

Instance Method Details

#annotate_json_error(error, builder) ⇒ Object

Annotate an exception that is being rendered to the browser - for example to add current user or security information if available.



200
201
202
203
204
205
# File 'lib/shamu/json_api/rails/controller.rb', line 200

def annotate_json_error( error, builder )
  if ::Rails.env.development?
    builder.meta :type, error.class.to_s
    builder.meta :backtrace, error.backtrace
  end
end

#json_context(fields: :not_set, namespaces: :not_set, presenters: :not_set) ⇒ JsonApi::Context

Build a Context for the current request and controller.

Parameters:

  • fields (Hash<Symbol,Array>) (defaults to: :not_set)

    to include in the response. If not provided looks for a fields request argument and parses that. See Context#initialize.

  • namespaces (Array<String>) (defaults to: :not_set)

    to look for presenters. If not provided automatically adds the controller name and it's namespace.

    For example in the Users::AccountController it will add the Users::Accounts and Users namespaces.

    See Context#find_presenter.

  • presenters (Hash<Class,Class>) (defaults to: :not_set)

    a hash that maps resource classes to the presenter class to use when building responses. See Context#find_presenter.

Returns:

  • (JsonApi::Context)

    the builder context honoring any filter parameters sent by the client.



230
231
232
233
234
235
# File 'lib/shamu/json_api/rails/controller.rb', line 230

def json_context( fields: :not_set, namespaces: :not_set, presenters: :not_set )
  Shamu::JsonApi::Context.new \
    fields: fields == :not_set ? json_context_fields : fields,
    namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
    presenters: presenters == :not_set ? json_context_presenters : presenters
end

#json_error(error = nil, **context) {|builder| ... } ⇒ JsonApi::Response

Write an error response. See Shamu::JsonApi::Response#error for details.

Yields:

  • (builder)

Yield Parameters:

Returns:



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/shamu/json_api/rails/controller.rb', line 180

def json_error( error = nil, **context, &block )
  response = build_json_response( context )

  response.error error do |builder|
    builder.http_status json_http_status_code_from_error( error )
    annotate_json_error( error, builder )
    yield builder if block_given?
  end

  response.to_json
end

#json_paginate(resources, builder, param: :page)

This method returns an undefined value.

Add page-based pagination links for the resources to the builder.

Parameters:

  • resources (#current_page, #next_page, #previous_page)

    a collection that responds to #current_page

  • builder (JsonApi::BaseBuilder)

    to add links to.

  • param (String) (defaults to: :page)

    the name of the key page parameter to adjust



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/shamu/json_api/rails/controller.rb', line 136

def json_paginate( resources, builder, param: :page )
  page = resources.current_page

  if resources.respond_to?( :next_page ) ? resources.next_page : true
    builder.link :next, url_for( json_page_parameter( param, :number, page + 1 ) )
  end

  if resources.respond_to?( :prev_page ) ? resources.prev_page : page > 1
    builder.link :prev, url_for( json_page_parameter( param, :number, page - 1 ) )
  end
end

#json_pagination(param: :page) ⇒ Pagination

Get the pagination request parameters.

Parameters:

  • param (Symbol) (defaults to: :page)

    the request parameter to read pagination options from.

Returns:



165
166
167
168
169
# File 'lib/shamu/json_api/rails/controller.rb', line 165

def json_pagination( param: :page )
  page_params = params[ param ] || {}

  Pagination.new( page_params.merge( param: param ) )
end

#json_request_payloadHash

Map a JSON body to a hash.

Returns:

  • (Hash)

    the parsed JSON payload.



279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/shamu/json_api/rails/controller.rb', line 279

def json_request_payload
  @json_request_payload ||=
    begin
      body = request.body.read || "{}"
      json = JSON.parse( body, symbolize_names: true )

      unless json.blank?
        fail NoJsonBodyError unless json[ :data ]
      end

      json ? json[ :data ] : {}
    end
end

#json_resource(resource, presenter = nil, **context) {|response| ... } ⇒ JsonApi::Response

Builds a well-formed JSON API response for a single resource.

Parameters:

  • resource (Object)

    to present as JSON.

  • presenter (Class) (defaults to: nil)

    Presenter class to use when building the response for the given resource. If not given, attempts to find a presenter by calling Context#find_presenter.

  • fields (Hash<Symbol,Array>)

    to include in the response. If not provided looks for a fields request argument and parses that. See Context#initialize.

  • namespaces (Array<String>)

    to look for presenters. If not provided automatically adds the controller name and it's namespace.

    For example in the Users::AccountController it will add the Users::Accounts and Users namespaces.

    See Context#find_presenter.

  • presenters (Hash<Class,Class>)

    a hash that maps resource classes to the presenter class to use when building responses. See Context#find_presenter.

Yields:

  • (response)

    write additional top-level links and meta information.

Yield Parameters:

Returns:



38
39
40
41
42
43
# File 'lib/shamu/json_api/rails/controller.rb', line 38

def json_resource( resource, presenter = nil, **context, &block )
  response = build_json_response( context )
  response.resource resource, presenter
  yield response if block_given?
  response.as_json
end

#render_resource(resource, presenter: nil, status: nil, location: nil, **context, &block) ⇒ Object

Present the resource as json and render it adding appropriate HTTP response codes and headers for standard JSON API actions.

Parameters:

  • status (Symbol, Number) (defaults to: nil)

    the HTTP status code.

  • resource (Object)

    to present as JSON.

  • presenter (Class) (defaults to: nil)

    Presenter class to use when building the response for the given resource. If not given, attempts to find a presenter by calling Context#find_presenter.

  • fields (Hash<Symbol,Array>)

    to include in the response. If not provided looks for a fields request argument and parses that. See Context#initialize.

  • namespaces (Array<String>)

    to look for presenters. If not provided automatically adds the controller name and it's namespace.

    For example in the Users::AccountController it will add the Users::Accounts and Users namespaces.

    See Context#find_presenter.

  • presenters (Hash<Class,Class>)

    a hash that maps resource classes to the presenter class to use when building responses. See Context#find_presenter.



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/shamu/json_api/rails/controller.rb', line 52

def render_resource( resource, presenter: nil, status: nil, location: nil, **context, &block )
  json = json_resource( resource, presenter, **context, &block )

  # Include canonical url to resource if present
  if data = json[ "data" ]
    if links = data[ "links" ]
      location ||= links[ "self" ] if links[ "self" ]
    end
  end

  render json: json, status: status, location: location
end

#render_result(result, presenter: nil, status: nil, **context, &block) ⇒ Object

Renders a Services::Result presenting either the validation errors or the entity.

Parameters:

  • result (Shamu::Services::Result)

    of a service call

  • resource (Object)

    to present as JSON.

  • presenter (Class) (defaults to: nil)

    Presenter class to use when building the response for the given resource. If not given, attempts to find a presenter by calling Context#find_presenter.

  • fields (Hash<Symbol,Array>)

    to include in the response. If not provided looks for a fields request argument and parses that. See Context#initialize.

  • namespaces (Array<String>)

    to look for presenters. If not provided automatically adds the controller name and it's namespace.

    For example in the Users::AccountController it will add the Users::Accounts and Users namespaces.

    See Context#find_presenter.

  • presenters (Hash<Class,Class>)

    a hash that maps resource classes to the presenter class to use when building responses. See Context#find_presenter.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/shamu/json_api/rails/controller.rb', line 72

def render_result( result, presenter: nil, status: nil, **context, &block )
  if result.valid?
    if result.entity
      status ||= case request.method
                 when "POST"   then :created
                 when "DELETE" then :no_content
                 else               :ok
                 end

      render_resource result.entity, presenter: presenter, status: status, **context, &block
    else
      head status || :no_content
    end
  else
    render json: json_validation_errors( result.errors, **context ), status: :unprocessable_entity
  end
end