summaryrefslogtreecommitdiff
path: root/lib/puppet/parser
diff options
context:
space:
mode:
authorKen Barber <ken@bob.sh>2011-04-29 09:27:10 +0200
committerKen Barber <ken@bob.sh>2011-04-30 15:59:31 +0200
commit99a93d366f2e1efb977fcc8fe300d3d8357c8214 (patch)
treefab7510323f123c83da6d7c6cae4fe9e3f2f1e27 /lib/puppet/parser
parent781a8720577d0cafe2147f3a1b938db7bec31789 (diff)
downloadpuppet-stdlib-99a93d366f2e1efb977fcc8fe300d3d8357c8214.tar.gz
puppet-stdlib-99a93d366f2e1efb977fcc8fe300d3d8357c8214.tar.bz2
Convert to module format.
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/functions/abs.rb34
-rw-r--r--lib/puppet/parser/functions/bool2num.rb45
-rw-r--r--lib/puppet/parser/functions/count.rb36
-rw-r--r--lib/puppet/parser/functions/delete_at.rb42
-rw-r--r--lib/puppet/parser/functions/empty.rb27
-rw-r--r--lib/puppet/parser/functions/fact.rb36
-rw-r--r--lib/puppet/parser/functions/is_array.rb21
-rw-r--r--lib/puppet/parser/functions/is_hash.rb21
-rw-r--r--lib/puppet/parser/functions/is_string.rb21
-rw-r--r--lib/puppet/parser/functions/join.rb34
-rw-r--r--lib/puppet/parser/functions/join_with_prefix.rb38
-rw-r--r--lib/puppet/parser/functions/keys.rb25
-rw-r--r--lib/puppet/parser/functions/load_variables.rb77
-rw-r--r--lib/puppet/parser/functions/member.rb33
-rw-r--r--lib/puppet/parser/functions/num2bool.rb38
-rw-r--r--lib/puppet/parser/functions/persistent_crontab_minutes.rb63
-rw-r--r--lib/puppet/parser/functions/prefix.rb38
-rw-r--r--lib/puppet/parser/functions/random_crontab_minutes.rb31
-rw-r--r--lib/puppet/parser/functions/range.rb59
-rw-r--r--lib/puppet/parser/functions/reverse.rb27
-rw-r--r--lib/puppet/parser/functions/shuffle.rb45
-rw-r--r--lib/puppet/parser/functions/size.rb47
-rw-r--r--lib/puppet/parser/functions/strftime.rb44
-rw-r--r--lib/puppet/parser/functions/time.rb37
-rw-r--r--lib/puppet/parser/functions/unique.rb34
-rw-r--r--lib/puppet/parser/functions/values.rb25
-rw-r--r--lib/puppet/parser/functions/values_at.rb79
27 files changed, 1057 insertions, 0 deletions
diff --git a/lib/puppet/parser/functions/abs.rb b/lib/puppet/parser/functions/abs.rb
new file mode 100644
index 0000000..0a554e4
--- /dev/null
+++ b/lib/puppet/parser/functions/abs.rb
@@ -0,0 +1,34 @@
+#
+# abs.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:abs, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "abs(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ if value.is_a?(String)
+ if value.match(/^-?(?:\d+)(?:\.\d+){1}$/)
+ value = value.to_f
+ elsif value.match(/^-?\d+$/)
+ value = value.to_i
+ else
+ raise(Puppet::ParseError, 'abs(): Requires float or ' +
+ 'integer to work with')
+ end
+ end
+
+ # We have numeric value to handle ...
+ result = value.abs
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/bool2num.rb b/lib/puppet/parser/functions/bool2num.rb
new file mode 100644
index 0000000..b2989d0
--- /dev/null
+++ b/lib/puppet/parser/functions/bool2num.rb
@@ -0,0 +1,45 @@
+#
+# bool2num.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:bool2num, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "bool2num(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ # We can have either true or false, or string which resembles boolean ...
+ unless [FalseClass, TrueClass, String].include?(klass)
+ raise(Puppet::ParseError, 'bool2num(): Requires either ' +
+ 'boolean or string to work with')
+ end
+
+ if value.is_a?(String)
+ # We consider all the yes, no, y, n and so on too ...
+ value = case value
+ #
+ # This is how undef looks like in Puppet ...
+ # We yield 0 (or false if you wish) in this case.
+ #
+ when /^$/, '' then false # Empty string will be false ...
+ when /^(1|t|y|true|yes)$/ then true
+ when /^(0|f|n|false|no)$/ then false
+ when /^(undef|undefined)$/ then false # This is not likely to happen ...
+ else
+ raise(Puppet::ParseError, 'bool2num(): Unknown type of boolean given')
+ end
+ end
+
+ # We have real boolean values as well ...
+ result = value ? 1 : 0
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/count.rb b/lib/puppet/parser/functions/count.rb
new file mode 100644
index 0000000..c4e2283
--- /dev/null
+++ b/lib/puppet/parser/functions/count.rb
@@ -0,0 +1,36 @@
+#
+# count.rb
+#
+
+# TODO(Krzysztof Wilczynski): We need to add support for regular expression ...
+# TODO(Krzysztof Wilczynski): Support for hash values would be nice too ...
+
+module Puppet::Parser::Functions
+ newfunction(:count, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "count(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, Hash, String].include?(klass)
+ raise(Puppet::ParseError, 'count(): Requires either ' +
+ 'array, hash or string to work with')
+ end
+
+ item = arguments[1] if arguments[1]
+
+ value = value.is_a?(Hash) ? value.keys : value
+
+ # No item to look for and count? Then just return current size ...
+ result = item ? value.count(item) : value.size
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/delete_at.rb b/lib/puppet/parser/functions/delete_at.rb
new file mode 100644
index 0000000..10190ba
--- /dev/null
+++ b/lib/puppet/parser/functions/delete_at.rb
@@ -0,0 +1,42 @@
+#
+# delete_at.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:delete_at, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "delete_at(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'delete_at(): Requires array to work with')
+ end
+
+ index = arguments[1]
+
+ if index.is_a?(String) and not index.match(/^\d+$/)
+ raise(Puppet::ParseError, 'delete_at(): You must provide ' +
+ 'non-negative numeric index')
+ end
+
+ result = array.clone
+
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ index = index.to_i
+
+ if index > result.size - 1 # First element is at index 0 is it not?
+ raise(Puppet::ParseError, 'delete_at(): Given index ' +
+ 'exceeds size of array given')
+ end
+
+ result.delete_at(index) # We ignore the element that got deleted ...
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/empty.rb b/lib/puppet/parser/functions/empty.rb
new file mode 100644
index 0000000..e78ebf0
--- /dev/null
+++ b/lib/puppet/parser/functions/empty.rb
@@ -0,0 +1,27 @@
+#
+# empty.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:empty, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "empty(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, Hash, String].include?(klass)
+ raise(Puppet::ParseError, 'empty(): Requires either ' +
+ 'array, hash or string to work with')
+ end
+
+ result = value.empty?
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/fact.rb b/lib/puppet/parser/functions/fact.rb
new file mode 100644
index 0000000..27b7bb2
--- /dev/null
+++ b/lib/puppet/parser/functions/fact.rb
@@ -0,0 +1,36 @@
+#
+# fact.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:fact, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "fact(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ fact = arguments[0]
+
+ unless fact.is_a?(String)
+ raise(Puppet::ParseError, 'fact(): Requires fact name to be a string')
+ end
+
+ raise(Puppet::ParseError, 'fact(): You must provide ' +
+ 'fact name') if fact.empty?
+
+ result = lookupvar(fact) # Get the value of interest from Facter ...
+
+ #
+ # Now this is a funny one ... Puppet does not have a concept of
+ # returning neither undef nor nil back for use within the Puppet DSL
+ # and empty string is as closest to actual undef as you we can get
+ # at this point in time ...
+ #
+ result = result.empty? ? '' : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_array.rb b/lib/puppet/parser/functions/is_array.rb
new file mode 100644
index 0000000..0b508fd
--- /dev/null
+++ b/lib/puppet/parser/functions/is_array.rb
@@ -0,0 +1,21 @@
+#
+# is_array.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_array, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_array(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ type = arguments[0]
+
+ result = type.is_a?(Array)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_hash.rb b/lib/puppet/parser/functions/is_hash.rb
new file mode 100644
index 0000000..259809c
--- /dev/null
+++ b/lib/puppet/parser/functions/is_hash.rb
@@ -0,0 +1,21 @@
+#
+# is_hash.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_array, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_hash(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ type = arguments[0]
+
+ result = type.is_a?(Hash)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_string.rb b/lib/puppet/parser/functions/is_string.rb
new file mode 100644
index 0000000..61037e3
--- /dev/null
+++ b/lib/puppet/parser/functions/is_string.rb
@@ -0,0 +1,21 @@
+#
+# is_string.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_string, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_string(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ type = arguments[0]
+
+ result = type.is_a?(String)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/join.rb b/lib/puppet/parser/functions/join.rb
new file mode 100644
index 0000000..945556a
--- /dev/null
+++ b/lib/puppet/parser/functions/join.rb
@@ -0,0 +1,34 @@
+#
+# join.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:join, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "join(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'join(): Requires array to work with')
+ end
+
+ suffix = arguments[1] if arguments[1]
+
+ if suffix
+ unless suffix.is_a?(String)
+ raise(Puppet::ParseError, 'join(): Requires string to work with')
+ end
+ end
+
+ result = suffix ? array.join(suffix) : array.join
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/join_with_prefix.rb b/lib/puppet/parser/functions/join_with_prefix.rb
new file mode 100644
index 0000000..8bf96d9
--- /dev/null
+++ b/lib/puppet/parser/functions/join_with_prefix.rb
@@ -0,0 +1,38 @@
+#
+# join_with_prefix.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:join_with_prefix, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # Technically we support three arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "join(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'join_with_prefix(): Requires ' +
+ 'array to work with')
+ end
+
+ prefix = arguments[1] if arguments[1]
+ suffix = arguments[2] if arguments[2]
+
+ if prefix and suffix
+ result = prefix + array.join(suffix + prefix)
+ elsif prefix and not suffix
+ result = array.collect { |i| prefix ? prefix + i : i }
+ elsif suffix and not prefix
+ result = array.join(suffix)
+ else
+ result = array.join
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/keys.rb b/lib/puppet/parser/functions/keys.rb
new file mode 100644
index 0000000..3a92a47
--- /dev/null
+++ b/lib/puppet/parser/functions/keys.rb
@@ -0,0 +1,25 @@
+#
+# keys.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:keys, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "keys(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ hash = arguments[0]
+
+ unless hash.is_a?(Hash)
+ raise(Puppet::ParseError, 'keys(): Requires hash to work with')
+ end
+
+ result = hash.keys
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/load_variables.rb b/lib/puppet/parser/functions/load_variables.rb
new file mode 100644
index 0000000..a28c64b
--- /dev/null
+++ b/lib/puppet/parser/functions/load_variables.rb
@@ -0,0 +1,77 @@
+#
+# load_variables.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:load_variables, :type => :statement, :doc => <<-EOS
+This function will allow for loading variables from an external YAML
+file and expose them for further use inside the Puppet manifest file ...
+
+For example:
+
+Given following content of the data.yaml file:
+
+ ---
+ host1.example.com:
+ foo: bar
+ baz: quux
+ question: 42
+ host2.example.com:
+ abc: def
+ this: that
+ darth: vader
+
+Then calling load_variables in Puppet manifest file as follows:
+
+ load_variables("/etc/puppet/data.yaml", $fqdn)
+
+Will result in addition of variables $foo, $baz and $question
+for matching host name as per the variable $fqdn ...
+
+Another example which uses per-host file:
+
+Given following content of the file data-host1.example.com.yaml:
+
+ ---
+ foo: bar
+
+Then when we call load_variables like this:
+
+ load_variables("/etc/puppet/data-${fqdn}.yaml")
+
+This will result in a variable $foo being added and ready for use.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "load_variables(): Wrong number of " +
+ "arguments given (#{arguments.size} for 2)") if arguments.size < 2
+
+ data = {}
+
+ file = arguments[0]
+ key = arguments[1] if arguments[1]
+
+ if File.exists?(file)
+
+ begin
+ data = YAML.load_file(file)
+ rescue => error
+ raise(Puppet::ParseError, "load_variables(): Unable to load data " +
+ "from the file `%s': %s" % file, error.to_s)
+ end
+
+ raise(Puppet::ParseError, "load_variables(): Data in the file `%s' " +
+ "is not a hash" % file) unless data.is_a?(Hash)
+
+ data = ((data[key] and data[key].is_a?(Hash)) ? data[key] : {}) if key
+ end
+
+ data.each do |param, value|
+ value = strinterp(value) # Evaluate any interpolated variable names ...
+
+ setvar(param, value)
+ end
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/member.rb b/lib/puppet/parser/functions/member.rb
new file mode 100644
index 0000000..a491a76
--- /dev/null
+++ b/lib/puppet/parser/functions/member.rb
@@ -0,0 +1,33 @@
+#
+# include.rb
+#
+
+# TODO(Krzysztof Wilczynski): We need to add support for regular expression ...
+# TODO(Krzysztof Wilczynski): Support for strings and hashes too ...
+
+module Puppet::Parser::Functions
+ newfunction(:includes, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "includes(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments[0]
+
+ if not array.is_a?(Array)
+ raise(Puppet::ParseError, 'includes(): Requires an array to work with')
+ end
+
+ item = arguments[1]
+
+ raise(Puppet::ParseError, 'includes(): You must provide item ' +
+ 'to search for within given array') if item.empty?
+
+ result = array.include?(item)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/num2bool.rb b/lib/puppet/parser/functions/num2bool.rb
new file mode 100644
index 0000000..2baef62
--- /dev/null
+++ b/lib/puppet/parser/functions/num2bool.rb
@@ -0,0 +1,38 @@
+#
+# num2bool.rb
+#
+
+# TODO(Krzysztof Wilczynski): We probably need to approach numeric values differently ...
+
+module Puppet::Parser::Functions
+ newfunction(:num2bool, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "num2bool(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ number = arguments[0]
+
+ # Only numbers allowed ...
+ unless number.match(/^\-?\d+$/)
+ raise(Puppet::ParseError, 'num2bool(): Requires integer to work with')
+ end
+
+ result = case number
+ when /^0$/
+ false
+ when /^\-?\d+$/
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ number = number.to_i
+ # We yield true for any positive number and false otherwise ...
+ number > 0 ? true : false
+ else
+ raise(Puppet::ParseError, 'num2bool(): Unknown numeric format given')
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/persistent_crontab_minutes.rb b/lib/puppet/parser/functions/persistent_crontab_minutes.rb
new file mode 100644
index 0000000..cd80094
--- /dev/null
+++ b/lib/puppet/parser/functions/persistent_crontab_minutes.rb
@@ -0,0 +1,63 @@
+#
+# persistent_crontab_minutes.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:persistent_crontab_minutes, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ require 'md5'
+
+ value = 0
+
+ job = arguments[0]
+ host = arguments[1]
+
+ environment = Puppet[:environment]
+
+ # We select first directory that exists. This might not be the best idea ...
+ modules = Puppet[:modulepath].split(':').select { |i| File.exists?(i) }.first
+
+ raise(Puppet::ParseError, "Unable to determine the storage " +
+ "directory for Puppet modules") unless modules
+
+ # Prepare the file where we store current value ...
+ file = "/puppet/state/crontab/#{host}-#{job}.minutes"
+ file = File.join(modules, file)
+
+ # Get the directory portion from the file name ...
+ directory = File.dirname(file)
+
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
+
+ if FileTest.exists?(file)
+ File.open(file, 'r') { |f| value = f.read.to_i }
+
+ raise(Puppet::ParseError, "The value for minutes in the file `%s' " +
+ "is out of the range from 0 to 59 inclusive") unless value < 60
+ else
+ #
+ # Pick a random number based on the job and host name. This will yield
+ # the same value for exactly the same combination of the job and host name.
+ #
+ value = MD5.new(job_name + host).to_s.hex % 60
+
+ # Minutes are from 0 to 59 inclusive ...
+ value = value < 60 ? value : 59
+
+ File.open(file, 'w') { |f| f.write(value) }
+ end
+
+ # Tell Puppet to keep an eye on this file ...
+ parser = Puppet::Parser::Parser.new(environment)
+ parser.watch_file(file) if File.exists?(file)
+
+ return value
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/prefix.rb b/lib/puppet/parser/functions/prefix.rb
new file mode 100644
index 0000000..0e0cee2
--- /dev/null
+++ b/lib/puppet/parser/functions/prefix.rb
@@ -0,0 +1,38 @@
+#
+# prefix.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:prefix, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "prefix(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'prefix(): Requires array to work with')
+ end
+
+ prefix = arguments[1] if arguments[1]
+
+ if prefix
+ unless prefix.is_a?(String)
+ raise(Puppet::ParseError, 'prefix(): Requires string to work with')
+ end
+ end
+
+ # Turn everything into string same as join would do ...
+ result = array.collect do |i|
+ i = i.to_s
+ prefix ? prefix + i : i
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/random_crontab_minutes.rb b/lib/puppet/parser/functions/random_crontab_minutes.rb
new file mode 100644
index 0000000..8ab29e1
--- /dev/null
+++ b/lib/puppet/parser/functions/random_crontab_minutes.rb
@@ -0,0 +1,31 @@
+#
+# random_crontab_minutes.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:random_crontab_minutes, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ require 'md5'
+
+ job_name = arguments[0]
+ host = arguments[1]
+
+ #
+ # Pick a random number based on the job and host name. This will yield
+ # the same value for exactly the same combination of the job and host name.
+ #
+ value = MD5.new(job_name + host).to_s.hex % 60
+
+ # Minutes are from 0 to 59 inclusive ...
+ value = value < 60 ? value : 59
+
+ return value
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/range.rb b/lib/puppet/parser/functions/range.rb
new file mode 100644
index 0000000..6afb50c
--- /dev/null
+++ b/lib/puppet/parser/functions/range.rb
@@ -0,0 +1,59 @@
+#
+# range.rb
+#
+
+# TODO(Krzysztof Wilczynski): We probably need to approach numeric values differently ...
+
+module Puppet::Parser::Functions
+ newfunction(:range, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # We support more than one argument but at least one is mandatory ...
+ raise(Puppet::ParseError, "range(): Wrong number of " +
+ "arguments given (#{arguments.size} for 1)") if arguments.size < 1
+
+ if arguments.size > 1
+ start = arguments[0]
+ stop = arguments[1]
+
+ type = '..' # We select simplest type for Range available in Ruby ...
+
+ elsif arguments.size > 0
+ value = arguments[0]
+
+ if m = value.match(/^(\w+)(\.\.\.?|\-)(\w+)$/)
+ start = m[1]
+ stop = m[3]
+
+ type = m[2]
+
+ elsif value.match(/^.+$/)
+ raise(Puppet::ParseError, 'range(): Unable to compute range ' +
+ 'from the value given')
+ else
+ raise(Puppet::ParseError, 'range(): Unknown format of range given')
+ end
+ end
+
+ # Check whether we have integer value if so then make it so ...
+ if start.match(/^\d+$/)
+ start = start.to_i
+ stop = stop.to_i
+ else
+ start = start.to_s
+ stop = stop.to_s
+ end
+
+ range = case type
+ when /^(\.\.|\-)$/ then (start .. stop)
+ when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ...
+ end
+
+ result = range.collect { |i| i } # Get them all ... Pokemon ...
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/reverse.rb b/lib/puppet/parser/functions/reverse.rb
new file mode 100644
index 0000000..79e9b93
--- /dev/null
+++ b/lib/puppet/parser/functions/reverse.rb
@@ -0,0 +1,27 @@
+#
+# reverse.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:reverse, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "reverse(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, String].include?(klass)
+ raise(Puppet::ParseError, 'reverse(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.reverse
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/shuffle.rb b/lib/puppet/parser/functions/shuffle.rb
new file mode 100644
index 0000000..73e798c
--- /dev/null
+++ b/lib/puppet/parser/functions/shuffle.rb
@@ -0,0 +1,45 @@
+#
+# shuffle.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:shuffle, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "shuffle(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, String].include?(klass)
+ raise(Puppet::ParseError, 'shuffle(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.clone
+
+ string = value.is_a?(String) ? true : false
+
+ # Check whether it makes sense to shuffle ...
+ return result if result.size <= 1
+
+ # We turn any string value into an array to be able to shuffle ...
+ result = string ? result.split('') : result
+
+ elements = result.size
+
+ # Simple implementation of Fisher–Yates in-place shuffle ...
+ elements.times do |i|
+ j = rand(elements - i) + i
+ result[j], result[i] = result[i], result[j]
+ end
+
+ result = string ? result.join : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/size.rb b/lib/puppet/parser/functions/size.rb
new file mode 100644
index 0000000..aa4f4ad
--- /dev/null
+++ b/lib/puppet/parser/functions/size.rb
@@ -0,0 +1,47 @@
+#
+# size.rb
+#
+
+# TODO(Krzysztof Wilczynski): Support for hashes would be nice too ...
+
+module Puppet::Parser::Functions
+ newfunction(:size, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "size(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ item = arguments[0]
+
+ if item.is_a?(String)
+
+ begin
+ #
+ # Check whether your item is a numeric value or not ...
+ # This will take care about positive and/or negative numbers
+ # for both integer and floating-point values ...
+ #
+ # Please note that Puppet has no notion of hexadecimal
+ # nor octal numbers for its DSL at this point in time ...
+ #
+ Float(item)
+
+ raise(Puppet::ParseError, 'size(): Requires either ' +
+ 'string or array to work with')
+
+ rescue ArgumentError
+ result = item.size
+ end
+
+ elsif item.is_a?(Array)
+ result = item.size
+ else
+ raise(Puppet::ParseError, 'size(): Unknown type given')
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/strftime.rb b/lib/puppet/parser/functions/strftime.rb
new file mode 100644
index 0000000..c919320
--- /dev/null
+++ b/lib/puppet/parser/functions/strftime.rb
@@ -0,0 +1,44 @@
+#
+# strftime.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:strftime, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "strftime(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ format = arguments[0]
+
+ raise(Puppet::ParseError, 'strftime(): You must provide ' +
+ 'format for evaluation') if format.empty?
+
+ # The Time Zone argument is optional ...
+ time_zone = arguments[1] if arguments[1]
+
+ time = Time.new
+
+ # There is probably a better way to handle Time Zone ...
+ if time_zone and not time_zone.empty?
+ original_zone = ENV['TZ']
+
+ local_time = time.clone
+ local_time = local_time.utc
+
+ ENV['TZ'] = time_zone
+
+ time = local_time.localtime
+
+ ENV['TZ'] = original_zone
+ end
+
+ result = time.strftime(format)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/time.rb b/lib/puppet/parser/functions/time.rb
new file mode 100644
index 0000000..f7c1041
--- /dev/null
+++ b/lib/puppet/parser/functions/time.rb
@@ -0,0 +1,37 @@
+#
+# time.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:time, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ # The Time Zone argument is optional ...
+ time_zone = arguments[0] if arguments[0]
+
+ time = Time.new
+
+ # There is probably a better way to handle Time Zone ...
+ if time_zone and not time_zone.empty?
+ original_zone = ENV['TZ']
+
+ local_time = time.clone
+ local_time = local_time.utc
+
+ ENV['TZ'] = time_zone
+
+ time = local_time.localtime
+
+ ENV['TZ'] = original_zone
+ end
+
+ # Calling Time#to_i on a receiver changes it. Trust me I am the Doctor.
+ result = time.strftime('%s')
+ result = result.to_i
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/unique.rb b/lib/puppet/parser/functions/unique.rb
new file mode 100644
index 0000000..a922c94
--- /dev/null
+++ b/lib/puppet/parser/functions/unique.rb
@@ -0,0 +1,34 @@
+#
+# unique.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:unique, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "unique(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, String].include?(klass)
+ raise(Puppet::ParseError, 'unique(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.clone
+
+ string = value.is_a?(String) ? true : false
+
+ # We turn any string value into an array to be able to shuffle ...
+ result = string ? result.split('') : result
+ result = result.uniq # Remove duplicates ...
+ result = string ? result.join : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/values.rb b/lib/puppet/parser/functions/values.rb
new file mode 100644
index 0000000..c1c4a77
--- /dev/null
+++ b/lib/puppet/parser/functions/values.rb
@@ -0,0 +1,25 @@
+#
+# values.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:values, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "values(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ hash = arguments[0]
+
+ unless hash.is_a?(Hash)
+ raise(Puppet::ParseError, 'values(): Requires hash to work with')
+ end
+
+ result = hash.values
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/values_at.rb b/lib/puppet/parser/functions/values_at.rb
new file mode 100644
index 0000000..331af6a
--- /dev/null
+++ b/lib/puppet/parser/functions/values_at.rb
@@ -0,0 +1,79 @@
+#
+# values_at.rb
+#
+
+# TODO(Krzysztof Wilczynski): Support for hashes would be nice too ...
+# TODO(Krzysztof Wilczynski): We probably need to approach numeric values differently ...
+
+module Puppet::Parser::Functions
+ newfunction(:values_at, :type => :rvalue, :doc => <<-EOS
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "values_at(): Wrong number of " +
+ "arguments given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments.shift
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'values_at(): Requires array to work with')
+ end
+
+ indices = *arguments # Get them all ... Pokemon ...
+
+ if not indices or indices.empty?
+ raise(Puppet::ParseError, 'values_at(): You must provide ' +
+ 'at least one positive index to collect')
+ end
+
+ result = []
+ indices_list = []
+
+ indices.each do |i|
+ if m = i.match(/^(\d+)(\.\.\.?|\-)(\d+)$/)
+ start = m[1].to_i
+ stop = m[3].to_i
+
+ type = m[2]
+
+ if start > stop
+ raise(Puppet::ParseError, 'values_at(): Stop index in ' +
+ 'given indices range is smaller than the start index')
+ elsif stop > array.size - 1 # First element is at index 0 is it not?
+ raise(Puppet::ParseError, 'values_at(): Stop index in ' +
+ 'given indices range exceeds array size')
+ end
+
+ range = case type
+ when /^(\.\.|\-)$/ then (start .. stop)
+ when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ...
+ end
+
+ range.each { |i| indices_list << i.to_i }
+ else
+ # Only positive numbers allowed in this case ...
+ if not i.match(/^\d+$/)
+ raise(Puppet::ParseError, 'values_at(): Unknown format ' +
+ 'of given index')
+ end
+
+ # In Puppet numbers are often string-encoded ...
+ i = i.to_i
+
+ if i > array.size - 1 # Same story. First element is at index 0 ...
+ raise(Puppet::ParseError, 'values_at(): Given index ' +
+ 'exceeds array size')
+ end
+
+ indices_list << i
+ end
+ end
+
+ # We remove nil values as they make no sense in Puppet DSL ...
+ result = indices_list.collect { |i| array[i] }.compact
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :