Internals

This doc explains more on the internals of the API.

5.1. Requesters

A Requester is any API object which can make further requests. In the case of GHContent for example, one can send GHContent>>updateContent:withMessage:. Requesters are users of the GHTRequester trait.

Not all API objects are Requesters. A good example are the subclasses of GHGitObject and GHRef, which are mutable for the purpose of passing them as parameters (more on the Git Data API 2 here).

Requesters can be more easily seen as an extension wrapper around Zinc's ZnClient class. Like ZnClient, Requesters are stateful. Requesters expand Zinc with functionality for the API. In short, these functionalities are:

  • Parsing the JSON representation to an API object
  • Error handling
  • Conditional requests

The following sections will go more in-depth on them.

5.2. Response Parsing

The parsing of the JSON is done with the excellent NeoJSON library (read this doc for an introduction). In NeoJSON there is the concept of a mapping, which defines how to read from (and write to) a JSON representation into an instantiated object. The mapping of an object can be one of ObjectMapping or CustomMapping. The first is one that maps to a class' properties (in our case just instance variables), the latter is one that allows for a custom definition of a mapping (how the value should be interpreted).

The API bindings provide a trait, GHTMappedToJSON, which contains only class-side methods related to NeoJSON mappings. The trait includes the mapping of URLs (instances of ZnUrl) and DateAndTime instance variables. Users of this trait can then extend the mappings with custom ones for other types.

Generating instance variables, accessors and mapping definitions

When creating a new API object, one might want to generate some of the instance variables, accessors and mapping definitions automatically.

This can be done using GHRelObject class>>generateInstanceVariablesAndMethodsWithAPIKeys:, which when provided an array of API keys (e.g. created_at, username), generates instance variables and accessors for them.

Furthermore, API keys with the suffix _url are automatically set to be mapped as ZnUrl, and those ending with _at will be mapped to DateAndTime instances.

5.3. Error Handling

The handling of errors is done before parsing the response. The error handlers are defined as BlockClosures which take a response as argument. They are stored in a Dictionary with as their key the HTTP integer error code (e.g. 404). By default they trigger an Error, which allows one to use the regular Smalltalk syntax for handling them:

[ github user ]
   on: GHBadCredentialsError
   do: [ UIManager default inform: 'Incorrect username or password!' ]

5.4. Conditional Requests

Conditional requests allow one to test if a resource was modified. If it was modified, one gets back the new resource as JSON. If not, the response will be 304 Not Modified without any content (for reducing bandwidth).

The check of whether or not a resource has changed is done with a hash value called an ETag. Any Requester has access to these ETags using the GHTRequester>>urlToETag accessor.

A conditional request is made by sending GHTRequester>>conditionalGet: with an URL. It uses the #urlToETag Dictionary to get the ETag when a new request is made. However it is probably not necessary to use this method directly.

Often one wants to ask to a domain object whether its remote resource has changed. This is provided by GHObject>>isOutdated, which performs a conditional request (if necessary) with its own url as argument.

Even more often one wants to ask a domain object to update itself if it changed, which can be done using GHObject>>update. This method uses #isOutdated internally, and if it returns true it copies all the instance variables of the response to itself.