Skip to content

Create an adapter class

Jess edited this page Jul 13, 2016 · 1 revision

An API adapter class contains methods for connecting to and searching a third party API. Whowas provides the public interface for all adapter classes, but there are required and optional private methods you should define.

First, generate a template:

rails generate whowas:adapter MyAdapter

This will create a file app/whowas/adapters/my_adapter.rb that looks like this (but with comments):

module Whowas
  class MyAdapter
    include Whowas::Adapter
  
    def search_api(input)
      ""
    end

    def validate(input)
      true ||
      (raise Whowas::Errors::InvalidInput, "Invalid input for #{self.class.name}")
    end
    
    def format(input)
      input
    end
  end
end

The entire interface consists of one method, search, which is defined in Whowas::Adapter

def search(input)
  validate(input)
  input = format(input)
  search_api(input)
end

As you can see, all it does is call the private methods defined on each adapter in order. validate and format are optional, but search_api must be defined in each adapter class.

validate [optional]

Validations defined here check the input for all search methods using this adaoter; therefore, use for conditions that are universal to this API's queries. (You can run validations at the search method level as well.)

Example: MyAdapter requires a valid timestamp for any query it runs.

def validate(input)
  DateTime.parse(input[:timestamp]) && true ||
  (raise Whowas::Errors::InvalidInput, "Invalid input for MyAdapter")
end

validate should return true or raise Whowas::Errors::InvalidInput.

If undefined, validate always returns true.

format [optional]

format allows you to transform the input to match the API's requirements. Again, this method is adapter-wide; you can define transformations specific to a particular search method at that level. Feel free to define helper methods, such as format_timestamp below.

Example: The input hash needs to be formatted to match the API specifications.

def format(input)
  {
    query: "search #{input[:query]}",
    timestamp: format_timestamp(input[:timestamp])
  }
end

def format_timestamp(timestamp)
  DateTime.parse(timestamp).strftime("Y-%m-%dT%H:%M:%S")
end

format should return data in the format the API requires. Specifically, the output from format will be the input to search_api (more below).

If undefined, format returns its argument unchanged.

search_api [REQUIRED]

Here is the core of the API search. The contents of this method will depend on the API itself, of course, but an example search_api method might look like this:

def search_api(input)
  connection = api.connect!
  results = connection.search(input)
  if results
    results.to_s
  else
    ""
  end
rescue Api::ServiceDown => e
  raise Whowas::Errors::ServiceUnavailable, e
end

Again, feel free to define helper methods as necessary. It is useful sometimes to create a class-level variable for the connection itself, so it can be shared between multiple calls.

search_api must return a string, either with the results or empty. If your API returns its results in another format, you must transform it to a string.

search_api may raise Whowas::Errors::ServiceUnavailable if the third-party service is unreachable.