Class: JsonapiCompliable::Resource

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/jsonapi_compliable/resource.rb

Overview

Resources hold configuration: How do you want to process incoming JSONAPI requests?

Let's say we start with an empty hash as our scope object:

render_jsonapi({})

Let's define the behavior of various parameters. Here we'll merge options into our hash when the user filters, sorts, and paginates. Then, we'll pass that hash off to an HTTP Client:

class PostResource < ApplicationResource
type :posts
use_adapter JsonapiCompliable::Adapters::Null

# What do do when filter[active] parameter comes in
allow_filter :active do |scope, value|
  scope.merge(active: value)
end

# What do do when sorting parameters come in
sort do |scope, attribute, direction|
  scope.merge(order: { attribute => direction })
end

# What do do when pagination parameters come in
page do |scope, current_page, per_page|
  scope.merge(page: current_page, per_page: per_page)
end

# Resolve the scope by passing the hash to an HTTP Client
def resolve(scope)
  MyHttpClient.get(scope)
end
end

This code can quickly become duplicative - we probably want to reuse this logic for other objects that use the same HTTP client.

That's why we also have Adapters. Adapters encapsulate common, reusable resource configuration. That's why we don't need to specify the above code when using ActiveRecord - the default logic is already in the adapter.

class PostResource < ApplicationResource
type :posts
use_adapter JsonapiCompliable::Adapters::ActiveRecord

allow_filter :title
end

Of course, we can always override the Resource directly for one-off customizations:

class PostResource < ApplicationResource
type :posts
use_adapter JsonapiCompliable::Adapters::ActiveRecord

allow_filter :title_prefix do |scope, value|
  scope.where(["title LIKE ?", "#{value}%"])
end
end

Resources can also define Sideloads. Sideloads define the relationships between resources:

allow_sideload :comments, resource: CommentResource do
# How to fetch the associated objects
# This will be further chained down the line
scope do |posts|
  Comment.where(post_id: posts.map(&:id))
end

# Now that we've resolved everything, how to assign the objects
assign do |posts, comments|
  posts.each do |post|
    relevant_comments = comments.select { |c| c.post_id === post.id }
    post.comments = relevant_comments
  end
end
end

Once again, we can DRY this up using an Adapter:

use_adapter JsonapiCompliable::Adapters::ActiveRecord

has_many :comments,
scope: -> { Comment.all },
resource: CommentResource,
foreign_key: :post_id

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.config ⇒ Hash

This is where we store all information set via DSL. Useful for introspection. Gets dup'd when inherited.

Returns:

  • (Hash) —

    the current configuration



415
416
417
# File 'lib/jsonapi_compliable/resource.rb', line 415

def config
  @config
end

Instance Attribute Details

#context ⇒ Hash (readonly)

The current context set by #with_context in the form of

{ object: context_obj, namespace: :index }

Returns:

  • (Hash) —

    the context hash

See Also:



92
93
94
# File 'lib/jsonapi_compliable/resource.rb', line 92

def context
  @context
end

Class Method Details

.allow_filter(name, options = {}) ⇒ Object

Whitelist a filter

If a filter is not allowed, a Jsonapi::Errors::BadFilter error will be raised.

Examples:

Basic Filtering

allow_filter :title

# When using ActiveRecord, this code is equivalent
allow_filter :title do |scope, value|
  scope.where(title: value)
end

Custom Filtering

# All filters can be customized with a block
allow_filter :title_prefix do |scope, value|
  scope.where('title LIKE ?', "#{value}%")
end

Guarding Filters

# Only allow the current user to filter on a property
allow_filter :title, if: :admin?

def admin?
  current_user.role == 'admin'
end

Parameters:

  • name (Symbol) —

    The name of the filter

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

Options Hash (options):

  • :if (Symbol) —

    A method name on the current context - If the method returns false, BadFilter will be raised.

  • :aliases (Array<Symbol>) —

    Allow the user to specify these aliases in the URL, then match to this filter. Mainly used for backwards-compatibility.

Yield Parameters:

  • scope —

    The object being scoped

  • value —

    The sanitized value from the URL



