awesome_print 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,19 +3,27 @@
3
3
  # Awesome Print is freely distributable under the terms of MIT license.
4
4
  # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
5
  #------------------------------------------------------------------------------
6
- autoload :CGI, "cgi"
7
- require "shellwords"
6
+ require_relative "formatters/object_formatter"
7
+ require_relative "formatters/hash_formatter"
8
+ require_relative "formatters/array_formatter"
9
+ require_relative "formatters/simple_formatter"
10
+ require_relative "formatters/method_formatter"
11
+ require_relative "formatters/class_formatter"
12
+ require_relative "formatters/dir_formatter"
13
+ require_relative "formatters/file_formatter"
14
+ require_relative "colorize"
8
15
 
9
16
  module AwesomePrint
10
17
  class Formatter
18
+ include Colorize
19
+
20
+ attr_reader :inspector, :options
11
21
 
12
22
  CORE = [ :array, :bigdecimal, :class, :dir, :file, :hash, :method, :rational, :set, :struct, :unboundmethod ]
13
- DEFAULT_LIMIT_SIZE = 7
14
23
 
15
24
  def initialize(inspector)
16
25
  @inspector = inspector
17
26
  @options = inspector.options
18
- @indentation = @options[:indent].abs
19
27
  end
20
28
 
21
29
  # Main entry point to format an object.
@@ -37,25 +45,6 @@ module AwesomePrint
37
45
  CORE.grep(type)[0] || :self
38
46
  end
39
47
 
40
- # Pick the color and apply it to the given string as necessary.
41
- #------------------------------------------------------------------------------
42
- def colorize(str, type)
43
- str = CGI.escapeHTML(str) if @options[:html]
44
- if @options[:plain] || !@options[:color][type] || !@inspector.colorize?
45
- str
46
- #
47
- # Check if the string color method is defined by awesome_print and accepts
48
- # html parameter or it has been overriden by some gem such as colorize.
49
- #
50
- elsif str.method(@options[:color][type]).arity == -1 # Accepts html parameter.
51
- str.send(@options[:color][type], @options[:html])
52
- else
53
- str = %Q|<kbd style="color:#{@options[:color][type]}">#{str}</kbd>| if @options[:html]
54
- str.send(@options[:color][type])
55
- end
56
- end
57
-
58
-
59
48
  private
60
49
 
61
50
  # Catch all method to format an arbitrary object.
@@ -63,345 +52,83 @@ module AwesomePrint
63
52
  def awesome_self(object, type)
64
53
  if @options[:raw] && object.instance_variables.any?
65
54
  awesome_object(object)
66
- elsif object.respond_to?(:to_hash)
67
- awesome_hash(object.to_hash)
68
- else
69
- colorize(object.inspect.to_s, type)
70
- end
71
- end
72
-
73
- # Format an array.
74
- #------------------------------------------------------------------------------
75
- def awesome_array(a)
76
- return "[]" if a == []
77
-
78
- if a.instance_variable_defined?('@__awesome_methods__')
79
- methods_array(a)
80
- elsif @options[:multiline]
81
- width = (a.size - 1).to_s.size
82
-
83
- data = a.inject([]) do |arr, item|
84
- index = indent
85
- index << colorize("[#{arr.size.to_s.rjust(width)}] ", :array) if @options[:index]
86
- indented do
87
- arr << (index << @inspector.awesome(item))
88
- end
89
- end
90
-
91
- data = limited(data, width) if should_be_limited?
92
- "[\n" << data.join(",\n") << "\n#{outdent}]"
93
- else
94
- "[ " << a.map{ |item| @inspector.awesome(item) }.join(", ") << " ]"
95
- end
96
- end
97
-
98
- # Format a hash. If @options[:indent] if negative left align hash keys.
99
- #------------------------------------------------------------------------------
100
- def awesome_hash(h)
101
- return "{}" if h == {}
102
-
103
- keys = @options[:sort_keys] ? h.keys.sort { |a, b| a.to_s <=> b.to_s } : h.keys
104
- data = keys.map do |key|
105
- plain_single_line do
106
- [ @inspector.awesome(key), h[key] ]
107
- end
108
- end
109
-
110
- width = data.map { |key, | key.size }.max || 0
111
- width += @indentation if @options[:indent] > 0
112
-
113
- data = data.map do |key, value|
114
- indented do
115
- align(key, width) << colorize(" => ", :hash) << @inspector.awesome(value)
116
- end
117
- end
118
-
119
- data = limited(data, width, :hash => true) if should_be_limited?
120
- if @options[:multiline]
121
- "{\n" << data.join(",\n") << "\n#{outdent}}"
55
+ elsif hash = convert_to_hash(object)
56
+ awesome_hash(hash)
122
57
  else
