module JsonapiCompliable::Base

Provides main interface to jsonapi_compliable

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

Attributes

_jsonapi_compliable[RW]
_sideload_whitelist[RW]

Public Class Methods

inherited(klass) click to toggle source
Calls superclass method
# File lib/jsonapi_compliable/base.rb, line 13
def self.inherited(klass)
  super
  klass._jsonapi_compliable = Class.new(_jsonapi_compliable)
  klass._sideload_whitelist = _sideload_whitelist.dup if _sideload_whitelist
end

Public Instance Methods

default_jsonapi_render_options() click to toggle source

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

@example

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

@see render_jsonapi @return [Hash] the options hash you define

# File lib/jsonapi_compliable/base.rb, line 328
def default_jsonapi_render_options
  {}.tap do |options|
  end
end
deserialized_params() click to toggle source

@see Deserializer#initialize @return [Deserializer]

# File lib/jsonapi_compliable/base.rb, line 188
def deserialized_params
  @deserialized_params ||= JsonapiCompliable::Deserializer.new(params, request.env)
end
jsonapi_context() click to toggle source

Override if you'd like the “context” to be something other than an instance of this class. In Rails, context is the controller instance.

@example Overriding

# within your controller
def jsonapi_context
  current_user
end

# within a resource
default_filter :by_user do |scope, context|
  scope.where(user_id: context.id)
end

@return the context object you'd like

# File lib/jsonapi_compliable/base.rb, line 161
def jsonapi_context
  self
end
jsonapi_create() click to toggle source

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

@example 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

@see Resource.model @see resource @see deserialized_params @return [Util::ValidationResponse]

# File lib/jsonapi_compliable/base.rb, line 216
def jsonapi_create
  _persist do
    jsonapi_resource.persist_with_relationships \
      deserialized_params.meta,
      deserialized_params.attributes,
      deserialized_params.relationships
  end
end
jsonapi_destroy() click to toggle source

Delete the model Any error, including validation errors, will roll back the transaction.

Note: before_commit hooks still run unless excluded

@return [Util::ValidationResponse]

# File lib/jsonapi_compliable/base.rb, line 262
def jsonapi_destroy
  jsonapi_resource.transaction do
    model = jsonapi_resource.destroy(params[:id])
    validator = ::JsonapiCompliable::Util::ValidationResponse.new \
      model, deserialized_params
    validator.validate!
    jsonapi_resource.before_commit(model, :destroy)
    validator
  end
end
jsonapi_resource() click to toggle source

Returns an instance of the associated Resource

In other words, if you configured your controller as:

jsonapi resource: MyResource

This returns MyResource.new

@return [Resource] the configured Resource for this controller

# File lib/jsonapi_compliable/base.rb, line 108
def jsonapi_resource
  @jsonapi_resource ||= begin
    resource = self.class._jsonapi_compliable
    if resource.is_a?(Hash)
      resource[action_name.to_sym].new
    else
      resource.new
    end
  end
end
jsonapi_scope(scope, opts = {}) click to toggle source

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

@example 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)

@example 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]

@see Resource#build_scope @return [Scope] the configured scope

# File lib/jsonapi_compliable/base.rb, line 182
def jsonapi_scope(scope, opts = {})
  jsonapi_resource.build_scope(scope, query, opts)
end
jsonapi_update() click to toggle source

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

@example 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

@see jsonapi_create @return [Util::ValidationResponse]

# File lib/jsonapi_compliable/base.rb, line 247
def jsonapi_update
  _persist do
    jsonapi_resource.persist_with_relationships \
      deserialized_params.meta,
      deserialized_params.attributes,
      deserialized_params.relationships
  end
end
query() click to toggle source

Instantiates the relevant Query object

@see Query @return [Query] the Query object for this resource/params

# File lib/jsonapi_compliable/base.rb, line 123
def query
  @query ||= Query.new(jsonapi_resource, params)
end
query_hash() click to toggle source

@see Query#to_hash @return [Hash] the normalized query hash for only the current resource

# File lib/jsonapi_compliable/base.rb, line 129
def query_hash
  @query_hash ||= query.to_hash[jsonapi_resource.type]
end
render_jsonapi(scope, opts = {}) click to toggle source

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::SuccessRenderer#render+from {jsonapi-rb.org jsonapi-rb}

@example 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

@example 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

@param scope [Scope, Object] the scope to build or render. @param [Hash] opts the render options passed to {jsonapi-rb.org jsonapi-rb} @option opts [Boolean] :scope Default: true. Should we call jsonapi_scope on this object? @see jsonapi_scope

# File lib/jsonapi_compliable/base.rb, line 304
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

  if opts[:include].empty? && force_includes?
    opts[:include] = deserialized_params.include_directive
  end

  perform_render_jsonapi(opts)
end
sideload_whitelist() click to toggle source

@api private

# File lib/jsonapi_compliable/base.rb, line 95
def sideload_whitelist
  self.class._sideload_whitelist || {}
end
wrap_context() { || ... } click to toggle source

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

@api private @yieldreturn Code to run within the current context

# File lib/jsonapi_compliable/base.rb, line 139
def wrap_context
  jsonapi_resource.with_context(jsonapi_context, action_name.to_sym) do
    yield
  end
end

Private Instance Methods

_persist() { || ... } click to toggle source
# File lib/jsonapi_compliable/base.rb, line 335
def _persist
  jsonapi_resource.transaction do
    ::JsonapiCompliable::Util::Hooks.record do
      model = yield
      validator = ::JsonapiCompliable::Util::ValidationResponse.new \
        model, deserialized_params
      validator.validate!
      validator
    end
  end
end
force_includes?() click to toggle source
# File lib/jsonapi_compliable/base.rb, line 347
def force_includes?
  not (deserialized_params.data.nil? || deserialized_params.data.empty?)
end
perform_render_jsonapi(opts) click to toggle source
# File lib/jsonapi_compliable/base.rb, line 351
def perform_render_jsonapi(opts)
  # TODO(beauby): Reuse renderer.
  JSONAPI::Serializable::Renderer.new
    .render(opts.delete(:jsonapi), opts).to_json
end