gem_plugin 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ Ruby is copyrighted free software by Zed A. Shaw <zedshaw at zedshaw dot com>
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) rename any non-standard executables so the names do not conflict
21
+ with standard executables, which must also be provided.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or executable
26
+ form, provided that you do at least ONE of the following:
27
+
28
+ a) distribute the executables and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard executables non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under this terms.
43
+
44
+ They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
45
+ files under the ./missing directory. See each file for the copying
46
+ condition.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
58
+
data/README ADDED
@@ -0,0 +1,115 @@
1
+ = GemPlugin: Gem Based Plugin System
2
+
3
+ GemPlugin is a system that lets your users install gems and lets you load
4
+ them as additional features to use in your software. It originated from the
5
+ Mongrel (http://mongrel.rubyforge.org) project but proved useful enough to
6
+ break out into a separate project.
7
+
8
+ GemPlugin works by listing the gems installed, and doing a require_gem on
9
+ any that have the right dependencies. For example, if a gem depends on
10
+ "gem_plugin" and "mongrel" then it'll load as a Mongrel plugin. This
11
+ makes it so that users of the plugins only need to gem install (and maybe
12
+ config a bit), and plugin authors only need to make gems.
13
+
14
+
15
+ == Implementers
16
+
17
+ To use GemPlugin in your system you only have to require 'gem_plugin' and
18
+ then use the GemPlugin::Manager.create, GemPlugin::Manager.load, and
19
+ GemPlugin::Manager.available methods to work with them.
20
+
21
+ * GemPlugin::Manager.load -- Takes a "depend include/exclude map" and loads plugins based on it.
22
+ * GemPlugin::Manager.create -- Takes a URI style name and some options then creates one for you.
23
+ * GemPlugin::Manager.available -- Lets you inspect and mess with the internal plugin registry.
24
+
25
+ === Loading Plugins
26
+
27
+ As an example from Mongrel it's necessary to load plugins that depend on rails after
28
+ the Rails system is configured, but load other plugins right when Mongrel is ready.
29
+ To do this we very first do:
30
+
31
+ GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
32
+
33
+ Later, when it's ready to load Rails plugins as well we do this:
34
+
35
+ GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE
36
+
37
+ This simply loads any plugins that remain and are ready for use in Rails.
38
+
39
+ === Creating Plugins
40
+
41
+ Creating a plugin is cake:
42
+
43
+ plug = GemPlugin::Manager.instance.create("/commands/snazzy", "something" => "yeah")
44
+
45
+ In this case we're making the snazzy command and passing a couple fake options.
46
+
47
+ === Finding Available Plugins
48
+
49
+ Finding plugins is also very easy, you just call GemPlugin::Manager.instance.available
50
+ and you get a Hash that maps categories to name => class. For example, if I had
51
+ the "/commands/snazzy" plugin registered above, then I'd get the following:
52
+
53
+ puts GemPlugin::Manager.instance.available["/commands"].inspect
54
+ -> { "/snazzy" => Snazzy}
55
+
56
+
57
+ === Plugins Inside Modules
58
+
59
+ Plugins that are placed in modules are also lowercased when registered but
60
+ still retain their module. So, if Snazzy was actually MyModule::Snazzy, then
61
+ it'd be registered as "/commands/mymodule::snazzy".
62
+
63
+
64
+ == Plugin Authors
65
+
66
+ People who wish to write gem plugins have a faily easy time of it, but need
67
+ to know the particular rules for the target system. To keep this example
68
+ concrete we'll assume you want to write a Mongrel command plugin.
69
+
70
+ First thing is create your project like normal and setup Rake to make
71
+ your gem. Your plugin then needs to be created like so:
72
+
73
+ class Snazzy < GemPlugin::Plugin "/commands"
74
+ ...
75
+ end
76
+
77
+ And place this code in a file you will have RubyGems autorequire (I use lib/init.rb).
78
+
79
+ Next you need to add the following to whatever Rakefile code you use to create
80
+ your gem:
81
+
82
+ spec.add_dependency('gem_plugin', '>= 0.1')
83
+ spec.add_dependency('mongrel', '>= 0.3.9')
84
+ spec.autorequire = 'init.rb'
85
+
86
+ This does three things:
87
+
88
+ * Tells GemPlugins::Manager.load that this is a GemPlugin.
89
+ * Tells Mongrel that this is a Mongrel specific GemPlugin.
90
+ * Tells RubyGems to run init.rb when the gem is required, just hooking up your plugin.
91
+
92
+ Now, all the users of your plugin have to do is gem install it and then
93
+ they get the plugin automagically.
94
+
95
+ People writing GemPlugins for other systems would have to check the
96
+ documentation from that project to get an idea of what extra
97
+ requirements might be needed. For example, you'd probably have to
98
+ depend on another project other that *mongrel* and most likely have
99
+ a few more things to configure in your init.rb.
100
+
101
+ == Plugin Users
102
+
103
+ Plugin users have it the easiest of all. They simply do:
104
+
105
+ gem install mongrel_command_snazzy
106
+
107
+ And that's it. When they run mongrel_rails (given the above example)
108
+ this snazzy command get loaded automatically without any intervention.
109
+
110
+ The only thing missing in this release is a way for end users to configure
111
+ such a plugin. I really think this is the job of the implementers to define.
112
+
113
+ == Contact
114
+
115
+ E-mail zedshaw at zedshaw.com and I'll help. Comments about the API are welcome.
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'tools/rakehelp'
7
+ require 'fileutils'
8
+ include FileUtils
9
+
10
+ setup_tests
11
+ setup_clean ["pkg", "lib/*.bundle", "*.gem", ".config"]
12
+
13
+ setup_rdoc ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc']
14
+
15
+ desc "Does a full compile, test run"
16
+ task :default => [:test, :package]
17
+
18
+ version="0.1"
19
+ summary = "A plugin system based only on rubygems"
20
+ test_file = "test/test_plugins.rb"
21
+ author="Zed A. Shaw"
22
+ name="gem_plugin"
23
+ scripts=[]
24
+
25
+ setup_gem(name, version, author, summary, scripts, test_file) do |spec|
26
+ spec.autorequire = "gem_plugin"
27
+ end
28
+
29
+ task :gem_test => [:package] do
30
+ sh %{sudo gem install pkg/gem_plugin-#{version}}
31
+ end
32
+
33
+ task :site => [:rerdoc] do
34
+ sh %{ scp -r doc/rdoc/* #{ENV['SSH_USER']}@rubyforge.org:/var/www/gforge-projects/mongrel/gem_plugin_rdoc/ }
35
+ end
@@ -0,0 +1,247 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
+
6
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
+ <head>
8
+ <title>Module: GemPlugin</title>
9
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
+ <script type="text/javascript">
13
+ // <![CDATA[
14
+
15
+ function popupCode( url ) {
16
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
+ }
18
+
19
+ function toggleCode( id ) {
20
+ if ( document.getElementById )
21
+ elem = document.getElementById( id );
22
+ else if ( document.all )
23
+ elem = eval( "document.all." + id );
24
+ else
25
+ return false;
26
+
27
+ elemStyle = elem.style;
28
+
29
+ if ( elemStyle.display != "block" ) {
30
+ elemStyle.display = "block"
31
+ } else {
32
+ elemStyle.display = "none"
33
+ }
34
+
35
+ return true;
36
+ }
37
+
38
+ // Make codeblocks hidden by default
39
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
+
41
+ // ]]>
42
+ </script>
43
+
44
+ </head>
45
+ <body>
46
+
47
+
48
+
49
+ <div id="classHeader">
50
+ <table class="header-table">
51
+ <tr class="top-aligned-row">
52
+ <td><strong>Module</strong></td>
53
+ <td class="class-name-in-header">GemPlugin</td>
54
+ </tr>
55
+ <tr class="top-aligned-row">
56
+ <td><strong>In:</strong></td>
57
+ <td>
58
+ <a href="../files/lib/gem_plugin_rb.html">
59
+ lib/gem_plugin.rb
60
+ </a>
61
+ <br />
62
+ </td>
63
+ </tr>
64
+
65
+ </table>
66
+ </div>
67
+ <!-- banner header -->
68
+
69
+ <div id="bodyContent">
70
+
71
+
72
+
73
+ <div id="contextContent">
74
+
75
+ <div id="description">
76
+ <p>
77
+ Implements a dynamic plugin loading, configuration, and discovery system
78
+ based on RubyGems and a simple additional name space that looks like a URI.
79
+ </p>
80
+ <p>
81
+ A plugin is created and put into a category with the following code:
82
+ </p>
83
+ <pre>
84
+ class MyThing &lt; GemPlugin::Plugin &quot;/things&quot;
85
+ ...
86
+ end
87
+ </pre>
88
+ <p>
89
+ What this does is sets up your MyThing in the plugin registry via <a
90
+ href="GemPlugin/Manager.html">GemPlugin::Manager</a>. You can then later
91
+ get this plugin with <a
92
+ href="GemPlugin/Manager.html#M000008">GemPlugin::Manager.create</a>(&quot;/things/mything&quot;)
93
+ and can also pass in options as a second parameter.
94
+ </p>
95
+ <p>
96
+ This isn&#8217;t such a big deal, but the power is really from the <a
97
+ href="GemPlugin/Manager.html#M000006">GemPlugin::Manager.load</a> method.
98
+ This method will go through the installed gems and require_gem any that
99
+ depend on the gem_plugin RubyGem. You can arbitrarily include or exclude
100
+ gems based on what they also depend on, thus letting you load these gems
101
+ when appropriate.
102
+ </p>
103
+ <p>
104
+ Since this system was written originally for the Mongrel project
105
+ that&#8217;ll be the best examle of using it.
106
+ </p>
107
+ <p>
108
+ Imagine you have a neat plugin for Mongrel called snazzy_command that give
109
+ the mongrel_rails a new command snazzy (like: mongrel_rails snazzy).
110
+ You&#8216;d like people to be able to grab this plugin if they want and use
111
+ it, because it&#8217;s snazzy.
112
+ </p>
113
+ <p>
114
+ First thing you do is create a gem of your project and make sure that it
115
+ depends on &quot;mongrel&quot; AND &quot;gem_plugin&quot;. This signals to
116
+ the <a href="GemPlugin.html">GemPlugin</a> system that this is a plugin for
117
+ mongrel.
118
+ </p>
119
+ <p>
120
+ Next you put this code into a file like lib/init.rb (can be anything
121
+ really):
122
+ </p>
123
+ <pre>
124
+ class Snazzy &lt; GemPlugin::Plugin &quot;/commands&quot;
125
+ ...
126
+ end
127
+ </pre>
128
+ <p>
129
+ Then when you create your gem you have the following bits in your Rakefile:
130
+ </p>
131
+ <pre>
132
+ spec.add_dependency('mongrel', '&gt;= 0.3.9')
133
+ spec.add_dependency('gem_plugin', '&gt;= 0.1')
134
+ spec.autorequire = 'init.rb'
135
+ </pre>
136
+ <p>
137
+ Finally, you just have to now publish this gem for people to install and
138
+ Mongrel will &quot;magically&quot; be able to install it.
139
+ </p>
140
+ <p>
141
+ The &quot;magic&quot; part though is pretty simple and done via the <a
142
+ href="GemPlugin/Manager.html#M000006">GemPlugin::Manager.load</a> method.
143
+ Read that to see how it is really done.
144
+ </p>
145
+
146
+ </div>
147
+
148
+
149
+ </div>
150
+
151
+ <div id="method-list">
152
+ <h3 class="section-bar">Methods</h3>
153
+
154
+ <div class="name-list">
155
+ <a href="#M000001">Plugin</a>&nbsp;&nbsp;
156
+ </div>
157
+ </div>
158
+
159
+ </div>
160
+
161
+
162
+ <!-- if includes -->
163
+
164
+ <div id="section">
165
+
166
+ <div id="class-list">
167
+ <h3 class="section-bar">Classes and Modules</h3>
168
+
169
+ Class <a href="GemPlugin/Base.html" class="link">GemPlugin::Base</a><br />
170
+ Class <a href="GemPlugin/Manager.html" class="link">GemPlugin::Manager</a><br />
171
+
172
+ </div>
173
+
174
+ <div id="constants-list">
175
+ <h3 class="section-bar">Constants</h3>
176
+
177
+ <div class="name-list">
178
+ <table summary="Constants">
179
+ <tr class="top-aligned-row context-row">
180
+ <td class="context-item-name">EXCLUDE</td>
181
+ <td>=</td>
182
+ <td class="context-item-value">true</td>
183
+ </tr>
184
+ <tr class="top-aligned-row context-row">
185
+ <td class="context-item-name">INCLUDE</td>
186
+ <td>=</td>
187
+ <td class="context-item-value">false</td>
188
+ </tr>
189
+ </table>
190
+ </div>
191
+ </div>
192
+
193
+
194
+
195
+
196
+
197
+
198
+ <!-- if method_list -->
199
+ <div id="methods">
200
+ <h3 class="section-bar">Public Class methods</h3>
201
+
202
+ <div id="method-M000001" class="method-detail">
203
+ <a name="M000001"></a>
204
+
205
+ <div class="method-heading">
206
+ <a href="GemPlugin.src/M000001.html" target="Code" class="method-signature"
207
+ onclick="popupCode('GemPlugin.src/M000001.html');return false;">
208
+ <span class="method-name">Plugin</span><span class="method-args">(c)</span>
209
+ </a>
210
+ </div>
211
+
212
+ <div class="method-description">
213
+ <p>
214
+ This nifty function works with the <a
215
+ href="GemPlugin/Base.html">GemPlugin::Base</a> to give you the syntax:
216
+ </p>
217
+ <pre>
218
+ class MyThing &lt; GemPlugin::Plugin &quot;/things&quot;
219
+ ...
220
+ end
221
+ </pre>
222
+ <p>
223
+ What it does is temporarily sets the GemPlugin::Base.category, and then
224
+ returns <a href="GemPlugin/Base.html">GemPlugin::Base</a>. Since the next
225
+ immediate thing Ruby does is use this returned class to create the new
226
+ class, <a href="GemPlugin/Base.html#M000002">GemPlugin::Base.inherited</a>
227
+ gets called. <a
228
+ href="GemPlugin/Base.html#M000002">GemPlugin::Base.inherited</a> then uses
229
+ the set category, class name, and class to register the plugin in the right
230
+ way.
231
+ </p>
232
+ </div>
233
+ </div>
234
+
235
+
236
+ </div>
237
+
238
+
239
+ </div>
240
+
241
+
242
+ <div id="validator-badges">
243
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
244
+ </div>
245
+
246
+ </body>
247
+ </html>