mirror of
https://github.com/awesome-print/awesome_print
synced 2023-03-27 23:22:34 -04:00
Merge pull request #237 from waldyr/refactor/add-formatters
Refactoring - Add formatters
This commit is contained in:
commit
f3eea33763
13 changed files with 562 additions and 351 deletions
24
lib/awesome_print/colorize.rb
Normal file
24
lib/awesome_print/colorize.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
autoload :CGI, "cgi"
|
||||
|
||||
module AwesomePrint
|
||||
module Colorize
|
||||
|
||||
# Pick the color and apply it to the given string as necessary.
|
||||
#------------------------------------------------------------------------------
|
||||
def colorize(str, type)
|
||||
str = CGI.escapeHTML(str) if options[:html]
|
||||
if options[:plain] || !options[:color][type] || !inspector.colorize?
|
||||
str
|
||||
#
|
||||
# Check if the string color method is defined by awesome_print and accepts
|
||||
# html parameter or it has been overriden by some gem such as colorize.
|
||||
#
|
||||
elsif str.method(options[:color][type]).arity == -1 # Accepts html parameter.
|
||||
str.send(options[:color][type], options[:html])
|
||||
else
|
||||
str = %Q|<kbd style="color:#{options[:color][type]}">#{str}</kbd>| if options[:html]
|
||||
str.send(options[:color][type])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,19 +3,27 @@
|
|||
# Awesome Print is freely distributable under the terms of MIT license.
|
||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||
#------------------------------------------------------------------------------
|
||||
autoload :CGI, "cgi"
|
||||
require "shellwords"
|
||||
require_relative "formatters/object_formatter"
|
||||
require_relative "formatters/hash_formatter"
|
||||
require_relative "formatters/array_formatter"
|
||||
require_relative "formatters/simple_formatter"
|
||||
require_relative "formatters/method_formatter"
|
||||
require_relative "formatters/class_formatter"
|
||||
require_relative "formatters/dir_formatter"
|
||||
require_relative "formatters/file_formatter"
|
||||
require_relative "colorize"
|
||||
|
||||
module AwesomePrint
|
||||
class Formatter
|
||||
include Colorize
|
||||
|
||||
attr_reader :inspector, :options
|
||||
|
||||
CORE = [ :array, :bigdecimal, :class, :dir, :file, :hash, :method, :rational, :set, :struct, :unboundmethod ]
|
||||
DEFAULT_LIMIT_SIZE = 7
|
||||
|
||||
def initialize(inspector)
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
@indentation = @options[:indent].abs
|
||||
end
|
||||
|
||||
# Main entry point to format an object.
|
||||
|
@ -37,292 +45,71 @@ module AwesomePrint
|
|||
CORE.grep(type)[0] || :self
|
||||
end
|
||||
|
||||
# Pick the color and apply it to the given string as necessary.
|
||||
#------------------------------------------------------------------------------
|
||||
def colorize(str, type)
|
||||
str = CGI.escapeHTML(str) if @options[:html]
|
||||
if @options[:plain] || !@options[:color][type] || !@inspector.colorize?
|
||||
str
|
||||
#
|
||||
# Check if the string color method is defined by awesome_print and accepts
|
||||
# html parameter or it has been overriden by some gem such as colorize.
|
||||
#
|
||||
elsif str.method(@options[:color][type]).arity == -1 # Accepts html parameter.
|
||||
str.send(@options[:color][type], @options[:html])
|
||||
else
|
||||
str = %Q|<kbd style="color:#{@options[:color][type]}">#{str}</kbd>| if @options[:html]
|
||||
str.send(@options[:color][type])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
# Check whether a variable_name is a method or ivar
|
||||
#------------------------------------------------------------------------------
|
||||
def valid_instance_var?(variable_name)
|
||||
variable_name.to_s.start_with?('@')
|
||||
end
|
||||
|
||||
# Catch all method to format an arbitrary object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_self(object, type)
|
||||
if @options[:raw] && object.instance_variables.any?
|
||||
return awesome_object(object)
|
||||
awesome_object(object)
|
||||
elsif hash = convert_to_hash(object)
|
||||
awesome_hash(hash)
|
||||
else
|
||||
colorize(object.inspect.to_s, type)
|
||||
awesome_simple(object.inspect.to_s, type, @inspector)
|
||||
end
|
||||
end
|
||||
|
||||
# Format an array.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_array(a)
|
||||
return "[]" if a == []
|
||||
|
||||
if a.instance_variable_defined?('@__awesome_methods__')
|
||||
methods_array(a)
|
||||
elsif @options[:multiline]
|
||||
width = (a.size - 1).to_s.size
|
||||
|
||||
data = a.inject([]) do |arr, item|
|
||||
index = indent
|
||||
index << colorize("[#{arr.size.to_s.rjust(width)}] ", :array) if @options[:index]
|
||||
indented do
|
||||
arr << (index << @inspector.awesome(item))
|
||||
end
|
||||
end
|
||||
|
||||
data = limited(data, width) if should_be_limited?
|
||||
"[\n" << data.join(",\n") << "\n#{outdent}]"
|
||||
else
|
||||
"[ " << a.map{ |item| @inspector.awesome(item) }.join(", ") << " ]"
|
||||
end
|
||||
end
|
||||
|
||||
# Format a hash. If @options[:indent] if negative left align hash keys.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_hash(h)
|
||||
return "{}" if h == {}
|
||||
|
||||
keys = @options[:sort_keys] ? h.keys.sort { |a, b| a.to_s <=> b.to_s } : h.keys
|
||||
data = keys.map do |key|
|
||||
plain_single_line do
|
||||
[ @inspector.awesome(key), h[key] ]
|
||||
end
|
||||
end
|
||||
|
||||
width = data.map { |key, | key.size }.max || 0
|
||||
width += @indentation if @options[:indent] > 0
|
||||
|
||||
data = data.map do |key, value|
|
||||
indented do
|
||||
align(key, width) << colorize(" => ", :hash) << @inspector.awesome(value)
|
||||
end
|
||||
end
|
||||
|
||||
data = limited(data, width, :hash => true) if should_be_limited?
|
||||
if @options[:multiline]
|
||||
"{\n" << data.join(",\n") << "\n#{outdent}}"
|
||||
else
|
||||
"{ #{data.join(', ')} }"
|
||||
end
|
||||
end
|
||||
|
||||
# Format an object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_object(o)
|
||||
awesome_object_data(o, o.instance_variables)
|
||||
end
|
||||
|
||||
def awesome_object_data(o, variables)
|
||||
vars = variables.map do |var|
|
||||
property = var.to_s[1..-1].to_sym # to_s because of some monkey patching done by Puppet.
|
||||
accessor = if o.respond_to?(:"#{property}=")
|
||||
o.respond_to?(property) ? :accessor : :writer
|
||||
else
|
||||
o.respond_to?(property) ? :reader : nil
|
||||
end
|
||||
if accessor
|
||||
[ "attr_#{accessor} :#{property}", var ]
|
||||
else
|
||||
[ var.to_s, var ]
|
||||
end
|
||||
end
|
||||
|
||||
data = vars.sort.map do |declaration, var|
|
||||
key = left_aligned do
|
||||
align(declaration, declaration.size)
|
||||
end
|
||||
|
||||
unless @options[:plain]
|
||||
if key =~ /(@\w+)/
|
||||
key.sub!($1, colorize($1, :variable))
|
||||
else
|
||||
key.sub!(/(attr_\w+)\s(\:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}")
|
||||
end
|
||||
end
|
||||
indented do
|
||||
var_contents = if valid_instance_var?(var)
|
||||
o.instance_variable_get(var)
|
||||
else
|
||||
o.send(var) # Enables handling of Struct attributes
|
||||
end
|
||||
|
||||
key << colorize(" = ", :hash) + @inspector.awesome(var_contents)
|
||||
end
|
||||
end
|
||||
if @options[:multiline]
|
||||
"#<#{awesome_instance(o)}\n#{data.join(%Q/,\n/)}\n#{outdent}>"
|
||||
else
|
||||
"#<#{awesome_instance(o)} #{data.join(', ')}>"
|
||||
end
|
||||
end
|
||||
|
||||
# Format a set.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_set(s)
|
||||
awesome_array(s.to_a)
|
||||
end
|
||||
|
||||
# Format a Struct.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_struct(s)
|
||||
awesome_object_data(s, s.members)
|
||||
end
|
||||
|
||||
# Format Class object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_class(c)
|
||||
if superclass = c.superclass # <-- Assign and test if nil.
|
||||
colorize("#{c.inspect} < #{superclass}", :class)
|
||||
else
|
||||
colorize(c.inspect, :class)
|
||||
end
|
||||
end
|
||||
|
||||
# Format File object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_file(f)
|
||||
ls = File.directory?(f) ? `ls -adlF #{f.path.shellescape}` : `ls -alF #{f.path.shellescape}`
|
||||
colorize(ls.empty? ? f.inspect : "#{f.inspect}\n#{ls.chop}", :file)
|
||||
end
|
||||
|
||||
# Format Dir object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_dir(d)
|
||||
ls = `ls -alF #{d.path.shellescape}`
|
||||
colorize(ls.empty? ? d.inspect : "#{d.inspect}\n#{ls.chop}", :dir)
|
||||
end
|
||||
|
||||
# Format BigDecimal object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_bigdecimal(n)
|
||||
colorize(n.to_s("F"), :bigdecimal)
|
||||
o = n.to_s("F")
|
||||
type = :bigdecimal
|
||||
awesome_simple(o, type, @inspector)
|
||||
end
|
||||
|
||||
# Format Rational object.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_rational(n)
|
||||
colorize(n.to_s, :rational)
|
||||
o = n.to_s
|
||||
type = :rational
|
||||
awesome_simple(o, type, @inspector)
|
||||
end
|
||||
|
||||
def awesome_simple(o, type, inspector)
|
||||
AwesomePrint::Formatters::SimpleFormatter.new(o, type, inspector).format
|
||||
end
|
||||
|
||||
def awesome_array(a)
|
||||
Formatters::ArrayFormatter.new(a, @inspector).format
|
||||
end
|
||||
|
||||
def awesome_set(s)
|
||||
Formatters::ArrayFormatter.new(s.to_a, @inspector).format
|
||||
end
|
||||
|
||||
def awesome_hash(h)
|
||||
Formatters::HashFormatter.new(h, @inspector).format
|
||||
end
|
||||
|
||||
def awesome_object(o)
|
||||
Formatters::ObjectFormatter.new(o, o.instance_variables, @inspector).format
|
||||
end
|
||||
|
||||
def awesome_struct(s)
|
||||
Formatters::ObjectFormatter.new(s, s.members, @inspector).format
|
||||
end
|
||||
|
||||
# Format a method.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_method(m)
|
||||
name, args, owner = method_tuple(m)
|
||||
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
|
||||
Formatters::MethodFormatter.new(m, @inspector).format
|
||||
end
|
||||
alias :awesome_unboundmethod :awesome_method
|
||||
|
||||
# Format object instance.
|
||||
#------------------------------------------------------------------------------
|
||||
def awesome_instance(o)
|
||||
"#{o.class}:0x%08x" % (o.__id__ * 2)
|
||||
def awesome_class(c)
|
||||
Formatters::ClassFormatter.new(c, @inspector).format
|
||||
end
|
||||
|
||||
# Format object.methods array.
|
||||
#------------------------------------------------------------------------------
|
||||
def methods_array(a)
|
||||
a.sort! { |x, y| x.to_s <=> y.to_s } # Can't simply a.sort! because of o.methods << [ :blah ]
|
||||
object = a.instance_variable_get('@__awesome_methods__')
|
||||
tuples = a.map do |name|
|
||||
if name.is_a?(Symbol) || name.is_a?(String) # Ignore garbage, ex. 42.methods << [ :blah ]
|
||||
tuple = if object.respond_to?(name, true) # Is this a regular method?
|
||||
the_method = object.method(name) rescue nil # Avoid potential ArgumentError if object#method is overridden.
|
||||
if the_method && the_method.respond_to?(:arity) # Is this original object#method?
|
||||
method_tuple(the_method) # Yes, we are good.
|
||||
end
|
||||
elsif object.respond_to?(:instance_method) # Is this an unbound method?
|
||||
method_tuple(object.instance_method(name)) rescue nil # Rescue to avoid NameError when the method is not
|
||||
end # available (ex. File.lchmod on Ubuntu 12).
|
||||
end
|
||||
tuple || [ name.to_s, '(?)', '?' ] # Return WTF default if all the above fails.
|
||||
end
|
||||
|
||||
width = (tuples.size - 1).to_s.size
|
||||
name_width = tuples.map { |item| item[0].size }.max || 0
|
||||
args_width = tuples.map { |item| item[1].size }.max || 0
|
||||
|
||||
data = tuples.inject([]) do |arr, item|
|
||||
index = indent
|
||||
index << "[#{arr.size.to_s.rjust(width)}]" if @options[:index]
|
||||
indented do
|
||||
arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
|
||||
end
|
||||
end
|
||||
|
||||
"[\n" << data.join("\n") << "\n#{outdent}]"
|
||||
def awesome_file(f)
|
||||
Formatters::FileFormatter.new(f, @inspector).format
|
||||
end
|
||||
|
||||
# Return [ name, arguments, owner ] tuple for a given method.
|
||||
#------------------------------------------------------------------------------
|
||||
def method_tuple(method)
|
||||
if method.respond_to?(:parameters) # Ruby 1.9.2+
|
||||
# See http://ruby.runpaint.org/methods#method-objects-parameters
|
||||
args = method.parameters.inject([]) do |arr, (type, name)|
|
||||
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
|
||||
arr << case type
|
||||
when :req then name.to_s
|
||||
when :opt, :rest then "*#{name}"
|
||||
when :block then "&#{name}"
|
||||
else '?'
|
||||
end
|
||||
end
|
||||
else # See http://ruby-doc.org/core/classes/Method.html#M001902
|
||||
args = (1..method.arity.abs).map { |i| "arg#{i}" }
|
||||
args[-1] = "*#{args[-1]}" if method.arity < 0
|
||||
end
|
||||
|
||||
# method.to_s formats to handle:
|
||||
#
|
||||
# #<Method: Fixnum#zero?>
|
||||
# #<Method: Fixnum(Integer)#years>
|
||||
# #<Method: User(#<Module:0x00000103207c00>)#_username>
|
||||
# #<Method: User(id: integer, username: string).table_name>
|
||||
# #<Method: User(id: integer, username: string)(ActiveRecord::Base).current>
|
||||
# #<UnboundMethod: Hello#world>
|
||||
#
|
||||
if method.to_s =~ /(Unbound)*Method: (.*)[#\.]/
|
||||
unbound, klass = $1 && '(unbound)', $2
|
||||
if klass && klass =~ /(\(\w+:\s.*?\))/ # Is this ActiveRecord-style class?
|
||||
klass.sub!($1, '') # Yes, strip the fields leaving class name only.
|
||||
end
|
||||
owner = "#{klass}#{unbound}".gsub('(', ' (')
|
||||
end
|
||||
|
||||
[ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
|
||||
end
|
||||
|
||||
# Format hash keys as plain strings regardless of underlying data type.
|
||||
#------------------------------------------------------------------------------
|
||||
def plain_single_line
|
||||
plain, multiline = @options[:plain], @options[:multiline]
|
||||
@options[:plain], @options[:multiline] = true, false
|
||||
yield
|
||||
ensure
|
||||
@options[:plain], @options[:multiline] = plain, multiline
|
||||
def awesome_dir(d)
|
||||
Formatters::DirFormatter.new(d, @inspector).format
|
||||
end
|
||||
|
||||
# Utility methods.
|
||||
|
@ -331,6 +118,7 @@ module AwesomePrint
|
|||
if ! object.respond_to?(:to_hash)
|
||||
return nil
|
||||
end
|
||||
|
||||
if object.method(:to_hash).arity != 0
|
||||
return nil
|
||||
end
|
||||
|
@ -342,91 +130,5 @@ module AwesomePrint
|
|||
|
||||
return hash
|
||||
end
|
||||
|
||||
def align(value, width)
|
||||
if @options[:multiline]
|
||||
if @options[:indent] > 0
|
||||
value.rjust(width)
|
||||
elsif @options[:indent] == 0
|
||||
indent + value.ljust(width)
|
||||
else
|
||||
indent[0, @indentation + @options[:indent]] + value.ljust(width)
|
||||
end
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
def indented
|
||||
@indentation += @options[:indent].abs
|
||||
yield
|
||||
ensure
|
||||
@indentation -= @options[:indent].abs
|
||||
end
|
||||
|
||||
def left_aligned
|
||||
current, @options[:indent] = @options[:indent], 0
|
||||
yield
|
||||
ensure
|
||||
@options[:indent] = current
|
||||
end
|
||||
|
||||
def indent
|
||||
' ' * @indentation
|
||||
end
|
||||
|
||||
def outdent
|
||||
' ' * (@indentation - @options[:indent].abs)
|
||||
end
|
||||
|
||||
# To support limited output, for example:
|
||||
#
|
||||
# ap ('a'..'z').to_a, :limit => 3
|
||||
# [
|
||||
# [ 0] "a",
|
||||
# [ 1] .. [24],
|
||||
# [25] "z"
|
||||
# ]
|
||||
#
|
||||
# ap (1..100).to_a, :limit => true # Default limit is 7.
|
||||
# [
|
||||
# [ 0] 1,
|
||||
# [ 1] 2,
|
||||
# [ 2] 3,
|
||||
# [ 3] .. [96],
|
||||
# [97] 98,
|
||||
# [98] 99,
|
||||
# [99] 100
|
||||
# ]
|
||||
#------------------------------------------------------------------------------
|
||||
def should_be_limited?
|
||||
@options[:limit] == true or (@options[:limit].is_a?(Fixnum) and @options[:limit] > 0)
|
||||
end
|
||||
|
||||
def get_limit_size
|
||||
@options[:limit] == true ? DEFAULT_LIMIT_SIZE : @options[:limit]
|
||||
end
|
||||
|
||||
def limited(data, width, is_hash = false)
|
||||
limit = get_limit_size
|
||||
if data.length <= limit
|
||||
data
|
||||
else
|
||||
# Calculate how many elements to be displayed above and below the separator.
|
||||
head = limit / 2
|
||||
tail = head - (limit - 1) % 2
|
||||
|
||||
# Add the proper elements to the temp array and format the separator.
|
||||
temp = data[0, head] + [ nil ] + data[-tail, tail]
|
||||
|
||||
if is_hash
|
||||
temp[head] = "#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
|
||||
else
|
||||
temp[head] = "#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]"
|
||||
end
|
||||
|
||||
temp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
73
lib/awesome_print/formatters/array_formatter.rb
Normal file
73
lib/awesome_print/formatters/array_formatter.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class ArrayFormatter < BaseFormatter
|
||||
|
||||
attr_reader :array, :inspector, :options
|
||||
|
||||
def initialize(array, inspector)
|
||||
@array = array
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
return "[]" if array == []
|
||||
|
||||
if array.instance_variable_defined?('@__awesome_methods__')
|
||||
methods_array(array)
|
||||
elsif options[:multiline]
|
||||
width = (array.size - 1).to_s.size
|
||||
|
||||
data = array.inject([]) do |arr, item|
|
||||
index = indent
|
||||
index << colorize("[#{arr.size.to_s.rjust(width)}] ", :array) if options[:index]
|
||||
indented do
|
||||
arr << (index << inspector.awesome(item))
|
||||
end
|
||||
end
|
||||
|
||||
data = limited(data, width) if should_be_limited?
|
||||
"[\n" << data.join(",\n") << "\n#{outdent}]"
|
||||
else
|
||||
"[ " << array.map{ |item| inspector.awesome(item) }.join(", ") << " ]"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def methods_array(a)
|
||||
a.sort! { |x, y| x.to_s <=> y.to_s } # Can't simply a.sort! because of o.methods << [ :blah ]
|
||||
object = a.instance_variable_get('@__awesome_methods__')
|
||||
tuples = a.map do |name|
|
||||
if name.is_a?(Symbol) || name.is_a?(String) # Ignore garbage, ex. 42.methods << [ :blah ]
|
||||
tuple = if object.respond_to?(name, true) # Is this a regular method?
|
||||
the_method = object.method(name) rescue nil # Avoid potential ArgumentError if object#method is overridden.
|
||||
if the_method && the_method.respond_to?(:arity) # Is this original object#method?
|
||||
method_tuple(the_method) # Yes, we are good.
|
||||
end
|
||||
elsif object.respond_to?(:instance_method) # Is this an unbound method?
|
||||
method_tuple(object.instance_method(name)) rescue nil # Rescue to avoid NameError when the method is not
|
||||
end # available (ex. File.lchmod on Ubuntu 12).
|
||||
end
|
||||
tuple || [ name.to_s, '(?)', '?' ] # Return WTF default if all the above fails.
|
||||
end
|
||||
|
||||
width = (tuples.size - 1).to_s.size
|
||||
name_width = tuples.map { |item| item[0].size }.max || 0
|
||||
args_width = tuples.map { |item| item[1].size }.max || 0
|
||||
|
||||
data = tuples.inject([]) do |arr, item|
|
||||
index = indent
|
||||
index << "[#{arr.size.to_s.rjust(width)}]" if @options[:index]
|
||||
indented do
|
||||
arr << "#{index} #{colorize(item[0].rjust(name_width), :method)}#{colorize(item[1].ljust(args_width), :args)} #{colorize(item[2], :class)}"
|
||||
end
|
||||
end
|
||||
|
||||
"[\n" << data.join("\n") << "\n#{outdent}]"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
138
lib/awesome_print/formatters/base_formatter.rb
Normal file
138
lib/awesome_print/formatters/base_formatter.rb
Normal file
|
@ -0,0 +1,138 @@
|
|||
require_relative "../colorize"
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class BaseFormatter
|
||||
include Colorize
|
||||
|
||||
DEFAULT_LIMIT_SIZE = 7
|
||||
|
||||
# To support limited output, for example:
|
||||
#
|
||||
# ap ('a'..'z').to_a, :limit => 3
|
||||
# [
|
||||
# [ 0] "a",
|
||||
# [ 1] .. [24],
|
||||
# [25] "z"
|
||||
# ]
|
||||
#
|
||||
# ap (1..100).to_a, :limit => true # Default limit is 7.
|
||||
# [
|
||||
# [ 0] 1,
|
||||
# [ 1] 2,
|
||||
# [ 2] 3,
|
||||
# [ 3] .. [96],
|
||||
# [97] 98,
|
||||
# [98] 99,
|
||||
# [99] 100
|
||||
# ]
|
||||
#------------------------------------------------------------------------------
|
||||
def should_be_limited?
|
||||
options[:limit] or (options[:limit].is_a?(Fixnum) and options[:limit] > 0)
|
||||
end
|
||||
|
||||
def get_limit_size
|
||||
case options[:limit]
|
||||
when true
|
||||
DEFAULT_LIMIT_SIZE
|
||||
else
|
||||
options[:limit]
|
||||
end
|
||||
end
|
||||
|
||||
def limited(data, width, is_hash = false)
|
||||
limit = get_limit_size
|
||||
if data.length <= limit
|
||||
data
|
||||
else
|
||||
# Calculate how many elements to be displayed above and below the separator.
|
||||
head = limit / 2
|
||||
tail = head - (limit - 1) % 2
|
||||
|
||||
# Add the proper elements to the temp array and format the separator.
|
||||
temp = data[0, head] + [ nil ] + data[-tail, tail]
|
||||
|
||||
if is_hash
|
||||
temp[head] = "#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
|
||||
else
|
||||
temp[head] = "#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]"
|
||||
end
|
||||
|
||||
temp
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def method_tuple(method)
|
||||
if method.respond_to?(:parameters) # Ruby 1.9.2+
|
||||
# See http://ruby.runpaint.org/methods#method-objects-parameters
|
||||
args = method.parameters.inject([]) do |arr, (type, name)|
|
||||
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
|
||||
arr << case type
|
||||
when :req then name.to_s
|
||||
when :opt, :rest then "*#{name}"
|
||||
when :block then "&#{name}"
|
||||
else '?'
|
||||
end
|
||||
end
|
||||
else # See http://ruby-doc.org/core/classes/Method.html#M001902
|
||||
args = (1..method.arity.abs).map { |i| "arg#{i}" }
|
||||
args[-1] = "*#{args[-1]}" if method.arity < 0
|
||||
end
|
||||
|
||||
# method.to_s formats to handle:
|
||||
#
|
||||
# #<Method: Fixnum#zero?>
|
||||
# #<Method: Fixnum(Integer)#years>
|
||||
# #<Method: User(#<Module:0x00000103207c00>)#_username>
|
||||
# #<Method: User(id: integer, username: string).table_name>
|
||||
# #<Method: User(id: integer, username: string)(ActiveRecord::Base).current>
|
||||
# #<UnboundMethod: Hello#world>
|
||||
#
|
||||
if method.to_s =~ /(Unbound)*Method: (.*)[#\.]/
|
||||
unbound, klass = $1 && '(unbound)', $2
|
||||
if klass && klass =~ /(\(\w+:\s.*?\))/ # Is this ActiveRecord-style class?
|
||||
klass.sub!($1, '') # Yes, strip the fields leaving class name only.
|
||||
end
|
||||
owner = "#{klass}#{unbound}".gsub('(', ' (')
|
||||
end
|
||||
|
||||
[ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
|
||||
end
|
||||
|
||||
#
|
||||
# Indentation related methods
|
||||
#-----------------------------------------
|
||||
def indentation
|
||||
inspector.current_indentation
|
||||
end
|
||||
|
||||
def indented
|
||||
inspector.increase_indentation(&Proc.new)
|
||||
end
|
||||
|
||||
def indent
|
||||
' ' * indentation
|
||||
end
|
||||
|
||||
def outdent
|
||||
' ' * (indentation - options[:indent].abs)
|
||||
end
|
||||
|
||||
def align(value, width)
|
||||
if options[:multiline]
|
||||
if options[:indent] > 0
|
||||
value.rjust(width)
|
||||
elsif options[:indent] == 0
|
||||
indent + value.ljust(width)
|
||||
else
|
||||
indent[0, indentation + options[:indent]] + value.ljust(width)
|
||||
end
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
24
lib/awesome_print/formatters/class_formatter.rb
Normal file
24
lib/awesome_print/formatters/class_formatter.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class ClassFormatter < BaseFormatter
|
||||
|
||||
attr_reader :klass, :inspector, :options
|
||||
|
||||
def initialize(klass, inspector)
|
||||
@klass = klass
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
if superclass = klass.superclass # <-- Assign and test if nil.
|
||||
colorize("#{klass.inspect} < #{superclass}", :class)
|
||||
else
|
||||
colorize(klass.inspect, :class)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
lib/awesome_print/formatters/dir_formatter.rb
Normal file
22
lib/awesome_print/formatters/dir_formatter.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require_relative 'base_formatter'
|
||||
require "shellwords"
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class DirFormatter < BaseFormatter
|
||||
|
||||
attr_reader :dir, :inspector, :options
|
||||
|
||||
def initialize(dir, inspector)
|
||||
@dir = dir
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
ls = `ls -alF #{dir.path.shellescape}`
|
||||
colorize(ls.empty? ? dir.inspect : "#{dir.inspect}\n#{ls.chop}", :dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
lib/awesome_print/formatters/file_formatter.rb
Normal file
22
lib/awesome_print/formatters/file_formatter.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require_relative 'base_formatter'
|
||||
require "shellwords"
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class FileFormatter < BaseFormatter
|
||||
|
||||
attr_reader :file, :inspector, :options
|
||||
|
||||
def initialize(file, inspector)
|
||||
@file = file
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
ls = File.directory?(file) ? `ls -adlF #{file.path.shellescape}` : `ls -alF #{file.path.shellescape}`
|
||||
colorize(ls.empty? ? file.inspect : "#{file.inspect}\n#{ls.chop}", :file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
54
lib/awesome_print/formatters/hash_formatter.rb
Normal file
54
lib/awesome_print/formatters/hash_formatter.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class HashFormatter < BaseFormatter
|
||||
|
||||
attr_reader :hash, :inspector, :options
|
||||
|
||||
def initialize(hash, inspector)
|
||||
@hash = hash
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
return "{}" if hash == {}
|
||||
|
||||
keys = hash.keys
|
||||
keys = keys.sort { |a, b| a.to_s <=> b.to_s } if options[:sort_keys]
|
||||
data = keys.map do |key|
|
||||
plain_single_line do
|
||||
[ inspector.awesome(key), hash[key] ]
|
||||
end
|
||||
end
|
||||
|
||||
width = data.map { |key, | key.size }.max || 0
|
||||
width += indentation if options[:indent] > 0
|
||||
|
||||
data = data.map do |key, value|
|
||||
indented do
|
||||
align(key, width) << colorize(" => ", :hash) << inspector.awesome(value)
|
||||
end
|
||||
end
|
||||
|
||||
data = limited(data, width, :hash => true) if should_be_limited?
|
||||
if options[:multiline]
|
||||
"{\n" << data.join(",\n") << "\n#{outdent}}"
|
||||
else
|
||||
"{ #{data.join(', ')} }"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def plain_single_line
|
||||
plain, multiline = options[:plain], options[:multiline]
|
||||
options[:plain], options[:multiline] = true, false
|
||||
yield
|
||||
ensure
|
||||
options[:plain], options[:multiline] = plain, multiline
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
lib/awesome_print/formatters/method_formatter.rb
Normal file
22
lib/awesome_print/formatters/method_formatter.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class MethodFormatter < BaseFormatter
|
||||
|
||||
attr_reader :method, :inspector, :options
|
||||
|
||||
def initialize(method, inspector)
|
||||
@method = method
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
name, args, owner = method_tuple(method)
|
||||
|
||||
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
80
lib/awesome_print/formatters/object_formatter.rb
Normal file
80
lib/awesome_print/formatters/object_formatter.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class ObjectFormatter < BaseFormatter
|
||||
|
||||
attr_reader :object, :variables, :inspector, :options
|
||||
|
||||
def initialize(object, variables, inspector)
|
||||
@object = object
|
||||
@variables = variables
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
vars = variables.map do |var|
|
||||
property = var.to_s[1..-1].to_sym # to_s because of some monkey patching done by Puppet.
|
||||
accessor = if object.respond_to?(:"#{property}=")
|
||||
object.respond_to?(property) ? :accessor : :writer
|
||||
else
|
||||
object.respond_to?(property) ? :reader : nil
|
||||
end
|
||||
if accessor
|
||||
[ "attr_#{accessor} :#{property}", var ]
|
||||
else
|
||||
[ var.to_s, var ]
|
||||
end
|
||||
end
|
||||
|
||||
data = vars.sort.map do |declaration, var|
|
||||
key = left_aligned do
|
||||
align(declaration, declaration.size)
|
||||
end
|
||||
|
||||
unless options[:plain]
|
||||
if key =~ /(@\w+)/
|
||||
key.sub!($1, colorize($1, :variable))
|
||||
else
|
||||
key.sub!(/(attr_\w+)\s(\:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}")
|
||||
end
|
||||
end
|
||||
|
||||
indented do
|
||||
var_contents = if valid_instance_var?(var)
|
||||
object.instance_variable_get(var)
|
||||
else
|
||||
object.send(var) # Enables handling of Struct attributes
|
||||
end
|
||||
|
||||
key << colorize(" = ", :hash) + inspector.awesome(var_contents)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:multiline]
|
||||
"#<#{awesome_instance}\n#{data.join(%Q/,\n/)}\n#{outdent}>"
|
||||
else
|
||||
"#<#{awesome_instance} #{data.join(', ')}>"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_instance_var?(variable_name)
|
||||
variable_name.to_s.start_with?('@')
|
||||
end
|
||||
|
||||
def awesome_instance
|
||||
"#{object.class}:0x%08x" % (object.__id__ * 2)
|
||||
end
|
||||
|
||||
def left_aligned
|
||||
current, options[:indent] = options[:indent], 0
|
||||
yield
|
||||
ensure
|
||||
options[:indent] = current
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
lib/awesome_print/formatters/simple_formatter.rb
Normal file
21
lib/awesome_print/formatters/simple_formatter.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require_relative 'base_formatter'
|
||||
|
||||
module AwesomePrint
|
||||
module Formatters
|
||||
class SimpleFormatter < BaseFormatter
|
||||
|
||||
attr_reader :string, :type, :inspector, :options
|
||||
|
||||
def initialize(string, type, inspector)
|
||||
@string = string
|
||||
@type = type
|
||||
@inspector = inspector
|
||||
@options = inspector.options
|
||||
end
|
||||
|
||||
def format
|
||||
colorize(string, type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
lib/awesome_print/indentator.rb
Normal file
18
lib/awesome_print/indentator.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module AwesomePrint
|
||||
class Indentator
|
||||
|
||||
attr_reader :shift_width, :indentation
|
||||
|
||||
def initialize(indentation)
|
||||
@indentation = indentation
|
||||
@shift_width = indentation.freeze
|
||||
end
|
||||
|
||||
def indent
|
||||
@indentation += shift_width
|
||||
yield
|
||||
ensure
|
||||
@indentation -= shift_width
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
# Awesome Print is freely distributable under the terms of MIT license.
|
||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||
#------------------------------------------------------------------------------
|
||||
require_relative "indentator"
|
||||
|
||||
module AwesomePrint
|
||||
|
||||
class << self # Class accessors for custom defaults.
|
||||
|
@ -48,7 +50,7 @@ module AwesomePrint
|
|||
end
|
||||
|
||||
class Inspector
|
||||
attr_accessor :options
|
||||
attr_accessor :options, :indentator
|
||||
|
||||
AP = :__awesome_print__
|
||||
|
||||
|
@ -90,8 +92,17 @@ module AwesomePrint
|
|||
merge_options!(options)
|
||||
|
||||
@formatter = AwesomePrint::Formatter.new(self)
|
||||
@indentator = AwesomePrint::Indentator.new(@options[:indent].abs)
|
||||
Thread.current[AP] ||= []
|
||||
end
|
||||
|
||||
def current_indentation
|
||||
indentator.indentation
|
||||
end
|
||||
|
||||
def increase_indentation
|
||||
indentator.indent(&Proc.new)
|
||||
end
|
||||
|
||||
# Dispatcher that detects data nesting and invokes object-aware formatter.
|
||||
#------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue