Class: GemPlugin::Manager

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/gem_plugin.rb

Overview

This class is used by people who use gem plugins (but don’t necessarily make them) to add plugins to their own systems. It provides a way to load plugins, list them, and create them as needed.

It is a singleton so you use like this: GemPlugins::Manager.instance.load

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeManager

Returns a new instance of Manager.



69
70
71
72
73
74
75
76
# File 'lib/gem_plugin.rb', line 69

def initialize
  # plugins that have been loaded
  @plugins = {}

  # keeps track of gems which have been loaded already by the manager *and*
  # where they came from so that they can be referenced later
  @gems = {}
end

Instance Attribute Details

#gemsObject (readonly)

Returns the value of attribute gems.



66
67
68
# File 'lib/gem_plugin.rb', line 66

def gems
  @gems
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



65
66
67
# File 'lib/gem_plugin.rb', line 65

def plugins
  @plugins
end

Instance Method Details

#config(gem_name, options = {}) ⇒ Object

While Manager.resource will find arbitrary resources, a special case is when you need to load a set of configuration defaults. GemPlugin normalizes this to be if you have a file “resources/defaults.yaml” then you’ll be able to load them via Manager.config.

How you use the method is you get the options the user wants set, pass them to Manager.instance.config, and what you get back is a new Hash with the user’s settings overriding the defaults.

opts = Manager.instance.config "mygem", :age => 12, :max_load => .9

In the above case, if defaults had => 14 then it would be changed to 12.

This loads the .yaml file on the fly every time, so doing it a whole lot is very stupid. If you need to make frequent calls to this, call it once with no options (Manager.instance.config) then use the returned defaults directly from then on.



218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/gem_plugin.rb', line 218

def config(gem_name, options={})
  config_file = Manager.instance.resource(gem_name, "/defaults.yaml")
  if config_file
    begin
      defaults = YAML.load_file(config_file)
      return defaults.merge(options)
    rescue
      raise "Error loading config #{config_file} for gem #{gem_name}"
    end
  else
    return options
  end
end

#create(name, options = {}) ⇒ Object

Resolves the given name (should include /category/name) to find the plugin class and create an instance. You can pass a second hash option that is then given to the Plugin to configure it.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/gem_plugin.rb', line 145

def create(name, options = {})
  last_slash = name.rindex("/")
  category = name[0 ... last_slash]
  plugin = name[last_slash .. -1]

  map = @plugins[category]
  if not map
    raise "Plugin category #{category} does not exist"
  elsif not map.has_key? plugin
    raise "Plugin #{plugin} does not exist in category #{category}"
  else
    map[plugin].new(options)
  end
end

#load(needs = {}) ⇒ Object

Responsible for going through the list of available gems and loading any plugins requested. It keeps track of what it’s loaded already and won’t load them again.

It accepts one parameter which is a hash of gem depends that should include or exclude a gem from being loaded. A gem must depend on gem_plugin to be considered, but then each system has to add it’s own INCLUDE to make sure that only plugins related to it are loaded.

An example again comes from Mongrel. In order to load all Mongrel plugins:

GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE

Which will load all plugins that depend on mongrel AND gem_plugin. Now, one extra thing we do is we delay loading Rails Mongrel plugins until after rails is configured. Do do this the mongrel_rails script has:

GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE

The only thing to remember is that this is saying “include a plugin if it depends on gem_plugin, mongrel, but NOT rails”. If a plugin also depends on other stuff then it’s loaded just fine. Only gem_plugin, mongrel, and rails are ever used to determine if it should be included.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/gem_plugin.rb', line 101

def load(needs = {})
  sdir = File.join(Gem.dir, "specifications")
  gems = Gem::SourceIndex.from_installed_gems(sdir)
  needs = needs.merge({"gem_plugin" => INCLUDE})

  gems.each do |path, gem|
    # don't load gems more than once
    next if @gems.has_key? gem.name        
    check = needs.dup

    # rolls through the depends and inverts anything it finds
    gem.dependencies.each do |dep|
      # this will fail if a gem is depended more than once
      if check.has_key? dep.name
        check[dep.name] = !check[dep.name]
      end
    end
    
    # now since excluded gems start as true, inverting them
    # makes them false so we'll skip this gem if any excludes are found
    if (check.select {|name,test| !test}).length == 0
      # looks like no needs were set to false, so it's good
      require "#{gem.name}/init"
      @gems[gem.name] = File.join(Gem.dir, "gems", "#{gem.name}-#{gem.version}")
    end
  end

  return nil
end

#loaded?(gem_name) ⇒ Boolean

Simply says whether the given gem has been loaded yet or not.

Returns:

  • (Boolean)


162
163
164
# File 'lib/gem_plugin.rb', line 162

def loaded?(gem_name)
  @gems.has_key? gem_name
end

#register(category, name, klass) ⇒ Object

Not necessary for you to call directly, but this is how GemPlugin::Base.inherited actually adds a plugin to a category.



135
136
137
138
# File 'lib/gem_plugin.rb', line 135

def register(category, name, klass)
  @plugins[category] ||= {}
  @plugins[category][name.downcase] = klass
end

#resource(gem_name, path) ⇒ Object

GemPlugins can have a ‘resources’ directory which is packaged with them and can hold any data resources the plugin may need. The main problem is that it’s difficult to figure out where these resources are actually located on the file system. The resource method tries to locate the real path for a given resource path.

Let’s say you have a ‘resources/stylesheets/default.css’ file in your gem distribution, then finding where this file really is involves:

Manager.instance.resource("mygem", "/stylesheets/default.css")

You either get back the full path to the resource or you get a nil if it doesn’t exist.

If you request a path for a GemPlugin that hasn’t been loaded yet then it will throw an PluginNotLoaded exception. The gem may be present on your system in this case, but you just haven’t loaded it with Manager.instance.load properly.



185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/gem_plugin.rb', line 185

def resource(gem_name, path)
  if not loaded? gem_name
    raise PluginNotLoaded.new("Plugin #{gem_name} not loaded when getting resource #{path}")
  end
  
  file = File.join(@gems[gem_name], "resources", path)

  if File.exist? file
    return file
  else
    return nil
  end
end