Needle Manual :: Chapter 9: Customizing Needle

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

| Previous (8. Service Libraries) | Up # 9. Customizing Needle ## 9.1. Namespaces By default, when you create a namespace in Needle, the namespace is registered as a service. The type of the service is determined by the `:namespace_impl_factory` service, which (by default) returns the `Needle::Container` class. You can specify your own custom implementation for namespaces by registering your own `:namespace_impl_factory` service. In fact, each namespace can have its own implementation of subnamespaces—just register a `:namespace_impl_factory` in each one that you want to be specialized. Here’s a contrived example. Suppose you want each namespace to keep track of the precise time that it was created. Custom namespace implementations [ruby]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
    class TimeTrackerNamespace < Needle::Container
      attr_reader :birth_date

      def initialize( *args )
        super
        @birth_date = Time.now
      end
    end

    reg = Needle::Registry.new
    reg.register( :namespace_impl_factory ) { TimeTrackerNamespace }

    reg.namespace :hello
    p reg.hello.birth_date
In general, you’ll be better off having your custom implementation extend `Needle::Container`, although the only real requirement is that your implementation publish the same interface as the default namespace implementation. ## 9.2. Interceptors When you attach an interceptor to a service, that new interceptor is wrapped in a definition object that includes various metadata about the interceptor, including its implementation, its priority, its name, and so forth. The implementation of this interceptor definition is determined by the value of the `:interceptor_impl_factory` service, which by default returns `Needle::Interceptor`. It is this wrapper object that allows interceptor definitions to be done using method chaining: Configuring an interceptor [ruby]
    reg.intercept( :foo ).with { ... }.with_options(...)
If you wish to add custom, domain-specific functionality to the interceptor wrapper, you can register your own implementation of the `:interceptor_impl_factory`. Consider the following contrived example, where an “only_if” clause is given to determine when the interceptor should be invoked. Advanced configuration of an interceptor [ruby]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
    class OnlyIfInterceptor < Needle::Interceptor
      def only_if( &block )
        @only_if = block
        self
      end

      def action
        action_proc = super
        lambda do |chain,ctx|
          if @only_if.call( chain, ctx )
            action_proc.call( chain, ctx )
          else
            chain.process_next( ctx )
          end
        end
      end
    end

    reg = Needle::Registry.new
    reg.register( :interceptor_impl_factory ) { OnlyIfInterceptor }
    reg.register( :foo ) { Bar.new }

    reg.intercept( :foo ).
      with { |c| c.logging_interceptor }.
      only_if { |ch,ctx| something_is_true( ch, ctx ) }.
      with_options(...)
## 9.3. Contexts A definition context is used when registering services using any of the `#define` interfaces. For example, `Container#define` yields an instance of a definition context to the given block, and `Container#define!` uses the block in an `instance_eval` on a definition context. The default implementation used for definition contexts is defined by the `:definition_context_factory` service. By default, this service returns `Needle::DefinitionContext`, but you can specify your own definition context implementations by overriding this service. In fact, each namespace could have its own definition context implementation, if needed. Consider the following contrived example, where you want to provide a convenient way to register services of type Hash. Custom DefinitionContext example [ruby]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
    class MyDefinitionContext < Needle::DefinitionContext
      def register_hash( name, opts={} )
        this_container.register( name, opts ) { Hash.new }
      end
    end

    reg = Needle::Registry.new
    reg.register( :definition_context_factory ) { MyDefinitionContext }

    reg.define do |b|
      b.register_hash( :test1 )
      b.register_hash( :test2 )
    end

    reg.test1[:key] = "value"
    reg.test2[:foo] = "bar"
Previous (8. Service Libraries) | Up