puppet 5.0.1-x64-mingw32 → 5.1.0-x64-mingw32

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.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puppet/agent.rb +1 -0
  3. data/lib/puppet/defaults.rb +1 -1
  4. data/lib/puppet/functions.rb +6 -7
  5. data/lib/puppet/functions/all.rb +100 -0
  6. data/lib/puppet/functions/any.rb +105 -0
  7. data/lib/puppet/functions/defined.rb +3 -3
  8. data/lib/puppet/functions/new.rb +2 -1
  9. data/lib/puppet/functions/reduce.rb +31 -0
  10. data/lib/puppet/functions/tree_each.rb +200 -0
  11. data/lib/puppet/module.rb +30 -0
  12. data/lib/puppet/parser/functions/new.rb +67 -0
  13. data/lib/puppet/parser/functions/reduce.rb +31 -0
  14. data/lib/puppet/parser/relationship.rb +2 -2
  15. data/lib/puppet/parser/resource.rb +39 -10
  16. data/lib/puppet/parser/scope.rb +1 -1
  17. data/lib/puppet/pops/evaluator/access_operator.rb +11 -4
  18. data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -1
  19. data/lib/puppet/pops/evaluator/evaluator_impl.rb +4 -4
  20. data/lib/puppet/pops/evaluator/relationship_operator.rb +1 -1
  21. data/lib/puppet/pops/evaluator/runtime3_converter.rb +3 -3
  22. data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +1 -1
  23. data/lib/puppet/pops/loader/module_loaders.rb +1 -1
  24. data/lib/puppet/pops/loader/static_loader.rb +1 -2
  25. data/lib/puppet/pops/loaders.rb +1 -2
  26. data/lib/puppet/pops/lookup/context.rb +1 -1
  27. data/lib/puppet/pops/lookup/lookup_adapter.rb +2 -1
  28. data/lib/puppet/pops/model/ast.rb +440 -436
  29. data/lib/puppet/pops/model/factory.rb +140 -140
  30. data/lib/puppet/pops/pcore.rb +1 -2
  31. data/lib/puppet/pops/resource/param.rb +1 -1
  32. data/lib/puppet/pops/resource/resource_type_impl.rb +1 -1
  33. data/lib/puppet/pops/serialization/from_data_converter.rb +0 -12
  34. data/lib/puppet/pops/time/timestamp.rb +29 -17
  35. data/lib/puppet/pops/types/annotatable.rb +2 -2
  36. data/lib/puppet/pops/types/annotation.rb +8 -8
  37. data/lib/puppet/pops/types/class_loader.rb +3 -3
  38. data/lib/puppet/pops/types/implementation_registry.rb +1 -1
  39. data/lib/puppet/pops/types/iterable.rb +2 -2
  40. data/lib/puppet/pops/types/p_binary_type.rb +2 -2
  41. data/lib/puppet/pops/types/p_init_type.rb +238 -0
  42. data/lib/puppet/pops/types/p_meta_type.rb +14 -11
  43. data/lib/puppet/pops/types/p_object_type.rb +15 -15
  44. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +2 -2
  45. data/lib/puppet/pops/types/p_sem_ver_type.rb +2 -2
  46. data/lib/puppet/pops/types/p_sensitive_type.rb +2 -2
  47. data/lib/puppet/pops/types/p_timespan_type.rb +6 -6
  48. data/lib/puppet/pops/types/p_timestamp_type.rb +6 -2
  49. data/lib/puppet/pops/types/p_type_set_type.rb +10 -9
  50. data/lib/puppet/pops/types/ruby_generator.rb +6 -5
  51. data/lib/puppet/pops/types/ruby_method.rb +2 -2
  52. data/lib/puppet/pops/types/string_converter.rb +1 -1
  53. data/lib/puppet/pops/types/tree_iterators.rb +250 -0
  54. data/lib/puppet/pops/types/type_calculator.rb +13 -13
  55. data/lib/puppet/pops/types/type_factory.rb +26 -7
  56. data/lib/puppet/pops/types/type_formatter.rb +9 -4
  57. data/lib/puppet/pops/types/type_parser.rb +8 -3
  58. data/lib/puppet/pops/types/type_set_reference.rb +2 -2
  59. data/lib/puppet/pops/types/types.rb +168 -109
  60. data/lib/puppet/provider/package/gem.rb +10 -9
  61. data/lib/puppet/provider/package/pip.rb +12 -3
  62. data/lib/puppet/provider/package/yum.rb +9 -1
  63. data/lib/puppet/resource/capability_finder.rb +30 -11
  64. data/lib/puppet/type/file.rb +21 -13
  65. data/lib/puppet/util/execution.rb +67 -14
  66. data/lib/puppet/util/suidmanager.rb +1 -0
  67. data/lib/puppet/version.rb +1 -1
  68. data/locales/puppet.pot +130 -66
  69. data/man/man5/puppet.conf.5 +1 -1
  70. data/spec/fixtures/unit/provider/package/yum/yum-check-update-simple.txt +1 -0
  71. data/spec/integration/parser/collection_spec.rb +40 -1
  72. data/spec/shared_contexts/types_setup.rb +41 -2
  73. data/spec/unit/agent_spec.rb +11 -0
  74. data/spec/unit/file_bucket/dipper_spec.rb +13 -4
  75. data/spec/unit/functions/all_spec.rb +97 -0
  76. data/spec/unit/functions/any_spec.rb +109 -0
  77. data/spec/unit/functions/hiera_spec.rb +5 -0
  78. data/spec/unit/functions/new_spec.rb +66 -0
  79. data/spec/unit/functions/tree_each_spec.rb +444 -0
  80. data/spec/unit/module_spec.rb +29 -0
  81. data/spec/unit/pops/serialization/serialization_spec.rb +2 -2
  82. data/spec/unit/pops/serialization/to_from_hr_spec.rb +2 -2
  83. data/spec/unit/pops/types/iterable_spec.rb +9 -9
  84. data/spec/unit/pops/types/p_init_type_spec.rb +285 -0
  85. data/spec/unit/pops/types/p_object_type_spec.rb +8 -8
  86. data/spec/unit/pops/types/p_sensitive_type_spec.rb +4 -0
  87. data/spec/unit/pops/types/p_timespan_type_spec.rb +14 -0
  88. data/spec/unit/pops/types/p_timestamp_type_spec.rb +19 -1
  89. data/spec/unit/pops/types/p_type_set_type_spec.rb +2 -2
  90. data/spec/unit/pops/types/ruby_generator_spec.rb +9 -22
  91. data/spec/unit/pops/types/string_converter_spec.rb +2 -2
  92. data/spec/unit/pops/types/type_acceptor_spec.rb +2 -2
  93. data/spec/unit/pops/types/type_calculator_spec.rb +43 -38
  94. data/spec/unit/pops/types/type_factory_spec.rb +6 -6
  95. data/spec/unit/pops/types/type_formatter_spec.rb +6 -6
  96. data/spec/unit/pops/types/types_spec.rb +16 -4
  97. data/spec/unit/provider/package/gem_spec.rb +6 -6
  98. data/spec/unit/provider/package/pip_spec.rb +44 -23
  99. data/spec/unit/provider/package/puppet_gem_spec.rb +1 -1
  100. data/spec/unit/provider/package/yum_spec.rb +8 -0
  101. data/spec/unit/resource/capability_finder_spec.rb +4 -5
  102. data/spec/unit/type/file_spec.rb +19 -14
  103. data/spec/unit/util/execution_spec.rb +216 -82
  104. data/tasks/generate_ast_model.rake +10 -2
  105. metadata +15 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bcf38819e320b8b8a3003ed93824d2821fc0ce7b
