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
@@ -0,0 +1,922 @@
|
|
1
|
+
module Puppet::Pops
|
2
|
+
module Types
|
3
|
+
|
4
|
+
# Converts Puppet runtime objects to String under the control of a Format.
|
5
|
+
# Use from Puppet Language is via the function `new`.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
class StringConverter
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
class FormatError < ArgumentError
|
13
|
+
def initialize(type_string, actual, expected)
|
14
|
+
super "Illegal format '#{actual}' specified for value of #{type_string} type - expected one of the characters '#{expected}'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Indentation
|
19
|
+
attr_reader :level
|
20
|
+
attr_reader :first
|
21
|
+
attr_reader :is_indenting
|
22
|
+
alias :first? :first
|
23
|
+
alias :is_indenting? :is_indenting
|
24
|
+
|
25
|
+
def initialize(level, first, is_indenting)
|
26
|
+
@level = level
|
27
|
+
@first = first
|
28
|
+
@is_indenting = is_indenting
|
29
|
+
end
|
30
|
+
|
31
|
+
def subsequent
|
32
|
+
first? ? self.class.new(level, false, @is_indenting) : self
|
33
|
+
end
|
34
|
+
|
35
|
+
def indenting(indenting_flag)
|
36
|
+
self.class.new(level, first?, indenting_flag)
|
37
|
+
end
|
38
|
+
|
39
|
+
def increase(indenting_flag = false)
|
40
|
+
self.class.new(level + 1, true, indenting_flag)
|
41
|
+
end
|
42
|
+
|
43
|
+
def breaks?
|
44
|
+
is_indenting? && level > 0 && ! first?
|
45
|
+
end
|
46
|
+
|
47
|
+
def padding
|
48
|
+
return ' ' * 2 * level
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Format represents one format specification that is textually represented by %<flags><width>.<precision><format>
|
53
|
+
# Format parses and makes the individual parts available when an instance is created.
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
class Format
|
58
|
+
# Boolean, alternate form (varies in meaning)
|
59
|
+
attr_reader :alt
|
60
|
+
alias :alt? :alt
|
61
|
+
|
62
|
+
# Nil or Integer with width of field > 0
|
63
|
+
attr_reader :width
|
64
|
+
# Nil or Integer precisions
|
65
|
+
attr_reader :prec
|
66
|
+
# One char symbol denoting the format
|
67
|
+
attr_reader :format
|
68
|
+
# Symbol, :space, :plus, :ignore
|
69
|
+
attr_reader :plus
|
70
|
+
# Boolean, left adjust in given width or not
|
71
|
+
attr_reader :left
|
72
|
+
# Boolean left_pad with zero instead of space
|
73
|
+
attr_reader :zero_pad
|
74
|
+
# Delimiters for containers, a "left" char representing the pair <[{(
|
75
|
+
attr_reader :delimiters
|
76
|
+
|
77
|
+
# Map of type to format for elements contained in an object this format applies to
|
78
|
+
attr_accessor :container_string_formats
|
79
|
+
|
80
|
+
# Separator string inserted between elements in a container
|
81
|
+
attr_accessor :separator
|
82
|
+
|
83
|
+
# Separator string inserted between sub elements in a container
|
84
|
+
attr_accessor :separator2
|
85
|
+
|
86
|
+
attr_reader :orig_fmt
|
87
|
+
|
88
|
+
FMT_PATTERN = /^%([\s\+\-#0\[\{<\(\|]*)([1-9][0-9]*)?(?:\.([0-9]+))?([a-zA-Z])/
|
89
|
+
DELIMITERS = [ '[', '{', '(', '<', '|',]
|
90
|
+
DELIMITER_MAP = {
|
91
|
+
'[' => ['[', ']'],
|
92
|
+
'{' => ['{', '}'],
|
93
|
+
'(' => ['(', ')'],
|
94
|
+
'<' => ['<', '>'],
|
95
|
+
'|' => ['|', '|'],
|
96
|
+
:space => ['', '']
|
97
|
+
}.freeze
|
98
|
+
|
99
|
+
def initialize(fmt)
|
100
|
+
@orig_fmt = fmt
|
101
|
+
match = FMT_PATTERN.match(fmt)
|
102
|
+
unless match
|
103
|
+
raise ArgumentError, "The format '#{fmt}' is not a valid format on the form '%<flags><width>.<prec><format>'"
|
104
|
+
end
|
105
|
+
|
106
|
+
@format = match[4]
|
107
|
+
unless @format.is_a?(String) && @format.length == 1
|
108
|
+
raise ArgumentError, "The format must be a one letter format specifier, got '#{@format}'"
|
109
|
+
end
|
110
|
+
@format = @format.to_sym
|
111
|
+
flags = match[1].split('') || []
|
112
|
+
unless flags.uniq.size == flags.size
|
113
|
+
raise ArgumentError, "The same flag can only be used once, got '#{fmt}'"
|
114
|
+
end
|
115
|
+
@left = flags.include?('-')
|
116
|
+
@alt = flags.include?('#')
|
117
|
+
@plus = (flags.include?(' ') ? :space : (flags.include?('+') ? :plus : :ignore))
|
118
|
+
@zero_pad = flags.include?('0')
|
119
|
+
|
120
|
+
@delimiters = nil
|
121
|
+
DELIMITERS.each do |d|
|
122
|
+
next unless flags.include?(d)
|
123
|
+
if !@delimiters.nil?
|
124
|
+
raise ArgumentError, "Only one of the delimiters [ { ( < | can be given in the format flags, got '#{fmt}'"
|
125
|
+
end
|
126
|
+
@delimiters = d
|
127
|
+
end
|
128
|
+
|
129
|
+
@width = match[2] ? match[2].to_i : nil
|
130
|
+
@prec = match[3] ? match[3].to_i : nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# Merges one format into this and returns a new `Format`. The `other` format overrides this.
|
134
|
+
# @param [Format] other
|
135
|
+
# @returns [Format] a merged format
|
136
|
+
#
|
137
|
+
def merge(other)
|
138
|
+
result = Format.new(other.orig_fmt)
|
139
|
+
result.separator = other.separator || separator
|
140
|
+
result.separator2 = other.separator2 || separator2
|
141
|
+
result.container_string_formats = Format.merge_string_formats(container_string_formats, other.container_string_formats)
|
142
|
+
result
|
143
|
+
end
|
144
|
+
|
145
|
+
# Merges two formats where the `higher` format overrides the `lower`. Produces a new `Format`
|
146
|
+
# @param [Format] lower
|
147
|
+
# @param [Format] higher
|
148
|
+
# @returns [Format] the merged result
|
149
|
+
#
|
150
|
+
def self.merge(lower, higher)
|
151
|
+
unless lower && higher
|
152
|
+
return lower || higher
|
153
|
+
end
|
154
|
+
lower.merge(higher)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Merges a type => format association and returns a new merged and sorted association.
|
158
|
+
# @param [Format] lower
|
159
|
+
# @param [Format] higher
|
160
|
+
# @returns [Hash] the merged type => format result
|
161
|
+
#
|
162
|
+
def self.merge_string_formats(lower, higher)
|
163
|
+
unless lower && higher
|
164
|
+
return lower || higher
|
165
|
+
end
|
166
|
+
merged = (lower.keys + higher.keys).uniq.map do |k|
|
167
|
+
[k, merge(lower[k], higher[k])]
|
168
|
+
end
|
169
|
+
sort_formats(merged)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Sorts format based on generality of types - most specific types before general
|
173
|
+
#
|
174
|
+
def self.sort_formats(format_map)
|
175
|
+
format_map = format_map.sort do |(a,_),(b,_)|
|
176
|
+
ab = b.assignable?(a)
|
177
|
+
ba = a.assignable?(b)
|
178
|
+
if a == b
|
179
|
+
0
|
180
|
+
elsif ab && !ba
|
181
|
+
-1
|
182
|
+
elsif !ab && ba
|
183
|
+
1
|
184
|
+
else
|
185
|
+
# arbitrary order if disjunct (based on name of type)
|
186
|
+
rank_a = type_rank(a)
|
187
|
+
rank_b = type_rank(b)
|
188
|
+
if rank_a == 0 || rank_b == 0
|
189
|
+
a.to_s <=> b.to_s
|
190
|
+
else
|
191
|
+
rank_a <=> rank_b
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
Hash[format_map]
|
196
|
+
end
|
197
|
+
|
198
|
+
# Ranks type on specificity where it matters
|
199
|
+
# lower number means more specific
|
200
|
+
def self.type_rank(t)
|
201
|
+
case t
|
202
|
+
when PStructType
|
203
|
+
1
|
204
|
+
when PHashType
|
205
|
+
2
|
206
|
+
when PTupleType
|
207
|
+
3
|
208
|
+
when PArrayType
|
209
|
+
4
|
210
|
+
when PPatternType
|
211
|
+
10
|
212
|
+
when PEnumType
|
213
|
+
11
|
214
|
+
when PStringType
|
215
|
+
12
|
216
|
+
else
|
217
|
+
0
|
218
|
+
end
|
219
|
+
end
|
220
|
+
# Returns an array with a delimiter pair derived from the format.
|
221
|
+
# If format does not contain a delimiter specification the given default is returned
|
222
|
+
#
|
223
|
+
# @param [Array<String>] the default delimiters
|
224
|
+
# @returns [Array<String>] a tuple with left, right delimiters
|
225
|
+
#
|
226
|
+
def delimiter_pair(default = StringConverter::DEFAULT_ARRAY_DELIMITERS)
|
227
|
+
DELIMITER_MAP[ @delimiters || @plus ] || default
|
228
|
+
end
|
229
|
+
|
230
|
+
def to_s
|
231
|
+
"%#{@flags}#{@width}.#{@prec}#{@format}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# @api public
|
236
|
+
def self.convert(value, string_formats = :default)
|
237
|
+
singleton.convert(value, string_formats)
|
238
|
+
end
|
239
|
+
|
240
|
+
# @return [TypeConverter] the singleton instance
|
241
|
+
#
|
242
|
+
# @api public
|
243
|
+
def self.singleton
|
244
|
+
@tconv_instance ||= new
|
245
|
+
end
|
246
|
+
|
247
|
+
# @api private
|
248
|
+
#
|
249
|
+
def initialize
|
250
|
+
@@string_visitor ||= Visitor.new(self, "string", 3, 3)
|
251
|
+
end
|
252
|
+
|
253
|
+
DEFAULT_INDENTATION = Indentation.new(0, true, false).freeze
|
254
|
+
|
255
|
+
# format used by default for values in a container
|
256
|
+
# (basically strings are quoted since they may contain a ','))
|
257
|
+
#
|
258
|
+
DEFAULT_CONTAINER_FORMATS = {
|
259
|
+
PAnyType::DEFAULT => Format.new('%p').freeze, # quoted string (Ruby inspect)
|
260
|
+
}.freeze
|
261
|
+
|
262
|
+
DEFAULT_ARRAY_FORMAT = Format.new('%a')
|
263
|
+
DEFAULT_ARRAY_FORMAT.separator = ','.freeze
|
264
|
+
DEFAULT_ARRAY_FORMAT.separator2 = ','.freeze
|
265
|
+
DEFAULT_ARRAY_FORMAT.container_string_formats = DEFAULT_CONTAINER_FORMATS
|
266
|
+
DEFAULT_ARRAY_FORMAT.freeze
|
267
|
+
|
268
|
+
DEFAULT_HASH_FORMAT = Format.new('%h')
|
269
|
+
DEFAULT_HASH_FORMAT.separator = ','.freeze
|
270
|
+
DEFAULT_HASH_FORMAT.separator2 = ' => '.freeze
|
271
|
+
DEFAULT_HASH_FORMAT.container_string_formats = DEFAULT_CONTAINER_FORMATS
|
272
|
+
DEFAULT_HASH_FORMAT.freeze
|
273
|
+
|
274
|
+
DEFAULT_HASH_DELIMITERS = ['{', '}'].freeze
|
275
|
+
DEFAULT_ARRAY_DELIMITERS = ['[', ']'].freeze
|
276
|
+
|
277
|
+
DEFAULT_STRING_FORMATS = {
|
278
|
+
PFloatType::DEFAULT => Format.new('%f').freeze, # float
|
279
|
+
PNumericType::DEFAULT => Format.new('%d').freeze, # decimal number
|
280
|
+
PArrayType::DEFAULT => DEFAULT_ARRAY_FORMAT.freeze,
|
281
|
+
PHashType::DEFAULT => DEFAULT_HASH_FORMAT.freeze,
|
282
|
+
PAnyType::DEFAULT => Format.new('%s').freeze, # unquoted string
|
283
|
+
}.freeze
|
284
|
+
|
285
|
+
|
286
|
+
# Converts the given value to a String, under the direction of formatting rules per type.
|
287
|
+
#
|
288
|
+
# When converting to string it is possible to use a set of built in conversion rules.
|
289
|
+
#
|
290
|
+
# A format is specified on the form:
|
291
|
+
#
|
292
|
+
# ´´´
|
293
|
+
# %[Flags][Width][.Precision]Format
|
294
|
+
# ´´´
|
295
|
+
#
|
296
|
+
# `Width` is the number of characters into which the value should be fitted. This allocated space is
|
297
|
+
# padded if value is shorter. By default it is space padded, and the flag 0 will cause padding with 0
|
298
|
+
# for numerical formats.
|
299
|
+
#
|
300
|
+
# `Precision` is the number of fractional digits to show for floating point, and the maximum characters
|
301
|
+
# included in a string format.
|
302
|
+
#
|
303
|
+
# Note that all data type supports the formats `s` and `p` with the meaning "default to-string" and
|
304
|
+
# "default-programmatic to-string".
|
305
|
+
#
|
306
|
+
# ### Integer
|
307
|
+
#
|
308
|
+
# | Format | Integer Formats
|
309
|
+
# | ------ | ---------------
|
310
|
+
# | d | Decimal, negative values produces leading '-'
|
311
|
+
# | x X | Hexadecimal in lower or upper case. Uses ..f/..F for negative values unless # is also used
|
312
|
+
# | o | Octal. Uses ..0 for negative values unless # is also used
|
313
|
+
# | b B | Binary with prefix 'b' or 'B'. Uses ..1/..1 for negative values unless # is also used
|
314
|
+
# | c | numeric value representing a Unicode value, result is a one unicode character string, quoted if alternative flag # is used
|
315
|
+
# | s | same as d, or d in quotes if alternative flag # is used
|
316
|
+
# | p | same as d
|
317
|
+
# | eEfgGaA | converts integer to float and formats using the floating point rules
|
318
|
+
#
|
319
|
+
# Defaults to `d`
|
320
|
+
#
|
321
|
+
# ### Float
|
322
|
+
#
|
323
|
+
# | Format | Float formats
|
324
|
+
# | ------ | -------------
|
325
|
+
# | f | floating point in non exponential notation
|
326
|
+
# | e E | exponential notation with 'e' or 'E'
|
327
|
+
# | g G | conditional exponential with 'e' or 'E' if exponent < -4 or >= the precision
|
328
|
+
# | a A | hexadecimal exponential form, using 'x'/'X' as prefix and 'p'/'P' before exponent
|
329
|
+
# | s | converted to string using format p, then applying string formatting rule, alternate form # quotes result
|
330
|
+
# | p | f format with minimum significant number of fractional digits, prec has no effect
|
331
|
+
# | dxXobBc | converts float to integer and formats using the integer rules
|
332
|
+
#
|
333
|
+
# Defaults to `p`
|
334
|
+
#
|
335
|
+
# ### String
|
336
|
+
#
|
337
|
+
# | Format | String
|
338
|
+
# | ------ | ------
|
339
|
+
# | s | unquoted string, verbatim output of control chars
|
340
|
+
# | p | programmatic representation - strings are quoted, interior quotes and control chars are escaped
|
341
|
+
# | C | each :: name segment capitalized, quoted if alternative flag # is used
|
342
|
+
# | c | capitalized string, quoted if alternative flag # is used
|
343
|
+
# | d | downcased string, quoted if alternative flag # is used
|
344
|
+
# | u | upcased string, quoted if alternative flag # is used
|
345
|
+
# | t | trims leading and trailing whitespace from the string, quoted if alternative flag # is used
|
346
|
+
#
|
347
|
+
# Defaults to `s` at top level and `p` inside array or hash.
|
348
|
+
#
|
349
|
+
# ### Boolean
|
350
|
+
#
|
351
|
+
# | Format | Boolean Formats
|
352
|
+
# | ---- | -------------------
|
353
|
+
# | t T | 'true'/'false' or 'True'/'False' , first char if alternate form is used (i.e. 't'/'f' or 'T'/'F').
|
354
|
+
# | y Y | 'yes'/'no', 'Yes'/'No', 'y'/'n' or 'Y'/'N' if alternative flag # is used
|
355
|
+
# | dxXobB | numeric value 0/1 in accordance with the given format which must be valid integer format
|
356
|
+
# | eEfgGaA | numeric value 0.0/1.0 in accordance with the given float format and flags
|
357
|
+
# | s | 'true' / 'false'
|
358
|
+
# | p | 'true' / 'false'
|
359
|
+
#
|
360
|
+
# ### Regexp
|
361
|
+
#
|
362
|
+
# | Format | Regexp Formats (%/)
|
363
|
+
# | ---- | ------------------
|
364
|
+
# | s | / / delimiters, alternate flag replaces / delimiters with quotes
|
365
|
+
# | p | / / delimiters
|
366
|
+
#
|
367
|
+
# ### Undef
|
368
|
+
#
|
369
|
+
# | Format | Undef formats
|
370
|
+
# | ------ | -------------
|
371
|
+
# | s | empty string, or quoted empty string if alternative flag # is used
|
372
|
+
# | p | 'undef', or quoted '"undef"' if alternative flag # is used
|
373
|
+
# | n | 'nil', or 'null' if alternative flag # is used
|
374
|
+
# | dxXobB | 'NaN'
|
375
|
+
# | eEfgGaA | 'NaN'
|
376
|
+
# | v | 'n/a'
|
377
|
+
# | V | 'N/A'
|
378
|
+
# | u | 'undef', or 'undefined' if alternative # flag is used
|
379
|
+
#
|
380
|
+
# ### Default (value)
|
381
|
+
#
|
382
|
+
# | Format | Default formats
|
383
|
+
# | ------ | ---------------
|
384
|
+
# | d D | 'default' or 'Default', alternative form # causes value to be quoted
|
385
|
+
# | s | same as d
|
386
|
+
# | p | same as d
|
387
|
+
#
|
388
|
+
# ### Array & Tuple
|
389
|
+
#
|
390
|
+
# | Format | Array/Tuple Formats
|
391
|
+
# | ------ | -------------
|
392
|
+
# | a | formats with `[ ]` delimiters and `,`, alternate form `#` indents nested arrays/hashes
|
393
|
+
# | s | same as a
|
394
|
+
# | p | same as a
|
395
|
+
#
|
396
|
+
# See "Flags" `<[({\|` for formatting of delimiters, and "Additional parameters for containers; Array and Hash" for
|
397
|
+
# more information about options.
|
398
|
+
#
|
399
|
+
# The alternate form flag `#` will cause indentation of nested array or hash containers. If width is also set
|
400
|
+
# it is taken as the maximum allowed length of a sequence of elements (not including delimiters). If this max length
|
401
|
+
# is exceeded, each element will be indented.
|
402
|
+
#
|
403
|
+
# ### Hash & Struct
|
404
|
+
#
|
405
|
+
# | Format | Hash/Struct Formats
|
406
|
+
# | ------ | -------------
|
407
|
+
# | h | formats with `{ }` delimiters, `,` element separator and ` => ` inner element separator unless overridden by flags
|
408
|
+
# | s | same as h
|
409
|
+
# | p | same as h
|
410
|
+
# | a | converts the hash to an array of [k,v] tuples and formats it using array rule(s)
|
411
|
+
#
|
412
|
+
# See "Flags" `<[({\|` for formatting of delimiters, and "Additional parameters for containers; Array and Hash" for
|
413
|
+
# more information about options.
|
414
|
+
#
|
415
|
+
# The alternate form flag `#` will format each hash key/value entry indented on a separate line.
|
416
|
+
#
|
417
|
+
# ### Type
|
418
|
+
#
|
419
|
+
# | Format | Array/Tuple Formats
|
420
|
+
# | ------ | -------------
|
421
|
+
# | s | The same as p, quoted if alternative flag # is used
|
422
|
+
# | p | Outputs the type in string form as specified by the Puppet Language
|
423
|
+
#
|
424
|
+
# ### Flags
|
425
|
+
#
|
426
|
+
# | Flag | Effect
|
427
|
+
# | ------ | ------
|
428
|
+
# | (space) | space instead of + for numeric output (- is shown), for containers skips delimiters
|
429
|
+
# | # | alternate format; prefix 0x/0x, 0 (octal) and 0b/0B for binary, Floats force decimal '.'. For g/G keep trailing 0.
|
430
|
+
# | + | show sign +/- depending on value's sign, changes x,X, o,b, B format to not use 2's complement form
|
431
|
+
# | - | left justify the value in the given width
|
432
|
+
# | 0 | pad with 0 instead of space for widths larger than value
|
433
|
+
# | <[({\| | defines an enclosing pair <> [] () {} or \| \| when used with a container type
|
434
|
+
#
|
435
|
+
#
|
436
|
+
# ### Additional parameters for containers; Array and Hash
|
437
|
+
#
|
438
|
+
# For containers (Array and Hash), the format is specified by a hash where the following keys can be set:
|
439
|
+
# * `'format'` - the format specifier for the container itself
|
440
|
+
# * `'separator'` - the separator string to use between elements, should not contain padding space at the end
|
441
|
+
# * `'separator2'` - the separator string to use between association of hash entries key/value
|
442
|
+
# * `'string_formats'´ - a map of type to format for elements contained in the container
|
443
|
+
#
|
444
|
+
# Note that the top level format applies to Array and Hash objects contained/nested in an Array or a Hash.
|
445
|
+
#
|
446
|
+
# Given format mappings are merged with (default) formats and a format specified for a narrower type
|
447
|
+
# wins over a broader.
|
448
|
+
#
|
449
|
+
# @param mode [String, Symbol] :strict or :extended (or :default which is the same as :strict)
|
450
|
+
# @param string_formats [String, Hash] format tring, or a hash mapping type to a format string, and for Array and Hash types map to hash of details
|
451
|
+
#
|
452
|
+
def convert(value, string_formats = :default)
|
453
|
+
options = DEFAULT_STRING_FORMATS
|
454
|
+
|
455
|
+
if string_formats.is_a?(String)
|
456
|
+
# add the format given for the exact type
|
457
|
+
t = TypeCalculator.infer_set(value)
|
458
|
+
string_formats = { t => string_formats }
|
459
|
+
end
|
460
|
+
|
461
|
+
case string_formats
|
462
|
+
when :default
|
463
|
+
# do nothing, use default formats
|
464
|
+
|
465
|
+
when Hash
|
466
|
+
# Convert and validate user input
|
467
|
+
string_formats = validate_input(string_formats)
|
468
|
+
# Merge user given with defaults such that user options wins, merge is deep and format specific
|
469
|
+
options = Format.merge_string_formats(DEFAULT_STRING_FORMATS, string_formats)
|
470
|
+
else
|
471
|
+
raise ArgumentError, "string conversion expects a Default value or a Hash of type to format mappings, got a '#{string_formats.class}'"
|
472
|
+
end
|
473
|
+
|
474
|
+
_convert(TypeCalculator.infer_set(value), value, options, DEFAULT_INDENTATION)
|
475
|
+
end
|
476
|
+
|
477
|
+
# # A method only used for manual debugging as the default output of the formatting rules is
|
478
|
+
# # very hard to read otherwise.
|
479
|
+
# #
|
480
|
+
# # @api private
|
481
|
+
# def dump_string_formats(f, indent = 1)
|
482
|
+
# return f.to_s unless f.is_a?(Hash)
|
483
|
+
# "{#{f.map {|k,v| "#{k.to_s} => #{dump_string_formats(v,indent+1)}"}.join(",\n#{' '*indent} ")}}"
|
484
|
+
# end
|
485
|
+
|
486
|
+
def _convert(val_type, value, format_map, indentation)
|
487
|
+
@@string_visitor.visit_this_3(self, val_type, value, format_map, indentation)
|
488
|
+
end
|
489
|
+
private :_convert
|
490
|
+
|
491
|
+
def validate_input(fmt)
|
492
|
+
return nil if fmt.nil?
|
493
|
+
unless fmt.is_a?(Hash)
|
494
|
+
raise ArgumentError, "expected a hash with type to format mappings, got instance of '#{fmt.class}'"
|
495
|
+
end
|
496
|
+
fmt.reduce({}) do | result, entry|
|
497
|
+
key, value = entry
|
498
|
+
unless key.is_a?(Types::PAnyType)
|
499
|
+
raise ArgumentError, "top level keys in the format hash must be data types, got instance of '#{key.class}'"
|
500
|
+
end
|
501
|
+
if value.is_a?(Hash)
|
502
|
+
result[key] = validate_container_input(value)
|
503
|
+
else
|
504
|
+
result[key] = Format.new(value)
|
505
|
+
end
|
506
|
+
result
|
507
|
+
end
|
508
|
+
end
|
509
|
+
private :validate_input
|
510
|
+
|
511
|
+
FMT_KEYS = %w{separator separator2 format string_formats}.freeze
|
512
|
+
|
513
|
+
def validate_container_input(fmt)
|
514
|
+
if (fmt.keys - FMT_KEYS).size > 0
|
515
|
+
raise ArgumentError, "only #{FMT_KEYS}.map {|k| "'#{k}'"}.join(', ')} are allowed in a container format, got #{fmt}"
|
516
|
+
end
|
517
|
+
result = Format.new(fmt['format'])
|
518
|
+
result.separator = fmt['separator']
|
519
|
+
result.separator2 = fmt['separator2']
|
520
|
+
result.container_string_formats = validate_input(fmt['string_formats'])
|
521
|
+
result
|
522
|
+
end
|
523
|
+
private :validate_container_input
|
524
|
+
|
525
|
+
def string_PRuntimeType(val_type, val, format_map)
|
526
|
+
case (f = get_format(val_type, format_map))
|
527
|
+
when :s
|
528
|
+
val.to_s
|
529
|
+
when :q
|
530
|
+
val.to_s.inspect
|
531
|
+
when :puppet
|
532
|
+
puppet_safe(val.to_s)
|
533
|
+
when :i, :d, :x, :o, :f, :puppet
|
534
|
+
converted = convert(o, PNumericType) # rest is default
|
535
|
+
"%#{f}" % converted
|
536
|
+
else
|
537
|
+
throw(:failed_conversion, [o, PStringType::DEFAULT, f])
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
# Given an unsafe string make it safe for puppet
|
542
|
+
def puppet_safe(str)
|
543
|
+
str = str.inspect # all specials are now quoted
|
544
|
+
# all $ variables must be quoted
|
545
|
+
str.gsub!("\$", "\\\$")
|
546
|
+
str
|
547
|
+
end
|
548
|
+
|
549
|
+
# Basically string_PAnyType converts the value to a String and then formats it according
|
550
|
+
# to the resulting type
|
551
|
+
#
|
552
|
+
# @api private
|
553
|
+
def string_PAnyType(val_type, val, format_map, _)
|
554
|
+
f = get_format(val_type, format_map)
|
555
|
+
Kernel.format(f.orig_fmt, val)
|
556
|
+
end
|
557
|
+
|
558
|
+
def string_PDefaultType(val_type, val, format_map, _)
|
559
|
+
f = get_format(val_type, format_map)
|
560
|
+
case f.format
|
561
|
+
when :d, :s, :p
|
562
|
+
f.alt? ? '"default"' : 'default'
|
563
|
+
when :D
|
564
|
+
f.alt? ? '"Default"' : 'Default'
|
565
|
+
else
|
566
|
+
raise FormatError.new('Default', f.format, 'dDsp')
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
# @api private
|
571
|
+
def string_PUndefType(val_type, val, format_map, _)
|
572
|
+
f = get_format(val_type, format_map)
|
573
|
+
undef_str =
|
574
|
+
case f.format
|
575
|
+
when :n
|
576
|
+
f.alt? ? 'null' : 'nil'
|
577
|
+
when :u
|
578
|
+
f.alt? ? 'undefined' : 'undef'
|
579
|
+
when :d, :x, :X, :o, :b, :B, :e, :E, :f, :g, :G, :a, :A
|
580
|
+
'NaN'
|
581
|
+
when :v
|
582
|
+
'n/a'
|
583
|
+
when :V
|
584
|
+
'N/A'
|
585
|
+
when :s
|
586
|
+
f.alt? ? '""' : ''
|
587
|
+
when :p
|
588
|
+
f.alt? ? '"undef"' : 'undef'
|
589
|
+
else
|
590
|
+
raise FormatError.new('Undef', f.format, 'nudxXobBeEfgGaAvVsp')
|
591
|
+
end
|
592
|
+
fmt = "%#{f.left ? '-' : ''}"
|
593
|
+
fmt << "#{f.width}" if f.width
|
594
|
+
fmt << ".#{f.prec}" if f.prec
|
595
|
+
fmt << "s"
|
596
|
+
Kernel.format(fmt,undef_str)
|
597
|
+
end
|
598
|
+
|
599
|
+
# @api private
|
600
|
+
def string_PBooleanType(val_type, val, format_map, indentation)
|
601
|
+
f = get_format(val_type, format_map)
|
602
|
+
case f.format
|
603
|
+
when :t
|
604
|
+
# 'true'/'false' or 't'/'f' if in alt mode
|
605
|
+
str_bool = val.to_s
|
606
|
+
f.alt? ? str_bool[0] : str_bool
|
607
|
+
|
608
|
+
when :T
|
609
|
+
# 'True'/'False' or 'T'/'F' if in alt mode
|
610
|
+
str_bool = val.to_s.capitalize
|
611
|
+
f.alt? ? str_bool[0] : str_bool
|
612
|
+
|
613
|
+
when :y
|
614
|
+
# 'yes'/'no' or 'y'/'n' if in alt mode
|
615
|
+
str_bool = val ? 'yes' : 'no'
|
616
|
+
f.alt? ? str_bool[0] : str_bool
|
617
|
+
|
618
|
+
when :Y
|
619
|
+
# 'Yes'/'No' or 'Y'/'N' if in alt mode
|
620
|
+
str_bool = val ? 'Yes' : 'No'
|
621
|
+
f.alt? ? str_bool[0] : str_bool
|
622
|
+
|
623
|
+
when :d, :x, :X, :o, :b, :B
|
624
|
+
# Boolean in numeric form, formated by integer rule
|
625
|
+
numeric_bool = val ? 1 : 0
|
626
|
+
string_formats = { Puppet::Pops::Types::PIntegerType::DEFAULT => f}
|
627
|
+
_convert(TypeCalculator.infer_set(numeric_bool), numeric_bool, string_formats, indentation)
|
628
|
+
|
629
|
+
when :e, :E, :f, :g, :G, :a, :A
|
630
|
+
# Boolean in numeric form, formated by float rule
|
631
|
+
numeric_bool = val ? 1.0 : 0.0
|
632
|
+
string_formats = { Puppet::Pops::Types::PFloatType::DEFAULT => f}
|
633
|
+
_convert(TypeCalculator.infer_set(numeric_bool), numeric_bool, string_formats, indentation)
|
634
|
+
|
635
|
+
when :s
|
636
|
+
val.to_s
|
637
|
+
|
638
|
+
when :p
|
639
|
+
val.inspect
|
640
|
+
|
641
|
+
else
|
642
|
+
raise FormatError.new('Boolean', f.format, 'tTyYdxXobBeEfgGaAsp')
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
# @api private
|
647
|
+
def string_PIntegerType(val_type, val, format_map, _)
|
648
|
+
f = get_format(val_type, format_map)
|
649
|
+
case f.format
|
650
|
+
when :d, :x, :X, :o, :b, :B, :p
|
651
|
+
Kernel.format(f.orig_fmt, val)
|
652
|
+
|
653
|
+
when :e, :E, :f, :g, :G, :a, :A
|
654
|
+
Kernel.format(f.orig_fmt, val.to_f)
|
655
|
+
|
656
|
+
when :c
|
657
|
+
char = [val].pack("U")
|
658
|
+
char = f.alt? ? "\"#{char}\"" : char
|
659
|
+
char = Kernel.format(f.orig_fmt.gsub('c','s'), char)
|
660
|
+
|
661
|
+
when :s
|
662
|
+
fmt = f.alt? ? 'p' : 's'
|
663
|
+
int_str = Kernel.format('%d', val)
|
664
|
+
Kernel.format(f.orig_fmt.gsub('s', fmt), int_str)
|
665
|
+
|
666
|
+
else
|
667
|
+
raise FormatError.new('Integer', f.format, 'dxXobBeEfgGaAspc')
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# @api private
|
672
|
+
def string_PFloatType(val_type, val, format_map, _)
|
673
|
+
f = get_format(val_type, format_map)
|
674
|
+
case f.format
|
675
|
+
when :d, :x, :X, :o, :b, :B
|
676
|
+
Kernel.format(f.orig_fmt, val.to_i)
|
677
|
+
|
678
|
+
when :e, :E, :f, :g, :G, :a, :A, :p
|
679
|
+
Kernel.format(f.orig_fmt, val)
|
680
|
+
|
681
|
+
when :s
|
682
|
+
float_str = f.alt? ? "\"#{Kernel.format('%p', val)}\"" : Kernel.format('%p', val)
|
683
|
+
Kernel.format(f.orig_fmt, float_str)
|
684
|
+
|
685
|
+
else
|
686
|
+
raise FormatError.new('Float', f.format, 'dxXobBeEfgGaAsp')
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
# @api private
|
691
|
+
def string_PStringType(val_type, val, format_map, _)
|
692
|
+
f = get_format(val_type, format_map)
|
693
|
+
case f.format
|
694
|
+
when :s, :p
|
695
|
+
Kernel.format(f.orig_fmt, val)
|
696
|
+
|
697
|
+
when :c
|
698
|
+
c_val = val.capitalize
|
699
|
+
substitute = f.alt? ? 'p' : 's'
|
700
|
+
Kernel.format(f.orig_fmt.gsub('c', substitute), c_val)
|
701
|
+
|
702
|
+
when :C
|
703
|
+
c_val = val.split('::').map {|s| s.capitalize }.join('::')
|
704
|
+
substitute = f.alt? ? 'p' : 's'
|
705
|
+
Kernel.format(f.orig_fmt.gsub('C', substitute), c_val)
|
706
|
+
|
707
|
+
when :u
|
708
|
+
substitute = f.alt? ? 'p' : 's'
|
709
|
+
Kernel.format(f.orig_fmt.gsub('u', substitute), val).upcase
|
710
|
+
|
711
|
+
when :d
|
712
|
+
substitute = f.alt? ? 'p' : 's'
|
713
|
+
Kernel.format(f.orig_fmt.gsub('d', substitute), val).downcase
|
714
|
+
|
715
|
+
when :t # trim
|
716
|
+
c_val = val.strip
|
717
|
+
substitute = f.alt? ? 'p' : 's'
|
718
|
+
Kernel.format(f.orig_fmt.gsub('t', substitute), c_val)
|
719
|
+
|
720
|
+
else
|
721
|
+
raise FormatError.new('String', f.format, 'cCudspt')
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
# @api private
|
726
|
+
def string_PRegexpType(val_type, val, format_map, _)
|
727
|
+
f = get_format(val_type, format_map)
|
728
|
+
case f.format
|
729
|
+
when :p
|
730
|
+
Kernel.format(f.orig_fmt, val)
|
731
|
+
when :s
|
732
|
+
str_regexp = val.inspect
|
733
|
+
str_regexp = f.alt? ? "\"#{str_regexp[1..-2]}\"" : str_regexp
|
734
|
+
Kernel.format(f.orig_fmt, str_regexp)
|
735
|
+
else
|
736
|
+
raise FormatError.new('Regexp', f.format, 'rsp')
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
def string_PArrayType(val_type, val, format_map, indentation)
|
741
|
+
format = get_format(val_type, format_map)
|
742
|
+
sep = format.separator || DEFAULT_ARRAY_FORMAT.separator
|
743
|
+
string_formats = format.container_string_formats || DEFAULT_CONTAINER_FORMATS
|
744
|
+
delims = format.delimiter_pair(DEFAULT_ARRAY_DELIMITERS)
|
745
|
+
|
746
|
+
# Make indentation active, if array is in alternative format, or if nested in indenting
|
747
|
+
indentation = indentation.indenting(format.alt? || indentation.is_indenting?)
|
748
|
+
|
749
|
+
case format.format
|
750
|
+
when :a, :s, :p
|
751
|
+
buf = ''
|
752
|
+
if indentation.breaks?
|
753
|
+
buf << "\n"
|
754
|
+
buf << indentation.padding
|
755
|
+
end
|
756
|
+
buf << delims[0]
|
757
|
+
|
758
|
+
# Make a first pass to format each element
|
759
|
+
children_indentation = indentation.increase(format.alt?) # tell children they are expected to indent
|
760
|
+
mapped = val.map do |v|
|
761
|
+
if children_indentation.first?
|
762
|
+
children_indentation = children_indentation.subsequent
|
763
|
+
end
|
764
|
+
val_t = TypeCalculator.infer_set(v)
|
765
|
+
_convert(val_t, v, is_container?(val_t) ? format_map : string_formats, children_indentation)
|
766
|
+
end
|
767
|
+
|
768
|
+
# compute widest run in the array, skip nested arrays and hashes
|
769
|
+
# then if size > width, set flag if a break on each element should be performed
|
770
|
+
if format.alt? && format.width
|
771
|
+
widest = val.each_with_index.reduce([0]) do | memo, v_i |
|
772
|
+
# array or hash breaks
|
773
|
+
if is_a_or_h?(v_i[0])
|
774
|
+
memo << 0
|
775
|
+
else
|
776
|
+
memo[-1] += mapped[v_i[1]].length
|
777
|
+
end
|
778
|
+
memo
|
779
|
+
end
|
780
|
+
widest = widest.max
|
781
|
+
sz_break = widest > (format.width || Float::INFINITY)
|
782
|
+
else
|
783
|
+
sz_break = false
|
784
|
+
end
|
785
|
+
|
786
|
+
# output each element with breaks and padding
|
787
|
+
children_indentation = indentation.increase(format.alt?)
|
788
|
+
val.each_with_index do |v, i|
|
789
|
+
str_val = mapped[i]
|
790
|
+
if children_indentation.first?
|
791
|
+
children_indentation = children_indentation.subsequent
|
792
|
+
# if breaking, indent first element by one
|
793
|
+
if sz_break && !is_a_or_h?(v)
|
794
|
+
buf << ' '
|
795
|
+
end
|
796
|
+
else
|
797
|
+
buf << sep
|
798
|
+
# if break on each (and breaking will not occur because next is an array or hash)
|
799
|
+
# or, if indenting, and previous was an array or hash, then break and continue on next line
|
800
|
+
# indented.
|
801
|
+
if (sz_break && !is_a_or_h?(v)) || (format.alt? && i > 0 && is_a_or_h?(val[i-1]) && !is_a_or_h?(v))
|
802
|
+
buf << "\n"
|
803
|
+
buf << children_indentation.padding
|
804
|
+
elsif !(format.alt? && is_a_or_h?(v))
|
805
|
+
buf << ' '
|
806
|
+
end
|
807
|
+
end
|
808
|
+
buf << str_val
|
809
|
+
end
|
810
|
+
buf << delims[1]
|
811
|
+
buf
|
812
|
+
else
|
813
|
+
raise FormatError.new('Array', format.format, 'asp')
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
def is_a_or_h?(x)
|
818
|
+
x.is_a?(Array) || x.is_a?(Hash)
|
819
|
+
end
|
820
|
+
|
821
|
+
def is_container?(t)
|
822
|
+
case t
|
823
|
+
when PArrayType, PHashType, PStructType, PTupleType
|
824
|
+
true
|
825
|
+
else
|
826
|
+
false
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
# @api private
|
831
|
+
def string_PTupleType(val_type, val, format_map, indentation)
|
832
|
+
string_PArrayType(val_type, val, format_map, indentation)
|
833
|
+
end
|
834
|
+
|
835
|
+
# @api private
|
836
|
+
def string_PIteratorType(val_type, val, format_map, indentation)
|
837
|
+
v = val.to_a
|
838
|
+
_convert(TypeCalculator.infer_set(v), v, format_map, indentation)
|
839
|
+
end
|
840
|
+
|
841
|
+
# @api private
|
842
|
+
def string_PHashType(val_type, val, format_map, indentation)
|
843
|
+
format = get_format(val_type, format_map)
|
844
|
+
sep = format.separator || DEFAULT_HASH_FORMAT.separator
|
845
|
+
assoc = format.separator2 || DEFAULT_HASH_FORMAT.separator2
|
846
|
+
string_formats = format.container_string_formats || DEFAULT_CONTAINER_FORMATS
|
847
|
+
delims = format.delimiter_pair(DEFAULT_HASH_DELIMITERS)
|
848
|
+
|
849
|
+
sep = format.alt? ? "#{sep}\n" : "#{sep} "
|
850
|
+
|
851
|
+
cond_break = ''
|
852
|
+
padding = ''
|
853
|
+
|
854
|
+
case format.format
|
855
|
+
when :a
|
856
|
+
# Convert to array and use array rules
|
857
|
+
array_hash = val.to_a
|
858
|
+
_convert(TypeCalculator.infer_set(array_hash), array_hash, format_map, indentation)
|
859
|
+
|
860
|
+
when :h, :s, :p
|
861
|
+
indentation = indentation.indenting(format.alt? || indentation.is_indenting?)
|
862
|
+
buf = ''
|
863
|
+
if indentation.breaks?
|
864
|
+
buf << "\n"
|
865
|
+
buf << indentation.padding
|
866
|
+
end
|
867
|
+
|
868
|
+
children_indentation = indentation.increase
|
869
|
+
if format.alt?
|
870
|
+
cond_break = "\n"
|
871
|
+
padding = children_indentation.padding
|
872
|
+
end
|
873
|
+
buf << delims[0]
|
874
|
+
buf << cond_break # break after opening delimiter if pretty printing
|
875
|
+
buf << val.map do |k,v|
|
876
|
+
key_type = TypeCalculator.infer_set(k)
|
877
|
+
val_type = TypeCalculator.infer_set(v)
|
878
|
+
key = _convert(key_type, k, is_container?(key_type) ? format_map : string_formats, children_indentation)
|
879
|
+
val = _convert(val_type, v, is_container?(val_type) ? format_map : string_formats, children_indentation)
|
880
|
+
"#{padding}#{key}#{assoc}#{val}"
|
881
|
+
end.join(sep)
|
882
|
+
if format.alt?
|
883
|
+
buf << cond_break
|
884
|
+
buf << indentation.padding
|
885
|
+
end
|
886
|
+
buf << delims[1]
|
887
|
+
buf
|
888
|
+
else
|
889
|
+
raise FormatError.new('Hash', format.format, 'hasp')
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
# @api private
|
894
|
+
def string_PStructType(val_type, val, format_map, indentation)
|
895
|
+
string_PHashType(val_type, val, format_map, indentation)
|
896
|
+
end
|
897
|
+
|
898
|
+
# @api private
|
899
|
+
def string_PType(val_type, val, format_map, _)
|
900
|
+
f = get_format(val_type, format_map)
|
901
|
+
case f.format
|
902
|
+
when :s
|
903
|
+
str_val = f.alt? ? "\"#{val.to_s}\"" : val.to_s
|
904
|
+
Kernel.format(f.orig_fmt, str_val)
|
905
|
+
when :p
|
906
|
+
Kernel.format(f.orig_fmt.gsub('p', 's'), val.to_s)
|
907
|
+
else
|
908
|
+
raise FormatError.new('Type', f.format, 'sp')
|
909
|
+
end
|
910
|
+
end
|
911
|
+
|
912
|
+
# Maps the inferred type of o to a formatting rule
|
913
|
+
def get_format(val_t, format_options)
|
914
|
+
fmt = format_options.find {|k,_| k.assignable?(val_t) }
|
915
|
+
return fmt[1] unless fmt.nil?
|
916
|
+
return Format.new("%s")
|
917
|
+
end
|
918
|
+
private :get_format
|
919
|
+
|
920
|
+
end
|
921
|
+
end
|
922
|
+
end
|