summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielsdeleo <dan@opscode.com>2014-01-31 16:44:48 -0800
committerdanielsdeleo <dan@opscode.com>2014-02-03 17:08:54 -0800
commitb4c8facbf495423950345937ab4a8d6d8792ad16 (patch)
tree6ef2f4bc852b4ee3e2080f612dd9b43921f1e9ac
parent040865b55964056d3249de896baafe60587f5105 (diff)
downloadchef-b4c8facbf495423950345937ab4a8d6d8792ad16.tar.gz
Expose resource creation via more static methods
-rw-r--r--lib/chef/dsl/recipe.rb136
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb2
-rw-r--r--spec/unit/recipe_spec.rb48
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