pluginfactory 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,131 @@
1
+
2
+
3
+
4
+
5
+ The "Artistic License"
6
+
7
+ Preamble
8
+
9
+ The intent of this document is to state the conditions under which a
10
+ Package may be copied, such that the Copyright Holder maintains some
11
+ semblance of artistic control over the development of the package,
12
+ while giving the users of the package the right to use and distribute
13
+ the Package in a more-or-less customary fashion, plus the right to make
14
+ reasonable modifications.
15
+
16
+ Definitions:
17
+
18
+ "Package" refers to the collection of files distributed by the
19
+ Copyright Holder, and derivatives of that collection of files
20
+ created through textual modification.
21
+
22
+ "Standard Version" refers to such a Package if it has not been
23
+ modified, or has been modified in accordance with the wishes
24
+ of the Copyright Holder as specified below.
25
+
26
+ "Copyright Holder" is whoever is named in the copyright or
27
+ copyrights for the package.
28
+
29
+ "You" is you, if you're thinking about copying or distributing
30
+ this Package.
31
+
32
+ "Reasonable copying fee" is whatever you can justify on the
33
+ basis of media cost, duplication charges, time of people involved,
34
+ and so on. (You will not be required to justify it to the
35
+ Copyright Holder, but only to the computing community at large
36
+ as a market that must bear the fee.)
37
+
38
+ "Freely Available" means that no fee is charged for the item
39
+ itself, though there may be fees involved in handling the item.
40
+ It also means that recipients of the item may redistribute it
41
+ under the same conditions they received it.
42
+
43
+ 1. You may make and give away verbatim copies of the source form of the
44
+ Standard Version of this Package without restriction, provided that you
45
+ duplicate all of the original copyright notices and associated disclaimers.
46
+
47
+ 2. You may apply bug fixes, portability fixes and other modifications
48
+ derived from the Public Domain or from the Copyright Holder. A Package
49
+ modified in such a way shall still be considered the Standard Version.
50
+
51
+ 3. You may otherwise modify your copy of this Package in any way, provided
52
+ that you insert a prominent notice in each changed file stating how and
53
+ when you changed that file, and provided that you do at least ONE of the
54
+ following:
55
+
56
+ a) place your modifications in the Public Domain or otherwise make them
57
+ Freely Available, such as by posting said modifications to Usenet or
58
+ an equivalent medium, or placing the modifications on a major archive
59
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
60
+ your modifications in the Standard Version of the Package.
61
+
62
+ b) use the modified Package only within your corporation or organization.
63
+
64
+ c) rename any non-standard executables so the names do not conflict
65
+ with standard executables, which must also be provided, and provide
66
+ a separate manual page for each non-standard executable that clearly
67
+ documents how it differs from the Standard Version.
68
+
69
+ d) make other distribution arrangements with the Copyright Holder.
70
+
71
+ 4. You may distribute the programs of this Package in object code or
72
+ executable form, provided that you do at least ONE of the following:
73
+
74
+ a) distribute a Standard Version of the executables and library files,
75
+ together with instructions (in the manual page or equivalent) on where
76
+ to get the Standard Version.
77
+
78
+ b) accompany the distribution with the machine-readable source of
79
+ the Package with your modifications.
80
+
81
+ c) give non-standard executables non-standard names, and clearly
82
+ document the differences in manual pages (or equivalent), together
83
+ with instructions on where to get the Standard Version.
84
+
85
+ d) make other distribution arrangements with the Copyright Holder.
86
+
87
+ 5. You may charge a reasonable copying fee for any distribution of this
88
+ Package. You may charge any fee you choose for support of this
89
+ Package. You may not charge a fee for this Package itself. However,
90
+ you may distribute this Package in aggregate with other (possibly
91
+ commercial) programs as part of a larger (possibly commercial) software
92
+ distribution provided that you do not advertise this Package as a
93
+ product of your own. You may embed this Package's interpreter within
94
+ an executable of yours (by linking); this shall be construed as a mere
95
+ form of aggregation, provided that the complete Standard Version of the
96
+ interpreter is so embedded.
97
+
98
+ 6. The scripts and library files supplied as input to or produced as
99
+ output from the programs of this Package do not automatically fall
100
+ under the copyright of this Package, but belong to whoever generated
101
+ them, and may be sold commercially, and may be aggregated with this
102
+ Package. If such scripts or library files are aggregated with this
103
+ Package via the so-called "undump" or "unexec" methods of producing a
104
+ binary executable image, then distribution of such an image shall
105
+ neither be construed as a distribution of this Package nor shall it
106
+ fall under the restrictions of Paragraphs 3 and 4, provided that you do
107
+ not represent such an executable image as a Standard Version of this
108
+ Package.
109
+
110
+ 7. C subroutines (or comparably compiled subroutines in other
111
+ languages) supplied by you and linked into this Package in order to
112
+ emulate subroutines and variables of the language defined by this
113
+ Package shall not be considered part of this Package, but are the
114
+ equivalent of input as in Paragraph 6, provided these subroutines do
115
+ not change the language in any way that would cause it to fail the
116
+ regression tests for the language.
117
+
118
+ 8. Aggregation of this Package with a commercial distribution is always
119
+ permitted provided that the use of this Package is embedded; that is,
120
+ when no overt attempt is made to make this Package's interfaces visible
121
+ to the end user of the commercial distribution. Such use shall not be
122
+ construed as a distribution of this Package.
123
+
124
+ 9. The name of the Copyright Holder may not be used to endorse or promote
125
+ products derived from this software without specific prior written permission.
126
+
127
+ 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
128
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
129
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
130
+
131
+ The End
@@ -0,0 +1,102 @@
1
+ 2004-03-09 22:32 Michael Granger <ged@FaerieMUD.org>
2
+
3
+ * makedist.rb (1.1), utils.rb (1.1), docs/.cvsignore (1.1),
4
+ docs/CATALOG (1.1), docs/makedocs.rb (1.1):
5
+
6
+ Initial checkin.
7
+
8
+ 2004-03-08 00:07 Michael Granger <ged@FaerieMUD.org>
9
+
10
+ * tests/: othersub.rb (1.2), dir/deepsubofmybase.rb (1.2):
11
+
12
+ - Fixed class/file agreement.
13
+
14
+ 2004-03-08 00:06 Michael Granger <ged@FaerieMUD.org>
15
+
16
+ * tests/mybase.rb (1.2):
17
+
18
+ - Fixed derivativeDirs method.
19
+
20
+ 2004-03-08 00:05 Michael Granger <ged@FaerieMUD.org>
21
+
22
+ * lib/pluginfactory.rb (1.4):
23
+
24
+ - Documentation additions
25
+
26
+ - Refactored path-search code
27
+
28
+ 2004-03-07 21:30 Michael Granger <ged@FaerieMUD.org>
29
+
30
+ * test.rb (1.3):
31
+
32
+ - Added a hook into -d that turns on logging.
33
+
34
+ 2004-03-07 19:13 Martin Chase <stillflame@FaerieMUD.org>
35
+
36
+ * README (1.4):
37
+
38
+ no, this is not using the default install.rb installation process
39
+
40
+ 2004-03-07 19:07 Martin Chase <stillflame@FaerieMUD.org>
41
+
42
+ * lib/pluginfactory.rb (1.3):
43
+
44
+ FactoryError now inherits from RuntimeError
45
+
46
+ 2004-03-07 19:06 Martin Chase <stillflame@FaerieMUD.org>
47
+
48
+ * test.rb (1.2):
49
+
50
+ filling in
51
+
52
+ 2004-03-07 19:05 Martin Chase <stillflame@FaerieMUD.org>
53
+
54
+ * tests/: mybase.rb (1.1), othersub.rb (1.1), subofmybase.rb (1.1),
55
+ dir/deepsubofmybase.rb (1.1):
56
+
57
+ initial import
58
+
59
+ 2004-03-07 15:42 Martin Chase <stillflame@FaerieMUD.org>
60
+
61
+ * README (1.3):
62
+
63
+ reformatted
64
+
65
+ 2004-03-07 15:41 Martin Chase <stillflame@FaerieMUD.org>
66
+
67
+ * install.rb (1.2):
68
+
69
+ filled in
70
+
71
+ 2004-03-07 14:36 Martin Chase <stillflame@FaerieMUD.org>
72
+
73
+ * COPYRIGHT (1.2), README (1.2):
74
+
75
+ filled in
76
+
77
+ 2004-03-07 10:49 Martin Chase <stillflame@FaerieMUD.org>
78
+
79
+ * lib/pluginfactory.rb (1.2):
80
+
81
+ cleaned out ties to other modules
82
+
83
+ 2004-03-07 09:33 Martin Chase <stillflame@FaerieMUD.org>
84
+
85
+ * lib/: factory.rb (1.2), pluginfactory.rb (1.1):
86
+
87
+ renaming
88
+
89
+ 2004-03-07 09:31 Martin Chase <stillflame@FaerieMUD.org>
90
+
91
+ * COPYRIGHT (1.1), README (1.1), install.rb (1.1), test.rb (1.1),
92
+ lib/factory.rb (1.1):
93
+
94
+ Initial revision
95
+
96
+ 2004-03-07 09:31 Martin Chase <stillflame@FaerieMUD.org>
97
+
98
+ * COPYRIGHT (1.1.1.1), README (1.1.1.1), install.rb (1.1.1.1),
99
+ test.rb (1.1.1.1), lib/factory.rb (1.1.1.1) (utags: release):
100
+
101
+ initial stripping out of Arrow/MUES
102
+
data/README ADDED
@@ -0,0 +1,39 @@
1
+ = The PluginFactory mixin
2
+
3
+ A mixin implementing a factory pattern, modified to load in subclasses on
4
+ request.
5
+
6
+ == Install
7
+
8
+ $ sudo ./install.rb
9
+
10
+ == Tests
11
+
12
+ $ ./test.rb
13
+
14
+ == Authors
15
+
16
+ Michael Granger <ged@FaerieMUD.org>
17
+
18
+ == Contributors
19
+
20
+ Martin Chase <stillflame@FaerieMUD.org>
21
+
22
+ == License/Legal
23
+
24
+ All assets of the project are copyright (c) 2003-2004, The FaerieMUD
25
+ Consortium. All rights reserved.
26
+
27
+ You may use, modify, and/or redistribute this software under the terms of the
28
+ Perl Artistic License, a copy of which should have been included in this
29
+ distribution (See the file COPYRIGHT). If it was not, a copy of it may be
30
+ obtained from http://language.perl.com/misc/Artistic.html or
31
+ http://www.faeriemud.org/artistic.html).
32
+
33
+ THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
34
+ INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
35
+ FITNESS FOR A PARTICULAR PURPOSE.
36
+
37
+ == Rcsid
38
+
39
+ $Id: README 14 2004-03-08 03:13:38Z stillflame $
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Module Install Script
4
+ # $Id: install.rb 31 2004-11-21 17:14:13Z ged $
5
+ #
6
+ # Thanks to Masatoshi SEKI for ideas found in his install.rb.
7
+ #
8
+ # Copyright (c) 2001-2004 The FaerieMUD Consortium.
9
+ #
10
+ # This is free software. You may use, modify, and/or redistribute this
11
+ # software under the terms of the Perl Artistic License. (See
12
+ # http://language.perl.com/misc/Artistic.html)
13
+ #
14
+
15
+ require './utils.rb'
16
+ include UtilityFunctions
17
+
18
+ require 'rbconfig'
19
+ include Config
20
+
21
+ require 'find'
22
+ require 'ftools'
23
+ require 'optparse'
24
+
25
+ $version = %q$Revision: 31 $
26
+ $rcsId = %q$Id: install.rb 31 2004-11-21 17:14:13Z ged $
27
+
28
+ # Define required libraries
29
+ RequiredLibraries = [
30
+ # libraryname, nice name, RAA URL, Download URL, e.g.,
31
+ #[ 'strscan', "Strscan",
32
+ # 'http://www.ruby-lang.org/en/raa-list.rhtml?name=strscan',
33
+ # 'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz',
34
+ #],
35
+ ]
36
+
37
+ class Installer
38
+
39
+ @@PrunePatterns = [
40
+ /CVS/,
41
+ /~$/,
42
+ %r:(^|/)\.:,
43
+ /\.tpl$/,
44
+ ]
45
+
46
+ def initialize( testing=false )
47
+ @ftools = (testing) ? self : File
48
+ end
49
+
50
+ ### Make the specified dirs (which can be a String or an Array of Strings)
51
+ ### with the specified mode.
52
+ def makedirs( dirs, mode=0755, verbose=false )
53
+ dirs = [ dirs ] unless dirs.is_a? Array
54
+
55
+ oldumask = File::umask
56
+ File::umask( 0777 - mode )
57
+
58
+ for dir in dirs
59
+ if @ftools == File
60
+ File::mkpath( dir, $verbose )
61
+ else
62
+ $stderr.puts "Make path %s with mode %o" % [ dir, mode ]
63
+ end
64
+ end
65
+
66
+ File::umask( oldumask )
67
+ end
68
+
69
+ def install( srcfile, dstfile, mode=nil, verbose=false )
70
+ dstfile = File.catname(srcfile, dstfile)
71
+ unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
72
+ $stderr.puts " install #{srcfile} -> #{dstfile}"
73
+ else
74
+ $stderr.puts " skipping #{dstfile}: unchanged"
75
+ end
76
+ end
77
+
78
+ public
79
+
80
+ def installFiles( src, dstDir, mode=0444, verbose=false )
81
+ directories = []
82
+ files = []
83
+
84
+ if File.directory?( src )
85
+ Find.find( src ) {|f|
86
+ Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
87
+ next if f == src
88
+
89
+ if FileTest.directory?( f )
90
+ directories << f.gsub( /^#{src}#{File::Separator}/, '' )
91
+ next
92
+
93
+ elsif FileTest.file?( f )
94
+ files << f.gsub( /^#{src}#{File::Separator}/, '' )
95
+
96
+ else
97
+ Find.prune
98
+ end
99
+ }
100
+ else
101
+ files << File.basename( src )
102
+ src = File.dirname( src )
103
+ end
104
+
105
+ dirs = [ dstDir ]
106
+ dirs |= directories.collect {|d| File.join(dstDir,d)}
107
+ makedirs( dirs, 0755, verbose )
108
+ files.each {|f|
109
+ srcfile = File.join(src,f)
110
+ dstfile = File.dirname(File.join( dstDir,f ))
111
+
112
+ if verbose
113
+ if mode
114
+ $stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
115
+ else
116
+ $stderr.puts "Install #{srcfile} -> #{dstfile}"
117
+ end
118
+ end
119
+
120
+ @ftools.install( srcfile, dstfile, mode, verbose )
121
+ }
122
+ end
123
+
124
+ end
125
+
126
+
127
+ if $0 == __FILE__
128
+ for lib in RequiredLibraries
129
+ testForRequiredLibrary( *lib )
130
+ end
131
+
132
+ dryrun = false
133
+
134
+ # Parse command-line switches
135
+ ARGV.options {|oparser|
136
+ oparser.banner = "Usage: #$0 [options]\n"
137
+
138
+ oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
139
+ $VERBOSE = true
140
+ debugMsg "Turned verbose on."
141
+ }
142
+
143
+ oparser.on( "--dry-run", "-n", TrueClass, "Don't really install anything" ) {
144
+ debugMsg "Turned dry-run on."
145
+ dryrun = true
146
+ }
147
+
148
+ # Handle the 'help' option
149
+ oparser.on( "--help", "-h", "Display this text." ) {
150
+ $stderr.puts oparser
151
+ exit!(0)
152
+ }
153
+
154
+ oparser.parse!
155
+ }
156
+
157
+ debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
158
+ sitelibdir = CONFIG['sitelibdir']
159
+ debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
160
+ sitearchdir = CONFIG['sitearchdir']
161
+
162
+ message "Installing..."
163
+ i = Installer.new( dryrun )
164
+ #i.installFiles( "redist", sitelibdir, 0444, verbose )
165
+ i.installFiles( "lib", sitelibdir, 0444, $VERBOSE )
166
+
167
+ message "done.\n"
168
+ end
169
+
170
+
171
+
172
+
@@ -0,0 +1,389 @@
1
+ #!/usr/bin/env ruby -w
2
+ #
3
+ # This module contains the PluginFactory mixin. Including PluginFactory in your
4
+ # class turns it into a factory for its derivatives, capable of searching for
5
+ # and loading them by name. This is useful when you have an abstract base class
6
+ # which defines an interface and basic functionality for a part of a larger
7
+ # system, and a collection of subclasses which implement the interface for
8
+ # different underlying functionality.
9
+ #
10
+ # An example of where this might be useful is in a program which talks to a
11
+ # database. To avoid coupling it to a specific database, you use a Driver class
12
+ # which encapsulates your program's interaction with the database behind a
13
+ # useful interface. Now you can create a concrete implementation of the Driver
14
+ # class for each kind of database you wish to talk to. If you make the base
15
+ # Driver class a PluginFactory, too, you can add new drivers simply by dropping
16
+ # them in a directory and using the Driver's <tt>create</tt> method to
17
+ # instantiate them:
18
+ #
19
+ # == Creation Argument Variants
20
+ #
21
+ # The +create+ class method added to your class by PluginFactory searches for your module using
22
+ #
23
+ # == Synopsis
24
+ #
25
+ # in driver.rb:
26
+ #
27
+ # require "PluginFactory"
28
+ #
29
+ # class Driver
30
+ # include PluginFactory
31
+ # def self::derivative_dirs
32
+ # ["drivers"]
33
+ # end
34
+ # end
35
+ #
36
+ # in drivers/mysql.rb:
37
+ #
38
+ # require 'driver'
39
+ #
40
+ # class MysqlDriver < Driver
41
+ # ...implementation...
42
+ # end
43
+ #
44
+ # in /usr/lib/ruby/1.8/PostgresDriver.rb:
45
+ #
46
+ # require 'driver'
47
+ #
48
+ # class PostgresDriver < Driver
49
+ # ...implementation...
50
+ # end
51
+ #
52
+ # elsewhere
53
+ #
54
+ # require 'driver'
55
+ #
56
+ # config[:driver_type] #=> "mysql"
57
+ # driver = Driver.create( config[:driver_type] )
58
+ # driver.class #=> MysqlDriver
59
+ # pgdriver = Driver.create( "PostGresDriver" )
60
+ #
61
+ # == Subversion ID
62
+ #
63
+ # $Id: pluginfactory.rb 38 2007-03-13 18:20:58Z deveiant $
64
+ #
65
+ # == Authors
66
+ #
67
+ # * Martin Chase <stillflame@FaerieMUD.org>
68
+ # * Michael Granger <ged@FaerieMUD.org>
69
+ #
70
+ #:include: COPYRIGHT
71
+ #
72
+ #---
73
+ #
74
+ # Please see the file docs/COPYRIGHT for licensing details.
75
+ #
76
+
77
+
78
+ ### An exception class for PluginFactory specific errors.
79
+ class FactoryError < RuntimeError
80
+ def initialize( *args )
81
+ if ! args.empty?
82
+ msg = args.collect {|a| a.to_s}.join
83
+ super( msg )
84
+ else
85
+ super( message )
86
+ end
87
+ end
88
+ end # class FactoryError
89
+
90
+
91
+ ### A mixin that adds PluginFactory class methods to a base class, so that
92
+ ### subclasses may be instantiated by name.
93
+ module PluginFactory
94
+
95
+ ### A callback for logging the various debug and information this module
96
+ ### has to log. Should take two arguments, the log level, possibly as a
97
+ ### symbol, and the log message itself.
98
+ @logger_callback = nil
99
+ class << self
100
+ attr_accessor :logger_callback
101
+ end
102
+
103
+ ### If the logger callback is set, use it to pass on a log entry. First argument is
104
+ def self::log(level, *msg)
105
+ @logger_callback.call(level, msg.join) if @logger_callback
106
+ end
107
+
108
+
109
+ ### Inclusion callback -- extends the including class.
110
+ def self::included( klass )
111
+ klass.extend( self )
112
+ end
113
+
114
+
115
+ ### Raise an exception if the object being extended is anything but a
116
+ ### class.
117
+ def self::extend_object( obj )
118
+ unless obj.is_a?( Class )
119
+ raise TypeError, "Cannot extend a #{obj.class.name}", caller(1)
120
+ end
121
+ obj.instance_variable_set( :@derivatives, {} )
122
+ super
123
+ end
124
+
125
+
126
+ #############################################################
127
+ ### M I X I N M E T H O D S
128
+ #############################################################
129
+
130
+ ### Return the Hash of derivative classes, keyed by various versions of
131
+ ### the class name.
132
+ def derivatives
133
+ ancestors.each {|klass|
134
+ if klass.instance_variables.include?( "@derivatives" )
135
+ break klass.instance_variable_get( :@derivatives )
136
+ end
137
+ }
138
+ end
139
+
140
+
141
+ ### Returns the type name used when searching for a derivative.
142
+ def factory_type
143
+ base = nil
144
+ self.ancestors.each {|klass|
145
+ if klass.instance_variables.include?( "@derivatives" )
146
+ base = klass
147
+ break
148
+ end
149
+ }
150
+
151
+ raise FactoryError, "Couldn't find factory base for #{self.name}" if
152
+ base.nil?
153
+
154
+ if base.name =~ /^.*::(.*)/
155
+ return $1
156
+ else
157
+ return base.name
158
+ end
159
+ end
160
+ alias_method :factoryType, :factory_type
161
+
162
+
163
+ ### Inheritance callback -- Register subclasses in the derivatives hash
164
+ ### so that ::create knows about them.
165
+ def inherited( subclass )
166
+ keys = [ subclass.name, subclass.name.downcase, subclass ]
167
+
168
+ # Handle class names like 'FooBar' for 'Bar' factories.
169
+ if subclass.name.match( /(?:.*::)?(\w+)(?:#{self.factory_type})/i )
170
+ keys << Regexp.last_match[1].downcase
171
+ else
172
+ keys << subclass.name.sub( /.*::/, '' ).downcase
173
+ end
174
+
175
+ keys.uniq.each {|key|
176
+ #PluginFactory::log :info, "Registering %s derivative of %s as %p" %
177
+ # [ subclass.name, self.name, key ]
178
+ self.derivatives[ key ] = subclass
179
+ }
180
+ super
181
+ end
182
+
183
+
184
+ ### Returns an Array of registered derivatives
185
+ def derivative_classes
186
+ self.derivatives.values.uniq
187
+ end
188
+ alias_method :derivativeClasses, :derivative_classes
189
+
190
+
191
+ ### Given the <tt>className</tt> of the class to instantiate, and other
192
+ ### arguments bound for the constructor of the new object, this method
193
+ ### loads the derivative class if it is not loaded already (raising a
194
+ ### LoadError if an appropriately-named file cannot be found), and
195
+ ### instantiates it with the given <tt>args</tt>. The <tt>className</tt>
196
+ ### may be the the fully qualified name of the class, the class object
197
+ ### itself, or the unique part of the class name. The following examples
198
+ ### would all try to load and instantiate a class called "FooListener"
199
+ ### if Listener included Factory
200
+ ### obj = Listener::create( 'FooListener' )
201
+ ### obj = Listener::create( FooListener )
202
+ ### obj = Listener::create( 'Foo' )
203
+ def create( subType, *args, &block )
204
+ subclass = get_subclass( subType )
205
+
206
+ return subclass.new( *args, &block )
207
+ rescue => err
208
+ nicetrace = err.backtrace.reject {|frame| /#{__FILE__}/ =~ frame}
209
+ msg = "When creating '#{subType}': " + err.message
210
+ Kernel::raise( err.class, msg, nicetrace )
211
+ end
212
+
213
+
214
+ ### Given a <tt>className</tt> like that of the first argument to
215
+ ### #create, attempt to load the corresponding class if it is not
216
+ ### already loaded and return the class object.
217
+ def get_subclass( className )
218
+ return self if ( self.name == className || className == '' )
219
+ return className if className.is_a?( Class ) && className >= self
220
+
221
+ unless self.derivatives.has_key?( className.downcase )
222
+ self.load_derivative( className )
223
+
224
+ unless self.derivatives.has_key?( className.downcase )
225
+ raise FactoryError,
226
+ "load_derivative(%s) didn't add a '%s' key to the "\
227
+ "registry for %s" %
228
+ [ className, className.downcase, self.name ]
229
+ end
230
+
231
+ subclass = self.derivatives[ className.downcase ]
232
+ unless subclass.is_a?( Class )
233
+ raise FactoryError,
234
+ "load_derivative(%s) added something other than a class "\
235
+ "to the registry for %s: %p" %
236
+ [ className, self.name, subclass ]
237
+ end
238
+ end
239
+
240
+ return self.derivatives[ className.downcase ]
241
+ end
242
+ alias_method :getSubclass, :get_subclass
243
+
244
+
245
+ ### Calculates an appropriate filename for the derived class using the
246
+ ### name of the base class and tries to load it via <tt>require</tt>. If
247
+ ### the including class responds to a method named
248
+ ### <tt>derivativeDirs</tt>, its return value (either a String, or an
249
+ ### array of Strings) is added to the list of prefix directories to try
250
+ ### when attempting to require a modules. Eg., if
251
+ ### <tt>class.derivativeDirs</tt> returns <tt>['foo','bar']</tt> the
252
+ ### require line is tried with both <tt>'foo/'</tt> and <tt>'bar/'</tt>
253
+ ### prepended to it.
254
+ def load_derivative( className )
255
+ className = className.to_s
256
+
257
+ #PluginFactory::log :debug, "Loading derivative #{className}"
258
+
259
+ # Get the unique part of the derived class name and try to
260
+ # load it from one of the derivative subdirs, if there are
261
+ # any.
262
+ mod_name = self.get_module_name( className )
263
+ self.require_derivative( mod_name )
264
+
265
+ # Check to see if the specified listener is now loaded. If it
266
+ # is not, raise an error to that effect.
267
+ unless self.derivatives[ className.downcase ]
268
+ raise FactoryError,
269
+ "Couldn't find a %s named '%s'. Loaded derivatives are: %p" % [
270
+ self.factory_type,
271
+ className.downcase,
272
+ self.derivatives.keys,
273
+ ], caller(3)
274
+ end
275
+
276
+ return true
277
+ end
278
+ alias_method :loadDerivative, :load_derivative
279
+
280
+
281
+ ### Build and return the unique part of the given <tt>className</tt>
282
+ ### either by stripping leading namespaces if the name already has the
283
+ ### name of the factory type in it (eg., 'My::FooService' for Service,
284
+ ### or by appending the factory type if it doesn't.
285
+ def get_module_name( className )
286
+ if className =~ /\w+#{self.factory_type}/
287
+ mod_name = className.sub( /(?:.*::)?(\w+)(?:#{self.factory_type})/, "\\1" )
288
+ else
289
+ mod_name = className
290
+ end
291
+
292
+ return mod_name
293
+ end
294
+ alias_method :getModuleName, :get_module_name
295
+
296
+
297
+ ### If the factory responds to the #derivativeDirs method, call
298
+ ### it and use the returned array as a list of directories to
299
+ ### search for the module with the specified <tt>mod_name</tt>.
300
+ def require_derivative( mod_name )
301
+
302
+ # See if we have a list of special subdirs that derivatives
303
+ # live in
304
+ if ( self.respond_to?(:derivative_dirs) )
305
+ subdirs = self.derivative_dirs
306
+
307
+ elsif ( self.respond_to?(:derivativeDirs) )
308
+ subdirs = self.derivativeDirs
309
+
310
+ # If not, just try requiring it from $LOAD_PATH
311
+ else
312
+ subdirs = ['']
313
+ end
314
+
315
+ subdirs = [ subdirs ] unless subdirs.is_a?( Array )
316
+ PluginFactory::log :debug, "Subdirs are: %p" % [subdirs]
317
+ fatals = []
318
+
319
+ # Iterate over the subdirs until we successfully require a
320
+ # module.
321
+ catch( :found ) {
322
+ subdirs.collect {|dir| dir.strip}.each do |subdir|
323
+ self.make_require_path( mod_name, subdir ).each {|path|
324
+ PluginFactory::log :debug, "Trying #{path}..."
325
+
326
+ # Try to require the module, saving errors and jumping
327
+ # out of the catch block on success.
328
+ begin
329
+ require( path.untaint )
330
+ rescue LoadError => err
331
+ PluginFactory::log :debug,
332
+ "No module at '%s', trying the next alternative: '%s'" %
333
+ [ path, err.message ]
334
+ rescue ScriptError,StandardError => err
335
+ fatals << err
336
+ PluginFactory::log :error,
337
+ "Found '#{path}', but encountered an error: %s\n\t%s" %
338
+ [ err.message, err.backtrace.join("\n\t") ]
339
+ else
340
+ #PluginFactory::log :debug,
341
+ # "Found '#{path}'. Throwing :found"
342
+ throw :found
343
+ end
344
+ }
345
+ end
346
+
347
+ #PluginFactory::log :debug, "fatals = %p" % [ fatals ]
348
+
349
+ # Re-raise is there was a file found, but it didn't load for
350
+ # some reason.
351
+ if ! fatals.empty?
352
+ #PluginFactory::log :debug, "Re-raising first fatal error"
353
+ Kernel::raise( fatals.first )
354
+ end
355
+
356
+ nil
357
+ }
358
+ end
359
+ alias_method :requireDerivative, :require_derivative
360
+
361
+
362
+ ### Make a list of permutations of the given +modname+ for the given
363
+ ### +subdir+. Called on a +DataDriver+ class with the arguments 'Socket' and
364
+ ### 'drivers', returns:
365
+ ### ["drivers/socketdatadriver", "drivers/socketDataDriver",
366
+ ### "drivers/SocketDataDriver", "drivers/socket", "drivers/Socket"]
367
+ def make_require_path( modname, subdir )
368
+ path = []
369
+ myname = self.factory_type
370
+
371
+ # Make permutations of the two parts
372
+ path << modname
373
+ path << modname.downcase
374
+ path << modname + myname
375
+ path << modname.downcase + myname
376
+ path << modname.downcase + myname.downcase
377
+
378
+ # If a non-empty subdir was given, prepend it to all the items in the
379
+ # path
380
+ unless subdir.nil? or subdir.empty?
381
+ path.collect! {|m| File::join(subdir, m)}
382
+ end
383
+
384
+ PluginFactory::log :debug, "Path is: #{path.uniq.reverse.inspect}..."
385
+ return path.uniq.reverse
386
+ end
387
+ alias_method :makeRequirePath, :make_require_path
388
+
389
+ end # module Factory