201
202
203
204
205
206
207
208
209
# File 'lib/jsonapi_compliable/resource.rb', line 201

def self.allow_filter(name, *args, &blk)
  opts = args.extract_options!
  aliases = [name, opts[:aliases]].flatten.compact
  config[:filters][name.to_sym] = {
    aliases: aliases,
    if: opts[:if],
    filter: blk
  }
end

.allow_sideload ⇒ Object



102
# File 'lib/jsonapi_compliable/resource.rb', line 102

def_delegator :sideloading, :allow_sideload

.allow_stat(symbol_or_hash) {|scope, attr| ... } ⇒ Object

Whitelist a statistic.

Statistics are requested like

GET /posts?stats[total]=count

And returned in meta:

{
data: [...],
meta: { stats: { total: { count: 100 } } }
}

Statistics take into account the current scope, without pagination.

Examples:

Total Count

allow_stat total: [:count]

Average Rating

allow_stat rating: [:average]

Custom Stat

allow_stat rating: [:average] do
  standard_deviation { |scope, attr| ... }
end

Parameters:

  • symbol_or_hash (Symbol, Hash) —

    The attribute and metric

Yield Parameters:

  • scope —

    The object being scoped

  • attr (Symbol) —

    The name of the metric



240
241
242
243
244
# File 'lib/jsonapi_compliable/resource.rb', line 240

def self.allow_stat(symbol_or_hash, &blk)
  dsl = Stats::DSL.new(config[:adapter], symbol_or_hash)
  dsl.instance_eval(&blk) if blk
  config[:stats][dsl.name] = dsl
end

.belongs_to ⇒ Object



111
# File 'lib/jsonapi_compliable/resource.rb', line 111

def_delegator :sideloading, :belongs_to

.default_filter(name) {|scope| ... } ⇒ Object

When you want a filter to always apply, on every request.

Default filters can be overridden if there is a corresponding allow_filter:

Examples:

Only Active Posts

default_filter :active do |scope|
  scope.where(active: true)
end

Overriding Default Filters

allow_filter :active

default_filter :active do |scope|
  scope.where(active: true)
end

# GET /posts?filter[active]=false
# Returns only active posts

Parameters:

  • name (Symbol) —

    The default filter name

Yield Parameters:

  • scope —

    The object being scoped

See Also:



268
269
270
271
272
# File 'lib/jsonapi_compliable/resource.rb', line 268

def self.default_filter(name, &blk)
  config[:default_filters][name.to_sym] = {
    filter: blk
  }
end

.default_page_number(val) ⇒ Object

Set an alternative default page number. Defaults to 1.

Parameters:

  • val (Integer) —

    The new default



395
396
397
# File 'lib/jsonapi_compliable/resource.rb', line 395

def self.default_page_number(val)
  config[:default_page_number] = val
end

.default_page_size(val) ⇒ Object

Set an alternate default page size, when not specified in query parameters.

Examples:

# GET /employees will only render 10 employees
default_page_size 10

Parameters:

  • val (Integer) —

    The new default page size.



406
407
408
# File 'lib/jsonapi_compliable/resource.rb', line 406

def self.default_page_size(val)
  config[:default_page_size] = val
end

.default_sort(val) ⇒ Object

Override default sort applied when not present in the query parameters.

Default: [{ id: :asc }]

Examples:

Order by created_at descending by default

# GET /employees will order by created_at descending
default_sort([{ created_at: :desc }])

Parameters:

  • val (Array<Hash>) —

    Array of sorting criteria



367
368
369
# File 'lib/jsonapi_compliable/resource.rb', line 367

def self.default_sort(val)
  config[:default_sort] = val
end

.extra_field(name) {|scope, current_page, per_page| ... } ⇒ Object

Perform special logic when an extra field is requested. Often used to eager load data that will be used to compute the extra field.

This is not required if you have no custom logic.

Examples:

Eager load if extra field is required

# GET /employees?extra_fields[employees]=net_worth
extra_field(employees: [:net_worth]) do |scope|
  scope.includes(:assets)
