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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 74b416f4c7869bb0e92694c2cf9204934eb1c34e
4
- data.tar.gz: afc7030991affa25440542dbcc8b2034d5b30a03
2
+ SHA256:
3
+ metadata.gz: 4484023e3e262179c2c404956a61d7b2f45e39a4f35ec3a467ebb58487248a8c
4
+ data.tar.gz: 18c4dc97074347467326b56caaee97d198224179b5a2b927653999f3ef65cca6
5
5
  SHA512:
6
- metadata.gz: baa2b85007768cdcb9737f652b0bff158dfe8126b110894a0f534950ce4d8b0b428794d9567593f261856db5d3f7b64430f01429f03fd06fde016bbc9053c2d5
7
- data.tar.gz: 3073b7764f21c6c25bbf272e5802dcdcf20ea6dcf2af4a14f8e42531c8e50d1d77e520afeee87c2319f40d94d678347654e4ab1c1e506ce89e0790640295443e
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
+
@@ -1,22 +1,35 @@
1
- = pluggability
1
+ # pluggability
2
2
 
3
- project:: https://bitbucket.org/ged/pluggability
4
- docs :: http://deveiate.org/code/pluggability
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
- == Description
9
+ code
10
+ : https://hg.sr.ht/~ged/Pluggability/browse
9
11
 
10
- Pluggability is a mixin module that turns an including class into a
11
- factory for its derivatives, capable of searching for and loading them
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
- An example of where this might be useful is in a program which generates
18
- output with a 'driver' object, which provides a unified interface but
19
- generates different kinds of output.
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", "drivers/compat"
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
- === How Plugins Are Loaded
76
+ ### How Plugins Are Loaded
64
77
 
65
- The +create+ class method added to your class by Pluggability searches
66
- for your module using several different strategies. It tries various
67
- permutations of the base class's name in combination with the derivative
68
- requested. For example, assume we want to make a +LogReader+ base
69
- class, and then use plugins to define readers for different log
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
- so you can also tell Pluggability that plugins should be loaded from a
93
- subdirectory by declaring one or more +plugin_prefixes+ in the base
94
- class. Each prefix will be tried (in the order they're declared) when
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
- the message will list all the permutations that were tried.
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
- === Preloaded Plugins
152
+ ### Preloaded Plugins
142
153
 
143
- Sometimes you don't want to wait for plugins to be loaded on demand. For
144
- that case, Pluggability provides the load_all[rdoc-ref:Pluggability#load_all]
145
- method. This will find all possible matches for plugin files and load
146
- them, returning an Array of all the loaded classes:
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
- === Excluding Some Files
167
+ ### Excluding Some Files
157
168
 
158
- You can also prevent some files from being automatically loaded by
159
- either create[rdoc-ref:Pluggability#create] or
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
- === Logging
178
+ ### Logging
169
179
 
170
- If you need a little more insight into what's going on, Pluggability
171
- uses the Loggability[https://rubygems.org/gems/loggability] library.
172
- Just set the log level to 'debug' and it'll explain what's going on:
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
- == Installation
226
+ ## Installation
217
227
 
218
228
  gem install pluggability
219
229
 
220
230
 
221
- == Contributing
231
+ ## Contributing
222
232
 
223
233
  You can check out the current development source with Mercurial via its
224
- {Mercurial repo}[https://bitbucket.org/ged/pluggability]. Or if you
225
- prefer Git, via {its Github mirror}[https://github.com/ged/pluggability].
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
- == License
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-2013, Michael Granger and Martin Chase
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.4.3'
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-z])([A-Z])/, "\\1_\\2" ).downcase
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
- ### name of the base class and tries to load it via <tt>require</tt>. If
291
- ### the including class responds to a method named
292
- ### <tt>derivativeDirs</tt>, its return value (either a String, or an
293
- ### array of Strings) is added to the list of prefix directories to try
294
- ### when attempting to require a modules. Eg., if
295
- ### <tt>class.derivativeDirs</tt> returns <tt>['foo','bar']</tt> the
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
- subdirs = self.plugin_prefixes
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
- tries
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
@@ -11,6 +11,7 @@ RSpec.configure do |config|
11
11
  config.run_all_when_everything_filtered = true
12
12
  config.filter_run :focus
13
13
  config.order = 'random'
14
+ config.warnings = true
14
15
  config.mock_with( :rspec ) do |mock_config|
15
16
  mock_config.syntax = :expect
16
17
  end