class Puppet::Pops::Serialization::ToStringifiedConverter
Class
that can process an arbitrary object into a value that is assignable to `Data` and where contents is converted from rich data to one of:
-
Numeric (Integer, Float)
-
Boolean
-
Undef (nil)
-
String
-
Array
-
Hash
The conversion is lossy - the result cannot be deserialized to produce the original data types. All rich values are transformed to strings.. Hashes with rich keys are transformed to use string representation of such keys.
@api public
Public Class Methods
Converts the given value according to the given options and return the result of the conversion
@param value [Object] the value to convert @param options {Symbol => <Boolean,String>} options hash @option options [String] :message_prefix String to prepend to in warnings and errors @option options [String] :semantic object (AST
) to pass to the issue reporter @return [Data] the processed result. An object assignable to `Data` with rich data stringified.
@api public
# File lib/puppet/pops/serialization/to_stringified_converter.rb 30 def self.convert(value, options = EMPTY_HASH) 31 new(options).convert(value) 32 end
Creates a new instance of the processor
@param options {Symbol => Object} options hash @option options [String] :message_prefix String to prepend to path in warnings and errors @option semantic [Object] :semantic object to pass to the issue reporter
# File lib/puppet/pops/serialization/to_stringified_converter.rb 39 def initialize(options = EMPTY_HASH) 40 @message_prefix = options[:message_prefix] 41 @semantic = options[:semantic] 42 end
Public Instance Methods
Converts the given value
@param value [Object] the value to convert @return [Data] the processed result. An object assignable to `Data` with rich data stringified.
@api public
# File lib/puppet/pops/serialization/to_stringified_converter.rb 50 def convert(value) 51 @path = [] 52 @values = {} 53 to_data(value) 54 end
Private Instance Methods
# File lib/puppet/pops/serialization/to_stringified_converter.rb 195 def non_string_keyed_hash_to_data(hash) 196 result = {} 197 hash.each_pair do |key, value| 198 if key.is_a?(Symbol) 199 key = key.to_s 200 elsif !key.is_a?(String) 201 key = unknown_key_to_string(key) 202 end 203 if key == "__ptype" || key =="__pvalue" 204 key = "reserved key: #{key}" 205 end 206 with(key) { result[key] = to_data(value) } 207 end 208 result 209 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 58 def path_to_s 59 s = @message_prefix || '' 60 s << JsonPath.to_json_path(@path)[1..-1] 61 s 62 end
Performs a check for endless recursion before it yields to the given block. The result of yielding is returned.
@param value [Object] the value @yield The block that will produce the data for the value @return [Data] the result of yielding to the given block, or a hash denoting a reference
@api private
# File lib/puppet/pops/serialization/to_stringified_converter.rb 119 def process(value, &block) 120 with_recursive_guard(value, &block) 121 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 211 def serialization_issue(issue, options = EMPTY_HASH) 212 semantic = @semantic 213 if semantic.nil? 214 tos = Puppet::Pops::PuppetStack.top_of_stack 215 if tos.empty? 216 semantic = Puppet::Pops::SemanticError.new(issue, nil, EMPTY_HASH) 217 else 218 file, line = stacktrace 219 semantic = Puppet::Pops::SemanticError.new(issue, nil, {:file => file, :line => line}) 220 end 221 end 222 optionally_fail(issue, semantic, options) 223 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 64 def to_data(value) 65 if value.is_a?(String) 66 to_string_or_binary(value) 67 elsif value.nil? || Types::PScalarDataType::DEFAULT.instance?(value) 68 value 69 elsif :default == value 70 'default' 71 elsif value.is_a?(Symbol) 72 value.to_s 73 elsif value.instance_of?(Array) 74 process(value) do 75 result = [] 76 value.each_with_index do |elem, index| 77 with(index) { result << to_data(elem) } 78 end 79 result 80 end 81 elsif value.instance_of?(Hash) 82 process(value) do 83 if value.keys.all? { |key| key.is_a?(String) && key != PCORE_TYPE_KEY && key != PCORE_VALUE_KEY } 84 result = {} 85 value.each_pair { |key, elem| with(key) { result[key] = to_data(elem) } } 86 result 87 else 88 non_string_keyed_hash_to_data(value) 89 end 90 end 91 else 92 unknown_to_string(value) 93 end 94 end
Turns an ASCII-8BIT encoded string into a Binary, returns US_ASCII encoded and transforms all other strings to UTF-8 with replacements for non Unicode characters. If String cannot be represented as UTF-8
# File lib/puppet/pops/serialization/to_stringified_converter.rb 99 def to_string_or_binary(value) 100 encoding = value.encoding 101 if encoding == Encoding::ASCII_8BIT 102 Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(value).to_s 103 else 104 # Transform to UTF-8 (do not assume UTF-8 is correct) with source invalid byte 105 # sequences and UTF-8 undefined characters replaced by the default unicode uFFFD character 106 # (black diamond with question mark). 107 value.encode(Encoding::UTF_8, encoding, :invalid => :replace, :undef => :replace) 108 end 109 end
A hash key that is non conforming
# File lib/puppet/pops/serialization/to_stringified_converter.rb 156 def unknown_key_to_string(value) 157 unknown_to_string(value) 158 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 160 def unknown_to_string(value) 161 if value.is_a?(Regexp) 162 return Puppet::Pops::Types::PRegexpType.regexp_to_s_with_delimiters(value) 163 164 elsif value.instance_of?(Types::PSensitiveType::Sensitive) 165 # to_s does not differentiate between instances - if they were used as keys in a hash 166 # the stringified result would override all Sensitive keys with the last such key's value 167 # this adds object_id. 168 # 169 return "#<#{value}:#{value.object_id}>" 170 171 elsif value.is_a?(Puppet::Pops::Types::PObjectType) 172 # regular to_s on an ObjectType gives the entire definition 173 return value.name 174 175 end 176 177 # Do a to_s on anything else 178 result = value.to_s 179 180 # The result may be ascii-8bit encoded without being a binary (low level object.inspect returns ascii-8bit string) 181 # This can be the case if runtime objects have very simple implementation (no to_s or inspect method). 182 # They are most likely not of Binary nature. Therefore the encoding is forced and only if it errors 183 # will the result be taken as binary and encoded as base64 string. 184 if result.encoding == Encoding::ASCII_8BIT 185 begin 186 result.force_encoding(Encoding::UTF_8) 187 rescue 188 # The result cannot be represented in UTF-8, make it a binary Base64 encoded string 189 Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(result).to_s 190 end 191 end 192 result 193 end
Pushes `key` to the end of the path and yields to the given block. The `key` is popped when the yield returns. @param key [Object] the key to push on the current path @yield The block that will produce the returned value @return [Object] the result of yielding to the given block
@api private
# File lib/puppet/pops/serialization/to_stringified_converter.rb 130 def with(key) 131 @path.push(key) 132 value = yield 133 @path.pop 134 value 135 end
@param value [Object] the value to use when checking endless recursion @yield The block that will produce the data @return [Data] the result of yielding to the given block
# File lib/puppet/pops/serialization/to_stringified_converter.rb 140 def with_recursive_guard(value) 141 id = value.object_id 142 if @recursive_lock 143 if @recursive_lock.include?(id) 144 serialization_issue(Issues::SERIALIZATION_ENDLESS_RECURSION, :type_name => value.class.name) 145 end 146 @recursive_lock[id] = true 147 else 148 @recursive_lock = { id => true } 149 end 150 v = yield 151 @recursive_lock.delete(id) 152 v 153 end