pluggability 0.4.3 → 0.8.0
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.
- 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