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:

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

convert(value, options = EMPTY_HASH) click to toggle source

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
new(options = EMPTY_HASH) click to toggle source

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

convert(value) click to toggle source

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

non_string_keyed_hash_to_data(hash) click to toggle source
    # 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
path_to_s() click to toggle source
   # 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
process(value, &block) click to toggle source

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
serialization_issue(issue, options = EMPTY_HASH) click to toggle source
    # 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
to_data(value) click to toggle source
   # 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
to_string_or_binary(value) click to toggle source

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
unknown_key_to_string(value) click to toggle source

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
unknown_to_string(value) click to toggle source
    # 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
with(key) { || ... } click to toggle source

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
with_recursive_guard(value) { || ... } click to toggle source

@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