poise / poise-derived

A Chef cookbook for defining lazily evaluated node attributes.
Apache License 2.0
22 stars 5 forks source link

Lazy evaluation not working inside hash or array #4

Closed marcomancuso closed 7 years ago

marcomancuso commented 7 years ago

attributes/default.rb

node.default['a']['b'] = 'aaaa'
node.default['a']['c'] = lazy [{
  'a' => '%{a.b}/test',
  'b' => 'sasdasd'
}

recipes/default.rb

log 'message' do
  message node['a']['c'].to_s
  level :info
end

ERROR Message:

Recipe Compile Error in /tmp/kitchen/cache/cookbooks/.../recipes/default.rb

   NoMethodError
   -------------
   undefined method `scan' for #<Array:0x000000018e19e8>

   Cookbook Trace:
   ---------------
     /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:116:in `_evaluate'
     /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:77:in `to_s'
     /tmp/kitchen/cache/cookbooks/.../recipes/default.rb:18:in `block in from_file'
     /tmp/kitchen/cache/cookbooks/.../recipes/default.rb:17:in `from_file'
     /tmp/kitchen/cache/cookbooks/.../recipes/default.rb:8:in `from_file'

   Relevant File Content:
   ----------------------
   /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:

   109:      def _evaluate
   110:        return @evaluate_cache if defined?(@evaluate_cache)
   111:        @evaluate_cache = if @value.is_a?(Proc)
   112:          # Block mode, just run the block.
   113:          @node.instance_eval(&@value).to_s
   114:        else
   115:          # String mode, parse the template string and fill it in.
   116>>         format_keys = @value.scan(FORMAT_STRING_REGEXP).inject({}) do |memo, (key)|
   117:            # Keys must be symbols because Ruby.
   118:            memo[key.to_sym] = key.split(/\./).inject(@node) {|n, k| n.nil? ? n : n[k] }
   119:            memo
   120:          end
   121:          @value % format_keys
   122:        end
   123:      end
   124:    end
   125:  end

   Platform:
   ---------
   x86_64-linux
coderanger commented 7 years ago

That is correct, it has to be at the top level and when using the block form you have to do using the real interpolation so

node.default['a']['b'] = 'aaaa'
node.default['a']['c'] = lazy {
  [
    {
      'a' => "#{node['a']['b']}/test",
      'b' => 'sasdasd',
    },
  ]
}

Using arrays in node attributes is always a bit awkward and I strongly recommend you avoid them. If you could replace that array with a hash of some kind then you could expand it out and use the nicer syntax.