The Aglyph Context fluent API — Aglyph 3.0.0 documentation

# The Aglyph Context fluent API

Release: | 3.0.0

- Introduction to the Context fluent API
  - A note regarding the component() entry point method
- Using component references with the Context fluent API
- Using the Context fluent API to define templates
- Letting Aglyph introspect dotted names
- Overview of the Context fluent API methods

## Introduction to the Context fluent API

The easiest way to configure Aglyph programmatically is to use the “fluent” API exposed by `aglyph.context.Context`.

The Context fluent API consists of a set of chained-call-style methods (exposed on `Context` or its subclasses) that can be used to define components of any kind (prototype, singleton, borg, weakref) as well as templates.

There are six (6) “entry points” into the Context fluent API:

1. `aglyph.context.Context.prototype()` to define a prototype component
2. `aglyph.context.Context.singleton()` to define a singleton component
3. `aglyph.context.Context.borg()` to define a borg component
4. `aglyph.context.Context.weakref()` to define a weakref component
5. `aglyph.context.Context.template()` to define a template
6. `aglyph.context.Context.component()` to define any kind of component (more on this method later)

Each entry point returns a “builder” which can be used to further define the component or template.

For the sake of demonstration, assume we begin with the following initialization code:

    from aglyph.assembler import Assembler
    from aglyph.component import Reference as ref
    from aglyph.context import Context

    context = Context("example-context")

Let’s define a singleton component:

    context.singleton("my-singleton")

Here we have provided a unique ID for the component (“my-singleton”). Since this is not a dotted name, though, Aglyph doesn’t know which class to use to create the object. We will provide the class name with a chained call to the `create()` method:

    context.singleton("my-singleton").create("module.ClassName")

We could also have used the dotted name as the unique ID, in which case calling `create()` would not be necessary.

Now we wish to define the initialization arguments and keywords. We add another chained call to the `init()` method to do so. We can also break the chained call sequence onto multiple lines for readability:

    (context.singleton("my-singleton").
        create("module.ClassName").
        init("argument", keyword="keyword"))

Finally, we terminate the fluent sequence by calling the `register()` method, which actually creates an `aglyph.component.Component` and adds it to the context:

    (context.singleton("my-singleton").
        create("module.ClassName").
        init("argument", keyword="keyword").
        register())

Note

Terminating the fluent builder with a call to `register()` is crucial. The component definition is not actually created or stored in the context unless/until this method is called!

Now this context can be given to an assembler, and we can create an object:

    assembler = Assembler(context)
    my_singleton = assembler.assemble("my-singleton")

You can find many other examples of using the Context fluent API in the Aglyph cookbook.

### A note regarding the `component()` entry point method

The strategy-specific entry point methods (`prototype()`, `singleton()`, `borg()` and `weakref()`) are implemented in terms of `component()`.

For example, calling `singleton("package.ClassName")` is the shorthand equivalent of calling `component("package.ClassName").create(strategy="singleton")`.

If `component("my-id")` is used to define a component and no explicit strategy is specified, then the default strategy (“prototype”) is assumed.

Warning

There is one “special case” that warrants explanation:

When specifying a member using the fluent API (i.e. objects of the component are “created” by attribute access on the object identified by a dotted name), then the creation strategy is implicitly set to “_imported” and SHOULD NOT be set explicitly. In short - consider member and strategy to be mutually exclusive.

## Using component references with the Context fluent API

Let’s add another component to the context. This new component needs to be injected with an object of “my-singleton” via a property. We will use an `aglyph.component.Reference` to provide the dependency:

    (context.prototype("example-object").
        create("module2.ExampleObject").
        set(thing=ref("my-singleton")).
        register())

Assembling an object of “example-object” will resolve “my-singleton” to the same object we assembled earlier, and will set that object as the `module2.ExampleObject.thing` property.

## Using the Context fluent API to define templates

Defining templates via the Context fluent API is identical to defining components, with the exception that there is no `create()` method for template builders. Remember, templates cannot be assembled - they serve only as a basis for further defining other templates or components.

Here is an example:

    context.template("my-template").init("arg", keyword="kw").register()
    (context.singleton("my-singleton", parent="my-template").
        create("module.ClassName").
        register())

Notice that we have added `parent="my-template"` to the singleton method call.

Now when we assemble “my-singleton” its initializer will be called with the positional argument “arg” and the keyword argument keyword=”kw”.

## Letting Aglyph introspect dotted names

If preferred, classes (or other callables) that will be defined as components or templates can be introspected by Aglyph. Let’s revisit the earlier example, slightly modified, to demonstrate this:

    from module import ClassName
    from module2 import ExampleObject

    from aglyph.assembler import Assembler
    from aglyph.component import Reference as ref
    from aglyph.context import Context

    context = Context("example-context")

    (context.singleton(ClassName).
        init("argument", keyword="keyword").
        register())

    (context.prototype("example-object").
        create(ExampleObject).
        set(thing=ref(ClassName)).
        register())

    assembler = Assembler(context)
    example = assembler.assemble("example-object")

Notice that we call `singleton(ClassName)` as the entry point. Aglyph will automatically convert ClassName into its dotted name “module.ClassName” and also use that value as the unique ID. This means that the expicit call to `create()` is now unnecessary, and so it has been removed.

Next, notice the call to `create(ExampleObject)`. We used “example-object” as the component ID, so we must still tell Aglyph the dotted name of the class. But instead of passing the dotted name as a string, we again pass the class and let Aglyph determine the value.

