class JSONAPI::Consumer::Resource

Attributes

last_result_set[RW]
relationships[RW]

Public Class Methods

authorize_with(jwt, &block) click to toggle source

Run a command wrapped in an Authorization header

# File lib/jsonapi/consumer/resource.rb, line 164
def authorize_with(jwt, &block)
  with_headers(authorization: %(Bearer #{jwt}), &block)
end
authorize_with=(jwt) click to toggle source

Set the Authorization header to a JWT value

# File lib/jsonapi/consumer/resource.rb, line 170
def authorize_with=(jwt)
  if jwt.nil?
    self._custom_headers = {authorization: nil}
  else
    self._custom_headers = {authorization: %(Bearer #{jwt})}
  end
end
authorized?() click to toggle source

Returns based on the presence of an Authorization header

@return [Boolean]

# File lib/jsonapi/consumer/resource.rb, line 192
def authorized?
  !custom_headers[:authorization].nil?
end
authorized_as() click to toggle source

@return [String] The Authorization header

# File lib/jsonapi/consumer/resource.rb, line 185
def authorized_as
  custom_headers[:authorization]
end
clear_authorization!() click to toggle source

Clears the Authorization header

# File lib/jsonapi/consumer/resource.rb, line 180
def clear_authorization!
  self.authorize_with = nil
end
connection(rebuild = false, &block) click to toggle source

Return/build a connection object

@return [Connection] The connection to the json api server

# File lib/jsonapi/consumer/resource.rb, line 101
def connection(rebuild = false, &block)
  _build_connection(rebuild, &block)
  connection_object
end
create(attributes = {}) click to toggle source

Create a new instance of this resource class

@param attributes [Hash] The attributes to create this resource with @return [Resource] The instance you tried to create. You will have to check the persisted state or errors on this object to see success/failure.

# File lib/jsonapi/consumer/resource.rb, line 133
def create(attributes = {})
  new(attributes).tap do |resource|
    resource.save
  end
end
custom_headers() click to toggle source

The current custom headers to send with any request made by this resource class. This supports inheritance so it only needs to be set on the base class.

@return [Hash] Headers

# File lib/jsonapi/consumer/resource.rb, line 156
def custom_headers
  return _header_store.to_h if superclass == Object

  superclass.custom_headers.merge(_header_store.to_h)
end
default_attributes() click to toggle source

Default attributes that every instance of this resource should be initialized with. Optionally, override this method in a subclass.

@return [Hash] Default attributes

# File lib/jsonapi/consumer/resource.rb, line 207
def default_attributes
  {type: type}
end
key_formatter() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 218
def key_formatter
  JSONAPI::Consumer::Formatter.formatter_for(json_key_format)
end
load(params) click to toggle source

Load a resource object from attributes and consider it persisted

@return [Resource] Persisted resource object

# File lib/jsonapi/consumer/resource.rb, line 91
def load(params)
  new(params).tap do |resource|
    resource.mark_as_persisted!
    resource.clear_changes_information
  end
end
new(params = {}) click to toggle source

Instantiate a new resource object

@param params [Hash] Attributes, links, and relationships

# File lib/jsonapi/consumer/resource.rb, line 338
def initialize(params = {})
  @persisted = nil
  self.links = self.class.linker.new(params.delete("links") || {})
  self.relationships = self.class.relationship_linker.new(self.class, params.delete("relationships") || {})
  self.attributes = self.class.default_attributes.merge(params)

  self.class.schema.each_property do |property|
    attributes[property.name] = property.default unless attributes.has_key?(property.name) || property.default.nil?
  end

  self.class.associations.each do |association|
    if params.has_key?(association.attr_name.to_s)
      set_attribute(association.attr_name, params[association.attr_name.to_s])
    end
  end
end
path(params = nil) click to toggle source

Return the path or path pattern for this resource

# File lib/jsonapi/consumer/resource.rb, line 115
def path(params = nil)
  parts = [resource_path]
  if params && _prefix_path.present?
    path_params = params.delete(:path) || params
    parts.unshift(_set_prefix_path(path_params.symbolize_keys))
  else
    parts.unshift(_prefix_path)
  end
  parts.reject!(&:blank?)
  File.join(*parts)
rescue KeyError
  raise ArgumentError, "Not all prefix parameters specified"
end
prefix_params() click to toggle source

Param names that will be considered path params. They will be used to build the resource path rather than treated as attributes

@return [Array] Param name symbols of parameters that will be treated as path parameters

# File lib/jsonapi/consumer/resource.rb, line 110
def prefix_params
  _belongs_to_associations.map(&:param)
end
requestor() click to toggle source

Returns the requestor for this resource class

@return [Requestor] The requestor for this resource class

# File lib/jsonapi/consumer/resource.rb, line 199
def requestor
  @requestor ||= requestor_class.new(self)
end
resource_name() click to toggle source

The name of a single resource. i.e. Article -> article, Person -> person

@return [String]

# File lib/jsonapi/consumer/resource.rb, line 68
def resource_name
  name.demodulize.underscore
end
resource_path() click to toggle source

Specifies the relative path that should be used for this resource; by default, this is inferred from the resource class name.

@return [String] Resource path

# File lib/jsonapi/consumer/resource.rb, line 84
def resource_path
  table_name
end
route_formatter() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 222
def route_formatter
  JSONAPI::Consumer::Formatter.formatter_for(route_format)
end
schema() click to toggle source

Returns the schema for this resource class

@return [Schema] The schema for this resource class

# File lib/jsonapi/consumer/resource.rb, line 214
def schema
  @schema ||= Schema.new
end
table_name() click to toggle source

The table name for this resource. i.e. Article -> articles, Person -> people

@return [String] The table name for this resource

# File lib/jsonapi/consumer/resource.rb, line 61
def table_name
  route_formatter.format(resource_name.pluralize)
end
type() click to toggle source

Specifies the JSON API resource type. By default this is inferred from the resource class name.

@return [String] Resource path

# File lib/jsonapi/consumer/resource.rb, line 76
def type
  table_name
end
with_headers(headers) { || ... } click to toggle source

Within the given block, add these headers to all requests made by the resource class

@param headers [Hash] The headers to send along @param block [Block] The block where headers will be set for

# File lib/jsonapi/consumer/resource.rb, line 144
def with_headers(headers)
  self._custom_headers = headers
  yield
ensure
  self._custom_headers = {}
end

Protected Class Methods

_belongs_to_associations() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 295
def _belongs_to_associations
  associations.select{|association| association.is_a?(Associations::BelongsTo::Association) }
end
_build_connection(rebuild = false) { |conn| ... } click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 327
def _build_connection(rebuild = false)
  return connection_object unless connection_object.nil? || rebuild
  self.connection_object = connection_class.new(connection_options.merge(site: site)).tap do |conn|
    yield(conn) if block_given?
  end
end
_custom_headers=(headers) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 319
def _custom_headers=(headers)
  _header_store.replace(headers)
end
_header_store() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 323
def _header_store
  Thread.current["json_api_client-#{resource_name}"] ||= {}
end
_new_scope() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 315
def _new_scope
  query_builder.new(self)
end
_prefix_path() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 299
def _prefix_path
  paths = _belongs_to_associations.map do |a|
    a.to_prefix_path(route_formatter)
  end

  paths.join("/")
end
_set_prefix_path(attrs) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 307
def _set_prefix_path(attrs)
  paths = _belongs_to_associations.map do |a|
    a.set_prefix_path(attrs, route_formatter)
  end

  paths.join("/")
end
collection_endpoint(name, options = {}) click to toggle source

Declares a new class method that acts on the collection

@param name [Symbol] the name of the endpoint and the method name @param options [Hash] endpoint options @option options [Symbol] :request_method The request method (:get, :post, etc)

# File lib/jsonapi/consumer/resource.rb, line 247
def collection_endpoint(name, options = {})
  metaclass = class << self
    self
  end
  metaclass.instance_eval do
    define_method(name) do |*params|
      request_params = params.first || {}
      requestor.custom(name, options, request_params)
    end
  end
end
custom_endpoint(name, options = {}) click to toggle source

Declares a new class/instance method that acts on the collection/member

@param name [Symbol] the name of the endpoint @param options [Hash] endpoint options @option [Symbol] :on One of [:collection or :member] to decide whether it's a collect or member method @option [Symbol] :request_method The request method (:get, :post, etc)

# File lib/jsonapi/consumer/resource.rb, line 234
def custom_endpoint(name, options = {})
  if :collection == options.delete(:on)
    collection_endpoint(name, options)
  else
    member_endpoint(name, options)
  end
end
member_endpoint(name, options = {}) click to toggle source

Declares a new instance method that acts on the member object

@param name [Symbol] the name of the endpoint and the method name @param options [Hash] endpoint options @option options [Symbol] :request_method The request method (:get, :post, etc)

# File lib/jsonapi/consumer/resource.rb, line 264
def member_endpoint(name, options = {})
  define_method name do |*params|
    request_params = params.first || {}
    request_params[self.class.primary_key] = attributes.fetch(self.class.primary_key)
    self.class.requestor.custom(name, options, request_params)
  end
end
properties(*names) click to toggle source

Declare multiple properties with the same optional options

@param [Array<Symbol>] names @param options [Hash] property options @option options [Symbol] :type The property type @option options [Symbol] :default The default value for the property

# File lib/jsonapi/consumer/resource.rb, line 288
def properties(*names)
  options = names.last.is_a?(Hash) ? names.pop : {}
  names.each do |name|
    property name, options
  end
end
property(name, options = {}) click to toggle source

Declares a new property by name

@param name [Symbol] the name of the property @param options [Hash] property options @option options [Symbol] :type The property type @option options [Symbol] :default The default value for the property

# File lib/jsonapi/consumer/resource.rb, line 278
def property(name, options = {})
  schema.add(name, options)
end

Public Instance Methods

as_json(*) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 411
def as_json(*)
  attributes.slice(:id, :type).tap do |h|
    relationships.as_json.tap do |r|
      h[:relationships] = r unless r.empty?
    end
    h[:attributes] = attributes.except(:id, :type).as_json
  end
end
as_json_api(*) click to toggle source

When we represent this resource for serialization (create/update), we do so with this implementation

@return [Hash] Representation of this object as JSONAPI object

# File lib/jsonapi/consumer/resource.rb, line 402
def as_json_api(*)
  attributes.slice(:id, :type).tap do |h|
    relationships_for_serialization.tap do |r|
      h[:relationships] = self.class.key_formatter.format_keys(r) unless r.empty?
    end
    h[:attributes] = self.class.key_formatter.format_keys(attributes_for_serialization)
  end
end
as_relation() click to toggle source

When we represent this resource as a relationship, we do so with id & type

@return [Hash] Representation of this object as a relation

# File lib/jsonapi/consumer/resource.rb, line 394
def as_relation
  attributes.slice(:type, self.class.primary_key)
end
destroy() click to toggle source

Try to destroy this resource

@return [Boolean] Whether or not the destroy succeeded

# File lib/jsonapi/consumer/resource.rb, line 465
def destroy
  self.last_result_set = self.class.requestor.destroy(self)
  if last_result_set.has_errors?
    fill_errors
    false
  else
    self.attributes.clear
    true
  end
end
inspect() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 476
def inspect
  "#<#{self.class.name}:@attributes=#{attributes.inspect}>"
end
mark_as_persisted!() click to toggle source

Mark the record as persisted

# File lib/jsonapi/consumer/resource.rb, line 373
def mark_as_persisted!
  @persisted = true
end
new_record?() click to toggle source

Returns true if this is a new record (never persisted to the database)

@return [Boolean]

# File lib/jsonapi/consumer/resource.rb, line 387
def new_record?
  !persisted?
end
persisted?() click to toggle source

Whether or not this record has been persisted to the database previously

@return [Boolean]

# File lib/jsonapi/consumer/resource.rb, line 380
def persisted?
  !!@persisted && has_attribute?(self.class.primary_key)
end
save() click to toggle source

Commit the current changes to the resource to the remote server. If the resource was previously loaded from the server, we will try to update the record. Otherwise if it's a new record, then we will try to create it

@return [Boolean] Whether or not the save succeeded

# File lib/jsonapi/consumer/resource.rb, line 437
def save
  return false unless valid?

  self.last_result_set = if persisted?
    self.class.requestor.update(self)
  else
    self.class.requestor.create(self)
  end

  if last_result_set.has_errors?
    fill_errors
    false
  else
    self.errors.clear if self.errors
    mark_as_persisted!
    if updated = last_result_set.first
      self.attributes = updated.attributes
      self.links.attributes = updated.links.attributes
      self.relationships.attributes = updated.relationships.attributes
      clear_changes_information
    end
    true
  end
end
set_all_dirty!() click to toggle source

Mark all attributes for this record as dirty

# File lib/jsonapi/consumer/resource.rb, line 421
def set_all_dirty!
  set_all_attributes_dirty
  relationships.set_all_attributes_dirty if relationships
end
update(attrs = {}) click to toggle source

Alias to update_attributes

@param attrs [Hash] Attributes to update @return [Boolean] Whether the update succeeded or not

# File lib/jsonapi/consumer/resource.rb, line 368
def update(attrs = {})
  update_attributes(attrs)
end
update_attributes(attrs = {}) click to toggle source

Set the current attributes and try to save them

@param attrs [Hash] Attributes to update @return [Boolean] Whether the update succeeded or not

# File lib/jsonapi/consumer/resource.rb, line 359
def update_attributes(attrs = {})
  self.attributes = attrs
  save
end
valid?(context = nil) click to toggle source
Calls superclass method
# File lib/jsonapi/consumer/resource.rb, line 426
def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  super(context)
end

Protected Instance Methods

association_for(name) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 528
def association_for(name)
  self.class.associations.detect do |association|
    association.attr_name.to_s == self.class.key_formatter.unformat(name)
  end
end
attributes_for_serialization() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 534
def attributes_for_serialization
  attributes.except(*self.class.read_only_attributes).slice(*changed)
end
fill_errors() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 542
def fill_errors
  last_result_set.errors.each do |error|
    key = self.class.key_formatter.unformat(error.error_key)
    errors.add(key, error.error_msg)
  end
end
has_attribute?(attr_name) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 520
def has_attribute?(attr_name)
  !!property_for(attr_name) || super
end
method_missing(method, *args) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 482
def method_missing(method, *args)
  association = association_for(method)

  return super unless association || (relationships && relationships.has_attribute?(method))

  return nil unless relationship_definitions = relationships[method]

  # look in included data
  if relationship_definitions.key?("data")
    # included.data_for returns an array, if the association is a has_one, then pick the first, otherise return the whole array
    if association.is_a?(JSONAPI::Consumer::Associations::HasOne::Association)
      return last_result_set.included.data_for(method, relationship_definitions).try(:first)
    else
      return last_result_set.included.data_for(method, relationship_definitions)
    end
  end

  if association = association_for(method)
    # look for a defined relationship url
    if relationship_definitions["links"] && url = relationship_definitions["links"]["related"]
      return association.data(url)
    end
  end
  nil
end
property_for(name) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 524
def property_for(name)
  self.class.schema.find(name)
end
relationships_for_serialization() click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 538
def relationships_for_serialization
  relationships.as_json_api
end
respond_to_missing?(symbol, include_all = false) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 508
def respond_to_missing?(symbol, include_all = false)
  return true if relationships && relationships.has_attribute?(symbol)
  return true if association_for(symbol)
  super
end
set_attribute(name, value) click to toggle source
# File lib/jsonapi/consumer/resource.rb, line 514
def set_attribute(name, value)
  property = property_for(name)
  value = property.cast(value) if property
  super(name, value)
end