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 31 def self.convert(value, options = EMPTY_HASH) 32 new(options).convert(value) 33 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 40 def initialize(options = EMPTY_HASH) 41 @message_prefix = options[:message_prefix] 42 @semantic = options[:semantic] 43 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 51 def convert(value) 52 @path = [] 53 @values = {} 54 to_data(value) 55 end
Private Instance Methods
# File lib/puppet/pops/serialization/to_stringified_converter.rb 196 def non_string_keyed_hash_to_data(hash) 197 result = {} 198 hash.each_pair do |key, value| 199 if key.is_a?(Symbol) 200 key = key.to_s 201 elsif !key.is_a?(String) 202 key = unknown_key_to_string(key) 203 end 204 if key == "__ptype" || key =="__pvalue" 205 key = "reserved key: #{key}" 206 end 207 with(key) { result[key] = to_data(value) } 208 end 209 result 210 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 59 def path_to_s 60 s = @message_prefix || '' 61 s << JsonPath.to_json_path(@path)[1..-1] 62 s 63 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 120 def process(value, &block) 121 with_recursive_guard(value, &block) 122 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 212 def serialization_issue(issue, options = EMPTY_HASH) 213 semantic = @semantic 214 if semantic.nil? 215 tos = Puppet::Pops::PuppetStack.top_of_stack 216 if tos.empty? 217 semantic = Puppet::Pops::SemanticError.new(issue, nil, EMPTY_HASH) 218 else 219 file, line = stacktrace 220 semantic = Puppet::Pops::SemanticError.new(issue, nil, {:file => file, :line => line}) 221 end 222 end 223 optionally_fail(issue, semantic, options) 224 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 65 def to_data(value) 66 if value.instance_of?(String) 67 to_string_or_binary(value) 68 elsif value.nil? || Types::PScalarDataType::DEFAULT.instance?(value) 69 value 70 elsif :default == value 71 'default' 72 elsif value.is_a?(Symbol) 73 value.to_s 74 elsif value.instance_of?(Array) 75 process(value) do 76 result = [] 77 value.each_with_index do |elem, index| 78 with(index) { result << to_data(elem) } 79 end 80 result 81 end 82 elsif value.instance_of?(Hash) 83 process(value) do 84 if value.keys.all? { |key| key.is_a?(String) && key != PCORE_TYPE_KEY && key != PCORE_VALUE_KEY } 85 result = {} 86 value.each_pair { |key, elem| with(key) { result[key] = to_data(elem) } } 87 result 88 else 89 non_string_keyed_hash_to_data(value) 90 end 91 end 92 else 93 unknown_to_string(value) 94 end 95 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 100 def to_string_or_binary(value) 101 encoding = value.encoding 102 if encoding == Encoding::ASCII_8BIT 103 Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(value).to_s 104 else 105 # Transform to UTF-8 (do not assume UTF-8 is correct) with source invalid byte 106 # sequences and UTF-8 undefined characters replaced by the default unicode uFFFD character 107 # (black diamond with question mark). 108 value.encode(Encoding::UTF_8, encoding, :invalid => :replace, :undef => :replace) 109 end 110 end
A hash key that is non conforming
# File lib/puppet/pops/serialization/to_stringified_converter.rb 157 def unknown_key_to_string(value) 158 unknown_to_string(value) 159 end
# File lib/puppet/pops/serialization/to_stringified_converter.rb 161 def unknown_to_string(value) 162 if value.is_a?(Regexp) 163 return Puppet::Pops::Types::PRegexpType.regexp_to_s_with_delimiters(value) 164 165 elsif value.instance_of?(Types::PSensitiveType::Sensitive) 166 # to_s does not differentiate between instances - if they were used as keys in a hash 167 # the stringified result would override all Sensitive keys with the last such key's value 168 # this adds object_id. 169 # 170 return "#<#{value}:#{value.object_id}>" 171 172 elsif value.is_a?(Puppet::Pops::Types::PObjectType) 173 # regular to_s on an ObjectType gives the entire definition 174 return value.name 175 176 end 177 178 # Do a to_s on anything else 179 result = value.to_s 180 181 # The result may be ascii-8bit encoded without being a binary (low level object.inspect returns ascii-8bit string) 182 # This can be the case if runtime objects have very simple implementation (no to_s or inspect method). 183 # They are most likely not of Binary nature. Therefore the encoding is forced and only if it errors 184 # will the result be taken as binary and encoded as base64 string. 185 if result.encoding == Encoding::ASCII_8BIT 186 begin 187 result.force_encoding(Encoding::UTF_8) 188 rescue 189 # The result cannot be represented in UTF-8, make it a binary Base64 encoded string 190 Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(result).to_s 191 end 192 end 193 result 194 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 131 def with(key) 132 @path.push(key) 133 value = yield 134 @path.pop 135 value 136 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 141 def with_recursive_guard(value) 142 id = value.object_id 143 if @recursive_lock 144 if @recursive_lock.include?(id) 145 serialization_issue(Issues::SERIALIZATION_ENDLESS_RECURSION, :type_name => value.class.name) 146 end 147 @recursive_lock[id] = true 148 else 149 @recursive_lock = { id => true } 150 end 151 v = yield 152 @recursive_lock.delete(id) 153 v 154 end