summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielsdeleo <dan@opscode.com>2013-05-27 11:15:01 -0700
committerdanielsdeleo <dan@opscode.com>2013-05-29 11:32:22 -0700
commitca4cd1f830fae2fac3e2c18bbd2693b0caddcb33 (patch)
tree705a53ee2a2fd379096a8a15ebd6c070a7667639
parent00de51bb552ea2e613afec4825277b855775b71e (diff)
downloadchef-ca4cd1f830fae2fac3e2c18bbd2693b0caddcb33.tar.gz
Pass template extensions through to partials
- move the new TemplateContext code into mixin/template and consolidate with ChefContext - Copy extension modules from parent template to partial template. - Functional tests for helpers with partials.
-rw-r--r--lib/chef/mixin/template.rb83
-rw-r--r--lib/chef/provider/template/content.rb30
-rw-r--r--spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb1
-rw-r--r--spec/functional/resource/template_spec.rb15
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb8
-rw-r--r--spec/unit/mixin/template_spec.rb9
6 files changed, 101 insertions, 45 deletions
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index 4906734900..0e7bbeb73a 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -23,7 +23,32 @@ class Chef
module Mixin
module Template
+ # == ChefContext
+ # ChefContext was previously used to mix behavior into Erubis::Context so
+ # that it would be available to templates. This behavior has now moved to
+ # TemplateContext, but this module is still mixed in to the
+ # TemplateContext class so that any user code that modified ChefContext
+ # will continue to work correctly.
module ChefContext
+ end
+
+ # TODO: extract to file
+ # TODO: docs
+ class TemplateContext < Erubis::Context
+
+ include ChefContext
+
+ attr_reader :_extension_modules
+
+ def initialize(variables)
+ super
+ @_extension_modules = []
+ end
+
+ ###
+ # USER FACING API
+ ###
+
def node
return @node if @node
raise "Could not find a value for node. If you are explicitly setting variables in a template, " +
@@ -56,22 +81,60 @@ class Chef
def render(partial_name, options = {})
raise "You cannot render partials in this context" unless @template_finder
- if variables = options.delete(:variables)
- context = {}
- context.merge!(variables)
- context[:node] = @node
- context[:template_finder] = @template_finder
- else
- context = self.dup
- end
+ partial_variables = options.delete(:variables) || _public_instance_variables
+ partial_context = self.class.new(partial_variables)
+ partial_context._extend_modules(@_extension_modules)
template_location = @template_finder.find(partial_name, options)
eruby = Erubis::Eruby.new(IO.read(template_location))
- eruby.evaluate(context)
+ eruby.evaluate(partial_context)
+ end
+
+ ###
+ # INTERNAL PUBLIC API
+ ###
+
+ 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
+ @_extension_modules << helper_mod
+ extend(helper_mod)
+ end
+
+ def _define_helpers_from_blocks(blocks)
+ blocks.each do |module_body|
+ helper_mod = Module.new(&module_body)
+ extend(helper_mod)
+ @_extension_modules << helper_mod
+ end
+ end
+
+ def _extend_modules(module_names)
+ module_names.each do |mod|
+ extend(mod)
+ @_extension_modules << mod
+ end
+ end
+
+ def _public_instance_variables
+ all_ivars = instance_variables
+ all_ivars.delete(:@_extension_modules)
+ all_ivars.inject({}) do |ivar_map, ivar_symbol_name|
+ value = instance_variable_get(ivar_symbol_name)
+ name_without_at = ivar_symbol_name.to_s[1..-1].to_sym
+ ivar_map[name_without_at] = value
+ ivar_map
+ end
end
end
- ::Erubis::Context.send(:include, ChefContext)
# Render a template with Erubis. Takes a template as a string, and a
# context hash.
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index 5629ee9a19..f26b7a5b92 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -23,36 +23,6 @@ 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
diff --git a/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb b/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb
new file mode 100644
index 0000000000..f94b6b5979
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb
@@ -0,0 +1 @@
+<%= render("helper_test.erb").strip %>
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index 4d8c6c8738..d966aefb6a 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -42,6 +42,11 @@ describe Chef::Resource::Template do
resource = Chef::Resource::Template.new(path, run_context)
resource.source('openldap_stuff.conf.erb')
resource.cookbook('openldap')
+
+ # TODO: partials rely on `cookbook_name` getting set by chef internals and
+ # ignore the user-set `cookbook` attribute.
+ resource.cookbook_name = "openldap"
+
resource
end
@@ -167,5 +172,15 @@ describe Chef::Resource::Template do
it_behaves_like "a template with helpers"
end
+
+ context "using helpers with partial templates" do
+ before do
+ resource.source("helpers_via_partial_test.erb")
+ resource.helper(:helper_method) { "value from helper method" }
+ 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 cea5c89e87..7007d06920 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -33,8 +33,12 @@ describe Chef::Cookbook::SyntaxCheck do
@defn_files = %w{client.rb server.rb}.map { |f| File.join(cookbook_path, 'definitions', f)}
@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{helper_test.erb openldap_stuff.conf.erb openldap_variable_stuff.conf.erb test.erb}.map { |f| File.join(cookbook_path, 'templates', 'default', f)}
+ basenames = %w{ helpers_via_partial_test.erb
+ helper_test.erb
+ openldap_stuff.conf.erb
+ openldap_variable_stuff.conf.erb
+ test.erb }
+ @template_files = basenames.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/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb
index bded4a697c..1fc2265ee0 100644
--- a/spec/unit/mixin/template_spec.rb
+++ b/spec/unit/mixin/template_spec.rb
@@ -24,16 +24,19 @@ describe Chef::Mixin::Template, "render_template" do
before :each do
@template = TinyTemplateClass.new
+ @context = Chef::Mixin::Template::TemplateContext.new({})
end
it "should render the template evaluated in the given context" do
- @template.render_template("<%= @foo %>", { :foo => "bar" }) do |tmp|
+ @context[:foo] = "bar"
+ @template.render_template("<%= @foo %>", @context) do |tmp|
tmp.open.read.should == "bar"
end
end
it "should provide a node method to access @node" do
- @template.render_template("<%= node %>",{:node => "tehShizzle"}) do |tmp|
+ @context[:node] = "tehShizzle"
+ @template.render_template("<%= node %>", @context) do |tmp|
tmp.open.read.should == "tehShizzle"
end
end
@@ -64,7 +67,7 @@ describe Chef::Mixin::Template, "render_template" do
@content_provider = Chef::Provider::Template::Content.new(@resource, @current_resource, @run_context)
- @template_context = {}
+ @template_context = Chef::Mixin::Template::TemplateContext.new({})
@template_context[:node] = @node
@template_context[:template_finder] = Chef::Provider::TemplateFinder.new(@run_context, @resource.cookbook_name, @node)
end