Finally, notice that we use `ref(ClassName)` when setting the thing property. Like the fluent API entry point methods and `create()`, `aglyph.component.Reference` is also capable of introspecting a dotted name.

## Overview of the Context fluent API methods

The Context fluent API is made up of a number of “mixin” classes that are combined in different ways to support describing templates and components.

These classes are never instantiated directly; rather, the “entry point” methods are exposed as members of `aglyph.context.Context` that return either a `aglyph.context._ComponentBuilder` or a `aglyph.context._TemplateBuilder`; and those builder classes inherit relevant methods from the mixin classes.

Note

For reference, here is the class hierarchy for the Context fluent API (all classes are defined in the `aglyph.context` namespace):

class `Context`(_ContextBuilder)

class `_TemplateBuilder`(_InjectionBuilderMixin, _LifecycleBuilderMixin, _RegistrationMixin)

class `_ComponentBuilder`(_CreationBuilderMixin, _TemplateBuilder)

### “Entry point” methods to create component and template builders

`_ContextBuilder.` `component`(component_id_spec, parent=None)

  Return a fluent `Component` builder for component_id_spec.

  Parameters: | component_id_spec – a context-unique identifier for this component; or the object whose dotted name will identify this component parent – the context-unique identifier for this component’s parent template or component definition; or the object whose dotted name identifies this component’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

`_ContextBuilder.` `prototype`(component_id_spec, parent=None)

  Return a `prototype` `Component` builder for a component identified by component_id_spec.

  Parameters: | component_id_spec – a context-unique identifier for this component; or the object whose dotted name will identify this component parent – the context-unique identifier for this component’s parent template or component definition; or the object whose dotted name identifies this component’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

`_ContextBuilder.` `singleton`(component_id_spec, parent=None)

  Return a `singleton` `Component` builder for a component identified by component_id_spec.

  Parameters: | component_id_spec – a context-unique identifier for this component; or the object whose dotted name will identify this component parent – the context-unique identifier for this component’s parent template or component definition; or the object whose dotted name identifies this component’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

`_ContextBuilder.` `borg`(component_id_spec, parent=None)

  Return a `borg` `Component` builder for a component identified by component_id_spec.

  Parameters: | component_id_spec – a context-unique identifier for this component; or the object whose dotted name will identify this component parent – the context-unique identifier for this component’s parent template or component definition; or the object whose dotted name identifies this component’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

`_ContextBuilder.` `weakref`(component_id_spec, parent=None)

  Return a `weakref` `Component` builder for a component identified by component_id_spec.

  Parameters: | component_id_spec – a context-unique identifier for this component; or the object whose dotted name will identify this component parent – the context-unique identifier for this component’s parent template or component definition; or the object whose dotted name identifies this component’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

`_ContextBuilder.` `template`(template_id_spec, parent=None)

  Return a `Template` builder for a template identified by template_spec.

  Parameters: | template_id_spec – a context-unique identifier for this template; or the object whose dotted name will identify this template parent – the context-unique identifier for this template’s parent template or component definition; or the object whose dotted name identifies this template’s parent definition

  New in version 3.0.0: This method is an entry point into The Aglyph Context fluent API.

### Describing object creation for component builders

`_CreationBuilderMixin.` `create`(dotted_name=None, factory=None, member=None, strategy=None)

  Specify the object creation aspects of a component being defined.

  Parameters: | dotted_name – an importable dotted name or an object whose dotted name will be introspected factory – names a `callable` member of the object represented by the dotted name member – names any member of the object represented by the dotted name strategy – specifies the component assembly strategy
  Returns: | self (to support chained calls)

  Any keyword whose value is `None` will be ignored (i.e. `None` values are not explicitly set).

  Note

  The member and strategy keywords should be treated as mutually exclusive. Any component definition that specifies a member is implicitly assigned the special strategy “_imported”.

### Describing dependencies and lifecycle methods for template and component builders

`_InjectionBuilderMixin.` `init`(*args, **keywords)

  Specify the initialization arguments (positional and keyword) for templates and/or components.

  Parameters: | args (tuple) – the positional initialization arguments keywords (dict) – the keyword initialization arguments
  Returns: | self (to support chained calls)

  Note

  Successive calls to this method on the same instance have a cumulative effect; the list of positional arguments is extended, and the dictionary of keyword arguments is updated.

`_InjectionBuilderMixin.` `set`(*pairs, **attributes)

  Specify the setter (method/attribute/property) depdendencies for tempaltes and/or components.

  Parameters: | pairs – a sequence of (name, value) 2-tuples (optional) attributes – a mapping of name->value dependencies
  Returns: | self (to support chained calls)

  Note

  Successive calls to this method on the same instance have a cumulative effect (i.e. the attributes mapping is updated).

`_LifecycleBuilderMixin.` `call`(after_inject=None, before_clear=None)

  Specify the names of lifecycle methods to be called for templates and/or components.

  Parameters: | after_inject – the name of the method to call after a component has been assembled but before it is returned to the caller before_clear – the name of the method to call immediately before a singleton, borg, or weakref object is evicted from the internal cache
  Returns: | self (to support chained calls)

  Any keyword whose value is `None` will be ignored (i.e. `None` values are not explicitly set).

### Registering the template or component described by a fluent builder

`_RegistrationMixin.` `register`()

  Add a component or template definition to a context.

  Returns: | `None` (terminates the fluent call sequence)