Class: Megatest::PrettyPrint::Printer

Inherits:
PrettyPrint
  • Object
show all
Defined in:
lib/megatest/pretty_print.rb

Overview

This class is largely a copy of the ‘pp` gem but rewritten to not rely on monkey patches and with some small rendering modifications notably around multiline strings.

Constant Summary collapse

INSTANCE_VARIABLES =
Object.instance_method(:instance_variables)
CLASS =
Kernel.instance_method(:class)
METHOD =
Object.instance_method(:method)
OBJECT_INSPECT =
Object.instance_method(:inspect)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.pp(obj, out = +"",, width = 79) ⇒ Object



15
16
17
18
19
20
# File 'lib/megatest/pretty_print.rb', line 15

def pp(obj, out = +"", width = 79)
  q = new(out, width)
  q.guard_inspect_key { q.pp obj }
  q.flush
  out
end

Instance Method Details

#check_inspect_key?(id) ⇒ Boolean

Check whether the object_id id is in the current buffer of objects to be pretty printed. Used to break cycles in chains of objects to be pretty printed.

Returns:

  • (Boolean)


41
42
43
# File 'lib/megatest/pretty_print.rb', line 41

def check_inspect_key?(id)
  @recursive_key&.include?(id)
end

#comma_breakableObject

A convenience method which is same as follows:

text ','
breakable


100
101
102
103
# File 'lib/megatest/pretty_print.rb', line 100

def comma_breakable
  text ","
  breakable
end

#guard_inspect_keyObject

Yields to a block and preserves the previous set of objects being printed.



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/megatest/pretty_print.rb', line 25

def guard_inspect_key
  @recursive_key = {}.compare_by_identity

  save = @recursive_key

  begin
    @recursive_key = {}.compare_by_identity
    yield
  ensure
    @recursive_key = save
  end
end

#inspect_object(obj) ⇒ Object



278
279
280
281
282
# File 'lib/megatest/pretty_print.rb', line 278

def inspect_object(obj)
  obj.inspect
rescue NoMethodError # Basic Object etc.
  OBJECT_INSPECT.bind_call(obj)
end

#object_address_group(obj, &block) ⇒ Object

A convenience method, like object_group, but also reformats the Object’s object_id.



90
91
92
93
94
# File 'lib/megatest/pretty_print.rb', line 90

def object_address_group(obj, &block)
  str = Kernel.instance_method(:to_s).bind_call(obj)
  str.chomp!(">")
  group(1, str, ">", &block)
end

#object_group(obj, &block) ⇒ Object

A convenience method which is same as follows:

group(1, '#<' + obj.class.name, '>') { ... }


82
83
84
# File 'lib/megatest/pretty_print.rb', line 82

def object_group(obj, &block)
  group(1, "#<#{obj.class.name}>", &block)
end

#pop_inspect_key(id) ⇒ Object

Removes an object from the set of objects being pretty printed.



52
53
54
# File 'lib/megatest/pretty_print.rb', line 52

def pop_inspect_key(id)
  @recursive_key.delete id
end

#pp(obj) ⇒ Object

Adds obj to the pretty printing buffer using Object#pretty_print or Object#pretty_print_cycle.

Object#pretty_print_cycle is used when obj is already printed, a.k.a the object reference chain has a cycle.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/megatest/pretty_print.rb', line 61

def pp(obj)
  # If obj is a Delegator then use the object being delegated to for cycle
  # detection
  obj = obj.__getobj__ if defined?(::Delegator) && ::Delegator === obj

  if check_inspect_key?(obj)
    group { pretty_print_cycle(obj) }
    return
  end

  begin
    push_inspect_key(obj)
    group { pretty_print(obj) }
  ensure
    pop_inspect_key(obj)
  end
end

#pp_hash(obj) ⇒ Object

A pretty print for a Hash



164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/megatest/pretty_print.rb', line 164

def pp_hash(obj)
  group(1, "{", "}") do
    seplist(obj, nil, :each_pair) do |k, v|
      group do
        pp k
        text "=>"
        group(1) do
          breakable ""
          pp v
        end
      end
    end
  end
end

#pp_object(obj) ⇒ Object

A present standard failsafe for pretty printing any given Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/megatest/pretty_print.rb', line 143

def pp_object(obj)
  object_address_group(obj) do
    seplist(pretty_print_instance_variables(obj), -> { text "," }) do |v|
      breakable
      v = v.to_s if Symbol === v
      text v
      text "="
      group(1) do
        breakable ""
        pp(obj.instance_eval(v))
      end
    end
  end
end

#pretty_print(obj) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/megatest/pretty_print.rb', line 183

def pretty_print(obj)
  case obj
  when String
    if obj.size > 30 && obj.byterindex("\n", -1)
      text obj.inspect.gsub('\n', "\\n\n").sub(/\\n\n"\z/, '\n"')
    else
      text obj.inspect
    end
  when Array
    group(1, "[", "]") do
      seplist(obj) do |v|
        pp v
      end
    end
  when Hash
    pp_hash(obj)
  when Range
    pp obj.begin
    breakable ""
    text(obj.exclude_end? ? "..." : "..")
    breakable ""
    pp obj.end if obj.end
  when MatchData
    nc = []
    obj.regexp.named_captures.each do |name, indexes|
      indexes.each { |i| nc[i] = name }
    end

    object_group(obj) do
      breakable
      seplist(0...obj.size, -> { breakable }) do |i|
        if i != 0
          if nc[i]
            text nc[i]
          else
            pp i
          end
          text ":"
          pp obj[i]
        end
        pp obj[i]
      end
    end
  when Regexp, Symbol, Numeric, Module, true, false, nil
    text(obj.inspect)
  when Struct
    group(1, format("#<struct %s", CLASS.bind_call(obj)), ">") do
      seplist(Struct.instance_method(:members).bind_call(obj), -> { text "," }) do |member|
        breakable
        text member.to_s
        text "="
        group(1) do
          breakable ""
          pp obj[member]
        end
      end
    end
  else
    if ENV.equal?(obj)
      pp_hash(ENV.sort.to_h)
    elsif special_inspect?(obj)
      text(obj.inspect)
    else
      pp_object(obj)
    end
  end
end

#pretty_print_cycle(obj) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/megatest/pretty_print.rb', line 251

def pretty_print_cycle(obj)
  case obj
  when Array
    text(obj.empty? ? "[]" : "[...]")
  when Hash
    text(obj.empty? ? "{}" : "{...}")
  when Struct
    text format("#<struct %s:...>", CLASS.bind_call(obj))
  when Numeric, Symbol, FalseClass, TrueClass, NilClass, Module
    text obj.inspect
  else
    object_address_group(obj) do
      breakable
      text "..."
    end
  end
end

#pretty_print_instance_variables(obj) ⇒ Object



159
160
161
# File 'lib/megatest/pretty_print.rb', line 159

def pretty_print_instance_variables(obj)
  INSTANCE_VARIABLES.bind_call(obj).sort
end

#push_inspect_key(id) ⇒ Object

Adds the object_id id to the set of objects being pretty printed, so as to not repeat objects.



47
48
49
# File 'lib/megatest/pretty_print.rb', line 47

def push_inspect_key(id)
  @recursive_key[id] = true
end

#seplist(list, sep = nil, iter_method = :each) ⇒ Object

Adds a separated list. The list is separated by comma with breakable space, by default.

#seplist iterates the list using iter_method. It yields each object to the block given for #seplist. The procedure separator_proc is called between each yields.

If the iteration is zero times, separator_proc is not called at all.

If separator_proc is nil or not given, lambda { comma_breakable } is used. If iter_method is not given, :each is used.

For example, following 3 code fragments has similar effect.

q.seplist([1,2,3]) {|v| xxx v }

q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }

xxx 1
q.comma_breakable
xxx 2
q.comma_breakable
xxx 3


129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/megatest/pretty_print.rb', line 129

def seplist(list, sep = nil, iter_method = :each)
  sep ||= -> { comma_breakable }
  first = true
  list.__send__(iter_method) do |*v|
    if first
      first = false
    else
      sep.call
    end
    yield(*v)
  end
end

#special_inspect?(obj) ⇒ Boolean

Returns:

  • (Boolean)


270
271
272
273
274
# File 'lib/megatest/pretty_print.rb', line 270

def special_inspect?(obj)
  METHOD.bind_call(obj, :inspect).owner != Kernel
rescue NoMethodError
  false
end