<commit_before># Voorhees

## Design goals

* Be as fast as possible
* Be simple, yet configurable
* Include just what you need
* Don't stomp on object hierarcy (it's a mixin)
* Lazy load only the objects you need, when you need them

## Example usage

    class User
      include Voorhees::Resource
      
      json_service :list, :path => "/users/find.json"
    
      def messages
        json_request(Message) do |r|
          r.path = "/#{self.id}/messages.json"
        end
      end
    end
    
    users = User.list(:page => 2)
    
    user = users[0]
    user.json_attributes      => [:id, :login, :email]
    user.raw_json             => {:id => 1, :login => 'test', :email => '<EMAIL>'}
    user.login                => 'test'
    user.messages             => [Message, Message, Message, ...]

See [/examples/](master/examples/) directory for more.

## A bit more in-depth

### Configuration

Setup global configuration for requests with Voorhees::Config
These can all be overridden on individual requests/services

    Voorhees::Config.setup do |c|
      c[:base_uri]  = "http://api.example.com/json"
      c[:defaults]  = {:api_version => 2}
      c[:timeout]   = 10
      c[:retries]   = 3
    end

#### Global options

* logger: set a logger to use for debug messages, defaults to Logger.new(STDOUT) or RAILS_DEFAULT_LOGGER if it's defined

#### Request global options

These can be set in the global config and overridden on individual services/requests

* base_uri: Prepend all paths with this, usually the domain of the service
* defaults: A hash of default parameters
* http_method: The Net::HTTP method to use. One of Net::HTTP::Get (default), Net::HTTP::Post, Net::HTTP::Put or Net::HTTP::Delete 
* retries: Number of times to retry if it fails to load data from the service
* timeout: Number of seconds to wait for the service to send data

#### Request specific options

These cannot be globally set and can only be defined on individual services/requests

* hierarchy: Define the class hierarchy for nested data - see below for info
* parameters: Hash of data to send along with the request, overrides any defaults
* path: Path to the service. Can be relative if you have a base_uri set.
* required: Array of required parameters. Raises a Voorhees::ParameterMissingError if a required parameter is not set.

### Services and Requests

There are 3 ways to communicate with the service.

#### json_service

This sets up a class method

    class User
      include Voorhees::Resource
      json_service :list, :path => "/users.json"
    end

    User.list(:page => 3)   =>  [User, User, User, ...] 

By default it assumes you're getting items of the same class, you can override this like so:
    
    json_service :list, :path   => "/users.json",
                        :class  => OtherClass

#### json_request

This is used in instance methods:

    class User
      include Voorhees::Resource
      
      def friends
        json_request do |r|
          r.path => "/friends.json"<commit_msg>Added note about timeouts to README<commit_after># Voorhees

## Design goals

* Be as fast as possible
* Be simple, yet configurable
* Include just what you need
* Don't stomp on object hierarcy (it's a mixin)
* Lazy load only the objects you need, when you need them

## Example usage

    class User
      include Voorhees::Resource
      
      json_service :list, :path => "/users/find.json"
    
      def messages
        json_request(Message) do |r|
          r.path = "/#{self.id}/messages.json"
        end
      end
    end
    
    users = User.list(:page => 2)
    
    user = users[0]
    user.json_attributes      => [:id, :login, :email]
    user.raw_json             => {:id => 1, :login => 'test', :email => '<EMAIL>'}
    user.login                => 'test'
    user.messages             => [Message, Message, Message, ...]

See [/examples/](master/examples/) directory for more.

## A bit more in-depth

### Configuration

Setup global configuration for requests with Voorhees::Config
These can all be overridden on individual requests/services

    Voorhees::Config.setup do |c|
      c[:base_uri]  = "http://api.example.com/json"
      c[:defaults]  = {:api_version => 2}
      c[:timeout]   = 10
      c[:retries]   = 3
    end

#### Global options

* logger: set a logger to use for debug messages, defaults to Logger.new(STDOUT) or RAILS_DEFAULT_LOGGER if it's defined

#### Request global options

These can be set in the global config and overridden on individual services/requests

* base_uri: Prepend all paths with this, usually the domain of the service
* defaults: A hash of default parameters
* http_method: The Net::HTTP method to use. One of Net::HTTP::Get (default), Net::HTTP::Post, Net::HTTP::Put or Net::HTTP::Delete 
* retries: Number of times to retry if it fails to load data from the service
* timeout: Number of seconds to wait for the service to send data

#### Request specific options

These cannot be globally set and can only be defined on individual services/requests

* hierarchy: Define the class hierarchy for nested data - see below for info
* parameters: Hash of data to send along with the request, overrides any defaults
* path: Path to the service. Can be relative if you have a base_uri set.
* required: Array of required parameters. Raises a Voorhees::ParameterMissingError if a required parameter is not set.

### Timeouts and Retries

As well as setting the open_timeout/read_timeout of Net::HTTP, we also wrap each request in a timeout check.

If [SystemTimer](http://ph7spot.com/articles/system_timer) is installed it will use this, otherwise it falls back on the Timeout library.

If the request fails with a Timeout::Error, or a Errno::ECONNREFUSED, we attept the request again upto the number of retries specified.

For Errno::ECONNREFUSED errors, we also sleep for 1 second to give the service a chance to wake up.

### Services and Requests

There are 3 ways to communicate with the service.

#### json_service

This sets up a class method

    class User
      include Voorhees::Resource
      json_service :list, :path => "/users.json"
    end

    User.list(:page => 3)   =>  [User, User, User, ...] 

By default it assumes you're getting items of the same class, you can override this like so:
    
    json_service :list, :path   => "/users.json",
                        :class  => OtherClass

#### json_request

This is used in instance methods:

    class User
      include Voorhees::Resource
      
      def friends
        json_request do |r|
          r.path => "/friends.json"
{'fraction_non_alphanumeric': 0.09405865370004558, 'fraction_numerical': 0.00303905181583346, 'mean_word_length': 3.0012158054711247, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1015751', 'n_tokens_mistral': 1906, 'n_tokens_neox': 1833, 'n_words': 791}