plugin_manager 1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "..", "lib", "plugin_manager")
4
+
@@ -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,9 @@
1
+
2
+ class PluginManager
3
+ class Plugin
4
+ def self.define(&block)
5
+ builder = DefinitionBuilder.new(&block)
6
+ builder.build
7
+ end
8
+ end
9
+ 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
+