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.
- data/doc/LICENSE-BSD +27 -0
- data/doc/LICENSE-GPL +280 -0
- data/doc/LICENSE-RUBY +56 -0
- data/doc/README +70 -0
- data/doc/manual/chapter.erb +18 -0
- data/doc/manual/index.erb +29 -0
- data/doc/manual/manual.css +192 -0
- data/doc/manual/manual.rb +240 -0
- data/doc/manual/manual.yml +48 -0
- data/doc/manual/page.erb +86 -0
- data/doc/manual/parts/01_license.txt +5 -0
- data/doc/manual/parts/01_support.txt +1 -0
- data/doc/manual/parts/01_use_cases.txt +141 -0
- data/doc/manual/parts/01_what_is_needle.txt +1 -0
- data/doc/manual/parts/02_creating.txt +9 -0
- data/doc/manual/parts/02_namespaces.txt +47 -0
- data/doc/manual/parts/02_overview.txt +3 -0
- data/doc/manual/parts/02_services.txt +44 -0
- data/doc/manual/tutorial.erb +30 -0
- data/doc/manual-html/chapter-1.html +354 -0
- data/doc/manual-html/chapter-2.html +310 -0
- data/doc/manual-html/chapter-3.html +154 -0
- data/doc/manual-html/chapter-4.html +154 -0
- data/doc/manual-html/chapter-5.html +154 -0
- data/doc/manual-html/chapter-6.html +154 -0
- data/doc/manual-html/chapter-7.html +154 -0
- data/doc/manual-html/index.html +177 -0
- data/doc/manual-html/manual.css +192 -0
- data/lib/needle/container.rb +318 -0
- data/lib/needle/errors.rb +32 -0
- data/lib/needle/include-exclude.rb +116 -0
- data/lib/needle/interceptor-chain.rb +162 -0
- data/lib/needle/interceptor.rb +189 -0
- data/lib/needle/log-factory.rb +207 -0
- data/lib/needle/logger.rb +161 -0
- data/lib/needle/logging-interceptor.rb +62 -0
- data/lib/needle/models/prototype-deferred.rb +41 -0
- data/lib/needle/models/prototype.rb +39 -0
- data/lib/needle/models/proxy.rb +84 -0
- data/lib/needle/models/singleton-deferred.rb +57 -0
- data/lib/needle/models/singleton.rb +56 -0
- data/lib/needle/models.rb +44 -0
- data/lib/needle/registry.rb +110 -0
- data/lib/needle/service-point.rb +109 -0
- data/lib/needle/version.rb +28 -0
- data/lib/needle.rb +54 -0
- data/test/ALL-TESTS.rb +21 -0
- data/test/models/tc_prototype.rb +53 -0
- data/test/models/tc_prototype_deferred.rb +54 -0
- data/test/models/tc_proxy.rb +51 -0
- data/test/models/tc_singleton.rb +53 -0
- data/test/models/tc_singleton_deferred.rb +54 -0
- data/test/tc_container.rb +246 -0
- data/test/tc_interceptor.rb +92 -0
- data/test/tc_interceptor_chain.rb +181 -0
- data/test/tc_logger.rb +181 -0
- data/test/tc_models.rb +44 -0
- data/test/tc_registry.rb +34 -0
- data/test/tc_service_point.rb +100 -0
- 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
|