class JsonApiClient::Resource

Attributes

last_result_set[RW]
relationships[RW]
request_params[RW]

Public Class Methods

connection(rebuild = false, &block) click to toggle source

Return/build a connection object

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

# File lib/json_api_client/resource.rb, line 137
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/json_api_client/resource.rb, line 169
def create(attributes = {})
  new(attributes).tap do |resource|
    resource.save
  end
end
create!(attributes = {}) click to toggle source
# File lib/json_api_client/resource.rb, line 175
def create!(attributes = {})
  new(attributes).tap do |resource|
    raise(Errors::RecordNotSaved.new("Failed to save the record", resource)) unless resource.save
  end
end
custom_headers() click to toggle source

The current custom headers to send with any request made by this resource class

@return [Hash] Headers

# File lib/json_api_client/resource.rb, line 197
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/json_api_client/resource.rb, line 214
def default_attributes
  {type: type}
end
immutable(flag = true) click to toggle source

Indicates whether this resource is mutable or immutable; by default, all resources are mutable.

@return [Boolean]

# File lib/json_api_client/resource.rb, line 106
def immutable(flag = true)
  self._immutable = flag
end
inherited(subclass) click to toggle source
Calls superclass method
# File lib/json_api_client/resource.rb, line 110
def inherited(subclass)
  subclass._immutable = false
  super
end
key_formatter() click to toggle source
# File lib/json_api_client/resource.rb, line 225
def key_formatter
  JsonApiClient::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/json_api_client/resource.rb, line 126
def load(params)
  new(params).tap do |resource|
    resource.mark_as_persisted!
    resource.clear_changes_information
    resource.relationships.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/json_api_client/resource.rb, line 369
def initialize(params = {})
  params = params.with_indifferent_access
  @persisted = nil
  @destroyed = 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.except(*self.class.prefix_params)
  self.forget_change!(:type)
  self.__belongs_to_params = params.slice(*self.class.prefix_params)

  setup_default_properties

  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
  self.request_params = self.class.request_params_class.new(self.class)
end
path(params = nil) click to toggle source

Return the path or path pattern for this resource

# File lib/json_api_client/resource.rb, line 151
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/json_api_client/resource.rb, line 146
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/json_api_client/resource.rb, line 206
def requestor
  @requestor ||= requestor_class.new(self)
end
resolve_custom_type(type_name, class_name) click to toggle source
# File lib/json_api_client/resource.rb, line 75
def resolve_custom_type(type_name, class_name)
  classified_type = key_formatter.unformat(type_name.to_s).singularize.classify
  self.custom_type_to_class = custom_type_to_class.merge(classified_type => class_name.to_s)
end
resource_name() click to toggle source

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

@return [String]

# File lib/json_api_client/resource.rb, line 90
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/json_api_client/resource.rb, line 119
def resource_path
  table_name
end
route_formatter() click to toggle source
# File lib/json_api_client/resource.rb, line 229
def route_formatter
  JsonApiClient::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/json_api_client/resource.rb, line 221
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/json_api_client/resource.rb, line 83
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/json_api_client/resource.rb, line 98
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/json_api_client/resource.rb, line 186
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/json_api_client/resource.rb, line 315
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/json_api_client/resource.rb, line 347
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/json_api_client/resource.rb, line 339
def _custom_headers=(headers)
  _header_store.replace(headers)
end
_header_store() click to toggle source
# File lib/json_api_client/resource.rb, line 343
def _header_store
  Thread.current["json_api_client-#{resource_name}"] ||= {}
end
_name_for_route_format(name) click to toggle source
# File lib/json_api_client/resource.rb, line 354
def _name_for_route_format(name)
  case self.route_format
  when :dasherized_route
    name.to_s.dasherize
  when :camelized_route
    name.to_s.camelize(:lower)
  else
    name
  end
end
_new_scope() click to toggle source
# File lib/json_api_client/resource.rb, line 335
def _new_scope
  query_builder.new(self)
