diff --git a/lib/puppet/functions/assert_type.rb b/lib/puppet/functions/assert_type.rb index bcbd2a47f..2c2a37ac5 100644 --- a/lib/puppet/functions/assert_type.rb +++ b/lib/puppet/functions/assert_type.rb @@ -1,56 +1,56 @@ # Returns the given value if it is an instance of the given type, and raises an error otherwise. # Optionally, if a block is given (accepting two parameters), it will be called instead of raising # an error. This to enable giving the user richer feedback, or to supply a default value. # # @example how to assert type # # assert that `$b` is a non empty `String` and assign to `$a` # $a = assert_type(String[1], $b) # # @example using custom error message # $a = assert_type(String[1], $b) |$expected, $actual| { fail("The name cannot be empty") } # -# @example, using a warning and a default +# @example using a warning and a default # $a = assert_type(String[1], $b) |$expected, $actual| { warning("Name is empty, using default") 'anonymous' } # # See the documentation for "The Puppet Type System" for more information about types. # Puppet::Functions.create_function(:assert_type) do dispatch :assert_type do param 'Type', 'type' param 'Any', 'value' optional_block_param 'Callable[Any, Any]', 'block' end dispatch :assert_type_s do param 'String', 'type_string' param 'Any', 'value' optional_block_param 'Callable[Any, Any]', 'block' end # @param type [Type] the type the value must be an instance of # @param value [Object] the value to assert # def assert_type(type, value, block=nil) unless Puppet::Pops::Types::TypeCalculator.instance?(type,value) inferred_type = Puppet::Pops::Types::TypeCalculator.infer(value) # Do not give all the details - i.e. format as Integer, instead of Integer[n, n] for exact value, which # is just confusing. (OTOH: may need to revisit, or provide a better "type diff" output. # actual = Puppet::Pops::Types::TypeCalculator.generalize!(inferred_type) if block value = block.call(nil, type, actual) else raise Puppet::ParseError, "assert_type(): Expected type #{type} does not match actual: #{actual}" end end value end # @param type_string [String] the type the value must be an instance of given in String form # @param value [Object] the value to assert # def assert_type_s(type_string, value) t = Puppet::Pops::Types::TypeParser.new.parse(type_string) assert_type(t, value) end end diff --git a/lib/puppet/functions/filter.rb b/lib/puppet/functions/filter.rb index 5f0c7b9eb..e268a3609 100644 --- a/lib/puppet/functions/filter.rb +++ b/lib/puppet/functions/filter.rb @@ -1,92 +1,92 @@ -# Applies a parameterized block to each element in a sequence of entries from the first -# argument and returns an array or hash (same type as left operand for array/hash, and array for -# other enumerable types) with the entries for which the block evaluates to `true`. +# Applies a parameterized block to each element in a sequence of entries from the first +# argument and returns an array or hash (same type as left operand for array/hash, and array for +# other enumerable types) with the entries for which the block evaluates to `true`. # -# This function takes two mandatory arguments: the first should be an Array, a Hash, or an -# Enumerable object (integer, Integer range, or String), -# and the second a parameterized block as produced by the puppet syntax: +# This function takes two mandatory arguments: the first should be an Array, a Hash, or an +# Enumerable object (integer, Integer range, or String), +# and the second a parameterized block as produced by the puppet syntax: # -# $a.filter |$x| { ... } -# filter($a) |$x| { ... } +# $a.filter |$x| { ... } +# filter($a) |$x| { ... } # -# When the first argument is something other than a Hash, the block is called with each entry in turn. -# When the first argument is a Hash the entry is an array with `[key, value]`. +# When the first argument is something other than a Hash, the block is called with each entry in turn. +# When the first argument is a Hash the entry is an array with `[key, value]`. # -# @example Using filter with one parameter +# @example Using filter with one parameter # -# # selects all that end with berry -# $a = ["raspberry", "blueberry", "orange"] -# $a.filter |$x| { $x =~ /berry$/ } # rasberry, blueberry +# # selects all that end with berry +# $a = ["raspberry", "blueberry", "orange"] +# $a.filter |$x| { $x =~ /berry$/ } # rasberry, blueberry # -# If the block defines two parameters, they will be set to `index, value` (with index starting at 0) for all -# enumerables except Hash, and to `key, value` for a Hash. +# If the block defines two parameters, they will be set to `index, value` (with index starting at 0) for all +# enumerables except Hash, and to `key, value` for a Hash. # # @example Using filter with two parameters # # # selects all that end with 'berry' at an even numbered index # $a = ["raspberry", "blueberry", "orange"] # $a.filter |$index, $x| { $index % 2 == 0 and $x =~ /berry$/ } # raspberry # # # selects all that end with 'berry' and value >= 1 # $a = {"raspberry"=>0, "blueberry"=>1, "orange"=>1} # $a.filter |$key, $x| { $x =~ /berry$/ and $x >= 1 } # blueberry # -# @since 3.4 for Array and Hash -# @since 3.5 for other enumerables -# @note requires `parser = future` +# @since 3.4 for Array and Hash +# @since 3.5 for other enumerables +# @note requires `parser = future` # Puppet::Functions.create_function(:filter) do dispatch :filter_Hash do param 'Hash[Any, Any]', :hash required_block_param end dispatch :filter_Enumerable do param 'Any', :enumerable required_block_param end require 'puppet/util/functions/iterative_support' include Puppet::Util::Functions::IterativeSupport def filter_Hash(hash, pblock) if asserted_serving_size(pblock, 'key') == 1 result = hash.select {|x, y| pblock.call(self, [x, y]) } else result = hash.select {|x, y| pblock.call(self, x, y) } end # Ruby 1.8.7 returns Array result = Hash[result] unless result.is_a? Hash result end def filter_Enumerable(enumerable, pblock) result = [] index = 0 enum = asserted_enumerable(enumerable) if asserted_serving_size(pblock, 'index') == 1 begin loop do it = enum.next if pblock.call(nil, it) == true result << it end end rescue StopIteration end else begin loop do it = enum.next if pblock.call(nil, index, it) == true result << it end index += 1 end rescue StopIteration end end result end end diff --git a/lib/puppet/functions/match.rb b/lib/puppet/functions/match.rb index ea7d9d997..8808a29b6 100644 --- a/lib/puppet/functions/match.rb +++ b/lib/puppet/functions/match.rb @@ -1,101 +1,102 @@ # Returns the match result of matching a String or Array[String] with one of: # # * Regexp # * String - transformed to a Regexp # * Pattern type # * Regexp type # # Returns An Array with the entire match at index 0, and each subsequent submatch at index 1-n. # If there was no match, nil (ie. undef) is returned. If the value to match is an Array, a array # with mapped match results is returned. # # @example matching # "abc123".match(/([a-z]+)[1-9]+/) # => ["abc"] # "abc123".match(/([a-z]+)([1-9]+)/) # => ["abc", "123"] # # See the documentation for "The Puppet Type System" for more information about types. # @since 3.7.0 # Puppet::Functions.create_function(:match) do dispatch :match do param 'String', 'string' param 'Variant[Any, Type]', 'pattern' end dispatch :enumerable_match do param 'Array[String]', 'string' param 'Variant[Any, Type]', 'pattern' end def initialize(closure_scope, loader) super # Make this visitor shared among all instantiations of this function since it is faster. # This can be used because it is not possible to replace # a puppet runtime (where this function is) without a reboot. If you model a function in a module after # this class, use a regular instance variable instead to enable reloading of the module without reboot # @@match_visitor ||= Puppet::Pops::Visitor.new(self, "match", 1, 1) end # Matches given string against given pattern and returns an Array with matches. # @param string [String] the string to match # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern # @return [Array] matches where first match is the entire match, and index 1-n are captures from left to right # def match(string, pattern) @@match_visitor.visit_this_1(self, pattern, string) end # Matches given Array[String] against given pattern and returns an Array with mapped match results. - # @param string [Array] the array of strings to match + # + # @param array [Array] the array of strings to match # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern # @return [Array>] Array with matches (see {#match}), non matching entries produce a nil entry # def enumerable_match(array, pattern) array.map {|s| match(s, pattern) } end protected def match_Object(obj, s) msg = "match() expects pattern of T, where T is String, Regexp, Regexp[r], Pattern[p], or Array[T]. Got #{obj.class}" raise ArgumentError, msg end def match_String(pattern_string, s) do_match(s, Regexp.new(pattern_string)) end def match_Regexp(regexp, s) do_match(s, regexp) end def match_PRegexpType(regexp_t, s) raise ArgumentError, "Given Regexp Type has no regular expression" unless regexp_t.pattern do_match(s, regexp_t.regexp) end def match_PPatternType(pattern_t, s) # Since we want the actual match result (not just a boolean), an iteration over # Pattern's regular expressions is needed. (They are of PRegexpType) result = nil pattern_t.patterns.find {|pattern| result = match(s, pattern) } result end # Returns the first matching entry def match_Array(array, s) result = nil array.flatten.find {|entry| result = match(s, entry) } result end private def do_match(s, regexp) if result = regexp.match(s) result.to_a end end end