pluggability 0.4.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/History.md +87 -0
- data/{README.rdoc → README.md} +64 -48
- data/lib/pluggability.rb +47 -58
- data/spec/helpers.rb +1 -0
- data/spec/pluggability_spec.rb +65 -31
- data.tar.gz.sig +0 -0
- metadata +51 -130
- metadata.gz.sig +0 -0
- data/.gemtest +0 -0
- data/ChangeLog +0 -868
- data/History.rdoc +0 -54
- data/Manifest.txt +0 -8
- data/Rakefile +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4484023e3e262179c2c404956a61d7b2f45e39a4f35ec3a467ebb58487248a8c
|
4
|
+
data.tar.gz: 18c4dc97074347467326b56caaee97d198224179b5a2b927653999f3ef65cca6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c91ad9882dbe4106aeb597d48400a5e2ca9e83fb89ac3961f6cacbd20bd2fdeb437e4b3e1e16bcefe4822c468865c91b82ba12dbdb311f6add6728de95dd46b4
|
7
|
+
data.tar.gz: 74b488aee0aae9ff89b74631f1a412b27356a51f3e62653d61715226871bd685ae9051880d5f26c573049b9944fbc50b239b86becae8b185d19e7e373180d2d7
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/History.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Release History for pluggability
|
2
|
+
|
3
|
+
---
|
4
|
+
## v0.8.0 [2022-12-01] Michael Granger <ged@faeriemud.org>
|
5
|
+
|
6
|
+
Improvements:
|
7
|
+
|
8
|
+
- Change default Ruby to 3.1
|
9
|
+
- Fix support for classes with number in their name
|
10
|
+
|
11
|
+
|
12
|
+
## v0.7.0 [2020-02-05] Michael Granger <ged@faeriemud.org>
|
13
|
+
|
14
|
+
Improvements:
|
15
|
+
|
16
|
+
- Updated for Ruby 2.7
|
17
|
+
|
18
|
+
|
19
|
+
## v0.6.0 [2018-03-12] Michael Granger <ged@FaerieMUD.org>
|
20
|
+
|
21
|
+
Bugfix:
|
22
|
+
|
23
|
+
- Switch back to require for loading derivatives
|
24
|
+
|
25
|
+
|
26
|
+
## v0.5.0 [2018-01-19] Michael Granger <ged@FaerieMUD.org>
|
27
|
+
|
28
|
+
Enhancements:
|
29
|
+
|
30
|
+
- Update the mechanism used to search for derivatives for more-modern Rubygems,
|
31
|
+
Bundler, etc.
|
32
|
+
|
33
|
+
|
34
|
+
## v0.4.3 [2015-03-04] Michael Granger <ged@FaerieMUD.org>
|
35
|
+
|
36
|
+
Bugfix:
|
37
|
+
|
38
|
+
- Add a workaround for older Rubygems to avoid Bundler problems.
|
39
|
+
|
40
|
+
|
41
|
+
## v0.4.2 [2015-03-04] Michael Granger <ged@FaerieMUD.org>
|
42
|
+
|
43
|
+
Bugfixes:
|
44
|
+
|
45
|
+
- Set the minimum Rubygems version for #find_latest_files support [#1].
|
46
|
+
|
47
|
+
|
48
|
+
## v0.4.1 [2015-03-03] Mahlon E. Smith <mahlon@martini.nu>
|
49
|
+
|
50
|
+
Bugfix:
|
51
|
+
|
52
|
+
- Only consider the latest versions of each installed gem
|
53
|
+
when finding files to load for .load_all.
|
54
|
+
|
55
|
+
|
56
|
+
## v0.4.0 [2014-01-08] Michael Granger <ged@FaerieMUD.org>
|
57
|
+
|
58
|
+
- Add a name attribute to plugins for introspection.
|
59
|
+
|
60
|
+
|
61
|
+
## v0.3.0 [2013-09-25] Michael Granger <ged@FaerieMUD.org>
|
62
|
+
|
63
|
+
- Add plugin exclusion patterns
|
64
|
+
|
65
|
+
|
66
|
+
## v0.2.0 [2013-03-28] Michael Granger <ged@FaerieMUD.org>
|
67
|
+
|
68
|
+
- Fix loading of grandchildren of plugins
|
69
|
+
- Rename Pluggability::FactoryError to
|
70
|
+
Pluggability::PluginError (with backward-compatibility aliases)
|
71
|
+
|
72
|
+
|
73
|
+
## v0.1.0 [2013-03-27] Michael Granger <ged@FaerieMUD.org>
|
74
|
+
|
75
|
+
- Add loading via underbarred name variants (CommaDelimitedThing ->
|
76
|
+
comma_delimited)
|
77
|
+
- Rename some stuff for consistency.
|
78
|
+
|
79
|
+
## v0.0.2 [2012-08-13] Michael Granger <ged@FaerieMUD.org>
|
80
|
+
|
81
|
+
Simplify Pluggability#derivatives.
|
82
|
+
|
83
|
+
|
84
|
+
## v0.0.1 [2012-08-03] Michael Granger <ged@FaerieMUD.org>
|
85
|
+
|
86
|
+
First release after renaming from PluginFactory.
|
87
|
+
|
data/{README.rdoc → README.md}
RENAMED
@@ -1,22 +1,35 @@
|
|
1
|
-
|
1
|
+
# pluggability
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
github :: http://github.com/ged/pluggability
|
3
|
+
home
|
4
|
+
: https://hg.sr.ht/~ged/Pluggability
|
6
5
|
|
6
|
+
docs
|
7
|
+
: https://deveiate.org/code/pluggability
|
7
8
|
|
8
|
-
|
9
|
+
code
|
10
|
+
: https://hg.sr.ht/~ged/Pluggability/browse
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
by name. This is useful when you have an abstract base class which
|
13
|
-
defines an interface and basic functionality for a part of a larger
|
14
|
-
system, and a collection of subclasses which implement the interface for
|
15
|
-
different underlying functionality.
|
12
|
+
github
|
13
|
+
: https://github.com/ged/pluggability
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
|
16
|
+
## Description
|
17
|
+
|
18
|
+
Pluggability is a toolkit for creating plugins.
|
19
|
+
|
20
|
+
It provides a mixin that extends your class with methods to load and instantiate its subclasses by name. So instead of:
|
21
|
+
|
22
|
+
require 'acme/adapter/png'
|
23
|
+
png_adapter = Acme::Adapter::PNG.new( 'file.png' )
|
24
|
+
|
25
|
+
you can do:
|
26
|
+
|
27
|
+
require 'acme/adapter'
|
28
|
+
png_adapter = Acme::Adapter.create( :png, 'file.png' )
|
29
|
+
|
30
|
+
A full example of where this might be useful is in a program which generates
|
31
|
+
output with a 'driver' object, which provides a unified interface but generates
|
32
|
+
different kinds of output.
|
20
33
|
|
21
34
|
First the abstract base class, which is extended with Pluggability:
|
22
35
|
|
@@ -26,7 +39,7 @@ First the abstract base class, which is extended with Pluggability:
|
|
26
39
|
|
27
40
|
class MyGem::Driver
|
28
41
|
extend Pluggability
|
29
|
-
plugin_prefixes "drivers"
|
42
|
+
plugin_prefixes "mygem/drivers"
|
30
43
|
end
|
31
44
|
|
32
45
|
We can have one driver that outputs PDF documents:
|
@@ -60,14 +73,13 @@ it by its short name:
|
|
60
73
|
ascii_driver = MyGem::Driver.create( :ascii, :columns => 80 )
|
61
74
|
|
62
75
|
|
63
|
-
|
76
|
+
### How Plugins Are Loaded
|
64
77
|
|
65
|
-
The
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
formats:
|
78
|
+
The `create` class method added to your class by Pluggability searches for your
|
79
|
+
module using several different strategies. It tries various permutations of the
|
80
|
+
base class's name in combination with the derivative requested. For example,
|
81
|
+
assume we want to make a `LogReader` base class, and then use plugins to define
|
82
|
+
readers for different log formats:
|
71
83
|
|
72
84
|
require 'pluggability'
|
73
85
|
|
@@ -88,11 +100,10 @@ Pluggability searches for modules with the following names:
|
|
88
100
|
apache_log_reader
|
89
101
|
apache
|
90
102
|
|
91
|
-
Obviously the last one might load something other than what is intended,
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
searching for a subclass:
|
103
|
+
Obviously the last one might load something other than what is intended, so you
|
104
|
+
can also tell Pluggability that plugins should be loaded from a subdirectory by
|
105
|
+
declaring one or more `plugin_prefixes` in the base class. Each prefix will be
|
106
|
+
tried (in the order they're declared) when searching for a subclass:
|
96
107
|
|
97
108
|
class LogReader
|
98
109
|
extend Pluggability
|
@@ -134,16 +145,16 @@ will change the search to include:
|
|
134
145
|
'logreader/apache'
|
135
146
|
'logreader/Apache'
|
136
147
|
|
137
|
-
If the plugin is not found, a Pluggability::PluginError is raised, and
|
138
|
-
|
148
|
+
If the plugin is not found, a Pluggability::PluginError is raised, and the
|
149
|
+
message will list all the permutations that were tried.
|
139
150
|
|
140
151
|
|
141
|
-
|
152
|
+
### Preloaded Plugins
|
142
153
|
|
143
|
-
Sometimes you don't want to wait for plugins to be loaded on demand. For
|
144
|
-
|
145
|
-
|
146
|
-
|
154
|
+
Sometimes you don't want to wait for plugins to be loaded on demand. For that
|
155
|
+
case, Pluggability provides the Pluggability#load_all method. This will find
|
156
|
+
all possible matches for plugin files and load them, returning an Array of all
|
157
|
+
the loaded classes:
|
147
158
|
|
148
159
|
class Template::Tag
|
149
160
|
extend Pluggability
|
@@ -153,11 +164,10 @@ them, returning an Array of all the loaded classes:
|
|
153
164
|
tag_classes = Template::Tag.load_all
|
154
165
|
|
155
166
|
|
156
|
-
|
167
|
+
### Excluding Some Files
|
157
168
|
|
158
|
-
You can also prevent some files from being automatically loaded by
|
159
|
-
|
160
|
-
load_all[rdoc-ref:Pluggability#load_all] by setting one or more exclusion
|
169
|
+
You can also prevent some files from being automatically loaded by either
|
170
|
+
Pluggability#create or Pluggability#load_all by setting one or more exclusion
|
161
171
|
patterns:
|
162
172
|
|
163
173
|
LogReader.plugin_exclusions 'spec/*', %r{/test/}
|
@@ -165,11 +175,11 @@ patterns:
|
|
165
175
|
The patterns can either be Regexps or glob Strings.
|
166
176
|
|
167
177
|
|
168
|
-
|
178
|
+
### Logging
|
169
179
|
|
170
|
-
If you need a little more insight into what's going on, Pluggability
|
171
|
-
|
172
|
-
|
180
|
+
If you need a little more insight into what's going on, Pluggability uses the
|
181
|
+
[Loggability](https://rubygems.org/gems/loggability) library. Just set the log
|
182
|
+
level to 'debug' and it'll explain what's going on:
|
173
183
|
|
174
184
|
require 'pluggability'
|
175
185
|
require 'loggability'
|
@@ -213,16 +223,16 @@ this might generate a log that looks (something) like:
|
|
213
223
|
"ringbufferlogreader", "ringbufferLogReader", "ringbuffer"]
|
214
224
|
|
215
225
|
|
216
|
-
|
226
|
+
## Installation
|
217
227
|
|
218
228
|
gem install pluggability
|
219
229
|
|
220
230
|
|
221
|
-
|
231
|
+
## Contributing
|
222
232
|
|
223
233
|
You can check out the current development source with Mercurial via its
|
224
|
-
|
225
|
-
|
234
|
+
[Mercurial repo](https://bitbucket.org/ged/pluggability). Or if you prefer Git,
|
235
|
+
via [its Github mirror](https://github.com/ged/pluggability).
|
226
236
|
|
227
237
|
After checking out the source, run:
|
228
238
|
|
@@ -232,9 +242,15 @@ This task will install any missing dependencies, run the tests/specs,
|
|
232
242
|
and generate the API documentation.
|
233
243
|
|
234
244
|
|
235
|
-
|
245
|
+
## Authors
|
246
|
+
|
247
|
+
- Michael Granger <ged@faeriemud.org>
|
248
|
+
- Martin Chase <outofculture@gmail.com>
|
249
|
+
|
250
|
+
|
251
|
+
## License
|
236
252
|
|
237
|
-
Copyright (c) 2008-
|
253
|
+
Copyright (c) 2008-2020, Michael Granger and Martin Chase
|
238
254
|
All rights reserved.
|
239
255
|
|
240
256
|
Redistribution and use in source and binary forms, with or without
|
data/lib/pluggability.rb
CHANGED
@@ -12,7 +12,7 @@ module Pluggability
|
|
12
12
|
|
13
13
|
|
14
14
|
# Library version
|
15
|
-
VERSION = '0.
|
15
|
+
VERSION = '0.8.0'
|
16
16
|
|
17
17
|
|
18
18
|
# An exception class for Pluggability specific errors.
|
@@ -170,7 +170,7 @@ module Pluggability
|
|
170
170
|
|
171
171
|
simple_name = subclass.name.sub( /^.*::/i, '' ).sub( /\W+$/, '' )
|
172
172
|
keys << simple_name << simple_name.downcase
|
173
|
-
keys << simple_name.gsub( /([a-
|
173
|
+
keys << simple_name.gsub( /([a-z0-9])([A-Z])/, "\\1_\\2" ).downcase
|
174
174
|
|
175
175
|
# Handle class names like 'FooBar' for 'Bar' factories.
|
176
176
|
Pluggability.log.debug "Inherited %p for %p-type plugins" % [ subclass, self.plugin_type ]
|
@@ -273,12 +273,13 @@ module Pluggability
|
|
273
273
|
else
|
274
274
|
Gem.find_files( glob )
|
275
275
|
end
|
276
|
+
|
276
277
|
Pluggability.log.debug " found %d matching files" % [ candidates.length ]
|
277
278
|
next if candidates.empty?
|
278
279
|
|
279
280
|
candidates.each do |path|
|
280
281
|
next if self.is_excluded_path?( path )
|
281
|
-
require( path )
|
282
|
+
Kernel.require( path )
|
282
283
|
end
|
283
284
|
end
|
284
285
|
|
@@ -286,15 +287,13 @@ module Pluggability
|
|
286
287
|
end
|
287
288
|
|
288
289
|
|
289
|
-
### Calculates an appropriate filename for the derived class using the
|
290
|
-
###
|
291
|
-
###
|
292
|
-
###
|
293
|
-
###
|
294
|
-
###
|
295
|
-
### <tt>
|
296
|
-
### require line is tried with both <tt>'foo/'</tt> and <tt>'bar/'</tt>
|
297
|
-
### prepended to it.
|
290
|
+
### Calculates an appropriate filename for the derived class using the name of
|
291
|
+
### the base class and tries to load it via <tt>load</tt>. If the including
|
292
|
+
### class responds to a method named <tt>plugin_prefixes</tt>, its return value
|
293
|
+
### (either a String, or an array of Strings) is added to the list of prefix
|
294
|
+
### directories to try when attempting to load modules. Eg., if
|
295
|
+
### <tt>class.plugin_prefixes</tt> returns <tt>['foo','bar']</tt> the require
|
296
|
+
### line is tried with both <tt>'foo/'</tt> and <tt>'bar/'</tt> prepended to it.
|
298
297
|
def load_derivative( class_name )
|
299
298
|
Pluggability.log.debug "Loading derivative #{class_name}"
|
300
299
|
|
@@ -334,58 +333,48 @@ module Pluggability
|
|
334
333
|
|
335
334
|
|
336
335
|
### Search for the module with the specified <tt>mod_name</tt>, using any
|
337
|
-
### #plugin_prefixes that have been set.
|
336
|
+
### #plugin_prefixes that have been set. Return the path that was required.
|
338
337
|
def require_derivative( mod_name )
|
339
|
-
|
340
|
-
|
341
|
-
subdirs << '' if subdirs.empty?
|
342
|
-
Pluggability.log.debug "Subdirs are: %p" % [subdirs]
|
343
|
-
fatals = []
|
344
|
-
tries = []
|
345
|
-
|
346
|
-
# Iterate over the subdirs until we successfully require a
|
347
|
-
# module.
|
348
|
-
subdirs.map( &:strip ).each do |subdir|
|
349
|
-
self.make_require_path( mod_name, subdir ).each do |path|
|
350
|
-
next if self.is_excluded_path?( path )
|
351
|
-
|
352
|
-
Pluggability.logger.debug "Trying #{path}..."
|
353
|
-
tries << path
|
354
|
-
|
355
|
-
# Try to require the module, saving errors and jumping
|
356
|
-
# out of the catch block on success.
|
357
|
-
begin
|
358
|
-
require( path.untaint )
|
359
|
-
rescue LoadError => err
|
360
|
-
Pluggability.log.debug "No module at '%s', trying the next alternative: '%s'" %
|
361
|
-
[ path, err.message ]
|
362
|
-
rescue Exception => err
|
363
|
-
fatals << err
|
364
|
-
Pluggability.log.error "Found '#{path}', but encountered an error: %s\n\t%s" %
|
365
|
-
[ err.message, err.backtrace.join("\n\t") ]
|
366
|
-
else
|
367
|
-
Pluggability.log.info "Loaded '#{path}' without error."
|
368
|
-
return path
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
Pluggability.logger.debug "fatals = %p" % [ fatals ]
|
374
|
-
|
375
|
-
# Re-raise is there was a file found, but it didn't load for
|
376
|
-
# some reason.
|
377
|
-
if fatals.empty?
|
338
|
+
plugin_path = self.find_plugin_path( mod_name )
|
339
|
+
unless plugin_path
|
378
340
|
errmsg = "Couldn't find a %s named '%s': tried %p" % [
|
379
341
|
self.plugin_type,
|
380
342
|
mod_name,
|
381
|
-
|
382
|
-
|
343
|
+
self.plugin_path_candidates( mod_name )
|
344
|
+
]
|
383
345
|
Pluggability.log.error( errmsg )
|
384
|
-
raise PluginError, errmsg
|
385
|
-
else
|
386
|
-
Pluggability.log.debug "Re-raising first fatal error"
|
387
|
-
Kernel.raise( fatals.first )
|
346
|
+
raise Pluggability::PluginError, errmsg
|
388
347
|
end
|
348
|
+
|
349
|
+
Kernel.require( plugin_path )
|
350
|
+
|
351
|
+
return plugin_path
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
### Search for the file that corresponds to +mod_name+ using the plugin prefixes
|
356
|
+
### and current Gem load path and return the path to the first candidate that
|
357
|
+
### exists.
|
358
|
+
def find_plugin_path( mod_name )
|
359
|
+
candidates = self.plugin_path_candidates( mod_name )
|
360
|
+
Pluggability.log.debug "Candidates for %p are: %p" % [ mod_name, candidates ]
|
361
|
+
|
362
|
+
candidate_paths = candidates.
|
363
|
+
flat_map {|path| Gem.find_latest_files( path ) }.
|
364
|
+
reject {|path| self.is_excluded_path?( path ) || ! File.file?(path) }
|
365
|
+
Pluggability.log.debug "Valid candidates in the current gemset: %p" % [ candidate_paths ]
|
366
|
+
|
367
|
+
return candidate_paths.first
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
### Return an Array of all the filenames a plugin of the given +mod_name+ might
|
372
|
+
### map to given the current plugin_prefixes.
|
373
|
+
def plugin_path_candidates( mod_name )
|
374
|
+
prefixes = self.plugin_prefixes
|
375
|
+
prefixes << '' if prefixes.empty?
|
376
|
+
|
377
|
+
return prefixes.flat_map {|pre| self.make_require_path(mod_name, pre) }
|
389
378
|
end
|
390
379
|
|
391
380
|
|
data/spec/helpers.rb
CHANGED