Module: Poise::Helpers::TemplateContent

Includes:
LWRPPolyfill, LazyDefault, OptionCollector
Included in:
Resource
Defined in:
lib/poise/helpers/template_content.rb

Overview

A resource mixin to add a new kind of attribute, template content. TODO

Since:

  • 1.0.0

Class Method Summary collapse

Methods included from OptionCollector

option_collector_attribute

Methods included from LazyDefault

lazy, #set_or_return

Class Method Details

.attribute(name, options = {}) ⇒ Object

Since:

  • 1.0.0



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/poise/helpers/template_content.rb', line 38

def attribute(name, options={})
  if options.delete(:template)
    name_prefix = name.empty? ? '' : "#{name}_"

    # If you are reading this, I'm so sorry
    # This is used for computing the default cookbook below
    parent_filename = caller.first.reverse.split(':', 4).last.reverse

    # If our parent class also declared a template_content attribute on the same name, inherit its options
    if superclass.respond_to?("_#{name_prefix}_template_content_options")
      options = superclass.send("_#{name_prefix}_template_content_options").merge(options)
    end

    # Template source path if using a template
    attribute("#{name_prefix}source", kind_of: String)
    define_method("_#{name_prefix}source") do
      send("#{name_prefix}source") || maybe_eval(options[:default_source])
    end

    # Template cookbook name if using a template
    attribute("#{name_prefix}cookbook", kind_of: [String, Symbol], default: lazy do
      if send("#{name_prefix}source")
        cookbook_name
      elsif options[:default_cookbook]
        maybe_eval(options[:default_cookbook])
      else
        Poise::Utils.find_cookbook_name(run_context, parent_filename)
      end
    end)

    # Template variables if using a template
    attribute("#{name_prefix}options", option_collector: true)

    # The big one, get/set content, but if you are getting and no
    # explicit content was given, try to render the template
    define_method("#{name_prefix}content") do |arg=nil, no_compute=false|
      ret = set_or_return("#{name_prefix}content", arg, kind_of: String)
      if !ret && !arg && !no_compute
        ret = send("_#{name_prefix}content")
        # Cache the results for next time
        set_or_return("#{name_prefix}content", ret, {}) if ret
      end
      ret
    end

    # Validate that arguments work
    define_method("_#{name_prefix}validate") do
      if options[:required] && !send("_#{name_prefix}source") && !send("#{name_prefix}content", nil, true)
        raise Chef::Exceptions::ValidationFailed, "#{self}: One of #{name_prefix}source or #{name_prefix}content is required"
      end
      if send("#{name_prefix}source") && send("#{name_prefix}content", nil, true)
        raise Chef::Exceptions::ValidationFailed, "#{self}: Only one of #{name_prefix}source or #{name_prefix}content can be specified"
      end
    end

    # Monkey patch #after_create to run best-effort validation. Arguments
    # could be changed after creation, but this gives nicer errors for
    # most cases.
    unless options[:no_validate_on_create]
      old_after_created = instance_method(:after_created)
      define_method(:after_created) do
        old_after_created.bind(self).call
        send("_#{name_prefix}validate")
      end
    end

    # Compile the needed content
    define_method("_#{name_prefix}content") do
      # Run validation again
      send("_#{name_prefix}validate")
      # Get all the relevant parameters
      content = send("#{name_prefix}content", nil, true)
      source = send("_#{name_prefix}source")
      if content
        content # I don't think it can ever hit this branch
      elsif source
        cookbook = send("#{name_prefix}cookbook")
        template_options = send("#{name_prefix}options")
        send("_#{name_prefix}render_template", source, cookbook, template_options)
      else
        maybe_eval(options[:default])
      end
    end

    # Actually render a template
    define_method("_#{name_prefix}render_template") do |source, cookbook, template_options|
      all_template_options = {}
      all_template_options.update(maybe_eval(options[:default_options])) if options[:default_options]
      all_template_options.update(template_options)
      all_template_options[:new_resource] = self
      finder = Chef::Provider::TemplateFinder.new(run_context, cookbook, node)
      context = Chef::Mixin::Template::TemplateContext.new(all_template_options)
      context[:node] = node
      context[:template_finder] = finder
      context.render_template(finder.find(source))
    end

    # Used to check if a parent class already defined a template_content thing here
    define_singleton_method("_#{name_prefix}_template_content_options") do
      options
    end
  else
    super if defined?(super)
  end
end