needle 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/doc/LICENSE-BSD +27 -0
  2. data/doc/LICENSE-GPL +280 -0
  3. data/doc/LICENSE-RUBY +56 -0
  4. data/doc/README +70 -0
  5. data/doc/manual/chapter.erb +18 -0
  6. data/doc/manual/index.erb +29 -0
  7. data/doc/manual/manual.css +192 -0
  8. data/doc/manual/manual.rb +240 -0
  9. data/doc/manual/manual.yml +48 -0
  10. data/doc/manual/page.erb +86 -0
  11. data/doc/manual/parts/01_license.txt +5 -0
  12. data/doc/manual/parts/01_support.txt +1 -0
  13. data/doc/manual/parts/01_use_cases.txt +141 -0
  14. data/doc/manual/parts/01_what_is_needle.txt +1 -0
  15. data/doc/manual/parts/02_creating.txt +9 -0
  16. data/doc/manual/parts/02_namespaces.txt +47 -0
  17. data/doc/manual/parts/02_overview.txt +3 -0
  18. data/doc/manual/parts/02_services.txt +44 -0
  19. data/doc/manual/tutorial.erb +30 -0
  20. data/doc/manual-html/chapter-1.html +354 -0
  21. data/doc/manual-html/chapter-2.html +310 -0
  22. data/doc/manual-html/chapter-3.html +154 -0
  23. data/doc/manual-html/chapter-4.html +154 -0
  24. data/doc/manual-html/chapter-5.html +154 -0
  25. data/doc/manual-html/chapter-6.html +154 -0
  26. data/doc/manual-html/chapter-7.html +154 -0
  27. data/doc/manual-html/index.html +177 -0
  28. data/doc/manual-html/manual.css +192 -0
  29. data/lib/needle/container.rb +318 -0
  30. data/lib/needle/errors.rb +32 -0
  31. data/lib/needle/include-exclude.rb +116 -0
  32. data/lib/needle/interceptor-chain.rb +162 -0
  33. data/lib/needle/interceptor.rb +189 -0
  34. data/lib/needle/log-factory.rb +207 -0
  35. data/lib/needle/logger.rb +161 -0
  36. data/lib/needle/logging-interceptor.rb +62 -0
  37. data/lib/needle/models/prototype-deferred.rb +41 -0
  38. data/lib/needle/models/prototype.rb +39 -0
  39. data/lib/needle/models/proxy.rb +84 -0
  40. data/lib/needle/models/singleton-deferred.rb +57 -0
  41. data/lib/needle/models/singleton.rb +56 -0
  42. data/lib/needle/models.rb +44 -0
  43. data/lib/needle/registry.rb +110 -0
  44. data/lib/needle/service-point.rb +109 -0
  45. data/lib/needle/version.rb +28 -0
  46. data/lib/needle.rb +54 -0
  47. data/test/ALL-TESTS.rb +21 -0
  48. data/test/models/tc_prototype.rb +53 -0
  49. data/test/models/tc_prototype_deferred.rb +54 -0
  50. data/test/models/tc_proxy.rb +51 -0
  51. data/test/models/tc_singleton.rb +53 -0
  52. data/test/models/tc_singleton_deferred.rb +54 -0
  53. data/test/tc_container.rb +246 -0
  54. data/test/tc_interceptor.rb +92 -0
  55. data/test/tc_interceptor_chain.rb +181 -0
  56. data/test/tc_logger.rb +181 -0
  57. data/test/tc_models.rb +44 -0
  58. data/test/tc_registry.rb +34 -0
  59. data/test/tc_service_point.rb +100 -0
  60. metadata +107 -0
