Implemented "around" callbacks on VM for convenient `before_foo` and `after_foo` callbacks

This commit is contained in:
Mitchell Hashimoto 2010-02-15 19:02:23 -08:00
parent ebc3c70f8b
commit dd5abc5452
7 changed files with 47 additions and 26 deletions

View File

@ -2,14 +2,12 @@ module Vagrant
module Actions module Actions
class Import < Base class Import < Base
def execute! def execute!
@vm.invoke_callback(:before_import) @vm.invoke_around_callback(:import) do
Busy.busy do Busy.busy do
logger.info "Importing base VM (#{Vagrant.config[:vm][:base]})..." logger.info "Importing base VM (#{Vagrant.config[:vm][:base]})..."
@vm.vm = VirtualBox::VM.import(File.expand_path(Vagrant.config[:vm][:base])) @vm.vm = VirtualBox::VM.import(File.expand_path(Vagrant.config[:vm][:base]))
end end
end
@vm.invoke_callback(:after_import)
end end
end end
end end

View File

@ -2,8 +2,7 @@ module Vagrant
module Actions module Actions
class Start < Base class Start < Base
def execute! def execute!
@vm.invoke_callback(:before_boot) @vm.invoke_around_callback(:boot) do
# Startup the VM # Startup the VM
boot boot
@ -14,8 +13,7 @@ module Vagrant
Failed to connect to VM! Failed to boot? Failed to connect to VM! Failed to boot?
error error
end end
end
@vm.invoke_callback(:after_boot)
end end
def boot def boot

View File

@ -98,6 +98,15 @@ error
end end
end end
# Invokes an "around callback" which invokes before_name and
# after_name for the given callback name, yielding a block between
# callback invokations.
def invoke_around_callback(name, *args)
invoke_callback("before_#{name}".to_sym, *args)
yield
invoke_callback("after_#{name}".to_sym, *args)
end
def invoke_callback(name, *args) def invoke_callback(name, *args)
# Attempt to call the method for the callback on each of the # Attempt to call the method for the callback on each of the
# actions # actions

View File

@ -66,6 +66,7 @@ class Test::Unit::TestCase
@mock_vm.stubs(:vm).returns(@vm) @mock_vm.stubs(:vm).returns(@vm)
@mock_vm.stubs(:vm=) @mock_vm.stubs(:vm=)
@mock_vm.stubs(:invoke_callback) @mock_vm.stubs(:invoke_callback)
@mock_vm.stubs(:invoke_around_callback).yields
@action = action_klass.new(@mock_vm) @action = action_klass.new(@mock_vm)
[@mock_vm, @vm, @action] [@mock_vm, @vm, @action]

View File

@ -12,11 +12,8 @@ class ImportActionTest < Test::Unit::TestCase
@import.execute! @import.execute!
end end
should "invoke before/after callbacks around the import" do should "invoke an around callback around the import" do
callback_seq = sequence("callback_seq") @mock_vm.expects(:invoke_around_callback).with(:import).once
@mock_vm.expects(:invoke_callback).with(:before_import).once.in_sequence(callback_seq)
VirtualBox::VM.expects(:import).once.in_sequence(callback_seq)
@mock_vm.expects(:invoke_callback).with(:after_import).once.in_sequence(callback_seq)
@import.execute! @import.execute!
end end

View File

@ -8,12 +8,11 @@ class StartActionTest < Test::Unit::TestCase
end end
context "execution" do context "execution" do
should "invoke before callback, boot, and invoke the after callback" do should "invoke the 'boot' around callback" do
boot_seq = sequence("boot_seq") boot_seq = sequence("boot_seq")
@mock_vm.expects(:invoke_callback).with(:before_boot).once.in_sequence(boot_seq) @mock_vm.expects(:invoke_around_callback).with(:boot).once.in_sequence(boot_seq).yields
@action.expects(:boot).in_sequence(boot_seq) @action.expects(:boot).in_sequence(boot_seq)
@action.expects(:wait_for_boot).returns(true).in_sequence(boot_seq) @action.expects(:wait_for_boot).returns(true).in_sequence(boot_seq)
@mock_vm.expects(:invoke_callback).with(:after_boot).once.in_sequence(boot_seq)
@action.execute! @action.execute!
end end
@ -21,7 +20,6 @@ class StartActionTest < Test::Unit::TestCase
fail_boot_seq = sequence("fail_boot_seq") fail_boot_seq = sequence("fail_boot_seq")
@action.expects(:boot).once.in_sequence(fail_boot_seq) @action.expects(:boot).once.in_sequence(fail_boot_seq)
@action.expects(:wait_for_boot).returns(false).in_sequence(fail_boot_seq) @action.expects(:wait_for_boot).returns(false).in_sequence(fail_boot_seq)
@action.expects(:invoke_callback).with(:after_boot).never
@action.expects(:error_and_exit).once.in_sequence(fail_boot_seq) @action.expects(:error_and_exit).once.in_sequence(fail_boot_seq)
@action.execute! @action.execute!
end end

View File

@ -16,6 +16,26 @@ class VMTest < Test::Unit::TestCase
@vm = Vagrant::VM.new(@mock_vm) @vm = Vagrant::VM.new(@mock_vm)
end end
context "around callbacks" do
should "invoke before/after_name for around callbacks" do
block_obj = mock("block_obj")
around_seq = sequence("around_seq")
@vm.expects(:invoke_callback).with(:before_foo).once.in_sequence(around_seq)
block_obj.expects(:foo).once.in_sequence(around_seq)
@vm.expects(:invoke_callback).with(:after_foo).once.in_sequence(around_seq)
@vm.invoke_around_callback(:foo) do
block_obj.foo
end
end
should "forward arguments to invoke_callback" do
@vm.expects(:invoke_callback).with(:before_foo, "foo").once
@vm.expects(:invoke_callback).with(:after_foo, "foo").once
@vm.invoke_around_callback(:foo, "foo") do; end
end
end
should "not invoke callback on actions which don't respond to it" do should "not invoke callback on actions which don't respond to it" do
action = mock("action") action = mock("action")
action.stubs(:respond_to?).with(:foo).returns(false) action.stubs(:respond_to?).with(:foo).returns(false)