Needle— to the point --> | Needle Version: 1.3.0 Manual Last Updated: 2005-12-24 15:20 UTC

Previous (1. Introduction) | Up | Next (3. Service Locator)

# 2. Registry

## 2.1. Overview

The registry is at the heart of any dependency-injected application or library. All services are registered with the registry, so that when an application needs an instance of a particular service, it may obtain that service reference by querying the registry.

In order to use Needle, you only really need to understand how to create and manipulate registry objects.

## 2.2. Creating

Creating a registry is as simple as calling `Needle::Registry.new`. This will give you a new registry object, bootstrapped to contain a few general services.

Creating a registry [ruby]

1
2
3

    require 'needle'

    registry = Needle::Registry.new

Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.

Alternatively, you can pass a block to `#new`:

Creating a registry with a block [ruby]

1
2
3

    registry = Needle::Registry.new do |r|
      ...
    end

The parameter to the block will be a reference to the registry. This allows you to register services with the registry as soon as it is created.

Another convenience method is `#define!`:

Creating a registry with #define! [ruby]

1
2
3

    registry = Needle::Registry.define! do
      ...
    end

This block accepts no parameters, and evaluates the block as if it were passed to `Registry#define!` (see below).

There can be problems with using `define!`, however, since it uses `instance_eval` to evaluate the block within the context of another object. If you find yourself running into scoping issues, you might want to consider using `#define`:

Creating a registry with #define [ruby]

1
2
3

    registry = Needle::Registry.define do |b|
      ...
    end

This block accepts a single parameter—a “builder” object to aid in registering services—and evaluates the block as if it were passed to `Registry#define` (see below).

## 2.3. Services

Registering services with a Needle registry is very straightforward. The simplest way to do it is:

Registering services [ruby]

    registry.register( :foo ) { Bar.new }

The above will register a new service with the registry, naming it `:foo`. When `:foo` is requested from the registry, a new instance of `Bar` will be instantiated and returned.

You get services from the registry in either of two ways:

Accessing services [ruby]

1
2
3
4
5

    # Treating the registry as a Hash
    svc = registry[:foo]

    # Treating the service as a property of the registry
    svc = registry.foo

### Convenience Methods

Because you will often need to register many services with a registry at once, two convenience methods have been provided to make this use case lean and mean.

The first is `define`. Just pass a block to define that accepts one parameter. This parameter will be a “builder” object that allows you to define services just by sending them as messages to the builder:

Defining services [ruby]

1
2
3
4
5

    registry.define do |b|
      b.foo { Bar.new }
      b.bar { Foo.new }
      ...
    end

Alternative, you can call `define!`, passing a block that accepts no parameters. This block will be evaluated in the “builder” object’s context, with any unrecognized method call being interpreted as a new service registration of that name:

Defining services via #define! [ruby]

1
2
3
4
5

    registry.define! do
      foo { Bar.new }
      bar { Foo.new }
      ...
    end

Both of the above will register two new services with the registry, `:foo` and `:bar`.

### Default Lifestyle

By default, a service is only instantiated once per registry. This means that (using the above example) if the service `:foo` were queried twice, the registry would return the same object for both queries:

Demonstrating singleton service behavior [ruby]

1
2
3
4

    svc1 = registry.foo
    svc2 = registry.foo

    p svc1.object_id == svc2.object_id #=> true

You can change this behavior, with service models. See the chapter on Service Models for more information.

### Parameterized Services

Needle also supports parameterized services. These are services that, when requested, require contextual information to be passed as a parameter so that the service can be correctly initialized.

Consider the following example, in which some hypothetical `Printer` class represents any of a number of printers, based on a parameter given to its constructor.

Parameterized services [ruby]

1
2
3
4
5
6

    registry.register( :printer, :model => :multiton ) do |c,p,name|
      Printer.new( c.log_for( p ), name )
    end

    mono  = registry.printer( :monochrome )
    color = registry.printer( :color )

There are a few things to note about the above example:

1. The `:multiton` model is explicitly requested. This is necessary because the default service model (`:singleton`) does not allow parameterized services. Most of the time, you’ll use the multiton service model with parameterized services, but you don’t have to. You could also use any of the prototype models as well.

1. The constructor block for the `:printer` service takes three parameters, `c`, `p`, and `name`. The first two parameters, `c` and `p`, represent the container and the service point, respectively. Any parameters after those two are the contextual parameters given when the service is requested. In this case, there is only one contextual parameter: the name of the printer.

1. Notice the first parameter to the Printer constructor: `c.log_for(p)`. This is itself invoking a parameterized service, named `:log_for`, and passing `p` as the contextual information. This will return a new logger handle for the service point `p` (i.e., the current service point).

1. See how the printer service is requested on the last two lines. In this case, the `#printer` message is sent to the registry with a single parameter. You can also request the service in two other ways:

Accessing parameterized services [ruby]

1
2

    dot_matrix = registry[ :printer, :dot_matrix ]
    ink_jet    = registry.get( :printer, :ink_jet )

Choose the style that works best for you.

## 2.4. Namespaces

Namespaces allow you to organize your services. The feature has many different applications, including:

1. Third-parties may distribute Needle-enabled libraries without worrying about their choice of service names conflicting with the service names of their clients.
2. Developers may organize complex applications into modules, and the service definitions may be stored in the registry to reflect that organization.
3. Services deeper in the hierarchy may override services higher up.

Creating a namespace is as easy as invoking the `#namespace` method of the registry (or of another namespace):

Creating a namespace [ruby]

    registry.namespace :stuff

This would create a new namespace in the registry called `:stuff`. The application may then proceed to register services inside that namespace:

Registering services in a namespace [ruby]

1
2
3

    registry.stuff.register( :foo ) { Bar.new }
    ...
    svc = registry.stuff.foo

Here’s a tip: namespaces are just a special kind of service. This means that you can access namespaces in the same ways that you can access services:

Accessing a namespace [ruby]

    svc = registry[:stuff][:foo]

### Convenience Methods

Because it is often the case that you will be creating a namespace and then immediately registering services on it, you can pass a block to `namespace`. The block will receive a reference to the new namespace:

More registering services in a namespace [ruby]

1
2
3
4

    registry.namespace :stuff do |spc|
      spc.register( :foo ) { Bar.new }
      ...
    end

If you prefer the `define` approach to registering services, you may like `namespace_define`, which creates the new namespace and immediately calls `define` on it:

Creating namespaces with #namespace_define [ruby]

1
2
3
4

    registry.namespace_define :stuff do |b|
      b.foo { Bar.new }
      ...
    end

And, to mirror the `namespace_define` method, there is also a `namespace_define!` method. This method creates a new namespace and then does a `define!` call on that namespace.

Creating namespaces with #namespace_define! [ruby]

1
2
3
4

    registry.namespace_define! :stuff do
      foo { Bar.new }
      ...
    end

The above code would create a new namespace called `:stuff` in the registry, and would then proceed to register a service called `:foo` in the new namespace.

Previous (1. Introduction) | Up | Next (3. Service Locator)