needle 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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