@@ -0,0 +1,189 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'needle/errors'
18
+
19
+ module Needle
20
+
21
+ # This represents the definition of an interceptor as it is attached to a
22
+ # service point. Instances of Interceptor are also used for configuring
23
+ # themselves programmatically.
24
+ #
25
+ # You will almost never instantiate an Interceptor object directly. Instead,
26
+ # use the Container#intercept method. You can then configure the new
27
+ # interceptor by chaining methods of the new object together, quite
28
+ # readably:
29
+ #
30
+ # container.intercept( :foo ).with! { some_interceptor }.
31
+ # with_options( :arg => :value )
32
+ #
33
+ # You can also create new interceptors on the fly via the Interceptor#doing
34
+ # method.
35
+ class Interceptor
36
+
37
+ # This is the wrapper for on-the-fly interceptors that have been created
38
+ # via Interceptor#doing. The callback registered with Interceptor#doing
39
+ # gets wrapped by an instance of this class, to comply with the interface
40
+ # required by InterceptorChainBuilder.
41
+ #
42
+ # This class should rarely (if ever) be instantiated directly. Instead,
43
+ # using the Interceptor#doing method to create dynamic interceptors.
44
+ class DynamicInterceptor
45
+
46
+ # Create a new DynamicInterceptor instance that wraps the given
47
+ # callback.
48
+ def initialize( callback )
49
+ @callback = callback
50
+ end
51
+
52
+ # This method is a concession to the required interceptor factory
53
+ # interface. It should return the new interceptor, configured to be
54
+ # attached to the given service point, and with the given options.
55
+ # It will always return +self+.
56
+ def new( point, opts )
57
+ @point = point
58
+ @options = opts
59
+ self
60
+ end
61
+
62
+ # Process this link in the interceptor chain. This will invoke the
63
+ # wrapped callback, passing in the chain and context parameters.
64
+ # Before invoking the callback, the options and service point
65
+ # references that were given in #new are assigned to context
66
+ # data members (so they can be referenced inside the callback).
67
+ def process( chain, context )
68
+ context.data[:options] = @options
69
+ context.data[:point] = @point
70
+ @callback.call( chain, context )
71
+ end
72
+
73
+ end
74
+
75
+ # The set of options that were given to this interceptor via the
76
+ # #with_options method.
77
+ attr_reader :options
78
+
79
+ # Create a new Interceptor definition. By default, it has no
80
+ # implementation and a priority of 0.
81
+ def initialize
82
+ @options = { :priority => 0 }
83
+ @doing = @with = nil
84
+ end
85
+
86
+ # Returns the action that was specified for this interceptor as a proc
87
+ # instance. This will either be the block passed to #with, or a proc
88
+ # that wraps the instantiation of a DynamicInterceptor (when #doing
89
+ # was used).
90
+ #
91
+ # If neither #with nor #doing were specified, an
92
+ # InterceptorConfigurationError is raised.
93
+ def action
94
+ return @with if @with
95
+ raise InterceptorConfigurationError,
96
+ "You must specify either 'with' or 'doing'" unless @doing
97
+
98
+ return proc { |c| DynamicInterceptor.new( @doing ) }
99
+ end
100
+
101
+ # Sets the action for this interceptor to be that defined by the
102
+ # interceptor returned when the block is executed. You can only
103
+ # invoke #with once, and never after previously invoking #doing on the
104
+ # same interceptor instance.
105
+ #
106
+ # Usage:
107
+ #
108
+ # container.intercept( :foo ).
109
+ # with { |c| c.logging_interceptor }
110
+ def with( &block )
111
+ if @with
112
+ raise InterceptorConfigurationError,
113
+ "you cannot redefine 'with' behavior"
114
+ end
115
+
116
+ if @doing
117
+ raise InterceptorConfigurationError,
118
+ "cannot specify 'with' after specifying 'doing'"
119
+ end
120
+
121
+ if block.nil?
122
+ raise InterceptorConfigurationError,
123
+ "you must specify a block to 'with'"
124
+ end
125
+
126
+ @with = block
127
+ self
128
+ end
129
+
130
+ # This is identical to #with, but it wraps the block in another proc that
131
+ # calls +instance_eval+ on the container, with the block.
132
+ #
133
+ # Usage:
134
+ #
135
+ # container.intercept( :foo ).
136
+ # with! { logging_interceptor }
137
+ def with!( &block )
138
+ with { |c| c.instance_eval( &block ) }
139
+ end
140
+
141
+ # This allows new interceptors to be defined "on-the-fly". The associated
142
+ # block must accept two parameters--an object representing the chain of
143
+ # interceptors, and the context of the current method invocation. The block
144
+ # should then invoke #process_next on the chain (passing the context as
145
+ # the lone parameter) when the next element of the chain should be invoked.
146
+ #
147
+ # You should only call #doing once per interceptor, and never after
148
+ # invoking #with on the same interceptor.
149
+ def doing( &block )
150
+ if @doing
151
+ raise InterceptorConfigurationError,
152
+ "you cannot redefine 'doing' behavior"
153
+ end
154
+
155
+ if @with
156
+ raise InterceptorConfigurationError,
157
+ "cannot specify 'doing' after specifying 'with'"
158
+ end
159
+
160
+ if block.nil?
161
+ raise InterceptorConfigurationError,
162
+ "you must specify a block to 'doing'"
163
+ end
164
+
165
+ @doing = block
166
+ self
167
+ end
168
+
169
+ # Merge the given +opts+ hash into the interceptors options hash.
170
+ def with_options( opts={} )
171
+ @options.update opts
172
+ self
173
+ end
174
+
175
+ # A convenience method for querying the options on an interceptor
176
+ # definition.
177
+ def []( name )
178
+ @options[ name ]
179
+ end
180
+
181
+ # A convenience method for setting the options on an interceptor
182
+ # definition.
183
+ def []=( name, value )
184
+ @options[ name ] = value
185
+ end
186
+
187
+ end
188
+
189
+ end
@@ -0,0 +1,207 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'needle/logger'
18
+ require 'thread'
19
+
20
+ module Needle
21
+
22
+ # A factory class that returns Logger instances. Since each registry
23
+ # has its own logger factory, the logger factory must be separately
24
+ # instantiable.
25
+ class LogFactory
26
+
27
+ # The default name of the log file to write to.
28
+ DEFAULT_LOG_FILENAME = "./needle.log"
29
+
30
+ # The default format of the log messages (see Logger for more info)
31
+ DEFAULT_MESSAGE_FORMAT = "[%-5p] %d -- %C: %m"
32
+
33
+ # Translate names of levels to their actual values.
34
+ LEVEL_TRANSLATOR = {
35
+ "DEBUG" => Logger::DEBUG,
36
+ "INFO" => Logger::INFO,
37
+ "WARN" => Logger::WARN,
38
+ "ERROR" => Logger::ERROR,
39
+ "FATAL" => Logger::FATAL,
40
+ "UNKNOWN" => Logger::UNKNOWN
41
+ }
42
+
43
+ # The default date format string to use when logging.
44
+ attr_reader :default_date_format
45
+
46
+ # The default message format string to use when logging.
47
+ attr_reader :default_message_format
48
+
49
+ # The default log level to use for logs that are created.
50
+ attr_reader :default_level
51
+
52
+ # The device that logs will write to.
53
+ attr_reader :device
54
+
55
+ # Create a new LogFactory using the given initialization parameters.
56
+ # The valid options are:
57
+ #
58
+ # * <tt>:device</tt>: the device (pseudo-IO object) to write log
59
+ # messages to. Either this, or <tt>:filename</tt> must be specified.
60
+ # * <tt>:filename</tt>: the filename of the log to write to.
61
+ # * <tt>:roll_age</tt>: the number of days before the log should be
62
+ # rolled.
63
+ # * <tt>:roll_frequency</tt>: either 'daily', 'weekly', or 'monthly'.
64
+ # * <tt>:roll_size</tt>: the maximum size of a log file.
65
+ # * <tt>:default_date_format</tt>: the default date format string for
66
+ # the log.
67
+ # * <tt>:default_message_format</tt>: the default message format string
68
+ # for the log.
69
+ # * <tt>:default_level</tt>: the default log level for the log.
70
+ # * <tt>:levels</tt>: a hash of patterns that map to a hash of 'level'
71
+ # 'date_format', and 'message_format' keys, specifying the log level,
72
+ # date format, and message format for any log whose name matches the key.
73
+ def initialize( opts={} )
74
+ if opts[:device]
75
+ @device = opts[:device]
76
+ else
77
+ filename = opts[:filename] || DEFAULT_LOG_FILENAME
78
+ roll_age = opts[:roll_age ] || opts[:roll_frequency] || 0
79
+ roll_size = opts[:roll_size] || 0
80
+ @device = Logger::LogDevice.new( filename,
81
+ :shift_age=>roll_age, :shift_size=>roll_size )
82
+ end
83
+
84
+ @default_date_format = opts[:default_date_format]
85
+ @default_message_format = opts[:default_message_format] ||
86
+ DEFAULT_MESSAGE_FORMAT
87
+ @default_level = opts[:default_level]
88
+
89
+ if @default_level.is_a?( String ) || @default_level.is_a?( Symbol )
90
+ @default_level = LEVEL_TRANSLATOR[@default_level.to_s]
91
+ end
92
+
93
+ @levels = Hash.new "level" => nil, "date-format" => nil,
94
+ "message-format" => nil
95
+
96
+ ( opts[:levels] || {} ).each_pair do |key, value|
97
+ key = Regexp.new( "^" + key.gsub( /\./, "\\." ).gsub( /\*/, ".*" ) )
98
+
99
+ if value.is_a?( String ) || value.is_a?( Symbol )
100
+ value = { "level" => value.to_s }
101
+ end
102
+
103
+ @levels[ key ] = value
104
+ end
105
+
106
+ @loggers = Hash.new
107
+ @mutex = Mutex.new
108
+ @closed = false
109
+ end
110
+
111
+ # Changes the device that the loggers write to. Every log that was created
112
+ # by this log factory will be changed to use the given device.
113
+ def write_to( device, shift_age = 0, shift_size = 1048576 )
114
+ saved_critical = Thread.critical
115
+ Thread.critical = true
116
+
117
+ @device.close if @device unless [ $stdout, $stderr ].include?( @device )
118
+ if device.respond_to?( :write ) && device.respond_to?( :close )
119
+ @device = device
120
+ else
121
+ @device = Logger::LogDevice.new( device.to_str,
122
+ :shift_age => shift_age,
123
+ :shift_size => shift_size )
124
+ end
125
+
126
+ @loggers.each_value { |logger| logger.write_to( @device ) }
127
+ device
128
+
129
+ ensure
130
+ Thread.critical = saved_critical
131
+ end
132
+
133
+ # Searches for a level definition hash that matches the given log
134
+ # name.
135
+ def find_definition( name )
136
+ best_match = { :length => 0, :value => Hash.new }
137
+
138
+ @levels.each_pair do |key, value|
139
+ length = key.to_s.length
140
+ if best_match[ :length ] < length && key === name
141
+ best_match[ :length ] = length
142
+ best_match[ :value ] = value
143
+ end
144
+ end
145
+
146
+ return best_match[ :value ]
147
+ end
148
+ private :find_definition
149
+
150
+ # Retrieves the logger with the given name. If no such log has been
151
+ # created, the log will be created and initialized. Otherwise, the
152
+ # log with the given name is returned.
153
+ def get( name )
154
+ name = name.fullname if name.respond_to?( :fullname )
155
+ name = name.name if name.respond_to?( :name )
156
+ name = name.to_s
157
+
158
+ # the common case first, outside the synchronize, for speed
159
+ return @loggers[ name ] if @loggers[ name ]
160
+
161
+ @mutex.synchronize do
162
+ # check again, inside the synchronize, to avoid race conditions
163
+ return @loggers[ name ] if @loggers[ name ]
164
+
165
+ definition = find_definition( name )
166
+
167
+ level = definition[ "level" ] || @default_level
168
+ date_format = definition[ "date-format" ] || @default_date_format
169
+ message_format = definition[ "message-format" ] ||
170
+ @default_message_format
171
+
172
+ level = LEVEL_TRANSLATOR[ level ] || level
173
+
174
+ logger = Needle::Logger.new( @device )
175
+ logger.level = level if level
176
+ logger.progname = name
177
+ logger.datetime_format = date_format if date_format
178
+ logger.message_format = message_format if message_format
179
+
180
+ @loggers[ name ] = logger
181
+ return logger
182
+ end
183
+ end
184
+
185
+ # Closes the logging device and makes this factory invalid for future
186
+ # accesses.
187
+ def close
188
+ @mutex.synchronize do
189
+ return if @closed
190
+
191
+ if @device
192
+ @device.close unless [ $stdout, $stderr ].include?( @device )
193
+ end
194
+
195
+ @loggers = nil
196
+ @closed = true
197
+ end
198
+ end
199
+
200
+ # Returns true if the factory has been closed.
201
+ def closed?
202
+ @closed
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,161 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'logger'
18
+ require 'strscan'
19
+ require 'needle/errors'
20
+
21
+ module Needle
22
+
23
+ # A specialization of the standard Logger class that comes with Ruby. This
24
+ # provides the additional functionality of a fully-customizable message
25
+ # format, whereas the original only provided a customizable date format.
26
+ class Logger < ::Logger
27
+
28
+ # The brief name of this logger (derived from #progname).
29
+ attr_reader :name
30
+
31
+ # The format string for the message (+nil+ if the default should be used)
32
+ attr_reader :message_format
33
+
34
+ # The map of specifier options supported by this class.
35
+ SPECIFIER_OPTIONS = {
36
+ "c" => { :type => "s", :value => "@name" },
37
+ "C" => { :type => "s", :value => "self.progname" },
38
+ "d" => { :type => "s", :value => "opts[:timestamp]" },
39
+ "F" => { :type => "s", :value => "opts[:caller_file]" },
40
+ "l" => { :type => "s", :value => "opts[:caller_info]" },
41
+ "L" => { :type => "d", :value => "opts[:caller_line]" },
42
+ "m" => { :type => "s", :value => "opts[:msg]" },
43
+ "M" => { :type => "s", :value => "opts[:caller_method]" },
44
+ "n" => { :type => "s", :value => "$/" },
45
+ "p" => { :type => "s", :value => "opts[:severity]" },
46
+ "t" => { :type => "d", :value => "Thread.current.__id__" },
47
+ "%" => { :type => "s", :value => "'%'" },
48
+ "P" => { :type => "s", :value => "opts[:progname]" },
49
+ "$" => { :type => "d", :value => "$$" }
50
+ }
51
+
52
+ # The regular expression for matching specifier patterns in the
53
+ # format strings.
54
+ SPECIFIER_PATTERN = /(.*?)%(-?\d*(?:\.\d+)?)?([cCdFlLmMnpt%$P])/
55
+
56
+ # Extracts the unqualified name from the progname, after setting the
57
+ # progname.
58
+ def progname=( progname )
59
+ super
60
+ @name = progname.split( /\./ ).last
61
+ end
62
+
63
+ # Changes the device that the given logger writes to, to be the given
64
+ # device. Does so in a thread-safe manner.
65
+ def write_to( device, shift_age = 0, shift_size = 1048576 )
66
+ saved_critical = Thread.critical
67
+ Thread.critical = true
68
+
69
+ if device
70
+ if device.respond_to?( :write ) && device.respond_to?( :close )
71
+ @logdev = device
72
+ else
73
+ @logdev = Logger::LogDevice.new( device,
74
+ :shift_age => shift_age,
75
+ :shift_size => shift_size )
76
+ end
77
+ end
78
+
79
+ device
80
+ ensure
81
+ Thread.critical = saved_critical
82
+ end
83
+
84
+ # Set the message format string to the given string. This also pre-parses
85
+ # the format for faster processing.
86
+ #
87
+ # The format string is a printf-formatted string, which supports the following
88
+ # format specifiers:
89
+ #
90
+ # c:: the unqualified name of the logger
91
+ # C:: the fully-qualified name of the logger
92
+ # d:: the date/time string (as formatted by the #datetime_format string)
93
+ # F:: the filename of the calling routine
94
+ # l:: the location of the calling routine
95
+ # L:: the line number of the calling routine
96
+ # m:: the message to log
97
+ # M:: the name of the calling method
98
+ # n:: the newline character
99
+ # p:: the name of the priority (or severity) used to log this method
100
+ # t:: the id of the current thread
101
+ # %:: a percentage character
102
+ # P:: the progname that was passed to the logger method
103
+ # $:: the current process id
104
+ def message_format=( format )
105
+ @message_format = format
106
+ return unless format
107
+
108
+ @needs_caller_info = false
109
+
110
+ format_string = ""
111
+ format_parameters = []
112
+
113
+ @message_format_tokens = []
114
+ format.scan( SPECIFIER_PATTERN ) do |v|
115
+ format_string << v[0] if v[0].length > 0
116
+ opts = SPECIFIER_OPTIONS[ v[2] ]
117
+ format_string << "%#{v[1]}#{opts[:type]}"
118
+ format_parameters << opts[:value]
119
+ @needs_caller_info = true if v[2] =~ /[FlLM]/
120
+ end
121
+ format_string << $' if $'.length > 0
122
+ format_string << "\n"
123
+
124
+ definition =
125
+ "proc { |opts| #{format_string.inspect} " +
126
+ "% [ #{format_parameters.join(',')} ] }"
127
+ @message_formatter = eval( definition )
128
+ end
129
+
130
+ # Format the message using the given parameters. If a message format has
131
+ # not been given, just call the superclass's implementation. Otherwise,
132
+ # process the tokens from the parsed message format.
133
+ def format_message( severity, timestamp, msg, progname )
134
+ if @message_format.nil?
135
+ super
136
+ else
137
+ opts = {
138
+ :severity => severity,
139
+ :timestamp => timestamp,
140
+ :msg => msg,
141
+ :progname => progname
142
+ }
143
+
144
+ if @needs_caller_info
145
+ stack = caller
146
+ stack.shift while stack.first =~ /\blogger\.rb/
147
+ opts[:caller_info] = caller_info = stack.first
148
+ match = caller_info.match( /(.*):(\d+)(?::in `(.*)')?/ )
149
+ opts[:caller_file] = match[1]
150
+ opts[:caller_line] = match[2]
151
+ opts[:caller_method] = match[3]
152
+ end
153
+
154
+ @message_formatter.call( opts )
155
+ end
156
+ end
157
+ private :format_message
158
+
159
+ end
160
+
161
+ end
@@ -0,0 +1,62 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'needle/include-exclude'
18
+
19
+ module Needle
20
+
21
+ # A LoggingInterceptor is an interceptor object that logs method
22
+ # invocations and exceptions. It includes the IncludeExclude functionality.
23
+ class LoggingInterceptor
24
+ include IncludeExclude
25
+
26
+ # Create a new LoggingInterceptor for the given service point, with the
27
+ # given list of include and exclude patterns. The logger object will be
28
+ # created as well, named with the service point's full name.
29
+ def initialize( point, parms )
30
+ @log = point.container.logs.get( point.fullname )
31
+
32
+ @includes = build_map( parms[ :include ] )
33
+ @excludes = build_map( parms[ :exclude ] )
34
+ end
35
+
36
+ # Processes a method invocation context. If the current log has debugging
37
+ # activated, and the requested method is not excluded by the
38
+ # interceptor's exclude and include patterns, then a message will be
39
+ # written for the method's invocation, its return code, and any exception
40
+ # that is thrown.
41
+ def process( chain, context )
42
+ if @log.debug? && match( context )
43
+ args = context.args.map { |i| i.inspect } .join( ', ' )
44
+ @log.debug "#{context.sym}( #{args} )"
45
+
46
+ begin
47
+ result = chain.process_next( context )
48
+ rescue Exception => e
49
+ @log.debug "#{context.sym} raised #{e.message.inspect} (#{e.class})"
50
+ raise
51
+ end
52
+
53
+ @log.debug "#{context.sym} => #{result.inspect}"
54
+ return result
55
+ else
56
+ return chain.process_next( context )
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,41 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'needle/models/proxy'
18
+
19
+ module Needle
20
+ module Models
21
+
22
+ # The definition of the "prototype-deferred" lifecycle service model.
23
+ # This will result in deferred instantiation of the requested service,
24
+ # with a new instance being returned every time #instance is invoked.
25
+ class PrototypeDeferred
26
+
27
+ # Create a new PrototypeDeferred service model.
28
+ def initialize( container, opts={}, &callback )
29
+ @container = container
30
+ @callback = callback
31
+ end
32
+
33
+ # Return a new Proxy instance that wraps the container and callback
34
+ # references given when the service model was instantiated.
35
+ def instance
36
+ Proxy.new( @container, &@callback )
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ module Needle
18
+ module Models
19
+
20
+ # The definition of the "prototype" lifecycle service model. This will
21
+ # result in immediate instantiation of the requested service, with a new
22
+ # instance being returned every time #instance is invoked.
23
+ class Prototype
24
+
25
+ # Create a new instance of the Prototype service model.
26
+ def initialize( container, opts={}, &callback )
27
+ @container = container
28
+ @callback = callback
29
+ end
30
+
31
+ # Invoke the callback that was given when the service model was
32
+ # instantiated, passing the service model's container reference.
33
+ def instance
34
+ @callback.call( @container )
35
+ end
36
+ end
37
+
38
+ end
39
+ end