adrift on a cosmic ocean

Writings on various topics (mostly technical) from Oliver Hookins and Angela Collins. We have lived in Berlin since 2009, have two kids, and have far too little time to really justify having a blog.

Did you know...

Posted by Oliver on the 7th of April, 2011 in category Thoughts
Tagged with: erbpuppetruby

that if you pass a Ruby symbol through the Puppet external node classifier interface, through a parametrised class and into an ERB template, it will arrive (relatively) unscathed? OK not really, but the special undef Puppet symbol does.

ENC says: myvariable: :undef

Puppet interprets it as a standard undef (which is internally represented as Puppet::Parser::AST::Undef).

When passed through to an ERB template, it again arrives as the symbol form :undef, which means you can do things like

<% if proxy_host != :undef -%>
proxyhost=<%= proxy_host %>
proxyport=<%= proxy_port %>
<% end -%>

This does bring up an interesting conundrum about variable usage in templates. When you are faced with the pattern of "check some variable, and if the condition is true, enable a whole block of text which involves substitution", what is the best way to express it? There are a few options:

  1. Reuse one of the substitution variables by testing that it is not an empty string. Requires the parameter in the class to default to empty string.
  2. As above, but default to undef in the class, and test for :undef (as I have done above).
  3. As above, but default to false in the class, and test for false.
  4. Use string values of 'true' and 'false' for configurations that also require enablers to be explicit before providing parameters in that section
  5. Use a special "enabler" variable to enable the entire block, but this variable is ultimately useless for substitution in other ways.

One of my gripes with the Puppet DSL (and let's face it, who doesn't have at least one) is that the type system is inherently different to Ruby's. While there is the undef "value", it doesn't behave like the Ruby nil. Empty strings evaluate as false in Puppet; in Ruby they evaluate as true. It is not permissible to pass undef to a class or definition that wasn't already set up with a default value. This clearly contrasts with Ruby where nil is a perfectly permissible value to supply (and evaluates as false).

My problems with the above patterns are as follows:

  1. I don't like that an empty string has some kind of semantic value. I may not want to imply anything about the absence of a value, like an empty string (if for example I'm passing in booleans or numbers).
  2. Passing and testing for :undef is really ugly and totally blurs the Puppet/Ruby border. It is not clear how :undef is handled inside of Puppet to the outside observer.
  3. Again, false has semantic value. I don't want something to be boolean if I'm passing a string or a number. Why not just use '' when handling string values and false when handling booleans? It destroys your consistency (especially when in Ruby you could just test for "if X != nil" or just "unless X").
  4. This leads to confusion between real boolean values and the string versions. On the other hand you could use something like <%= proxy_enabler : 'true' ? 'false' %> but this seems even more confusing, even if you understand the ternary operator.
  5. This just seems wasteful. Especially when your class interfaces can consist of 20 or more variables, another one is not welcome. Incidentally, bring on class parameter hashes!

This shows just some of the madness I have to endure day to day. I'd love to know your thoughts on this, as I still haven't really made my mind up. I did actually request nil to be added to the Puppet language but my ideas on how best this can be applied is still floating about in the wind.

© 2010-2018 Oliver Hookins and Angela Collins