end

Parameters:

  • name (Symbol) —

    Name of the extra field

Yield Parameters:

  • scope —

    The current object being scoped

  • current_page (Integer) —

    The page parameter value

  • per_page (Integer) —

    The page parameter value

See Also:



343
344
345
# File 'lib/jsonapi_compliable/resource.rb', line 343

def self.extra_field(name, &blk)
  config[:extra_fields][name] = blk
end

.has_and_belongs_to_many ⇒ Object



114
# File 'lib/jsonapi_compliable/resource.rb', line 114

def_delegator :sideloading, :has_and_belongs_to_many

.has_many ⇒ Object



105
# File 'lib/jsonapi_compliable/resource.rb', line 105

def_delegator :sideloading, :has_many

.has_one ⇒ Object



108
# File 'lib/jsonapi_compliable/resource.rb', line 108

def_delegator :sideloading, :has_one

.inherited(klass) ⇒ Object



128
129
130
# File 'lib/jsonapi_compliable/resource.rb', line 128

def self.inherited(klass)
  klass.config = Util::Hash.deep_dup(self.config)
end

.model(klass) ⇒ Object

The Model object associated with this class.

This model will be utilized on write requests.

Models need not be ActiveRecord ;)

Examples:

class PostResource < ApplicationResource
  # ... code ...
  model Post
end

Parameters:

  • klass (Class) —

    The associated Model class



287
288
289
# File 'lib/jsonapi_compliable/resource.rb', line 287

def self.model(klass)
  config[:model] = klass
end

.paginate {|scope, current_page, per_page| ... } ⇒ Object

Define custom pagination logic

Examples:

Use will_paginate instead of Kaminari

# GET /employees?page[size]=10&page[number]=2
paginate do |scope, current_page, per_page|
  scope.paginate(page: current_page, per_page: per_page)
end

Yield Parameters:

  • scope —

    The current object being scoped

  • current_page (Integer) —

    The page parameter value

  • per_page (Integer) —

    The page parameter value



321
322
323
# File 'lib/jsonapi_compliable/resource.rb', line 321

def self.paginate(&blk)
  config[:pagination] = blk
end

.polymorphic_belongs_to ⇒ Object



117
# File 'lib/jsonapi_compliable/resource.rb', line 117

def_delegator :sideloading, :polymorphic_belongs_to

.polymorphic_has_many ⇒ Object

See Also:

  • Adapters::ActiveRecordSideloading#polymorphic_has_many


120
# File 'lib/jsonapi_compliable/resource.rb', line 120

def_delegator :sideloading, :polymorphic_has_many

.sideload_whitelist(whitelist) ⇒ Object

Set the sideload whitelist. You may want to omit sideloads for security or performance reasons.

Uses JSONAPI::IncludeDirective from jsonapi-rb}

Examples:

Whitelisting Relationships

# Given the following whitelist
class PostResource < ApplicationResource
  # ... code ...
  sideload_whitelist([:blog, { comments: :author }])
end

# A request to sideload 'tags'
#
# GET /posts?include=tags
#
# ...will silently fail.
#
# A request for comments and tags:
#
# GET /posts?include=tags,comments
#
# ...will only sideload comments

Parameters:

  • whitelist (Hash, Array, Symbol)

See Also:



163
164
165
# File 'lib/jsonapi_compliable/resource.rb', line 163

def self.sideload_whitelist(whitelist)
  config[:sideload_whitelist] = JSONAPI::IncludeDirective.new(whitelist).to_hash
end

.sideloading ⇒ Object

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.



133
134
135
# File 'lib/jsonapi_compliable/resource.rb', line 133

def self.sideloading
  @sideloading ||= Sideload.new(:base, resource: self)
end

.sort {|scope, att, dir| ... } ⇒ Object

Define custom sorting logic

Examples:

Sort on alternate table

# GET /employees?sort=title
sort do |scope, att, dir|
  if att == :title
    scope.joins(:current_position).order("title #{dir}")
  else
    scope.order(att => dir)
  end
end

