Module: JsonapiCompliable::Base

Extended by:
ActiveSupport::Concern
Defined in:
lib/jsonapi_compliable/base.rb

Overview

Provides main interface to jsonapi_compliable

This gets mixed in to a “context” class, such as a Rails controller.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.jsonapi(foo = 'bar', resource: nil, &blk) ⇒ void

This method returns an undefined value.

Define your JSONAPI configuration

Examples:

Inline Resource

# 'Quick and Dirty' solution that does not require a separate
# Resource object
class PostsController < ApplicationController
  jsonapi do
    type :posts
    use_adapter JsonapiCompliable::Adapters::ActiveRecord

    allow_filter :title
  end
end

Resource Class (preferred)

# Make code reusable by encapsulating it in a Resource class
class PostsController < ApplicationController
  jsonapi resource: PostResource
end

Parameters:

  • resource (Resource)

    the Resource class associated to this endpoint

See Also:



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/jsonapi_compliable/base.rb', line 44

def jsonapi(foo = 'bar', resource: nil, &blk)
  if resource
    self._jsonapi_compliable = resource
  else
    if !self._jsonapi_compliable
      self._jsonapi_compliable = Class.new(JsonapiCompliable::Resource)
    end
  end

  self._jsonapi_compliable.class_eval(&blk) if blk
end

Instance Method Details

#default_jsonapi_render_optionsHash

Define a hash that will be automatically merged into your render_jsonapi call

Examples:

# this
render_jsonapi(foo)
# is equivalent to this
render jsonapi: foo, default_jsonapi_render_options

Returns:

  • (Hash)

    the options hash you define

See Also:



238
239
240
241
# File 'lib/jsonapi_compliable/base.rb', line 238

def default_jsonapi_render_options
  {}.tap do |options|
  end
end

#deserialized_paramsDeserializer

Returns:

See Also:



119
120
121
# File 'lib/jsonapi_compliable/base.rb', line 119

def deserialized_params
  @deserialized_params ||= JsonapiCompliable::Deserializer.new(params, request.env)
end

#jsonapi_createUtil::ValidationResponse

Create the resource model and process all nested relationships via the serialized parameters. Any error, including validation errors, will roll back the transaction.

Examples:

Basic Rails

# Example Resource must have 'model'
#
# class PostResource < ApplicationResource
#   model Post
# end
def create
  post, success = jsonapi_create.to_a

  if success
    render_jsonapi(post, scope: false)
  else
    render_errors_for(post)
  end
end

Returns:

See Also:



147
148
149
150
151
152
153
154
# File 'lib/jsonapi_compliable/base.rb', line 147

def jsonapi_create
  _persist do
    jsonapi_resource.persist_with_relationships \
      deserialized_params.meta,
      deserialized_params.attributes,
      deserialized_params.relationships
  end
end

#jsonapi_resourceResource

Returns an instance of the associated Resource

In other words, if you configured your controller as:

jsonapi resource: MyResource

This returns MyResource.new

Returns:

  • (Resource)

    the configured Resource for this controller



66
67
68
# File 'lib/jsonapi_compliable/base.rb', line 66

def jsonapi_resource
  @jsonapi_resource ||= self.class._jsonapi_compliable.new
end

#jsonapi_scope(scope, opts = {}) ⇒ Scope

Use when direct, low-level access to the scope is required.

Examples:

Show Action

# Scope#resolve returns an array, but we only want to render
# one object, not an array
scope = jsonapi_scope(Employee.where(id: params[:id]))
render_jsonapi(scope.resolve.first, scope: false)

Scope Chaining

# Chain onto scope after running through typical DSL
# Here, we'll add active: true to our hash if the user
# is filtering on something
scope = jsonapi_scope({})
scope.object.merge!(active: true) if scope.object[:filter]

Returns:

  • (Scope)

    the configured scope

See Also:



113
114
115
# File 'lib/jsonapi_compliable/base.rb', line 113

def jsonapi_scope(scope, opts = {})
  jsonapi_resource.build_scope(scope, query, opts)
end

#jsonapi_updateUtil::ValidationResponse

Update the resource model and process all nested relationships via the serialized parameters. Any error, including validation errors, will roll back the transaction.

Examples:

Basic Rails

# Example Resource must have 'model'
#
# class PostResource < ApplicationResource
#   model Post
# end
def update
  post, success = jsonapi_update.to_a

  if success
    render_jsonapi(post, scope: false)
  else
    render_errors_for(post)
  end
end

Returns:

See Also:



178
179
180
181
182
183
184
185
# File 'lib/jsonapi_compliable/base.rb', line 178

def jsonapi_update
  _persist do
    jsonapi_resource.persist_with_relationships \
      deserialized_params.meta,
      deserialized_params.attributes,
      deserialized_params.relationships
  end
end

#queryQuery

Instantiates the relevant Query object

Returns:

  • (Query)

    the Query object for this resource/params

See Also:



74
75
76
# File 'lib/jsonapi_compliable/base.rb', line 74

def query
  @query ||= Query.new(jsonapi_resource, params)
end

#query_hashHash

Returns the normalized query hash for only the current resource

Returns:

  • (Hash)

    the normalized query hash for only the current resource

See Also:



80
81
82
# File 'lib/jsonapi_compliable/base.rb', line 80

def query_hash
  @query_hash ||= query.to_hash[jsonapi_resource.type]
end

#render_jsonapi(scope, opts = {}) ⇒ Object

Similar to render :json or render :jsonapi

By default, this will “build” the scope via #jsonapi_scope. To avoid this, pass scope: false

This builds relevant options and sends them to JSONAPI::Serializable::Renderer.renderfrom jsonapi-rb

Examples:

Build Scope by Default

# Employee.all returns an ActiveRecord::Relation. No SQL is fired at this point.
# We further 'chain' onto this scope, applying pagination, sorting,
# filters, etc that the user has requested.
def index
  employees = Employee.all
  render_jsonapi(employees)
end

Avoid Building Scope by Default

# Maybe we already manually scoped, and don't want to fire the logic twice
# This code is equivalent to the above example
def index
  scope = jsonapi_scope(Employee.all)
  # ... do other things with the scope ...
  render_jsonapi(scope.resolve, scope: false)
end

Parameters:

  • scope (Scope, Object)

    the scope to build or render.

  • opts (Hash) (defaults to: {})

    the render options passed to jsonapi-rb

Options Hash (opts):

  • :scope (Boolean)

    Default: true. Should we call #jsonapi_scope on this object?

See Also:



218
219
220
221
222
223
224
225
# File 'lib/jsonapi_compliable/base.rb', line 218

def render_jsonapi(scope, opts = {})
  scope = jsonapi_scope(scope) unless opts[:scope] == false || scope.is_a?(JsonapiCompliable::Scope)
  opts  = default_jsonapi_render_options.merge(opts)
  opts  = Util::RenderOptions.generate(scope, query_hash, opts)
  opts[:expose][:context] = self
  opts[:include] = deserialized_params.include_directive if force_includes?
  perform_render_jsonapi(opts)
end

#wrap_contextObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Tracks the current context so we can refer to it within any random object. Helpful for easy-access to things like the current user.

Yield Returns:

  • Code to run within the current context



90
91
92
93
94
# File 'lib/jsonapi_compliable/base.rb', line 90

def wrap_context
  jsonapi_resource.with_context(self, action_name.to_sym) do
    yield
  end
end