summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-12-13 12:34:50 -0800
committerJohn Keiser <john@johnkeiser.com>2015-12-13 12:34:50 -0800
commitf08d833465d9d2ea083d12a66958753afa2b1c7e (patch)
treede01dc2a2cf8cbb04a64b0d22fd4735a98bcb96e
parent7814a405d629b612cd0b93266db79e2427f59d7d (diff)
parent50b463e33a2ea6645f3d6fcee836b6f88842bd32 (diff)
downloadchef-f08d833465d9d2ea083d12a66958753afa2b1c7e.tar.gz
Merge branch 'jk/action-class-do'
-rw-r--r--lib/chef/resource.rb40
-rw-r--r--spec/integration/recipes/resource_action_spec.rb75
2 files changed, 98 insertions, 17 deletions
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index f969ccd84c..a7b1f63463 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -1172,14 +1172,18 @@ class Chef
# using `action :x do ... end`, then there is no need for this class and
# `action_class` will be `nil`.
#
+ # If a block is passed, the action_class is always created and the block is
+ # run inside it.
+ #
# @api private
#
- def self.action_class
- @action_class ||
- # If the superclass needed one, then we need one as well.
- if superclass.respond_to?(:action_class) && superclass.action_class
- declare_action_class
- end
+ def self.action_class(&block)
+ return @action_class if @action_class && !block
+ # If the superclass needed one, then we need one as well.
+ if block || (superclass.respond_to?(:action_class) && superclass.action_class)
+ @action_class = declare_action_class(&block)
+ end
+ @action_class
end
#
@@ -1189,19 +1193,21 @@ class Chef
# If a block is passed, it is run inside the action_class.
#
# @api private
- def self.declare_action_class
- return @action_class if @action_class
-
- if superclass.respond_to?(:action_class)
- base_provider = superclass.action_class
- end
- base_provider ||= Chef::Provider
+ def self.declare_action_class(&block)
+ @action_class ||= begin
+ if superclass.respond_to?(:action_class)
+ base_provider = superclass.action_class
+ end
+ base_provider ||= Chef::Provider
- resource_class = self
- @action_class = Class.new(base_provider) do
- include ActionClass
- self.resource_class = resource_class
+ resource_class = self
+ Class.new(base_provider) do
+ include ActionClass
+ self.resource_class = resource_class
+ end
end
+ @action_class.class_eval(&block) if block
+ @action_class
end
#
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index fe6b4083f1..0ea67ea5f2 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -425,6 +425,81 @@ describe "Resource.action" do
}.to raise_error(/Property template of has_property_named_template\[hi\] cannot be passed a block! If you meant to create a resource named template instead, you'll need to first rename the property./)
end
end
+
+ context "When a resource declares methods in action_class and declare_action_class" do
+ class DeclaresActionClassMethods < Chef::Resource
+ use_automatic_resource_name
+ property :x
+ action :create do
+ new_resource.x = a + b + c + d
+ end
+ action_class do
+ def a
+ 1
+ end
+ end
+ declare_action_class do
+ def b
+ 2
+ end
+ end
+ action_class do
+ def c
+ 3
+ end
+ end
+ declare_action_class do
+ def d
+ 4
+ end
+ end
+ end
+
+ it "the methods are not available on the resource" do
+ expect { DeclaresActionClassMethods.new('hi').a }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').b }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').c }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').d }.to raise_error(NameError)
+ end
+
+ it "the methods are available to the action" do
+ r = nil
+ expect_recipe {
+ r = declares_action_class_methods 'hi'
+ }.to emit_no_warnings_or_errors
+ expect(r.x).to eq(10)
+ end
+
+ context "And a subclass also creates a method" do
+ class DeclaresActionClassMethodsToo < DeclaresActionClassMethods
+ use_automatic_resource_name
+ action :create do
+ new_resource.x a+b+c+d+e
+ end
+ action_class do
+ def e
+ 5
+ end
+ end
+ end
+
+ it "the methods are not available on the resource" do
+ expect { DeclaresActionClassMethods.new('hi').a }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').b }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').c }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').d }.to raise_error(NameError)
+ expect { DeclaresActionClassMethods.new('hi').e }.to raise_error(NameError)
+ end
+
+ it "the methods are available to the action" do
+ r = nil
+ expect_recipe {
+ r = declares_action_class_methods_too 'hi'
+ }.to emit_no_warnings_or_errors
+ expect(r.x).to eq(15)
+ end
+ end
+ end
end
end