Yield Parameters:

  • scope —

    The current object being scoped

  • att (Symbol) —

    The requested sort attribute

  • dir (Symbol) —

    The requested sort direction (:asc/:desc)



306
307
308
# File 'lib/jsonapi_compliable/resource.rb', line 306

def self.sort(&blk)
  config[:sorting] = blk
end

.type(value = nil) ⇒ Object

The JSONAPI Type. For instance if you queried:

GET /employees?fields=title

And/Or got back in the response

{ id: '1', type: 'positions' }

The type would be :positions

This should match the type set in your serializer.

Examples:

class PostResource < ApplicationResource
  type :posts
end

Parameters:

  • value (Array<Hash>) (defaults to: nil) —

    Array of sorting criteria



389
390
391
# File 'lib/jsonapi_compliable/resource.rb', line 389

def self.type(value = nil)
  config[:type] = value
end

.use_adapter(klass) ⇒ Object

Configure the adapter you want to use.

Examples:

ActiveRecord Adapter

require 'jsonapi_compliable/adapters/active_record'
use_adapter JsonapiCompliable::Adapters::ActiveRecord

Parameters:

  • klass (Class) —

    The adapter class



354
355
356
# File 'lib/jsonapi_compliable/resource.rb', line 354

def self.use_adapter(klass)
  config[:adapter] = klass.new
end

Instance Method Details

#adapter ⇒ Object

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.

See Also:



709
710
711
# File 'lib/jsonapi_compliable/resource.rb', line 709

def adapter
  self.class.config[:adapter]
end

#allowed_sideloads(namespace = nil) ⇒ Hash

An Include Directive Hash of all possible sideloads for the current context namespace, taking into account the sideload whitelist.

In other words, say we have this resource:

class PostResource < ApplicationResource
sideload_whitelist({
  index: :comments,
  show: { comments: :author }
})
end

Expected behavior:

allowed_sideloads(:index) # => { comments: {} }
allowed_sideloads(:show) # => { comments: { author: {} }

instance.with_context({}, :index) do
instance.allowed_sideloads # => { comments: {} }
end

Parameters:

  • namespace (Symbol) (defaults to: nil) —

    Can be :index/:show/etc - The current context namespace will be used by default.

Returns:

  • (Hash) —

    the scrubbed include directive

See Also:



592
593
594
595
596
597
598
599
600
601
# File 'lib/jsonapi_compliable/resource.rb', line 592

def allowed_sideloads(namespace = nil)
  return {} unless sideloading

  namespace ||= context_namespace
  sideloads = sideloading.to_hash[:base]
  if !sideload_whitelist.empty? && namespace
    sideloads = Util::IncludeParams.scrub(sideloads, sideload_whitelist[namespace])
  end
  sideloads
end

#association_names ⇒ Array<Symbol>

All possible sideload names, including nested names

{ comments: { author: {} } }

Becomes

[:comments, :author]

Returns:

  • (Array<Symbol>) —

    the list of association names

See Also:



557
558
559
560
561
562
563
564
565
# File 'lib/jsonapi_compliable/resource.rb', line 557

def association_names
  @association_names ||= begin
    if sideloading
      Util::Hash.keys(sideloading.to_hash[:base])
    else
      []
    end
  end
end

#build_scope(base, query, opts = {}) ⇒ Scope

Build a scope using this Resource configuration

Essentially "api private", but can be useful for testing.

Parameters:

  • base —

    The base scope we are going to chain

  • query —

    The relevant Query object

  • opts (defaults to: {}) —

    Opts passed to Scope.new

Returns:

  • (Scope) —

    a configured Scope instance

See Also:



478
479
480
# File 'lib/jsonapi_compliable/resource.rb', line 478

def build_scope(base, query, opts = {})
  Scope.new(base, self, query, opts)
end

#context_namespace ⇒ Object



464
465
466
# File 'lib/jsonapi_compliable/resource.rb', line 464

def context_namespace
  JsonapiCompliable.context[:namespace]
end

#create(create_params) ⇒ Object

Create the relevant model. You must configure a model (see .model) to create. If you override, you must return the created instance.

