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
30 def self.convert(value, options = EMPTY_HASH)
31   new(options).convert(value)
32 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
39 def initialize(options = EMPTY_HASH)
40   @message_prefix = options[:message_prefix]
41   @semantic = options[:semantic]
42 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
50 def convert(value)
51   @path = []
52   @values = {}
53   to_data(value)
54 end

Private Instance Methods

non_string_keyed_hash_to_data(hash) click to toggle source
    # 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
path_to_s() click to toggle source
   # 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
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
119 def process(value, &block)
120   with_recursive_guard(value, &block)
121 end
serialization_issue(issue, options = EMPTY_HASH) click to toggle source
    # 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
to_data(value) click to toggle source
   # 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
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
 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
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
156 def unknown_key_to_string(value)
157   unknown_to_string(value)
158 end
unknown_to_string(value) click to toggle source
    # 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
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
130 def with(key)
131   @path.push(key)
132   value = yield
133   @path.pop
134   value
135 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
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