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
@@ -11,6 +11,10 @@ class EvaluatingParser
11
11
  @parser = Parser.new()
12
12
  end
13
13
 
14
+ def self.singleton
15
+ @instance ||= new
16
+ end
17
+
14
18
  def parse_string(s, file_source = nil)
15
19
  @file_source = file_source
16
20
  clear()
@@ -1,6 +1,8 @@
1
+ module Puppet::Pops
2
+ module Parser
1
3
  # Helper class that keeps track of where line breaks are located and can answer questions about positions.
2
4
  #
3
- class Puppet::Pops::Parser::Locator
5
+ class Locator
4
6
  # Creates, or recreates a Locator. A Locator is created if index is not given (a scan is then
5
7
  # performed of the given source string.
6
8
  #
@@ -73,14 +75,14 @@ class Puppet::Pops::Parser::Locator
73
75
  #
74
76
  # Note that leading_offset must be the same for all lines and measured in characters.
75
77
  #
76
- class SubLocator < Puppet::Pops::Parser::Locator
78
+ class SubLocator < Locator
77
79
  attr_reader :locator
78
80
  attr_reader :leading_line_count
79
81
  attr_reader :leading_offset
80
82
  attr_reader :leading_line_offset
81
83
 
82
84
  def self.sub_locator(string, file, leading_line_count, leading_offset, leading_line_offset)
83
- self.new(Puppet::Pops::Parser::Locator.locator(string, file),
85
+ self.new(Locator.locator(string, file),
84
86
  leading_line_count,
85
87
  leading_offset,
86
88
  leading_line_offset)
@@ -130,7 +132,7 @@ class Puppet::Pops::Parser::Locator
130
132
 
131
133
  private
132
134
 
133
- class AbstractLocator < Puppet::Pops::Parser::Locator
135
+ class AbstractLocator < Locator
134
136
  attr_accessor :line_index
135
137
  attr_accessor :string
136
138
  attr_accessor :prev_offset
@@ -286,3 +288,5 @@ class Puppet::Pops::Parser::Locator
286
288
  end
287
289
  end
288
290
  end
291
+ end
292
+ end
@@ -0,0 +1,30 @@
1
+ require 'uri'
2
+
3
+ module Puppet::Pops
4
+ module Pcore
5
+ TYPE_QUALIFIED_REFERENCE = Types::TypeFactory.pattern(Types::TypeFactory.regexp(Patterns::CLASSREF_EXT))
6
+
7
+ def self.init(loader, ir)
8
+ add_alias('Puppet::Pcore::QualifiedReference', TYPE_QUALIFIED_REFERENCE, loader)
9
+
10
+ ir.register_implementation_namespace('Puppet::Pcore', 'Puppet::Pops::Pcore', loader)
11
+ end
12
+
13
+ def self.add_alias(name, type, loader)
14
+ add_type(Types::PTypeAliasType.new(name, nil, type), loader)
15
+ end
16
+
17
+ def self.add_type(type, loader)
18
+ loader.set_entry(Loader::Loader::TypedName.new(:type, type.name.downcase), type)
19
+ end
20
+
21
+ def self.register_implementations(*impls)
22
+ Loaders.loaders.register_implementations(*impls)
23
+ end
24
+
25
+ def self.register_aliases(aliases)
26
+ loader = Loaders.loaders.private_environment_loader
27
+ aliases.each { |name, type_string| add_type(Types::PTypeAliasType.new(name, Types::TypeFactory.type_reference(type_string), nil), loader) }
28
+ end
29
+ end
30
+ end
@@ -76,11 +76,9 @@ class ClassLoader
76
76
  end
77
77
 
78
78
  def self.provide_from_string(name)
79
- name_path = name.split('::')
79
+ name_path = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)
80
80
  # always from the root, so remove an empty first segment
81
- if name_path[0].empty?
82
- name_path = name_path[1..-1]
83
- end
81
+ name_path.shift if name_path[0].empty?
84
82
  provide_from_name_path(name, name_path)
85
83
  end
86
84
 
