TalentBox/harpy

View on GitHub
README.md

Summary

Maintainability
Test Coverage
Harpy
======

[![Build Status](https://travis-ci.org/TalentBox/harpy.png?branch=master)](https://travis-ci.org/TalentBox/harpy)
[![Code Climate](https://codeclimate.com/github/TalentBox/harpy.png)](https://codeclimate.com/github/TalentBox/harpy)

Client for REST API with HATEOAS

Dependencies
------------

* Ruby >= 2.4.
* gem "typhoeus", ">= 0.6.5"
* gem "activesupport", ">= 5.2.0"
* gem "activemodel", ">= 5.2.0"
* gem "hash-deep-merge", ">= 0.1.1"

Usage
-----

* Set entry_point url:

        Harpy.entry_point_url = "http://localhost"

* Include `Harpy::Resource` in your model:

        class MyModel
          include Harpy::Resource
        end

        # Mass assignment
        model = MyModel.new "firstname" => "Anthony", "lastname" => "Stark"
        model.attributes = {"company" => "Stark Enterprises"}
        model.firstname # => "Anthony"
        model.company # => "Stark Enterprises"

        # Because model is not persisted you can read any attribute, allowing
        # to use form_for on new resources to which the client doesn't know the
        # existing attributes yet
        model.email # => nil

        # Fetch by url
        MyModel.from_url "http://localhost/mymodel/1"
        # => instance of MyModel with attributes filled in on 200
        # => nil on 404
        # => raises Harpy::ClientTimeout on timeout
        # => raises Harpy::ClientError on Curl error
        # => raises Harpy::InvalidResponseCode on other response codes

        # Fetch multiple by url in parallel
        MyModel.from_url ["http://localhost/mymodel/1", "http://localhost/mymodel/2"]

        # Get index
        MyModel.search
        # will call GET http://localhost/mymodel given the following entry_point response:
          {
            "link": [
              {"rel": "my_model", "href": "http://localhost/mymodel"}
            ]
          }
        # => return an array of MyModel instances on 200
        # => raises Harpy::ClientTimeout on timeout
        # => raises Harpy::ClientError on Curl error
        # => raises Harpy::InvalidResponseCode on other response codes

        # Search by first_name
        MyModel.search :firstname => "Anthony" # GET http://localhost/mymodel?firstname=Anthony

        # Create (POST)
        model = MyModel.new "firstname" => "Anthony"
        model.save # POST http://localhost/mymodel with {"firstname":"Anthony"}

        # Get an existing resource by url:
        model = MyModel.from_url "http://localhost/mymodel/1"
        # if the service returns the following response:
          {
            "firstname": "Anthony",
            "lastname": null,
            "urn": "urn:mycompany:mymodel:1"
            "link" => [
              {"rel" => "self", "href" => "http://localhost/mymodel/1"},
              {"rel" => "accounts", "href" => "http://localhost/mymodel/1/accounts"}
            ]
          }
        # we can then do:
        model.firstname # => "Anthony"
        model.link "self" # => "http://localhost/mymodel/1"
        model.link :accounts # => "http://localhost/mymodel/1/accounts"

        # Update (PUT) requires resource to have both urn and link to self
        model.attributes = {"firstname" => "Tony"}
        model.save # PUT http://localhost/mymodel/1

        # The resource is persisted once it has an urn:
        model.persisted? # => true

        # If persisted you can no longer read undefined attributes:
        model.lastname # => nil
        model.email # => will raise NoMethodError

* To find a resource by id you need to define `.urn`:

        class MyModel
          include Harpy::Resource
          def self.urn(id)
            "urn:mycompany:mymodel:#{id}"
          end
        end

        model = MyModel.from_id 1 # will GET http://localhost/urn:mycompany:mymodel:1
        # expecting a permanent redirect (301) to follow or not found (404)

* Rel name to search for in entry_point when getting index can be overridden:

        class MyCustomModel
          include Harpy::Resource
          def self.resource_name
            "custom_model"
          end
        end

* or you can use `.with_url(url)` for getting index of nested resources:

        class Account
          include Harpy::Resource
          def users
            User.with_url(link "user") do
              User.search
            end
          end
        end
        class User
          include Harpy::Resource
        end

* you can override `#url_collection` to create nested resources:

        class Account
          include Harpy::Resource
        end
        class User
          include Harpy::Resource
          attr_accessor :account
          def url_collection
            account ? account.link("user") : super
          end
        end

* Fetch multiple resources in parallel:

        class FirstModel
          include Harpy::Resource
        end
        class SecondModel
          include Harpy::Resource
        end

        Harpy::Resource.from_url({
           FirstModel => ["http://localhost/firstmodel/1", "http://localhost/firstmodel/2"],
           SecondModel => ["http://localhost/secondmodel/1"],
        })
        # => {FirstModel => [...], SecondModel => [...]}

License
-------

harpy is Copyright © 2011 TalentBox SA. It is free software, and may be redistributed under the terms specified in the LICENSE file.