summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/provider/template/content.rb37
-rw-r--r--lib/chef/resource/template.rb22
-rw-r--r--spec/data/cookbooks/openldap/templates/default/helper_test.erb1
-rw-r--r--spec/functional/resource/template_spec.rb97
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb3
-rw-r--r--spec/unit/provider/template/content_spec.rb10
-rw-r--r--spec/unit/resource/template_spec.rb43
7 files changed, 208 insertions, 5 deletions
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index 756db4642c..5629ee9a19 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -22,6 +22,37 @@ require 'chef/file_content_management/content_base'
class Chef
class Provider
class Template
+
+ # TODO: extract to file
+ # TODO: integrate into mixin/template (make it work with partials)
+ # TODO: docs
+ class TemplateContext < Erubis::Context
+
+ def _define_helpers(helper_methods)
+ # TODO (ruby 1.8 hack)
+ # This is most elegantly done with Object#define_singleton_method,
+ # however ruby 1.8.7 does not support that, so we create a module and
+ # include it. This should be revised when 1.8 support is not needed.
+ helper_mod = Module.new do
+ helper_methods.each do |method_name, method_body|
+ define_method(method_name, &method_body)
+ end
+ end
+ extend(helper_mod)
+ end
+
+ def _define_helpers_from_blocks(blocks)
+ blocks.each do |module_body|
+ helper_mod = Module.new(&module_body)
+ extend(helper_mod)
+ end
+ end
+
+ def _extend_modules(module_names)
+ module_names.each { |mod| extend(mod) }
+ end
+ end
+
class Content < Chef::FileContentManagement::ContentBase
include Chef::Mixin::Template
@@ -35,10 +66,12 @@ class Chef
private
def file_for_provider
- context = {}
- context.merge!(@new_resource.variables)
+ context = TemplateContext.new(@new_resource.variables)
context[:node] = @run_context.node
context[:template_finder] = template_finder
+ context._define_helpers(@new_resource.inline_helper_blocks)
+ context._define_helpers_from_blocks(@new_resource.inline_helper_modules)
+ context._extend_modules(@new_resource.helper_modules)
file = nil
render_template(IO.read(template_location), context) { |t| file = t }
file
diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb
index af51b64700..0e468ed401 100644
--- a/lib/chef/resource/template.rb
+++ b/lib/chef/resource/template.rb
@@ -29,6 +29,10 @@ class Chef
provides :template, :on_platforms => :all
+ attr_reader :inline_helper_blocks
+ attr_reader :inline_helper_modules
+ attr_reader :helper_modules
+
def initialize(name, run_context=nil)
super
@resource_name = :template
@@ -38,6 +42,9 @@ class Chef
@local = false
@variables = Hash.new
@provider = Chef::Provider::Template
+ @inline_helper_blocks = {}
+ @inline_helper_modules = []
+ @helper_modules = []
end
def source(file=nil)
@@ -71,6 +78,21 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
+
+ def helper(method_name, &block)
+ # TODO: method_name must be symbol or coerce.
+ # TODO: block is not optional.
+ @inline_helper_blocks[method_name] = block
+ end
+
+ def helpers(module_name=nil,&block)
+ if block_given?
+ @inline_helper_modules << block
+ else
+ @helper_modules << module_name
+ end
+ end
+
end
end
end
diff --git a/spec/data/cookbooks/openldap/templates/default/helper_test.erb b/spec/data/cookbooks/openldap/templates/default/helper_test.erb
new file mode 100644
index 0000000000..92e6fe0427
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/helper_test.erb
@@ -0,0 +1 @@
+<%= helper_method %>
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index ae568a496a..4d8c6c8738 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -71,4 +71,101 @@ describe Chef::Resource::Template do
IO.read(path).should == expected_content
end
end
+
+ describe "when the template resource defines helper methods" do
+
+ include_context "diff disabled"
+
+ let!(:resource) do
+ r = create_resource
+ r.source "helper_test.erb"
+ r
+ end
+
+ let(:expected_content) { "value from helper method\n" }
+
+ shared_examples "a template with helpers" do
+ it "generates expected content by calling helper methods" do
+ resource.run_action(:create)
+ IO.read(path).should == expected_content
+ end
+ end
+
+ context "using single helper syntax" do
+ before do
+ resource.helper(:helper_method) { "value from helper method" }
+ end
+
+ it_behaves_like "a template with helpers"
+ end
+
+ context "using single helper syntax referencing @node" do
+ before do
+ node.set[:helper_test_attr] = "value from helper method"
+ resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" }
+ end
+
+ it_behaves_like "a template with helpers"
+ end
+
+ context "using an inline block to define helpers" do
+ before do
+ resource.helpers do
+ def helper_method
+ "value from helper method"
+ end
+ end
+ end
+
+ it_behaves_like "a template with helpers"
+ end
+
+ context "using an inline block referencing @node" do
+ before do
+ node.set[:helper_test_attr] = "value from helper method"
+
+ resource.helpers do
+ def helper_method
+ @node[:helper_test_attr]
+ end
+ end
+ end
+
+ it_behaves_like "a template with helpers"
+
+ end
+
+ context "using a module from a library" do
+
+ module ExampleModule
+ def helper_method
+ "value from helper method"
+ end
+ end
+
+ before do
+ resource.helpers(ExampleModule)
+ end
+
+ it_behaves_like "a template with helpers"
+
+ end
+ context "using a module from a library referencing @node" do
+
+ module ExampleModuleReferencingATNode
+ def helper_method
+ @node[:helper_test_attr]
+ end
+ end
+
+ before do
+ node.set[:helper_test_attr] = "value from helper method"
+
+ resource.helpers(ExampleModuleReferencingATNode)
+ end
+
+ it_behaves_like "a template with helpers"
+
+ end
+ end
end
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 948b3c5a38..cea5c89e87 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -34,8 +34,7 @@ describe Chef::Cookbook::SyntaxCheck do
@recipes = %w{default.rb gigantor.rb one.rb}.map { |f| File.join(cookbook_path, 'recipes', f) }
@ruby_files = @attr_files + @defn_files + @recipes
- @template_files = %w{openldap_stuff.conf.erb openldap_variable_stuff.conf.erb test.erb}.map { |f| File.join(cookbook_path, 'templates', 'default', f)}
-
+ @template_files = %w{helper_test.erb openldap_stuff.conf.erb openldap_variable_stuff.conf.erb test.erb}.map { |f| File.join(cookbook_path, 'templates', 'default', f)}
end
it "creates a syntax checker given the cookbook name when Chef::Config.cookbook_path is set" do
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 946549238c..4061d99d7b 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -21,7 +21,15 @@ require 'spec_helper'
describe Chef::Provider::Template::Content do
let(:new_resource) do
- mock("Chef::Resource::Template (new)", :cookbook_name => 'openldap', :source => 'openldap_stuff.conf.erb', :local => false, :cookbook => nil, :variables => {})
+ mock("Chef::Resource::Template (new)",
+ :cookbook_name => 'openldap',
+ :source => 'openldap_stuff.conf.erb',
+ :local => false,
+ :cookbook => nil,
+ :variables => {},
+ :inline_helper_blocks => {},
+ :inline_helper_modules => [],
+ :helper_modules => [])
end
let(:rendered_file_location) { Dir.tmpdir + '/openldap_stuff.conf' }
diff --git a/spec/unit/resource/template_spec.rb b/spec/unit/resource/template_spec.rb
index fc16d6c21d..35c684c9ff 100644
--- a/spec/unit/resource/template_spec.rb
+++ b/spec/unit/resource/template_spec.rb
@@ -105,4 +105,47 @@ describe Chef::Resource::Template do
@resource.identity.should == "/tmp/foo.txt"
end
end
+
+ describe "defining helper methods" do
+
+ it "collects helper method bodies as blocks" do
+ @resource.helper(:example_1) { "example_1" }
+ @resource.helper(:example_2) { "example_2" }
+ @resource.inline_helper_blocks[:example_1].call.should == "example_1"
+ @resource.inline_helper_blocks[:example_2].call.should == "example_2"
+ end
+
+ it "raises an error when attempting to define a helper method without a method body" do
+ pending
+ @resource.helper(:example) # should raise_error()
+ end
+
+ it "collects helper module bodies as blocks" do
+ @resource.helpers do
+ def example_1
+ "example_1"
+ end
+ end
+ module_body = @resource.inline_helper_modules.first
+ module_body.should be_a(Proc)
+ test_mod = Module.new(&module_body)
+ test_context = Object.new
+ test_context.extend(test_mod)
+ test_context.example_1.should == "example_1"
+ end
+
+ module ExampleHelpers
+ end
+
+ it "collects helper modules" do
+ @resource.helpers(ExampleHelpers)
+ @resource.helper_modules.should include(ExampleHelpers)
+ end
+
+ it "raises an error if a non-module is given as a helper module" do
+ pending
+ end
+
+ end
+
end