puppet 4.4.2 → 4.5.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.

Files changed (218) hide show
  1. data/CONTRIBUTING.md +5 -5
  2. data/Gemfile +2 -2
  3. data/LICENSE +2 -2
  4. data/README.md +5 -0
  5. data/ext/project_data.yaml +2 -0
  6. data/lib/hiera_puppet.rb +6 -14
  7. data/lib/puppet/application/agent.rb +2 -3
  8. data/lib/puppet/data_providers/hiera_config.rb +2 -4
  9. data/lib/puppet/data_providers/hiera_interpolate.rb +12 -154
  10. data/lib/puppet/data_providers/json_data_provider_factory.rb +0 -7
  11. data/lib/puppet/data_providers/yaml_data_provider_factory.rb +2 -8
  12. data/lib/puppet/defaults.rb +70 -7
  13. data/lib/puppet/functions.rb +69 -0
  14. data/lib/puppet/functions/dig.rb +39 -0
  15. data/lib/puppet/functions/lest.rb +53 -0
  16. data/lib/puppet/functions/lookup.rb +40 -27
  17. data/lib/puppet/functions/new.rb +502 -0
  18. data/lib/puppet/functions/regsubst.rb +11 -10
  19. data/lib/puppet/functions/then.rb +74 -0
  20. data/lib/puppet/functions/type.rb +4 -4
  21. data/lib/puppet/functions/with.rb +1 -1
  22. data/lib/puppet/indirector/catalog/compiler.rb +2 -0
  23. data/lib/puppet/indirector/resource_type/parser.rb +5 -0
  24. data/lib/puppet/indirector/rest.rb +5 -1
  25. data/lib/puppet/loaders.rb +2 -0
  26. data/lib/puppet/metatype/manager.rb +19 -2
  27. data/lib/puppet/module_tool/applications/application.rb +1 -1
  28. data/lib/puppet/module_tool/skeleton/templates/generator/Gemfile +6 -2
  29. data/lib/puppet/module_tool/skeleton/templates/generator/Rakefile +19 -4
  30. data/lib/puppet/module_tool/skeleton/templates/generator/{tests → examples}/init.pp.erb +1 -1
  31. data/lib/puppet/module_tool/skeleton/templates/generator/spec/classes/init_spec.rb.erb +0 -1
  32. data/lib/puppet/network/http/api/master/v3/environment.rb +6 -2
  33. data/lib/puppet/parser/ast/pops_bridge.rb +20 -3
  34. data/lib/puppet/parser/compiler/catalog_validator/relationship_validator.rb +24 -2
  35. data/lib/puppet/parser/e4_parser_adapter.rb +13 -12
  36. data/lib/puppet/parser/environment_compiler.rb +2 -2
  37. data/lib/puppet/parser/resource.rb +14 -5
  38. data/lib/puppet/parser/scope.rb +18 -15
  39. data/lib/puppet/plugins/data_providers/data_provider.rb +19 -8
  40. data/lib/puppet/pops.rb +6 -0
  41. data/lib/puppet/pops/adapters.rb +5 -1
  42. data/lib/puppet/pops/evaluator/access_operator.rb +52 -14
  43. data/lib/puppet/pops/evaluator/compare_operator.rb +34 -4
  44. data/lib/puppet/pops/evaluator/evaluator_impl.rb +75 -22
  45. data/lib/puppet/pops/evaluator/literal_evaluator.rb +7 -6
  46. data/lib/puppet/pops/evaluator/runtime3_converter.rb +13 -1
  47. data/lib/puppet/pops/evaluator/runtime3_support.rb +14 -4
  48. data/lib/puppet/pops/functions/dispatcher.rb +1 -1
  49. data/lib/puppet/pops/issues.rb +18 -2
  50. data/lib/puppet/pops/loader/base_loader.rb +48 -7
  51. data/lib/puppet/pops/loader/dependency_loader.rb +27 -2
  52. data/lib/puppet/pops/loader/loader.rb +12 -0
  53. data/lib/puppet/pops/loader/predefined_loader.rb +29 -0
  54. data/lib/puppet/pops/loader/runtime3_type_loader.rb +57 -0
  55. data/lib/puppet/pops/loader/static_loader.rb +92 -5
  56. data/lib/puppet/pops/loader/type_definition_instantiator.rb +25 -3
  57. data/lib/puppet/pops/loaders.rb +84 -14
  58. data/lib/puppet/pops/lookup/explainer.rb +38 -1
  59. data/lib/puppet/pops/lookup/interpolation.rb +115 -0
  60. data/lib/puppet/pops/lookup/sub_lookup.rb +86 -0
  61. data/lib/puppet/pops/model/ast_transformer.rb +8 -1
  62. data/lib/puppet/pops/model/factory.rb +31 -8
  63. data/lib/puppet/pops/model/model.rb +8 -0
  64. data/lib/puppet/pops/model/model_label_provider.rb +1 -0
  65. data/lib/puppet/pops/model/model_meta.rb +7 -1
  66. data/lib/puppet/pops/model/model_tree_dumper.rb +4 -0
  67. data/lib/puppet/pops/parser/egrammar.ra +24 -7
  68. data/lib/puppet/pops/parser/eparser.rb +863 -798
  69. data/lib/puppet/pops/parser/evaluating_parser.rb +4 -0
  70. data/lib/puppet/pops/parser/locator.rb +8 -4
  71. data/lib/puppet/pops/pcore.rb +30 -0
  72. data/lib/puppet/pops/types/class_loader.rb +2 -4
  73. data/lib/puppet/pops/types/implementation_registry.rb +146 -0
  74. data/lib/puppet/pops/types/iterable.rb +4 -4
  75. data/lib/puppet/pops/types/p_object_type.rb +846 -0
  76. data/lib/puppet/pops/types/p_runtime_type.rb +102 -0
  77. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +164 -0
  78. data/lib/puppet/pops/types/p_sem_ver_type.rb +113 -0
  79. data/lib/puppet/pops/types/puppet_object.rb +21 -0
  80. data/lib/puppet/pops/types/ruby_generator.rb +258 -0
  81. data/lib/puppet/pops/types/string_converter.rb +922 -0
  82. data/lib/puppet/pops/types/type_calculator.rb +29 -5
  83. data/lib/puppet/pops/types/type_conversion_error.rb +15 -0
  84. data/lib/puppet/pops/types/type_factory.rb +49 -16
  85. data/lib/puppet/pops/types/type_formatter.rb +335 -112
  86. data/lib/puppet/pops/types/type_mismatch_describer.rb +110 -29
  87. data/lib/puppet/pops/types/type_parser.rb +205 -197
  88. data/lib/puppet/pops/types/types.rb +481 -103
  89. data/lib/puppet/pops/validation.rb +1 -1
  90. data/lib/puppet/pops/validation/checker4_0.rb +66 -4
  91. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  92. data/lib/puppet/pops/visitor.rb +3 -1
  93. data/lib/puppet/property.rb +1 -1
  94. data/lib/puppet/provider/augeas/augeas.rb +1 -1
  95. data/lib/puppet/provider/package/pip.rb +64 -20
  96. data/lib/puppet/provider/package/rpm.rb +112 -0
  97. data/lib/puppet/provider/package/yum.rb +7 -68
  98. data/lib/puppet/provider/service/daemontools.rb +3 -3
  99. data/lib/puppet/provider/service/init.rb +4 -2
  100. data/lib/puppet/provider/service/runit.rb +3 -3
  101. data/lib/puppet/provider/service/smf.rb +6 -3
  102. data/lib/puppet/provider/service/systemd.rb +59 -73
  103. data/lib/puppet/reference/providers.rb +1 -2
  104. data/lib/puppet/resource.rb +54 -37
  105. data/lib/puppet/resource/catalog.rb +31 -29
  106. data/lib/puppet/resource/type_collection.rb +23 -8
  107. data/lib/puppet/settings.rb +4 -2
  108. data/lib/puppet/settings/base_setting.rb +9 -3
  109. data/lib/puppet/settings/symbolic_enum_setting.rb +17 -0
  110. data/lib/puppet/test/test_helper.rb +0 -1
  111. data/lib/puppet/type.rb +9 -3
  112. data/lib/puppet/type/exec.rb +17 -17
  113. data/lib/puppet/type/file.rb +12 -0
  114. data/lib/puppet/type/file/content.rb +6 -6
  115. data/lib/puppet/type/file/ensure.rb +4 -4
  116. data/lib/puppet/type/file/source.rb +4 -4
  117. data/lib/puppet/type/file/target.rb +2 -2
  118. data/lib/puppet/type/mount.rb +18 -1
  119. data/lib/puppet/type/package.rb +3 -3
  120. data/lib/puppet/type/schedule.rb +4 -4
  121. data/lib/puppet/type/service.rb +15 -0
  122. data/lib/puppet/type/sshkey.rb +5 -3
  123. data/lib/puppet/type/tidy.rb +3 -3
  124. data/lib/puppet/type/zone.rb +5 -5
  125. data/lib/puppet/util/feature.rb +1 -1
  126. data/lib/puppet/util/monkey_patches.rb +8 -0
  127. data/lib/puppet/util/network_device/cisco/device.rb +16 -6
  128. data/lib/puppet/util/network_device/cisco/interface.rb +5 -6
  129. data/lib/puppet/util/plist.rb +3 -3
  130. data/lib/puppet/version.rb +1 -1
  131. data/spec/fixtures/unit/application/environments/production/data/common.yaml +13 -0
  132. data/spec/fixtures/unit/data_providers/environments/production/modules/abc/lib/puppet/functions/abc/data.rb +2 -1
  133. data/spec/fixtures/unit/data_providers/environments/production/modules/abc/manifests/init.pp +2 -1
  134. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/data/empty_key.json +1 -0
  135. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/hiera.yaml +5 -0
  136. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/manifests/init.pp +2 -0
  137. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/metadata.json +9 -0
  138. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/data/empty_key.yaml +1 -0
  139. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/hiera.yaml +5 -0
  140. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/manifests/init.pp +2 -0
  141. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/metadata.json +9 -0
  142. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/data/empty.yaml +1 -0
  143. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/type/usee_type.rb +5 -0
  144. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/manifests/init.pp +6 -0
  145. data/spec/fixtures/unit/provider/service/smf/svcs.out +4 -3
  146. data/spec/integration/module_tool/tar/mini_spec.rb +27 -27
  147. data/spec/integration/parser/catalog_spec.rb +14 -2
  148. data/spec/integration/parser/compiler_spec.rb +94 -3
  149. data/spec/integration/parser/resource_expressions_spec.rb +1 -1
  150. data/spec/integration/resource/type_collection_spec.rb +8 -0
  151. data/spec/lib/puppet_spec/compiler.rb +11 -4
  152. data/spec/shared_contexts/types_setup.rb +4 -0
  153. data/spec/unit/application/lookup_spec.rb +91 -9
  154. data/spec/unit/appmgmt_spec.rb +44 -35
  155. data/spec/unit/capability_spec.rb +33 -53
  156. data/spec/unit/data_providers/function_data_provider_spec.rb +19 -1
  157. data/spec/unit/data_providers/hiera_data_provider_spec.rb +1 -1
  158. data/spec/unit/defaults_spec.rb +18 -0
  159. data/spec/unit/functions/assert_type_spec.rb +1 -1
  160. data/spec/unit/functions/dig_spec.rb +58 -0
  161. data/spec/unit/functions/lest_spec.rb +34 -0
  162. data/spec/unit/functions/lookup_spec.rb +108 -2
  163. data/spec/unit/functions/new_spec.rb +543 -0
  164. data/spec/unit/functions/regsubst_spec.rb +8 -0
  165. data/spec/unit/functions/then_spec.rb +40 -0
  166. data/spec/unit/functions4_spec.rb +78 -10
  167. data/spec/unit/hiera_puppet_spec.rb +49 -8
  168. data/spec/unit/indirector/resource_type/parser_spec.rb +5 -0
  169. data/spec/unit/indirector/rest_spec.rb +12 -0
  170. data/spec/unit/network/http/api/master/v3/environment_spec.rb +60 -0
  171. data/spec/unit/node/environment_spec.rb +10 -0
  172. data/spec/unit/parser/compiler_spec.rb +20 -1
  173. data/spec/unit/parser/functions/create_resources_spec.rb +2 -2
  174. data/spec/unit/parser/functions/shared.rb +1 -1
  175. data/spec/unit/parser/resource_spec.rb +8 -1
  176. data/spec/unit/parser/scope_spec.rb +45 -0
  177. data/spec/unit/pops/evaluator/access_ops_spec.rb +14 -0
  178. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +13 -5
  179. data/spec/unit/pops/loaders/static_loader_spec.rb +92 -1
  180. data/spec/unit/{data_providers/hiera_interpolation_spec.rb → pops/lookup/interpolation_spec.rb} +7 -5
  181. data/spec/unit/pops/parser/lexer2_spec.rb +2 -9
  182. data/spec/unit/pops/parser/parse_application_spec.rb +3 -8
  183. data/spec/unit/pops/parser/parse_basic_expressions_spec.rb +19 -0
  184. data/spec/unit/pops/parser/parse_capabilities_spec.rb +3 -10
  185. data/spec/unit/pops/parser/parse_site_spec.rb +19 -10
  186. data/spec/unit/pops/parser/parser_rspec_helper.rb +0 -4
  187. data/spec/unit/pops/types/enumeration_spec.rb +13 -12
  188. data/spec/unit/pops/types/iterable_spec.rb +2 -2
  189. data/spec/unit/pops/types/p_object_type_spec.rb +1060 -0
  190. data/spec/unit/pops/types/p_sem_ver_type_spec.rb +285 -0
  191. data/spec/unit/pops/types/recursion_guard_spec.rb +19 -17
  192. data/spec/unit/pops/types/ruby_generator_spec.rb +261 -0
  193. data/spec/unit/pops/types/string_converter_spec.rb +904 -0
  194. data/spec/unit/pops/types/type_calculator_spec.rb +430 -406
  195. data/spec/unit/pops/types/type_factory_spec.rb +119 -104
  196. data/spec/unit/pops/types/type_formatter_spec.rb +73 -6
  197. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +2 -2
  198. data/spec/unit/pops/types/type_parser_spec.rb +54 -15
  199. data/spec/unit/pops/types/types_spec.rb +113 -8
  200. data/spec/unit/pops/validator/validator_spec.rb +84 -10
  201. data/spec/unit/provider/package/pip3_spec.rb +9 -270
  202. data/spec/unit/provider/package/pip_spec.rb +85 -30
  203. data/spec/unit/provider/package/rpm_spec.rb +160 -3
  204. data/spec/unit/provider/package/yum_spec.rb +23 -134
  205. data/spec/unit/provider/service/smf_spec.rb +14 -2
  206. data/spec/unit/provider/service/systemd_spec.rb +33 -41
  207. data/spec/unit/resource/capability_finder_spec.rb +10 -2
  208. data/spec/unit/settings/file_setting_spec.rb +6 -0
  209. data/spec/unit/transaction/additional_resource_generator_spec.rb +80 -65
  210. data/spec/unit/type/mount_spec.rb +51 -10
  211. data/spec/unit/type/service_spec.rb +16 -0
  212. data/spec/unit/type_spec.rb +14 -0
  213. data/spec/unit/util/feature_spec.rb +1 -1
  214. data/spec/unit/util/monkey_patches_spec.rb +60 -0
  215. data/spec/unit/util/network_device/cisco/device_spec.rb +1 -1
  216. metadata +63 -11
  217. data/lib/puppet/pops/types/types_meta.rb +0 -0
  218. data/spec/integration/provider/package_spec.rb +0 -35