123
- "{ #{data.join(', ')} }"
58
+ awesome_simple(object.inspect.to_s, type, @inspector)
124
59
  end
125
60
  end
126
61
 
127
- # Format an object.
128
- #------------------------------------------------------------------------------
129
- def awesome_object(o)
130
- vars = o.instance_variables.map do |var|
131
- property = var.to_s[1..-1].to_sym # to_s because of some monkey patching done by Puppet.
132
- accessor = if o.respond_to?(:"#{property}=")
133
- o.respond_to?(property) ? :accessor : :writer
134
- else
135
- o.respond_to?(property) ? :reader : nil
136
- end
137
- if accessor
138
- [ "attr_#{accessor} :#{property}", var ]
139
- else
140
- [ var.to_s, var ]
141
- end
142
- end
143
-
144
- data = vars.sort.map do |declaration, var|
145
- key = left_aligned do
146
- align(declaration, declaration.size)
147
- end
148
-
149
- unless @options[:plain]
150
- if key =~ /(@\w+)/
151
- key.sub!($1, colorize($1, :variable))
152
- else
153
- key.sub!(/(attr_\w+)\s(\:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}")
154
- end
155
- end
156
- indented do
157
- key << colorize(" = ", :hash) + @inspector.awesome(o.instance_variable_get(var))
158
- end
159
- end
160
- if @options[:multiline]
161
- "#<#{awesome_instance(o)}\n#{data.join(%Q/,\n/)}\n#{outdent}>"
162
- else
163
- "#<#{awesome_instance(o)} #{data.join(', ')}>"
164
- end
62
+ def awesome_bigdecimal(n)
63
+ o = n.to_s("F")
64
+ type = :bigdecimal
65
+ awesome_simple(o, type, @inspector)
165
66
  end
166
67
 
167
- # Format a set.
168
- #------------------------------------------------------------------------------
169
- def awesome_set(s)
170
- awesome_array(s.to_a)
68
+ def awesome_rational(n)
69
+ o = n.to_s
70
+ type = :rational
71
+ awesome_simple(o, type, @inspector)
171
72
  end
172
73
 
173
- # Format a Struct.
174
- #------------------------------------------------------------------------------
175
- def awesome_struct(s)
176
- #
177
- # The code is slightly uglier because of Ruby 1.8.6 quirks:
178
- # awesome_hash(Hash[s.members.zip(s.values)]) <-- ArgumentError: odd number of arguments for Hash)
179
- # awesome_hash(Hash[*s.members.zip(s.values).flatten]) <-- s.members returns strings, not symbols.
180
- #
181
- hash = {}
182
- s.each_pair { |key, value| hash[key] = value }
183
- awesome_hash(hash)
74
+ def awesome_simple(o, type, inspector)
75
+ AwesomePrint::Formatters::SimpleFormatter.new(o, type, inspector).format
184
76
  end
185
77
 
186
- # Format Class object.
187
- #------------------------------------------------------------------------------
188
- def awesome_class(c)
189
- if superclass = c.superclass # <-- Assign and test if nil.
190
- colorize("#{c.inspect} < #{superclass}", :class)
191
- else
192
- colorize(c.inspect, :class)
193
- end
78
+ def awesome_array(a)
79
+ Formatters::ArrayFormatter.new(a, @inspector).format
194
80
  end
195
81
 
196
- # Format File object.
197
- #------------------------------------------------------------------------------
198
- def awesome_file(f)
199
- ls = File.directory?(f) ? `ls -adlF #{f.path.shellescape}` : `ls -alF #{f.path.shellescape}`
200
- colorize(ls.empty? ? f.inspect : "#{f.inspect}\n#{ls.chop}", :file)
82
+ def awesome_set(s)
83
+ Formatters::ArrayFormatter.new(s.to_a, @inspector).format
201
84
  end
202
85
 
203
- # Format Dir object.
204
- #------------------------------------------------------------------------------
205
- def awesome_dir(d)
206
- ls = `ls -alF #{d.path.shellescape}`
207
- colorize(ls.empty? ? d.inspect : "#{d.inspect}\n#{ls.chop}", :dir)
86
+ def awesome_hash(h)
87
+ Formatters::HashFormatter.new(h, @inspector).format
208
88
  end
209
89
 
210
- # Format BigDecimal object.
211
- #------------------------------------------------------------------------------
212
- def awesome_bigdecimal(n)
213
- colorize(n.to_s("F"), :bigdecimal)
90
+ def awesome_object(o)
91
+ Formatters::ObjectFormatter.new(o, o.instance_variables, @inspector).format
214
92
  end
215
93
 
216
- # Format Rational object.
217
- #------------------------------------------------------------------------------
218
- def awesome_rational(n)
219
- colorize(n.to_s, :rational)
94
+ def awesome_struct(s)
95
+ Formatters::ObjectFormatter.new(s, s.members, @inspector).format
220
96
  end
221
97
 
222
- # Format a method.
223
- #------------------------------------------------------------------------------
224
98
  def awesome_method(m)
225
- name, args, owner = method_tuple(m)
226
- "#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
99
+ Formatters::MethodFormatter.new(m, @inspector).format
227
100
  end
228
101
  alias :awesome_unboundmethod :awesome_method
229
102
 
230
- # Format object instance.
231
- #------------------------------------------------------------------------------
232
- def awesome_instance(o)
233
- "#{o.class}:0x%08x" % (o.__id__ * 2)
234
- end
235
-
236
- # Format object.methods array.
237
- #------------------------------------------------------------------------------
238
- def methods_array(a)
239
- a.sort! { |x, y| x.to_s <=> y.to_s } # Can't simply a.sort! because of o.methods << [ :blah ]
240
- object = a.instance_variable_get('@__awesome_methods__')
241
- tuples = a.map do |name|
242
- if name.is_a?(Symbol) || name.is_a?(String) # Ignore garbage, ex. 42.methods << [ :blah ]
243
- tuple = if object.respond_to?(name, true) # Is this a regular method?
244
- the_method = object.method(name) rescue nil # Avoid potential ArgumentError if object#method is overridden.
245
- if the_method && the_method.respond_to?(:arity) # Is this original object#method?
246
- method_tuple(the_method) # Yes, we are good.
247
- end
248
- elsif object.respond_to?(:instance_method) # Is this an unbound method?
249
- method_tuple(object.instance_method(name)) rescue nil # Rescue to avoid NameError when the method is not
250
- end # available (ex. File.lchmod on Ubuntu 12).
251
- end
252
- tuple || [ name.to_s, '(?)', '?' ] # Return WTF default if all the above fails.
253
- end
254
-
255
- width = (tuples.size - 1).to_s.size
256
- name_width = tuples.map { |item| item[0].size }.max || 0
257
- args_width = tuples.map { |item| item[1].size }.max || 0
258
-
259
- data = tuples.inject([]) do |arr, item|
260
- index = indent
261
- index << "[#{arr.size.to_s.rjust(width)}]" if @options[:index]
262
- indented do
263
- arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
264
- end
265
- end
266
-
267
- "[\n" << data.join("\n") << "\n#{outdent}]"
103
+ def awesome_class(c)
104
+ Formatters::ClassFormatter.new(c, @inspector).format
268
105
  end
269
106
 
270
- # Return [ name, arguments, owner ] tuple for a given method.
271
- #------------------------------------------------------------------------------
272
- def method_tuple(method)
273
- if method.respond_to?(:parameters) # Ruby 1.9.2+
274
- # See http://ruby.runpaint.org/methods#method-objects-parameters
275
- args = method.parameters.inject([]) do |arr, (type, name)|
276
- name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
277
- arr << case type
278
- when :req then name.to_s
279
- when :opt, :rest then "*#{name}"
280
- when :block then "&#{name}"
281
- else '?'
282
- end
283
- end
284
- else # See http://ruby-doc.org/core/classes/Method.html#M001902
285
- args = (1..method.arity.abs).map { |i| "arg#{i}" }
286
- args[-1] = "*#{args[-1]}" if method.arity < 0
287
- end
288
-
289
- # method.to_s formats to handle:
290
- #
291
- # #<Method: Fixnum#zero?>
292
- # #<Method: Fixnum(Integer)#years>
293
- # #<Method: User(#<Module:0x00000103207c00>)#_username>
294
- # #<Method: User(id: integer, username: string).table_name>
295
- # #<Method: User(id: integer, username: string)(ActiveRecord::Base).current>
296
- # #<UnboundMethod: Hello#world>
297
- #
298
- if method.to_s =~ /(Unbound)*Method: (.*)[#\.]/
299
- unbound, klass = $1 && '(unbound)', $2
300
- if klass && klass =~ /(\(\w+:\s.*?\))/ # Is this ActiveRecord-style class?
301
- klass.sub!($1, '') # Yes, strip the fields leaving class name only.
302
- end
303
- owner = "#{klass}#{unbound}".gsub('(', ' (')
304
- end
305
-
306
- [ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
107
+ def awesome_file(f)
108
+ Formatters::FileFormatter.new(f, @inspector).format
307
109
  end
308
110
 
309
- # Format hash keys as plain strings regardless of underlying data type.
310
- #------------------------------------------------------------------------------
311
- def plain_single_line
312
- plain, multiline = @options[:plain], @options[:multiline]
313
- @options[:plain], @options[:multiline] = true, false
314
- yield
315
- ensure
316
- @options[:plain], @options[:multiline] = plain, multiline
111
+ def awesome_dir(d)
112
+ Formatters::DirFormatter.new(d, @inspector).format
317
113
  end
318
114
 
319
115
  # Utility methods.
320
116
  #------------------------------------------------------------------------------
321
- def align(value, width)
322
- if @options[:multiline]
323
- if @options[:indent] > 0
324
- value.rjust(width)
325
- elsif @options[:indent] == 0
326
- indent + value.ljust(width)
327
- else
328
- indent[0, @indentation + @options[:indent]] + value.ljust(width)
329
- end
330
- else
331
- value
117
+ def convert_to_hash(object)
118
+ if ! object.respond_to?(:to_hash)
119
+ return nil
332
120
  end
333
- end
334
-
335
- def indented
336
- @indentation += @options[:indent].abs
337
- yield
338
- ensure
339
- @indentation -= @options[:indent].abs
340
- end
341
-
342
- def left_aligned
343
- current, @options[:indent] = @options[:indent], 0
344
- yield
345
- ensure
346
- @options[:indent] = current
347
- end
348
-
349
- def indent
350
- ' ' * @indentation
351
- end
352
121
 
353
- def outdent
354
- ' ' * (@indentation - @options[:indent].abs)
355
- end
356
-
357
- # To support limited output, for example:
358
- #
359
- # ap ('a'..'z').to_a, :limit => 3
360
- # [
361
- # [ 0] "a",
362
- # [ 1] .. [24],
363
- # [25] "z"
364
- # ]
365
- #
366
- # ap (1..100).to_a, :limit => true # Default limit is 7.
367
- # [
368
- # [ 0] 1,
369
- # [ 1] 2,
370
- # [ 2] 3,
371
- # [ 3] .. [96],
372
- # [97] 98,
373
- # [98] 99,
374
- # [99] 100
375
- # ]
376
- #------------------------------------------------------------------------------
377
- def should_be_limited?
378
- @options[:limit] == true or (@options[:limit].is_a?(Fixnum) and @options[:limit] > 0)
379
- end
380
-
381
- def get_limit_size
382
- @options[:limit] == true ? DEFAULT_LIMIT_SIZE : @options[:limit]
383
- end
384
-
385
- def limited(data, width, is_hash = false)
386
- limit = get_limit_size
387
- if data.length <= limit
388
- data
389
- else
390
- # Calculate how many elements to be displayed above and below the separator.
391
- head = limit / 2
392
- tail = head - (limit - 1) % 2
393
-
394
- # Add the proper elements to the temp array and format the separator.
395
- temp = data[0, head] + [ nil ] + data[-tail, tail]
396
-
397
- if is_hash
398
- temp[head] = "#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
399
- else
400
- temp[head] = "#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]"
401
- end
122
+ if object.method(:to_hash).arity != 0
123
+ return nil
124
+ end
402
125
 
403
- temp
126
+ hash = object.to_hash
127
+ if ! hash.respond_to?(:keys) || ! hash.respond_to?('[]')
128
+ return nil
404
129
  end
130
+
131
+ return hash
405
132
  end
406
133
  end
407
134
  end
@@ -0,0 +1,73 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module AwesomePrint
4
+ module Formatters
5
+ class ArrayFormatter < BaseFormatter
6
+
7
+ attr_reader :array, :inspector, :options
8
+
9
+ def initialize(array, inspector)
10
+ @array = array
11
+ @inspector = inspector
12
+ @options = inspector.options
13
+ end
14
+
15
+ def format
16
+ return "[]" if array == []
17
+
18
+ if array.instance_variable_defined?('@__awesome_methods__')
19
+ methods_array(array)
20
+ elsif options[:multiline]
21
+ width = (array.size - 1).to_s.size
22
+
23
+ data = array.inject([]) do |arr, item|
24
+ index = indent
25
+ index << colorize("[#{arr.size.to_s.rjust(width)}] ", :array) if options[:index]
26
+ indented do
27
+ arr << (index << inspector.awesome(item))
28
+ end
29
+ end
30
+
31
+ data = limited(data, width) if should_be_limited?
32
+ "[\n" << data.join(",\n") << "\n#{outdent}]"
33
+ else
34
+ "[ " << array.map{ |item| inspector.awesome(item) }.join(", ") << " ]"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def methods_array(a)
41
+ a.sort! { |x, y| x.to_s <=> y.to_s } # Can't simply a.sort! because of o.methods << [ :blah ]
42
+ object = a.instance_variable_get('@__awesome_methods__')
43
+ tuples = a.map do |name|
44
+ if name.is_a?(Symbol) || name.is_a?(String) # Ignore garbage, ex. 42.methods << [ :blah ]
45
+ tuple = if object.respond_to?(name, true) # Is this a regular method?
46
+ the_method = object.method(name) rescue nil # Avoid potential ArgumentError if object#method is overridden.
47
+ if the_method && the_method.respond_to?(:arity) # Is this original object#method?
48
+ method_tuple(the_method) # Yes, we are good.
49
+ end
50
+ elsif object.respond_to?(:instance_method) # Is this an unbound method?
51
+ method_tuple(object.instance_method(name)) rescue nil # Rescue to avoid NameError when the method is not
52
+ end # available (ex. File.lchmod on Ubuntu 12).
53
+ end
54
+ tuple || [ name.to_s, '(?)', '?' ] # Return WTF default if all the above fails.
55
+ end
56
+
57
+ width = (tuples.size - 1).to_s.size
58
+ name_width = tuples.map { |item| item[0].size }.max || 0
59
+ args_width = tuples.map { |item| item[1].size }.max || 0
60
+
61
+ data = tuples.inject([]) do |arr, item|
62
+ index = indent
63
+ index << "[#{arr.size.to_s.rjust(width)}]" if @options[:index]
64
+ indented do
65
+ arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
66
+ end
67
+ end
68
+
69
+ "[\n" << data.join("\n") << "\n#{outdent}]"
70
+ end
71
+ end
72
+ end
73
+ end