Examples:

Send e-mail on creation

def create(attributes)
  instance = model.create(attributes)
  UserMailer.welcome_email(instance).deliver_later
  instance
end

Parameters:

  • create_params (Hash) —

    The relevant attributes, including id and foreign keys

Returns:

  • (Object) —

    an instance of the just-created model

See Also:



497
498
499
# File 'lib/jsonapi_compliable/resource.rb', line 497

def create(create_params)
  adapter.create(model, create_params)
end

#default_filters ⇒ Object

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.

See Also:



697
698
699
# File 'lib/jsonapi_compliable/resource.rb', line 697

def default_filters
  self.class.config[:default_filters]
end

#default_page_number ⇒ Object

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.



642
643
644
# File 'lib/jsonapi_compliable/resource.rb', line 642

def default_page_number
  self.class.config[:default_page_number] || 1
end

#default_page_size ⇒ Object

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.

See Also:



648
649
650
# File 'lib/jsonapi_compliable/resource.rb', line 648

def default_page_size
  self.class.config[:default_page_size] || 20
end

#default_sort ⇒ Object

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.

See Also:



636
637
638
# File 'lib/jsonapi_compliable/resource.rb', line 636

def default_sort
  self.class.config[:default_sort] || [{ id: :asc }]
end

#destroy(id) ⇒ Object

Destroy the relevant model. You must configure a model (see .model) to destroy. If you override, you must return the destroyed instance.

Examples:

Send e-mail on destroy

def destroy(attributes)
  instance = model_class.find(id)
  instance.destroy
  UserMailer.goodbye_email(instance).deliver_later
  instance
end

Parameters:

  • id (String) —

    The id of the relevant Model

Returns:

  • (Object) —

    an instance of the just-created model

See Also:



536
537
538
# File 'lib/jsonapi_compliable/resource.rb', line 536

def destroy(id)
  adapter.destroy(model, id)
end

#extra_fields ⇒ Object

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.

See Also:



685
686
687
# File 'lib/jsonapi_compliable/resource.rb', line 685

def extra_fields
  self.class.config[:extra_fields]
end

#filters ⇒ Object

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.

See Also:



661
662
663
# File 'lib/jsonapi_compliable/resource.rb', line 661

def filters
  self.class.config[:filters]
end

#model ⇒ Object

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.

See Also:



703
704
705
# File 'lib/jsonapi_compliable/resource.rb', line 703

def model
  self.class.config[:model]
end

#pagination ⇒ Object

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.

See Also:



679
680
681
# File 'lib/jsonapi_compliable/resource.rb', line 679

def pagination
  self.class.config[:pagination]
end

#persist_with_relationships(meta, attributes, relationships) ⇒ Object

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.



541
542
543
544
545
# File 'lib/jsonapi_compliable/resource.rb', line 541

def persist_with_relationships(meta, attributes, relationships)
  persistence = JsonapiCompliable::Util::Persistence \
    .new(self, meta, attributes, relationships)
  persistence.run
end

#resolve(scope) ⇒ Array

How do you want to resolve the scope?

For ActiveRecord, when we want to actually fire SQL, it's #to_a.

This method must return an array of resolved model objects.

By default, delegates to the adapter. You likely want to alter your adapter rather than override this directly.

Examples:

Custom API Call

