needle 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/doc/manual-html/chapter-1.html +138 -78
  2. data/doc/manual-html/chapter-2.html +180 -99
  3. data/doc/manual-html/chapter-3.html +111 -75
  4. data/doc/manual-html/chapter-4.html +80 -48
  5. data/doc/manual-html/chapter-5.html +106 -56
  6. data/doc/manual-html/chapter-6.html +82 -34
  7. data/doc/manual-html/chapter-7.html +74 -38
  8. data/doc/manual-html/chapter-8.html +70 -41
  9. data/doc/manual-html/chapter-9.html +88 -63
  10. data/doc/manual-html/index.html +6 -6
  11. data/doc/manual-html/needle.png +0 -0
  12. data/doc/manual-html/{manual.css → stylesheets/manual.css} +83 -10
  13. data/doc/manual-html/stylesheets/ruby.css +17 -0
  14. data/doc/manual/chapter.erb +20 -0
  15. data/doc/manual/img/Needle.ai +0 -0
  16. data/doc/manual/img/needle.png +0 -0
  17. data/doc/manual/manual.rb +80 -5
  18. data/doc/manual/manual.yml +3 -3
  19. data/doc/manual/page.erb +1 -1
  20. data/doc/manual/parts/01_use_cases.txt +70 -70
  21. data/doc/manual/parts/02_creating.txt +19 -19
  22. data/doc/manual/parts/02_namespaces.txt +29 -29
  23. data/doc/manual/parts/02_services.txt +40 -41
  24. data/doc/manual/parts/03_conventional.txt +20 -20
  25. data/doc/manual/parts/03_locator.txt +44 -44
  26. data/doc/manual/parts/04_overview.txt +1 -1
  27. data/doc/manual/parts/04_setup.txt +32 -32
  28. data/doc/manual/parts/customizing_contexts.txt +14 -14
  29. data/doc/manual/parts/customizing_interceptors.txt +25 -25
  30. data/doc/manual/parts/customizing_namespaces.txt +12 -12
  31. data/doc/manual/parts/interceptors_attaching.txt +29 -30
  32. data/doc/manual/parts/interceptors_custom.txt +16 -16
  33. data/doc/manual/parts/interceptors_ordering.txt +5 -5
  34. data/doc/manual/parts/libraries_creating.txt +18 -18
  35. data/doc/manual/parts/libraries_using.txt +19 -19
  36. data/doc/manual/parts/logging_configuration.txt +13 -13
  37. data/doc/manual/parts/logging_logfactory.txt +21 -22
  38. data/doc/manual/parts/models_models.txt +8 -8
  39. data/doc/manual/parts/models_overview.txt +1 -1
  40. data/doc/manual/parts/models_pipelines.txt +22 -22
  41. data/doc/manual/{manual.css → stylesheets/manual.css} +83 -10
  42. data/doc/manual/stylesheets/ruby.css +17 -0
  43. data/lib/needle/definition-context.rb +3 -2
  44. data/lib/needle/lifecycle/proxy.rb +1 -1
  45. data/lib/needle/version.rb +1 -1
  46. metadata +94 -85
@@ -1,29 +1,29 @@
1
1
  A conventional architecture will have each component instantiate its own dependencies. For example, the @Application@ would do something like this:
2
2
 
3
- <pre>
4
- class Application
5
- def initialize
6
- @logger = Logger.new
7
- @authenticator = Authenticator.new
8
- @database = Database.new
9
- @view = View.new
10
- @session = Session.new
11
- end
3
+ {{{lang=ruby,number=true,caption=A conventional application implementation
4
+ class Application
5
+ def initialize
6
+ @logger = Logger.new
7
+ @authenticator = Authenticator.new
8
+ @database = Database.new
9
+ @view = View.new
10
+ @session = Session.new
12
11
  end
13
- </pre>
12
+ end
13
+ }}}
14
14
 
15
15
  However, the above is already flawed, because the @Authenticator@ and the @Session@ both need access to the @Database@, so you really need to make sure you instantiate things in the right order and pass them as parameters to the constructor of each object that needs them, like so:
16
16
 
