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 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
@@ -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
- def initialize
12
- @unloaded_plugins = []
13
- @loaded_plugins = []
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
- @unloaded_plugins + @loaded_plugins
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
- 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 => e
32
- puts "Unreadable plugin definition: #{file}"
33
- puts " " + e.message
34
- puts e.backtrace.map {|l| " " + l }
35
- @unreadable_definitions << file
36
- nil
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
- end.compact.sort_by {|p| p.name.downcase }
39
- @unloaded_plugins += new_definitions
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
- previous_length = @unloaded_plugins.length + 1
44
- while previous_length > @unloaded_plugins.length
45
- previous_length = @unloaded_plugins.length
46
- if plugin = next_to_load
47
- begin
48
- puts "[PluginManager] loading #{plugin.name}" if ENV["PLUGIN_DEBUG"]
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
- @unloaded_plugins.delete(plugin)
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
- private
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 next_to_load
65
- # this ordering ensures we try the most recent version of a plugin first
66
- remaining_plugins = @unloaded_plugins.sort_by {|pl| pl.version }.reverse
67
-
68
- remaining_plugins.detect do |d|
69
- next if @loaded_plugins.map {|pl| pl.name }.include?(d.name)
70
- (d.dependencies||[]).all? do |dep|
71
- req_name, req_ver = *dep
72
- @loaded_plugins.detect do |d1|
73
- d1.name == req_name and
74
- PluginManager.compare_version(req_ver, d1.version)
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 << [name, ver]
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
- :dependencies,
9
- :definition_file
8
+ :definition_file,
9
+ :resources
10
10
 
11
11
  def inspect1
12
- "<Plugin #{name} #{version} depends:[#{(dependencies||[]).map{|dep| dep.join("")}.join(", ")}] #{required_files.length} files>"
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
- version: "1.1"
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: 2010-01-17 00:00:00 +00:00
13
+ date: 2011-06-12 00:00:00 +01:00
13
14
  default_executable:
14
15
  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:
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
- - plugin_manager
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
- - lib/plugin_manager/definition_builder.rb
36
- - lib/plugin_manager/plugin.rb
37
- - lib/plugin_manager/plugin_definition.rb
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
- - !ruby/object:Gem::Version
53
- version: "0"
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
- - !ruby/object:Gem::Version
59
- version: "0"
60
- version:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
61
64
  requirements: []
62
65
 
63
66
  rubyforge_project:
64
- rubygems_version: 1.3.5
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
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require File.join(File.dirname(__FILE__), "..", "lib", "plugin_manager")
4
-