ryansch-awesome_print 1.0.2.1
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.
- data/.gitignore +22 -0
- data/CHANGELOG +77 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +22 -0
- data/README.md +339 -0
- data/Rakefile +2 -0
- data/lib/ap.rb +10 -0
- data/lib/awesome_print/core_ext/array.rb +71 -0
- data/lib/awesome_print/core_ext/class.rb +22 -0
- data/lib/awesome_print/core_ext/kernel.rb +21 -0
- data/lib/awesome_print/core_ext/logger.rb +20 -0
- data/lib/awesome_print/core_ext/method.rb +21 -0
- data/lib/awesome_print/core_ext/object.rb +22 -0
- data/lib/awesome_print/core_ext/string.rb +31 -0
- data/lib/awesome_print/ext/action_view.rb +18 -0
- data/lib/awesome_print/ext/active_record.rb +64 -0
- data/lib/awesome_print/ext/active_support.rb +47 -0
- data/lib/awesome_print/ext/mongo_mapper.rb +38 -0
- data/lib/awesome_print/ext/mongoid.rb +65 -0
- data/lib/awesome_print/ext/nokogiri.rb +45 -0
- data/lib/awesome_print/formatter.rb +384 -0
- data/lib/awesome_print/inspector.rb +140 -0
- data/lib/awesome_print/version.rb +10 -0
- data/lib/awesome_print.rb +30 -0
- data/spec/colors_spec.rb +106 -0
- data/spec/formats_spec.rb +730 -0
- data/spec/methods_spec.rb +458 -0
- data/spec/objects_spec.rb +79 -0
- data/spec/spec_helper.rb +51 -0
- metadata +133 -0
@@ -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
|