class JsonApiClient::Resource
Attributes
Public Class Methods
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 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
# 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
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 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
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
# File lib/json_api_client/resource.rb, line 110 def inherited(subclass) subclass._immutable = false super end
# File lib/json_api_client/resource.rb, line 225 def key_formatter JsonApiClient::Formatter.formatter_for(json_key_format) end
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
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
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
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
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
# 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
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
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
# File lib/json_api_client/resource.rb, line 229 def route_formatter JsonApiClient::Formatter.formatter_for(route_format) end
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
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
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
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
# File lib/json_api_client/resource.rb, line 315 def _belongs_to_associations associations.select{|association| association.is_a?(Associations::BelongsTo::Association) } end
# 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
# File lib/json_api_client/resource.rb, line 339 def _custom_headers=(headers) _header_store.replace(headers) end
# File lib/json_api_client/resource.rb, line 343 def _header_store Thread.current["json_api_client-#{resource_name}"] ||= {} end
# 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
# File lib/json_api_client/resource.rb, line 335 def _new_scope query_builder.new(self) end
# 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
# 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
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
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
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
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
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
# 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
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
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
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
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
# File lib/json_api_client/resource.rb, line 539 def inspect "#<#{self.class.name}:@attributes=#{attributes.inspect}>" end
Mark the record as destroyed
# File lib/json_api_client/resource.rb, line 428 def mark_as_destroyed! @destroyed = true end
Mark the record as persisted
# File lib/json_api_client/resource.rb, line 416 def mark_as_persisted! @persisted = true end
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
# 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
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
# File lib/json_api_client/resource.rb, line 543 def request_includes(*includes) self.request_params.add_includes(includes) self end
# 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
# File lib/json_api_client/resource.rb, line 548 def reset_request_includes! self.request_params.reset_includes! self end
# 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
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
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
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
# File lib/json_api_client/resource.rb, line 411 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/json_api_client/resource.rb, line 393 def update_attributes(attrs = {}) self.attributes = attrs save end
# 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
# File lib/json_api_client/resource.rb, line 481 def valid?(context = nil) context ||= (new_record? ? :create : :update) super(context) end
Protected Instance Methods
# 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
# File lib/json_api_client/resource.rb, line 652 def attributes_for_serialization attributes.except(*non_serializing_attributes).slice(*changed) end
# File lib/json_api_client/resource.rb, line 660 def error_message_for(error) error.error_msg end
# 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
JsonApiClient::Helpers::DynamicAttributes#has_attribute?
# File lib/json_api_client/resource.rb, line 634 def has_attribute?(attr_name) !!property_for(attr_name) || super end
# 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
JsonApiClient::Helpers::Dirty#method_missing
# 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
# File lib/json_api_client/resource.rb, line 648 def non_serializing_attributes self.class.read_only_attributes end
# File lib/json_api_client/resource.rb, line 638 def property_for(name) self.class.schema.find(name) end
# 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
# 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
# File lib/json_api_client/resource.rb, line 583 def relationship_definition_for(name) relationships[name] if relationships && relationships.has_attribute?(name) end
# File lib/json_api_client/resource.rb, line 656 def relationships_for_serialization relationships.as_json_api end
JsonApiClient::Helpers::DynamicAttributes#respond_to_missing?
# 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
JsonApiClient::Helpers::Dirty#set_attribute
# 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
# 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