class JSONAPI::BasicResource

Attributes

_allowed_filters[W]
_allowed_sort[W]
_attributes[RW]
_model_hints[RW]
_paginator[W]
_relationships[RW]
_routed[RW]
_type[RW]
_warned_missing_route[RW]
context[R]

Public Class Methods

new(model, context) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 27
def initialize(model, context)
  @model = model
  @context = context
  @reload_needed = false
  @changing = false
  @save_needed = false
end

Private Class Methods

_abstract() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 958
def _abstract
  @abstract
end
_add_relationship(klass, *attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1071
def _add_relationship(klass, *attrs)
  _clear_fields_cache

  options = attrs.extract_options!
  options[:parent_resource] = self

  attrs.each do |name|
    relationship_name = name.to_sym
    check_reserved_relationship_name(relationship_name)
    check_duplicate_relationship_name(relationship_name)

    define_relationship_methods(relationship_name.to_sym, klass, options)
  end
end
_allowed_filter?(filter) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1040
def _allowed_filter?(filter)
  !_allowed_filters[filter].nil?
end
_allowed_filters() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 900
def _allowed_filters
  defined?(@_allowed_filters) ? @_allowed_filters : { id: {} }
end
_allowed_sort() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 904
def _allowed_sort
  @_allowed_sort ||= {}
end
_as_parent_key() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 896
def _as_parent_key
  @_as_parent_key ||= "#{_type.to_s.singularize}_id"
end
_attribute_delegated_name(attr) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 838
def _attribute_delegated_name(attr)
  @_attributes.fetch(attr.to_sym, {}).fetch(:delegate, attr)
end
_attribute_options(attr) click to toggle source

quasi private class methods

# File lib/jsonapi/basic_resource.rb, line 834
def _attribute_options(attr)
  @_cached_attribute_options[attr] ||= default_attribute_options.merge(@_attributes[attr])
end
_cache_field() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 888
def _cache_field
  @_cache_field || JSONAPI.configuration.default_resource_cache_field
end
_caching() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1003
def _caching
  @caching
end
_clear_cached_attribute_options() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1118
def _clear_cached_attribute_options
  @_cached_attribute_options = {}
end
_clear_fields_cache() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1122
def _clear_fields_cache
  @_fields_cache = nil
end
_default_primary_key() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 884
def _default_primary_key
  @_default_primary_key ||=_model_class.respond_to?(:primary_key) ? _model_class.primary_key : :id
end
_has_attribute?(attr) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 842
def _has_attribute?(attr)
  @_attributes.keys.include?(attr.to_sym)
end
_has_sort?(sorting) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1044
def _has_sort?(sorting)
  !_allowed_sort[sorting.to_sym].nil?
end
_immutable() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 966
def _immutable
  @immutable
end
_model_class() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1024
def _model_class
  return nil if _abstract

  return @model_class if @model_class

  model_name = _model_name
  return nil if model_name.to_s.blank?

  @model_class = model_name.to_s.safe_constantize
  if @model_class.nil?
    warn "[MODEL NOT FOUND] Model could not be found for #{self.name}. If this is a base Resource declare it as abstract."
  end

  @model_class
end
_model_name() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 860
def _model_name
  if _abstract
     ''
  else
    return @_model_name.to_s if defined?(@_model_name)
    class_name = self.name
    return '' if class_name.nil?
    @_model_name = class_name.demodulize.sub(/Resource$/, '')
    @_model_name.to_s
  end
end
_paginator() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 908
def _paginator
  @_paginator || JSONAPI.configuration.default_paginator
end
_polymorphic() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 916
def _polymorphic
  @_polymorphic
end
_polymorphic_name() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 872
def _polymorphic_name
  if !_polymorphic
    ''
  else
    @_polymorphic_name ||= _model_name.to_s.underscore
  end
end
_polymorphic_resource_klasses() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 938
def _polymorphic_resource_klasses
  @_polymorphic_resource_klasses ||= _polymorphic_types.collect do |type|
    resource_klass_for(type)
  end
end
_polymorphic_types() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 924
def _polymorphic_types
  @poly_hash ||= {}.tap do |hash|
    ObjectSpace.each_object do |klass|
      next unless Module === klass
      if klass < ActiveRecord::Base
        klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection|
          (hash[reflection.options[:as]] ||= []) << klass.name.underscore
        end
      end
    end
  end
  @poly_hash[_polymorphic_name.to_sym]
end
_primary_key() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 880
def _primary_key
  @_primary_key ||= _default_primary_key
end
_relationship(type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 854
def _relationship(type)
  return nil unless type
  type = type.to_sym
  @_relationships[type]
end
_resource_name_from_type(type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 492
def _resource_name_from_type(type)
  "#{type.to_s.underscore.singularize}_resource".camelize
end
_singleton_options() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 649
def _singleton_options
  @_singleton_options ||= {}
end
_table_name() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 892
def _table_name
  @_table_name ||= _model_class.respond_to?(:table_name) ? _model_class.table_name : _model_name.tableize
end
_updatable_attributes() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 846
def _updatable_attributes
  _attributes.map { |key, options| key unless options[:readonly] }.compact
end
_updatable_relationships() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 850
def _updatable_relationships
  @_relationships.map { |key, relationship| key unless relationship.readonly? }.compact
end
abstract(val = true) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 954
def abstract(val = true)
  @abstract = val
end
attribute(attribute_name, options = {}) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 532
def attribute(attribute_name, options = {})
  _clear_cached_attribute_options
  _clear_fields_cache

  attr = attribute_name.to_sym

  check_reserved_attribute_name(attr)

  if (attr == :id) && (options[:format].nil?)
    ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
  end

  check_duplicate_attribute_name(attr) if options[:format].nil?

  @_attributes ||= {}
  @_attributes[attr] = options
  define_method attr do
    @model.public_send(options[:delegate] ? options[:delegate].to_sym : attr)
  end unless method_defined?(attr)

  define_method "#{attr}=" do |value|
    @model.public_send("#{options[:delegate] ? options[:delegate].to_sym : attr}=", value)
  end unless method_defined?("#{attr}=")

  if options.fetch(:sortable, true) && !_has_sort?(attr)
    sort attr
  end
end
attribute_caching_context(_context) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1015
def attribute_caching_context(_context)
  nil
end
attribute_to_model_field(attribute) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 561
def attribute_to_model_field(attribute)
  field_name = if attribute == :_cache_field
                 _cache_field
               else
                 # Note: this will allow the returning of model attributes without a corresponding
                 # resource attribute, for example a belongs_to id such as `author_id` or bypassing
                 # the delegate.
                 attr = @_attributes[attribute]
                 attr && attr[:delegate] ? attr[:delegate].to_sym : attribute
               end
  if Rails::VERSION::MAJOR >= 5
    attribute_type = _model_class.attribute_types[field_name.to_s]
  else
    attribute_type = _model_class.column_types[field_name.to_s]
  end
  { name: field_name, type: attribute_type}
end
attributes(*attrs) click to toggle source

Methods used in defining a resource class

# File lib/jsonapi/basic_resource.rb, line 525
def attributes(*attrs)
  options = attrs.extract_options!.dup
  attrs.each do |attr|
    attribute(attr, options)
  end
end
belongs_to(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 610
def belongs_to(*attrs)
  ActiveSupport::Deprecation.warn "In #{name} you exposed a `has_one` relationship "\
                                  " using the `belongs_to` class method. We think `has_one`" \
                                  " is more appropriate. If you know what you're doing," \
                                  " and don't want to see this warning again, override the" \
                                  " `belongs_to` class method on your resource."
  _add_relationship(Relationship::ToOne, *attrs)
end
cache_field(field) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 678
def cache_field(field)
  @_cache_field = field.to_sym
end
caching(val = true) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 999
def caching(val = true)
  @caching = val
end
caching?() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1007
def caching?
  if @caching.nil?
    !JSONAPI.configuration.resource_cache.nil? && JSONAPI.configuration.default_caching
  else
    @caching && !JSONAPI.configuration.resource_cache.nil?
  end
end
call_method_or_proc(strategy, *args) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 753
def call_method_or_proc(strategy, *args)
  if strategy.is_a?(Symbol) || strategy.is_a?(String)
    send(strategy, *args)
  else
    strategy.call(*args)
  end
end
cast_to_attribute_type(value, type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 579
def cast_to_attribute_type(value, type)
  if Rails::VERSION::MAJOR >= 5
    return type.cast(value)
  else
    return type.type_cast_from_database(value)
  end
end
check_duplicate_attribute_name(name) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1155
def check_duplicate_attribute_name(name)
  if _attributes.include?(name.to_sym)
    warn "[DUPLICATE ATTRIBUTE] `#{name}` has already been defined in #{_resource_name_from_type(_type)}."
  end
end
check_duplicate_relationship_name(name) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1149
def check_duplicate_relationship_name(name)
  if _relationships.include?(name.to_sym)
    warn "[DUPLICATE RELATIONSHIP] `#{name}` has already been defined in #{_resource_name_from_type(_type)}."
  end
end
check_reserved_attribute_name(name) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1135
def check_reserved_attribute_name(name)
  # Allow :id since it can be used to specify the format. Since it is a method on the base Resource
  # an attribute method won't be created for it.
  if [:type, :_cache_field, :cache_field].include?(name.to_sym)
    warn "[NAME COLLISION] `#{name}` is a reserved key in #{_resource_name_from_type(_type)}."
  end
end
check_reserved_relationship_name(name) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1143
def check_reserved_relationship_name(name)
  if [:id, :ids, :type, :types].include?(name.to_sym)
    warn "[NAME COLLISION] `#{name}` is a reserved relationship name in #{_resource_name_from_type(_type)}."
  end
end
check_reserved_resource_name(type, name) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1128
def check_reserved_resource_name(type, name)
  if [:ids, :types, :hrefs, :links].include?(type)
    warn "[NAME COLLISION] `#{name}` is a reserved resource name."
    return
  end
end
construct_order_options(sort_params) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1060
def construct_order_options(sort_params)
  sort_params = default_sort if sort_params.blank?

  return {} unless sort_params

  sort_params.each_with_object({}) do |sort, order_hash|
    field = sort[:field].to_s == 'id' ? _primary_key : sort[:field].to_s
    order_hash[field] = sort[:direction]
  end
end
creatable_fields(_context = nil) click to toggle source

Override in your resource to filter the creatable keys

# File lib/jsonapi/basic_resource.rb, line 688
def creatable_fields(_context = nil)
  _updatable_relationships | _updatable_attributes
end
create(context) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 508
def create(context)
  new(create_model, context)
end
create_model() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 512
def create_model
  _model_class.new
end
default_attribute_options() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 587
def default_attribute_options
  { format: :default }
end
default_sort() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1056
def default_sort
  [{field: 'id', direction: :asc}]
end
define_foreign_key_setter(relationship) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1096
def define_foreign_key_setter(relationship)
  if relationship.polymorphic?
    define_on_resource "#{relationship.foreign_key}=" do |v|
      _model.method("#{relationship.foreign_key}=").call(v[:id])
      _model.public_send("#{relationship.polymorphic_type}=", v[:type])
    end
  else
    define_on_resource "#{relationship.foreign_key}=" do |value|
      _model.method("#{relationship.foreign_key}=").call(value)
    end
  end
end
define_on_resource(method_name, &block) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1109
def define_on_resource(method_name, &block)
  return if method_defined?(method_name)
  define_method(method_name, block)
end
define_relationship_methods(relationship_name, relationship_klass, options) click to toggle source

ResourceBuilder methods

# File lib/jsonapi/basic_resource.rb, line 1087
def define_relationship_methods(relationship_name, relationship_klass, options)
  relationship = register_relationship(
      relationship_name,
      relationship_klass.new(relationship_name, options)
  )

  define_foreign_key_setter(relationship)
end
fields() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 701
def fields
  @_fields_cache ||= _relationships.keys | _attributes.keys
end
filter(attr, *args) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 661
def filter(attr, *args)
  @_allowed_filters[attr.to_sym] = args.extract_options!
end
filters(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 657
def filters(*attrs)
  @_allowed_filters.merge!(attrs.inject({}) { |h, attr| h[attr] = {}; h })
end
has_many(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 619
def has_many(*attrs)
  _add_relationship(Relationship::ToMany, *attrs)
end
has_one(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 606
def has_one(*attrs)
  _add_relationship(Relationship::ToOne, *attrs)
end
hash_cache_field(value) click to toggle source

Generate a hashcode from the value to be used as part of the cache lookup

# File lib/jsonapi/basic_resource.rb, line 1020
def hash_cache_field(value)
  value.hash
end
immutable(val = true) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 962
def immutable(val = true)
  @immutable = val
end
inherited(subclass) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 421
def inherited(subclass)
  subclass.abstract(false)
  subclass.immutable(false)
  subclass.caching(_caching)
  subclass.cache_field(_cache_field) if @_cache_field
  subclass.singleton(singleton?, (_singleton_options.dup || {}))
  subclass.exclude_links(_exclude_links)
  subclass.paginator(@_paginator)
  subclass._attributes = (_attributes || {}).dup
  subclass.polymorphic(false)
  subclass.key_type(@_resource_key_type)

  subclass._model_hints = (_model_hints || {}).dup

  unless _model_name.empty? || _immutable
    subclass.model_name(_model_name, add_model_hint: (_model_hints && !_model_hints[_model_name].nil?) == true)
  end

  subclass.rebuild_relationships(_relationships || {})

  subclass._allowed_filters = (_allowed_filters || Set.new).dup

  subclass._allowed_sort = _allowed_sort.dup

  type = subclass.name.demodulize.sub(/Resource$/, '').underscore
  subclass._type = type.pluralize.to_sym

  unless subclass._attributes[:id]
    subclass.attribute :id, format: :id, readonly: true
  end

  check_reserved_resource_name(subclass._type, subclass.name)

  subclass._routed = false
  subclass._warned_missing_route = false

  subclass._clear_cached_attribute_options
  subclass._clear_fields_cache
end
is_filter_relationship?(filter) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 725
def is_filter_relationship?(filter)
  filter == _type || _relationships.include?(filter)
end
key_type(key_type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 761
def key_type(key_type)
  @_resource_key_type = key_type
end
model_hint(model: _model_name, resource: _type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 638
def model_hint(model: _model_name, resource: _type)
  resource_type = ((resource.is_a?(Class)) && (resource < JSONAPI::Resource)) ? resource._type : resource.to_s

  _model_hints[model.to_s.gsub('::', '/').underscore] = resource_type.to_s
end
model_name(model, options = {}) click to toggle source

@model_class is inherited from superclass, and this causes some issues: “` CarResource._model_class #=> Vehicle # it should be Car “` so in order to invoke the right class from subclasses, we should call this method to override it.

# File lib/jsonapi/basic_resource.rb, line 629
def model_name(model, options = {})
  @model_class = nil
  @_model_name = model.to_sym

  model_hint(model: @_model_name, resource: self) unless options[:add_model_hint] == false

  rebuild_relationships(_relationships)
end
module_path() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1048
def module_path
  if name == 'JSONAPI::Resource'
    ''
  else
    name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').underscore : ''
  end
end
mutable?() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 970
def mutable?
  !@immutable
end
paginator(paginator) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 912
def paginator(paginator)
  @_paginator = paginator
end
polymorphic(polymorphic = true) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 920
def polymorphic(polymorphic = true)
  @_polymorphic = polymorphic
end
primary_key(key) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 674
def primary_key(key)
  @_primary_key = key.to_sym
end
rebuild_relationships(relationships) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 461
def rebuild_relationships(relationships)
  original_relationships = relationships.deep_dup

  @_relationships = {}

  if original_relationships.is_a?(Hash)
    original_relationships.each_value do |relationship|
      options = relationship.options.dup
      options[:parent_resource] = self
      options[:inverse_relationship] = relationship.inverse_relationship
      _add_relationship(relationship.class, relationship.name, options)
    end
  end
end
register_relationship(name, relationship_object) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 1114
def register_relationship(name, relationship_object)
  @_relationships[name] = relationship_object
end
relationship(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 591
def relationship(*attrs)
  options = attrs.extract_options!
  klass = case options[:to]
            when :one
              Relationship::ToOne
            when :many
              Relationship::ToMany
            else
              #:nocov:#
              fail ArgumentError.new('to: must be either :one or :many')
              #:nocov:#
          end
  _add_relationship(klass, *attrs, options.except(:to))
end
resource_for(model_record, context) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 711
def resource_for(model_record, context)
  resource_klass = self.resource_klass_for_model(model_record)
  resource_klass.new(model_record, context)
end
resource_key_type() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 765
def resource_key_type
  @_resource_key_type || JSONAPI.configuration.resource_key_type
end
resource_klass_for(type) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 476
def resource_klass_for(type)
  type = type.underscore
  type_with_module = type.start_with?(module_path) ? type : module_path + type

  resource_name = _resource_name_from_type(type_with_module)
  resource = resource_name.safe_constantize if resource_name
  if resource.nil?
    fail NameError, "JSONAPI: Could not find resource '#{type}'. (Class #{resource_name} not found)"
  end
  resource
end
resource_klass_for_model(model) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 488
def resource_klass_for_model(model)
  resource_klass_for(resource_type_for(model))
end
resource_type_for(model) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 496
def resource_type_for(model)
  model_name = model.class.to_s.underscore
  if _model_hints[model_name]
    _model_hints[model_name]
  else
    model_name.rpartition('/').last
  end
end
resources_for(records, context) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 705
def resources_for(records, context)
  records.collect do |record|
    resource_for(record, context)
  end
end
root?() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 950
def root?
  @root
end
root_resource() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 944
def root_resource
  @abstract = true
  @immutable = true
  @root = true
end
routing_options(options) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 516
def routing_options(options)
  @_routing_resource_options = options
end
routing_resource_options() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 520
def routing_resource_options
  @_routing_resource_options ||= {}
end
singleton(*attrs) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 644
def singleton(*attrs)
  @_singleton = (!!attrs[0] == attrs[0]) ? attrs[0] : true
  @_singleton_options = attrs.extract_options!
end
singleton?() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 653
def singleton?
  @_singleton ||= false
end
singleton_key(context) click to toggle source

override to all resolution of masked ids to actual ids. Because singleton routes do not specify the id this will be needed to allow lookup of singleton resources. Alternately singleton resources can override `verify_key`

# File lib/jsonapi/basic_resource.rb, line 772
def singleton_key(context)
  if @_singleton_options && @_singleton_options[:singleton_key]
    strategy = @_singleton_options[:singleton_key]
    case strategy
      when Proc
        key = strategy.call(context)
      when Symbol, String
        key = send(strategy, context)
      else
        raise "singleton_key must be a proc or function name"
    end
  end
  key
end
sort(sorting, options = {}) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 665
def sort(sorting, options = {})
  self._allowed_sort[sorting.to_sym] = options
end
sortable_field?(key, context = nil) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 697
def sortable_field?(key, context = nil)
  sortable_fields(context).include? key.to_sym
end
sortable_fields(_context = nil) click to toggle source

Override in your resource to filter the sortable keys

# File lib/jsonapi/basic_resource.rb, line 693
def sortable_fields(_context = nil)
  _allowed_sort.keys
end
sorts(*args) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 669
def sorts(*args)
  options = args.extract_options!
  _allowed_sort.merge!(args.inject({}) { |h, sorting| h[sorting.to_sym] = options.dup; h })
end
updatable_fields(_context = nil) click to toggle source

Override in your resource to filter the updatable keys

# File lib/jsonapi/basic_resource.rb, line 683
def updatable_fields(_context = nil)
  _updatable_relationships | _updatable_attributes - [:id]
end
verify_custom_filter(filter, value, _context = nil) click to toggle source

Either add a custom :verify lambda or override verify_custom_filter to allow for custom filters

# File lib/jsonapi/basic_resource.rb, line 823
def verify_custom_filter(filter, value, _context = nil)
  [filter, value]
end
verify_filter(filter, raw, context = nil) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 729
def verify_filter(filter, raw, context = nil)
  filter_values = []
  if raw.present?
    begin
      filter_values += raw.is_a?(String) ? CSV.parse_line(raw) : [raw]
    rescue CSV::MalformedCSVError
      filter_values << raw
    end
  end

  strategy = _allowed_filters.fetch(filter, Hash.new)[:verify]

  if strategy
    values = call_method_or_proc(strategy, filter_values, context)
    [filter, values]
  else
    if is_filter_relationship?(filter)
      verify_relationship_filter(filter, filter_values, context)
    else
      verify_custom_filter(filter, filter_values, context)
    end
  end
end
verify_filters(filters, context = nil) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 716
def verify_filters(filters, context = nil)
  verified_filters = {}
  filters.each do |filter, raw_value|
    verified_filter = verify_filter(filter, raw_value, context)
    verified_filters[verified_filter[0]] = verified_filter[1]
  end
  verified_filters
end
verify_key(key, context = nil) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 787
def verify_key(key, context = nil)
  key_type = resource_key_type

  case key_type
  when :integer
    return if key.nil?
    Integer(key)
  when :string
    return if key.nil?
    if key.to_s.include?(',')
      raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
    else
      key
    end
  when :uuid
    return if key.nil?
    if key.to_s.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)
      key
    else
      raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
    end
  else
    key_type.call(key, context)
  end
rescue
  raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
end
verify_keys(keys, context = nil) click to toggle source

override to allow for key processing and checking

# File lib/jsonapi/basic_resource.rb, line 816
def verify_keys(keys, context = nil)
  return keys.collect do |key|
    verify_key(key, context)
  end
end
verify_relationship_filter(filter, raw, _context = nil) click to toggle source

Either add a custom :verify lambda or override verify_relationship_filter to allow for custom relationship logic, such as uuids, multiple keys or permission checks on keys

# File lib/jsonapi/basic_resource.rb, line 829
def verify_relationship_filter(filter, raw, _context = nil)
  [filter, raw]
end

Public Instance Methods

_model() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 35
def _model
  @model
end
cache_id() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 47
def cache_id
  [id, self.class.hash_cache_field(_model.public_send(self.class._cache_field))]
end
change(callback) { |== :completed)| ... } click to toggle source
# File lib/jsonapi/basic_resource.rb, line 55
def change(callback)
  completed = false

  if @changing
    run_callbacks callback do
      completed = (yield == :completed)
    end
  else
    run_callbacks is_new? ? :create : :update do
      @changing = true
      run_callbacks callback do
        completed = (yield == :completed)
      end

      completed = (save == :completed) if @save_needed || is_new?
    end
  end

  return completed ? :completed : :accepted
end
fetchable_fields() click to toggle source

Override this on a resource instance to override the fetchable keys

# File lib/jsonapi/basic_resource.rb, line 125
def fetchable_fields
  self.class.fields
end
id() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 39
def id
  _model.public_send(self.class._primary_key)
end
identity() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 43
def identity
  JSONAPI::ResourceIdentity.new(self.class, id)
end
is_new?() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 51
def is_new?
  id.nil?
end
meta(_options) click to toggle source

Override this to return resource level meta data must return a hash, and if the hash is empty the meta section will not be serialized with the resource meta keys will be not be formatted with the key formatter for the serializer by default. They can however use the serializer's format_key and format_value methods if desired the _options hash will contain the serializer and the serialization_options

# File lib/jsonapi/basic_resource.rb, line 161
def meta(_options)
  {}
end
model_error_messages() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 129
def model_error_messages
  _model.errors.messages
end
remove() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 76
def remove
  run_callbacks :remove do
    _remove
  end
end
replace_fields(field_data) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 118
def replace_fields(field_data)
  change :replace_fields do
    _replace_fields(field_data)
  end
end
validation_error_metadata() click to toggle source

Add metadata to validation error objects.

Suppose `model_error_messages` returned the following error messages hash:

{password: ["too_short", "format"]}

Then to add data to the validation error `validation_error_metadata` could return:

{
  password: {
    "too_short": {"minimum_length" => 6},
    "format": {"requirement" => "must contain letters and numbers"}
  }
}

The specified metadata is then be merged into the validation error object.

# File lib/jsonapi/basic_resource.rb, line 152
def validation_error_metadata
  {}
end

Private Instance Methods

_remove() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 221
def _remove
  unless @model.destroy
    fail JSONAPI::Exceptions::ValidationErrors.new(self)
  end
  :completed

rescue ActiveRecord::DeleteRestrictionError => e
  fail JSONAPI::Exceptions::RecordLocked.new(e.message)
end
_replace_fields(field_data) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 388
def _replace_fields(field_data)
  field_data[:attributes].each do |attribute, value|
    begin
      send "#{attribute}=", value
      @save_needed = true
    rescue ArgumentError
      # :nocov: Will be thrown if an enum value isn't allowed for an enum. Currently not tested as enums are a rails 4.1 and higher feature
      raise JSONAPI::Exceptions::InvalidFieldValue.new(attribute, value)
      # :nocov:
    end
  end

  field_data[:to_one].each do |relationship_type, value|
    if value.nil?
      remove_to_one_link(relationship_type)
    else
      case value
      when Hash
        replace_polymorphic_to_one_link(relationship_type.to_s, value.fetch(:id), value.fetch(:type))
      else
        replace_to_one_link(relationship_type, value)
      end
    end
  end if field_data[:to_one]

  field_data[:to_many].each do |relationship_type, values|
    replace_to_many_links(relationship_type, values)
  end if field_data[:to_many]

  :completed
end
_save(validation_context = nil) click to toggle source

Override this on a resource to return a different result code. Any value other than :completed will result in operations returning `:accepted`

For example to return `:accepted` if your model does not immediately save resources to the database you could override `_save` as follows:

“` def _save

super
return :accepted

end “`

# File lib/jsonapi/basic_resource.rb, line 195
def _save(validation_context = nil)
  unless @model.valid?(validation_context)
    fail JSONAPI::Exceptions::ValidationErrors.new(self)
  end

  if defined? @model.save
    saved = @model.save(validate: false)

    unless saved
      if @model.errors.present?
        fail JSONAPI::Exceptions::ValidationErrors.new(self)
      else
        fail JSONAPI::Exceptions::SaveFailed.new
      end
    end
  else
    saved = true
  end
  @model.reload if @reload_needed
  @reload_needed = false

  @save_needed = !saved

  :completed
end
reflect_relationship?(relationship, options) click to toggle source
# File lib/jsonapi/basic_resource.rb, line 231
def reflect_relationship?(relationship, options)
  return false if !relationship.reflect ||
    (!JSONAPI.configuration.use_relationship_reflection || options[:reflected_source])

  inverse_relationship = relationship.resource_klass._relationships[relationship.inverse_relationship]
  if inverse_relationship.nil?
    warn "Inverse relationship could not be found for #{self.class.name}.#{relationship.name}. Relationship reflection disabled."
    return false
  end
  true
end
save() click to toggle source
# File lib/jsonapi/basic_resource.rb, line 176
def save
  run_callbacks :save do
    _save
  end
end