require 'active_model' module ApiClient # ApiClient::Base provides a way to make easy api requests as well as making possible to use it inside rails. # A possible implementation: # class Car < ApiClient::Base # attr_accessor :color, :name, :year # end # This class will handle Rails form as well as it works with respond_with. class Base include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming extend ApiClient::ClassMethods include ApiClient::InstanceMethods # @return [Integer] the id of the object. attr_accessor :id # @return [Hash] the request response. attr_accessor :response # @return [Hash] the errors object. attr_reader :errors # Initialize an object based on a hash of attributes. # # @param [Hash] attributes object attributes. # @return [Base] the object initialized. def initialize(attributes = {}) @errors = ApiClient::Errors.new(self) remove_root(attributes).each do |name, value| send("#{name.to_s}=", value) end end # Return if a object is persisted on the database or not. # # @return [False] always return false. def persisted? false end # Return the api name to be used by this model. # # @return [False] return the default api name. def self.path @path || :default end # Return the api name to be used by this model. # # @return [False] return the default api name. def self.path=(path) @path = path.to_sym end # Return the api name to be used by this model. # # @return [False] return the default api name. def path self.class.path end # Return the resource path of the object on the api url. # # @return [String] the resource path on the api for this object. def self.resource_path return self.to_s.gsub('::', '/').downcase.pluralize unless @resource_path @resource_path end # Set the resource path of the object on the api. # # @param [String] resource_path path string. def self.resource_path=(resource_path) resource_path = resource_path[1, resource_path.size - 1] if resource_path[0, 1] == '/' @resource_path = resource_path end # Return the Root node name for this Class. # # @return [String] a string with the root node name for this class. def self.root_node @root_node.blank? ? self.to_s.split('::').last.underscore : @root_node end # Set a custom root node name instead of the Class name. # # @param [String] root_node root node name. def self.root_node=(root_node) @root_node = root_node end # Set methods to initialize associated objects. # # @param [Hash] associations classes. def self.associations=(associations = {}) associations.each do |association, class_name| class_eval <<-EVAL def #{association.to_s}=(attributes = {}) return @#{association.to_s} = attributes.map { |attr| #{class_name.constantize}.new(attr) } if attributes.instance_of?(Array) @#{association.to_s} = #{class_name.constantize}.new(attributes) end def #{association.to_s} @#{association.to_s} end EVAL end end class << self alias_method :association=, :associations= end # Overwrite #attr_acessor method to save instance_variable names. # # @param [Array] vars instance variables. def self.attr_accessor(*vars) @attributes ||= [] @attributes.concat(vars) super end # Return an array with all instance variables setted through attr_accessor. # # @return [Array] instance variables. def self.attributes @attributes end # Return a hash with all instance variables setted through attr_accessor and its currently values. # # @return [Hash] instance variables and its values. def attributes self.class.instance_variable_get('@attributes').inject({}) { |hash, attribute| hash.merge(attribute.to_sym => self.send("#{attribute}")) } end # Update instance values based on a hash # # @param attr New attributes def attributes=(attr = {}) remove_root(attr).each do |key, value| send("#{key}=", value) end end # Return a hash with a root node and all instance variables setted through attr_accessor and its currently values. # # @return [Hash] instance variables and its values. def to_hash { self.class.root_node.to_sym => attributes } end # Initialize a collection of objects. The collection will be an ApiClient::Collection object. # The objects in the collection will be all instances of this (ApiClient::Base) class. # # @return [Collection] a collection of objects. def self.collection url = "#{ApiClient.config.path[path]}#{resource_path}" attributes = ApiClient::Parser.response(ApiClient::Dispatcher.get(url), url) ApiClient::Collection.new(attributes, self) end class << self alias_method :all, :collection end # Set the hash of errors, making keys symbolic. # # @param [Hash] errs errors of the object. def errors=(errs = {}) errors.add_errors(Hash[errs.map{|(key,value)| [key.to_sym,value]}]) end end end