4
- data.tar.gz: 369e5134b2ac2fa4d9db9db29fb2870f88484e5f
3
+ metadata.gz: 546f7f1230017f6375fbbbaacbebf8bdfb789892
4
+ data.tar.gz: 4dfd7ad883172c76974b1b1113fea94f4fb9a3db
5
5
  SHA512:
6
- metadata.gz: 3cef521d3d62929dde32f15670acdbc6a2669c7e65dc150e0e90606f54275c9ae1861e793744515023fb3a10d643b21cdde31ba01ea29872210d6e575c4e8b5b
7
- data.tar.gz: e5d065acd42ec80f390b48046ba63fb061ea05fd3d8d30f8a64559104e445e573f9e6899de5ab8530328bac6ecd83a2f23b3bf3780807db09063fb843fdbc0d0
6
+ metadata.gz: d328cc30f31eaf236b2f9db633dc6bfe264747763912e77728f7636d80b43fef824c5a801de524cf90c2d4a15a2b7e80e9cab5fb761680b118e75205cd450c06
7
+ data.tar.gz: 7f56e513bd513f4bb76c9e359a02658fdd3d1a6dfc9239368691f496d53bf7eee73317ddd6e63f45ece61835f7cec0bf4a08281da19096fac3fac55fa855d2bf
@@ -49,6 +49,7 @@ class Puppet::Agent
49
49
  return
50
50
  rescue StandardError => detail
51
51
  Puppet.log_exception(detail, _("Could not run %{client_class}: %{detail}") % { client_class: client_class, detail: detail })
52
+ 1
52
53
  end
53
54
  end
54
55
  end
@@ -1047,7 +1047,7 @@ EOT
1047
1047
  the process table.",
1048
1048
  },
1049
1049
  :bindaddress => {
1050
- :default => "0.0.0.0",
1050
+ :default => "*",
1051
1051
  :desc => "The address a listening server should bind to.",
1052
1052
  }
1053
1053
  )
@@ -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::TypeParser.singleton, Puppet::Pops::Types::PCallableType::DEFAULT, loader)
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, type_parser, all_callables, loader)
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 = @type_parser.parse(type_string, loader)
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
- @type_parser.parse(t, loader)
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 = @type_parser.parse(return_type, loader) unless return_type.nil?
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::TypeParser.singleton, Puppet::Pops::Types::PCallableType::DEFAULT, loader)
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::PHostClassType
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::PType
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::PHostClassType
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
  #
@@ -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(loader).new(scope, loader) }
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