ryansch-awesome_print 1.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ class String
7
+ #
8
+ # ANSI color codes:
9
+ # \e => escape
10
+ # 30 => color base
11
+ # 1 => bright
12
+ # 0 => normal
13
+ #
14
+ # For HTML coloring we use <kbd> tag instead of <span> to require monospace
15
+ # font. Note that beloved <tt> has been removed from HTML5.
16
+ #
17
+ %w(gray red green yellow blue purple cyan white).zip(
18
+ %w(black darkred darkgreen brown navy darkmagenta darkcyan slategray)).each_with_index do |(color, shade), i|
19
+ define_method color do |*html|
20
+ html[0] ? %Q|<kbd style="color:#{color}">#{self}</kbd>| : "\e[1;#{30+i}m#{self}\e[0m"
21
+ end
22
+
23
+ define_method "#{color}ish" do |*html|
24
+ html[0] ? %Q|<kbd style="color:#{shade}">#{self}</kbd>| : "\e[0;#{30+i}m#{self}\e[0m"
25
+ end
26
+ end
27
+
28
+ alias :black :grayish
29
+ alias :pale :whiteish
30
+
31
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module ActionView
8
+
9
+ # Use HTML colors and add default "debug_dump" class to the resulting HTML.
10
+ def ap_debug(object, options = {})
11
+ object.ai(options.merge(:html => true)).sub(/^<pre([\s>])/, '<pre class="debug_dump"\\1')
12
+ end
13
+
14
+ alias_method :ap, :ap_debug
15
+ end
16
+ end
17
+
18
+ ActionView::Base.send(:include, AwesomePrint::ActionView)
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module ActiveRecord
8
+
9
+ def self.included(base)
10
+ base.send :alias_method, :cast_without_active_record, :cast
11
+ base.send :alias_method, :cast, :cast_with_active_record
12
+ end
13
+
14
+ # Add ActiveRecord class names to the dispatcher pipeline.
15
+ #------------------------------------------------------------------------------
16
+ def cast_with_active_record(object, type)
17
+ cast = cast_without_active_record(object, type)
18
+ return cast if !defined?(::ActiveRecord)
19
+
20
+ if object.is_a?(::ActiveRecord::Base)
21
+ cast = :active_record_instance
22
+ elsif object.is_a?(Class) && object.ancestors.include?(::ActiveRecord::Base)
23
+ cast = :active_record_class
24
+ end
25
+ cast
26
+ end
27
+
28
+ private
29
+
30
+ # Format ActiveRecord instance object.
31
+ #
32
+ # NOTE: by default only instance attributes (i.e. columns) are shown. To format
33
+ # ActiveRecord instance as regular object showing its instance variables and
34
+ # accessors use :raw => true option:
35
+ #
36
+ # ap record, :raw => true
37
+ #
38
+ #------------------------------------------------------------------------------
39
+ def awesome_active_record_instance(object)
40
+ return object.inspect if !defined?(::ActiveSupport::OrderedHash)
41
+ return awesome_object(object) if @options[:raw]
42
+
43
+ data = object.class.column_names.inject(::ActiveSupport::OrderedHash.new) do |hash, name|
44
+ hash[name.to_sym] = object.send(name) if object.has_attribute?(name) || object.new_record?
45
+ hash
46
+ end
47
+ "#{object} " << awesome_hash(data)
48
+ end
49
+
50
+ # Format ActiveRecord class object.
51
+ #------------------------------------------------------------------------------
52
+ def awesome_active_record_class(object)
53
+ return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:columns) || object.to_s == "ActiveRecord::Base"
54
+
55
+ data = object.columns.inject(::ActiveSupport::OrderedHash.new) do |hash, c|
56
+ hash[c.name.to_sym] = c.type
57
+ hash
58
+ end
59
+ "class #{object} < #{object.superclass} " << awesome_hash(data)
60
+ end
61
+ end
62
+ end
63
+
64
+ AwesomePrint::Formatter.send(:include, AwesomePrint::ActiveRecord)
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module ActiveSupport
8
+
9
+ def self.included(base)
10
+ base.send :alias_method, :cast_without_active_support, :cast
11
+ base.send :alias_method, :cast, :cast_with_active_support
12
+ end
13
+
14
+ def cast_with_active_support(object, type)
15
+ cast = cast_without_active_support(object, type)
16
+ if defined?(::ActiveSupport) && defined?(::HashWithIndifferentAccess)
17
+ if object.is_a?(::ActiveSupport::TimeWithZone) || object.is_a?(::Date)
18
+ cast = :active_support_time
19
+ elsif object.is_a?(::HashWithIndifferentAccess)
20
+ cast = :hash_with_indifferent_access
21
+ end
22
+ end
23
+ cast
24
+ end
25
+
26
+ # Format ActiveSupport::TimeWithZone as standard Time.
27
+ #------------------------------------------------------------------------------
28
+ def awesome_active_support_time(object)
29
+ colorize(object.inspect, :time)
30
+ end
31
+
32
+ # Format HashWithIndifferentAccess as standard Hash.
33
+ #------------------------------------------------------------------------------
34
+ def awesome_hash_with_indifferent_access(object)
35
+ awesome_hash(object)
36
+ end
37
+ end
38
+ end
39
+
40
+ AwesomePrint::Formatter.send(:include, AwesomePrint::ActiveSupport)
41
+ #
42
+ # Colorize Rails logs.
43
+ #
44
+ if defined?(ActiveSupport::LogSubscriber)
45
+ AwesomePrint.force_colors! ActiveSupport::LogSubscriber.colorize_logging
46
+ end
47
+
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module MongoMapper
8
+
9
+ def self.included(base)
10
+ base.send :alias_method, :cast_without_mongo_mapper, :cast
11
+ base.send :alias_method, :cast, :cast_with_mongo_mapper
12
+ end
13
+
14
+ # Add MongoMapper class names to the dispatcher pipeline.
15
+ #------------------------------------------------------------------------------
16
+ def cast_with_mongo_mapper(object, type)
17
+ cast = cast_without_mongo_mapper(object, type)
18
+ if defined?(::MongoMapper::Document) && object.is_a?(Class) && (object.ancestors & [ ::MongoMapper::Document, ::MongoMapper::EmbeddedDocument ]).size > 0
19
+ cast = :mongo_mapper_class
20
+ end
21
+ cast
22
+ end
23
+
24
+ # Format MongoMapper class object.
25
+ #------------------------------------------------------------------------------
26
+ def awesome_mongo_mapper_class(object)
27
+ return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:keys)
28
+
29
+ data = object.keys.sort.inject(::ActiveSupport::OrderedHash.new) do |hash, c|
30
+ hash[c.first] = (c.last.type || "undefined").to_s.underscore.intern
31
+ hash
32
+ end
33
+ "class #{object} < #{object.superclass} " << awesome_hash(data)
34
+ end
35
+ end
36
+ end
37
+
38
+ AwesomePrint::Formatter.send(:include, AwesomePrint::MongoMapper)
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module Mongoid
8
+
9
+ def self.included(base)
10
+ base.send :alias_method, :cast_without_mongoid, :cast
11
+ base.send :alias_method, :cast, :cast_with_mongoid
12
+ end
13
+
14
+ # Add Mongoid class names to the dispatcher pipeline.
15
+ #------------------------------------------------------------------------------
16
+ def cast_with_mongoid(object, type)
17
+ cast = cast_without_mongoid(object, type)
18
+ if defined?(::Mongoid::Document)
19
+ if object.is_a?(Class) && object.ancestors.include?(::Mongoid::Document)
20
+ cast = :mongoid_class
21
+ elsif object.class.ancestors.include?(::Mongoid::Document)
22
+ cast = :mongoid_document
23
+ elsif object.is_a?(::BSON::ObjectId)
24
+ cast = :mongoid_bson_id
25
+ end
26
+ end
27
+ cast
28
+ end
29
+
30
+ # Format Mongoid class object.
31
+ #------------------------------------------------------------------------------
32
+ def awesome_mongoid_class(object)
33
+ return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:fields)
34
+
35
+ data = object.fields.sort_by { |key| key }.inject(::ActiveSupport::OrderedHash.new) do |hash, c|
36
+ hash[c[1].name.to_sym] = (c[1].type || "undefined").to_s.underscore.intern
37
+ hash
38
+ end
39
+ "class #{object} < #{object.superclass} " << awesome_hash(data)
40
+ end
41
+
42
+ # Format Mongoid Document object.
43
+ #------------------------------------------------------------------------------
44
+ def awesome_mongoid_document(object)
45
+ return object.inspect if !defined?(::ActiveSupport::OrderedHash)
46
+
47
+ data = object.attributes.sort_by { |key| key }.inject(::ActiveSupport::OrderedHash.new) do |hash, c|
48
+ hash[c[0].to_sym] = c[1]
49
+ hash
50
+ end
51
+ if !object.errors.empty?
52
+ data = {:errors => object.errors, :attributes => data}
53
+ end
54
+ "#{object} #{awesome_hash(data)}"
55
+ end
56
+
57
+ # Format BSON::ObjectId
58
+ #------------------------------------------------------------------------------
59
+ def awesome_mongoid_bson_id(object)
60
+ object.inspect
61
+ end
62
+ end
63
+ end
64
+
65
+ AwesomePrint::Formatter.send(:include, AwesomePrint::Mongoid)
@@ -0,0 +1,45 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module AwesomePrint
7
+ module Nokogiri
8
+
9
+ def self.included(base)
10
+ base.send :alias_method, :cast_without_nokogiri, :cast
11
+ base.send :alias_method, :cast, :cast_with_nokogiri
12
+ end
13
+
14
+ # Add Nokogiri XML Node and NodeSet names to the dispatcher pipeline.
15
+ #------------------------------------------------------------------------------
16
+ def cast_with_nokogiri(object, type)
17
+ cast = cast_without_nokogiri(object, type)
18
+ if (defined?(::Nokogiri::XML::Node) && object.is_a?(::Nokogiri::XML::Node)) ||
19
+ (defined?(::Nokogiri::XML::NodeSet) && object.is_a?(::Nokogiri::XML::NodeSet))
20
+ cast = :nokogiri_xml_node
21
+ end
22
+ cast
23
+ end
24
+
25
+ #------------------------------------------------------------------------------
26
+ def awesome_nokogiri_xml_node(object)
27
+ if object.is_a?(::Nokogiri::XML::NodeSet) && object.empty?
28
+ return "[]"
29
+ end
30
+ xml = object.to_xml(:indent => 2)
31
+ #
32
+ # Colorize tag, id/class name, and contents.
33
+ #
34
+ xml.gsub!(/(<)(\/?[A-Za-z1-9]+)/) { |tag| "#{$1}#{colorize($2, :keyword)}" }
35
+ xml.gsub!(/(id|class)="[^"]+"/i) { |id| colorize(id, :class) }
36
+ xml.gsub!(/>([^<]+)</) do |contents|
37
+ contents = colorize($1, :trueclass) if contents && !contents.empty?
38
+ ">#{contents}<"
39
+ end
40
+ xml
41
+ end
42
+ end
43
+ end
44
+
45
+ AwesomePrint::Formatter.send(:include, AwesomePrint::Nokogiri)
@@ -0,0 +1,384 @@
1
+ # Copyright (c) 2010-2011 Michael Dvorkin
2
+ #
3
+ # Awesome Print is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require "cgi"
7
+ require "shellwords"
8
+
9
+ module AwesomePrint
10
+ class Formatter
11
+
12
+ CORE = [ :array, :hash, :class, :file, :dir, :bigdecimal, :rational, :struct, :method, :unboundmethod ]
13
+ DEFAULT_LIMIT_SIZE = 7
14
+
15
+ def initialize(inspector)
16
+ @inspector = inspector
17
+ @options = inspector.options
18
+ @indentation = @options[:indent].abs
19
+ end
20
+
21
+ # Main entry point to format an object.
22
+ #------------------------------------------------------------------------------
23
+ def format(object, type = nil)
24
+ core_class = cast(object, type)
25
+ awesome = if core_class != :self
26
+ send(:"awesome_#{core_class}", object) # Core formatters.
27
+ else
28
+ awesome_self(object, type) # Catch all that falls back to object.inspect.
29
+ end
30
+ @options[:html] ? "<pre>#{awesome}</pre>" : awesome
31
+ end
32
+
33
+ # Hook this when adding custom formatters. Check out lib/awesome_print/ext
34
+ # directory for custom formatters that ship with awesome_print.
35
+ #------------------------------------------------------------------------------
36
+ def cast(object, type)
37
+ CORE.grep(type)[0] || :self
38
+ end
39
+
40
+ # Pick the color and apply it to the given string as necessary.
41
+ #------------------------------------------------------------------------------
42
+ def colorize(s, type)
43
+ s = CGI.escapeHTML(s) if @options[:html]
44
+ if @options[:plain] || !@options[:color][type] || !@inspector.colorize?
45
+ s
46
+ else
47
+ s.send(@options[:color][type], @options[:html])
48
+ end
49
+ end
50
+
51
+
52
+ private
53
+
54
+ # Catch all method to format an arbitrary object.
55
+ #------------------------------------------------------------------------------
56
+ def awesome_self(object, type)
57
+ return awesome_object(object) if object.instance_variables.any?
58
+ colorize(object.inspect.to_s, type)
59
+ end
60
+
61
+ # Format an array.
62
+ #------------------------------------------------------------------------------
63
+ def awesome_array(a)
64
+ return "[]" if a == []
65
+
66
+ if a.instance_variable_defined?('@__awesome_methods__')
67
+ methods_array(a)
68
+ elsif @options[:multiline]
69
+ width = (a.size - 1).to_s.size
70
+
71
+ data = a.inject([]) do |arr, item|
72
+ index = indent
73
+ index << colorize("[#{arr.size.to_s.rjust(width)}] ", :array) if @options[:index]
74
+ indented do
75
+ arr << (index << @inspector.awesome(item))
76
+ end
77
+ end
78
+
79
+ data = limited(data, width) if should_be_limited?
80
+ "[\n" << data.join(",\n") << "\n#{outdent}]"
81
+ else
82
+ "[ " << a.map{ |item| @inspector.awesome(item) }.join(", ") << " ]"
83
+ end
84
+ end
85
+
86
+ # Format a hash. If @options[:indent] if negative left align hash keys.
87
+ #------------------------------------------------------------------------------
88
+ def awesome_hash(h)
89
+ return "{}" if h == {}
90
+
91
+ keys = @options[:sort_keys] ? h.keys.sort { |a, b| a.to_s <=> b.to_s } : h.keys
92
+ data = keys.map do |key|
93
+ plain_single_line do
94
+ [ @inspector.awesome(key), h[key] ]
95
+ end
96
+ end
97
+
98
+ width = data.map { |key, | key.size }.max || 0
99
+ width += @indentation if @options[:indent] > 0
100
+
101
+ data = data.map do |key, value|
102
+ indented do
103
+ align(key, width) << colorize(" => ", :hash) << @inspector.awesome(value)
104
+ end
105
+ end
106
+
107
+ data = limited(data, width, :hash => true) if should_be_limited?
108
+ if @options[:multiline]
109
+ "{\n" << data.join(",\n") << "\n#{outdent}}"
110
+ else
111
+ "{ #{data.join(', ')} }"
112
+ end
113
+ end
114
+
115
+ # Format an object.
116
+ #------------------------------------------------------------------------------
117
+ def awesome_object(o)
118
+ vars = o.instance_variables.map do |var|
119
+ property = var[1..-1].to_sym
120
+ accessor = if o.respond_to?(:"#{property}=")
121
+ o.respond_to?(property) ? :accessor : :writer
122
+ else
123
+ o.respond_to?(property) ? :reader : nil
124
+ end
125
+ if accessor
126
+ [ "attr_#{accessor} :#{property}", var ]
127
+ else
128
+ [ var.to_s, var ]
129
+ end
130
+ end
131
+
132
+ data = vars.sort.map do |declaration, var|
133
+ key = left_aligned do
134
+ align(declaration, declaration.size)
135
+ end
136
+
137
+ unless @options[:plain]
138
+ if key =~ /(@\w+)/
139
+ key.sub!($1, colorize($1, :variable))
140
+ else
141
+ key.sub!(/(attr_\w+)\s(\:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}")
142
+ end
143
+ end
144
+ indented do
145
+ key << colorize(" = ", :hash) + @inspector.awesome(o.instance_variable_get(var))
146
+ end
147
+ end
148
+ if @options[:multiline]
149
+ "#<#{awesome_instance(o)}\n#{data.join(%Q/,\n/)}\n#{outdent}>"
150
+ else
151
+ "#<#{awesome_instance(o)} #{data.join(', ')}>"
152
+ end
153
+ end
154
+
155
+ # Format a Struct.
156
+ #------------------------------------------------------------------------------
157
+ def awesome_struct(s)
158
+ #
159
+ # The code is slightly uglier because of Ruby 1.8.6 quirks:
160
+ # awesome_hash(Hash[s.members.zip(s.values)]) <-- ArgumentError: odd number of arguments for Hash)
161
+ # awesome_hash(Hash[*s.members.zip(s.values).flatten]) <-- s.members returns strings, not symbols.
162
+ #
163
+ hash = {}
164
+ s.each_pair { |key, value| hash[key] = value }
165
+ awesome_hash(hash)
166
+ end
167
+
168
+ # Format Class object.
169
+ #------------------------------------------------------------------------------
170
+ def awesome_class(c)
171
+ if superclass = c.superclass # <-- Assign and test if nil.
172
+ colorize("#{c.inspect} < #{superclass}", :class)
173
+ else
174
+ colorize(c.inspect, :class)
175
+ end
176
+ end
177
+
178
+ # Format File object.
179
+ #------------------------------------------------------------------------------
180
+ def awesome_file(f)
181
+ ls = File.directory?(f) ? `ls -adlF #{f.path.shellescape}` : `ls -alF #{f.path.shellescape}`
182
+ colorize(ls.empty? ? f.inspect : "#{f.inspect}\n#{ls.chop}", :file)
183
+ end
184
+
185
+ # Format Dir object.
186
+ #------------------------------------------------------------------------------
187
+ def awesome_dir(d)
188
+ ls = `ls -alF #{d.path.shellescape}`
189
+ colorize(ls.empty? ? d.inspect : "#{d.inspect}\n#{ls.chop}", :dir)
190
+ end
191
+
192
+ # Format BigDecimal and Rational objects by convering them to Float.
193
+ #------------------------------------------------------------------------------
194
+ def awesome_bigdecimal(n)
195
+ colorize(n.to_f.inspect, :bigdecimal)
196
+ end
197
+ alias :awesome_rational :awesome_bigdecimal
198
+
199
+ # Format a method.
200
+ #------------------------------------------------------------------------------
201
+ def awesome_method(m)
202
+ name, args, owner = method_tuple(m)
203
+ "#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
204
+ end
205
+ alias :awesome_unboundmethod :awesome_method
206
+
207
+ # Format object instance.
208
+ #------------------------------------------------------------------------------
209
+ def awesome_instance(o)
210
+ "#{o.class}:0x%08x" % (o.__id__ * 2)
211
+ end
212
+
213
+ # Format object.methods array.
214
+ #------------------------------------------------------------------------------
215
+ def methods_array(a)
216
+ a.sort! { |x, y| x.to_s <=> y.to_s } # Can't simply a.sort! because of o.methods << [ :blah ]
217
+ object = a.instance_variable_get('@__awesome_methods__')
218
+ tuples = a.map do |name|
219
+ if name.is_a?(Symbol) || name.is_a?(String) # Ignore garbage, ex. 42.methods << [ :blah ]
220
+ tuple = if object.respond_to?(name, true) # Is this a regular method?
221
+ the_method = object.method(name) rescue nil # Avoid potential ArgumentError if object#method is overridden.
222
+ if the_method && the_method.respond_to?(:arity) # Is this original object#method?
223
+ method_tuple(the_method) # Yes, we are good.
224
+ end
225
+ elsif object.respond_to?(:instance_method) # Is this an unbound method?
226
+ method_tuple(object.instance_method(name))
227
+ end
228
+ end
229
+ tuple || [ name.to_s, '(?)', '?' ] # Return WTF default if all the above fails.
230
+ end
231
+
232
+ width = (tuples.size - 1).to_s.size
233
+ name_width = tuples.map { |item| item[0].size }.max || 0
234
+ args_width = tuples.map { |item| item[1].size }.max || 0
235
+
236
+ data = tuples.inject([]) do |arr, item|
237
+ index = indent
238
+ index << "[#{arr.size.to_s.rjust(width)}]" if @options[:index]
239
+ indented do
240
+ arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
241
+ end
242
+ end
243
+
244
+ "[\n" << data.join("\n") << "\n#{outdent}]"
245
+ end
246
+
247
+ # Return [ name, arguments, owner ] tuple for a given method.
248
+ #------------------------------------------------------------------------------
249
+ def method_tuple(method)
250
+ if method.respond_to?(:parameters) # Ruby 1.9.2+
251
+ # See http://ruby.runpaint.org/methods#method-objects-parameters
252
+ args = method.parameters.inject([]) do |arr, (type, name)|
253
+ name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
254
+ arr << case type
255
+ when :req then name.to_s
256
+ when :opt, :rest then "*#{name}"
257
+ when :block then "&#{name}"
258
+ else '?'
259
+ end
260
+ end
261
+ else # See http://ruby-doc.org/core/classes/Method.html#M001902
262
+ args = (1..method.arity.abs).map { |i| "arg#{i}" }
263
+ args[-1] = "*#{args[-1]}" if method.arity < 0
264
+ end
265
+
266
+ # method.to_s formats to handle:
267
+ #
268
+ # #<Method: Fixnum#zero?>
269
+ # #<Method: Fixnum(Integer)#years>
270
+ # #<Method: User(#<Module:0x00000103207c00>)#_username>
271
+ # #<Method: User(id: integer, username: string).table_name>
272
+ # #<Method: User(id: integer, username: string)(ActiveRecord::Base).current>
273
+ # #<UnboundMethod: Hello#world>
274
+ #
275
+ if method.to_s =~ /(Unbound)*Method: (.*)[#\.]/
276
+ unbound, klass = $1 && '(unbound)', $2
277
+ if klass && klass =~ /(\(\w+:\s.*?\))/ # Is this ActiveRecord-style class?
278
+ klass.sub!($1, '') # Yes, strip the fields leaving class name only.
279
+ end
280
+ owner = "#{klass}#{unbound}".gsub('(', ' (')
281
+ end
282
+
283
+ [ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
284
+ end
285
+
286
+ # Format hash keys as plain strings regardless of underlying data type.
287
+ #------------------------------------------------------------------------------
288
+ def plain_single_line
289
+ plain, multiline = @options[:plain], @options[:multiline]
290
+ @options[:plain], @options[:multiline] = true, false
291
+ yield
292
+ ensure
293
+ @options[:plain], @options[:multiline] = plain, multiline
294
+ end
295
+
296
+ # Utility methods.
297
+ #------------------------------------------------------------------------------
298
+ def align(value, width)
299
+ if @options[:multiline]
300
+ if @options[:indent] > 0
301
+ value.rjust(width)
302
+ elsif @options[:indent] == 0
303
+ indent + value.ljust(width)
304
+ else
305
+ indent[0, @indentation + @options[:indent]] + value.ljust(width)
306
+ end
307
+ else
308
+ value
309
+ end
310
+ end
311
+
312
+ def indented
313
+ @indentation += @options[:indent].abs
314
+ yield
315
+ ensure
316
+ @indentation -= @options[:indent].abs
317
+ end
318
+
319
+ def left_aligned
320
+ current, @options[:indent] = @options[:indent], 0
321
+ yield
322
+ ensure
323
+ @options[:indent] = current
324
+ end
325
+
326
+ def indent
327
+ ' ' * @indentation
328
+ end
329
+
330
+ def outdent
331
+ ' ' * (@indentation - @options[:indent].abs)
332
+ end
333
+
334
+ # To support limited output, for example:
335
+ #
336
+ # ap ('a'..'z').to_a, :limit => 3
337
+ # [
338
+ # [ 0] "a",
339
+ # [ 1] .. [24],
340
+ # [25] "z"
341
+ # ]
342
+ #
343
+ # ap (1..100).to_a, :limit => true # Default limit is 7.
344
+ # [
345
+ # [ 0] 1,
346
+ # [ 1] 2,
347
+ # [ 2] 3,
348
+ # [ 3] .. [96],
349
+ # [97] 98,
350
+ # [98] 99,
351
+ # [99] 100
352
+ # ]
353
+ #------------------------------------------------------------------------------
354
+ def should_be_limited?
355
+ @options[:limit] == true or (@options[:limit].is_a?(Fixnum) and @options[:limit] > 0)
356
+ end
357
+
358
+ def get_limit_size
359
+ @options[:limit] == true ? DEFAULT_LIMIT_SIZE : @options[:limit]
360
+ end
361
+
362
+ def limited(data, width, is_hash = false)
363
+ limit = get_limit_size
364
+ if data.length <= limit
365
+ data
366
+ else
367
+ # Calculate how many elements to be displayed above and below the separator.
368
+ head = limit / 2
369
+ tail = head - (limit - 1) % 2
370
+
371
+ # Add the proper elements to the temp array and format the separator.
372
+ temp = data[0, head] + [ nil ] + data[-tail, tail]
373
+
374
+ if is_hash
375
+ temp[head] = "#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
376
+ else
377
+ temp[head] = "#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]"
378
+ end
379
+
380
+ temp
381
+ end
382
+ end
383
+ end
384
+ end