pluggability 0.8.0 → 0.10.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/History.md +16 -0
- data/lib/pluggability.rb +50 -33
- data/spec/helpers.rb +17 -8
- data/spec/pluggability_spec.rb +39 -7
- data.tar.gz.sig +0 -0
- metadata +27 -28
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acdddb8e543aa6046ffbdbd68d67588eed0cb3ba2ab70bf2f2c1ea8fb5e55e86
|
4
|
+
data.tar.gz: 06e1b5426c52c27250e940a401327a6176ce666d22b7cba988746c85de96cb95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f84aae2fa72d6425a69592bb4ea1fcc37e6e5dc96d9b03ab1f6c5d2954f86806f4316dd4e9baf0cb7111ffb1f9b2d682c43b44305750f84a66afb8faaf738368
|
7
|
+
data.tar.gz: 01bc40509082e86090d3770491e15473b0fdf5cabbb3021ec8bfdf4a9e56b73f579e43cfcebf5909454da4ec1802bae201ad68ea2107281ae7a15bc24e9bfaed
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/History.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Release History for pluggability
|
2
2
|
|
3
3
|
---
|
4
|
+
|
5
|
+
## v0.10.0 [2025-07-12] Michael Granger <ged@faeriemud.org>
|
6
|
+
|
7
|
+
Improvements:
|
8
|
+
|
9
|
+
- Remove support for non-Ruby idiomatic plugin paths
|
10
|
+
- Add support for newer Ruby features, e.g., Module#set_temporary_name.
|
11
|
+
|
12
|
+
|
13
|
+
## v0.9.0 [2023-06-08] Michael Granger <ged@faeriemud.org>
|
14
|
+
|
15
|
+
Improvements:
|
16
|
+
|
17
|
+
- Add support for keyword arguments.
|
18
|
+
|
19
|
+
|
4
20
|
## v0.8.0 [2022-12-01] Michael Granger <ged@faeriemud.org>
|
5
21
|
|
6
22
|
Improvements:
|
data/lib/pluggability.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
-
#encoding: utf-8
|
3
2
|
|
4
3
|
require 'loggability' unless defined?( Loggability )
|
5
4
|
|
5
|
+
|
6
6
|
# The Pluggability module
|
7
7
|
module Pluggability
|
8
8
|
extend Loggability
|
@@ -12,7 +12,7 @@ module Pluggability
|
|
12
12
|
|
13
13
|
|
14
14
|
# Library version
|
15
|
-
VERSION = '0.
|
15
|
+
VERSION = '0.10.0'
|
16
16
|
|
17
17
|
|
18
18
|
# An exception class for Pluggability specific errors.
|
@@ -20,12 +20,26 @@ module Pluggability
|
|
20
20
|
FactoryError = PluginError
|
21
21
|
|
22
22
|
|
23
|
+
module StringRefinements
|
24
|
+
refine( String ) do
|
25
|
+
|
26
|
+
def uncamelcase
|
27
|
+
return self.gsub( /([a-z0-9])([A-Z])/, "\\1_\\2" )
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
using StringRefinements
|
33
|
+
|
34
|
+
|
23
35
|
### Add the @derivatives instance variable to including classes.
|
24
36
|
def self::extend_object( obj )
|
25
37
|
obj.instance_variable_set( :@plugin_prefixes, [] )
|
26
38
|
obj.instance_variable_set( :@plugin_exclusions, [] )
|
27
39
|
obj.instance_variable_set( :@derivatives, {} )
|
28
40
|
|
41
|
+
obj.singleton_class.attr_accessor( :plugin_name )
|
42
|
+
|
29
43
|
Pluggability.pluggable_classes << obj
|
30
44
|
|
31
45
|
super
|
@@ -125,15 +139,30 @@ module Pluggability
|
|
125
139
|
return base.name
|
126
140
|
end
|
127
141
|
end
|
128
|
-
|
142
|
+
alias_method :factory_type, :plugin_type
|
129
143
|
|
130
144
|
|
131
145
|
### Inheritance callback -- Register subclasses in the derivatives hash
|
132
146
|
### so that ::create knows about them.
|
133
147
|
def inherited( subclass )
|
134
|
-
|
148
|
+
Pluggability.log.debug " %p inherited by %p" % [ self, subclass ]
|
149
|
+
self.register_plugin_type( subclass )
|
150
|
+
super
|
151
|
+
end
|
135
152
|
|
136
|
-
|
153
|
+
|
154
|
+
### Override Module#set_temporary_name so +new_name+ can be used to derive the
|
155
|
+
### plugin name. Note that this does *not* detect if you later assign the
|
156
|
+
### anonymous class to a constant and thus clear its temporary name.
|
157
|
+
def set_temporary_name( new_name )
|
158
|
+
super
|
159
|
+
self.register_plugin_type( self )
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
### Register the given +subclass+ as a plugin type of the receiving Pluggable class.
|
164
|
+
def register_plugin_type( subclass )
|
165
|
+
plugin_class = Pluggability.plugin_base_class( subclass )
|
137
166
|
keys = [ subclass ]
|
138
167
|
|
139
168
|
# If it's not an anonymous class, make some keys out of variants of its name
|
@@ -153,13 +182,8 @@ module Pluggability
|
|
153
182
|
plugin_class.derivatives[ key ] = subclass
|
154
183
|
end
|
155
184
|
|
156
|
-
|
157
|
-
|
158
|
-
attr_reader :plugin_name
|
159
|
-
end
|
160
|
-
subclass.instance_variable_set( :@plugin_name, keys.last )
|
161
|
-
|
162
|
-
super
|
185
|
+
Pluggability.log.debug "Setting plugin name of %p to %p" % [ subclass, keys.last ]
|
186
|
+
subclass.plugin_name = keys.last
|
163
187
|
end
|
164
188
|
|
165
189
|
|
@@ -168,19 +192,16 @@ module Pluggability
|
|
168
192
|
def make_derivative_names( subclass )
|
169
193
|
keys = []
|
170
194
|
|
171
|
-
|
195
|
+
# Order is important here, as the last non-nil one becomes the plugin_name.
|
196
|
+
simple_name = subclass.name.sub( /\A.*::/, '' ).sub( /\A(\w+).*/, '\\1' )
|
172
197
|
keys << simple_name << simple_name.downcase
|
173
|
-
keys << simple_name.
|
198
|
+
keys << simple_name.uncamelcase.downcase
|
174
199
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
keys << Regexp.last_match[1].downcase
|
179
|
-
else
|
180
|
-
keys << subclass.name.sub( /.*::/, '' ).downcase
|
181
|
-
end
|
200
|
+
simpler_name = simple_name.sub( /(?:#{self.plugin_type})\z/, '' )
|
201
|
+
keys << simpler_name << simpler_name.downcase
|
202
|
+
keys << simpler_name.uncamelcase.downcase
|
182
203
|
|
183
|
-
return keys
|
204
|
+
return keys.uniq
|
184
205
|
end
|
185
206
|
|
186
207
|
|
@@ -202,11 +223,11 @@ module Pluggability
|
|
202
223
|
### obj = Listener.create( 'FooListener' )
|
203
224
|
### obj = Listener.create( FooListener )
|
204
225
|
### obj = Listener.create( 'Foo' )
|
205
|
-
def create( class_name, *args, &block )
|
226
|
+
def create( class_name, *args, **keyword_args, &block )
|
206
227
|
subclass = get_subclass( class_name )
|
207
228
|
|
208
229
|
begin
|
209
|
-
return subclass.new( *args, &block )
|
230
|
+
return subclass.new( *args, **keyword_args, &block )
|
210
231
|
rescue => err
|
211
232
|
nicetrace = err.backtrace.reject {|frame| /#{__FILE__}/ =~ frame}
|
212
233
|
msg = "When creating '#{class_name}': " + err.message
|
@@ -361,6 +382,7 @@ module Pluggability
|
|
361
382
|
|
362
383
|
candidate_paths = candidates.
|
363
384
|
flat_map {|path| Gem.find_latest_files( path ) }.
|
385
|
+
uniq.
|
364
386
|
reject {|path| self.is_excluded_path?( path ) || ! File.file?(path) }
|
365
387
|
Pluggability.log.debug "Valid candidates in the current gemset: %p" % [ candidate_paths ]
|
366
388
|
|
@@ -374,7 +396,7 @@ module Pluggability
|
|
374
396
|
prefixes = self.plugin_prefixes
|
375
397
|
prefixes << '' if prefixes.empty?
|
376
398
|
|
377
|
-
return prefixes.flat_map {|pre| self.make_require_path(mod_name, pre) }
|
399
|
+
return prefixes.flat_map {|pre| self.make_require_path(mod_name, pre) }.uniq
|
378
400
|
end
|
379
401
|
|
380
402
|
|
@@ -385,17 +407,12 @@ module Pluggability
|
|
385
407
|
### "drivers/SocketDataDriver", "drivers/socket", "drivers/Socket"]
|
386
408
|
def make_require_path( modname, subdir )
|
387
409
|
path = []
|
388
|
-
myname = self.plugin_type
|
410
|
+
myname = self.plugin_type.uncamelcase.downcase
|
411
|
+
modname = modname.uncamelcase.downcase
|
389
412
|
|
390
413
|
# Make permutations of the two parts
|
391
414
|
path << modname
|
392
|
-
path << modname
|
393
|
-
path << modname + myname
|
394
|
-
path << modname.downcase + myname
|
395
|
-
path << modname.downcase + myname.downcase
|
396
|
-
path << modname + '_' + myname
|
397
|
-
path << modname.downcase + '_' + myname
|
398
|
-
path << modname.downcase + '_' + myname.downcase
|
415
|
+
path << modname + '_' + myname
|
399
416
|
|
400
417
|
# If a non-empty subdir was given, prepend it to all the items in the
|
401
418
|
# path
|
data/spec/helpers.rb
CHANGED
@@ -1,19 +1,28 @@
|
|
1
|
-
|
2
|
-
# coding: utf-8
|
1
|
+
# -*- ruby -*-
|
3
2
|
|
4
3
|
require 'rspec'
|
4
|
+
require 'diff/lcs'
|
5
5
|
require 'loggability/spechelpers'
|
6
6
|
require 'pluggability'
|
7
7
|
|
8
|
-
|
8
|
+
# Mock with Rspec
|
9
9
|
RSpec.configure do |config|
|
10
|
+
config.mock_with( :rspec ) do |mock|
|
11
|
+
mock.syntax = :expect
|
12
|
+
end
|
10
13
|
|
11
|
-
config.
|
14
|
+
config.disable_monkey_patching!
|
15
|
+
config.example_status_persistence_file_path = "spec/.status"
|
12
16
|
config.filter_run :focus
|
13
|
-
config.
|
14
|
-
|
15
|
-
config.
|
16
|
-
|
17
|
+
config.filter_run_when_matching :focus
|
18
|
+
config.order = :random
|
19
|
+
config.profile_examples = 5
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
22
|
+
config.warnings = true
|
23
|
+
|
24
|
+
config.expect_with( :rspec ) do |expectations|
|
25
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
17
26
|
end
|
18
27
|
|
19
28
|
config.include( Loggability::SpecHelpers )
|
data/spec/pluggability_spec.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative 'helpers'
|
4
4
|
|
5
5
|
require 'pluggability'
|
6
|
-
|
6
|
+
require 'loggability'
|
7
7
|
|
8
8
|
#
|
9
9
|
# Testing classes
|
@@ -21,6 +21,12 @@ class Carbon14Robot < Plugin; end
|
|
21
21
|
module Test
|
22
22
|
class LoadablePlugin < Plugin; end
|
23
23
|
end
|
24
|
+
class KeywordTest < Plugin
|
25
|
+
def initialize( **kwargs)
|
26
|
+
@kwargs = kwargs
|
27
|
+
end
|
28
|
+
attr_reader :kwargs
|
29
|
+
end
|
24
30
|
|
25
31
|
class SubSubPlugin < SubPlugin; end
|
26
32
|
|
@@ -28,7 +34,7 @@ class SubSubPlugin < SubPlugin; end
|
|
28
34
|
#
|
29
35
|
# Examples
|
30
36
|
#
|
31
|
-
describe Pluggability do
|
37
|
+
RSpec.describe Pluggability do
|
32
38
|
|
33
39
|
before( :each ) do
|
34
40
|
Plugin.plugin_exclusions = []
|
@@ -95,9 +101,9 @@ describe Pluggability do
|
|
95
101
|
at_least( :once ).
|
96
102
|
and_return( ['/some/path/to/plugins/dazzle.rb'] )
|
97
103
|
expect( Kernel ).to receive( :require ).with( '/some/path/to/plugins/dazzle.rb' ) do |*args|
|
98
|
-
loaded_class = Class.new( Plugin )
|
99
|
-
|
100
|
-
|
104
|
+
loaded_class = Class.new( Plugin ) do
|
105
|
+
set_temporary_name 'Plugin::Dazzle (testing class)'
|
106
|
+
end
|
101
107
|
true
|
102
108
|
end
|
103
109
|
|
@@ -105,11 +111,31 @@ describe Pluggability do
|
|
105
111
|
end
|
106
112
|
|
107
113
|
|
114
|
+
it "will load new plugins from the require path if given a camel-cased class name" do
|
115
|
+
loaded_class = nil
|
116
|
+
|
117
|
+
expect( Gem ).to receive( :find_latest_files ).
|
118
|
+
at_least( :once ).
|
119
|
+
and_return( ['/some/path/to/plugins/razzle_dazzle.rb'] )
|
120
|
+
expect( Kernel ).to receive( :require ) do |require_path|
|
121
|
+
expect( require_path ).to eq( '/some/path/to/plugins/razzle_dazzle.rb' )
|
122
|
+
|
123
|
+
loaded_class = Class.new( Plugin ) do
|
124
|
+
set_temporary_name 'Plugin::RazzleDazzle (testing class)'
|
125
|
+
end
|
126
|
+
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
expect( Plugin.create(:RazzleDazzle) ).to be_an_instance_of( loaded_class )
|
131
|
+
end
|
132
|
+
|
133
|
+
|
108
134
|
it "will output a sensible description of what it tried to load if requiring a " +
|
109
135
|
"derivative fails" do
|
110
136
|
|
111
137
|
# at least 6 -> 3 variants * 2 paths
|
112
|
-
expect( Gem ).to receive( :find_latest_files ).at_least(
|
138
|
+
expect( Gem ).to receive( :find_latest_files ).at_least( 4 ).times.
|
113
139
|
and_return( [] )
|
114
140
|
|
115
141
|
expect {
|
@@ -212,6 +238,12 @@ describe Pluggability do
|
|
212
238
|
Plugin.load_all
|
213
239
|
end
|
214
240
|
|
241
|
+
|
242
|
+
it "passes keyword arguments when creating derivatives" do
|
243
|
+
result = Plugin.create( KeywordTest, foo: :bar, baz: 2 )
|
244
|
+
expect( result.kwargs ).to eq({ foo: :bar, baz: 2 })
|
245
|
+
end
|
246
|
+
|
215
247
|
end
|
216
248
|
|
217
249
|
|
@@ -281,7 +313,7 @@ describe Pluggability do
|
|
281
313
|
|
282
314
|
|
283
315
|
it "still knows what the simplest version of its plugin name is" do
|
284
|
-
expect( SubSubPlugin.plugin_name ).to eq( '
|
316
|
+
expect( SubSubPlugin.plugin_name ).to eq( 'sub_sub' )
|
285
317
|
end
|
286
318
|
|
287
319
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,40 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pluggability
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Granger
|
8
8
|
- Martin Chase
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain:
|
12
11
|
- |
|
13
12
|
-----BEGIN CERTIFICATE-----
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
13
|
+
MIIEbDCCAtSgAwIBAgIBATANBgkqhkiG9w0BAQsFADA+MQwwCgYDVQQDDANnZWQx
|
14
|
+
GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
|
15
|
+
HhcNMjUwMTAxMDMzMTA5WhcNMjYwMTAxMDMzMTA5WjA+MQwwCgYDVQQDDANnZWQx
|
16
|
+
GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
|
17
|
+
ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC/JWGRHO+USzR97vXjkFgt
|
18
|
+
83qeNf2KHkcvrRTSnR64i6um/ziin0I0oX23H7VYrDJC9A/uoUa5nGRJS5Zw/+wW
|
19
|
+
ENcvWVZS4iUzi4dsYJGY6yEOsXh2CcF46+QevV8iE+UmbkU75V7Dy1JCaUOyizEt
|
20
|
+
TH5UHsOtUU7k9TYARt/TgYZKuaoAMZZd5qyVqhF1vV+7/Qzmp89NGflXf2xYP26a
|
21
|
+
4MAX2qqKX/FKXqmFO+AGsbwYTEds1mksBF3fGsFgsQWxftG8GfZQ9+Cyu2+l1eOw
|
22
|
+
cZ+lPcg834G9DrqW2zhqUoLr1MTly4pqxYGb7XoDhoR7dd1kFE2a067+DzWC/ADt
|
23
|
+
+QkcqWUm5oh1fN0eqr7NsZlVJDulFgdiiYPQiIN7UNsii4Wc9aZqBoGcYfBeQNPZ
|
24
|
+
soo/6za/bWajOKUmDhpqvaiRv9EDpVLzuj53uDoukMMwxCMfgb04+ckQ0t2G7wqc
|
25
|
+
/D+K9JW9DDs3Yjgv9k4h7YMhW5gftosd+NkNC/+Y2CkCAwEAAaN1MHMwCQYDVR0T
|
26
|
+
BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHKN/nkRusdqCJEuq3lgB3fJvyTg
|
27
|
+
MBwGA1UdEQQVMBOBEWdlZEBGYWVyaWVNVUQub3JnMBwGA1UdEgQVMBOBEWdlZEBG
|
28
|
+
YWVyaWVNVUQub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQBjrBzCKWzXFigswYSPzGO8
|
29
|
+
9atBtY/eQdcN6KCL+PTzQBD9yePGF7H/xsww3awRauP+D1VUjCFbiiC3Qb0Ww0Qd
|
30
|
+
OVA0s10T9KpZ8nb2XyKocSK7TfgDhcyr0V4H2MPxwK9SWYjGGh8z9z9HmT0i3PyX
|
31
|
+
fXOSzzEoG6u26HIOg0nxSpitEjiAHBekQxy9ka5NuQbxoxMg+eIHU4rU9IUhu0Rf
|
32
|
+
wl4wuvPVE3UQK0v0uqT6rJukEKQ1iNgK5R8klgEIv79XhQPgTkMt31FGfrwOp6HB
|
33
|
+
OE0HMwOwY9B0w3aOxxdMQyyRxaZVv3eWE5RimQI7T0TUaxPngtS33ByMZjTeidxi
|
34
|
+
ESIUEPVXoBCkFgLW1EVlBb+rG7WLYod4eVll4tKA42Bi2Ju90tqiJ1YQiyuRfCnp
|
35
|
+
8qAqdfV+4u6Huu1KzAuDQCheyEyISsLST37sU/irV3czV6BiFipWag1XiJciRT3A
|
36
|
+
wZqCfTNVHTdtsCbfdA1DsA3RdG2iEH3TOHzv1Rqzqh4=
|
36
37
|
-----END CERTIFICATE-----
|
37
|
-
date:
|
38
|
+
date: 2025-07-12 00:00:00.000000000 Z
|
38
39
|
dependencies:
|
39
40
|
- !ruby/object:Gem::Dependency
|
40
41
|
name: loggability
|
@@ -100,7 +101,6 @@ metadata:
|
|
100
101
|
changelog_uri: https://deveiate.org/code/pluggability/History_md.html
|
101
102
|
source_uri: https://hg.sr.ht/~ged/Pluggability/browse
|
102
103
|
bug_tracker_uri: https://todo.sr.ht/~ged/Pluggability/browse
|
103
|
-
post_install_message:
|
104
104
|
rdoc_options: []
|
105
105
|
require_paths:
|
106
106
|
- lib
|
@@ -115,8 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '0'
|
117
117
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
119
|
-
signing_key:
|
118
|
+
rubygems_version: 3.6.9
|
120
119
|
specification_version: 4
|
121
120
|
summary: Pluggability is a toolkit for creating plugins.
|
122
121
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|