@@ -256,7 +256,7 @@ describe 'The iterable support' do
256
256
  end
257
257
 
258
258
  it 'will produce the string Iterator[T] on to_s on an iterator instance with element type T' do
259
- expect(Iterable.on(18).to_s).to eq('Iterator[Integer] value')
259
+ expect(Iterable.on(18).to_s).to eq('Iterator[Integer]-Value')
260
260
  end
261
261
  end
262
- end
262
+ end
@@ -0,0 +1,1060 @@
1
+ require 'spec_helper'
2
+ require 'puppet/pops'
3
+ require 'puppet_spec/compiler'
4
+
5
+ module Puppet::Pops
6
+ module Types
7
+ describe 'The Object Type' do
8
+ include PuppetSpec::Compiler
9
+
10
+ let(:parser) { TypeParser.new }
11
+ let(:pp_parser) { Parser::EvaluatingParser.new }
12
+ let(:loader) { Loader::BaseLoader.new(nil, 'type_parser_unit_test_loader') }
13
+ let(:factory) { TypeFactory }
14
+
15
+ def type_object_t(name, body_string)
16
+ object = PObjectType.new(name, pp_parser.parse_string("{#{body_string}}").current.body)
17
+ loader.set_entry(Loader::Loader::TypedName.new(:type, name.downcase), object)
18
+ object
19
+ end
20
+
21
+ def parse_object(name, body_string)
22
+ type_object_t(name, body_string)
23
+ parser.parse(name, loader)
24
+ end
25
+
26
+ context 'when dealing with attributes' do
27
+ it 'raises an error when the attribute type is not a type' do
28
+ obj = <<-OBJECT
29
+ attributes => {
30
+ a => 23
31
+ }
32
+ OBJECT
33
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,
34
+ /attribute MyObject\[a\] had wrong type, expected a Type value, got Integer/)
35
+ end
36
+
37
+ it 'raises an error if the type is missing' do
38
+ obj = <<-OBJECT
39
+ attributes => {
40
+ a => { kind => derived }
41
+ }
42
+ OBJECT
43
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,
44
+ /expected a value for key 'type'/)
45
+ end
46
+
47
+ it 'raises an error when value is of incompatible type' do
48
+ obj = <<-OBJECT
49
+ attributes => {
50
+ a => { type => Integer, value => 'three' }
51
+ }
52
+ OBJECT
53
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,
54
+ /attribute MyObject\[a\] value had wrong type, expected an Integer value, got String/)
55
+ end
56
+
57
+ it 'raises an error if the kind is invalid' do
58
+ obj = <<-OBJECT
59
+ attributes => {
60
+ a => { type => String, kind => derivd }
61
+ }
62
+ OBJECT
63
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,
64
+ /expected a match for Enum\['constant', 'derived', 'given_or_derived'\], got 'derivd'/)
65
+ end
66
+
67
+ it 'stores value in attribute' do
68
+ tp = parse_object('MyObject', <<-OBJECT)
69
+ attributes => {
70
+ a => { type => Integer, value => 3 }
71
+ }
72
+ OBJECT
73
+ attr = tp['a']
74
+ expect(attr).to be_a(PObjectType::PAttribute)
75
+ expect(attr.value).to eql(3)
76
+ end
77
+
78
+ it 'attribute with defined value responds true to value?' do
79
+ tp = parse_object('MyObject', <<-OBJECT)
80
+ attributes => {
81
+ a => { type => Integer, value => 3 }
82
+ }
83
+ OBJECT
84
+ attr = tp['a']
85
+ expect(attr.value?).to be_truthy
86
+ end
87
+
88
+ it 'attribute without defined value responds false to value?' do
89
+ tp = parse_object('MyObject', <<-OBJECT)
90
+ attributes => {
91
+ a => Integer
92
+ }
93
+ OBJECT
94
+ attr = tp['a']
95
+ expect(attr.value?).to be_falsey
96
+ end
97
+
98
+ it 'raises an error when value is requested from an attribute that has no value' do
99
+ tp = parse_object('MyObject', <<-OBJECT)
100
+ attributes => {
101
+ a => Integer
102
+ }
103
+ OBJECT
104
+ expect { tp['a'].value }.to raise_error(Puppet::Error, 'attribute MyObject[a] has no value')
105
+ end
106
+
107
+ context 'that are constants' do
108
+ it 'sets final => true' do
109
+ tp = parse_object('MyObject', <<-OBJECT)
110
+ attributes => {
111
+ a => {
112
+ type => Integer,
113
+ kind => constant,
114
+ value => 3
115
+ }
116
+ }
117
+ OBJECT
118
+ expect(tp['a'].final?).to be_truthy
119
+ end
120
+
121
+ it 'raises an error when no value is declared' do
122
+ obj = <<-OBJECT
123
+ attributes => {
124
+ a => {
125
+ type => Integer,
126
+ kind => constant
127
+ }
128
+ }
129
+ OBJECT
130
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
131
+ "attribute MyObject[a] of kind 'constant' requires a value")
132
+ end
133
+
134
+ it 'raises an error when final => false' do
135
+ obj = <<-OBJECT
136
+ attributes => {
137
+ a => {
138
+ type => Integer,
139
+ kind => constant,
140
+ final => false
141
+ }
142
+ }
143
+ OBJECT
144
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
145
+ "attribute MyObject[a] of kind 'constant' cannot be combined with final => false")
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'when dealing with functions' do
151
+ it 'raises an error when the function type is a Type[Callable]' do
152
+ obj = <<-OBJECT
153
+ functions => {
154
+ a => String
155
+ }
156
+ OBJECT
157
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError,
158
+ /function MyObject\[a\] had wrong type, expected a Type\[Callable\] value, got Type\[String\]/)
159
+ end
160
+
161
+ it 'raises an error when a function has the same name as an attribute' do
162
+ obj = <<-OBJECT
163
+ attributes => {
164
+ a => Integer
165
+ },
166
+ functions => {
167
+ a => Callable
168
+ }
169
+ OBJECT
170
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
171
+ 'function MyObject[a] conflicts with attribute with the same name')
172
+ end
173
+ end
174
+
175
+ context 'when dealing with overrides' do
176
+ it 'can redefine inherited member to assignable type' do
177
+ parent = <<-OBJECT
178
+ attributes => {
179
+ a => Integer
180
+ }
181
+ OBJECT
182
+ obj = <<-OBJECT
183
+ parent => MyObject,
184
+ attributes => {
185
+ a => { type => Integer[0,10], override => true }
186
+ }
187
+ OBJECT
188
+ parse_object('MyObject', parent)
189
+ tp = parse_object('MyDerivedObject', obj)
190
+ expect(tp['a'].type).to eql(PIntegerType.new(0,10))
191
+ end
192
+
193
+ it 'raises an error when an attribute overrides a function' do
194
+ parent = <<-OBJECT
195
+ attributes => {
196
+ a => Integer
197
+ }
198
+ OBJECT
199
+ obj = <<-OBJECT
200
+ parent => MyObject,
201
+ functions => {
202
+ a => { type => Callable, override => true }
203
+ }
204
+ OBJECT
205
+ parse_object('MyObject', parent)
206
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
207
+ 'function MyDerivedObject[a] attempts to override attribute MyObject[a]')
208
+ end
209
+
210
+ it 'raises an error when the an function overrides an attribute' do
211
+ parent = <<-OBJECT
212
+ functions => {
213
+ a => Callable
214
+ }
215
+ OBJECT
216
+ obj = <<-OBJECT
217
+ parent => MyObject,
218
+ attributes => {
219
+ a => { type => Integer, override => true }
220
+ }
221
+ OBJECT
222
+ parse_object('MyObject', parent)
223
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
224
+ 'attribute MyDerivedObject[a] attempts to override function MyObject[a]')
225
+ end
226
+
227
+ it 'raises an error on attempts to redefine inherited member to unassignable type' do
228
+ parent = <<-OBJECT
229
+ attributes => {
230
+ a => Integer
231
+ }
232
+ OBJECT
233
+ obj = <<-OBJECT
234
+ parent => MyObject,
235
+ attributes => {
236
+ a => { type => String, override => true }
237
+ }
238
+ OBJECT
239
+ parse_object('MyObject', parent)
240
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
241
+ 'attribute MyDerivedObject[a] attempts to override attribute MyObject[a] with a type that does not match')
242
+ end
243
+
244
+ it 'raises an error when an attribute overrides a final attribute' do
245
+ parent = <<-OBJECT
246
+ attributes => {
247
+ a => { type => Integer, final => true }
248
+ }
249
+ OBJECT
250
+ obj = <<-OBJECT
251
+ parent => MyObject,
252
+ attributes => {
253
+ a => { type => Integer, override => true }
254
+ }
255
+ OBJECT
256
+ parse_object('MyObject', parent)
257
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
258
+ 'attribute MyDerivedObject[a] attempts to override final attribute MyObject[a]')
259
+ end
260
+
261
+ it 'raises an error when an overriding attribute is not declared with override => true' do
262
+ parent = <<-OBJECT
263
+ attributes => {
264
+ a => Integer
265
+ }
266
+ OBJECT
267
+ obj = <<-OBJECT
268
+ parent => MyObject,
269
+ attributes => {
270
+ a => Integer
271
+ }
272
+ OBJECT
273
+ parse_object('MyObject', parent)
274
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
275
+ 'attribute MyDerivedObject[a] attempts to override attribute MyObject[a] without having override => true')
276
+ end
277
+
278
+ it 'raises an error when an attribute declared with override => true does not override' do
279
+ parent = <<-OBJECT
280
+ attributes => {
281
+ a => Integer
282
+ }
283
+ OBJECT
284
+ obj = <<-OBJECT
285
+ parent => MyObject,
286
+ attributes => {
287
+ b => { type => Integer, override => true }
288
+ }
289
+ OBJECT
290
+ parse_object('MyObject', parent)
291
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
292
+ "expected attribute MyDerivedObject[b] to override an inherited attribute, but no such attribute was found")
293
+ end
294
+ end
295
+
296
+ context 'when dealing with equality' do
297
+ it 'the attributes can be declared as an array of names' do
298
+ obj = <<-OBJECT
299
+ attributes => {
300
+ a => Integer,
301
+ b => Integer
302
+ },
303
+ equality => [a,b]
304
+ OBJECT
305
+ tp = parse_object('MyObject', obj)
306
+ expect(tp.equality).to eq(['a','b'])
307
+ expect(tp.equality_attributes.keys).to eq(['a','b'])
308
+ end
309
+
310
+ it 'a single [<name>] can be declared as <name>' do
311
+ obj = <<-OBJECT
312
+ attributes => {
313
+ a => Integer,
314
+ b => Integer
315
+ },
316
+ equality => a
317
+ OBJECT
318
+ tp = parse_object('MyObject', obj)
319
+ expect(tp.equality).to eq(['a'])
320
+ end
321
+
322
+ it 'includes all non-constant attributes by default' do
323
+ obj = <<-OBJECT
324
+ attributes => {
325
+ a => Integer,
326
+ b => { type => Integer, kind => constant, value => 3 },
327
+ c => Integer
328
+ }
329
+ OBJECT
330
+ tp = parse_object('MyObject', obj)
331
+ expect(tp.equality).to be_nil
332
+ expect(tp.equality_attributes.keys).to eq(['a','c'])
333
+ end
334
+
335
+ it 'equality_include_type is true by default' do
336
+ obj = <<-OBJECT
337
+ attributes => {
338
+ a => Integer
339
+ },
340
+ equality => a
341
+ OBJECT
342
+ expect(parse_object('MyObject', obj).equality_include_type?).to be_truthy
343
+ end
344
+
345
+ it 'will allow an empty list of attributes' do
346
+ obj = <<-OBJECT
347
+ attributes => {
348
+ a => Integer,
349
+ b => Integer
350
+ },
351
+ equality => []
352
+ OBJECT
353
+ tp = parse_object('MyObject', obj)
354
+ expect(tp.equality).to be_empty
355
+ expect(tp.equality_attributes).to be_empty
356
+ end
357
+
358
+ it 'will extend default equality in parent' do
359
+ parent = <<-OBJECT
360
+ attributes => {
361
+ a => Integer,
362
+ b => Integer
363
+ }
364
+ OBJECT
365
+ obj = <<-OBJECT
366
+ parent => MyObject,
367
+ attributes => {
368
+ c => Integer,
369
+ d => Integer
370
+ }
371
+ OBJECT
372
+ parse_object('MyObject', parent)
373
+ tp = parse_object('MyDerivedObject', obj)
374
+ expect(tp.equality).to be_nil
375
+ expect(tp.equality_attributes.keys).to eq(['a','b','c','d'])
376
+ end
377
+
378
+ it 'extends equality declared in parent' do
379
+ parent = <<-OBJECT
380
+ attributes => {
381
+ a => Integer,
382
+ b => Integer
383
+ },
384
+ equality => a
385
+ OBJECT
386
+ obj = <<-OBJECT
387
+ parent => MyObject,
388
+ attributes => {
389
+ c => Integer,
390
+ d => Integer
391
+ }
392
+ OBJECT
393
+ parse_object('MyObject', parent)
394
+ tp = parse_object('MyDerivedObject', obj)
395
+ expect(tp.equality).to be_nil
396
+ expect(tp.equality_attributes.keys).to eq(['a','c','d'])
397
+ end
398
+
399
+ it 'parent defined attributes can be included in equality if not already included by a parent' do
400
+ parent = <<-OBJECT
401
+ attributes => {
402
+ a => Integer,
403
+ b => Integer
404
+ },
405
+ equality => a
406
+ OBJECT
407
+ obj = <<-OBJECT
408
+ parent => MyObject,
409
+ attributes => {
410
+ c => Integer,
411
+ d => Integer
412
+ },
413
+ equality => [b,c]
414
+ OBJECT
415
+ parse_object('MyObject', parent)
416
+ tp = parse_object('MyDerivedObject', obj)
417
+ expect(tp.equality).to eq(['b','c'])
418
+ expect(tp.equality_attributes.keys).to eq(['a','b','c'])
419
+ end
420
+
421
+ it 'raises an error when attempting to extend default equality in parent' do
422
+ parent = <<-OBJECT
423
+ attributes => {
424
+ a => Integer,
425
+ b => Integer
426
+ }
427
+ OBJECT
428
+ obj = <<-OBJECT
429
+ parent => MyObject,
430
+ attributes => {
431
+ c => Integer,
432
+ d => Integer
433
+ },
434
+ equality => a
435
+ OBJECT
436
+ parse_object('MyObject', parent)
437
+ expect { parse_object('MyDerivedObject', obj) }.to raise_error(Puppet::ParseError,
438
+ "MyDerivedObject equality is referencing attribute MyObject[a] which is included in equality of MyObject")
439
+ end
440
+
441
+ it 'raises an error when equality references a constant attribute' do
442
+ obj = <<-OBJECT
443
+ attributes => {
444
+ a => Integer,
445
+ b => { type => Integer, kind => constant, value => 3 }
446
+ },
447
+ equality => [a,b]
448
+ OBJECT
449
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
450
+ 'MyObject equality is referencing constant attribute MyObject[b]. Reference to constant is not allowed in equality')
451
+ end
452
+
453
+ it 'raises an error when equality references a function' do
454
+ obj = <<-OBJECT
455
+ attributes => {
456
+ a => Integer,
457
+ },
458
+ functions => {
459
+ b => Callable
460
+ },
461
+ equality => [a,b]
462
+ OBJECT
463
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
464
+ 'MyObject equality is referencing function MyObject[b]. Only attribute references are allowed')
465
+ end
466
+
467
+ it 'raises an error when equality references a non existent attributes' do
468
+ obj = <<-OBJECT
469
+ attributes => {
470
+ a => Integer
471
+ },
472
+ equality => [a,b]
473
+ OBJECT
474
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
475
+ "MyObject equality is referencing non existent attribute 'b'")
476
+ end
477
+
478
+ it 'raises an error when equality_include_type = false and attributes are provided' do
479
+ obj = <<-OBJECT
480
+ attributes => {
481
+ a => Integer
482
+ },
483
+ equality => a,
484
+ equality_include_type => false
485
+ OBJECT
486
+ expect { parse_object('MyObject', obj) }.to raise_error(Puppet::ParseError,
487
+ 'equality_include_type = false cannot be combined with non empty equality specification')
488
+ end
489
+ end
490
+
491
+ it 'raises an error when initialization hash contains invalid keys' do
492
+ obj = <<-OBJECT
493
+ attribrutes => {
494
+ a => Integer
495
+ }
496
+ OBJECT
497
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError, /object initializer had wrong type, unrecognized key 'attribrutes'/)
498
+ end
499
+
500
+ it 'raises an error when attribute contains invalid keys' do
501
+ obj = <<-OBJECT
502
+ attributes => {
503
+ a => { type => Integer, knid => constant }
504
+ }
505
+ OBJECT
506
+ expect { parse_object('MyObject', obj) }.to raise_error(TypeAssertionError, /initializer for attribute MyObject\[a\] had wrong type, unrecognized key 'knid'/)
507
+ end
508
+
509
+ context 'when inheriting from a another Object type' do
510
+ let(:parent) { <<-OBJECT }
511
+ attributes => {
512
+ a => Integer
513
+ },
514
+ functions => {
515
+ b => Callable
516
+ }
517
+ OBJECT
518
+
519
+ let(:derived) { <<-OBJECT }
520
+ parent => MyObject,
521
+ attributes => {
522
+ c => String,
523
+ d => Boolean
524
+ }
525
+ OBJECT
526
+
527
+ it 'includes the inherited type and its members' do
528
+ parse_object('MyObject', parent)
529
+ t = parse_object('MyDerivedObject', derived)
530
+ members = t.members.values
531
+ expect{ |b| members.each {|m| m.name.tap(&b) }}.to yield_successive_args('c', 'd')
532
+ expect{ |b| members.each {|m| m.type.simple_name.tap(&b) }}.to yield_successive_args('String', 'Boolean')
533
+ members = t.members(true).values
534
+ expect{ |b| members.each {|m| m.name.tap(&b) }}.to yield_successive_args('a', 'b', 'c', 'd')
535
+ expect{ |b| members.each {|m| m.type.simple_name.tap(&b) }}.to(yield_successive_args('Integer', 'Callable', 'String', 'Boolean'))
536
+ end
537
+
538
+ it 'is assignable to its inherited type' do
539
+ p = parse_object('MyObject', parent)
540
+ t = parse_object('MyDerivedObject', derived)
541
+ expect(p).to be_assignable(t)
542
+ end
543
+
544
+ it 'does not consider inherited type to be assignable' do
545
+ p = parse_object('MyObject', parent)
546
+ d = parse_object('MyDerivedObject', derived)
547
+ expect(d).not_to be_assignable(p)
548
+ end
549
+
550
+ it 'ruby access operator can retrieve parent member' do
551
+ p = parse_object('MyObject', parent)
552
+ d = parse_object('MyDerivedObject', derived)
553
+ expect(d['b'].container).to equal(p)
554
+ end
555
+
556
+ context 'that in turn inherits another Object type' do
557
+ let(:derived2) { <<-OBJECT }
558
+ parent => MyDerivedObject,
559
+ attributes => {
560
+ e => String,
561
+ f => Boolean
562
+ }
563
+ OBJECT
564
+
565
+ it 'is assignable to all inherited types' do
566
+ p = parse_object('MyObject', parent)
567
+ d1 = parse_object('MyDerivedObject', derived)
568
+ d2 = parse_object('MyDerivedObject2', derived2)
569
+ expect(p).to be_assignable(d2)
570
+ expect(d1).to be_assignable(d2)
571
+ end
572
+
573
+ it 'does not consider any of the inherited types to be assignable' do
574
+ p = parse_object('MyObject', parent)
575
+ d1 = parse_object('MyDerivedObject', derived)
576
+ d2 = parse_object('MyDerivedObject2', derived2)
577
+ expect(d2).not_to be_assignable(p)
578
+ expect(d2).not_to be_assignable(d1)
579
+ end
580
+ end
581
+ end
582
+
583
+ context 'when producing an i12n_type' do
584
+ it 'produces a struct of all attributes that are not derived or constant' do
585
+ t = parse_object('MyObject', <<-OBJECT)
586
+ attributes => {
587
+ a => { type => Integer },
588
+ b => { type => Integer, kind => given_or_derived },
589
+ c => { type => Integer, kind => derived },
590
+ d => { type => Integer, kind => constant, value => 4 }
591
+ }
592
+ OBJECT
593
+ expect(t.i12n_type).to eql(factory.struct({
594
+ 'a' => factory.integer,
595
+ 'b' => factory.integer
596
+ }))
597
+ end
598
+
599
+ it 'produces a struct where optional entires are denoted with an optional key' do
600
+ t = parse_object('MyObject', <<-OBJECT)
601
+ attributes => {
602
+ a => { type => Integer },
603
+ b => { type => Integer, value => 4 }
604
+ }
605
+ OBJECT
606
+ expect(t.i12n_type).to eql(factory.struct({
607
+ 'a' => factory.integer,
608
+ factory.optional('b') => factory.integer
609
+ }))
610
+ end
611
+
612
+ it 'produces a struct that includes parameters from parent type' do
613
+ t1 = parse_object('MyObject', <<-OBJECT)
614
+ attributes => {
615
+ a => { type => Integer }
616
+ }
617
+ OBJECT
618
+ t2 = parse_object('MyDerivedObject', <<-OBJECT)
619
+ parent => MyObject,
620
+ attributes => {
621
+ b => { type => Integer }
622
+ }
623
+ OBJECT
624
+ expect(t1.i12n_type).to eql(factory.struct({ 'a' => factory.integer }))
625
+ expect(t2.i12n_type).to eql(factory.struct({ 'a' => factory.integer, 'b' => factory.integer }))
626
+ end
627
+
628
+ it 'produces a struct that reflects overrides made in derived type' do
629
+ t1 = parse_object('MyObject', <<-OBJECT)
630
+ attributes => {
631
+ a => { type => Integer },
632
+ b => { type => Integer }
633
+ }
634
+ OBJECT
635
+ t2 = parse_object('MyDerivedObject', <<-OBJECT)
636
+ parent => MyObject,
637
+ attributes => {
638
+ b => { type => Integer, override => true, value => 5 }
639
+ }
640
+ OBJECT
641
+ expect(t1.i12n_type).to eql(factory.struct({ 'a' => factory.integer, 'b' => factory.integer }))
642
+ expect(t2.i12n_type).to eql(factory.struct({ 'a' => factory.integer, factory.optional('b') => factory.integer }))
643
+ end
644
+ end
645
+
646
+ context 'with attributes and parameters of its own type' do
647
+ it 'resolves an attribute type' do
648
+ t = parse_object('MyObject', <<-OBJECT)
649
+ attributes => {
650
+ a => MyObject
651
+ }
652
+ OBJECT
653
+ expect(t['a'].type).to equal(t)
654
+ end
655
+
656
+ it 'resolves a parameter type' do
657
+ t = parse_object('MyObject', <<-OBJECT)
658
+ functions => {
659
+ a => Callable[MyObject]
660
+ }
661
+ OBJECT
662
+ expect(t['a'].type).to eql(PCallableType.new(PTupleType.new([t])))
663
+ end
664
+ end
665
+
666
+ context 'when using the initialization hash' do
667
+ it 'produced hash that contains features using short form (type instead of detailed hash when only type is declared)' do
668
+ obj = t = parse_object('MyObject', <<-OBJECT)
669
+ attributes => {
670
+ a => { type => Integer }
671
+ }
672
+ OBJECT
673
+ expect(obj.to_s).to eql("Object[{name => 'MyObject', attributes => {'a' => Integer}}]")
674
+ end
675
+
676
+ it 'produced hash that does not include defaults' do
677
+ obj = t = parse_object('MyObject', <<-OBJECT)
678
+ attributes => {
679
+ a => { type => Integer, value => 23, kind => constant, final => true },
680
+ },
681
+ equality_include_type => true
682
+ OBJECT
683
+ expect(obj.to_s).to eql("Object[{name => 'MyObject', attributes => {'a' => {type => Integer, kind => constant, value => 23}}}]")
684
+ end
685
+
686
+ it 'can create an equal copy from produced hash' do
687
+ obj = t = parse_object('MyObject', <<-OBJECT)
688
+ attributes => {
689
+ a => { type => Struct[{x => Integer, y => Integer}], value => {x => 4, y => 9}, kind => constant },
690
+ b => Integer
691
+ },
692
+ functions => {
693
+ x => Callable[MyObject,Integer]
694
+ },
695
+ equality => [b]
696
+ OBJECT
697
+ obj2 = PObjectType.new(obj.i12n_hash)
698
+ expect(obj).to eql(obj2)
699
+ end
700
+ end
701
+
702
+ context 'when used in Puppet expressions' do
703
+ it 'two anonymous empty objects are equal' do
704
+ code = <<-CODE
705
+ $x = Object[{}]
706
+ $y = Object[{}]
707
+ notice($x == $y)
708
+ CODE
709
+ expect(eval_and_collect_notices(code)).to eql(['true'])
710
+ end
711
+
712
+ it 'two objects where one object inherits another object are different' do
713
+ code = <<-CODE
714
+ type MyFirstObject = Object[{}]
715
+ type MySecondObject = Object[{ parent => MyFirstObject }]
716
+ notice(MyFirstObject == MySecondObject)
717
+ CODE
718
+ expect(eval_and_collect_notices(code)).to eql(['false'])
719
+ end
720
+
721
+ it 'two anonymous objects that inherits the same parent are equal' do
722
+ code = <<-CODE
723
+ type MyFirstObject = Object[{}]
724
+ $x = Object[{ parent => MyFirstObject }]
725
+ $y = Object[{ parent => MyFirstObject }]
726
+ notice($x == $y)
727
+ CODE
728
+ expect(eval_and_collect_notices(code)).to eql(['true'])
729
+ end
730
+
731
+ it 'an object type is an instance of an object type type' do
732
+ code = <<-CODE
733
+ type MyObject = Object[{ attributes => { a => Integer }}]
734
+ notice(MyObject =~ Type[MyObject])
735
+ CODE
736
+ expect(eval_and_collect_notices(code)).to eql(['true'])
737
+ end
738
+
739
+ it 'an object that inherits another object is an instance of the type of its parent' do
740
+ code = <<-CODE
741
+ type MyFirstObject = Object[{}]
742
+ type MySecondObject = Object[{ parent => MyFirstObject }]
743
+ notice(MySecondObject =~ Type[MyFirstObject])
744
+ CODE
745
+ expect(eval_and_collect_notices(code)).to eql(['true'])
746
+ end
747
+
748
+ it 'a named object is not added to the loader unless a type <name> = <definition> is made' do
749
+ code = <<-CODE
750
+ $x = Object[{ name => 'MyFirstObject' }]
751
+ notice($x == MyFirstObject)
752
+ CODE
753
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /Resource type not found: MyFirstObject/)
754
+ end
755
+
756
+ it 'a type alias on a named object overrides the name' do
757
+ code = <<-CODE
758
+ type MyObject = Object[{ name => 'MyFirstObject', attributes => { a => { type => Integer, final => true }}}]
759
+ type MySecondObject = Object[{ parent => MyObject, attributes => { a => { type => Integer[10], override => true }}}]
760
+ notice(MySecondObject =~ Type)
761
+ CODE
762
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error,
763
+ /attribute MySecondObject\[a\] attempts to override final attribute MyObject\[a\]/)
764
+ end
765
+
766
+ it 'raises an error when object when circular inheritance is detected' do
767
+ code = <<-CODE
768
+ type MyFirstObject = Object[{
769
+ parent => MySecondObject
770
+ }]
771
+ type MySecondObject = Object[{
772
+ parent => MyFirstObject
773
+ }]
774
+ notice(MySecondObject =~ Type[MyFirstObject])
775
+ CODE
776
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /inherits from itself/)
777
+ end
778
+
779
+ it 'notices the expanded string form expected content' do
780
+ code = <<-CODE
781
+ type MyFirstObject = Object[{
782
+ attributes => {
783
+ first_a => Integer,
784
+ first_b => { type => String, kind => constant, value => 'the first constant' },
785
+ first_c => { type => String, final => true, kind => derived },
786
+ first_d => { type => String, kind => given_or_derived },
787
+ first_e => { type => String }
788
+ },
789
+ functions => {
790
+ first_x => Callable[Integer],
791
+ first_y => Callable[String]
792
+ },
793
+ equality => first_a
794
+ }]
795
+ type MySecondObject = Object[{
796
+ parent => MyFirstObject,
797
+ attributes => {
798
+ second_a => Integer,
799
+ second_b => { type => String, kind => constant, value => 'the second constant' },
800
+ first_e => { type => Enum[foo,fee,fum], final => true, override => true, value => 'fee' }
801
+ },
802
+ functions => {
803
+ second_x => Callable[Integer],
804
+ second_y => Callable[String]
805
+ },
806
+ equality => second_a
807
+ }]
808
+ notice(MyFirstObject)
809
+ notice(MySecondObject)
810
+ CODE
811
+ expect(eval_and_collect_notices(code)).to eql([
812
+ "Object[{"+
813
+ "name => 'MyFirstObject', "+
814
+ "attributes => {"+
815
+ "'first_a' => Integer, "+
816
+ "'first_b' => {type => String, kind => constant, value => 'the first constant'}, "+
817
+ "'first_c' => {type => String, final => true, kind => derived}, "+
818
+ "'first_d' => {type => String, kind => given_or_derived}, "+
819
+ "'first_e' => String"+
820
+ "}, "+
821
+ "functions => {"+
822
+ "'first_x' => Callable[Integer], "+
823
+ "'first_y' => Callable[String]"+
824
+ "}, "+
825
+ "equality => ['first_a']"+
826
+ "}]",
827
+ "Object[{"+
828
+ "name => 'MySecondObject', "+
829
+ "parent => MyFirstObject, "+
830
+ "attributes => {"+
831
+ "'second_a' => Integer, "+
832
+ "'second_b' => {type => String, kind => constant, value => 'the second constant'}, "+
833
+ "'first_e' => {type => Enum['fee', 'foo', 'fum'], final => true, override => true, value => 'fee'}"+
834
+ "}, "+
835
+ "functions => {"+
836
+ "'second_x' => Callable[Integer], "+
837
+ "'second_y' => Callable[String]"+
838
+ "}, "+
839
+ "equality => ['second_a']"+
840
+ "}]"
841
+ ])
842
+ end
843
+ end
844
+
845
+ context "when used with function 'new'" do
846
+ context 'with ordered parameters' do
847
+ it 'creates an instance with initialized attributes' do
848
+ code = <<-CODE
849
+ type MyFirstObject = Object[{
850
+ attributes => {
851
+ a => Integer,
852
+ b => String
853
+ }
854
+ }]
855
+ $obj = MyFirstObject.new(3, 'hi')
856
+ notice($obj.a)
857
+ notice($obj.b)
858
+ CODE
859
+ expect(eval_and_collect_notices(code)).to eql(['3', 'hi'])
860
+ end
861
+
862
+ it 'creates an instance with default attribute values' do
863
+ code = <<-CODE
864
+ type MyFirstObject = Object[{
865
+ attributes => {
866
+ a => { type => String, value => 'the default' }
867
+ }
868
+ }]
869
+ $obj = MyFirstObject.new
870
+ notice($obj.a)
871
+ CODE
872
+ expect(eval_and_collect_notices(code)).to eql(['the default'])
873
+ end
874
+
875
+ it 'creates an instance with constant attributes' do
876
+ code = <<-CODE
877
+ type MyFirstObject = Object[{
878
+ attributes => {
879
+ a => { type => String, kind => constant, value => 'the constant' }
880
+ }
881
+ }]
882
+ $obj = MyFirstObject.new
883
+ notice($obj.a)
884
+ CODE
885
+ expect(eval_and_collect_notices(code)).to eql(['the constant'])
886
+ end
887
+
888
+ it 'creates an instance with overridden attribute defaults' do
889
+ code = <<-CODE
890
+ type MyFirstObject = Object[{
891
+ attributes => {
892
+ a => { type => String, value => 'the default' }
893
+ }
894
+ }]
895
+ $obj = MyFirstObject.new('not default')
896
+ notice($obj.a)
897
+ CODE
898
+ expect(eval_and_collect_notices(code)).to eql(['not default'])
899
+ end
900
+
901
+ it 'fails on an attempt to provide a constant attribute value' do
902
+ code = <<-CODE
903
+ type MyFirstObject = Object[{
904
+ attributes => {
905
+ a => { type => String, kind => constant, value => 'the constant' }
906
+ }
907
+ }]
908
+ $obj = MyFirstObject.new('not constant')
909
+ notice($obj.a)
910
+ CODE
911
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects no arguments/)
912
+ end
913
+
914
+ it 'fails when a required key is missing' do
915
+ code = <<-CODE
916
+ type MyFirstObject = Object[{
917
+ attributes => {
918
+ a => String
919
+ }
920
+ }]
921
+ $obj = MyFirstObject.new
922
+ notice($obj.a)
923
+ CODE
924
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects 1 argument, got none/)
925
+ end
926
+
927
+ it 'creates a derived instance with initialized attributes' do
928
+ code = <<-CODE
929
+ type MyFirstObject = Object[{
930
+ attributes => {
931
+ a => Integer,
932
+ b => { type => String, kind => constant, value => 'the first constant' },
933
+ c => String
934
+ }
935
+ }]
936
+ type MySecondObject = Object[{
937
+ parent => MyFirstObject,
938
+ attributes => {
939
+ d => { type => Integer, value => 34 }
940
+ }
941
+ }]
942
+ $obj = MySecondObject.new(3, 'hi')
943
+ notice($obj.a)
944
+ notice($obj.b)
945
+ notice($obj.c)
946
+ notice($obj.d)
947
+ CODE
948
+ expect(eval_and_collect_notices(code)).to eql(['3', 'the first constant', 'hi', '34'])
949
+ end
950
+ end
951
+
952
+ context 'with named parameters' do
953
+ it 'creates an instance with initialized attributes' do
954
+ code = <<-CODE
955
+ type MyFirstObject = Object[{
956
+ attributes => {
957
+ a => Integer,
958
+ b => String
959
+ }
960
+ }]
961
+ $obj = MyFirstObject.new({b => 'hi', a => 3})
962
+ notice($obj.a)
963
+ notice($obj.b)
964
+ CODE
965
+ expect(eval_and_collect_notices(code)).to eql(['3', 'hi'])
966
+ end
967
+
968
+ it 'creates an instance with default attribute values' do
969
+ code = <<-CODE
970
+ type MyFirstObject = Object[{
971
+ attributes => {
972
+ a => { type => String, value => 'the default' }
973
+ }
974
+ }]
975
+ $obj = MyFirstObject.new({})
976
+ notice($obj.a)
977
+ CODE
978
+ expect(eval_and_collect_notices(code)).to eql(['the default'])
979
+ end
980
+
981
+ it 'creates an instance with constant attributes' do
982
+ code = <<-CODE
983
+ type MyFirstObject = Object[{
984
+ attributes => {
985
+ a => { type => String, kind => constant, value => 'the constant' }
986
+ }
987
+ }]
988
+ $obj = MyFirstObject.new({})
989
+ notice($obj.a)
990
+ CODE
991
+ expect(eval_and_collect_notices(code)).to eql(['the constant'])
992
+ end
993
+
994
+ it 'creates an instance with overridden attribute defaults' do
995
+ code = <<-CODE
996
+ type MyFirstObject = Object[{
997
+ attributes => {
998
+ a => { type => String, value => 'the default' }
999
+ }
1000
+ }]
1001
+ $obj = MyFirstObject.new({a => 'not default'})
1002
+ notice($obj.a)
1003
+ CODE
1004
+ expect(eval_and_collect_notices(code)).to eql(['not default'])
1005
+ end
1006
+
1007
+ it 'fails on an attempt to provide a constant attribute value' do
1008
+ code = <<-CODE
1009
+ type MyFirstObject = Object[{
1010
+ attributes => {
1011
+ a => { type => String, kind => constant, value => 'the constant' }
1012
+ }
1013
+ }]
1014
+ $obj = MyFirstObject.new({a => 'not constant'})
1015
+ notice($obj.a)
1016
+ CODE
1017
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /unrecognized key 'a'/)
1018
+ end
1019
+
1020
+ it 'fails when a required key is missing' do
1021
+ code = <<-CODE
1022
+ type MyFirstObject = Object[{
1023
+ attributes => {
1024
+ a => String
1025
+ }
1026
+ }]
1027
+ $obj = MyFirstObject.new({})
1028
+ notice($obj.a)
1029
+ CODE
1030
+ expect { eval_and_collect_notices(code) }.to raise_error(Puppet::Error, /expects size to be 1, got 0/)
1031
+ end
1032
+
1033
+ it 'creates a derived instance with initialized attributes' do
1034
+ code = <<-CODE
1035
+ type MyFirstObject = Object[{
1036
+ attributes => {
1037
+ a => Integer,
1038
+ b => { type => String, kind => constant, value => 'the first constant' },
1039
+ c => String
1040
+ }
1041
+ }]
1042
+ type MySecondObject = Object[{
1043
+ parent => MyFirstObject,
1044
+ attributes => {
1045
+ d => { type => Integer, value => 34 }
1046
+ }
1047
+ }]
1048
+ $obj = MySecondObject.new({c => 'hi', a => 3})
1049
+ notice($obj.a)
1050
+ notice($obj.b)
1051
+ notice($obj.c)
1052
+ notice($obj.d)
1053
+ CODE
1054
+ expect(eval_and_collect_notices(code)).to eql(['3', 'the first constant', 'hi', '34'])
1055
+ end
1056
+ end
1057
+ end
1058
+ end
1059
+ end
1060
+ end