@@ -0,0 +1,146 @@
1
+ module Puppet::Pops
2
+ module Types
3
+ # The {ImplementationRegistry} maps names types in the Puppet Type System to names of corresponding implementation
4
+ # modules/classes. Each mapping is unique and bidirectional so that for any given type name there is only one
5
+ # implementation and vice versa.
6
+ #
7
+ # @api private
8
+ class ImplementationRegistry
9
+ TYPE_REGEXP_SUBST = TypeFactory.tuple([PRegexpType::DEFAULT, PStringType::NON_EMPTY])
10
+
11
+ def self.singleton
12
+ @singleton ||= new(Loaders.static_loader)
13
+ end
14
+
15
+ # Create a new instance. This method is normally only called once
16
+ #
17
+ # The initializer will create mappings for well known types that can be loaded using the static loader
18
+ #
19
+ def initialize(static_loader)
20
+ @type_names_per_implementation = {}
21
+ @implementations_per_type_name = {}
22
+ @type_name_substitutions = []
23
+ @impl_name_substitutions = []
24
+ @type_parser = Types::TypeParser.new
25
+
26
+ register_implementation('Pcore::AST::Locator', 'Puppet::Pops::Parser::Locator::Locator19', static_loader)
27
+ register_implementation_namespace('Pcore::AST', 'Puppet::Pops::Model', static_loader)
28
+
29
+ TypeParser.type_map.values.each { |type| register_implementation(type.simple_name, type.class.name, static_loader) }
30
+ end
31
+
32
+ # Register a bidirectional type mapping.
33
+ #
34
+ # @overload register_type_mapping(runtime_type, puppet_type)
35
+ # @param runtime_type [PRuntimeType] type that represents the runtime module or class to map to a puppet type
36
+ # @param puppet_type [PAnyType] type that will be mapped to the runtime module or class
37
+ # @param loader [Loader::Loader] the loader to use when resolving names
38
+ # @overload register_type_mapping(runtime_type, pattern_replacement)
39
+ # @param runtime_type [PRuntimeType] type containing the pattern and replacement to map the runtime type to a puppet type
40
+ # @param puppet_type [Array(Regexp,String)] the pattern and replacement to map a puppet type to a runtime type
41
+ # @param loader [Loader::Loader] the loader to use when resolving names
42
+ def register_type_mapping(runtime_type, puppet_type_or_pattern, loader)
43
+ TypeAsserter.assert_assignable('First argument of type mapping', PRuntimeType::RUBY, runtime_type)
44
+ expr = runtime_type.name_or_pattern
45
+ if expr.is_a?(Array)
46
+ TypeAsserter.assert_instance_of('Second argument of type mapping', TYPE_REGEXP_SUBST, puppet_type_or_pattern)
47
+ register_implementation_regexp(puppet_type_or_pattern, expr, loader)
48
+ else
49
+ TypeAsserter.assert_instance_of('Second argument of type mapping', PType::DEFAULT, puppet_type_or_pattern)
50
+ register_implementation(puppet_type_or_pattern, expr, loader)
51
+ end
52
+ end
53
+
54
+ # Register a bidirectional namespace mapping
55
+ #
56
+ # @param type_namespace [String] the namespace for the puppet types
57
+ # @param impl_namespace [String] the namespace for the implementations
58
+ # @param loader [Loader::Loader] the loader to use when resolving names
59
+ def register_implementation_namespace(type_namespace, impl_namespace, loader)
60
+ ns = TypeFormatter::NAME_SEGMENT_SEPARATOR
61
+ register_implementation_regexp(
62
+ [Regexp.compile("^#{type_namespace}#{ns}(\\w+)$"), "#{impl_namespace}#{ns}\\1"],
63
+ [Regexp.compile("^#{impl_namespace}#{ns}(\\w+)$"), "#{type_namespace}#{ns}\\1"],
64
+ loader)
65
+ end
66
+
67
+ # Register a bidiractional regexp mapping
68
+ #
69
+ # @param type_name_subst [Array(Regexp,String)] regexp and replacement mapping type names to runtime names
70
+ # @param impl_name_subst [Array(Regexp,String)] regexp and replacement mapping runtime names to type names
71
+ # @param loader [Loader::Loader] the loader to use when resolving names
72
+ def register_implementation_regexp(type_name_subst, impl_name_subst, loader)
73
+ @type_name_substitutions << [type_name_subst, loader]
74
+ @impl_name_substitutions << [impl_name_subst, loader]
75
+ nil
76
+ end
77
+
78
+ # Register a bidirectional mapping between a type and an implementation
79
+ #
80
+ # @param type [PAnyType,String] the type or type name
81
+ # @param impl_module[Module,String] the module or module name
82
+ # @param loader [Loader::Loader] the loader to use when resolving names
83
+ def register_implementation(type, impl_module, loader)
84
+ type = type.name if type.is_a?(PAnyType)
85
+ impl_module = impl_module.name if impl_module.is_a?(Module)
86
+ @type_names_per_implementation[impl_module] = [type, loader]
87
+ @implementations_per_type_name[type] = [impl_module, loader]
88
+ nil
89
+ end
90
+
91
+ # Find the name for the module that corresponds to the given type or type name
92
+ #
93
+ # @param type [PAnyType,String] the name of the type
94
+ # @return [String,nil] the name of the implementation module, or `nil` if no mapping was found
95
+ def module_name_for_type(type)
96
+ type = type.name if type.is_a?(PAnyType)
97
+ find_mapping(type, @implementations_per_type_name, @type_name_substitutions)
98
+ end
99
+
100
+ # Find the module that corresponds to the given type or type name
101
+ #
102
+ # @param type [PAnyType,String] the name of the type
103
+ # @return [Module,nil] the name of the implementation module, or `nil` if no mapping was found
104
+ def module_for_type(type)
105
+ name_and_loader = module_name_for_type(type)
106
+ # TODO Shouldn't ClassLoader be module specific?
107
+ name_and_loader.nil? ? nil : ClassLoader.provide(name_and_loader[0])
108
+ end
109
+
110
+ # Find the type name and loader that corresponds to the given runtime module or module name
111
+ #
112
+ # @param impl_module [Module,String] the implementation class or class name
113
+ # @return [Array(String,Loader::Loader),nil] the name and loader of the type, or `nil` if no mapping was found
114
+ def type_name_for_module(impl_module)
115
+ impl_module = impl_module.name if impl_module.is_a?(Module)
116
+ find_mapping(impl_module, @type_names_per_implementation, @impl_name_substitutions)
117
+ end
118
+
119
+ # Find the name for, and then load, the type that corresponds to the given runtime module or module name
120
+ # The method will return `nil` if no mapping is found, a TypeReference if a mapping was found but the
121
+ # loader didn't find the type, or the loaded type.
122
+ #
123
+ # @param impl_module [Module,String] the implementation class or class name
124
+ # @return [PAnyType,nil] the type, or `nil` if no mapping was found
125
+ def type_for_module(impl_module)
126
+ name_and_loader = type_name_for_module(impl_module)
127
+ if name_and_loader.nil?
128
+ nil
129
+ else
130
+ @type_parser.parse(*name_and_loader)
131
+ end
132
+ end
133
+
134
+ def find_mapping(name, names, substitutions)
135
+ found = names[name]
136
+ if found.nil?
137
+ substitutions.each do |subst|
138
+ substituted = name.sub(*subst[0])
139
+ return [substituted, subst[1]] unless substituted == name
140
+ end
141
+ end
142
+ found
143
+ end
144
+ end
145
+ end
146
+ end
@@ -54,7 +54,7 @@ module Puppet::Pops::Types
54
54
  Iterator.new(PUnitType::DEFAULT, o.each)
55
55
  else
56
56
  tc = TypeCalculator.singleton
57
- Iterator.new(tc.unwrap_single_variant(PVariantType.new(o.map {|e| tc.infer_set(e) })), o.each)
57
+ Iterator.new(PVariantType.maybe_create(o.map {|e| tc.infer_set(e) }), o.each)
58
58
  end
59
59
  when Hash
60
60
  # Each element is a two element [key, value] tuple.
@@ -63,8 +63,8 @@ module Puppet::Pops::Types
63
63
  else
64
64
  tc = TypeCalculator.singleton
65
65
  Iterator.new(PTupleType.new([
66
- tc.unwrap_single_variant(PVariantType.new(o.keys.map {|e| tc.infer_set(e) })),
67
- tc.unwrap_single_variant(PVariantType.new(o.values.map {|e| tc.infer_set(e) }))], PHashType::KEY_PAIR_TUPLE_SIZE), o.each_pair)
66
+ PVariantType.maybe_create(o.keys.map {|e| tc.infer_set(e) }),
67
+ PVariantType.maybe_create(o.values.map {|e| tc.infer_set(e) })], PHashType::KEY_PAIR_TUPLE_SIZE), o.each_pair)
68
68
  end
