class ApiResource::Base

Attributes

prefix_options[RW]

Public Class Methods

all(*args) click to toggle source

This is an alias for find(:all). You can pass in all the same arguments to this method as you can to find(:all)

# File lib/api_resource/base.rb, line 199
def all(*args)
  find(:all, *args)
end
build(attributes = {}) click to toggle source
# File lib/api_resource/base.rb, line 161
def build(attributes = {})
  self.new(attributes)
end
collection_path(prefix_options = {}, query_options = nil) click to toggle source
# File lib/api_resource/base.rb, line 156
def collection_path(prefix_options = {}, query_options = nil)
  prefix_options, query_options = split_options(prefix_options) if query_options.nil?
  "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
end
connection(refresh = false) click to toggle source
# File lib/api_resource/base.rb, line 107
def connection(refresh = false)
  @connection = Connection.new(self.site, self.format) if refresh || @connection.nil?
  @connection.timeout = self.timeout
  @connection
end
create(attributes = {}) click to toggle source
# File lib/api_resource/base.rb, line 165
def create(attributes = {})
  self.new(attributes).tap{ |resource| resource.save }
end
delete(id, options = {}) click to toggle source

Deletes the resources with the ID in the id parameter.

Options

All options specify prefix and query parameters.

Examples

Event.delete(2) # sends DELETE /events/2

Event.create(:name => 'Free Concert', :location => 'Community Center')
my_event = Event.find(:first) # let's assume this is event with ID 7
Event.delete(my_event.id) # sends DELETE /events/7

# Let's assume a request to events/5/cancel.xml
Event.delete(params[:id]) # sends DELETE /events/5
# File lib/api_resource/base.rb, line 218
def delete(id, options = {})
  connection.delete(element_path(id, options))
end
element_path(id, prefix_options = {}, query_options = nil) click to toggle source

alias_method :set_prefix, :prefix= alias_method :set_element_name, :element_name= alias_method :set_collection_name, :collection_name=

# File lib/api_resource/base.rb, line 147
def element_path(id, prefix_options = {}, query_options = nil)
  prefix_options, query_options = split_options(prefix_options) if query_options.nil?
  "#{prefix(prefix_options)}#{collection_name}/#{URI.escape id.to_s}.#{format.extension}#{query_string(query_options)}"
end
find(*arguments) click to toggle source
# File lib/api_resource/base.rb, line 169
def find(*arguments)
  scope   = arguments.slice!(0)
  options = arguments.slice!(0) || {}

  case scope
    when :all   then find_every(options)
    when :first then find_every(options).first
    when :last  then find_every(options).last
    when :one   then find_one(options)
    else             find_single(scope, options)
  end
end
first(*args) click to toggle source

A convenience wrapper for find(:first, *args). You can pass in all the same arguments to this method as you can to find(:first).

# File lib/api_resource/base.rb, line 186
def first(*args)
  find(:first, *args)
end
format() click to toggle source

Default format is json

# File lib/api_resource/base.rb, line 98
def format
  read_inheritable_attribute(:format) || ApiResource::Formats::JsonFormat
end
format=(mime_type_or_format) click to toggle source
# File lib/api_resource/base.rb, line 91
def format=(mime_type_or_format)
  format = mime_type_or_format.is_a?(Symbol) ? ApiResource::Formats[mime_type_or_format] : mime_type_or_format
  write_inheritable_attribute(:format, format)
  self.connection.format = format if self.site
end
headers() click to toggle source
# File lib/api_resource/base.rb, line 113
def headers
  @headers || {}
end
inherited(klass) click to toggle source
Calls superclass method
# File lib/api_resource/base.rb, line 25
      def inherited(klass)
        # Call the methods of the superclass to make sure inheritable accessors and the like have been inherited
        super
        # Now we need to define the inherited method on the klass that's doing the inheriting
        # it calls super which will allow the chaining effect we need
        klass.instance_eval <<-EOE, __FILE__, __LINE__ + 1
          def inherited(klass)
            super
          end
        EOE
        # Now we can make a call to setup the inheriting klass with its attributes
        klass.set_class_attributes_upon_load unless klass.instance_variable_defined?(:@class_data)
        klass.instance_variable_set(:@class_data, true)
      end
last(*args) click to toggle source

A convenience wrapper for find(:last, *args). You can pass in all the same arguments to this method as you can to find(:last).

# File lib/api_resource/base.rb, line 193
def last(*args)
  find(:last, *args)
end
new(attributes = {}) click to toggle source
# File lib/api_resource/base.rb, line 311
def initialize(attributes = {})
  @prefix_options = {}
  load(attributes)
end
new_element_path(prefix_options = {}) click to toggle source
# File lib/api_resource/base.rb, line 152
def new_element_path(prefix_options = {})
  "#{prefix(prefix_options)}#{collection_name}/new.#{format.extension}"