17
- <pre>
18
- class Application
19
- def initialize
20
- @view = View.new
21
- @logger = Logger.new
22
- @database = Database.new( @logger )
23
- @authenticator = Authenticator.new( @logger, @database )
24
- @session = Session.new( @logger, @database )
25
- end
17
+ {{{lang=ruby,number=true,caption=A parameterized application implementation
18
+ class Application
19
+ def initialize
20
+ @view = View.new
21
+ @logger = Logger.new
22
+ @database = Database.new( @logger )
23
+ @authenticator = Authenticator.new( @logger, @database )
24
+ @session = Session.new( @logger, @database )
26
25
  end
27
- </pre>
26
+ end
27
+ }}}
28
28
 
29
29
  The problem with this is that if you later decide that @View@ needs to access the database, you need to rearrange the order of how things are instantiated in the @Application@ constructor.
@@ -1,40 +1,40 @@
1
1
  The _service locator_ pattern makes things a _little_ easier. Instead of instantiating everything in the constructor of the @Application@, you can create a factory method somewhere that returns the new @Application@ instance. Then, inside of this factory method, you assign each new object to collection, and pass that collection to each constructor.
2
2
 
3
- <pre>
4
- require 'needle'
5
-
6
- def create_application
7
- locator = Needle::Registry.new
8
-
9
- locator.register( :view ) { View.new(locator) }
10
- locator.register( :logger ) { Logger.new(locator) }
11
- locator.register( :database ) { Database.new(locator) }
12
- locator.register( :authenticator ) {Authenticator.new(locator) }
13
- locator.register( :session ) { Session.new(locator) }
14
- locator.register( :app ) { Application.new(locator) }
15
-
16
- locator[:app]
3
+ {{{lang=ruby,number=true,caption=Service locator example
4
+ require 'needle'
5
+
6
+ def create_application
7
+ locator = Needle::Registry.new
8
+
9
+ locator.register( :view ) { View.new(locator) }
10
+ locator.register( :logger ) { Logger.new(locator) }
11
+ locator.register( :database ) { Database.new(locator) }
12
+ locator.register( :authenticator ) {Authenticator.new(locator) }
13
+ locator.register( :session ) { Session.new(locator) }
14
+ locator.register( :app ) { Application.new(locator) }
15
+
16
+ locator[:app]
17
+ end
18
+
19
+ class Application
20
+ def initialize( locator )
21
+ @view = locator[:view]
22
+ @logger = locator[:logger]
23
+ @database = locator[:database]
24
+ @authenticator = locator[:authenticator]
25
+ @session = locator[:session]
17
26
  end
27
+ end
18
28
 
19
- class Application
20
- def initialize( locator )
21
- @view = locator[:view]
22
- @logger = locator[:logger]
23
- @database = locator[:database]
24
- @authenticator = locator[:authenticator]
25
- @session = locator[:session]
26
- end
29
+ class Session
30
+ def initialize( locator )
31
+ @database = locator[:database]
32
+ @logger = locator[:logger]
27
33
  end
34
+ end
28
35
 
29
- class Session
30
- def initialize( locator )
31
- @database = locator[:database]
32
- @logger = locator[:logger]
33
- end
34
- end
35
-
36
- ...
37
- </pre>
36
+ ...
37
+ }}}
38
38
 
39
39
  This has the benefit of allowing each object to construct itself _� la carte_ from the objects in the locator. Also, each object no longer cares what class implements each service--it only cares that each object implements the methods it will attempt to invoke on that object.
40
40
 
@@ -44,17 +44,17 @@ Thus, when we get the @:app@ service (on the last line), the @Application@ const
44
44
 
45
45
  In the interest of brevity, the @create_application@ could have been written like this, using a "builder" object (called @b@ in the example below) to help register the services:
46
46
 
47
- <pre>
48
- def create_application
49
- locator = Needle::Registry.define do |b|
50
- b.view { View.new(locator) }
51
- b.logger { Logger.new(locator) }
52
- b.database { Database.new(locator) }
53
- b.authenticator {Authenticator.new(locator) }
54
- b.session { Session.new(locator) }
55
- b.app { Application.new(locator) }
56
- end
57
-
58
- locator[:app]
47
+ {{{lang=ruby,number=true,caption=Service locator example using #define
48
+ def create_application
49
+ locator = Needle::Registry.define do |b|
50
+ b.view { View.new(locator) }
51
+ b.logger { Logger.new(locator) }
52
+ b.database { Database.new(locator) }
53
+ b.authenticator {Authenticator.new(locator) }
54
+ b.session { Session.new(locator) }
55
+ b.app { Application.new(locator) }
59
56
  end
60
- </pre>
57
+
58
+ locator[:app]
59
+ end
60
+ }}}
@@ -6,4 +6,4 @@ The service locator works well when there are few dependencies, and the dependen
6
6
 