69
69
  when Integer
70
70
  if o == 0
@@ -210,7 +210,7 @@ module Puppet::Pops::Types
210
210
 
211
211
  def to_s
212
212
  et = element_type
213
- et.nil? ? 'Iterator value' : "Iterator[#{et.generalize}] value"
213
+ et.nil? ? 'Iterator-Value' : "Iterator[#{et.generalize}]-Value"
214
214
  end
215
215
 
216
216
  def unbounded?
@@ -0,0 +1,846 @@
1
+ require_relative 'ruby_generator'
2
+
3
+ module Puppet::Pops
4
+ module Types
5
+
6
+ # @api public
7
+ class PObjectType < PAnyType
8
+ KEY_ANNOTATIONS = 'annotations'.freeze
9
+ KEY_ATTRIBUTES = 'attributes'.freeze
10
+ KEY_CHECKS = 'checks'.freeze
11
+ KEY_EQUALITY = 'equality'.freeze
12
+ KEY_EQUALITY_INCLUDE_TYPE = 'equality_include_type'.freeze
13
+ KEY_FINAL = 'final'.freeze
14
+ KEY_FUNCTIONS = 'functions'.freeze
15
+ KEY_KIND = 'kind'.freeze
16
+ KEY_NAME = 'name'.freeze
17
+ KEY_OVERRIDE = 'override'.freeze
18
+ KEY_PARENT = 'parent'.freeze
19
+ KEY_TYPE = 'type'.freeze
20
+ KEY_VALUE = 'value'.freeze
21
+
22
+ ATTRIBUTE_KIND_CONSTANT = 'constant'.freeze
23
+ ATTRIBUTE_KIND_DERIVED = 'derived'.freeze
24
+ ATTRIBUTE_KIND_GIVEN_OR_DERIVED = 'given_or_derived'.freeze
25
+ TYPE_ATTRIBUTE_KIND = TypeFactory.enum(ATTRIBUTE_KIND_CONSTANT, ATTRIBUTE_KIND_DERIVED, ATTRIBUTE_KIND_GIVEN_OR_DERIVED)
26
+
27
+ TYPE_ANNOTATION_KEY_TYPE = PType::DEFAULT # TBD
28
+ TYPE_ANNOTATION_VALUE_TYPE = PStructType::DEFAULT #TBD
29
+ TYPE_ANNOTATIONS = PHashType.new(TYPE_ANNOTATION_KEY_TYPE, TYPE_ANNOTATION_VALUE_TYPE)
30
+
31
+ TYPE_OBJECT_NAME = Pcore::TYPE_QUALIFIED_REFERENCE
32
+ TYPE_MEMBER_NAME = PPatternType.new([PRegexpType.new(Patterns::PARAM_NAME)])
33
+
34
+ TYPE_ATTRIBUTE = TypeFactory.struct({
35
+ KEY_TYPE => PType::DEFAULT,
36
+ KEY_ANNOTATIONS => TypeFactory.optional(TYPE_ANNOTATIONS),
37
+ KEY_FINAL => TypeFactory.optional(PBooleanType::DEFAULT),
38
+ KEY_OVERRIDE => TypeFactory.optional(PBooleanType::DEFAULT),
39
+ KEY_KIND => TypeFactory.optional(TYPE_ATTRIBUTE_KIND),
40
+ KEY_VALUE => PAnyType::DEFAULT
41
+ })
42
+ TYPE_ATTRIBUTES = TypeFactory.hash_kv(TYPE_MEMBER_NAME, TypeFactory.not_undef)
43
+ TYPE_ATTRIBUTE_CALLABLE = TypeFactory.callable(0,0)
44
+
45
+ TYPE_FUNCTION_TYPE = PType.new(PCallableType::DEFAULT)
46
+
47
+ TYPE_FUNCTION = TypeFactory.struct({
48
+ KEY_TYPE => TYPE_FUNCTION_TYPE,
49
+ KEY_ANNOTATIONS => TypeFactory.optional(TYPE_ANNOTATIONS),
50
+ KEY_FINAL => TypeFactory.optional(PBooleanType::DEFAULT),
51
+ KEY_OVERRIDE => TypeFactory.optional(PBooleanType::DEFAULT)
52
+ })
53
+ TYPE_FUNCTIONS = TypeFactory.hash_kv(TYPE_MEMBER_NAME, TypeFactory.not_undef)
54
+
55
+ TYPE_EQUALITY = TypeFactory.variant(TYPE_MEMBER_NAME, TypeFactory.array_of(TYPE_MEMBER_NAME))
56
+
57
+ TYPE_CHECKS = PAnyType::DEFAULT # TBD
58
+
59
+ TYPE_OBJECT_I12N = TypeFactory.struct({
60
+ KEY_NAME => TypeFactory.optional(TYPE_OBJECT_NAME),
61
+ KEY_PARENT => TypeFactory.optional(PType::DEFAULT),
62
+ KEY_ATTRIBUTES => TypeFactory.optional(TYPE_ATTRIBUTES),
63
+ KEY_FUNCTIONS => TypeFactory.optional(TYPE_FUNCTIONS),
64
+ KEY_EQUALITY => TypeFactory.optional(TYPE_EQUALITY),
65
+ KEY_EQUALITY_INCLUDE_TYPE => TypeFactory.optional(PBooleanType::DEFAULT),
66
+ KEY_CHECKS => TypeFactory.optional(TYPE_CHECKS),
67
+ KEY_ANNOTATIONS => TypeFactory.optional(TYPE_ANNOTATIONS)
68
+ })
69
+
70
+ # @abstract Encapsulates behavior common to {PAttribute} and {PFunction}
71
+ # @api public
72
+ class PAnnotatedMember
73
+
74
+ # @return [PObjectType] the object type containing this member
75
+ # @api public
76
+ attr_reader :container
77
+
78
+ # @return [String] the name of this member
79
+ # @api public
80
+ attr_reader :name
81
+
82
+ # @return [PAnyType] the type of this member
83
+ # @api public
84
+ attr_reader :type
85
+
86
+ # @return [Hash{PType => Hash}] the annotations or `nil`
87
+ # @api public
88
+ attr_reader :annotations
89
+
90
+ # @param name [String] The name of the member
91
+ # @param container [PObjectType] The containing object type
92
+ # @param i12n_hash [Hash{String=>Object}] Hash containing feature options
93
+ # @option i12n_hash [PAnyType] 'type' The member type (required)
94
+ # @option i12n_hash [Boolean] 'override' `true` if this feature must override an inherited feature. Default is `false`.
95
+ # @option i12n_hash [Boolean] 'final' `true` if this feature cannot be overridden. Default is `false`.
96
+ # @option i12n_hash [Hash{PType => Hash}] 'annotations' Annotations hash. Default is `nil`.
97
+ # @api public
98
+ def initialize(name, container, i12n_hash)
99
+ @name = name
100
+ @container = container
101
+ @type = i12n_hash[KEY_TYPE]
102
+ @override = i12n_hash[KEY_OVERRIDE]
103
+ @override = false if @override.nil?
104
+ @final = i12n_hash[KEY_FINAL]
105
+ @final = false if @final.nil?
106
+ @annotations = i12n_hash[KEY_ANNOTATIONS]
107
+ @annotations.freeze unless @annotations.nil?
108
+ end
109
+
110
+ # Delegates to the contained type
111
+ # @param visitor [TypeAcceptor] the visitor
112
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
113
+ # @api public
114
+ def accept(visitor, guard)
115
+ @type.accept(visitor, guard)
116
+ @annotations.each_key { |key| key.accept(visitor, guard) } unless @annotations.nil?
117
+ end
118
+
119
+ # Checks if the this _member_ overrides an inherited member, and if so, that this member is declared with override = true and that
120
+ # the inherited member accepts to be overridden by this member.
121
+ #
122
+ # @param parent_members [Hash{String=>PAnnotatedMember}] the hash of inherited members
123
+ # @return [PAnnotatedMember] this instance
124
+ # @raises [Puppet::ParseError] if the assertion fails
125
+ # @api private
126
+ def assert_override(parent_members)
127
+ parent_member = parent_members[@name]
128
+ if parent_member.nil?
129
+ raise Puppet::ParseError, "expected #{label} to override an inherited #{feature_type}, but no such #{feature_type} was found" if @override
130
+ self
131
+ else
132
+ parent_member.assert_can_be_overridden(self)
133
+ end
134
+ end
135
+
136
+ # Checks if the given _member_ can override this member.
137
+ #
138
+ # @param member [PAnnotatedMember] the overriding member
139
+ # @return [PAnnotatedMember] its argument
140
+ # @raises [Puppet::ParseError] if the assertion fails
141
+ # @api private
142
+ def assert_can_be_overridden(member)
143
+ raise Puppet::ParseError, "#{member.label} attempts to override #{label}" unless self.class == member.class
144
+ raise Puppet::ParseError, "#{member.label} attempts to override final #{label}" if @final
145
+ raise Puppet::ParseError, "#{member.label} attempts to override #{label} without having override => true" unless member.override?
146
+ raise Puppet::ParseError, "#{member.label} attempts to override #{label} with a type that does not match" unless @type.assignable?(member.type)
147
+ member
148
+ end
149
+
150
+ # @return [Boolean] `true` if this feature cannot be overridden
151
+ # @api public
152
+ def final?
153
+ @final
154
+ end
155
+
156
+ # @return [Boolean] `true` if this feature must override an inherited feature
157
+ # @api public
158
+ def override?
159
+ @override
160
+ end
161
+
162
+ # @api public
163
+ def hash
164
+ @name.hash ^ @type.hash
165
+ end
166
+
167
+ # @api public
168
+ def eql?(o)
169
+ self.class == o.class && @name == o.name && @type == o.type && @override == o.override? && @final == o.final?
170
+ end
171
+
172
+ # @api public
173
+ def ==(o)
174
+ eql?(o)
175
+ end
176
+
177
+ # Returns the member as a hash suitable as an argument for constructor. Name is excluded
178
+ # @return [Hash{String=>Object}] the initialization hash
179
+ # @api private
180
+ def i12n_hash
181
+ hash = { KEY_TYPE => @type }
182
+ hash[KEY_FINAL] = true if @final
183
+ hash[KEY_OVERRIDE] = true if @override
184
+ hash[KEY_ANNOTATIONS] = @annotations unless @annotations.nil?
185
+ hash
186
+ end
187
+
188
+ # @api private
189
+ def feature_type
190
+ self.class.feature_type
191
+ end
192
+
193
+ # @api private
194
+ def label
195
+ self.class.label(@container, @name)
196
+ end
197
+
198
+ # Performs type checking of arguments and invokes the method that corresponds to this
199
+ # method. The result of the invocation is returned
200
+ #
201
+ # @param receiver [Object] The receiver of the call
202
+ # @param scope [Puppet::Parser::Scope] The caller scope
203
+ # @param args [Array] Array of arguments.
204
+ # @return [Object] The result returned by the member function or attribute
205
+ #
206
+ # @api private
207
+ def invoke(receiver, scope, args, &block)
208
+ @dispatch ||= create_dispatch(receiver)
209
+
210
+ args_type = TypeCalculator.infer_set(block_given? ? args + [block] : args)
211
+ unless @dispatch.type.callable?(args_type)
212
+ raise ArgumentError, TypeMismatchDescriber.describe_signatures(label, [@dispatch], args_type)
213
+ end
214
+ @dispatch.invoke(receiver, scope, args, &block)
215
+ end
216
+
217
+ # @api private
218
+ def create_dispatch(instance)
219
+ # TODO: Assumes Ruby implementation for now
220
+ Functions::Dispatch.new(callable_type, name, [],
221
+ callable_type.block_type.nil? ? nil : 'block', nil, nil, false)
222
+ end
223
+
224
+ # @api private
225
+ def self.feature_type
226
+ raise NotImplementedError, "'#{self.class.name}' should implement #feature_type"
227
+ end
228
+
229
+ def self.label(container, name)
230
+ "#{feature_type} #{container.label}[#{name}]"
231
+ end
232
+ end
233
+
234
+ # Describes a named Attribute in an Object type
235
+ # @api public
236
+ class PAttribute < PAnnotatedMember
237
+
238
+ # @return [String,nil] The attribute kind as defined by #TYPE_ATTRIBUTE_KIND, or `nil` to
239
+ # indicate that
240
+ attr_reader :kind
241
+
242
+ # @param name [String] The name of the attribute
243
+ # @param container [PObjectType] The containing object type
244
+ # @param i12n_hash [Hash{String=>Object}] Hash containing attribute options
245
+ # @option i12n_hash [PAnyType] 'type' The attribute type (required)
246
+ # @option i12n_hash [Object] 'value' The default value, must be an instanceof the given `type` (optional)
247
+ # @option i12n_hash [String] 'kind' The attribute kind, matching #TYPE_ATTRIBUTE_KIND
248
+ # @api public
249
+ def initialize(name, container, i12n_hash)
250
+ super(name, container, TypeAsserter.assert_instance_of(nil, TYPE_ATTRIBUTE, i12n_hash) { "initializer for #{self.class.label(container, name)}" })
251
+ @kind = i12n_hash[KEY_KIND]
252
+ if @kind == ATTRIBUTE_KIND_CONSTANT # final is implied
253
+ if i12n_hash.include?(KEY_FINAL) && !@final
254
+ raise Puppet::ParseError, "#{label} of kind 'constant' cannot be combined with final => false"
255
+ end
256
+ @final = true
257
+ end
258
+
259
+ if i12n_hash.include?(KEY_VALUE)
260
+ if @kind == ATTRIBUTE_KIND_DERIVED || @kind == ATTRIBUTE_KIND_GIVEN_OR_DERIVED
261
+ raise Puppet::ParseError, "#{label} of kind '#{@kind}' cannot be combined with an attribute value"
262
+ end
263
+ @value = TypeAsserter.assert_instance_of(nil, type, i12n_hash[KEY_VALUE]) {"#{label} #{KEY_VALUE}" }
264
+ else
265
+ raise Puppet::ParseError, "#{label} of kind 'constant' requires a value" if @kind == ATTRIBUTE_KIND_CONSTANT
266
+ @value = :undef # Not to be confused with nil or :default
267
+ end
268
+ end
269
+
270
+ def callable_type
271
+ TYPE_ATTRIBUTE_CALLABLE
272
+ end
273
+
274
+ # @api public
275
+ def eql?(o)
276
+ super && @kind == o.kind && @value == (o.value? ? o.value : :undef)
277
+ end
278
+
279
+ # Returns the member as a hash suitable as an argument for constructor. Name is excluded
280
+ # @return [Hash{String=>Object}] the hash
281
+ # @api private
282
+ def i12n_hash
283
+ hash = super
284
+ unless @kind.nil?
285
+ hash[KEY_KIND] = @kind
286
+ hash.delete(KEY_FINAL) if @kind == ATTRIBUTE_KIND_CONSTANT # final is implied
287
+ end
288
+ hash[KEY_VALUE] = @value unless @value == :undef
289
+ hash
290
+ end
291
+
292
+ # @return [Boolean] `true` if a value has been defined for this attribute.
293
+ def value?
294
+ @value != :undef
295
+ end
296
+
297
+ # Returns the value of this attribute, or raises an error if no value has been defined. Raising an error
298
+ # is necessary since a defined value may be `nil`.
299
+ #
300
+ # @return [Object] the value that has been defined for this attribute.
301
+ # @raise [Puppet::Error] if no value has been defined
302
+ # @api public
303
+ def value
304
+ # An error must be raised here since `nil` is a valid value and it would be bad to leak the :undef symbol
305
+ raise Puppet::Error, "#{label} has no value" if @value == :undef
306
+ @value
307
+ end
308
+
309
+ # @api private
310
+ def self.feature_type
311
+ 'attribute'
312
+ end
313
+ end
314
+
315
+ # Describes a named Function in an Object type
316
+ # @api public
317
+ class PFunction < PAnnotatedMember
318
+
319
+ # @param name [String] The name of the attribute
320
+ # @param container [PObjectType] The containing object type
321
+ # @param i12n_hash [Hash{String=>Object}] Hash containing function options
322
+ # @api public
323
+ def initialize(name, container, i12n_hash)
324
+ super(name, container, TypeAsserter.assert_instance_of(["initializer for function '%s'", name], TYPE_FUNCTION, i12n_hash))
325
+ end
326
+
327
+ def callable_type
328
+ type
329
+ end
330
+
331
+ # @api private
332
+ def self.feature_type
333
+ 'function'
334
+ end
335
+ end
336
+
337
+ attr_reader :name
338
+ attr_reader :parent
339
+ attr_reader :attributes
340
+ attr_reader :functions
341
+ attr_reader :equality
342
+ attr_reader :checks
343
+ attr_reader :annotations
344
+
345
+ # Initialize an Object Type instance. The initialization will use either a name and an initialization
346
+ # hash expression, or a fully resolved initialization hash.
347
+ #
348
+ # @overload initialize(name, i12n_hash_expression)
349
+ # Used when the Object type is loaded using a type alias expression. When that happens, it is important that
350
+ # the actual resolution of the expression is deferred until all definitions have been made known to the current
351
+ # loader. The object will then be resolved when it is loaded by the {TypeParser}. "resolved" here, means that
352
+ # the hash expression is fully resolved, and then passed to the {#initialize_from_hash} method.
353
+ # @param name [String] The name of the object
354
+ # @param i12n_hash_expression [Model::LiteralHash] The hash describing the Object features
355
+ #
356
+ # @overload initialize(i12n_hash)
357
+ # Used when the object is created by the {TypeFactory}. The i12n_hash must be fully resolved.
358
+ # @param i12n_hash [Hash{String=>Object}] The hash describing the Object features
359
+ #
360
+ # @api private
361
+ def initialize(name_or_i12n_hash, i12n_hash_expression = nil)
362
+ @attributes = EMPTY_HASH
363
+ @functions = EMPTY_HASH
364
+
365
+ if name_or_i12n_hash.is_a?(Hash)
366
+ initialize_from_hash(name_or_i12n_hash)
367
+ else
368
+ @name = TypeAsserter.assert_instance_of('object name', TYPE_OBJECT_NAME, name_or_i12n_hash)
369
+ @i12n_hash_expression = i12n_hash_expression
370
+ end
371
+ end
372
+
373
+ # @api private
374
+ def new_function(loader)
375
+ @new_function ||= create_new_function(loader)
376
+ end
377
+
378
+ # @api private
379
+ def create_new_function(loader)
380
+ impl_name = Loaders.implementation_registry.module_name_for_type(self)
381
+ if impl_name.nil?
382
+ # Use generator to create a default implementation
383
+ impl_class = RubyGenerator.new.create_class(self)
384
+ class_name = "Anonymous Ruby class for #{name}"
385
+ else
386
+ # Can the mapping be loaded?
387
+ class_name = impl_name[0]
388
+ impl_class = ClassLoader.provide(class_name)
389
+
390
+ raise Puppet::Error, "Unable to load class #{class_name}" if impl_class.nil?
391
+ unless impl_class < PuppetObject
392
+ raise Puppet::Error, "Unable to create an instance of #{name}. #{class_name} does not include module #{PuppetObject.name}"
393
+ end
394
+ end
395
+
396
+ i12n_t = i12n_type
397
+ from_hash_type = TypeFactory.callable(i12n_t, 1, 1)
398
+
399
+ # Create a types and a names array where optional entries ends up last
400
+ opt_types = []
401
+ opt_names = []
402
+ non_opt_types = []
403
+ non_opt_names = []
404
+ i12n_t.elements.each do |se|
405
+ if se.key_type.is_a?(POptionalType)
406
+ opt_names << se.name
407
+ opt_types << se.value_type
408
+ else
409
+ non_opt_names << se.name
410
+ non_opt_types << se.value_type
411
+ end
412
+ end
413
+ param_names = non_opt_names + opt_names
414
+ param_types = non_opt_types + opt_types
415
+
416
+ # Create the callable with a size that reflects the required and optional parameters
417
+ param_types << non_opt_types.size
418
+ param_types << param_names.size
419
+ create_type = TypeFactory.callable(*param_types)
420
+
421
+ # Create and return a #new_XXX function where the dispatchers are added programmatically.
422
+ Puppet::Functions.create_loaded_function(:"new_#{name}", loader) do
423
+
424
+ # The class that creates new instances must be available to the constructor methods
425
+ # and is therefore declared as a variable and accessor on the class that represents
426
+ # this added function.
427
+ @impl_class = impl_class
428
+
429
+ def self.impl_class
430
+ @impl_class
431
+ end
432
+
433
+ # It's recommended that an implementor of an Object type provides the method #from_asserted_hash.
434
+ # This method should accept a hash and assume that type assertion has been made already (it is made
435
+ # by the dispatch added here).
436
+ if impl_class.respond_to?(:from_asserted_hash)
437
+ dispatcher.add_dispatch(from_hash_type, :from_hash, ['hash'], nil, EMPTY_ARRAY, EMPTY_ARRAY, false)
438
+ def from_hash(hash)
439
+ self.class.impl_class.from_asserted_hash(hash)
440
+ end
441
+ end
442
+
443
+ # Add the dispatch that uses the standard #new method on the class. It's assumed that the #new
444
+ # method performs no assertions.
445
+ dispatcher.add_dispatch(create_type, :create, param_names, nil, EMPTY_ARRAY, EMPTY_ARRAY, false)
446
+ def create(*args)
447
+ self.class.impl_class.new(*args)
448
+ end
449
+ end
450
+ end
451
+
452
+ # @api private
453
+ def include_class_in_equality?
454
+ @equality_include_type && !(@parent.is_a?(PObjectType) && parent.include_class_in_equality?)
455
+ end
456
+
457
+ # Called from the TypeParser once it has found a type using the Loader. The TypeParser will
458
+ # interpret the contained expression and the resolved type is remembered. This method also
459
+ # checks and remembers if the resolve type contains self recursion.
460
+ #
461
+ # @param type_parser [TypeParser] type parser that will interpret the type expression
462
+ # @param loader [Loader::Loader] loader to use when loading type aliases
463
+ # @return [PObjectType] the receiver of the call, i.e. `self`
464
+ # @api private
465
+ def resolve(type_parser, loader)
466
+ unless @i12n_hash_expression.nil?
467
+ @self_recursion = true # assumed while it being found out below
468
+
469
+ i12n_hash_expression = @i12n_hash_expression
470
+ @i12n_hash_expression = nil
471
+ if i12n_hash_expression.is_a?(Model::LiteralHash)
472
+ i12n_hash = resolve_literal_hash(type_parser, loader, i12n_hash_expression)
473
+ else
474
+ i12n_hash = resolve_hash(type_parser, loader, i12n_hash_expression)
475
+ end
476
+ initialize_from_hash(i12n_hash)
477
+
478
+ # Find out if this type is recursive. A recursive type has performance implications
479
+ # on several methods and this knowledge is used to avoid that for non-recursive
480
+ # types.
481
+ guard = RecursionGuard.new
482
+ accept(NoopTypeAcceptor::INSTANCE, guard)
483
+ @self_recursion = guard.recursive_this?(self)
484
+ end
485
+ self
486
+ end
487
+
488
+ def resolve_literal_hash(type_parser, loader, i12n_hash_expression)
489
+ type_parser.interpret_LiteralHash(i12n_hash_expression, loader)
490
+ end
491
+
492
+ def resolve_hash(type_parser, loader, i12n_hash)
493
+ resolve_type_refs(type_parser, loader, i12n_hash)
494
+ end
495
+
496
+ def resolve_type_refs(type_parser, loader, o)
497
+ case o
498
+ when Hash
499
+ Hash[o.map { |k, v| [resolve_type_refs(type_parser, loader, k), resolve_type_refs(type_parser, loader, v)] }]
500
+ when Array
501
+ o.map { |e| resolve_type_refs(type_parser, loader, e) }
502
+ when PTypeReferenceType
503
+ o.resolve(type_parser, loader)
504
+ else
505
+ o
506
+ end
507
+ end
508
+
509
+ # @api private
510
+ def initialize_from_hash(i12n_hash)
511
+ TypeAsserter.assert_instance_of('object initializer', TYPE_OBJECT_I12N, i12n_hash)
512
+
513
+ # Name given to the loader have higher precedence than a name declared in the type
514
+ @name ||= i12n_hash[KEY_NAME]
515
+ @name.freeze unless @name.nil?
516
+
517
+ @parent = i12n_hash[KEY_PARENT]
518
+
519
+ parent_members = EMPTY_HASH
520
+ parent_object_type = nil
521
+ unless @parent.nil?
522
+ check_self_recursion(self)
523
+ rp = resolved_parent
524
+ if rp.is_a?(PObjectType)
525
+ parent_object_type = rp
526
+ parent_members = rp.members(true)
527
+ end
528
+ end
529
+
530
+ attr_specs = i12n_hash[KEY_ATTRIBUTES]
531
+ unless attr_specs.nil? || attr_specs.empty?
532
+ @attributes = Hash[attr_specs.map do |key, attr_spec|
533
+ attr_spec = { KEY_TYPE => TypeAsserter.assert_instance_of(nil, PType::DEFAULT, attr_spec) { "attribute #{label}[#{key}]" } } unless attr_spec.is_a?(Hash)
534
+ attr = PAttribute.new(key, self, attr_spec)
535
+ [attr.name, attr.assert_override(parent_members)]
536
+ end].freeze
537
+ end
538
+
539
+ func_specs = i12n_hash[KEY_FUNCTIONS]
540
+ unless func_specs.nil? || func_specs.empty?
541
+ @functions = Hash[func_specs.map do |key, func_spec|
542
+ func_spec = { KEY_TYPE => TypeAsserter.assert_instance_of(nil, TYPE_FUNCTION_TYPE, func_spec) { "function #{label}[#{key}]" } } unless func_spec.is_a?(Hash)
543
+ func = PFunction.new(key, self, func_spec)
544
+ name = func.name
545
+ raise Puppet::ParseError, "#{func.label} conflicts with attribute with the same name" if @attributes.include?(name)
546
+ [name, func.assert_override(parent_members)]
547
+ end].freeze
548
+ end
549
+
550
+ @equality_include_type = i12n_hash[KEY_EQUALITY_INCLUDE_TYPE]
551
+ @equality_include_type = true if @equality_include_type.nil?
552
+
553
+ equality = i12n_hash[KEY_EQUALITY]
554
+ equality = [equality] if equality.is_a?(String)
555
+ if equality.is_a?(Array)
556
+ unless equality.empty?
557
+ raise Puppet::ParseError, 'equality_include_type = false cannot be combined with non empty equality specification' unless @equality_include_type
558
+ parent_eq_attrs = nil
559
+ equality.each do |attr_name|
560
+
561
+ attr = parent_members[attr_name]
562
+ if attr.nil?
563
+ attr = @attributes[attr_name] || @functions[attr_name]
564
+ elsif attr.is_a?(PAttribute)
565
+ # Assert that attribute is not already include by parent equality
566
+ parent_eq_attrs ||= parent_object_type.equality_attributes
567
+ if parent_eq_attrs.include?(attr_name)
568
+ including_parent = find_equality_definer_of(attr)
569
+ raise Puppet::ParseError, "#{label} equality is referencing #{attr.label} which is included in equality of #{including_parent.label}"
570
+ end
571
+ end
572
+
573
+ unless attr.is_a?(PAttribute)
574
+ raise Puppet::ParseError, "#{label} equality is referencing non existent attribute '#{attr_name}'" if attr.nil?
575
+ raise Puppet::ParseError, "#{label} equality is referencing #{attr.label}. Only attribute references are allowed"
576
+ end
577
+ if attr.kind == ATTRIBUTE_KIND_CONSTANT
578
+ raise Puppet::ParseError, "#{label} equality is referencing constant #{attr.label}. Reference to constant is not allowed in equality"
579
+ end
580
+ end
581
+ end
582
+ equality.freeze
583
+ end
584
+ @equality = equality
585
+
586
+ @checks = i12n_hash[KEY_CHECKS]
587
+
588
+ @annotations = i12n_hash[KEY_ANNOTATIONS]
589
+ @annotations.freeze unless @annotations.nil?
590
+ end
591
+
592
+ def [](name)
593
+ member = @attributes[name] || @functions[name]
594
+ if member.nil?
595
+ rp = resolved_parent
596
+ member = rp[name] if rp.is_a?(PObjectType)
597
+ end
598
+ member
599
+ end
600
+
601
+ def accept(visitor, guard)
602
+ guarded_recursion(guard, nil) do |g|
603
+ super(visitor, g)
604
+ @parent.accept(visitor, g) unless parent.nil?
605
+ @attributes.values.each { |a| a.accept(visitor, g) }
606
+ @functions.values.each { |f| f.accept(visitor, g) }
607
+ @annotations.each_key { |key| key.accept(visitor, g) } unless @annotations.nil?
608
+ end
609
+ end
610
+
611
+ def callable_args?(callable, guard)
612
+ @parent.nil? ? false : @parent.callable_args?(callable, guard)
613
+ end
614
+
615
+ # Returns the type that a initialization hash used for creating instances of this type must conform to.
616
+ #
617
+ # @return [PStructType] the initialization hash type
618
+ # @api public
619
+ def i12n_type
620
+ @i12n_type ||= create_i12n_type
621
+ end
622
+
623
+ # Creates the type that a initialization hash used for creating instances of this type must conform to.
624
+ #
625
+ # @return [PStructType] the initialization hash type
626
+ # @api private
627
+ def create_i12n_type
628
+ struct_elems = {}
629
+ attributes(true).values.each do |attr|
630
+ unless attr.kind == ATTRIBUTE_KIND_CONSTANT || attr.kind == ATTRIBUTE_KIND_DERIVED
631
+ if attr.value?
632
+ struct_elems[TypeFactory.optional(attr.name)] = attr.type
633
+ else
634
+ struct_elems[attr.name] = attr.type
635
+ end
636
+ end
637
+ end
638
+ TypeFactory.struct(struct_elems)
639
+ end
640
+
641
+ # The i12n_hash is primarily intended for serialization and string representation purposes. It creates a hash
642
+ # suitable for passing to {PObjectType#new(i12n_hash)}
643
+ #
644
+ # @return [Hash{String=>Object}] the features hash
645
+ # @api public
646
+ def i12n_hash(include_name = true)
647
+ result = {}
648
+ result[KEY_NAME] = @name if include_name && !@name.nil?
649
+ result[KEY_PARENT] = @parent unless @parent.nil?
650
+ result[KEY_ATTRIBUTES] = compressed_members_hash(@attributes) unless @attributes.empty?
651
+ result[KEY_FUNCTIONS] = compressed_members_hash(@functions) unless @functions.empty?
652
+ result[KEY_EQUALITY] = @equality unless @equality.nil?
653
+ result[KEY_CHECKS] = @checks unless @checks.nil?
654
+ result[KEY_ANNOTATIONS] = @annotations unless @annotations.nil?
655
+ result
656
+ end
657
+
658
+ def eql?(o)
659
+ self.class == o.class &&
660
+ @name == o.name &&
661
+ @parent == o.parent &&
662
+ @attributes == o.attributes &&
663
+ @functions == o.functions &&
664
+ @equality == o.equality &&
665
+ @checks == o.checks
666
+ end
667
+
668
+ def hash
669
+ @name.nil? ? [@parent, @attributes, @functions].hash : @name.hash
670
+ end
671
+
672
+ def kind_of_callable?(optional=true, guard = nil)
673
+ @parent.nil? ? false : @parent.kind_of_callable?(optional, guard)
674
+ end
675
+
676
+ def instance?(o, guard = nil)
677
+ assignable?(TypeCalculator.infer(o), guard)
678
+ end
679
+
680
+ def iterable?(guard = nil)
681
+ @parent.nil? ? false : @parent.iterable?(guard)
682
+ end
683
+
684
+ def iterable_type(guard = nil)
685
+ @parent.nil? ? false : @parent.iterable_type(guard)
686
+ end
687
+
688
+ # Returns the members (attributes and functions) of this `Object` type. If _include_parent_ is `true`, then all
689
+ # inherited members will be included in the returned `Hash`.
690
+ #
691
+ # @param include_parent [Boolean] `true` if inherited members should be included
692
+ # @return [Hash{String=>PAnnotatedMember}] a hash with the members
693
+ # @api public
694
+ def members(include_parent = false)
695
+ get_members(include_parent, :both)
696
+ end
697
+
698
+ # Returns the attributes of this `Object` type. If _include_parent_ is `true`, then all
699
+ # inherited attributes will be included in the returned `Hash`.
700
+ #
701
+ # @param include_parent [Boolean] `true` if inherited attributes should be included
702
+ # @return [Hash{String=>PAttribute}] a hash with the attributes
703
+ # @api public
704
+ def attributes(include_parent = false)
705
+ get_members(include_parent, :attributes)
706
+ end
707
+
708
+ # Returns the attributes that participate in equality comparison. Inherited equality attributes
709
+ # are included.
710
+ # @return [Hash{String=>PAttribute}] a hash of attributes
711
+ # @api public
712
+ def equality_attributes
713
+ all = {}
714
+ collect_equality_attributes(all)
715
+ all
716
+ end
717
+
718
+ # @return [Boolean] `true` if this type is included when comparing instances
719
+ # @api public
720
+ def equality_include_type?
721
+ @equality_include_type
722
+ end
723
+
724
+ # Returns the functions of this `Object` type. If _include_parent_ is `true`, then all
725
+ # inherited functions will be included in the returned `Hash`.
726
+ #
727
+ # @param include_parent [Boolean] `true` if inherited functions should be included
728
+ # @return [Hash{String=>PFunction}] a hash with the functions
729
+ # @api public
730
+ def functions(include_parent = false)
731
+ get_members(include_parent, :functions)
732
+ end
733
+
734
+ DEFAULT = PObjectType.new(EMPTY_HASH)
735
+ # Assert that this type does not inherit from itself
736
+ # @api private
737
+ def check_self_recursion(originator)
738
+ unless @parent.nil?
739
+ raise Puppet::Error, "The Object type '#{originator.label}' inherits from itself" if @parent.equal?(originator)
740
+ @parent.check_self_recursion(originator)
741
+ end
742
+ end
743
+
744
+ # Returns the expanded string the form of the alias, e.g. <alias name> = <resolved type>
745
+ #
746
+ # @return [String] the expanded form of this alias
747
+ # @api public
748
+ def to_s
749
+ TypeFormatter.singleton.alias_expanded_string(self)
750
+ end
751
+
752
+ # @api private
753
+ def label
754
+ @name || '<anonymous object type>'
755
+ end
756
+
757
+ # @api private
758
+ def resolved_parent
759
+ parent = @parent
760
+ while parent.is_a?(PTypeAliasType)
761
+ parent = parent.resolved_type
762
+ end
763
+ parent
764
+ end
765
+
766
+ protected
767
+
768
+ # An Object type is only assignable from another Object type. The other type
769
+ # or one of its parents must be equal to this type.
770
+ def _assignable?(o, guard)
771
+ if self == o
772
+ true
773
+ else
774
+ if o.is_a?(PObjectType)
775
+ op = o.parent
776
+ op.nil? ? false : assignable?(op, guard)
777
+ else
778
+ false
779
+ end
780
+ end
781
+ end
782
+
783
+ def get_members(include_parent, member_type)
784
+ all = {}
785
+ collect_members(all, include_parent, member_type)
786
+ all
787
+ end
788
+
789
+ def collect_members(collector, include_parent, member_type)
790
+ if include_parent
791
+ parent = resolved_parent
792
+ parent.collect_members(collector, include_parent, member_type) if parent.is_a?(PObjectType)
793
+ end
794
+ collector.merge!(@attributes) unless member_type == :functions
795
+ collector.merge!(@functions) unless member_type == :attributes
796
+ nil
797
+ end
798
+
799
+ def collect_equality_attributes(collector)
800
+ parent = resolved_parent
801
+ parent.collect_equality_attributes(collector) if parent.is_a?(PObjectType)
802
+ if @equality.nil?
803
+ # All attributes except constants participate
804
+ collector.merge!(@attributes.reject { |_, attr| attr.kind == ATTRIBUTE_KIND_CONSTANT })
805
+ else
806
+ collector.merge!(Hash[@equality.map { |attr_name| [attr_name, @attributes[attr_name]] }])
807
+ end
808
+ nil
809
+ end
810
+
811
+ private
812
+
813
+ def compressed_members_hash(features)
814
+ Hash[features.values.map do |feature|
815
+ fh = feature.i12n_hash
816
+ if fh.size == 1
817
+ type = fh[KEY_TYPE]
818
+ fh = type unless type.nil?
819
+ end
820
+ [feature.name, fh]
821
+ end]
822
+ end
823
+
824
+ # @return [PObjectType] the topmost parent who's #equality_attributes include the given _attr_
825
+ def find_equality_definer_of(attr)
826
+ type = self
827
+ while !type.nil? do
828
+ p = type.parent
829
+ return type if p.nil?
830
+ return type unless p.equality_attributes.include?(attr.name)
831
+ type = p
832
+ end
833
+ nil
834
+ end
835
+
836
+ def guarded_recursion(guard, dflt)
837
+ if @self_recursion
838
+ guard ||= RecursionGuard.new
839
+ (guard.add_this(self) & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt
840
+ else
841
+ yield(guard)
842
+ end
843
+ end
844
+ end
845
+ end
846
+ end