puppet 5.0.1 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/puppet/agent.rb +1 -0
- data/lib/puppet/defaults.rb +1 -1
- data/lib/puppet/functions.rb +6 -7
- data/lib/puppet/functions/all.rb +100 -0
- data/lib/puppet/functions/any.rb +105 -0
- data/lib/puppet/functions/defined.rb +3 -3
- data/lib/puppet/functions/new.rb +2 -1
- data/lib/puppet/functions/reduce.rb +31 -0
- data/lib/puppet/functions/tree_each.rb +200 -0
- data/lib/puppet/module.rb +30 -0
- data/lib/puppet/parser/functions/new.rb +67 -0
- data/lib/puppet/parser/functions/reduce.rb +31 -0
- data/lib/puppet/parser/relationship.rb +2 -2
- data/lib/puppet/parser/resource.rb +39 -10
- data/lib/puppet/parser/scope.rb +1 -1
- data/lib/puppet/pops/evaluator/access_operator.rb +11 -4
- data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -1
- data/lib/puppet/pops/evaluator/evaluator_impl.rb +4 -4
- data/lib/puppet/pops/evaluator/relationship_operator.rb +1 -1
- data/lib/puppet/pops/evaluator/runtime3_converter.rb +3 -3
- data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +1 -1
- data/lib/puppet/pops/loader/module_loaders.rb +1 -1
- data/lib/puppet/pops/loader/static_loader.rb +1 -2
- data/lib/puppet/pops/loaders.rb +1 -2
- data/lib/puppet/pops/lookup/context.rb +1 -1
- data/lib/puppet/pops/lookup/lookup_adapter.rb +2 -1
- data/lib/puppet/pops/model/ast.rb +440 -436
- data/lib/puppet/pops/model/factory.rb +140 -140
- data/lib/puppet/pops/pcore.rb +1 -2
- data/lib/puppet/pops/resource/param.rb +1 -1
- data/lib/puppet/pops/resource/resource_type_impl.rb +1 -1
- data/lib/puppet/pops/serialization/from_data_converter.rb +0 -12
- data/lib/puppet/pops/time/timestamp.rb +29 -17
- data/lib/puppet/pops/types/annotatable.rb +2 -2
- data/lib/puppet/pops/types/annotation.rb +8 -8
- data/lib/puppet/pops/types/class_loader.rb +3 -3
- data/lib/puppet/pops/types/implementation_registry.rb +1 -1
- data/lib/puppet/pops/types/iterable.rb +2 -2
- data/lib/puppet/pops/types/p_binary_type.rb +2 -2
- data/lib/puppet/pops/types/p_init_type.rb +238 -0
- data/lib/puppet/pops/types/p_meta_type.rb +14 -11
- data/lib/puppet/pops/types/p_object_type.rb +15 -15
- data/lib/puppet/pops/types/p_sem_ver_range_type.rb +2 -2
- data/lib/puppet/pops/types/p_sem_ver_type.rb +2 -2
- data/lib/puppet/pops/types/p_sensitive_type.rb +2 -2
- data/lib/puppet/pops/types/p_timespan_type.rb +6 -6
- data/lib/puppet/pops/types/p_timestamp_type.rb +6 -2
- data/lib/puppet/pops/types/p_type_set_type.rb +10 -9
- data/lib/puppet/pops/types/ruby_generator.rb +6 -5
- data/lib/puppet/pops/types/ruby_method.rb +2 -2
- data/lib/puppet/pops/types/string_converter.rb +1 -1
- data/lib/puppet/pops/types/tree_iterators.rb +250 -0
- data/lib/puppet/pops/types/type_calculator.rb +13 -13
- data/lib/puppet/pops/types/type_factory.rb +26 -7
- data/lib/puppet/pops/types/type_formatter.rb +9 -4
- data/lib/puppet/pops/types/type_parser.rb +8 -3
- data/lib/puppet/pops/types/type_set_reference.rb +2 -2
- data/lib/puppet/pops/types/types.rb +168 -109
- data/lib/puppet/provider/package/gem.rb +10 -9
- data/lib/puppet/provider/package/pip.rb +12 -3
- data/lib/puppet/provider/package/yum.rb +9 -1
- data/lib/puppet/resource/capability_finder.rb +30 -11
- data/lib/puppet/type/file.rb +21 -13
- data/lib/puppet/util/execution.rb +67 -14
- data/lib/puppet/util/suidmanager.rb +1 -0
- data/lib/puppet/version.rb +1 -1
- data/locales/puppet.pot +130 -66
- data/man/man5/puppet.conf.5 +1 -1
- data/spec/fixtures/unit/provider/package/yum/yum-check-update-simple.txt +1 -0
- data/spec/integration/parser/collection_spec.rb +40 -1
- data/spec/shared_contexts/types_setup.rb +41 -2
- data/spec/unit/agent_spec.rb +11 -0
- data/spec/unit/file_bucket/dipper_spec.rb +13 -4
- data/spec/unit/functions/all_spec.rb +97 -0
- data/spec/unit/functions/any_spec.rb +109 -0
- data/spec/unit/functions/hiera_spec.rb +5 -0
- data/spec/unit/functions/new_spec.rb +66 -0
- data/spec/unit/functions/tree_each_spec.rb +444 -0
- data/spec/unit/module_spec.rb +29 -0
- data/spec/unit/pops/serialization/serialization_spec.rb +2 -2
- data/spec/unit/pops/serialization/to_from_hr_spec.rb +2 -2
- data/spec/unit/pops/types/iterable_spec.rb +9 -9
- data/spec/unit/pops/types/p_init_type_spec.rb +285 -0
- data/spec/unit/pops/types/p_object_type_spec.rb +8 -8
- data/spec/unit/pops/types/p_sensitive_type_spec.rb +4 -0
- data/spec/unit/pops/types/p_timespan_type_spec.rb +14 -0
- data/spec/unit/pops/types/p_timestamp_type_spec.rb +19 -1
- data/spec/unit/pops/types/p_type_set_type_spec.rb +2 -2
- data/spec/unit/pops/types/ruby_generator_spec.rb +9 -22
- data/spec/unit/pops/types/string_converter_spec.rb +2 -2
- data/spec/unit/pops/types/type_acceptor_spec.rb +2 -2
- data/spec/unit/pops/types/type_calculator_spec.rb +43 -38
- data/spec/unit/pops/types/type_factory_spec.rb +6 -6
- data/spec/unit/pops/types/type_formatter_spec.rb +6 -6
- data/spec/unit/pops/types/types_spec.rb +16 -4
- data/spec/unit/provider/package/gem_spec.rb +6 -6
- data/spec/unit/provider/package/pip_spec.rb +44 -23
- data/spec/unit/provider/package/puppet_gem_spec.rb +1 -1
- data/spec/unit/provider/package/yum_spec.rb +8 -0
- data/spec/unit/resource/capability_finder_spec.rb +4 -5
- data/spec/unit/type/file_spec.rb +19 -14
- data/spec/unit/util/execution_spec.rb +216 -82
- data/tasks/generate_ast_model.rake +10 -2
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e8346fcb8889c845065221225acaa27879d0a45
|
4
|
+
data.tar.gz: 35a5e924332456aef1f156761c4e1258e2b68382
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73df3e749ae5430b47acc11aee4c09d9a8a14c17a67633ce1ad1f41c7e83a792b27ce6b2817ecba02604c80fa5efbc66a3a339efaf2dd56287d1fb14d91678dd
|
7
|
+
data.tar.gz: 23cd3d57c21de9308fa452034b572e5650ae2f768858e224d80ec252a182f3b78238a3e72b0432f2830b22a1b6f26418a6798b8034880ec44b9514e7dc16554a
|
data/lib/puppet/agent.rb
CHANGED
data/lib/puppet/defaults.rb
CHANGED
data/lib/puppet/functions.rb
CHANGED
@@ -301,7 +301,7 @@ module Puppet::Functions
|
|
301
301
|
|
302
302
|
# @api private
|
303
303
|
def self.builder
|
304
|
-
DispatcherBuilder.new(dispatcher, Puppet::Pops::Types::
|
304
|
+
DispatcherBuilder.new(dispatcher, Puppet::Pops::Types::PCallableType::DEFAULT, loader)
|
305
305
|
end
|
306
306
|
|
307
307
|
# Dispatch any calls that match the signature to the provided method name.
|
@@ -389,8 +389,7 @@ module Puppet::Functions
|
|
389
389
|
attr_reader :loader
|
390
390
|
|
391
391
|
# @api private
|
392
|
-
def initialize(dispatcher,
|
393
|
-
@type_parser = type_parser
|
392
|
+
def initialize(dispatcher, all_callables, loader)
|
394
393
|
@all_callables = all_callables
|
395
394
|
@dispatcher = dispatcher
|
396
395
|
@loader = loader
|
@@ -476,7 +475,7 @@ module Puppet::Functions
|
|
476
475
|
name = type_and_name[0]
|
477
476
|
when 2
|
478
477
|
type_string, name = type_and_name
|
479
|
-
type =
|
478
|
+
type = Puppet::Pops::Types::TypeParser.singleton.parse(type_string, loader)
|
480
479
|
else
|
481
480
|
raise ArgumentError, _("block_param accepts max 2 arguments (type, name), got %{size}.") % { size: type_and_name.size }
|
482
481
|
end
|
@@ -572,10 +571,10 @@ module Puppet::Functions
|
|
572
571
|
# @api private
|
573
572
|
def create_callable(types, block_type, return_type, from, to)
|
574
573
|
mapped_types = types.map do |t|
|
575
|
-
|
574
|
+
Puppet::Pops::Types::TypeParser.singleton.parse(t, loader)
|
576
575
|
end
|
577
576
|
param_types = Puppet::Pops::Types::PTupleType.new(mapped_types, from > 0 && from == to ? nil : Puppet::Pops::Types::PIntegerType.new(from, to))
|
578
|
-
return_type =
|
577
|
+
return_type = Puppet::Pops::Types::TypeParser.singleton.parse(return_type, loader) unless return_type.nil?
|
579
578
|
Puppet::Pops::Types::PCallableType.new(param_types, block_type, return_type)
|
580
579
|
end
|
581
580
|
end
|
@@ -646,7 +645,7 @@ module Puppet::Functions
|
|
646
645
|
class InternalFunction < Function
|
647
646
|
# @api private
|
648
647
|
def self.builder
|
649
|
-
InternalDispatchBuilder.new(dispatcher, Puppet::Pops::Types::
|
648
|
+
InternalDispatchBuilder.new(dispatcher, Puppet::Pops::Types::PCallableType::DEFAULT, loader)
|
650
649
|
end
|
651
650
|
|
652
651
|
# Allows the implementation of a function to call other functions by name and pass the caller
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Runs a [lambda](http://docs.puppetlabs.com/puppet/latest/reference/lang_lambdas.html)
|
2
|
+
# repeatedly using each value in a data structure until the lambda returns a non "truthy" value which
|
3
|
+
# makes the function return `false`, or if the end of the iteration is reached, `true` is returned.
|
4
|
+
#
|
5
|
+
# This function takes two mandatory arguments, in this order:
|
6
|
+
#
|
7
|
+
# 1. An array, hash, or other iterable object that the function will iterate over.
|
8
|
+
# 2. A lambda, which the function calls for each element in the first argument. It can
|
9
|
+
# request one or two parameters.
|
10
|
+
#
|
11
|
+
# @example Using the `all` function
|
12
|
+
#
|
13
|
+
# `$data.all |$parameter| { <PUPPET CODE BLOCK> }`
|
14
|
+
#
|
15
|
+
# or
|
16
|
+
#
|
17
|
+
# `all($data) |$parameter| { <PUPPET CODE BLOCK> }`
|
18
|
+
#
|
19
|
+
# @example Using the `all` function with an Array and a one-parameter lambda
|
20
|
+
#
|
21
|
+
# ~~~ puppet
|
22
|
+
# # For the array $data, run a lambda that checks that all values are multiples of 10
|
23
|
+
# $data = [10, 20, 30]
|
24
|
+
# notice $data.all |$item| { $item % 10 == 0 }
|
25
|
+
# ~~~
|
26
|
+
#
|
27
|
+
# Would notice `true`.
|
28
|
+
#
|
29
|
+
# When the first argument is a `Hash`, Puppet passes each key and value pair to the lambda
|
30
|
+
# as an array in the form `[key, value]`.
|
31
|
+
#
|
32
|
+
# @example Using the `all` function with a `Hash` and a one-parameter lambda
|
33
|
+
#
|
34
|
+
# ~~~ puppet
|
35
|
+
# # For the hash $data, run a lambda using each item as a key-value array
|
36
|
+
# $data = { 'a_0'=> 10, 'b_1' => 20 }
|
37
|
+
# notice $data.all |$item| { $item[1] % 10 == 0 }
|
38
|
+
# ~~~
|
39
|
+
#
|
40
|
+
# Would notice `true` if all values in the hash are multiples of 10.
|
41
|
+
#
|
42
|
+
# When the lambda accepts two arguments, the first argument gets the index in an array
|
43
|
+
# or the key from a hash, and the second argument the value.
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# @example Using the `all` function with a hash and a two-parameter lambda
|
47
|
+
#
|
48
|
+
# ~~~ puppet
|
49
|
+
# # Check that all values are a multiple of 10 and keys start with 'abc'
|
50
|
+
# $data = {abc_123 => 10, abc_42 => 20, abc_blue => 30}
|
51
|
+
# notice $data.all |$key, $value| { $value % 10 == 0 and $key =~ /^abc/ }
|
52
|
+
# ~~~
|
53
|
+
#
|
54
|
+
# Would notice true.
|
55
|
+
#
|
56
|
+
# For an general examples that demonstrates iteration, see the Puppet
|
57
|
+
# [iteration](https://docs.puppetlabs.com/puppet/latest/reference/lang_iteration.html)
|
58
|
+
# documentation.
|
59
|
+
#
|
60
|
+
# @since 5.2.0
|
61
|
+
#
|
62
|
+
Puppet::Functions.create_function(:all) do
|
63
|
+
dispatch :all_Hash_2 do
|
64
|
+
param 'Hash[Any, Any]', :hash
|
65
|
+
block_param 'Callable[2,2]', :block
|
66
|
+
end
|
67
|
+
|
68
|
+
dispatch :all_Hash_1 do
|
69
|
+
param 'Hash[Any, Any]', :hash
|
70
|
+
block_param 'Callable[1,1]', :block
|
71
|
+
end
|
72
|
+
|
73
|
+
dispatch :all_Enumerable_2 do
|
74
|
+
param 'Iterable', :enumerable
|
75
|
+
block_param 'Callable[2,2]', :block
|
76
|
+
end
|
77
|
+
|
78
|
+
dispatch :all_Enumerable_1 do
|
79
|
+
param 'Iterable', :enumerable
|
80
|
+
block_param 'Callable[1,1]', :block
|
81
|
+
end
|
82
|
+
|
83
|
+
def all_Hash_1(hash)
|
84
|
+
hash.each_pair.all? { |x| yield(x) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def all_Hash_2(hash)
|
88
|
+
hash.each_pair.all? { |x,y| yield(x,y) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def all_Enumerable_1(enumerable)
|
92
|
+
Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable).all? { |e| yield(e) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def all_Enumerable_2(enumerable)
|
96
|
+
enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)
|
97
|
+
enum.each_with_index { |e, i| return false unless yield(i, e) }
|
98
|
+
true
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Runs a [lambda](http://docs.puppetlabs.com/puppet/latest/reference/lang_lambdas.html)
|
2
|
+
# repeatedly using each value in a data structure until the lambda returns a "truthy" value which
|
3
|
+
# makes the function return `true`, or if the end of the iteration is reached, false is returned.
|
4
|
+
#
|
5
|
+
# This function takes two mandatory arguments, in this order:
|
6
|
+
#
|
7
|
+
# 1. An array, hash, or other iterable object that the function will iterate over.
|
8
|
+
# 2. A lambda, which the function calls for each element in the first argument. It can
|
9
|
+
# request one or two parameters.
|
10
|
+
#
|
11
|
+
# @example Using the `any` function
|
12
|
+
#
|
13
|
+
# `$data.any |$parameter| { <PUPPET CODE BLOCK> }`
|
14
|
+
#
|
15
|
+
# or
|
16
|
+
#
|
17
|
+
# `any($data) |$parameter| { <PUPPET CODE BLOCK> }`
|
18
|
+
#
|
19
|
+
# @example Using the `any` function with an Array and a one-parameter lambda
|
20
|
+
#
|
21
|
+
# ~~~ puppet
|
22
|
+
# # For the array $data, run a lambda that checks if an unknown hash contains those keys
|
23
|
+
# $data = ["routers", "servers", "workstations"]
|
24
|
+
# $looked_up = lookup('somekey', Hash)
|
25
|
+
# notice $data.any |$item| { $looked_up[$item] }
|
26
|
+
# ~~~
|
27
|
+
#
|
28
|
+
# Would notice `true` if the looked up hash had a value that is neither `false` nor `undef` for at least
|
29
|
+
# one of the keys. That is, it is equivalent to the expression
|
30
|
+
# `$lookued_up[routers] || $looked_up[servers] || $looked_up[workstations]`.
|
31
|
+
#
|
32
|
+
# When the first argument is a `Hash`, Puppet passes each key and value pair to the lambda
|
33
|
+
# as an array in the form `[key, value]`.
|
34
|
+
#
|
35
|
+
# @example Using the `any` function with a `Hash` and a one-parameter lambda
|
36
|
+
#
|
37
|
+
# ~~~ puppet
|
38
|
+
# # For the hash $data, run a lambda using each item as a key-value array.
|
39
|
+
# $data = {"rtr" => "Router", "svr" => "Server", "wks" => "Workstation"}
|
40
|
+
# $looked_up = lookup('somekey', Hash)
|
41
|
+
# notice $data.any |$item| { $looked_up[$item[0]] }
|
42
|
+
# ~~~
|
43
|
+
#
|
44
|
+
# Would notice `true` if the looked up hash had a value for one of the wanted key that is
|
45
|
+
# neither `false` nor `undef`.
|
46
|
+
#
|
47
|
+
# When the lambda accepts two arguments, the first argument gets the index in an array
|
48
|
+
# or the key from a hash, and the second argument the value.
|
49
|
+
#
|
50
|
+
#
|
51
|
+
# @example Using the `any` function with an array and a two-parameter lambda
|
52
|
+
#
|
53
|
+
# ~~~ puppet
|
54
|
+
# # Check if there is an even numbered index that has a non String value
|
55
|
+
# $data = [key1, 1, 2, 2]
|
56
|
+
# notice $data.any |$index, $value| { $index % 2 == 0 and $value !~ String }
|
57
|
+
# ~~~
|
58
|
+
#
|
59
|
+
# Would notice true as the index `2` is even and not a `String`
|
60
|
+
#
|
61
|
+
# For an general examples that demonstrates iteration, see the Puppet
|
62
|
+
# [iteration](https://docs.puppetlabs.com/puppet/latest/reference/lang_iteration.html)
|
63
|
+
# documentation.
|
64
|
+
#
|
65
|
+
# @since 5.2.0
|
66
|
+
#
|
67
|
+
Puppet::Functions.create_function(:any) do
|
68
|
+
dispatch :any_Hash_2 do
|
69
|
+
param 'Hash[Any, Any]', :hash
|
70
|
+
block_param 'Callable[2,2]', :block
|
71
|
+
end
|
72
|
+
|
73
|
+
dispatch :any_Hash_1 do
|
74
|
+
param 'Hash[Any, Any]', :hash
|
75
|
+
block_param 'Callable[1,1]', :block
|
76
|
+
end
|
77
|
+
|
78
|
+
dispatch :any_Enumerable_2 do
|
79
|
+
param 'Iterable', :enumerable
|
80
|
+
block_param 'Callable[2,2]', :block
|
81
|
+
end
|
82
|
+
|
83
|
+
dispatch :any_Enumerable_1 do
|
84
|
+
param 'Iterable', :enumerable
|
85
|
+
block_param 'Callable[1,1]', :block
|
86
|
+
end
|
87
|
+
|
88
|
+
def any_Hash_1(hash)
|
89
|
+
hash.each_pair.any? { |x| yield(x) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def any_Hash_2(hash)
|
93
|
+
hash.each_pair.any? { |x,y| yield(x, y) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def any_Enumerable_1(enumerable)
|
97
|
+
Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable).any? { |e| yield(e) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def any_Enumerable_2(enumerable)
|
101
|
+
enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, enumerable)
|
102
|
+
enum.each_with_index { |e, i| return true if yield(i, e) }
|
103
|
+
false
|
104
|
+
end
|
105
|
+
end
|
@@ -133,18 +133,18 @@ Puppet::Functions.create_function(:'defined', Puppet::Functions::InternalFunctio
|
|
133
133
|
type = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(scope, val.type_name)
|
134
134
|
val.title.nil? ? type : scope.compiler.findresource(type, val.title)
|
135
135
|
|
136
|
-
when Puppet::Pops::Types::
|
136
|
+
when Puppet::Pops::Types::PClassType
|
137
137
|
raise ArgumentError, _('The given class type is a reference to all classes') if val.class_name.nil?
|
138
138
|
scope.compiler.findresource(:class, val.class_name)
|
139
139
|
|
140
|
-
when Puppet::Pops::Types::
|
140
|
+
when Puppet::Pops::Types::PTypeType
|
141
141
|
case val.type
|
142
142
|
when Puppet::Pops::Types::PResourceType
|
143
143
|
# It is most reasonable to take Type[File] and Type[File[foo]] to mean the same as if not wrapped in a Type
|
144
144
|
# Since the difference between File and File[foo] already captures the distinction of type vs instance.
|
145
145
|
is_defined(scope, val.type)
|
146
146
|
|
147
|
-
when Puppet::Pops::Types::
|
147
|
+
when Puppet::Pops::Types::PClassType
|
148
148
|
# Interpreted as asking if a class (and nothing else) is defined without having to be included in the catalog
|
149
149
|
# (this is the same as asking for just the class' name, but with the added certainty that it cannot be a defined type.
|
150
150
|
#
|
data/lib/puppet/functions/new.rb
CHANGED
@@ -13,6 +13,7 @@ Puppet::Functions.create_function(:new, Puppet::Functions::InternalFunction) do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def new_instance(scope, t, *args)
|
16
|
+
return args[0] if args.size == 1 && !t.is_a?(Puppet::Pops::Types::PInitType) && t.instance?(args[0])
|
16
17
|
result = catch :undefined_value do
|
17
18
|
new_function_for_type(t, scope).call(scope, *args)
|
18
19
|
end
|
@@ -21,7 +22,7 @@ Puppet::Functions.create_function(:new, Puppet::Functions::InternalFunction) do
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def new_function_for_type(t, scope)
|
24
|
-
@new_function_cache ||= Hash.new() {|hsh, key| hsh[key] = key.new_function
|
25
|
+
@new_function_cache ||= Hash.new() {|hsh, key| hsh[key] = key.new_function.new(scope, loader) }
|
25
26
|
@new_function_cache[t]
|
26
27
|
end
|
27
28
|
|
@@ -94,6 +94,37 @@
|
|
94
94
|
# # $combine contains [dabc, 10]
|
95
95
|
# ~~~
|
96
96
|
#
|
97
|
+
# @example Using the `reduce` function to reduce a hash of hashes
|
98
|
+
#
|
99
|
+
# ~~~ puppet
|
100
|
+
# # Reduce a hash of hashes $data, merging defaults into the inner hashes.
|
101
|
+
# $data = {
|
102
|
+
# 'connection1' => {
|
103
|
+
# 'username' => 'user1',
|
104
|
+
# 'password' => 'pass1',
|
105
|
+
# },
|
106
|
+
# 'connection_name2' => {
|
107
|
+
# 'username' => 'user2',
|
108
|
+
# 'password' => 'pass2',
|
109
|
+
# },
|
110
|
+
# }
|
111
|
+
#
|
112
|
+
# $defaults = {
|
113
|
+
# 'maxActive' => '20',
|
114
|
+
# 'maxWait' => '10000',
|
115
|
+
# 'username' => 'defaultuser',
|
116
|
+
# 'password' => 'defaultpass',
|
117
|
+
# }
|
118
|
+
#
|
119
|
+
# $merged = $data.reduce( {} ) |$memo, $x| {
|
120
|
+
# $memo + { $x[0] => $defaults + $data[$x[0]] }
|
121
|
+
# }
|
122
|
+
# # At the start of the lambda's first iteration, $memo is set to {}, and $x is set to
|
123
|
+
# # the first [key, value] tuple. The key in $data is, therefore, given by $x[0]. In
|
124
|
+
# # subsequent rounds, $memo retains the value returned by the expression, i.e.
|
125
|
+
# # $memo + { $x[0] => $defaults + $data[$x[0]] }.
|
126
|
+
# ~~~
|
127
|
+
#
|
97
128
|
# @since 4.0.0
|
98
129
|
#
|
99
130
|
Puppet::Functions.create_function(:reduce) do
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# Runs a [lambda](http://docs.puppetlabs.com/puppet/latest/reference/lang_lambdas.html)
|
2
|
+
# recursively and repeatedly using values from a data structure, then returns the unchanged data structure, or if
|
3
|
+
# a lambda is not given, returns an `Iterator` for the tree.
|
4
|
+
#
|
5
|
+
# This function takes one mandatory argument, one optional, and an optional block in this order:
|
6
|
+
#
|
7
|
+
# 1. An `Array`, `Hash`, `Iterator`, or `Object` that the function will iterate over.
|
8
|
+
# 2. An optional hash with the options:
|
9
|
+
# * `include_containers` => `Optional[Boolean]` # default true - if containers should be given to the lambda
|
10
|
+
# * `include_values` => `Optional[Boolean]` # default true - if non containers should be given to the lambda
|
11
|
+
# * `include_root` => `Optional[Boolean]` # default true - if the root container should be given to the lambda
|
12
|
+
# * `container_type` => `Optional[Type[Variant[Array, Hash, Object]]]` # a type that determines what a container is - can only
|
13
|
+
# be set to a type that matches the default `Variant[Array, Hash, Object]`.
|
14
|
+
# * `order` => `Enum[depth_first, breadth_first]` # default ´depth_first`, the order in which elements are visited
|
15
|
+
# * `include_refs` => Optional[Boolean] # default `false`, if attributes in objects marked as bing of `reference` kind
|
16
|
+
# should be included.
|
17
|
+
# 3. An optional lambda, which the function calls for each element in the first argument. It must
|
18
|
+
# accept one or two arguments; either `$path`, and `$value`, or just `$value`.
|
19
|
+
#
|
20
|
+
# @example Using the `tree_each` function
|
21
|
+
#
|
22
|
+
# `$data.tree_each |$path, $value| { <PUPPET CODE BLOCK> }`
|
23
|
+
# `$data.tree_each |$value| { <PUPPET CODE BLOCK> }`
|
24
|
+
#
|
25
|
+
# or
|
26
|
+
#
|
27
|
+
# `tree_each($data) |$path, $value| { <PUPPET CODE BLOCK> }`
|
28
|
+
# `tree_each($data) |$value| { <PUPPET CODE BLOCK> }`
|
29
|
+
#
|
30
|
+
# The parameter `$path` is always given as an `Array` containing the path that when applied to
|
31
|
+
# the tree as `$data.dig(*$path) yields the `$value`.
|
32
|
+
# The `$value` is the value at that path.
|
33
|
+
#
|
34
|
+
# For `Array` values, the path will contain `Integer` entries with the array index,
|
35
|
+
# and for `Hash` values, the path will contain the hash key, which may be `Any` value.
|
36
|
+
# For `Object` containers, the entry is the name of the attribute (a `String`).
|
37
|
+
#
|
38
|
+
# The tree is walked in either depth-first order, or in breadth-first order under the control of the
|
39
|
+
# `order` option, yielding each `Array`, `Hash`, `Object`, and each entry/attribute.
|
40
|
+
# The default is `depth_first` which means that children are processed before siblings.
|
41
|
+
# An order of `breadth_first` means that siblings are processed before children.
|
42
|
+
#
|
43
|
+
# @example depth- or breadth-first order
|
44
|
+
#
|
45
|
+
# ~~~ puppet
|
46
|
+
# [1, [2, 3], 4]
|
47
|
+
# ~~~
|
48
|
+
#
|
49
|
+
# Results in:
|
50
|
+
#
|
51
|
+
# If containers are skipped:
|
52
|
+
#
|
53
|
+
# * `depth_first` order `1`, `2`, `3`, `4`
|
54
|
+
# * `breadth_first` order `1`, `4`,`2`, `3`
|
55
|
+
#
|
56
|
+
# If containers and root, are included:
|
57
|
+
#
|
58
|
+
# * `depth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `2`, `3`, `4`
|
59
|
+
# * `breadth_first` order `[1, [2, 3], 4]`, `1`, `[2, 3]`, `4`, `2`, `3`
|
60
|
+
#
|
61
|
+
# Typical use of the `tree_each` function include:
|
62
|
+
# * a more efficient way to iterate over a tree than first using `flatten` on an array
|
63
|
+
# as that requires a new (potentially very large) array to be created
|
64
|
+
# * when a tree needs to be transformed and 'pretty printed' in a template
|
65
|
+
# * avoiding having to write a special recursive function when tree contains hashes (flatten does
|
66
|
+
# not work on hashes)
|
67
|
+
#
|
68
|
+
# @example A flattened iteration over a tree excluding Collections
|
69
|
+
#
|
70
|
+
# ~~~ puppet
|
71
|
+
# $data = [1, 2, [3, [4, 5]]]
|
72
|
+
# $data.tree_each({include_containers => false}) |$v| { notice "$v" }
|
73
|
+
# ~~~
|
74
|
+
#
|
75
|
+
# This would call the lambda 5 times with with the following values in sequence: `1`, `2`, `3`, `4`, `5`
|
76
|
+
#
|
77
|
+
# @example A flattened iteration over a tree (including containers by default)
|
78
|
+
#
|
79
|
+
# ~~~ puppet
|
80
|
+
# $data = [1, 2, [3, [4, 5]]]
|
81
|
+
# $data.tree_each |$v| { notice "$v" }
|
82
|
+
# ~~~
|
83
|
+
#
|
84
|
+
# This would call the lambda 7 times with the following values in sequence:
|
85
|
+
# `1`, `2`, `[3, [4, 5]]`, `3`, `[4, 5]`, `4`, `5`
|
86
|
+
#
|
87
|
+
# @example A flattened iteration over a tree (including only non root containers)
|
88
|
+
#
|
89
|
+
# ~~~ puppet
|
90
|
+
# $data = [1, 2, [3, [4, 5]]]
|
91
|
+
# $data.tree_each({include_values => false, include_root => false}) |$v| { notice "$v" }
|
92
|
+
# ~~~
|
93
|
+
#
|
94
|
+
# This would call the lambda 2 times with the following values in sequence:
|
95
|
+
# `[3, [4, 5]]`, `[4, 5]`
|
96
|
+
|
97
|
+
# Any Puppet Type system data type can be used to filter what is
|
98
|
+
# considered to be a container, but it must be a narrower type than one of
|
99
|
+
# the default Array, Hash, Object types - for example it is not possible to make a
|
100
|
+
# `String` be a container type.
|
101
|
+
#
|
102
|
+
# @example Only `Array` as container type
|
103
|
+
#
|
104
|
+
# ~~~ puppet
|
105
|
+
# $data = [1, {a => 'hello', b => [100, 200]}, [3, [4, 5]]]
|
106
|
+
# $data.tree_each({container_type => Array, include_containers => false} |$v| { notice "$v" }
|
107
|
+
# ~~~
|
108
|
+
#
|
109
|
+
# Would call the lambda 5 times with `1`, `{a => 'hello', b => [100, 200]}`, `3`, `4`, `5`
|
110
|
+
#
|
111
|
+
# **Chaining** When calling `tree_each` without a lambda the function produces an `Iterator`
|
112
|
+
# that can be chained into another iteration. Thus it is easy to use one of:
|
113
|
+
#
|
114
|
+
# * `reverse_each` - get "leaves before root"
|
115
|
+
# * `filter` - prune the tree
|
116
|
+
# * `map` - transform each element
|
117
|
+
# * `reduce` - produce something else
|
118
|
+
#
|
119
|
+
# Note than when chaining, the value passed on is a `Tuple` with `[path, value]`.
|
120
|
+
#
|
121
|
+
# @example pruning a tree
|
122
|
+
#
|
123
|
+
# ~~~ puppet
|
124
|
+
# # A tree of some complexity (here very simple for readability)
|
125
|
+
# $tree = [
|
126
|
+
# { name => 'user1', status => 'inactive', id => '10'},
|
127
|
+
# { name => 'user2', status => 'active', id => '20'}
|
128
|
+
# ]
|
129
|
+
# notice $tree.tree_each.filter |$v| {
|
130
|
+
# $value = $v[1]
|
131
|
+
# $value =~ Hash and $value[status] == active
|
132
|
+
# }
|
133
|
+
# ~~~
|
134
|
+
#
|
135
|
+
# Would notice `[[[1], {name => user2, status => active, id => 20}]]`, which can then be processed
|
136
|
+
# further as each filtered result appears as a `Tuple` with `[path, value]`.
|
137
|
+
#
|
138
|
+
#
|
139
|
+
# For general examples that demonstrates iteration see the Puppet
|
140
|
+
# [iteration](https://docs.puppetlabs.com/puppet/latest/reference/lang_iteration.html)
|
141
|
+
# documentation.
|
142
|
+
#
|
143
|
+
# @since 5.0.0
|
144
|
+
#
|
145
|
+
Puppet::Functions.create_function(:tree_each) do
|
146
|
+
|
147
|
+
local_types do
|
148
|
+
type "OptionsType = Struct[{\
|
149
|
+
container_type => Optional[Type],\
|
150
|
+
include_root => Optional[Boolean],
|
151
|
+
include_containers => Optional[Boolean],\
|
152
|
+
include_values => Optional[Boolean],\
|
153
|
+
order => Optional[Enum[depth_first, breadth_first]],\
|
154
|
+
include_refs => Optional[Boolean]\
|
155
|
+
}]"
|
156
|
+
end
|
157
|
+
|
158
|
+
dispatch :tree_Enumerable2 do
|
159
|
+
param 'Variant[Iterator, Array, Hash, Object]', :tree
|
160
|
+
optional_param 'OptionsType', :options
|
161
|
+
block_param 'Callable[2,2]', :block
|
162
|
+
end
|
163
|
+
|
164
|
+
dispatch :tree_Enumerable1 do
|
165
|
+
param 'Variant[Iterator, Array, Hash, Object]', :tree
|
166
|
+
optional_param 'OptionsType', :options
|
167
|
+
block_param 'Callable[1,1]', :block
|
168
|
+
end
|
169
|
+
|
170
|
+
dispatch :tree_Iterable do
|
171
|
+
param 'Variant[Iterator, Array, Hash, Object]', :tree
|
172
|
+
optional_param 'OptionsType', :options
|
173
|
+
end
|
174
|
+
|
175
|
+
def tree_Enumerable1(enum, options = {}, &block)
|
176
|
+
iterator(enum, options).each {|_, v| yield(v) }
|
177
|
+
enum
|
178
|
+
end
|
179
|
+
|
180
|
+
def tree_Enumerable2(enum, options = {}, &block)
|
181
|
+
iterator(enum, options).each {|path, v| yield(path, v) }
|
182
|
+
enum
|
183
|
+
end
|
184
|
+
|
185
|
+
def tree_Iterable(enum, options = {}, &block)
|
186
|
+
Puppet::Pops::Types::Iterable.on(iterator(enum, options))
|
187
|
+
end
|
188
|
+
|
189
|
+
def iterator(enum, options)
|
190
|
+
if depth_first?(options)
|
191
|
+
Puppet::Pops::Types::Iterable::DepthFirstTreeIterator.new(enum, options)
|
192
|
+
else
|
193
|
+
Puppet::Pops::Types::Iterable::BreadthFirstTreeIterator.new(enum, options)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def depth_first?(options)
|
198
|
+
(order = options['order']).nil? ? true : order == 'depth_first'
|
199
|
+
end
|
200
|
+
end
|