module ActionKitApi::Searchable

Public Class Methods

included(class_name) click to toggle source

This layer is needed to have the methods added at a class level rather than an instance level

# File lib/action_kit_api/searchable.rb, line 5
def self.included(class_name)

  # Override's method missing to dynamically add find_by_* and
  # find_all_by_*.
  def class_name.method_missing(method, *args, &block)
    case method.to_s
      when /^find_by_(.+)$/
        run_find_by_method($1, *args)
      when /^find_all_by_(.+)$/
        run_find_all_by_method($1, *args, &block)
    else
      super
    end
  end

  # Perform an API search for an individual object
  def class_name.run_find_by_method(attrs, *args)
    # Get the name of the class that this module has been mixed into
    class_name = self.to_s.split("::").last

    search = self.parse_attributes(attrs, args)

    begin
      response = ActionKitApi.connection.call("#{class_name}.get", search)
    rescue XMLRPC::FaultException => exception
      if exception.faultCode == "DoesNotExist"
        return nil
      else
        raise exception
      end
    end

    # Build a new object with the response, this might be an empty
    # object if the search failed or was invalid
    self.new(response)
  end

  # Perform an API search for all objects that match the criteria, remote
  # API limit is 1000 results per request so this is added explicitely to
  # the call to prevent errors, in the future this request probably could
  # be paginated in a loop and using count to get all of the results that
  # match.
  def class_name.run_find_all_by_method(attrs, *args, &block)
    # Get the name of the class that this module has been mixed into
    class_name = self.to_s.split("::").last

    search = self.parse_attributes(attrs, args)
    
    # For when I get to retrieving all results, through pagination
    # Would be a good candidate for yielding (probably in batches of 50-100)
    #response_count = self.count(search)
    search["limit"] = 1000
    #search["offset"] = 0

    response = ActionKitApi.connection.call("#{class_name}.get", search)

    response_objects = []
    response.each do |obj|
      response_objects << self.new(obj)
    end

    response_objects
  end

  def class_name.count(search)
    ActionKitApi.connection.call("#{class_name}.count", search)
  end

  # Helper method for method_missing and the find_* virtual methods that
  # parses part of a method name and arguments into a hash usuable for the
  # API calls
  def class_name.parse_attributes(attrs, args)
    attrs = attrs.split('_and_')
    attrs_with_args = attrs.zip(args)
    Hash[attrs_with_args]
  end

  # Provide a means to check if the searchable module can respond to
  # virtual methods, could be more accurate by checking to ensure the
  # attributes exist
  def class_name.respond_to?(method)
    case method.to_s
      when /^find_by_(.*)$/
        true
      when /^find_all_by_(.*)$/
        true
    else
      super
    end
  end
end