sparkapi/spark_api

View on GitHub
lib/spark_api/models/base.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module SparkApi
  module Models
    # =API Model Base class
    # Intended to be a lot like working with ActiveResource, this class adds most of the basic 
    # active model type niceties.
    class Base
      extend Paginate
      include Dirty

      attr_accessor :attributes, :errors, :parent

      # More familiar accessor for our Spark API Id method
      def id
        self.Id
      end

      # Name of the resource as related to the path name
      def self.element_name
        # TODO I'd love to pull in active model at this point to provide default naming
        @element_name ||= "resource"
      end
      def self.element_name=(name)
        @element_name = name
      end

      # Resource path prefix, prepended to the url
      def self.prefix
        @prefix ||= "/"
      end
      def self.prefix=(prefix)
        @prefix = prefix
      end

      def resource_uri
        self.ResourceUri.sub(/^\/#{SparkApi.client.version}/, "") if persisted?
      end

      def self.path
        "#{prefix}#{element_name}"
      end
      def path
        if self.persisted?
          resource_uri.sub(/\/[0-9]{26}$/, "")
        else
          if @parent
            "#{@parent.class.path}/#{@parent.Id}#{self.class.path}"
          else
            self.class.path
          end
        end
      end

      def self.connection
        SparkApi.client
      end
      def connection
        self.class.connection
      end

      def initialize(attributes={})
        @attributes = {}
        @errors = []
        load(attributes, { :clean => true })
      end

      def load(attributes, options = {})
        attributes.each do |key,val|
          attribute_will_change!(key) unless options[:clean]
          @attributes[key.to_s] = val
        end
      end

      def self.get(options={})
        collect(connection.get(path, options))
      end

      def self.first(options={})
        get(options).first
      end

      def self.count(options={})
        connection.get(path, options.merge({:_pagination=>"count"}))
      end

      def method_missing(method_symbol, *arguments)
        method_name = method_symbol.to_s

        if method_name =~ /(=|\?|_will_change!)$/
          case $1
          when "=" 
            write_attribute($`, arguments.first)
            # TODO figure out a nice way to present setters for the standard fields
          when "?" 
            raise NoMethodError unless attributes.include?($`)
            attributes[$`] ? true : false
          when "_will_change!"
            raise NoMethodError unless attributes.include?($`)
            attribute_will_change!($`)
          end 
        else
          return attributes[method_name] if attributes.include?(method_name)
          super
        end
      end

      def respond_to?(method_symbol, include_all=false)
        if super
          return true
        else
          method_name = method_symbol.to_s

          if method_name =~ /=$/
            true
          elsif method_name =~ /(\?)$/
            attributes.include?($`)
          elsif method_name =~ /(\w*)_will_change!$/
            attributes.include?($1)
          else
            attributes.include?(method_name)
          end

        end
      end

      def parse_id(uri)
        uri[/\/.*\/(.+)$/, 1]
      end

      def persisted?;
        !@attributes['Id'].nil? && !@attributes['ResourceUri'].nil?
      end

      def to_param
        attributes['Id']
      end

      def to_partial_path
        "#{underscore(resource_pluralized)}/#{underscore(self.class.name.split('::').last)}"
      end

      # can be overridden
      def resource_pluralized
        resource = self.class.name.split('::').last
        unless resource.split('').last == "s"
          resource = resource + "s"
        end
        resource
      end

      protected

      def write_attribute(attribute, value)
        attribute = attribute.to_s
        unless attributes[attribute] == value
          attribute_will_change!(attribute)
          attributes[attribute] = value
        end
      end

      private

      def underscore(string)
        string.to_s.gsub(/::/, '/').
        gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
        gsub(/([a-z\d])([A-Z])/,'\1_\2').
        tr("-", "_").
        downcase
      end

    end
  end
end