pluginfactory 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYRIGHT +131 -0
- data/ChangeLog +102 -0
- data/README +39 -0
- data/install.rb +172 -0
- data/lib/pluginfactory.rb +389 -0
- data/test.rb +72 -0
- data/tests/dir/deepsubofmybase.rb +3 -0
- data/tests/mybase.rb +11 -0
- data/tests/othersub.rb +5 -0
- data/tests/subofmybase.rb +5 -0
- data/utils.rb +672 -0
- metadata +57 -0
data/COPYRIGHT
ADDED
@@ -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
|
data/ChangeLog
ADDED
@@ -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 $
|
data/install.rb
ADDED
@@ -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
|