summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchris <chris@opscodes-macbook-pro-2.local>2009-10-14 21:41:45 +0000
committerchris <chris@opscodes-macbook-pro-2.local>2009-10-14 21:42:00 +0000
commit8d97aadcea8e407a39190e3baf8df138cd69077d (patch)
tree1b07a40f231c5d693cec10f079c14c2d98e80405
parentb8ebb42d8be6d4b16daff6effdf1509875177000 (diff)
downloadchef-8d97aadcea8e407a39190e3baf8df138cd69077d.tar.gz
CHEF-614: Making enclosing provider's lexical scope available to embedded resources
-rw-r--r--chef/lib/chef/mixin/recipe_definition_dsl_core.rb38
-rw-r--r--chef/lib/chef/resource.rb13
-rw-r--r--chef/spec/unit/lwrp_spec.rb40
3 files changed, 67 insertions, 24 deletions
diff --git a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
index 9f80b41903..65d0cc9cfc 100644
--- a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
+++ b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
@@ -47,24 +47,26 @@ class Chef
method_name = method_symbol.to_s
rname = convert_to_class_name(method_name)
- resource = nil
- begin
- args << @collection
- args << @node
- resource = Chef::Resource.const_get(rname).new(*args)
- # If we have a resource like this one, we want to steal its state
- resource.load_prior_resource
- resource.cookbook_name = @cookbook_name
- resource.recipe_name = @recipe_name
- resource.params = @params
- resource.instance_eval(&block) if block
- rescue NameError => e
- if e.to_s =~ /Chef::Resource/
- raise NameError, "Cannot find #{rname} for #{method_name}\nOriginal: #{e.to_s}"
- else
- raise e
- end
- end
+ # If we have a resource like this one, we want to steal its state
+ resource = begin
+ args << @collection
+ args << @node
+ Chef::Resource.const_get(rname).new(*args)
+ rescue NameError => e
+ if e.to_s =~ /Chef::Resource/
+ raise NameError, "Cannot find #{rname} for #{method_name}\nOriginal exception: #{e.class}: #{e.message}"
+ else
+ raise e
+ end
+ end
+ resource.load_prior_resource
+ resource.cookbook_name = @cookbook_name
+ resource.recipe_name = @recipe_name
+ resource.params = @params
+ # Determine whether this resource is being created in the context of an enclosing Provider
+ resource.enclosing_provider = self.is_a?(Chef::Provider) ? self : nil
+ resource.instance_eval(&block) if block
+
@collection.insert(resource)
resource
end
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index 6d3304c5a7..3a85639295 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -32,7 +32,7 @@ class Chef
include Chef::Mixin::Language
include Chef::Mixin::ConvertToClassName
- attr_accessor :actions, :params, :provider, :updated, :allowed_actions, :collection, :cookbook_name, :recipe_name
+ attr_accessor :actions, :params, :provider, :updated, :allowed_actions, :collection, :cookbook_name, :recipe_name, :enclosing_provider
attr_reader :resource_name, :source_line, :node
def initialize(name, collection=nil, node=nil)
@@ -61,6 +61,17 @@ class Chef
@source_line = ::File.expand_path(@source_line) if @source_line
end
end
+
+ # If an unknown method is invoked, determine whether the enclosing Provider's
+ # lexical scope can fulfill the request. E.g. This happens when the Resource's
+ # block invokes new_resource.
+ def method_missing(method_symbol, *args, &block)
+ if enclosing_provider && enclosing_provider.respond_to?(method_symbol)
+ enclosing_provider.send(method_symbol, *args, &block)
+ else
+ raise NoMethodError, "undefined method `#{method_symbol.to_s}' for #{self.class.to_s}"
+ end
+ end
def load_prior_resource
begin
diff --git a/chef/spec/unit/lwrp_spec.rb b/chef/spec/unit/lwrp_spec.rb
index db11fabe6a..a993b8446c 100644
--- a/chef/spec/unit/lwrp_spec.rb
+++ b/chef/spec/unit/lwrp_spec.rb
@@ -68,13 +68,13 @@ describe Chef::Provider do
injector = Chef::Resource::LwrpFoo.new("morpheus")
injector.action(:pass_buck)
- injector.provider(Chef::Provider::LwrpBuckPasser)
+ injector.provider(:lwrp_buck_passer)
dummy = Chef::Resource::ZenMaster.new("keanu reeves")
dummy.provider(Chef::Provider::Easy)
rc.insert(injector)
rc.insert(dummy)
- Chef::Runner.new(node, rc, {}).converge
+ Chef::Runner.new(node, rc).converge
rc[0].should eql(injector)
rc[1].name.should eql(:prepared_thumbs)
@@ -88,10 +88,10 @@ describe Chef::Provider do
injector = Chef::Resource::LwrpFoo.new("morpheus")
injector.action(:pass_buck)
- injector.provider(Chef::Provider::LwrpBuckPasser)
+ injector.provider(:lwrp_buck_passer)
injector2 = Chef::Resource::LwrpBar.new("tank")
injector2.action(:pass_buck)
- injector2.provider(Chef::Provider::LwrpBuckPasser2)
+ injector2.provider(:lwrp_buck_passer_2)
dummy = Chef::Resource::ZenMaster.new("keanu reeves")
dummy.provider(Chef::Provider::Easy)
@@ -99,7 +99,7 @@ describe Chef::Provider do
rc.insert(dummy)
rc.insert(injector2)
- Chef::Runner.new(node, rc, {}).converge
+ Chef::Runner.new(node, rc).converge
rc[0].should eql(injector)
rc[1].name.should eql(:prepared_thumbs)
@@ -109,5 +109,35 @@ describe Chef::Provider do
rc[5].name.should eql(:prepared_eyes)
rc[6].name.should eql(:dried_paint_watched)
end
+
+ it "should properly handle a new_resource reference" do
+ node = Chef::Node.new
+ rc = Chef::ResourceCollection.new
+
+ res = Chef::Resource::LwrpFoo.new("morpheus")
+ res.monkey("bob")
+ res.action(:twiddle_thumbs)
+ res.provider(:lwrp_monkey_name_printer)
+ rc.insert(res)
+
+ STDOUT.should_receive(:write).with("my monkey's name is 'bob'").exactly(:once)
+ STDOUT.should_receive(:write).with("\n").exactly(:once)
+ Chef::Runner.new(node, rc).converge
+ end
+
+ it "should properly handle an embedded Resource accessing the enclosing Provider's scope" do
+ node = Chef::Node.new
+ rc = Chef::ResourceCollection.new
+
+ res = Chef::Resource::LwrpFoo.new("morpheus")
+ res.monkey("bob")
+ res.action(:twiddle_thumbs)
+ res.provider(:lwrp_embedded_resource_accesses_providers_scope)
+ rc.insert(res)
+
+ STDOUT.should_receive(:write).with("my monkey's name is 'bob, the monkey'").exactly(:once)
+ STDOUT.should_receive(:write).with("\n").exactly(:once)
+ Chef::Runner.new(node, rc).converge
+ end
end