diff options
-rw-r--r-- | lib/chef/dsl/recipe.rb | 136 | ||||
-rw-r--r-- | spec/support/lib/chef/resource/zen_master.rb | 2 | ||||
-rw-r--r-- | spec/unit/recipe_spec.rb | 48 |
3 files changed, 147 insertions, 39 deletions
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb index f3507a8031..a1bdd9d665 100644 --- a/lib/chef/dsl/recipe.rb +++ b/lib/chef/dsl/recipe.rb @@ -31,53 +31,113 @@ class Chef include Chef::Mixin::ConvertToClassName + # Default +cookbook_name+ implementation, always returns "none". This + # should be overriden in including classes whenever a meaningful value + # can be provided. + def cookbook_name + "none" + end + + # Default +recipe_name+ implementation, always returns "none". This + # should be overridden in including classes whenever a meaningful value + # can be provided. + def recipe_name + "none" + end + def method_missing(method_symbol, *args, &block) # If we have a definition that matches, we want to use that instead. This should # let you do some really crazy over-riding of "native" types, if you really want # to. - if run_context.definitions.has_key?(method_symbol) - # This dupes the high level object, but we still need to dup the params - new_def = run_context.definitions[method_symbol].dup - new_def.params = new_def.params.dup - new_def.node = run_context.node - # This sets up the parameter overrides - new_def.instance_eval(&block) if block - new_recipe = Chef::Recipe.new(cookbook_name, @recipe_name, run_context) - new_recipe.params = new_def.params - new_recipe.params[:name] = args[0] - new_recipe.instance_eval(&new_def.recipe) + if has_resource_definition?(method_symbol) + evaluate_resource_definition(method_symbol, *args, &block) else # Otherwise, we're rocking the regular resource call route. - - # Checks the new platform => short_name => resource mapping initially - # then fall back to the older approach (Chef::Resource.const_get) for - # backward compatibility - resource_class = Chef::Resource.resource_for_node(method_symbol, run_context.node) - - super unless resource_class - raise ArgumentError, "You must supply a name when declaring a #{method_symbol} resource" unless args.size > 0 - - # If we have a resource like this one, we want to steal its state - args << run_context - resource = resource_class.new(*args) - resource.source_line = caller[0] - 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 - # Evaluate resource attribute DSL - resource.instance_eval(&block) if block - - # Run optional resource hook - resource.after_created - - run_context.resource_collection.insert(resource) - resource + declare_resource(method_symbol, args[0], caller[0], &block) end end + def has_resource_definition?(name) + yes_or_no = run_context.definitions.has_key?(name) + + yes_or_no + end + + # Processes the arguments and block as a resource definition. + def evaluate_resource_definition(definition_name, *args, &block) + + # This dupes the high level object, but we still need to dup the params + new_def = run_context.definitions[definition_name].dup + + new_def.params = new_def.params.dup + new_def.node = run_context.node + # This sets up the parameter overrides + new_def.instance_eval(&block) if block + + + new_recipe = Chef::Recipe.new(cookbook_name, recipe_name, run_context) + new_recipe.params = new_def.params + new_recipe.params[:name] = args[0] + new_recipe.instance_eval(&new_def.recipe) + new_recipe + end + + # Instantiates a resource (via #build_resource), then adds it to the + # resource collection. Note that resource classes are looked up directly, + # so this will create the resource you intended even if the method name + # corresponding to that resource has been overridden. + def declare_resource(type, name, created_at=nil, &resource_attrs_block) + created_at ||= caller[0] + + resource = build_resource(type, name, created_at, &resource_attrs_block) + + run_context.resource_collection.insert(resource) + resource + end + + # Instantiate a resource of the given +type+ with the given +name+ and + # attributes as given in the +resource_attrs_block+. + # + # The resource is NOT added to the resource collection. + def build_resource(type, name, created_at=nil, &resource_attrs_block) + created_at ||= caller[0] + + # Checks the new platform => short_name => resource mapping initially + # then fall back to the older approach (Chef::Resource.const_get) for + # backward compatibility + resource_class = resource_class_for(type) + + super unless resource_class + raise ArgumentError, "You must supply a name when declaring a #{type} resource" if name.nil? + + resource = resource_class.new(name, run_context) + resource.source_line = created_at + resource.cookbook_name = cookbook_name + resource.recipe_name = recipe_name + # If we have a resource like this one, we want to steal its state + # This behavior is very counter-intuitive and should be removed. + # See CHEF-3694, https://tickets.opscode.com/browse/CHEF-3694 + resource.load_prior_resource + # Determine whether this resource is being created in the context of an enclosing Provider + resource.enclosing_provider = self.is_a?(Chef::Provider) ? self : nil + + # XXX: This is very crufty, but it's required for resource definitions + # to work properly :( + resource.params = @params + + # Evaluate resource attribute DSL + resource.instance_eval(&resource_attrs_block) if block_given? + + # Run optional resource hook + resource.after_created + + resource + end + + def resource_class_for(snake_case_name) + Chef::Resource.resource_for_node(snake_case_name, run_context.node) + end + end end end diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb index 7a7076ca4b..4725e77f49 100644 --- a/spec/support/lib/chef/resource/zen_master.rb +++ b/spec/support/lib/chef/resource/zen_master.rb @@ -34,7 +34,7 @@ class Chef end def something(arg=nil) - if arg == true or arg == false + if !arg.nil? @something = arg end @something diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index a2c6e4b4cb..bc3a62be3f 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -127,6 +127,54 @@ describe Chef::Recipe do end end + describe "creating resources via build_resource" do + let(:zm_resource) do + @recipe.build_resource(:zen_master, "klopp") do + something "bvb" + end + end + + it "applies attributes from the block to the resource" do + zm_resource.something.should == "bvb" + end + + it "sets contextual attributes on the resource" do + zm_resource.recipe_name.should == "test" + zm_resource.cookbook_name.should == "hjk" + zm_resource.source_line.should include(__FILE__) + end + + it "does not add the resource to the resource collection" do + zm_resource # force let binding evaluation + expect { @run_context.resource_collection.resources(:zen_master => "klopp") }.to raise_error(Chef::Exceptions::ResourceNotFound) + end + + end + + describe "creating resources via declare_resource" do + let(:zm_resource) do + @recipe.declare_resource(:zen_master, "klopp") do + something "bvb" + end + end + + it "applies attributes from the block to the resource" do + zm_resource.something.should == "bvb" + end + + it "sets contextual attributes on the resource" do + zm_resource.recipe_name.should == "test" + zm_resource.cookbook_name.should == "hjk" + zm_resource.source_line.should include(__FILE__) + end + + it "adds the resource to the resource collection" do + zm_resource # force let binding evaluation + @run_context.resource_collection.resources(:zen_master => "klopp").should == zm_resource + end + + end + describe "resource definitions" do it "should execute defined resources" do crow_define = Chef::ResourceDefinition.new |