Refactoring legacy 3.x functions

If you have Ruby functions written with the legacy 3.x API, refactor them to ensure that they work correctly with current versions of Puppet.

Refactoring legacy functions improves functionality and prevents errors. At minimum, refactor any extra methods in your 3.x functions, because these no longer work in Puppet.

Extra methods

Legacy functions that contain methods defined inside the function body or outside of the function return an error, such as:

raise SecurityError, _("Illegal method definition of method '%{method_name}' on line %{line}' in legacy function") % { method_name: mname, line: mline }

To fix these errors, refactor your 3.x functions to the 4.x function API, where defining multiple methods is permitted.

For example, the legacy function below has been refactored into the modern API, with the following changes:

  • Documentation for the function is now a comment before the call to create_function.

  • The default dispatcher dispatches all given arguments to a method with the same name as the function.

  • The extra_method has not been moved, but is legal in the modern API.

  • Not visible in the code sample, the function has been moved from lib/puppet/parser/functions to lib/puppet/functions.

3.x API function:

module Puppet::Parser::Functions
  newfunction(:sample, :type => :rvalue, :doc => <<-EOS
    The function's documentation
  EOS
  ) do |arguments|
    "the returned value"
  end

  def extra_method()
  end
end

4.x API function:

# The function's documentation
Puppet::Functions.create_function(:sample) do

  def sample(*arguments)
    "the returned value"
  end

  def extra_method()
  end
end

Function call forms

Change all function calls from the form function_*** to use the method call_function(name, args).

The function_*** form applies only to functions implemented in the 3.x API, so function with calls in that form can not call any function that has moved to the 4.x API.

For example, a 3.x function:
function_sprintf("%s", "example")
The refactored 4.x function:
call_function('sprintf', "%s", "example")

:rvalue specification

The 3.x API differentiated between functions returning a value (:type => :rvalue) and functions that did not return a value (:type => :statement). In the 4.x API, there is no such distinction. If you are refactoring a function where :rvalue => true, you do not need to make any changes. If you are refactoring a function where :rvalue => false, make sure the function returns nil.

Data values

The 4.x function API allows certain data values, such as Regexp, Timespan, and Timestamp. However, the 3.x API transformed these and similar data values into strings.

Review the logic in your refactored function with this in mind: instead of checking for empty strings, the function checks for nil. The function uses neither empty strings nor the :undef symbol in returned values to denote undef; again, use nil instead.

For String, Integer, Float, Array, Hash, and Boolean values, you do not need to make changes to your 3.x functions.

Documentation

The 4.x API supports Markdown and Puppet Strings documentation tags to document functions, including individual parameters and returned values. See the Strings documentation page for details about the correct format and style for documentation comments.

Namespacing

Namespace your function to the module in which it is defined, and update manifests that use it.

The function name is in the format module_name::function_name. For example, if the module name is mymodule:

# The function's documentation
Puppet::Functions.create_function(:'mymodule::sample') do

  def sample(*arguments)
    "the returned value"
  end

  def extra_method()
  end
end

The default dispatch uses the last part of the name when dispatching to a method in the function, so you only have to change the module namespace in the function's full name. You must also move the file containing the function to the correct location for 4.x API functions, mymodule/lib/puppet/functions.