8. Service Libraries
8.1. Overview
Using Needle as it has been presented so far works fine when you are dealing with a single application, all self-encapsulated. When you start dealing with combining multiple libraries, each potentially written by a different author, into a single registry, things get a little more complicated.
Needle provides a way for service authors to share their services. All it requires is that authors centralize their service configuration.
8.2. Creating Libraries
This centralization is implemented by creating a module for each library you want to have services for. That module should then define a module function called (by default) register_services
. This function should accept a single parameter—the Needle container that the services will be added to.
For example, if I had a library of cryptographic routines and I wanted to make them accessible as Needle services, I would create a module to contain the service definitions. Typically, this module will be defined in a file called “services.rb”, although you can certainly name it whatever you like.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | module Crypto def register_services( container ) container.namespace_define( :crypto ) do |b| b.prng do require 'crypto/prng' PRNG.new end b.des do require 'crypto/des' DES.new end ... end end module_function :register_services end |
Notice that there are no explicit dependencies on Needle, only on the interfaces Needle publishes. Thus, third-parties can add service configuration modules to their libraries without introducing dependencies on Needle.
Once a service library has been created, it can then be accessed from another library or application that wishes to import those services.
8.3. Using Libraries
Using the libraries is as simple as requiring the file that has the service definitions, and then invoking the #register_services
module function:
1 2 3 4 5 6 7 8 9 10 11 | require 'needle' reg = Needle::Registry.new reg.define do |b| b.foo { Foo.new } require 'crypto/services' Crypto.register_services( reg ) end prng = reg.crypto.prng |
To make this easier, the Container class has a convenience method named #require
:
1 2 3 4 5 6 7 8 9 | require 'needle' reg = Needle::Registry.new reg.define do |b| b.foo { Foo.new } b.require 'crypto/services', "Crypto" end prng = reg.crypto.prng |
The Container#require
method will require the file, and then look for a #register_services
method of the named module. It will execute that method, passing the container as an argument.