plugin_manager 1
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 +61 -0
- data/bin/plugin_manager +4 -0
- data/lib/plugin_manager/definition_builder.rb +37 -0
- data/lib/plugin_manager/plugin.rb +9 -0
- data/lib/plugin_manager/plugin_definition.rb +37 -0
- data/lib/plugin_manager.rb +104 -0
- metadata +69 -0
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
Plugin Manager
|
3
|
+
==============
|
4
|
+
|
5
|
+
This is a plugin loader for Ruby. Features:
|
6
|
+
|
7
|
+
* dependencies
|
8
|
+
* versioning
|
9
|
+
* multiple plugin sources
|
10
|
+
* safe against plugins with broken code
|
11
|
+
* plugin code reloading
|
12
|
+
|
13
|
+
It is not tied to Rubygems or Rails. A plugin is any directory with a plugin.rb file
|
14
|
+
inside that looks like this:
|
15
|
+
|
16
|
+
Plugin.define do
|
17
|
+
name "Extras"
|
18
|
+
version "1.0"
|
19
|
+
|
20
|
+
# the file to load to load the plugin. It is expected to be an .rb
|
21
|
+
# file relative to this definition
|
22
|
+
file "extras"
|
23
|
+
|
24
|
+
# this is an object that is defined by the plugin code
|
25
|
+
object "App::Extras"
|
26
|
+
|
27
|
+
# Dependencies of the plugin
|
28
|
+
dependencies "core", ">=1.0",
|
29
|
+
"fonts", ">=0.5, <1.9",
|
30
|
+
"debug", ">0, !=0.95, < 2"
|
31
|
+
end
|
32
|
+
|
33
|
+
See the spec/fixtures/example/ directory for an example of a set of plugins.
|
34
|
+
|
35
|
+
This
|
36
|
+
directory of plugins can be loaded with:
|
37
|
+
|
38
|
+
manager = PluginManager.new
|
39
|
+
manager.add_plugin_source("spec/fixtures/example")
|
40
|
+
manager.load
|
41
|
+
|
42
|
+
The code in the appropriate plugins will be loaded and you will then have available:
|
43
|
+
|
44
|
+
# plugins that were loaded successfully
|
45
|
+
manager.loaded_plugins
|
46
|
+
|
47
|
+
# plugins that could not be loaded because of unmet dependencies,
|
48
|
+
# or because a more recent version was available.
|
49
|
+
manager.unloaded_plugins
|
50
|
+
|
51
|
+
# plugin.rb files that could not be read
|
52
|
+
manager.unreadable_definitions
|
53
|
+
|
54
|
+
# plugins that raised exceptions while being loaded
|
55
|
+
manager.plugins_with_errors
|
56
|
+
|
57
|
+
|
58
|
+
License
|
59
|
+
=======
|
60
|
+
|
61
|
+
MIT
|
data/bin/plugin_manager
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
class PluginManager
|
3
|
+
class DefinitionBuilder
|
4
|
+
def initialize(&block)
|
5
|
+
@block = block
|
6
|
+
@definition = PluginDefinition.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
instance_eval(&@block)
|
11
|
+
@definition
|
12
|
+
end
|
13
|
+
|
14
|
+
def name(value)
|
15
|
+
@definition.name = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def version(value)
|
19
|
+
@definition.version = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def object(value)
|
23
|
+
@definition.object = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def file(value)
|
27
|
+
@definition.file = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def dependencies(*deps)
|
31
|
+
@definition.dependencies ||= []
|
32
|
+
deps.each_slice(2) do |name, ver|
|
33
|
+
@definition.dependencies << [name, ver]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
class PluginManager
|
3
|
+
class PluginDefinition
|
4
|
+
attr_accessor :name,
|
5
|
+
:version,
|
6
|
+
:object,
|
7
|
+
:file,
|
8
|
+
:dependencies,
|
9
|
+
:definition_file
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"<Plugin #{name} #{version} depends:[#{(dependencies||[]).map{|dep| dep.join("")}.join(", ")}] #{required_files.length} files>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def required_files
|
16
|
+
@required_files ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def load
|
20
|
+
required_files.each {|file| $".delete(file) }
|
21
|
+
new_files = log_requires do
|
22
|
+
require File.expand_path(File.join(File.dirname(definition_file), file))
|
23
|
+
end
|
24
|
+
required_files.unshift(*new_files)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def log_requires
|
30
|
+
before = $".dup
|
31
|
+
yield
|
32
|
+
after = $".dup
|
33
|
+
result = after - before
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'plugin_manager/plugin'
|
5
|
+
require 'plugin_manager/plugin_definition'
|
6
|
+
require 'plugin_manager/definition_builder'
|
7
|
+
|
8
|
+
class PluginManager
|
9
|
+
attr_reader :unreadable_definitions, :plugins_with_errors, :loaded_plugins
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@unloaded_plugins = []
|
13
|
+
@loaded_plugins = []
|
14
|
+
@unreadable_definitions = []
|
15
|
+
@plugins_with_errors = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def plugins
|
19
|
+
@unloaded_plugins + @loaded_plugins
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_plugin_source(directory)
|
23
|
+
definition_files = Dir[File.join(File.expand_path(directory), "*", "plugin.rb")]
|
24
|
+
definition_files.reject! {|f| plugins.any? {|pl| pl.definition_file == File.expand_path(f) } }
|
25
|
+
new_definitions =
|
26
|
+
definition_files.map do |file|
|
27
|
+
begin
|
28
|
+
definition = instance_eval(File.read(file))
|
29
|
+
definition.definition_file = File.expand_path(file)
|
30
|
+
definition
|
31
|
+
rescue Object
|
32
|
+
puts "Unreadable plugin definition: #{file}"
|
33
|
+
@unreadable_definitions << file
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end.compact.sort_by {|p| p.name.downcase }
|
37
|
+
@unloaded_plugins += new_definitions
|
38
|
+
end
|
39
|
+
|
40
|
+
def load
|
41
|
+
previous_length = @unloaded_plugins.length + 1
|
42
|
+
while previous_length > @unloaded_plugins.length
|
43
|
+
previous_length = @unloaded_plugins.length
|
44
|
+
if plugin = next_to_load
|
45
|
+
begin
|
46
|
+
plugin.load
|
47
|
+
rescue Object => e
|
48
|
+
puts "Error loading plugin: #{plugin}"
|
49
|
+
puts " " + e.message
|
50
|
+
puts e.backtrace.map {|l| " " + l }
|
51
|
+
@plugins_with_errors << plugin
|
52
|
+
end
|
53
|
+
@loaded_plugins << plugin
|
54
|
+
@unloaded_plugins.delete(plugin)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def next_to_load
|
62
|
+
# this ordering ensures we try the most recent version of a plugin first
|
63
|
+
remaining_plugins = @unloaded_plugins.sort_by {|pl| pl.version }.reverse
|
64
|
+
|
65
|
+
remaining_plugins.detect do |d|
|
66
|
+
next if @loaded_plugins.map {|pl| pl.name }.include?(d.name)
|
67
|
+
(d.dependencies||[]).all? do |dep|
|
68
|
+
req_name, req_ver = *dep
|
69
|
+
@loaded_plugins.detect do |d1|
|
70
|
+
d1.name == req_name and
|
71
|
+
PluginManager.compare_version(req_ver, d1.version)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.compare_version(required, got)
|
78
|
+
got = got.gsub(/(\.0)+$/, "")
|
79
|
+
required.split(",").all? do |req|
|
80
|
+
req = req.strip
|
81
|
+
req = req.gsub(/(\.0)+$/, "")
|
82
|
+
if md = req.match(/^(=|>|>=|<|<=|!=)?([\d\.]*)$/)
|
83
|
+
case md[1]
|
84
|
+
when ">"
|
85
|
+
got > md[2]
|
86
|
+
when ">="
|
87
|
+
got >= md[2]
|
88
|
+
when "<"
|
89
|
+
got < md[2]
|
90
|
+
when "<="
|
91
|
+
got <= md[2]
|
92
|
+
when "="
|
93
|
+
md[2] == got
|
94
|
+
when "!="
|
95
|
+
md[2] != got
|
96
|
+
end
|
97
|
+
else
|
98
|
+
puts "don't recognize version string: #{required.inspect}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plugin_manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Lucraft
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-16 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: dan@fluentradical.com
|
27
|
+
executables:
|
28
|
+
- plugin_manager
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.md
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- lib/plugin_manager/definition_builder.rb
|
36
|
+
- lib/plugin_manager/plugin.rb
|
37
|
+
- lib/plugin_manager/plugin_definition.rb
|
38
|
+
- lib/plugin_manager.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/danlucraft/plugin_manager
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --main
|
46
|
+
- README.md
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.3.5
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: A Ruby plugin loader
|
68
|
+
test_files: []
|
69
|
+
|