Plugin activation

Vagrant is only guaranteeing that the plugin definition superclass (the
Vagrant.plugin("1") part) is backwards compatible. Anything else, such
as Vagrant::Command::Base and so on, will likely change in future
versions. Beacuse of this, plugins should only immediately expose their
definition.

In order to support loading the other classes, plugins should defer
loading to the "activation" phase of a plugin. This can be done using
the `activated` block:

    class MyPlugin < Vagrant.plugin("1")
      name "my plugin"

      activated do
        require "myplugin/my_command"
      end

      command("foo") { MyCommand }
    end

Plugin activation is done at two specific times:

  * Right when a Vagrant::Environment is created and the global plugins
    (such as from ~.vagrantrc) are loaded.
  * Right before loading configuration, but after the Vagrantfiles have
    been evaluated. This allows plugins to be defined within these files
    as well.
This commit is contained in:
Mitchell Hashimoto 2012-05-21 22:23:50 -07:00
parent 5a3cf0e01b
commit de78a3637a
3 changed files with 68 additions and 0 deletions

View File

@ -100,6 +100,9 @@ module Vagrant
# Load the plugins
load_plugins
# Activate the plugins
activate_plugins
end
#---------------------------------------------------------------
@ -395,6 +398,10 @@ module Vagrant
config_loader.set(:vm, subvm.proc_stack)
end
# We activate plugins here because the files which we're loading
# configuration from may have defined new plugins as well.
activate_plugins
# Execute the configuration stack and store the result as the final
# value in the config ivar.
config_loader.load
@ -515,6 +522,14 @@ module Vagrant
nil
end
# This finds all the current plugins and activates them. This is an
# idempotent call so it is safe to call this as much as you need.
def activate_plugins
Vagrant.plugin("1").registered.each do |plugin|
plugin.activate!
end
end
# Loads the Vagrant plugins by properly setting up RubyGems so that
# our private gem repository is on the path.
def load_plugins

View File

@ -72,6 +72,19 @@ module Vagrant
hooks << block
end
# The given block will be called when this plugin is activated. The
# activation block should be used to load any of the classes used by
# the plugin other than the plugin definition itself. Vagrant only
# guarantees that the plugin definition will remain backwards
# compatible, but not any other classes (such as command base classes
# or so on). Therefore, to avoid crashing future versions of Vagrant,
# these classes should be placed in separate files and loaded in the
# activation block.
def self.activated(&block)
data[:activation_block] = block if block_given?
data[:activation_block]
end
# Defines additional command line commands available by key. The key
# becomes the subcommand, so if you register a command "foo" then
# "vagrant foo" becomes available.
@ -185,6 +198,18 @@ module Vagrant
data[:provisioners]
end
# Activates the current plugin. This shouldn't be called publicly.
def self.activate!
if !data[:activated_called]
LOGGER.info("Activating plugin: #{self.name}")
data[:activated_called] = true
# Call the activation block
block = activated
block.call if block
end
end
# Registers the plugin. This makes the plugin actually work with
# Vagrant. Prior to registering, the plugin is merely a skeleton.
#

View File

@ -35,6 +35,34 @@ describe Vagrant::Plugin::V1 do
end
end
describe "activation block" do
it "should have no activation block by default" do
plugin = Class.new(described_class)
plugin.activated.should be_nil
end
it "should be able to set and get the activation block" do
plugin = Class.new(described_class) do
activated do
42
end
end
plugin.activated.call.should == 42
end
it "should activate when `activate!` is called" do
plugin = Class.new(described_class) do
activated do
raise NotImplementedError
end
end
expect { plugin.activate! }.to raise_error(NotImplementedError)
expect { plugin.activate! }.to_not raise_error
end
end
describe "commands" do
it "should register command classes" do
plugin = Class.new(described_class) do