class JSONAPI::Consumer::Resource
Attributes
Public Class Methods
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 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
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 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
# File lib/jsonapi/consumer/resource.rb, line 218 def key_formatter JSONAPI::Consumer::Formatter.formatter_for(json_key_format) end
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
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
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
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
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
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
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
# File lib/jsonapi/consumer/resource.rb, line 222 def route_formatter JSONAPI::Consumer::Formatter.formatter_for(route_format) end
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
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
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
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
# File lib/jsonapi/consumer/resource.rb, line 295 def _belongs_to_associations associations.select{|association| association.is_a?(Associations::BelongsTo::Association) } end
# 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
# File lib/jsonapi/consumer/resource.rb, line 319 def _custom_headers=(headers) _header_store.replace(headers) end
# File lib/jsonapi/consumer/resource.rb, line 323 def _header_store Thread.current["json_api_client-#{resource_name}"] ||= {} end
# File lib/jsonapi/consumer/resource.rb, line 315 def _new_scope query_builder.new(self) end
# 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
# 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
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
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
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
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
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
# 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
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
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
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
# File lib/jsonapi/consumer/resource.rb, line 476 def inspect "#<#{self.class.name}:@attributes=#{attributes.inspect}>" end
Mark the record as persisted
# File lib/jsonapi/consumer/resource.rb, line 373 def mark_as_persisted! @persisted = true end
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
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
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
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
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
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
# File lib/jsonapi/consumer/resource.rb, line 426 def valid?(context = nil) context ||= (new_record? ? :create : :update) super(context) end
Protected Instance Methods
# 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
# File lib/jsonapi/consumer/resource.rb, line 534 def attributes_for_serialization attributes.except(*self.class.read_only_attributes).slice(*changed) end
# 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
JSONAPI::Consumer::Helpers::DynamicAttributes#has_attribute?
# File lib/jsonapi/consumer/resource.rb, line 520 def has_attribute?(attr_name) !!property_for(attr_name) || super end
JSONAPI::Consumer::Helpers::Dirty#method_missing
# 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
# File lib/jsonapi/consumer/resource.rb, line 524 def property_for(name) self.class.schema.find(name) end
# File lib/jsonapi/consumer/resource.rb, line 538 def relationships_for_serialization relationships.as_json_api end
JSONAPI::Consumer::Helpers::DynamicAttributes#respond_to_missing?
# 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
JSONAPI::Consumer::Helpers::Dirty#set_attribute
# 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