Class: PluginManager

Inherits:
Object
  • Object
show all
Defined in:
lib/plugin_manager.rb,
lib/plugin_manager/plugin.rb,
lib/plugin_manager/plugin_definition.rb,
lib/plugin_manager/definition_builder.rb

Defined Under Namespace

Classes: DefinitionBuilder, Dependency, Plugin, PluginDefinition

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(output = STDOUT) ⇒ PluginManager

Returns a new instance of PluginManager.



15
16
17
18
19
20
21
22
# File 'lib/plugin_manager.rb', line 15

def initialize(output = STDOUT)
  @plugins                = []
  @unloaded_plugins       = []
  @loaded_plugins         = []
  @unreadable_definitions = []
  @plugins_with_errors    = []
  @output = output
end

Class Attribute Details

.currentObject

Returns the value of attribute current.



12
13
14
# File 'lib/plugin_manager.rb', line 12

def current
  @current
end

Instance Attribute Details

#loaded_pluginsObject (readonly)

Returns the value of attribute loaded_plugins.



9
10
11
# File 'lib/plugin_manager.rb', line 9

def loaded_plugins
  @loaded_plugins
end

#outputObject (readonly)

Returns the value of attribute output.



9
10
11
# File 'lib/plugin_manager.rb', line 9

def output
  @output
end

#plugins_with_errorsObject (readonly)

Returns the value of attribute plugins_with_errors.



9
10
11
# File 'lib/plugin_manager.rb', line 9

def plugins_with_errors
  @plugins_with_errors
end

#unloaded_pluginsObject (readonly)

Returns the value of attribute unloaded_plugins.



9
10
11
# File 'lib/plugin_manager.rb', line 9

def unloaded_plugins
  @unloaded_plugins
end

#unreadable_definitionsObject (readonly)

Returns the value of attribute unreadable_definitions.



9
10
11
# File 'lib/plugin_manager.rb', line 9

def unreadable_definitions
  @unreadable_definitions
end

Class Method Details

.compare_version(required, got) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/plugin_manager.rb', line 204

def self.compare_version(required, got)
  got = got.gsub(/(\.0)+$/, "")
  required.split(",").all? do |req|
    req = req.strip
    req = req.gsub(/(\.0)+$/, "")
    if md = req.match(/^(=|>|>=|<|<=|!=)?([\d\.]*)$/)
      case md[1]
      when ">"
        got > md[2]
      when ">="
        got >= md[2]
      when "<"
        got < md[2]
      when "<="
        got <= md[2]
      when "="
        md[2] == got
      when "!="
        md[2] != got
      end
    else
      @output.puts "don't recognize version string: #{required.inspect}"
    end
  end
end

Instance Method Details

#add_definition_files(definition_files) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/plugin_manager.rb', line 89

def add_definition_files(definition_files)
  definition_files.each do |file|
    begin
      PluginManager.current = self
      definition = instance_eval(File.read(file))
      PluginManager.current = nil
      definition.definition_file = File.expand_path(file)
      if already_with_that_name = @plugins.detect {|pl| pl.name == definition.name }
        if already_with_that_name.version.to_f < definition.version.to_f
          @unloaded_plugins.delete(already_with_that_name)
          @plugins.delete(already_with_that_name)
          @unloaded_plugins << definition
          @plugins << definition
        end
      else
        @unloaded_plugins << definition
        @plugins << definition
      end
    rescue Object => e
      @output.puts "Unreadable plugin definition: #{file}"
      @output.puts "  " + e.message
      @output.puts e.backtrace.map {|l| "  " + l }
      @unreadable_definitions << file
      nil
    end
  end
  
  @plugins = @plugins.sort_by {|pl| pl.name }
  @unloaded_plugins = @unloaded_plugins.sort_by {|pl| pl.name }
end

#add_gem_plugin_sourceObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/plugin_manager.rb', line 61

def add_gem_plugin_source
  all_gem_names           = Gem::SourceIndex.from_installed_gems.map {|n, _| n}
  redcar_plugin_gem_names = all_gem_names.select {|n| n =~ /^redcar-/}
  
  definition_files = redcar_plugin_gem_names.map do |gem_name|
    [gem_name, Gem.source_index.specification(gem_name).full_gem_path + "/plugin.rb"]
  end

  definition_files = definition_files.select do |name, definition_file|
    File.exist?(definition_file)
  end
  
  if definition_files.any?
    gem_names = definition_files.map {|n, _| n }
    @output.puts "[PluginManager] found gem plugins #{gem_names.inspect}" if ENV["PLUGIN_DEBUG"]
  end
  

  add_definition_files(definition_files.map {|_, f| f})
