awesome_print 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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