7
7
  # For deep dependency graphs, it can become cumbersome to have to pass the locator to each constructor.
8
8
 
9
- This is where _dependency injection_ comes in. It allows you to define how each service is initialized, including setting dependencies (either via constructor parameters or via property accessors). In fact, it can do a lot more than that, even allowing you to specify how the lifecycle of the service should be managed and hooking "interceptors" onto the service to filter method invocations.
9
+ This is where _dependency injection_ comes in. It allows you to define how each service is initialized, including setting dependencies (either via constructor parameters or via property accessors). In fact, it can do a lot more than that, even allowing you to specify how the lifestyle of the service should be managed and hooking "interceptors" onto the service to filter method invocations.
@@ -1,43 +1,43 @@
1
1
  Setting up for DI is very similar to the setup for a service locator, but instead of passing the locator (we'll call it a _registry_ now), we only pass (or set) the dependencies that the service itself needs.
2
2
 
3
- <pre>
4
- require 'needle'
5
-
6
- def create_application
7
- registry = Needle::Registry.define do |b|
8
- b.view { View.new }
9
- b.logger { Logger.new }
10
- b.database { Database.new( b.logger ) }
11
- b.authenticator { Authenticator.new(b.logger, b.database) }
12
- b.session { Session.new(b.logger, b.database) }
13
-
14
- b.app do
15
- app = Application.new
16
- app.logger = b.logger
17
- app.view = b.view
18
- app.database = b.database
19
- app.authenticator = b.authenticator
20
- app.session = b.session
21
- app
22
- end
3
+ {{{lang=ruby,number=true,caption=Dependency injection example
4
+ require 'needle'
5
+
6
+ def create_application
7
+ registry = Needle::Registry.define do |b|
8
+ b.view { View.new }
9
+ b.logger { Logger.new }
10
+ b.database { Database.new( b.logger ) }
11
+ b.authenticator { Authenticator.new(b.logger, b.database) }
12
+ b.session { Session.new(b.logger, b.database) }
13
+
14
+ b.app do
15
+ app = Application.new
16
+ app.logger = b.logger
17
+ app.view = b.view
18
+ app.database = b.database
19
+ app.authenticator = b.authenticator
20
+ app.session = b.session
21
+ app
23
22
  end
24
-
25
- registry[:app]
26
23
  end
27
24
 
28
- class Application
29
- attr_writer :view, :logger, :database, :authenticator, :session
30
- end
25
+ registry[:app]
26
+ end
31
27
 
32
- class Session
33
- def initialize( logger, database )
34
- @database = database
35
- @logger = logger
36
- end
28
+ class Application
29
+ attr_writer :view, :logger, :database, :authenticator, :session
30
+ end
31
+
32
+ class Session
33
+ def initialize( logger, database )
34
+ @database = database
35
+ @logger = logger
37
36
  end
37
+ end
38
38
 
39
- ...
40
- </pre>
39
+ ...
40
+ }}}
41
41
 
42
42
  The @create_application@ method is now (necessarily) a little more complex, since it now contains all of the initialization logic for each service in the application. However, look how much simpler this made the other classes, especially the @Application@ class.
43
43
 
@@ -4,21 +4,21 @@ The default implementation used for definition contexts is defined by the @:defi
4
4
 
5
5
  Consider the following contrived example, where you want to provide a convenient way to register services of type Hash.
6
6
 
7
- <pre>
8
- class MyDefinitionContext < Needle::DefinitionContext
9
- def register_hash( name, opts={} )
10
- this_container.register( name, opts ) { Hash.new }
11
- end
7
+ {{{lang=ruby,number=true,caption=Custom DefinitionContext example
8
+ class MyDefinitionContext < Needle::DefinitionContext
9
+ def register_hash( name, opts={} )
10
+ this_container.register( name, opts ) { Hash.new }
12
11
  end
12
+ end
13
13
 
14
- reg = Needle::Registry.new
15
- reg.register( :definition_context_factory ) { MyDefinitionContext }
14
+ reg = Needle::Registry.new
15
+ reg.register( :definition_context_factory ) { MyDefinitionContext }
16
16
 
17
- reg.define do |b|
18
- b.register_hash( :test1 )
19
- b.register_hash( :test2 )
20
- end
17
+ reg.define do |b|
18
+ b.register_hash( :test1 )
19
+ b.register_hash( :test2 )
20
+ end
21
21
 
22
- reg.test1[:key] = "value"
23
- reg.test2[:foo] = "bar"
24
- </pre>
22
+ reg.test1[:key] = "value"
23
+ reg.test2[:foo] = "bar"
24
+ }}}
@@ -2,37 +2,37 @@ When you attach an interceptor to a service, that new interceptor is wrapped in
2
2
 
3
3
  It is this wrapper object that allows interceptor definitions to be done using method chaining:
4
4
 
5
- <pre>
6
- reg.intercept( :foo ).with { ... }.with_options(...)
7
- </pre>
5
+ {{{lang=ruby,caption=Configuring an interceptor
6
+ reg.intercept( :foo ).with { ... }.with_options(...)
7
+ }}}
8
8
 
9
9
  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.
10
10
 
11
- <pre>
12
- class OnlyIfInterceptor < Needle::Interceptor
13
- def only_if( &block )
14
- @only_if = block
15
- self
16
- end
11
+ {{{lang=ruby,number=true,caption=Advanced configuration of an interceptor
12
+ class OnlyIfInterceptor < Needle::Interceptor
13
+ def only_if( &block )
14
+ @only_if = block
15
+ self
16
+ end
17
17
 
18
- def action
19
- action_proc = super
20
- lambda do |chain,ctx|
21
- if @only_if.call( chain, ctx )
22
- action_proc.call( chain, ctx )
23
- else
24
- chain.process_next( ctx )
25
- end
18
+ def action
19
+ action_proc = super
20
+ lambda do |chain,ctx|
21
+ if @only_if.call( chain, ctx )
22
+ action_proc.call( chain, ctx )
23
+ else
24
+ chain.process_next( ctx )
26
25
  end
27
26
  end
28
27
  end
28
+ end
29
29
 
30
- reg = Needle::Registry.new
31
- reg.register( :interceptor_impl_factory ) { OnlyIfInterceptor }
32
- reg.register( :foo ) { Bar.new }
30
+ reg = Needle::Registry.new
31
+ reg.register( :interceptor_impl_factory ) { OnlyIfInterceptor }
32
+ reg.register( :foo ) { Bar.new }
33
33
 
34
- reg.intercept( :foo ).
35
- with { |c| c.logging_interceptor }.
36
- only_if { |ch,ctx| something_is_true( ch, ctx ) }.
37
- with_options(...)
38
- </pre>
34
+ reg.intercept( :foo ).
35
+ with { |c| c.logging_interceptor }.
36
+ only_if { |ch,ctx| something_is_true( ch, ctx ) }.
37
+ with_options(...)
38
+ }}}
@@ -4,21 +4,21 @@ You can specify your own custom implementation for namespaces by registering you
4
4
 
5
5
  Here's a contrived example. Suppose you want each namespace to keep track of the precise time that it was created.
6
6
 
7
- <pre>
8
- class TimeTrackerNamespace < Needle::Container
9
- attr_reader :birth_date
7
+ {{{lang=ruby,number=true,caption=Custom namespace implementations
8
+ class TimeTrackerNamespace < Needle::Container
9
+ attr_reader :birth_date
10
10
 
11
- def initialize( *args )
12
- super
13
- @birth_date = Time.now
14
- end
11
+ def initialize( *args )
12
+ super
13
+ @birth_date = Time.now
15
14
  end
15
+ end
16
16
 
17
- reg = Needle::Registry.new
18
- reg.register( :namespace_impl_factory ) { TimeTrackerNamespace }
17
+ reg = Needle::Registry.new
18
+ reg.register( :namespace_impl_factory ) { TimeTrackerNamespace }
19
19
 
20
- reg.namespace :hello
21
- p reg.hello.birth_date
22
- </pre>
20
+ reg.namespace :hello
21
+ p reg.hello.birth_date
22
+ }}}
23
23
 
24
24
  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.
@@ -8,37 +8,36 @@ An example is the LoggingInterceptor that ships with Needle. Because it is funct
8
8
 
9
9
  You can attach interceptor factories to your service using the @#interceptor(...).with {...}@ syntax:
10
10
 
11
- <pre>
12
- reg.register( :foo ) {...}
13
- reg.intercept( :foo ).with { MyInterceptorFactory }
14
- </pre>
11
+ {{{lang=ruby,number=true,caption=Attaching an interceptor to a service
12
+ reg.register( :foo ) {...}
13
+ reg.intercept( :foo ).with { MyInterceptorFactory }
14
+ }}}
15
15
 
16
16
  Note that you could also make the interceptor factory a service:
17
17
 
18
- <pre>
19
- reg.register( :foo ) {...}
20
- reg.register( :my_interceptor ) { MyInterceptorFactory }
21
- reg.intercept( :foo ).with { |c| c.my_interceptor }
22
- </pre>
18
+ {{{lang=ruby,number=true,caption=Attaching an service as an interceptor
19
+ reg.register( :foo ) {...}
20
+ reg.register( :my_interceptor ) { MyInterceptorFactory }
21
+ reg.intercept( :foo ).with { |c| c.my_interceptor }
22
+ }}}
23
23
 
24
24
  And, to make accessing interceptor services even more convenient, you can use the @#with!@ method (which executes its block within the context of the calling container):
25
25
 
26
- <pre>
27
- reg.register( :foo ) {...}
28
- reg.register( :my_interceptor ) { MyInterceptorFactory }
29
- reg.intercept( :foo ).with! { my_interceptor }
30
- </pre>
31
-
26
+ {{{lang=ruby,number=true,caption=Attaching an service as an interceptor via #with!
27
+ reg.register( :foo ) {...}
28
+ reg.register( :my_interceptor ) { MyInterceptorFactory }
29
+ reg.intercept( :foo ).with! { my_interceptor }
30
+ }}}
32
31
 
33
32
  h3. Blocks
34
33
 
35
34
  Sometimes creating an entire class to implement an interceptor is overkill. This is particularly the case during debugging or testing, when you might want to attach an interceptor to class to verify that a parameter passed is correct, or a return value is what you expect. To satisfy these conditions, you can using the
36
35
  @#doing@ method. Just give it a block that accepts two parameters (the chain, and context) and you're good to go!
37
36
 
38
- <pre>
39
- reg.register( :foo ) {...}
40
- reg.intercept( :foo ).doing { |chain,ctx| ...; chain.process_next( ctx ) }
41
- </pre>
37
+ {{{lang=ruby,number=true,caption=Defining interceptors on the fly
38
+ reg.register( :foo ) {...}
39
+ reg.intercept( :foo ).doing { |chain,ctx| ...; chain.process_next( ctx ) }
40
+ }}}
42
41
 
43
42
  Note that this approach is about 40% slower than using an interceptor factory, so it should not be used if performance is an issue.
44
43
 
@@ -46,19 +45,19 @@ h3. Options
46
45
 
47
46
  Some interceptors can accept configuration options. For example, the LoggingInterceptor allows clients to specify methods that should and shouldn't be intercepted. Options are specified via the @#with_options@ method.
48
47
 
49
- <pre>
50
- reg.register( :foo ) {...}
51
- reg.intercept( :foo ).
52
- with { |c| c.logging_interceptor }.
53
- with_options( :exclude => [ "method1", "method2" ] )
54
- </pre>
48
+ {{{lang=ruby,number=true,caption=Configuring interceptors
49
+ reg.register( :foo ) {...}
50
+ reg.intercept( :foo ).
51
+ with { |c| c.logging_interceptor }.
52
+ with_options( :exclude => [ "method1", "method2" ] )
53
+ }}}
55
54
 
56
55
  Options can apply to the blocks given to the @#doing@ method, too. The block may access the options via the @#data[:options]@ member of the context:
57
56
 
58
- <pre>
59
- reg.intercept( :foo ).
60
- doing { |ch,ctx| ...; p ctx.data[:options][:value]; ... }.
61
- with_options( :value => "hello" )
62
- </pre>
57
+ {{{lang=ruby,number=true,caption=Configuring #doing interceptors
58
+ reg.intercept( :foo ).
59
+ doing { |ch,ctx| ...; p ctx.data[:options][:value]; ... }.
60
+ with_options( :value => "hello" )
61
+ }}}
63
62
 
64
63
  With blocks, of course, the value of such an approach is limited.