diff options
author | chris <chris@opscodes-macbook-pro-2.local> | 2009-10-14 21:41:45 +0000 |
---|---|---|
committer | chris <chris@opscodes-macbook-pro-2.local> | 2009-10-14 21:42:00 +0000 |
commit | 8d97aadcea8e407a39190e3baf8df138cd69077d (patch) | |
tree | 1b07a40f231c5d693cec10f079c14c2d98e80405 | |
parent | b8ebb42d8be6d4b16daff6effdf1509875177000 (diff) | |
download | chef-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.rb | 38 | ||||
-rw-r--r-- | chef/lib/chef/resource.rb | 13 | ||||
-rw-r--r-- | chef/spec/unit/lwrp_spec.rb | 40 |
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 |