gem_plugin 0.1
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/COPYING +504 -0
- data/LICENSE +58 -0
- data/README +115 -0
- data/Rakefile +35 -0
- data/doc/rdoc/classes/GemPlugin.html +247 -0
- data/doc/rdoc/classes/GemPlugin.src/M000001.html +19 -0
- data/doc/rdoc/classes/GemPlugin/Base.html +193 -0
- data/doc/rdoc/classes/GemPlugin/Base.src/M000002.html +20 -0
- data/doc/rdoc/classes/GemPlugin/Base.src/M000003.html +18 -0
- data/doc/rdoc/classes/GemPlugin/Base.src/M000004.html +18 -0
- data/doc/rdoc/classes/GemPlugin/Manager.html +262 -0
- data/doc/rdoc/classes/GemPlugin/Manager.src/M000005.html +19 -0
- data/doc/rdoc/classes/GemPlugin/Manager.src/M000006.html +43 -0
- data/doc/rdoc/classes/GemPlugin/Manager.src/M000007.html +19 -0
- data/doc/rdoc/classes/GemPlugin/Manager.src/M000008.html +29 -0
- data/doc/rdoc/classes/GemPlugin/Manager.src/M000009.html +18 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/COPYING.html +756 -0
- data/doc/rdoc/files/LICENSE.html +164 -0
- data/doc/rdoc/files/README.html +273 -0
- data/doc/rdoc/files/lib/gem_plugin_rb.html +109 -0
- data/doc/rdoc/fr_class_index.html +29 -0
- data/doc/rdoc/fr_file_index.html +30 -0
- data/doc/rdoc/fr_method_index.html +35 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/lib/gem_plugin.rb +204 -0
- data/test/test_plugins.rb +73 -0
- data/tools/rakehelp.rb +111 -0
- metadata +80 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Classes
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Classes</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Classes</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/GemPlugin.html">GemPlugin</a><br />
|
24
|
+
<a href="classes/GemPlugin/Base.html">GemPlugin::Base</a><br />
|
25
|
+
<a href="classes/GemPlugin/Manager.html">GemPlugin::Manager</a><br />
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</body>
|
29
|
+
</html>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Files
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Files</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Files</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="files/COPYING.html">COPYING</a><br />
|
24
|
+
<a href="files/LICENSE.html">LICENSE</a><br />
|
25
|
+
<a href="files/README.html">README</a><br />
|
26
|
+
<a href="files/lib/gem_plugin_rb.html">lib/gem_plugin.rb</a><br />
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</body>
|
30
|
+
</html>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Methods
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Methods</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Methods</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/GemPlugin.html#M000001">Plugin (GemPlugin)</a><br />
|
24
|
+
<a href="classes/GemPlugin/Manager.html#M000009">available (GemPlugin::Manager)</a><br />
|
25
|
+
<a href="classes/GemPlugin/Base.html#M000003">category= (GemPlugin::Base)</a><br />
|
26
|
+
<a href="classes/GemPlugin/Manager.html#M000008">create (GemPlugin::Manager)</a><br />
|
27
|
+
<a href="classes/GemPlugin/Base.html#M000002">inherited (GemPlugin::Base)</a><br />
|
28
|
+
<a href="classes/GemPlugin/Manager.html#M000006">load (GemPlugin::Manager)</a><br />
|
29
|
+
<a href="classes/GemPlugin/Manager.html#M000005">new (GemPlugin::Manager)</a><br />
|
30
|
+
<a href="classes/GemPlugin/Base.html#M000004">new (GemPlugin::Base)</a><br />
|
31
|
+
<a href="classes/GemPlugin/Manager.html#M000007">register (GemPlugin::Manager)</a><br />
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</body>
|
35
|
+
</html>
|
data/doc/rdoc/index.html
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
5
|
+
|
6
|
+
<!--
|
7
|
+
|
8
|
+
RDoc Documentation
|
9
|
+
|
10
|
+
-->
|
11
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
12
|
+
<head>
|
13
|
+
<title>RDoc Documentation</title>
|
14
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
15
|
+
</head>
|
16
|
+
<frameset rows="20%, 80%">
|
17
|
+
<frameset cols="25%,35%,45%">
|
18
|
+
<frame src="fr_file_index.html" title="Files" name="Files" />
|
19
|
+
<frame src="fr_class_index.html" name="Classes" />
|
20
|
+
<frame src="fr_method_index.html" name="Methods" />
|
21
|
+
</frameset>
|
22
|
+
<frame src="files/README.html" name="docwin" />
|
23
|
+
</frameset>
|
24
|
+
</html>
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 90%;
|
5
|
+
margin: 0;
|
6
|
+
margin-left: 40px;
|
7
|
+
padding: 0;
|
8
|
+
background: white;
|
9
|
+
}
|
10
|
+
|
11
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
+
h1 { font-size: 150%; }
|
13
|
+
h2,h3,h4 { margin-top: 1em; }
|
14
|
+
|
15
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
+
a:hover { background: #039; color: #eef; }
|
17
|
+
|
18
|
+
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
+
td > a {
|
20
|
+
background: transparent;
|
21
|
+
color: #039;
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* and inside a section title */
|
26
|
+
.section-title > a {
|
27
|
+
background: transparent;
|
28
|
+
color: #eee;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
/* === Structural elements =================================== */
|
33
|
+
|
34
|
+
div#index {
|
35
|
+
margin: 0;
|
36
|
+
margin-left: -40px;
|
37
|
+
padding: 0;
|
38
|
+
font-size: 90%;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
div#index a {
|
43
|
+
margin-left: 0.7em;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#index .section-bar {
|
47
|
+
margin-left: 0px;
|
48
|
+
padding-left: 0.7em;
|
49
|
+
background: #ccc;
|
50
|
+
font-size: small;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
div#classHeader, div#fileHeader {
|
55
|
+
width: auto;
|
56
|
+
color: white;
|
57
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
+
margin: 0;
|
59
|
+
margin-left: -40px;
|
60
|
+
border-bottom: 3px solid #006;
|
61
|
+
}
|
62
|
+
|
63
|
+
div#classHeader a, div#fileHeader a {
|
64
|
+
background: inherit;
|
65
|
+
color: white;
|
66
|
+
}
|
67
|
+
|
68
|
+
div#classHeader td, div#fileHeader td {
|
69
|
+
background: inherit;
|
70
|
+
color: white;
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
div#fileHeader {
|
75
|
+
background: #057;
|
76
|
+
}
|
77
|
+
|
78
|
+
div#classHeader {
|
79
|
+
background: #048;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.class-name-in-header {
|
84
|
+
font-size: 180%;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
div#bodyContent {
|
90
|
+
padding: 0 1.5em 0 1.5em;
|
91
|
+
}
|
92
|
+
|
93
|
+
div#description {
|
94
|
+
padding: 0.5em 1.5em;
|
95
|
+
background: #efefef;
|
96
|
+
border: 1px dotted #999;
|
97
|
+
}
|
98
|
+
|
99
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
+
color: #125;;
|
101
|
+
background: transparent;
|
102
|
+
}
|
103
|
+
|
104
|
+
div#validator-badges {
|
105
|
+
text-align: center;
|
106
|
+
}
|
107
|
+
div#validator-badges img { border: 0; }
|
108
|
+
|
109
|
+
div#copyright {
|
110
|
+
color: #333;
|
111
|
+
background: #efefef;
|
112
|
+
font: 0.75em sans-serif;
|
113
|
+
margin-top: 5em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
padding: 0.5em 2em;
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
/* === Classes =================================== */
|
120
|
+
|
121
|
+
table.header-table {
|
122
|
+
color: white;
|
123
|
+
font-size: small;
|
124
|
+
}
|
125
|
+
|
126
|
+
.type-note {
|
127
|
+
font-size: small;
|
128
|
+
color: #DEDEDE;
|
129
|
+
}
|
130
|
+
|
131
|
+
.xxsection-bar {
|
132
|
+
background: #eee;
|
133
|
+
color: #333;
|
134
|
+
padding: 3px;
|
135
|
+
}
|
136
|
+
|
137
|
+
.section-bar {
|
138
|
+
color: #333;
|
139
|
+
border-bottom: 1px solid #999;
|
140
|
+
margin-left: -20px;
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
.section-title {
|
145
|
+
background: #79a;
|
146
|
+
color: #eee;
|
147
|
+
padding: 3px;
|
148
|
+
margin-top: 2em;
|
149
|
+
margin-left: -30px;
|
150
|
+
border: 1px solid #999;
|
151
|
+
}
|
152
|
+
|
153
|
+
.top-aligned-row { vertical-align: top }
|
154
|
+
.bottom-aligned-row { vertical-align: bottom }
|
155
|
+
|
156
|
+
/* --- Context section classes ----------------------- */
|
157
|
+
|
158
|
+
.context-row { }
|
159
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
+
.context-item-value { font-size: small; color: #448; }
|
161
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
+
|
163
|
+
/* --- Method classes -------------------------- */
|
164
|
+
.method-detail {
|
165
|
+
background: #efefef;
|
166
|
+
padding: 0;
|
167
|
+
margin-top: 0.5em;
|
168
|
+
margin-bottom: 1em;
|
169
|
+
border: 1px dotted #ccc;
|
170
|
+
}
|
171
|
+
.method-heading {
|
172
|
+
color: black;
|
173
|
+
background: #ccc;
|
174
|
+
border-bottom: 1px solid #666;
|
175
|
+
padding: 0.2em 0.5em 0 0.5em;
|
176
|
+
}
|
177
|
+
.method-signature { color: black; background: inherit; }
|
178
|
+
.method-name { font-weight: bold; }
|
179
|
+
.method-args { font-style: italic; }
|
180
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
+
|
182
|
+
/* --- Source code sections -------------------- */
|
183
|
+
|
184
|
+
a.source-toggle { font-size: 90%; }
|
185
|
+
div.method-source-code {
|
186
|
+
background: #262626;
|
187
|
+
color: #ffdead;
|
188
|
+
margin: 1em;
|
189
|
+
padding: 0.5em;
|
190
|
+
border: 1px dashed #999;
|
191
|
+
overflow: hidden;
|
192
|
+
}
|
193
|
+
|
194
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
+
|
196
|
+
/* --- Ruby keyword styles --------------------- */
|
197
|
+
|
198
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
+
|
200
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
data/lib/gem_plugin.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# Implements a dynamic plugin loading, configuration, and discovery system
|
5
|
+
# based on RubyGems and a simple additional name space that looks like a URI.
|
6
|
+
#
|
7
|
+
# A plugin is created and put into a category with the following code:
|
8
|
+
#
|
9
|
+
# class MyThing < GemPlugin::Plugin "/things"
|
10
|
+
# ...
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# What this does is sets up your MyThing in the plugin registry via GemPlugin::Manager.
|
14
|
+
# You can then later get this plugin with GemPlugin::Manager.create("/things/mything")
|
15
|
+
# and can also pass in options as a second parameter.
|
16
|
+
#
|
17
|
+
# This isn't such a big deal, but the power is really from the GemPlugin::Manager.load
|
18
|
+
# method. This method will go through the installed gems and require_gem any
|
19
|
+
# that depend on the gem_plugin RubyGem. You can arbitrarily include or exclude
|
20
|
+
# gems based on what they also depend on, thus letting you load these gems when appropriate.
|
21
|
+
#
|
22
|
+
# Since this system was written originally for the Mongrel project that'll be the
|
23
|
+
# best examle of using it.
|
24
|
+
#
|
25
|
+
# Imagine you have a neat plugin for Mongrel called snazzy_command that give the
|
26
|
+
# mongrel_rails a new command snazzy (like: mongrel_rails snazzy). You'd like
|
27
|
+
# people to be able to grab this plugin if they want and use it, because it's snazzy.
|
28
|
+
#
|
29
|
+
# First thing you do is create a gem of your project and make sure that it depends
|
30
|
+
# on "mongrel" AND "gem_plugin". This signals to the GemPlugin system that this is
|
31
|
+
# a plugin for mongrel.
|
32
|
+
#
|
33
|
+
# Next you put this code into a file like lib/init.rb (can be anything really):
|
34
|
+
#
|
35
|
+
# class Snazzy < GemPlugin::Plugin "/commands"
|
36
|
+
# ...
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Then when you create your gem you have the following bits in your Rakefile:
|
40
|
+
#
|
41
|
+
# spec.add_dependency('mongrel', '>= 0.3.9')
|
42
|
+
# spec.add_dependency('gem_plugin', '>= 0.1')
|
43
|
+
# spec.autorequire = 'init.rb'
|
44
|
+
#
|
45
|
+
# Finally, you just have to now publish this gem for people to install and Mongrel
|
46
|
+
# will "magically" be able to install it.
|
47
|
+
#
|
48
|
+
# The "magic" part though is pretty simple and done via the GemPlugin::Manager.load
|
49
|
+
# method. Read that to see how it is really done.
|
50
|
+
module GemPlugin
|
51
|
+
|
52
|
+
EXCLUDE = true
|
53
|
+
INCLUDE = false
|
54
|
+
|
55
|
+
# This class is used by people who use gem plugins (but don't necessarily make them)
|
56
|
+
# to add plugins to their own systems. It provides a way to load plugins, list them,
|
57
|
+
# and create them as needed.
|
58
|
+
#
|
59
|
+
# It is a singleton so you use like this: GemPlugins::Manager.instance.load
|
60
|
+
class Manager
|
61
|
+
include Singleton
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
@plugins = {}
|
65
|
+
@loaded_gems = []
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# Responsible for going through the list of available gems and loading
|
70
|
+
# any plugins requested. It keeps track of what it's loaded already
|
71
|
+
# and won't load them again.
|
72
|
+
#
|
73
|
+
# It accepts one parameter which is a hash of gem depends that should include
|
74
|
+
# or exclude a gem from being loaded. A gem must depend on gem_plugin to be
|
75
|
+
# considered, but then each system has to add it's own INCLUDE to make sure
|
76
|
+
# that only plugins related to it are loaded.
|
77
|
+
#
|
78
|
+
# An example again comes from Mongrel. In order to load all Mongrel plugins:
|
79
|
+
#
|
80
|
+
# GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE
|
81
|
+
#
|
82
|
+
# Which will load all plugins that depend on mongrel AND gem_plugin. Now, one
|
83
|
+
# extra thing we do is we delay loading Rails Mongrel plugins until after rails
|
84
|
+
# is configured. Do do this the mongrel_rails script has:
|
85
|
+
#
|
86
|
+
# GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
|
87
|
+
# The only thing to remember is that this is saying "include a plugin if it
|
88
|
+
# depends on gem_plugin, mongrel, but NOT rails". If a plugin also depends on other
|
89
|
+
# stuff then it's loaded just fine. Only gem_plugin, mongrel, and rails are
|
90
|
+
# ever used to determine if it should be included.
|
91
|
+
def load(needs = {})
|
92
|
+
sdir = File.join(Gem.dir, "specifications")
|
93
|
+
gems = Gem::SourceIndex.from_installed_gems(sdir)
|
94
|
+
needs = needs.merge({"gem_plugin" => INCLUDE})
|
95
|
+
|
96
|
+
gems.each do |path, gem|
|
97
|
+
# don't load gems more than once
|
98
|
+
next if @loaded_gems.include? gem.name
|
99
|
+
check = needs.dup
|
100
|
+
|
101
|
+
# rolls through the depends and inverts anything it finds
|
102
|
+
gem.dependencies.each do |dep|
|
103
|
+
# this will fail if a gem is depended more than once
|
104
|
+
if check.has_key? dep.name
|
105
|
+
check[dep.name] = !check[dep.name]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# now since excluded gems start as true, inverting them
|
110
|
+
# makes them false so we'll skip this gem if any excludes are found
|
111
|
+
if (check.select {|name,test| !test}).length == 0
|
112
|
+
# looks like no needs were set to false, so it's good
|
113
|
+
require_gem gem.name
|
114
|
+
@loaded_gems << gem.name
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# Not necessary for you to call directly, but this is
|
122
|
+
# how GemPlugin::Base.inherited actually adds a
|
123
|
+
# plugin to a category.
|
124
|
+
def register(category, name, klass)
|
125
|
+
@plugins[category] ||= {}
|
126
|
+
@plugins[category][name.downcase] = klass
|
127
|
+
end
|
128
|
+
|
129
|
+
# Resolves the given name (should include /category/name) to
|
130
|
+
# find the plugin class and create an instance. You can
|
131
|
+
# pass a second hash option that is then given to the Plugin
|
132
|
+
# to configure it.
|
133
|
+
def create(name, options = {})
|
134
|
+
last_slash = name.rindex("/")
|
135
|
+
category = name[0 ... last_slash]
|
136
|
+
plugin = name[last_slash .. -1]
|
137
|
+
|
138
|
+
map = @plugins[category]
|
139
|
+
if not map
|
140
|
+
raise "Plugin category #{category} does not exist"
|
141
|
+
elsif not map.has_key? plugin
|
142
|
+
raise "Plugin #{plugin} does not exist in category #{category}"
|
143
|
+
else
|
144
|
+
map[plugin].new(options)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# Returns a map of URIs->{"name" => Plugin} that you can
|
150
|
+
# use to investigate available handlers.
|
151
|
+
def available
|
152
|
+
return @plugins
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
# This base class for plugins reallys does nothing
|
158
|
+
# more than wire up the new class into the right category.
|
159
|
+
# It is not thread-safe yet but will be soon.
|
160
|
+
class Base
|
161
|
+
|
162
|
+
attr_reader :options
|
163
|
+
|
164
|
+
|
165
|
+
# See Mongrel::Plugin for an explanation.
|
166
|
+
def Base.inherited(klass)
|
167
|
+
name = "/" + klass.to_s.downcase
|
168
|
+
Manager.instance.register(@@category, name, klass)
|
169
|
+
@@category = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
# See Mongrel::Plugin for an explanation.
|
173
|
+
def Base.category=(category)
|
174
|
+
@@category = category
|
175
|
+
end
|
176
|
+
|
177
|
+
def initialize(options = {})
|
178
|
+
@options = options
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
# This nifty function works with the GemPlugin::Base to give you
|
184
|
+
# the syntax:
|
185
|
+
#
|
186
|
+
# class MyThing < GemPlugin::Plugin "/things"
|
187
|
+
# ...
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# What it does is temporarily sets the GemPlugin::Base.category, and then
|
191
|
+
# returns GemPlugin::Base. Since the next immediate thing Ruby does is
|
192
|
+
# use this returned class to create the new class, GemPlugin::Base.inherited
|
193
|
+
# gets called. GemPlugin::Base.inherited then uses the set category, class name,
|
194
|
+
# and class to register the plugin in the right way.
|
195
|
+
def GemPlugin::Plugin(c)
|
196
|
+
Base.category = c
|
197
|
+
Base
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
|
204
|
+
|