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.
- data/CONTRIBUTING.md +5 -5
- data/Gemfile +2 -2
- data/LICENSE +2 -2
- data/README.md +5 -0
- data/ext/project_data.yaml +2 -0
- data/lib/hiera_puppet.rb +6 -14
- data/lib/puppet/application/agent.rb +2 -3
- data/lib/puppet/data_providers/hiera_config.rb +2 -4
- data/lib/puppet/data_providers/hiera_interpolate.rb +12 -154
- data/lib/puppet/data_providers/json_data_provider_factory.rb +0 -7
- data/lib/puppet/data_providers/yaml_data_provider_factory.rb +2 -8
- data/lib/puppet/defaults.rb +70 -7
- data/lib/puppet/functions.rb +69 -0
- data/lib/puppet/functions/dig.rb +39 -0
- data/lib/puppet/functions/lest.rb +53 -0
- data/lib/puppet/functions/lookup.rb +40 -27
- data/lib/puppet/functions/new.rb +502 -0
- data/lib/puppet/functions/regsubst.rb +11 -10
- data/lib/puppet/functions/then.rb +74 -0
- data/lib/puppet/functions/type.rb +4 -4
- data/lib/puppet/functions/with.rb +1 -1
- data/lib/puppet/indirector/catalog/compiler.rb +2 -0
- data/lib/puppet/indirector/resource_type/parser.rb +5 -0
- data/lib/puppet/indirector/rest.rb +5 -1
- data/lib/puppet/loaders.rb +2 -0
- data/lib/puppet/metatype/manager.rb +19 -2
- data/lib/puppet/module_tool/applications/application.rb +1 -1
- data/lib/puppet/module_tool/skeleton/templates/generator/Gemfile +6 -2
- data/lib/puppet/module_tool/skeleton/templates/generator/Rakefile +19 -4
- data/lib/puppet/module_tool/skeleton/templates/generator/{tests → examples}/init.pp.erb +1 -1
- data/lib/puppet/module_tool/skeleton/templates/generator/spec/classes/init_spec.rb.erb +0 -1
- data/lib/puppet/network/http/api/master/v3/environment.rb +6 -2
- data/lib/puppet/parser/ast/pops_bridge.rb +20 -3
- data/lib/puppet/parser/compiler/catalog_validator/relationship_validator.rb +24 -2
- data/lib/puppet/parser/e4_parser_adapter.rb +13 -12
- data/lib/puppet/parser/environment_compiler.rb +2 -2
- data/lib/puppet/parser/resource.rb +14 -5
- data/lib/puppet/parser/scope.rb +18 -15
- data/lib/puppet/plugins/data_providers/data_provider.rb +19 -8
- data/lib/puppet/pops.rb +6 -0
- data/lib/puppet/pops/adapters.rb +5 -1
- data/lib/puppet/pops/evaluator/access_operator.rb +52 -14
- data/lib/puppet/pops/evaluator/compare_operator.rb +34 -4
- data/lib/puppet/pops/evaluator/evaluator_impl.rb +75 -22
- data/lib/puppet/pops/evaluator/literal_evaluator.rb +7 -6
- data/lib/puppet/pops/evaluator/runtime3_converter.rb +13 -1
- data/lib/puppet/pops/evaluator/runtime3_support.rb +14 -4
- data/lib/puppet/pops/functions/dispatcher.rb +1 -1
- data/lib/puppet/pops/issues.rb +18 -2
- data/lib/puppet/pops/loader/base_loader.rb +48 -7
- data/lib/puppet/pops/loader/dependency_loader.rb +27 -2
- data/lib/puppet/pops/loader/loader.rb +12 -0
- data/lib/puppet/pops/loader/predefined_loader.rb +29 -0
- data/lib/puppet/pops/loader/runtime3_type_loader.rb +57 -0
- data/lib/puppet/pops/loader/static_loader.rb +92 -5
- data/lib/puppet/pops/loader/type_definition_instantiator.rb +25 -3
- data/lib/puppet/pops/loaders.rb +84 -14
- data/lib/puppet/pops/lookup/explainer.rb +38 -1
- data/lib/puppet/pops/lookup/interpolation.rb +115 -0
- data/lib/puppet/pops/lookup/sub_lookup.rb +86 -0
- data/lib/puppet/pops/model/ast_transformer.rb +8 -1
- data/lib/puppet/pops/model/factory.rb +31 -8
- data/lib/puppet/pops/model/model.rb +8 -0
- data/lib/puppet/pops/model/model_label_provider.rb +1 -0
- data/lib/puppet/pops/model/model_meta.rb +7 -1
- data/lib/puppet/pops/model/model_tree_dumper.rb +4 -0
- data/lib/puppet/pops/parser/egrammar.ra +24 -7
- data/lib/puppet/pops/parser/eparser.rb +863 -798
- data/lib/puppet/pops/parser/evaluating_parser.rb +4 -0
- data/lib/puppet/pops/parser/locator.rb +8 -4
- data/lib/puppet/pops/pcore.rb +30 -0
- data/lib/puppet/pops/types/class_loader.rb +2 -4
- data/lib/puppet/pops/types/implementation_registry.rb +146 -0
- data/lib/puppet/pops/types/iterable.rb +4 -4
- data/lib/puppet/pops/types/p_object_type.rb +846 -0
- data/lib/puppet/pops/types/p_runtime_type.rb +102 -0
- data/lib/puppet/pops/types/p_sem_ver_range_type.rb +164 -0
- data/lib/puppet/pops/types/p_sem_ver_type.rb +113 -0
- data/lib/puppet/pops/types/puppet_object.rb +21 -0
- data/lib/puppet/pops/types/ruby_generator.rb +258 -0
- data/lib/puppet/pops/types/string_converter.rb +922 -0
- data/lib/puppet/pops/types/type_calculator.rb +29 -5
- data/lib/puppet/pops/types/type_conversion_error.rb +15 -0
- data/lib/puppet/pops/types/type_factory.rb +49 -16
- data/lib/puppet/pops/types/type_formatter.rb +335 -112
- data/lib/puppet/pops/types/type_mismatch_describer.rb +110 -29
- data/lib/puppet/pops/types/type_parser.rb +205 -197
- data/lib/puppet/pops/types/types.rb +481 -103
- data/lib/puppet/pops/validation.rb +1 -1
- data/lib/puppet/pops/validation/checker4_0.rb +66 -4
- data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
- data/lib/puppet/pops/visitor.rb +3 -1
- data/lib/puppet/property.rb +1 -1
- data/lib/puppet/provider/augeas/augeas.rb +1 -1
- data/lib/puppet/provider/package/pip.rb +64 -20
- data/lib/puppet/provider/package/rpm.rb +112 -0
- data/lib/puppet/provider/package/yum.rb +7 -68
- data/lib/puppet/provider/service/daemontools.rb +3 -3
- data/lib/puppet/provider/service/init.rb +4 -2
- data/lib/puppet/provider/service/runit.rb +3 -3
- data/lib/puppet/provider/service/smf.rb +6 -3
- data/lib/puppet/provider/service/systemd.rb +59 -73
- data/lib/puppet/reference/providers.rb +1 -2
- data/lib/puppet/resource.rb +54 -37
- data/lib/puppet/resource/catalog.rb +31 -29
- data/lib/puppet/resource/type_collection.rb +23 -8
- data/lib/puppet/settings.rb +4 -2
- data/lib/puppet/settings/base_setting.rb +9 -3
- data/lib/puppet/settings/symbolic_enum_setting.rb +17 -0
- data/lib/puppet/test/test_helper.rb +0 -1
- data/lib/puppet/type.rb +9 -3
- data/lib/puppet/type/exec.rb +17 -17
- data/lib/puppet/type/file.rb +12 -0
- data/lib/puppet/type/file/content.rb +6 -6
- data/lib/puppet/type/file/ensure.rb +4 -4
- data/lib/puppet/type/file/source.rb +4 -4
- data/lib/puppet/type/file/target.rb +2 -2
- data/lib/puppet/type/mount.rb +18 -1
- data/lib/puppet/type/package.rb +3 -3
- data/lib/puppet/type/schedule.rb +4 -4
- data/lib/puppet/type/service.rb +15 -0
- data/lib/puppet/type/sshkey.rb +5 -3
- data/lib/puppet/type/tidy.rb +3 -3
- data/lib/puppet/type/zone.rb +5 -5
- data/lib/puppet/util/feature.rb +1 -1
- data/lib/puppet/util/monkey_patches.rb +8 -0
- data/lib/puppet/util/network_device/cisco/device.rb +16 -6
- data/lib/puppet/util/network_device/cisco/interface.rb +5 -6
- data/lib/puppet/util/plist.rb +3 -3
- data/lib/puppet/version.rb +1 -1
- data/spec/fixtures/unit/application/environments/production/data/common.yaml +13 -0
- data/spec/fixtures/unit/data_providers/environments/production/modules/abc/lib/puppet/functions/abc/data.rb +2 -1
- data/spec/fixtures/unit/data_providers/environments/production/modules/abc/manifests/init.pp +2 -1
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/data/empty_key.json +1 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/hiera.yaml +5 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/manifests/init.pp +2 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_json/metadata.json +9 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/data/empty_key.yaml +1 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/hiera.yaml +5 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/manifests/init.pp +2 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_key_yaml/metadata.json +9 -0
- data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/data/empty.yaml +1 -0
- data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/lib/puppet/type/usee_type.rb +5 -0
- data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/manifests/init.pp +6 -0
- data/spec/fixtures/unit/provider/service/smf/svcs.out +4 -3
- data/spec/integration/module_tool/tar/mini_spec.rb +27 -27
- data/spec/integration/parser/catalog_spec.rb +14 -2
- data/spec/integration/parser/compiler_spec.rb +94 -3
- data/spec/integration/parser/resource_expressions_spec.rb +1 -1
- data/spec/integration/resource/type_collection_spec.rb +8 -0
- data/spec/lib/puppet_spec/compiler.rb +11 -4
- data/spec/shared_contexts/types_setup.rb +4 -0
- data/spec/unit/application/lookup_spec.rb +91 -9
- data/spec/unit/appmgmt_spec.rb +44 -35
- data/spec/unit/capability_spec.rb +33 -53
- data/spec/unit/data_providers/function_data_provider_spec.rb +19 -1
- data/spec/unit/data_providers/hiera_data_provider_spec.rb +1 -1
- data/spec/unit/defaults_spec.rb +18 -0
- data/spec/unit/functions/assert_type_spec.rb +1 -1
- data/spec/unit/functions/dig_spec.rb +58 -0
- data/spec/unit/functions/lest_spec.rb +34 -0
- data/spec/unit/functions/lookup_spec.rb +108 -2
- data/spec/unit/functions/new_spec.rb +543 -0
- data/spec/unit/functions/regsubst_spec.rb +8 -0
- data/spec/unit/functions/then_spec.rb +40 -0
- data/spec/unit/functions4_spec.rb +78 -10
- data/spec/unit/hiera_puppet_spec.rb +49 -8
- data/spec/unit/indirector/resource_type/parser_spec.rb +5 -0
- data/spec/unit/indirector/rest_spec.rb +12 -0
- data/spec/unit/network/http/api/master/v3/environment_spec.rb +60 -0
- data/spec/unit/node/environment_spec.rb +10 -0
- data/spec/unit/parser/compiler_spec.rb +20 -1
- data/spec/unit/parser/functions/create_resources_spec.rb +2 -2
- data/spec/unit/parser/functions/shared.rb +1 -1
- data/spec/unit/parser/resource_spec.rb +8 -1
- data/spec/unit/parser/scope_spec.rb +45 -0
- data/spec/unit/pops/evaluator/access_ops_spec.rb +14 -0
- data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +13 -5
- data/spec/unit/pops/loaders/static_loader_spec.rb +92 -1
- data/spec/unit/{data_providers/hiera_interpolation_spec.rb → pops/lookup/interpolation_spec.rb} +7 -5
- data/spec/unit/pops/parser/lexer2_spec.rb +2 -9
- data/spec/unit/pops/parser/parse_application_spec.rb +3 -8
- data/spec/unit/pops/parser/parse_basic_expressions_spec.rb +19 -0
- data/spec/unit/pops/parser/parse_capabilities_spec.rb +3 -10
- data/spec/unit/pops/parser/parse_site_spec.rb +19 -10
- data/spec/unit/pops/parser/parser_rspec_helper.rb +0 -4
- data/spec/unit/pops/types/enumeration_spec.rb +13 -12
- data/spec/unit/pops/types/iterable_spec.rb +2 -2
- data/spec/unit/pops/types/p_object_type_spec.rb +1060 -0
- data/spec/unit/pops/types/p_sem_ver_type_spec.rb +285 -0
- data/spec/unit/pops/types/recursion_guard_spec.rb +19 -17
- data/spec/unit/pops/types/ruby_generator_spec.rb +261 -0
- data/spec/unit/pops/types/string_converter_spec.rb +904 -0
- data/spec/unit/pops/types/type_calculator_spec.rb +430 -406
- data/spec/unit/pops/types/type_factory_spec.rb +119 -104
- data/spec/unit/pops/types/type_formatter_spec.rb +73 -6
- data/spec/unit/pops/types/type_mismatch_describer_spec.rb +2 -2
- data/spec/unit/pops/types/type_parser_spec.rb +54 -15
- data/spec/unit/pops/types/types_spec.rb +113 -8
- data/spec/unit/pops/validator/validator_spec.rb +84 -10
- data/spec/unit/provider/package/pip3_spec.rb +9 -270
- data/spec/unit/provider/package/pip_spec.rb +85 -30
- data/spec/unit/provider/package/rpm_spec.rb +160 -3
- data/spec/unit/provider/package/yum_spec.rb +23 -134
- data/spec/unit/provider/service/smf_spec.rb +14 -2
- data/spec/unit/provider/service/systemd_spec.rb +33 -41
- data/spec/unit/resource/capability_finder_spec.rb +10 -2
- data/spec/unit/settings/file_setting_spec.rb +6 -0
- data/spec/unit/transaction/additional_resource_generator_spec.rb +80 -65
- data/spec/unit/type/mount_spec.rb +51 -10
- data/spec/unit/type/service_spec.rb +16 -0
- data/spec/unit/type_spec.rb +14 -0
- data/spec/unit/util/feature_spec.rb +1 -1
- data/spec/unit/util/monkey_patches_spec.rb +60 -0
- data/spec/unit/util/network_device/cisco/device_spec.rb +1 -1
- metadata +63 -11
- data/lib/puppet/pops/types/types_meta.rb +0 -0
- data/spec/integration/provider/package_spec.rb +0 -35
@@ -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
|
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 <
|
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(
|
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 <
|
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(
|
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
|
-
|
67
|
-
|
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
|
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
|