plugin_manager 1.1 → 1.2
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.
- data/README.md +3 -0
- data/Rakefile +22 -0
- data/lib/plugin_manager.rb +172 -48
- data/lib/plugin_manager/definition_builder.rb +19 -1
- data/lib/plugin_manager/plugin_definition.rb +17 -3
- metadata +36 -33
- data/bin/plugin_manager +0 -4
data/README.md
CHANGED
@@ -54,6 +54,9 @@ The code in the appropriate plugins will be loaded and you will then have availa
|
|
54
54
|
# plugins that raised exceptions while being loaded
|
55
55
|
manager.plugins_with_errors
|
56
56
|
|
57
|
+
NB. There is a BIG difference between this and say, Rubygems, in that plugin_manager will ONLY EVER
|
58
|
+
LOAD THE MOST RECENT VERSION OF A PLUGIN. Older versions essentially DON'T EXIST from the point of view
|
59
|
+
of the plugin_manager.
|
57
60
|
|
58
61
|
License
|
59
62
|
=======
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/rdoctask"
|
3
|
+
|
4
|
+
require "spec"
|
5
|
+
require "spec/rake/spectask"
|
6
|
+
Spec::Rake::SpecTask.new do |t|
|
7
|
+
t.spec_opts = %w(--format specdoc --colour)
|
8
|
+
t.libs = ["spec"]
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => ["spec"]
|
12
|
+
|
13
|
+
# Generate documentation
|
14
|
+
Rake::RDocTask.new do |rd|
|
15
|
+
rd.main = "README.md"
|
16
|
+
rd.rdoc_files.include("README.md", "lib/**/*.rb")
|
17
|
+
rd.rdoc_dir = "rdoc"
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Clear out RDoc and generated packages'
|
21
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
22
|
+
end
|
data/lib/plugin_manager.rb
CHANGED
@@ -6,75 +6,199 @@ require 'plugin_manager/plugin_definition'
|
|
6
6
|
require 'plugin_manager/definition_builder'
|
7
7
|
|
8
8
|
class PluginManager
|
9
|
-
attr_reader :unreadable_definitions, :plugins_with_errors, :loaded_plugins, :unloaded_plugins
|
9
|
+
attr_reader :unreadable_definitions, :plugins_with_errors, :loaded_plugins, :unloaded_plugins, :output
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
class << self
|
12
|
+
attr_accessor :current
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(output = STDOUT)
|
16
|
+
@plugins = []
|
17
|
+
@unloaded_plugins = []
|
18
|
+
@loaded_plugins = []
|
14
19
|
@unreadable_definitions = []
|
15
|
-
@plugins_with_errors
|
20
|
+
@plugins_with_errors = []
|
21
|
+
@output = output
|
16
22
|
end
|
17
23
|
|
24
|
+
def on_load(&block)
|
25
|
+
@load_observer = block
|
26
|
+
end
|
27
|
+
|
18
28
|
def plugins
|
19
|
-
@
|
29
|
+
@plugins
|
20
30
|
end
|
31
|
+
|
32
|
+
def plugin_objects
|
33
|
+
@loaded_plugins.map {|definition| definition.object}
|
34
|
+
end
|
35
|
+
|
36
|
+
def objects_implementing(method_name)
|
37
|
+
plugin_objects.select {|obj| obj.respond_to?(method_name) }
|
38
|
+
end
|
39
|
+
|
40
|
+
class Dependency
|
41
|
+
attr_reader :required_name
|
42
|
+
attr_reader :required_version
|
43
|
+
|
44
|
+
def initialize(plugin_manager, required_name, required_version)
|
45
|
+
@plugin_manager = plugin_manager
|
46
|
+
@required_name = required_name
|
47
|
+
@required_version = required_version
|
48
|
+
end
|
49
|
+
|
50
|
+
def satisfied?
|
51
|
+
if loaded_plugin = @plugin_manager.loaded_plugins.detect {|pl| pl.name == required_name }
|
52
|
+
PluginManager.compare_version(required_version, loaded_plugin.version)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
"dep(#{required_name} #{required_version})"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_gem_plugin_source
|
62
|
+
all_gem_names = Gem::SourceIndex.from_installed_gems.map {|n, _| n}
|
63
|
+
redcar_plugin_gem_names = all_gem_names.select {|n| n =~ /^redcar-/}
|
64
|
+
|
65
|
+
definition_files = redcar_plugin_gem_names.map do |gem_name|
|
66
|
+
[gem_name, ENV["GEM_HOME"] + "/gems/" + gem_name + "/plugin.rb"]
|
67
|
+
end
|
68
|
+
|
69
|
+
definition_files = definition_files.select do |name, definition_file|
|
70
|
+
File.exist?(definition_file)
|
71
|
+
end
|
72
|
+
|
73
|
+
if definition_files.any?
|
74
|
+
gem_names = definition_files.map {|n, _| n }
|
75
|
+
@output.puts "[PluginManager] found gem plugins #{gem_names.inspect}" if ENV["PLUGIN_DEBUG"]
|
76
|
+
end
|
77
|
+
|
21
78
|
|
79
|
+
add_definition_files(definition_files.map {|_, f| f})
|
80
|
+
end
|
81
|
+
|
22
82
|
def add_plugin_source(directory)
|
23
83
|
definition_files = Dir[File.join(File.expand_path(directory), "*", "plugin.rb")]
|
24
84
|
definition_files.reject! {|f| plugins.any? {|pl| pl.definition_file == File.expand_path(f) } }
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
85
|
+
|
86
|
+
add_definition_files(definition_files)
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_definition_files(definition_files)
|
90
|
+
definition_files.each do |file|
|
91
|
+
begin
|
92
|
+
PluginManager.current = self
|
93
|
+
definition = instance_eval(File.read(file))
|
94
|
+
PluginManager.current = nil
|
95
|
+
definition.definition_file = File.expand_path(file)
|
96
|
+
if already_with_that_name = @plugins.detect {|pl| pl.name == definition.name }
|
97
|
+
if already_with_that_name.version.to_f < definition.version.to_f
|
98
|
+
@unloaded_plugins.delete(already_with_that_name)
|
99
|
+
@plugins.delete(already_with_that_name)
|
100
|
+
@unloaded_plugins << definition
|
101
|
+
@plugins << definition
|
102
|
+
end
|
103
|
+
else
|
104
|
+
@unloaded_plugins << definition
|
105
|
+
@plugins << definition
|
37
106
|
end
|
38
|
-
|
39
|
-
|
107
|
+
rescue Object => e
|
108
|
+
@output.puts "Unreadable plugin definition: #{file}"
|
109
|
+
@output.puts " " + e.message
|
110
|
+
@output.puts e.backtrace.map {|l| " " + l }
|
111
|
+
@unreadable_definitions << file
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
@plugins = @plugins.sort_by {|pl| pl.name }
|
117
|
+
@unloaded_plugins = @unloaded_plugins.sort_by {|pl| pl.name }
|
40
118
|
end
|
41
119
|
|
42
|
-
def load
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
plugin.load
|
50
|
-
@loaded_plugins << plugin
|
51
|
-
rescue Object => e
|
52
|
-
puts "Error loading plugin: #{plugin.inspect}"
|
53
|
-
puts " " + e.message
|
54
|
-
puts e.backtrace.map {|l| " " + l }
|
55
|
-
@plugins_with_errors << plugin
|
120
|
+
def load(*plugin_names)
|
121
|
+
if plugin_names.empty?
|
122
|
+
return load_maximal
|
123
|
+
else
|
124
|
+
target_dependencies = plugin_names.map do |n|
|
125
|
+
unless result = latest_version_by_name(n)
|
126
|
+
raise "can't find plugin named #{n}"
|
56
127
|
end
|
57
|
-
|
128
|
+
Dependency.new(self, n, ">0")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
remaining_to_load = expand_dependencies(target_dependencies)
|
132
|
+
while remaining_to_load.length > 0
|
133
|
+
previous_length = remaining_to_load.length
|
134
|
+
if plugin = next_to_load(remaining_to_load)
|
135
|
+
load_plugin(plugin)
|
136
|
+
remaining_to_load = remaining_to_load.reject {|dep| dep.required_name == plugin.name }
|
137
|
+
else
|
138
|
+
puts "no plugin to load from #{remaining_to_load.inspect}"
|
139
|
+
return
|
58
140
|
end
|
141
|
+
new_length = remaining_to_load.length
|
59
142
|
end
|
60
143
|
end
|
61
144
|
|
62
|
-
|
145
|
+
def latest_version_by_name(name)
|
146
|
+
@plugins.select {|pl| pl.name == name }.sort_by {|pl| pl.version }.last
|
147
|
+
end
|
63
148
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
149
|
+
def load_plugin(plugin)
|
150
|
+
begin
|
151
|
+
@output.puts "[PluginManager] loading #{plugin.name}" if ENV["PLUGIN_DEBUG"]
|
152
|
+
plugin.load
|
153
|
+
if @load_observer
|
154
|
+
@load_observer.call(plugin)
|
155
|
+
end
|
156
|
+
@loaded_plugins << plugin
|
157
|
+
rescue Object => e
|
158
|
+
@output.puts "Error loading plugin: #{plugin.inspect}"
|
159
|
+
@output.puts " " + e.message
|
160
|
+
@output.puts e.backtrace.map {|l| " " + l }
|
161
|
+
@plugins_with_errors << plugin
|
162
|
+
end
|
163
|
+
@unloaded_plugins.delete(plugin)
|
164
|
+
end
|
165
|
+
|
166
|
+
def load_maximal
|
167
|
+
while ready_plugin = @unloaded_plugins.detect {|pl| pl.dependencies.all? {|dep| dep.satisfied? }}
|
168
|
+
load_plugin(ready_plugin)
|
169
|
+
end
|
170
|
+
@load_observer = nil # After loading all possible plugins, remove the load observer
|
171
|
+
end
|
172
|
+
|
173
|
+
def expand_dependencies(dependency_array)
|
174
|
+
previous_length = dependency_array.length
|
175
|
+
new_dependency_array = dependency_array.map do |dep|
|
176
|
+
if pl = latest_version_by_name(dep.required_name)
|
177
|
+
[dep, pl.dependencies]
|
178
|
+
else
|
179
|
+
dep
|
180
|
+
end
|
181
|
+
end.flatten.uniq
|
182
|
+
if new_dependency_array.length > previous_length
|
183
|
+
expand_dependencies(new_dependency_array)
|
184
|
+
else
|
185
|
+
new_dependency_array
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def next_to_load(dependency_array)
|
190
|
+
hash = Hash.new {|h,k| h[k] = []}
|
191
|
+
dependency_array.each {|dep| hash[dep.required_name] << dep.required_version}
|
192
|
+
hash.each do |name, version_requirements|
|
193
|
+
if candidate_for_loading = unloaded_plugins.detect {|pl| pl.name == name}
|
194
|
+
all_requirements_met = version_requirements.all? do |version_requirement|
|
195
|
+
PluginManager.compare_version(version_requirement, candidate_for_loading.version)
|
75
196
|
end
|
197
|
+
all_candidate_deps_met = candidate_for_loading.dependencies.all? {|dep| dep.satisfied?}
|
198
|
+
return candidate_for_loading if all_requirements_met and all_candidate_deps_met
|
76
199
|
end
|
77
200
|
end
|
201
|
+
nil
|
78
202
|
end
|
79
203
|
|
80
204
|
def self.compare_version(required, got)
|
@@ -98,7 +222,7 @@ class PluginManager
|
|
98
222
|
md[2] != got
|
99
223
|
end
|
100
224
|
else
|
101
|
-
puts "don't recognize version string: #{required.inspect}"
|
225
|
+
@output.puts "don't recognize version string: #{required.inspect}"
|
102
226
|
end
|
103
227
|
end
|
104
228
|
end
|
@@ -27,10 +27,28 @@ class PluginManager
|
|
27
27
|
@definition.file = values
|
28
28
|
end
|
29
29
|
|
30
|
+
def install(*args)
|
31
|
+
if args.length == 1
|
32
|
+
if Hash === args.first
|
33
|
+
hash = args.first
|
34
|
+
else
|
35
|
+
hash = { args.first => File.basename(args.first) }
|
36
|
+
end
|
37
|
+
elsif args.length == 2
|
38
|
+
prefix = args.first
|
39
|
+
if Hash === args[1]
|
40
|
+
hash = Hash[args[1].map {|k, v| [prefix + k, v]}]
|
41
|
+
elsif Array === args[1]
|
42
|
+
hash = Hash[args[1].map {|path| [prefix + path, File.basename(path) ] } ]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
@definition.resources.merge!(hash)
|
46
|
+
end
|
47
|
+
|
30
48
|
def dependencies(*deps)
|
31
49
|
@definition.dependencies ||= []
|
32
50
|
deps.each_slice(2) do |name, ver|
|
33
|
-
@definition.dependencies <<
|
51
|
+
@definition.dependencies << PluginManager::Dependency.new(PluginManager.current, name, ver)
|
34
52
|
end
|
35
53
|
end
|
36
54
|
end
|
@@ -5,22 +5,35 @@ class PluginManager
|
|
5
5
|
:version,
|
6
6
|
:object_string,
|
7
7
|
:file,
|
8
|
-
:
|
9
|
-
:
|
8
|
+
:definition_file,
|
9
|
+
:resources
|
10
10
|
|
11
11
|
def inspect1
|
12
|
-
"<Plugin #{name} #{version} depends
|
12
|
+
"<Plugin #{name} #{version} depends:#{dependencies.inspect} #{required_files.length} files>"
|
13
13
|
end
|
14
14
|
|
15
15
|
def inspect
|
16
16
|
inspect1
|
17
17
|
end
|
18
18
|
|
19
|
+
def resources
|
20
|
+
@resources ||= {}
|
21
|
+
end
|
22
|
+
|
19
23
|
def required_files
|
20
24
|
@required_files ||= []
|
21
25
|
end
|
22
26
|
|
27
|
+
def dependencies
|
28
|
+
@dependencies ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_time
|
32
|
+
@load_time
|
33
|
+
end
|
34
|
+
|
23
35
|
def load
|
36
|
+
s = Time.now
|
24
37
|
required_files.each {|file| $".delete(file) }
|
25
38
|
load_file = File.expand_path(File.join(File.dirname(definition_file), file))
|
26
39
|
$:.unshift(File.dirname(load_file))
|
@@ -31,6 +44,7 @@ class PluginManager
|
|
31
44
|
if object.respond_to?(:loaded)
|
32
45
|
object.loaded
|
33
46
|
end
|
47
|
+
@load_time = Time.now - s
|
34
48
|
end
|
35
49
|
|
36
50
|
def object
|
metadata
CHANGED
@@ -1,67 +1,70 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plugin_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease:
|
5
|
+
version: "1.2"
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
|
-
- Daniel Lucraft
|
8
|
+
- Daniel Lucraft
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2011-06-12 00:00:00 +01:00
|
13
14
|
default_executable:
|
14
15
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
25
27
|
description:
|
26
28
|
email: dan@fluentradical.com
|
27
|
-
executables:
|
28
|
-
|
29
|
+
executables: []
|
30
|
+
|
29
31
|
extensions: []
|
30
32
|
|
31
33
|
extra_rdoc_files:
|
32
|
-
- README.md
|
34
|
+
- README.md
|
33
35
|
files:
|
34
|
-
- README.md
|
35
|
-
-
|
36
|
-
- lib/plugin_manager
|
37
|
-
- lib/plugin_manager/
|
38
|
-
- lib/plugin_manager.rb
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- lib/plugin_manager.rb
|
39
|
+
- lib/plugin_manager/definition_builder.rb
|
40
|
+
- lib/plugin_manager/plugin.rb
|
41
|
+
- lib/plugin_manager/plugin_definition.rb
|
39
42
|
has_rdoc: true
|
40
43
|
homepage: http://github.com/danlucraft/plugin_manager
|
41
44
|
licenses: []
|
42
45
|
|
43
46
|
post_install_message:
|
44
47
|
rdoc_options:
|
45
|
-
- --main
|
46
|
-
- README.md
|
48
|
+
- --main
|
49
|
+
- README.md
|
47
50
|
require_paths:
|
48
|
-
- lib
|
51
|
+
- lib
|
49
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
50
54
|
requirements:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
version:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
55
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
56
60
|
requirements:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
version:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
61
64
|
requirements: []
|
62
65
|
|
63
66
|
rubyforge_project:
|
64
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.5.1
|
65
68
|
signing_key:
|
66
69
|
specification_version: 3
|
67
70
|
summary: A Ruby plugin loader
|
data/bin/plugin_manager
DELETED