Purpose
The lutaml-hal gem provides a framework for interacting with HAL-compliant
APIs using the power of LutaML Models.
Hypertext Application Language (HAL) (HAL Internet-Draft) is a simple format for representing resources and their relationships in a hypermedia-driven API.
It allows clients to navigate and interact with resources using links, making it easier to build flexible and extensible applications.
This library provides a set of classes and methods for modeling HAL resources, links, and collections, as well as a client for making HTTP requests to HAL APIs.
Features
-
Classes for modeling HAL resources and links
-
A client for making HTTP requests to HAL APIs
-
Tools for pagination and resource resolution
-
Integration with the
lutaml-modelserialization framework -
Error handling and response validation for API interactions
Installation
Add this line to your application’s Gemfile:
gem 'lutaml-hal'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install lutaml-hal
Structure
The classes in this library are organized into the following modules:
Lutaml::Hal::Client-
A client for making HTTP requests to HAL APIs. It includes methods for setting the API endpoint, making GET requests, and handling responses.
NoteOnly GET requests are supported at the moment. Lutaml::Hal::ModelRegister-
A registry for managing HAL resource models and their endpoints. It allows you to register models, define their relationships, and fetch resources from the API.
Lutaml::Hal::Resource-
A base class for defining HAL resource models. It includes methods for defining attributes, links, and key-value mappings for resources.
Lutaml::Hal::Link-
A class for defining HAL links. It includes methods for specifying the relationship between resources and their links, as well as methods for resolving links to their target resources.
Lutaml::Hal::Page-
A class for handling pagination in HAL APIs. It includes methods for defining pagination attributes, such as
page,pages,limit, andtotal, as well as methods for accessing linked resources within a page.
Usage
General
In order to interact with a HAL API, the following steps are required:
-
Create a
Clientthat points to the API endpoint. -
Create a
ModelRegisterto manage the resource models and their respective endpoints. -
Define the resource models using the
Resourceclass. -
Register the models with the
ModelRegister. -
Fetch resources from the API using the
ModelRegister.-
Once the resources are fetched, you can access their attributes and links and navigate through the resource graph.
-
-
Pagination, such as on "index" type pages, can be handled by subclassing the
Pageclass. ThePageclass itself is also implemented as aResource, so you can use the same methods to access the page’s attributes and links.
Creating a HAL model register
require 'lutaml-hal'
# Create a new client with API endpoint
client = Lutaml::Hal::Client.new(api_url: 'https://api.example.com')
register = Lutaml::Hal::ModelRegister.new(client: client)
# Or set client later, `register.client = client`
register.add_endpoint(
id: :product_index,
type: :index,
url: '/products',
model: Product
)
register.add_endpoint(
id: :product_resource,
type: :resource,
url: '/products/{id}',
model: Product
)
register.fetch(:product_index)
# => client.get('/products')
# => {
# "page": 1,
# "pages": 10,
# "limit": 10,
# "total": 45,
# "_links": {
# "self": { "href": "/products/1" },
# "next": { "href": "/products/2" },
# "last": { "href": "/products/5" },
# "products": [
# { "id": 1, "name": "Product 1", "price": 10.0 },
# { "id": 2, "name": "Product 2", "price": 15.0 }
# ]
# }
product_1 = register.fetch(:product_resource, id: 1)
# => client.get('/products/1')
# => {
# "id": 1,
# "name": "Product 1",
# "price": 10.0,
# "_links": {
# "self": { "href": "/products/1" },
# "category": { "href": "/categories/1", "title": "Category 1" },
# "related": [
# { "href": "/products/3", "title": "Product 3" },
# { "href": "/products/5", "title": "Product 5" }
# ]
# }
# }
product_1
# => #<Product id: 1, name: "Product 1", price: 10.0, links:
# #<ProductLinks self: <ProductLink href: "/products/1">,
# category: <ProductLink href: "/categories/1", title: "Category 1">,
# related: [
# <ProductLink href: "/products/3", title: "Product 3">,
# <ProductLink href: "/products/5", title: "Product 5">
# ]}>
Defining resource models
module MyApi
class Product < Lutaml::Hal::Resource
attribute :id, :string
attribute :name, :string
attribute :price, :float
hal_link :self, key: 'self', realize_class: 'Product'
hal_link :category, key: 'category', realize_class: 'Category'
key_value do
map 'id', to: :id
map 'name', to: :name
map 'price', to: :price
end
end
# Register the model with the registry
Lutaml::Hal::ModelRegister.register(Product, '/products/{id}')
end
Registering endpoints
Fetching Resources
# Assume that the client is already created and registered at
# the ModelRegister
# Get a resource
product = client.get('products/123')
product_resource = MyApi::Product.from_json(product.to_json)
# Follow a link
category = product_resource.category.realize(register)
Working with Collections
class ProductPage < Lutaml::Hal::Page
# Define the relationship between page and items
end
response = client.get('/products')
products = ProductPage.from_json(response.to_json)
# Access pagination info
puts "Page #{products.page} of #{products.pages}, total: #{products.total}"
# Access linked items
products.links.products.each do |product|
puts "#{product.name}: $#{product.price}"
end
License and Copyright
This project is licensed under the BSD 2-clause License. See the LICENSE.md file for details.
Copyright Ribose.