# Let's build a hash and pass it off to an HTTP client
class PostResource < ApplicationResource
  type :posts
  use_adapter JsonapiCompliable::Adapters::Null

  sort do |scope, attribute, direction|
    scope.merge!(order: { attribute => direction }
  end

  page do |scope, current_page, per_page|
    scope.merge!(page: current_page, per_page: per_page)
  end

  def resolve(scope)
    MyHttpClient.get(scope)
  end
end

Parameters:

  • scope —

    The scope object we've built up

Returns:

  • (Array) —

    array of resolved model objects

See Also:



745
746
747
# File 'lib/jsonapi_compliable/resource.rb', line 745

def resolve(scope)
  adapter.resolve(scope)
end

#sideload ⇒ Object

See Also:



125
# File 'lib/jsonapi_compliable/resource.rb', line 125

def_delegator :sideloading, :sideload

#sideload_whitelist ⇒ Object

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.

See Also:



691
692
693
# File 'lib/jsonapi_compliable/resource.rb', line 691

def sideload_whitelist
  self.class.config[:sideload_whitelist]
end

#sideloading ⇒ Object

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.

Interface to the sideloads for this Resource



630
631
632
# File 'lib/jsonapi_compliable/resource.rb', line 630

def sideloading
  self.class.sideloading
end

#sorting ⇒ Object

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.

See Also:



667
668
669
# File 'lib/jsonapi_compliable/resource.rb', line 667

def sorting
  self.class.config[:sorting]
end

#stat(attribute, calculation) ⇒ Proc

The relevant proc for the given attribute and calculation.

Raises JsonapiCompliable::Errors::StatNotFound if not corresponding stat has been configured.

Examples:

Custom Stats

# Given this configuration
allow_stat :rating do
  average { |scope, attr| ... }
end

# We'd call the method like
resource.stat(:rating, :average)
# Which would return the custom proc

Parameters:

  • attribute (String, Symbol) —

    The attribute we're calculating.

  • calculation (String, Symbol) —

    The calculation to run

Returns:

  • (Proc) —

    the corresponding callable

Raises:

See Also:



622
623
624
625
626
# File 'lib/jsonapi_compliable/resource.rb', line 622

def stat(attribute, calculation)
  stats_dsl = stats[attribute] || stats[attribute.to_sym]
  raise Errors::StatNotFound.new(attribute, calculation) unless stats_dsl
  stats_dsl.calculation(calculation)
end

#stats ⇒ Object

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.

See Also:



673
674
675
# File 'lib/jsonapi_compliable/resource.rb', line 673

def stats
  self.class.config[:stats]
end

#transaction ⇒ Object

How to run write requests within a transaction.

Should roll back the transaction, but avoid bubbling up the error, if JsonapiCompliable::Errors::ValidationError is raised within the block.

By default, delegates to the adapter. You likely want to alter your adapter rather than override this directly.

Examples:

resource.transaction do
  # ... save calls ...
end

Returns:

  • the result of yield

See Also:



765
766
767
768
769
770
771
772
773
774
# File 'lib/jsonapi_compliable/resource.rb', line 765

def transaction
  response = nil
  begin
    adapter.transaction(model) do
      response = yield
    end
  rescue Errors::ValidationError
  end
  response
end

#type ⇒ Object

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.

Returns :undefined_jsonapi_type when not configured.

See Also:



655
656
657
# File 'lib/jsonapi_compliable/resource.rb', line 655

def type
  self.class.config[:type] || :undefined_jsonapi_type
end

#update(update_params) ⇒ Object

Update the relevant model. You must configure a model (see .model) to update. If you override, you must return the updated instance.

Examples:

Send e-mail on update

def update(attributes)
  instance = model.update_attributes(attributes)
  UserMailer.profile_updated_email(instance).deliver_later
  instance
end

Parameters:

  • update_params (Hash) —

    The relevant attributes, including id and foreign keys

Returns:

  • (Object) —

    an instance of the just-created model

See Also:



516
517
518
# File 'lib/jsonapi_compliable/resource.rb', line 516

def update(update_params)
  adapter.update(model, update_params)
end

#with_context(object, namespace = nil) ⇒ Object

Run code within a given context. Useful for running code within, say, a Rails controller context

When using Rails, controller actions are wrapped this way.

Examples:

Sinatra

get '/api/posts' do
  resource.with_context self, :index do
    scope = jsonapi_scope(Tweet.all)
    render_jsonapi(scope.resolve, scope: false)
  end
end

Parameters:

  • object —

    The context (Rails controller or equivalent)

  • namespace (defaults to: nil) —

    One of index/show/etc

See Also:



448
449
450
451
452
# File 'lib/jsonapi_compliable/resource.rb', line 448

def with_context(object, namespace = nil)
  JsonapiCompliable.with_context(object, namespace) do
    yield
  end
end