end
prefix(options = {}) click to toggle source
# File lib/api_resource/base.rb, line 117
def prefix(options = {})
  default = (self.site ? self.site.path : '/')
  default << '/' unless default[-1..-1] == '/'
  self.prefix = default
  prefix(options)
end
prefix=(value = '/') click to toggle source
# File lib/api_resource/base.rb, line 129
      def prefix=(value = '/')
        prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.escape options[#{key}].to_s}"}
        @prefix_parameters = nil
        silence_warnings do
          instance_eval <<-EOE, __FILE__, __LINE__ + 1
            def prefix_source() "#{value}" end
            def prefix(options={}) "#{prefix_call}" end
          EOE
        end
      rescue Exception => e
        logger.error "Couldn't set prefix: #{e}\n #{code}" if logger
        raise
      end
prefix_source() click to toggle source
# File lib/api_resource/base.rb, line 124
def prefix_source
  prefix
  prefix_source
end
reload_class_attributes() click to toggle source
# File lib/api_resource/base.rb, line 70
def reload_class_attributes
  # clear the public_attribute_names, protected_attribute_names
  self.clear_attributes
  self.clear_associations
  self.set_class_attributes_upon_load
end
set_class_attributes_upon_load() click to toggle source

This makes a request to new_element_path

# File lib/api_resource/base.rb, line 42
def set_class_attributes_upon_load
  begin
    class_data = self.connection.get(self.new_element_path)
    # Attributes go first
    if class_data["attributes"]
      define_attributes *(class_data["attributes"]["public"] || [])
      define_protected_attributes *(class_data["attributes"]["protected"] || [])
    end
    # Then scopes
    if class_data["scopes"]
      class_data["scopes"].each do |hash|
        self.scope hash.first[0], hash.first[1]
      end
    end
    # Then associations
    if class_data["associations"]
      class_data["associations"].each do |(key,hash)|
        hash.each do |assoc_name, assoc_options|
          self.send(key, assoc_name, assoc_options)
        end
      end
    end
  # Swallow up any loading errors because the site may be incorrect
  rescue Exception => e
    return nil
  end
end
site=(site) click to toggle source
# File lib/api_resource/base.rb, line 77
def site=(site)
  @connection = nil
  # reset class attributes and try to reload them if the site changed
  unless site.to_s == self.site.to_s
    self.reload_class_attributes
  end
  if site.nil?
    write_inheritable_attribute(:site, nil)
  else
    write_inheritable_attribute(:site, create_site_uri_from(site))
  end
end
timeout=(timeout) click to toggle source
# File lib/api_resource/base.rb, line 102
def timeout=(timeout)
  @connection = nil
  write_inheritable_attribute(:timeout, timeout)
end

Private Class Methods

create_proxy_uri_from(proxy) click to toggle source

Accepts a URI and creates the proxy URI from that.

# File lib/api_resource/base.rb, line 279
def create_proxy_uri_from(proxy)
  proxy.is_a?(URI) ? proxy.dup : uri_parser.parse(proxy)
end
create_site_uri_from(site) click to toggle source

Accepts a URI and creates the site URI from that.

# File lib/api_resource/base.rb, line 274
def create_site_uri_from(site)
  site.is_a?(URI) ? site.dup : uri_parser.parse(site)
end
find_every(options) click to toggle source

Find every resource

# File lib/api_resource/base.rb, line 224
def find_every(options)
  begin
    case from = options[:from]
    when Symbol
      instantiate_collection(get(from, options[:params]))
    when String
      path = "#{from}#{query_string(options[:params])}"
      instantiate_collection(connection.get(path, headers) || [])
    else
      prefix_options, query_options = split_options(options[:params])
      path = collection_path(prefix_options, query_options)
      instantiate_collection( (connection.get(path, headers) || []), prefix_options )
    end
  rescue ApiResource::ResourceNotFound
    # Swallowing ResourceNotFound exceptions and return nil - as per
    # ActiveRecord.
    nil
  end
end
find_one(options) click to toggle source

Find a single resource from a one-off URL

# File lib/api_resource/base.rb, line 245
def find_one(options)
  case from = options[:from]
  when Symbol
    instantiate_record(get(from, options[:params]))
  when String
    path = "#{from}#{query_string(options[:params])}"
    instantiate_record(connection.get(path, headers))
  end
end
find_single(scope, options) click to toggle source

Find a single resource from the default URL

# File lib/api_resource/base.rb, line 256
def find_single(scope, options)
  prefix_options, query_options = split_options(options[:params])
  path = element_path(scope, prefix_options, query_options)
  instantiate_record(connection.get(path, headers), prefix_options)
end
instantiate_collection(collection, prefix_options = {}) click to toggle source
# File lib/api_resource/base.rb, line 262
def instantiate_collection(collection, prefix_options = {})
  collection.collect! { |record| instantiate_record(record, prefix_options) }
end
instantiate_record(record, prefix_options = {}) click to toggle source
# File lib/api_resource/base.rb, line 266
def instantiate_record(record, prefix_options = {})
  new(record).tap do |resource|
    resource.prefix_options = prefix_options
  end
end
prefix_parameters() click to toggle source

contains a set of the current prefix parameters.

# File lib/api_resource/base.rb, line 284
def prefix_parameters
  @prefix_parameters ||= prefix_source.scan(/:\w+/).map { |key| key[1..-1].to_sym }.to_set
end
query_string(options) click to toggle source

Builds the query string for the request.

# File lib/api_resource/base.rb, line 289
def query_string(options)
  "?#{options.to_query}" unless options.nil? || options.empty?
end
split_options(options = {}) click to toggle source

split an option hash into two hashes, one containing the prefix options, and the other containing the leftovers.

# File lib/api_resource/base.rb, line 295
def split_options(options = {})
  prefix_options, query_options = {}, {}
  (options || {}).each do |key, value|
    next if key.blank?
    (prefix_parameters.include?(key.to_sym) ? prefix_options : query_options)[key.to_sym] = value
  end

  [ prefix_options, query_options ]
end
uri_parser() click to toggle source
# File lib/api_resource/base.rb, line 305
def uri_parser
  @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
end

Public Instance Methods

==(other) click to toggle source
# File lib/api_resource/base.rb, line 334
def ==(other)
  other.equal?(self) || (other.instance_of?(self.class) && other.id == self.id && other.prefix_options == self.prefix_options)
end
destroy() click to toggle source
# File lib/api_resource/base.rb, line 361
def destroy
  connection.delete(element_path(self.id), self.class.headers)
end
dup() click to toggle source
# File lib/api_resource/base.rb, line 346
def dup
  self.class.new.tap do |resource|
    resource.attributes = self.attributes
    resource.prefix_options = @prefix_options
  end
end
encode(options = {}) click to toggle source
# File lib/api_resource/base.rb, line 365
def encode(options = {})
  self.send("to_#{self.class.format.extension}", options)
end
eql?(other) click to toggle source
# File lib/api_resource/base.rb, line 338
def eql?(other)
  self == other
end
hash() click to toggle source
# File lib/api_resource/base.rb, line 342
def hash
  id.hash
end
id() click to toggle source
# File lib/api_resource/base.rb, line 325
def id
  self.attributes[self.class.primary_key]
end
id=(id) click to toggle source

Bypass dirty tracking for this field

# File lib/api_resource/base.rb, line 330
def id=(id)
  attributes[self.class.primary_key] = id
end
inspect()
Alias for: to_s
load(attributes) click to toggle source
# File lib/api_resource/base.rb, line 373
def load(attributes)
  return if attributes.nil?
  raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
  @prefix_options, attributes = split_options(attributes)
 
  attributes.symbolize_keys.each do |key, value|
    # If this attribute doesn't exist define it as a protected attribute
    self.class.define_protected_attributes(key) unless self.respond_to?(key)
    self.attributes[key] =
    case value
      when Array
        if self.has_many?(key)
          MultiObjectProxy.new(self.has_many_class_name(key), value)
        elsif self.association?(key)
          raise ArgumentError, "Expected a hash value or nil, got: #{value.inspect}"
        else
          value
        end
      when Hash
        if self.has_many?(key)
          MultiObjectProxy.new(self.has_many_class_name(key), value)
        elsif self.association?(key)
          SingleObjectProxy.new(self.association_class_name(key), value)
        else
          value
        end
      when NilClass
        # If it's nil and an association then create a blank object
        if self.has_many?(key)
          return MultiObjectProxy.new(self.has_many_class_name(key), [])
        elsif self.association?(key)
          SingleObjectProxy.new(self.association_class_name(key), value)
        end
      else
        raise ArgumentError, "expected an array or a hash for the association #{key}, got: #{value.inspect}" if self.association?(key)
        value.dup rescue value
    end
  end
  return self
end
new?() click to toggle source
# File lib/api_resource/base.rb, line 316
def new?
  id.nil?
end
Also aliased as: new_record?
new_record?()
Alias for: new?
persisted?() click to toggle source
# File lib/api_resource/base.rb, line 321
def persisted?
  !new?
end
reload() click to toggle source
# File lib/api_resource/base.rb, line 369
def reload
  self.load(self.class.find(to_param, :params => @prefix_options).attributes)
end
save(*args) click to toggle source
# File lib/api_resource/base.rb, line 353
def save(*args)
  new? ? create(*args) : update(*args)
end
save!(*args) click to toggle source
# File lib/api_resource/base.rb, line 357
def save!(*args)
  save(*args) || raise(ApiResource::ResourceInvalid.new(self))
end
serializable_hash(options = {}) click to toggle source
# File lib/api_resource/base.rb, line 433
def serializable_hash(options = {})
  options[:include_associations] = options[:include_associations] ? options[:include_associations].symbolize_array : []
  options[:include_extras] = options[:include_extras] ? options[:include_extras].symbolize_array : []
  options[:except] ||= []
  ret = self.attributes.inject({}) do |accum, (key,val)|
    # If this is an association and it's in include_associations then include it
    if self.association?(key) && options[:include_associations].include?(key.to_sym)
      accum.merge(key => val.serializable_hash({}))
    elsif options[:include_extras].include?(key.to_sym)
      accum.merge(key => val)
    elsif options[:except].include?(key.to_sym)
      accum
    else
      self.association?(key) || !self.attribute?(key) || self.protected_attribute?(key) ? accum : accum.merge(key => val)
    end
  end
end
to_json(options = {}) click to toggle source
# File lib/api_resource/base.rb, line 429
def to_json(options = {})
  self.class.include_root_in_json ? {self.class.element_name => self.serializable_hash(options)}.to_json : self.serializable_hash(options).to_json
end
to_s() click to toggle source

Override to_s and inspect so they only show attributes and not associations, this prevents force loading of associations when we call to_s or inspect on a descendent of base but allows it if we try to evaluate an association directly

# File lib/api_resource/base.rb, line 418
def to_s
  return "#<#{self.class}:0x%08x @attributes=#{self.attributes.inject({}){|accum,(k,v)| self.association?(k) ? accum : accum.merge(k => v)}}" % (self.object_id * 2)
end
Also aliased as: inspect
to_xml(options = {}) click to toggle source

Methods for serialization as json or xml, relying on the serializable_hash method

# File lib/api_resource/base.rb, line 425
def to_xml(options = {})
  self.serializable_hash(options).to_xml(:root => self.class.element_name)
end

Protected Instance Methods

collection_path(prefix_options = {},query_options = nil) click to toggle source
# File lib/api_resource/base.rb, line 468
def collection_path(prefix_options = {},query_options = nil)
  self.class.collection_path(prefix_options, query_options)
end
connection(refresh = false) click to toggle source
# File lib/api_resource/base.rb, line 452
def connection(refresh = false)
  self.class.connection(refresh)
end
create(*args) click to toggle source
# File lib/api_resource/base.rb, line 472
def create(*args)
  opts = args.extract_options!
  # When we create we should not include any blank attributes unless they are associations
  except = self.class.include_blank_attributes_on_create ? {} : self.attributes.select{|k,v| v.blank?}
  opts[:except] = opts[:except] ? opts[:except].concat(except.keys).uniq.symbolize_array : except.keys.symbolize_array
  opts[:include_associations] = opts[:include_associations] ? opts[:include_associations].concat(args) : []
  opts[:include_extras] ||= []
  body = RestClient::Payload.has_file?(self.attributes) ? self.serializable_hash(opts) : encode(opts)
  connection.post(collection_path, body, self.class.headers).tap do |response|
    load_attributes_from_response(response)
  end
end
element_path(id, prefix_options = {}, query_options = nil) click to toggle source
# File lib/api_resource/base.rb, line 460
def element_path(id, prefix_options = {}, query_options = nil)
  self.class.element_path(id, prefix_options, query_options)
end
load_attributes_from_response(response) click to toggle source
# File lib/api_resource/base.rb, line 456
def load_attributes_from_response(response)
  load(response)
end
new_element_path(prefix_options = {}) click to toggle source
# File lib/api_resource/base.rb, line 464
def new_element_path(prefix_options = {})
  self.class.new_element_path(prefix_options)
end
update(*args) click to toggle source
# File lib/api_resource/base.rb, line 485
def update(*args)
  opts = args.extract_options!
  # When we create we should not include any blank attributes
  except = self.class.attribute_names - self.changed.symbolize_array
  changed_associations = self.changed.symbolize_array.select{|item| self.association?(item)}
  opts[:except] = opts[:except] ? opts[:except].concat(except).uniq.symbolize_array : except.symbolize_array
  opts[:include_associations] = opts[:include_associations] ? opts[:include_associations].concat(args).concat(changed_associations).uniq : changed_associations.concat(args)
  opts[:include_extras] ||= []
  opts[:except] = [:id] if self.class.include_all_attributes_on_update
  body = RestClient::Payload.has_file?(self.attributes) ? self.serializable_hash(opts) : encode(opts)
  # We can just ignore the response
  connection.put(element_path(self.id, prefix_options), body, self.class.headers).tap do |response|
    load_attributes_from_response(response)
  end
end

Private Instance Methods

split_options(options = {}) click to toggle source
# File lib/api_resource/base.rb, line 503
def split_options(options = {})
  self.class.__send__(:split_options, options)
end