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 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