end
_prefix_path() click to toggle source
# File lib/json_api_client/resource.rb, line 319
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/json_api_client/resource.rb, line 327
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/json_api_client/resource.rb, line 259
def collection_endpoint(name, options = {})
  metaclass = class << self
    self
  end
  endpoint_name = _name_for_route_format(name)
  metaclass.instance_eval do
    define_method(name) do |*params|
      request_params = params.first || {}
      requestor.custom(endpoint_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/json_api_client/resource.rb, line 241
def custom_endpoint(name, options = {})
  if _immutable
    request_method = options.fetch(:request_method, :get).to_sym
    raise JsonApiClient::Errors::ResourceImmutableError if request_method != :get
  end

  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/json_api_client/resource.rb, line 277
def member_endpoint(name, options = {})
  endpoint_name = self._name_for_route_format(name)
  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(endpoint_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/json_api_client/resource.rb, line 308
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/json_api_client/resource.rb, line 292
def property(name, options = {})
  schema.add(name, options)
  define_method(name) do
    attributes[name]
  end
  define_method("#{name}=") do |value|
    set_attribute(name, value)
  end
end

Public Instance Methods

as_json(*) click to toggle source
# File lib/json_api_client/resource.rb, line 466
def as_json(*)
  attributes.slice(self.class.primary_key, :type).tap do |h|
    relationships.as_json.tap do |r|
      h[:relationships] = r unless r.empty?
    end
    h[:attributes] = attributes.except(self.class.primary_key, :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/json_api_client/resource.rb, line 457
def as_json_api(*)
  attributes.slice(self.class.primary_key, :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/json_api_client/resource.rb, line 449
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/json_api_client/resource.rb, line 524
def destroy
  raise JsonApiClient::Errors::ResourceImmutableError if _immutable

  self.last_result_set = self.class.requestor.destroy(self)
  if last_result_set.has_errors?
    fill_errors
    false
  else
    mark_as_destroyed!
    _clear_cached_relationships
    _clear_belongs_to_params
    true
  end
end
destroyed?() click to toggle source

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

@return [Boolean]

# File lib/json_api_client/resource.rb, line 435
def destroyed?
  !!@destroyed
end
inspect() click to toggle source
# File lib/json_api_client/resource.rb, line 539
def inspect
  "#<#{self.class.name}:@attributes=#{attributes.inspect}>"
end
mark_as_destroyed!() click to toggle source

Mark the record as destroyed

# File lib/json_api_client/resource.rb, line 428
def mark_as_destroyed!
  @destroyed = true
end
mark_as_persisted!() click to toggle source

Mark the record as persisted

# File lib/json_api_client/resource.rb, line 416
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/json_api_client/resource.rb, line 442
def new_record?
  !persisted? && !destroyed?
end
path_attributes() click to toggle source
# File lib/json_api_client/resource.rb, line 568
def path_attributes
  _belongs_to_params.merge attributes.slice( self.class.primary_key ).with_indifferent_access
end
persisted?() click to toggle source

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

@return [Boolean]

# File lib/json_api_client/resource.rb, line 423
def persisted?
  !!@persisted && !destroyed? && has_attribute?(self.class.primary_key)
end
request_includes(*includes) click to toggle source
# File lib/json_api_client/resource.rb, line 543
def request_includes(*includes)
  self.request_params.add_includes(includes)
  self
end
request_select(*fields) click to toggle source
# File lib/json_api_client/resource.rb, line 553
def request_select(*fields)
  fields_by_type = fields.extract_options!
  fields_by_type[type.to_sym] = fields if fields.any?
  fields_by_type.each do |field_type, field_names|
    self.request_params.set_fields(field_type, field_names)
  end
  self
end
reset_request_includes!() click to toggle source
# File lib/json_api_client/resource.rb, line 548
def reset_request_includes!
  self.request_params.reset_includes!
  self
end
reset_request_select!(*resource_types) click to toggle source
# File lib/json_api_client/resource.rb, line 562
def reset_request_select!(*resource_types)
  resource_types = self.request_params.field_types if resource_types.empty?
  resource_types.each { |resource_type| self.request_params.remove_fields(resource_type) }
  self
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/json_api_client/resource.rb, line 492
def save
  return false unless valid?
  raise JsonApiClient::Errors::ResourceImmutableError if _immutable

  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
    self.request_params.clear unless self.class.keep_request_params
    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
      self.relationships.clear_changes_information
      _clear_cached_relationships
    end
    true
  end
end
set_all_dirty!() click to toggle source

Mark all attributes for this record as dirty

# File lib/json_api_client/resource.rb, line 476
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/json_api_client/resource.rb, line 407
def update(attrs = {})
  update_attributes(attrs)
end
update!(attrs = {}) click to toggle source
# File lib/json_api_client/resource.rb, line 411
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/json_api_client/resource.rb, line 393
def update_attributes(attrs = {})
  self.attributes = attrs
  save
end
update_attributes!(attrs = {}) click to toggle source
# File lib/json_api_client/resource.rb, line 398
def update_attributes!(attrs = {})
  self.attributes = attrs
  save ? true : raise(Errors::RecordNotSaved.new("Failed to update the record", self))
end
valid?(context = nil) click to toggle source
Calls superclass method
# File lib/json_api_client/resource.rb, line 481
def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  super(context)
end

Protected Instance Methods

association_for(name) click to toggle source
# File lib/json_api_client/resource.rb, line 642
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/json_api_client/resource.rb, line 652
def attributes_for_serialization
  attributes.except(*non_serializing_attributes).slice(*changed)
end
error_message_for(error) click to toggle source
# File lib/json_api_client/resource.rb, line 660
def error_message_for(error)
  error.error_msg
end
fill_errors() click to toggle source
# File lib/json_api_client/resource.rb, line 664
def fill_errors
  last_result_set.errors.each do |error|
    key = self.class.key_formatter.unformat(error.error_key)
    errors.add(key, error_message_for(error))
  end
end
has_attribute?(attr_name) click to toggle source
# File lib/json_api_client/resource.rb, line 634
def has_attribute?(attr_name)
  !!property_for(attr_name) || super
end
included_data_for(name, relationship_definition) click to toggle source
# File lib/json_api_client/resource.rb, line 587
def included_data_for(name, relationship_definition)
  last_result_set.included.data_for(name, relationship_definition)
end
method_missing(method, *args) click to toggle source
# File lib/json_api_client/resource.rb, line 614
def method_missing(method, *args)
  relationship_definition = relationship_definition_for(method)

  return super unless relationship_definition

  relationship_data_for(method, relationship_definition)
end
non_serializing_attributes() click to toggle source
# File lib/json_api_client/resource.rb, line 648
def non_serializing_attributes
  self.class.read_only_attributes
end
property_for(name) click to toggle source
# File lib/json_api_client/resource.rb, line 638
def property_for(name)
  self.class.schema.find(name)
end
relation_objects_for(name, relationship_definition) click to toggle source
# File lib/json_api_client/resource.rb, line 607
def relation_objects_for(name, relationship_definition)
  data = relationship_definition["data"]
  assoc = association_for(name)
  return if data.nil? || assoc.nil?
  assoc.load_records(data)
end
relationship_data_for(name, relationship_definition) click to toggle source
# File lib/json_api_client/resource.rb, line 591
def relationship_data_for(name, relationship_definition)
  # look in included data
  if relationship_definition.key?("data")
    if relationships.attribute_changed?(name)
      return relation_objects_for(name, relationship_definition)
    else
      return included_data_for(name, relationship_definition)
    end
  end

  return unless links = relationship_definition["links"]
  return unless url = links["related"]

  association_for(name).data(url)
end
relationship_definition_for(name) click to toggle source
# File lib/json_api_client/resource.rb, line 583
def relationship_definition_for(name)
  relationships[name] if relationships && relationships.has_attribute?(name)
end
relationships_for_serialization() click to toggle source
# File lib/json_api_client/resource.rb, line 656
def relationships_for_serialization
  relationships.as_json_api
end
respond_to_missing?(symbol, include_all = false) click to toggle source
# File lib/json_api_client/resource.rb, line 622
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/json_api_client/resource.rb, line 628
def set_attribute(name, value)
  property = property_for(name)
  value = property.cast(value) if property
  super(name, value)
end
setup_default_properties() click to toggle source
# File lib/json_api_client/resource.rb, line 574
def setup_default_properties
  self.class.schema.each_property do |property|
    unless attributes.has_key?(property.name) || property.default.nil?
      attribute_will_change!(property.name) if add_defaults_to_changes
      attributes[property.name] = property.default
    end
  end
end