end

#add_plugin_source(directory) ⇒ Object



82
83
84
85
86
87
# File 'lib/plugin_manager.rb', line 82

def add_plugin_source(directory)
  definition_files = Dir[File.join(File.expand_path(directory), "*", "plugin.rb")]
  definition_files.reject! {|f| plugins.any? {|pl| pl.definition_file == File.expand_path(f) } }
  
  add_definition_files(definition_files)
end

#expand_dependencies(dependency_array) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/plugin_manager.rb', line 173

def expand_dependencies(dependency_array)
  previous_length = dependency_array.length
  new_dependency_array = dependency_array.map do |dep|
    if pl = latest_version_by_name(dep.required_name)
      [dep, pl.dependencies]
    else
      dep
    end
  end.flatten.uniq
  if new_dependency_array.length > previous_length
    expand_dependencies(new_dependency_array)
  else
    new_dependency_array
  end
end

#latest_version_by_name(name) ⇒ Object



145
146
147
# File 'lib/plugin_manager.rb', line 145

def latest_version_by_name(name)
  @plugins.select {|pl| pl.name == name }.sort_by {|pl| pl.version }.last
end

#load(*plugin_names) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/plugin_manager.rb', line 120

def load(*plugin_names)
  if plugin_names.empty?
    return load_maximal
  else
    target_dependencies = plugin_names.map do |n| 
      unless result = latest_version_by_name(n) 
        raise "can't find plugin named #{n}"
      end
      Dependency.new(self, n, ">0")
    end
  end
  remaining_to_load = expand_dependencies(target_dependencies)
  while remaining_to_load.length > 0
    previous_length = remaining_to_load.length
    if plugin = next_to_load(remaining_to_load)
      load_plugin(plugin)
      remaining_to_load = remaining_to_load.reject {|dep| dep.required_name == plugin.name }
    else
      puts "no plugin to load from #{remaining_to_load.inspect}"
      return
    end
    new_length = remaining_to_load.length
  end
end

#load_maximalObject



166
167
168
169
170
171
# File 'lib/plugin_manager.rb', line 166

def load_maximal
  while ready_plugin = @unloaded_plugins.detect {|pl| pl.dependencies.all? {|dep| dep.satisfied? }}
    load_plugin(ready_plugin)
  end
  @load_observer = nil # After loading all possible plugins, remove the load observer
end

#load_plugin(plugin) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/plugin_manager.rb', line 149

def load_plugin(plugin)
  begin
    @output.puts "[PluginManager] loading #{plugin.name}" if ENV["PLUGIN_DEBUG"]
    plugin.load
    if @load_observer
      @load_observer.call(plugin)
    end
    @loaded_plugins << plugin
  rescue Object => e
    @output.puts "Error loading plugin: #{plugin.inspect}"
    @output.puts "  " + e.message
    @output.puts e.backtrace.map {|l| "  " + l }
    @plugins_with_errors << plugin
  end
  @unloaded_plugins.delete(plugin)
end

#next_to_load(dependency_array) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/plugin_manager.rb', line 189

def next_to_load(dependency_array)
  hash = Hash.new {|h,k| h[k] = []}
  dependency_array.each {|dep| hash[dep.required_name] << dep.required_version}
  hash.each do |name, version_requirements|
    if candidate_for_loading = unloaded_plugins.detect {|pl| pl.name == name}
      all_requirements_met = version_requirements.all? do |version_requirement|
        PluginManager.compare_version(version_requirement, candidate_for_loading.version)
      end
      all_candidate_deps_met = candidate_for_loading.dependencies.all? {|dep| dep.satisfied?}
      return candidate_for_loading if all_requirements_met and all_candidate_deps_met
    end
  end
  nil
end

#objects_implementing(method_name) ⇒ Object



36
37
38
# File 'lib/plugin_manager.rb', line 36

def objects_implementing(method_name)
  plugin_objects.select {|obj| obj.respond_to?(method_name) }
end

#on_load(&block) ⇒ Object



24
25
26
# File 'lib/plugin_manager.rb', line 24

def on_load(&block)
  @load_observer = block
end

#plugin_objectsObject



32
33
34
# File 'lib/plugin_manager.rb', line 32

def plugin_objects
  @loaded_plugins.map {|definition| definition.object}
end

#pluginsObject



28
29
30
# File 'lib/plugin_manager.rb', line 28

def plugins
  @plugins
end