mirror of
https://github.com/awesome-print/awesome_print
synced 2023-03-27 23:22:34 -04:00
Major refactoring: split AwesomePrint class into Inspector and Formatter
This commit is contained in:
parent
0f14846fcd
commit
dfbebd4887
15 changed files with 568 additions and 523 deletions
|
@ -7,7 +7,8 @@
|
||||||
require File.dirname(__FILE__) + "/ap/core_ext/#{file}"
|
require File.dirname(__FILE__) + "/ap/core_ext/#{file}"
|
||||||
end
|
end
|
||||||
|
|
||||||
require File.dirname(__FILE__) + "/ap/awesome_print"
|
require File.dirname(__FILE__) + "/ap/inspector"
|
||||||
|
require File.dirname(__FILE__) + "/ap/formatter"
|
||||||
require File.dirname(__FILE__) + "/ap/core_ext/logger" if defined?(Logger)
|
require File.dirname(__FILE__) + "/ap/core_ext/logger" if defined?(Logger)
|
||||||
require File.dirname(__FILE__) + "/ap/mixin/action_view" if defined?(ActionView)
|
require File.dirname(__FILE__) + "/ap/mixin/action_view" if defined?(ActionView)
|
||||||
|
|
||||||
|
|
|
@ -1,332 +0,0 @@
|
||||||
# Copyright (c) 2010-2011 Michael Dvorkin
|
|
||||||
#
|
|
||||||
# Awesome Print is freely distributable under the terms of MIT license.
|
|
||||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
require "shellwords"
|
|
||||||
|
|
||||||
class AwesomePrint
|
|
||||||
AP = :__awesome_print__ unless defined?(AwesomePrint::AP)
|
|
||||||
CORE = [ :array, :hash, :class, :file, :dir, :bigdecimal, :rational, :struct, :method, :unboundmethod ] unless defined?(AwesomePrint::CORE)
|
|
||||||
|
|
||||||
def initialize(options = {})
|
|
||||||
@options = {
|
|
||||||
:multiline => true,
|
|
||||||
:plain => false,
|
|
||||||
:indent => 4,
|
|
||||||
:index => true,
|
|
||||||
:sorted_hash_keys => false,
|
|
||||||
:color => {
|
|
||||||
:array => :white,
|
|
||||||
:bigdecimal => :blue,
|
|
||||||
:class => :yellow,
|
|
||||||
:date => :greenish,
|
|
||||||
:falseclass => :red,
|
|
||||||
:fixnum => :blue,
|
|
||||||
:float => :blue,
|
|
||||||
:hash => :pale,
|
|
||||||
:struct => :pale,
|
|
||||||
:nilclass => :red,
|
|
||||||
:string => :yellowish,
|
|
||||||
:symbol => :cyanish,
|
|
||||||
:time => :greenish,
|
|
||||||
:trueclass => :green,
|
|
||||||
:method => :purpleish,
|
|
||||||
:args => :pale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Merge custom defaults and let explicit options parameter override them.
|
|
||||||
merge_custom_defaults!
|
|
||||||
merge_options!(options)
|
|
||||||
|
|
||||||
@indentation = @options[:indent].abs
|
|
||||||
Thread.current[AP] ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# 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 = if @options[:index]
|
|
||||||
colorize("#{indent}[#{arr.size.to_s.rjust(width)}] ", :array)
|
|
||||||
else
|
|
||||||
colorize(indent, :array)
|
|
||||||
end
|
|
||||||
indented do
|
|
||||||
arr << (index << awesome(item))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"[\n" << data.join(",\n") << "\n#{outdent}]"
|
|
||||||
else
|
|
||||||
"[ " << a.map{ |item| 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[:sorted_hash_keys] ? h.keys.sort { |a, b| a.to_s <=> b.to_s } : h.keys
|
|
||||||
data = keys.map do |key|
|
|
||||||
plain_single_line do
|
|
||||||
[ 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|
|
|
||||||
if @options[:multiline]
|
|
||||||
formatted_key = (@options[:indent] >= 0 ? key.rjust(width) : indent + key.ljust(width))
|
|
||||||
else
|
|
||||||
formatted_key = key
|
|
||||||
end
|
|
||||||
indented do
|
|
||||||
formatted_key << colorize(" => ", :hash) << awesome(value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if @options[:multiline]
|
|
||||||
"{\n" << data.join(",\n") << "\n#{outdent}}"
|
|
||||||
else
|
|
||||||
"{ #{data.join(', ')} }"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format a Struct. If @options[:indent] if negative left align hash keys.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_struct(s)
|
|
||||||
h = {}
|
|
||||||
s.each_pair { |k,v| h[k] = v }
|
|
||||||
awesome_hash(h)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format Class object.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_class(c)
|
|
||||||
if superclass = c.superclass # <-- Assign and test if nil.
|
|
||||||
awesome_self(c, :with => " < #{superclass}")
|
|
||||||
else
|
|
||||||
awesome_self(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format File object.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_file(f)
|
|
||||||
ls = File.directory?(f) ? `ls -adlF #{f.path.shellescape}` : `ls -alF #{f.path.shellescape}`
|
|
||||||
awesome_self(f, :with => ls.empty? ? nil : "\n#{ls.chop}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format Dir object.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_dir(d)
|
|
||||||
ls = `ls -alF #{d.path.shellescape}`
|
|
||||||
awesome_self(d, :with => ls.empty? ? nil : "\n#{ls.chop}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format BigDecimal and Rational objects by convering them to Float.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_bigdecimal(n)
|
|
||||||
awesome_self(n.to_f, :as => :bigdecimal)
|
|
||||||
end
|
|
||||||
alias :awesome_rational :awesome_bigdecimal
|
|
||||||
|
|
||||||
# Format a method.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_method(m)
|
|
||||||
name, args, owner = method_tuple(m)
|
|
||||||
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
|
|
||||||
end
|
|
||||||
alias :awesome_unboundmethod :awesome_method
|
|
||||||
|
|
||||||
# Catch all method to format an arbitrary object.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_self(object, appear = {})
|
|
||||||
colorize(object.inspect.to_s << appear[:with].to_s, appear[:as] || declassify(object))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Dispatcher that detects data nesting and invokes object-aware formatter.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome(object)
|
|
||||||
if Thread.current[AP].include?(object.object_id)
|
|
||||||
nested(object)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
Thread.current[AP] << object.object_id
|
|
||||||
send(:"awesome_#{printable(object)}", object)
|
|
||||||
ensure
|
|
||||||
Thread.current[AP].pop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format object.methods array.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def methods_array(a)
|
|
||||||
object = a.instance_variable_get('@__awesome_methods__')
|
|
||||||
tuples = a.map do |name|
|
|
||||||
if object.respond_to?(name, true) # Regular method?
|
|
||||||
method_tuple(object.method(name))
|
|
||||||
elsif object.respond_to?(:instance_method) # Unbound method?
|
|
||||||
method_tuple(object.instance_method(name))
|
|
||||||
else # WTF method.
|
|
||||||
[ name.to_s, '(?)', '' ]
|
|
||||||
end
|
|
||||||
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 = if @options[:index]
|
|
||||||
"#{indent}[#{arr.size.to_s.rjust(width)}]"
|
|
||||||
else
|
|
||||||
indent
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
||||||
# Format nested data, for example:
|
|
||||||
# arr = [1, 2]; arr << arr
|
|
||||||
# => [1,2, [...]]
|
|
||||||
# hsh = { :a => 1 }; hsh[:b] = hsh
|
|
||||||
# => { :a => 1, :b => {...} }
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def nested(object)
|
|
||||||
case printable(object)
|
|
||||||
when :array then colorize("[...]", :array)
|
|
||||||
when :hash then colorize("{...}", :hash)
|
|
||||||
when :struct then colorize("{...}", :struct)
|
|
||||||
else colorize("...#{object.class}...", :class)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return one of the "core" types that have a formatter of :self otherwise.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def printable(object)
|
|
||||||
CORE.grep(declassify(object))[0] || :self
|
|
||||||
end
|
|
||||||
|
|
||||||
# Turn class name into symbol, ex: Hello::World => :hello_world. Classes that
|
|
||||||
# inherit from Array, Hash, File, Dir, and Struct are treated as the base class.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def declassify(object)
|
|
||||||
case object
|
|
||||||
when Array then :array
|
|
||||||
when Hash then :hash
|
|
||||||
when File then :file
|
|
||||||
when Dir then :dir
|
|
||||||
when Struct then :struct
|
|
||||||
else object.class.to_s.gsub(/:+/, "_").downcase.to_sym
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Pick the color and apply it to the given string as necessary.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def colorize(s, type)
|
|
||||||
if @options[:plain] || @options[:color][type].nil?
|
|
||||||
s
|
|
||||||
else
|
|
||||||
s.send(@options[:color][type])
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
||||||
if method.to_s =~ /(Unbound)*Method: (.*?)[#\.]/
|
|
||||||
owner = "#{$2}#{$1 ? '(unbound)' : ''}".gsub('(', ' (')
|
|
||||||
end
|
|
||||||
|
|
||||||
[ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format hash keys as plain string 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
|
|
||||||
end
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def indented
|
|
||||||
@indentation += @options[:indent].abs
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
@indentation -= @options[:indent].abs
|
|
||||||
end
|
|
||||||
|
|
||||||
def indent
|
|
||||||
@indent = ' ' * @indentation
|
|
||||||
end
|
|
||||||
|
|
||||||
def outdent
|
|
||||||
@outdent = ' ' * (@indentation - @options[:indent].abs)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update @options by first merging the :color hash and then the remaining keys.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def merge_options!(options = {})
|
|
||||||
@options[:color].merge!(options.delete(:color) || {})
|
|
||||||
@options.merge!(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Load ~/.aprc file with custom defaults that override default options.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def merge_custom_defaults!
|
|
||||||
dotfile = File.join(ENV["HOME"], ".aprc")
|
|
||||||
if File.readable?(dotfile)
|
|
||||||
load dotfile
|
|
||||||
merge_options!(self.class.defaults)
|
|
||||||
end
|
|
||||||
rescue => e
|
|
||||||
$stderr.puts "Could not load #{dotfile}: #{e}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Class accessors for custom defaults.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def self.defaults
|
|
||||||
@@defaults ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.defaults=(args = {})
|
|
||||||
@@defaults = args
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -6,7 +6,7 @@
|
||||||
module Kernel
|
module Kernel
|
||||||
|
|
||||||
def ai(options = {})
|
def ai(options = {})
|
||||||
ap = AwesomePrint.new(options)
|
ap = AwesomePrint::Inspector.new(options)
|
||||||
ap.send(:awesome, self)
|
ap.send(:awesome, self)
|
||||||
end
|
end
|
||||||
alias :awesome_inspect :ai
|
alias :awesome_inspect :ai
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
# Awesome Print is freely distributable under the terms of MIT license.
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
module AwesomePrintLogger
|
module AwesomePrint
|
||||||
|
module Logger
|
||||||
|
|
||||||
# Add ap method to logger
|
# Add ap method to logger
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
def ap(object, level = nil)
|
def ap(object, level = nil)
|
||||||
level ||= AwesomePrint.defaults[:log_level] || :debug
|
level ||= AwesomePrint.defaults[:log_level] || :debug
|
||||||
send level, object.ai
|
send level, object.ai
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Logger.send(:include, AwesomePrintLogger)
|
Logger.send(:include, AwesomePrint::Logger)
|
||||||
ActiveSupport::BufferedLogger.send(:include, AwesomePrintLogger) if defined?(::ActiveSupport::BufferedLogger)
|
ActiveSupport::BufferedLogger.send(:include, AwesomePrint::Logger) if defined?(::ActiveSupport::BufferedLogger)
|
||||||
|
|
238
lib/ap/formatter.rb
Executable file
238
lib/ap/formatter.rb
Executable file
|
@ -0,0 +1,238 @@
|
||||||
|
# Copyright (c) 2010-2011 Michael Dvorkin
|
||||||
|
#
|
||||||
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
require "shellwords"
|
||||||
|
|
||||||
|
module AwesomePrint
|
||||||
|
class Formatter
|
||||||
|
|
||||||
|
CORE = [ :array, :hash, :class, :file, :dir, :bigdecimal, :rational, :struct, :method, :unboundmethod ]
|
||||||
|
|
||||||
|
def initialize(inspector)
|
||||||
|
@inspector = inspector
|
||||||
|
@options = inspector.options
|
||||||
|
@indentation = @options[:indent].abs
|
||||||
|
end
|
||||||
|
|
||||||
|
# Main entry point to format an object.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def format(object, type = nil)
|
||||||
|
klass = cast(object, type)
|
||||||
|
return send(:"awesome_#{klass}", object) if klass != :self
|
||||||
|
send(:awesome_self, object, :as => type) # Catch all that falls back on object.inspect.
|
||||||
|
end
|
||||||
|
|
||||||
|
# Hook this when adding custom formatters. Check out how it's done in
|
||||||
|
# ap/lib/mixin/active_record.rb
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def cast(object, type)
|
||||||
|
CORE.grep(type)[0] || :self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pick the color and apply it to the given string as necessary.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def colorize(s, type = nil)
|
||||||
|
return s if @options[:plain] || @options[:color][type].nil?
|
||||||
|
s.send(@options[:color][type])
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Catch all method to format an arbitrary object.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_self(object, appear = {})
|
||||||
|
colorize(object.inspect.to_s << appear[:with].to_s, appear[:as])
|
||||||
|
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 = if @options[:index]
|
||||||
|
colorize("#{indent}[#{arr.size.to_s.rjust(width)}] ", :array)
|
||||||
|
else
|
||||||
|
colorize(indent, :array)
|
||||||
|
end
|
||||||
|
indented do
|
||||||
|
arr << (index << @inspector.send(:awesome, item))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
"[\n" << data.join(",\n") << "\n#{outdent}]"
|
||||||
|
else
|
||||||
|
"[ " << a.map{ |item| @inspector.send(: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.send(: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|
|
||||||
|
if @options[:multiline]
|
||||||
|
formatted_key = (@options[:indent] >= 0 ? key.rjust(width) : indent + key.ljust(width))
|
||||||
|
else
|
||||||
|
formatted_key = key
|
||||||
|
end
|
||||||
|
indented do
|
||||||
|
formatted_key << colorize(" => ", :hash) << @inspector.send(:awesome, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if @options[:multiline]
|
||||||
|
"{\n" << data.join(",\n") << "\n#{outdent}}"
|
||||||
|
else
|
||||||
|
"{ #{data.join(', ')} }"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format a Struct. If @options[:indent] if negative left align hash keys.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_struct(s)
|
||||||
|
h = {}
|
||||||
|
s.each_pair { |k,v| h[k] = v }
|
||||||
|
awesome_hash(h)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format Class object.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_class(c)
|
||||||
|
if superclass = c.superclass # <-- Assign and test if nil.
|
||||||
|
awesome_self(c, :as => :class, :with => " < #{superclass}")
|
||||||
|
else
|
||||||
|
awesome_self(c, :as => :class)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format File object.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_file(f)
|
||||||
|
ls = File.directory?(f) ? `ls -adlF #{f.path.shellescape}` : `ls -alF #{f.path.shellescape}`
|
||||||
|
awesome_self(f, :as => :file, :with => ls.empty? ? nil : "\n#{ls.chop}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format Dir object.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_dir(d)
|
||||||
|
ls = `ls -alF #{d.path.shellescape}`
|
||||||
|
awesome_self(d, :as => :dir, :with => ls.empty? ? nil : "\n#{ls.chop}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format BigDecimal and Rational objects by convering them to Float.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_bigdecimal(n)
|
||||||
|
awesome_self(n.to_f, :as => :bigdecimal)
|
||||||
|
end
|
||||||
|
alias :awesome_rational :awesome_bigdecimal
|
||||||
|
|
||||||
|
# Format a method.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_method(m)
|
||||||
|
name, args, owner = method_tuple(m)
|
||||||
|
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"
|
||||||
|
end
|
||||||
|
alias :awesome_unboundmethod :awesome_method
|
||||||
|
|
||||||
|
# Format object.methods array.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def methods_array(a)
|
||||||
|
object = a.instance_variable_get('@__awesome_methods__')
|
||||||
|
tuples = a.map do |name|
|
||||||
|
if object.respond_to?(name, true) # Regular method?
|
||||||
|
method_tuple(object.method(name))
|
||||||
|
elsif object.respond_to?(:instance_method) # Unbound method?
|
||||||
|
method_tuple(object.instance_method(name))
|
||||||
|
else # WTF method.
|
||||||
|
[ name.to_s, '(?)', '' ]
|
||||||
|
end
|
||||||
|
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 = if @options[:index]
|
||||||
|
"#{indent}[#{arr.size.to_s.rjust(width)}]"
|
||||||
|
else
|
||||||
|
indent
|
||||||
|
end
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
if method.to_s =~ /(Unbound)*Method: (.*?)[#\.]/
|
||||||
|
owner = "#{$2}#{$1 ? '(unbound)' : ''}".gsub('(', ' (')
|
||||||
|
end
|
||||||
|
|
||||||
|
[ method.name.to_s, "(#{args.join(', ')})", owner.to_s ]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format hash keys as plain string 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
|
||||||
|
end
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def indented
|
||||||
|
@indentation += @options[:indent].abs
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
@indentation -= @options[:indent].abs
|
||||||
|
end
|
||||||
|
|
||||||
|
def indent
|
||||||
|
' ' * @indentation
|
||||||
|
end
|
||||||
|
|
||||||
|
def outdent
|
||||||
|
' ' * (@indentation - @options[:indent].abs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
122
lib/ap/inspector.rb
Executable file
122
lib/ap/inspector.rb
Executable file
|
@ -0,0 +1,122 @@
|
||||||
|
# Copyright (c) 2010-2011 Michael Dvorkin
|
||||||
|
#
|
||||||
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
module AwesomePrint
|
||||||
|
|
||||||
|
class << self # Class accessors for custom defaults.
|
||||||
|
attr_accessor :defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
class Inspector
|
||||||
|
attr_accessor :options
|
||||||
|
|
||||||
|
AP = :__awesome_print__
|
||||||
|
|
||||||
|
def initialize(options = {})
|
||||||
|
@options = {
|
||||||
|
:color => {
|
||||||
|
:array => :white,
|
||||||
|
:bigdecimal => :blue,
|
||||||
|
:class => :yellow,
|
||||||
|
:date => :greenish,
|
||||||
|
:falseclass => :red,
|
||||||
|
:fixnum => :blue,
|
||||||
|
:float => :blue,
|
||||||
|
:hash => :pale,
|
||||||
|
:struct => :pale,
|
||||||
|
:nilclass => :red,
|
||||||
|
:string => :yellowish,
|
||||||
|
:symbol => :cyanish,
|
||||||
|
:time => :greenish,
|
||||||
|
:trueclass => :green,
|
||||||
|
:method => :purpleish,
|
||||||
|
:args => :pale
|
||||||
|
},
|
||||||
|
:indent => 4,
|
||||||
|
:index => true,
|
||||||
|
:multiline => true,
|
||||||
|
:plain => false,
|
||||||
|
:sort_keys => false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Merge custom defaults and let explicit options parameter override them.
|
||||||
|
merge_custom_defaults!
|
||||||
|
merge_options!(options)
|
||||||
|
|
||||||
|
@formatter = AwesomePrint::Formatter.new(self)
|
||||||
|
Thread.current[AP] ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Dispatcher that detects data nesting and invokes object-aware formatter.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome(object)
|
||||||
|
if Thread.current[AP].include?(object.object_id)
|
||||||
|
nested(object)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Thread.current[AP] << object.object_id
|
||||||
|
unnested(object)
|
||||||
|
ensure
|
||||||
|
Thread.current[AP].pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format nested data, for example:
|
||||||
|
# arr = [1, 2]; arr << arr
|
||||||
|
# => [1,2, [...]]
|
||||||
|
# hash = { :a => 1 }; hash[:b] = hash
|
||||||
|
# => { :a => 1, :b => {...} }
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def nested(object)
|
||||||
|
case printable(object)
|
||||||
|
when :array then @formatter.colorize("[...]", :array)
|
||||||
|
when :hash then @formatter.colorize("{...}", :hash)
|
||||||
|
when :struct then @formatter.colorize("{...}", :struct)
|
||||||
|
else @formatter.colorize("...#{object.class}...", :class)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def unnested(object)
|
||||||
|
@formatter.format(object, printable(object))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Turn class name into symbol, ex: Hello::World => :hello_world. Classes that
|
||||||
|
# inherit from Array, Hash, File, Dir, and Struct are treated as the base class.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def printable(object)
|
||||||
|
case object
|
||||||
|
when Array then :array
|
||||||
|
when Hash then :hash
|
||||||
|
when File then :file
|
||||||
|
when Dir then :dir
|
||||||
|
when Struct then :struct
|
||||||
|
else object.class.to_s.gsub(/:+/, "_").downcase.to_sym
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update @options by first merging the :color hash and then the remaining keys.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def merge_options!(options = {})
|
||||||
|
@options[:color].merge!(options.delete(:color) || {})
|
||||||
|
@options.merge!(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Load ~/.aprc file with custom defaults that override default options.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def merge_custom_defaults!
|
||||||
|
dotfile = File.join(ENV["HOME"], ".aprc")
|
||||||
|
if File.readable?(dotfile)
|
||||||
|
load dotfile
|
||||||
|
merge_options!(AwesomePrint.defaults)
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "Could not load #{dotfile}: #{e}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,36 +3,37 @@
|
||||||
# Awesome Print is freely distributable under the terms of MIT license.
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
module AwesomePrintActionView
|
module AwesomePrint
|
||||||
|
module ActionView
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
unless base.const_defined?(:AP_ANSI_TO_HTML)
|
unless base.const_defined?(:AP_ANSI_TO_HTML)
|
||||||
hash = {} # Build ANSI => HTML color map.
|
hash = {} # Build ANSI => HTML color map.
|
||||||
[ :gray, :red, :green, :yellow, :blue, :purple, :cyan, :white ].each_with_index do |color, i|
|
[ :gray, :red, :green, :yellow, :blue, :purple, :cyan, :white ].each_with_index do |color, i|
|
||||||
hash["\033[1;#{30+i}m"] = color
|
hash["\033[1;#{30+i}m"] = color
|
||||||
|
end
|
||||||
|
[ :black, :darkred, :darkgreen, :brown, :navy, :darkmagenta, :darkcyan, :slategray ].each_with_index do |color, i|
|
||||||
|
hash["\033[0;#{30+i}m"] = color
|
||||||
|
end
|
||||||
|
base.const_set(:AP_ANSI_TO_HTML, hash.freeze)
|
||||||
end
|
end
|
||||||
[ :black, :darkred, :darkgreen, :brown, :navy, :darkmagenta, :darkcyan, :slategray ].each_with_index do |color, i|
|
|
||||||
hash["\033[0;#{30+i}m"] = color
|
|
||||||
end
|
|
||||||
base.const_set(:AP_ANSI_TO_HTML, hash.freeze)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ap_debug(object, options = {})
|
|
||||||
formatted = h(object.ai(options))
|
|
||||||
|
|
||||||
unless options[:plain]
|
|
||||||
self.class::AP_ANSI_TO_HTML.each do |key, value|
|
|
||||||
formatted.gsub!(key, %Q|<font color="#{value}">|)
|
|
||||||
end
|
|
||||||
formatted.gsub!("\033[0m", "</font>")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
content_tag(:pre, formatted, :class => "debug_dump")
|
def ap_debug(object, options = {})
|
||||||
|
formatted = h(object.ai(options))
|
||||||
|
|
||||||
|
unless options[:plain]
|
||||||
|
self.class::AP_ANSI_TO_HTML.each do |key, value|
|
||||||
|
formatted.gsub!(key, %Q|<font color="#{value}">|)
|
||||||
|
end
|
||||||
|
formatted.gsub!("\033[0m", "</font>")
|
||||||
|
end
|
||||||
|
|
||||||
|
content_tag(:pre, formatted, :class => "debug_dump")
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :ap, :ap_debug
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :ap, :ap_debug
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
ActionView::Base.send(:include, AwesomePrintActionView) if defined?(ActionView)
|
ActionView::Base.send(:include, AwesomePrint::ActionView) if defined?(ActionView)
|
||||||
|
|
|
@ -3,52 +3,52 @@
|
||||||
# Awesome Print is freely distributable under the terms of MIT license.
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
module AwesomePrintActiveRecord
|
module AwesomePrint
|
||||||
|
module ActiveRecord
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.send :alias_method, :printable_without_active_record, :printable
|
base.send :alias_method, :cast_without_active_record, :cast
|
||||||
base.send :alias_method, :printable, :printable_with_active_record
|
base.send :alias_method, :cast, :cast_with_active_record
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add ActiveRecord class names to the dispatcher pipeline.
|
def cast_with_active_record(object, type)
|
||||||
#------------------------------------------------------------------------------
|
cast = cast_without_active_record(object, type)
|
||||||
def printable_with_active_record(object)
|
if defined?(::ActiveRecord)
|
||||||
printable = printable_without_active_record(object)
|
if object.is_a?(::ActiveRecord::Base)
|
||||||
return printable if !defined?(ActiveRecord::Base)
|
cast = :active_record_instance
|
||||||
|
elsif object.is_a?(Class) and object.ancestors.include?(::ActiveRecord::Base)
|
||||||
if printable == :self
|
cast = :active_record_class
|
||||||
if object.is_a?(ActiveRecord::Base)
|
end
|
||||||
printable = :active_record_instance
|
|
||||||
end
|
end
|
||||||
elsif printable == :class and object.ancestors.include?(ActiveRecord::Base)
|
cast
|
||||||
printable = :active_record_class
|
|
||||||
end
|
end
|
||||||
printable
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format ActiveRecord instance object.
|
private
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def awesome_active_record_instance(object)
|
|
||||||
return object.inspect if !defined?(ActiveSupport::OrderedHash)
|
|
||||||
|
|
||||||
data = object.class.column_names.inject(ActiveSupport::OrderedHash.new) do |hash, name|
|
# Format ActiveRecord instance object.
|
||||||
hash[name.to_sym] = object.send(name) if object.has_attribute?(name) || object.new_record?
|
#------------------------------------------------------------------------------
|
||||||
hash
|
def awesome_active_record_instance(object)
|
||||||
|
return object.inspect if !defined?(::ActiveSupport::OrderedHash)
|
||||||
|
|
||||||
|
data = object.class.column_names.inject(::ActiveSupport::OrderedHash.new) do |hash, name|
|
||||||
|
hash[name.to_sym] = object.send(name) if object.has_attribute?(name) || object.new_record?
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
"#{object} " + awesome_hash(data)
|
||||||
end
|
end
|
||||||
"#{object} " + awesome_hash(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format ActiveRecord class object.
|
# Format ActiveRecord class object.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
def awesome_active_record_class(object)
|
def awesome_active_record_class(object)
|
||||||
return object.inspect if !defined?(ActiveSupport::OrderedHash) || !object.respond_to?(:columns)
|
return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:columns)
|
||||||
|
|
||||||
data = object.columns.inject(ActiveSupport::OrderedHash.new) do |hash, c|
|
data = object.columns.inject(::ActiveSupport::OrderedHash.new) do |hash, c|
|
||||||
hash[c.name.to_sym] = c.type
|
hash[c.name.to_sym] = c.type
|
||||||
hash
|
hash
|
||||||
|
end
|
||||||
|
"class #{object} < #{object.superclass} " << awesome_hash(data)
|
||||||
end
|
end
|
||||||
"class #{object} < #{object.superclass} " << awesome_hash(data)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
AwesomePrint.send(:include, AwesomePrintActiveRecord)
|
AwesomePrint::Formatter.send(:include, AwesomePrint::ActiveRecord)
|
||||||
|
|
|
@ -3,44 +3,38 @@
|
||||||
# Awesome Print is freely distributable under the terms of MIT license.
|
# Awesome Print is freely distributable under the terms of MIT license.
|
||||||
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
module AwesomePrintActiveSupport
|
module AwesomePrint
|
||||||
|
module ActiveSupport
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.send :alias_method, :printable_without_active_support, :printable
|
base.send :alias_method, :cast_without_active_support, :cast
|
||||||
base.send :alias_method, :printable, :printable_with_active_support
|
base.send :alias_method, :cast, :cast_with_active_support
|
||||||
end
|
|
||||||
|
|
||||||
# Add ActiveSupport class names to the dispatcher pipeline.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
def printable_with_active_support(object)
|
|
||||||
printable = printable_without_active_support(object)
|
|
||||||
return printable if !defined?(ActiveSupport::TimeWithZone) || !defined?(HashWithIndifferentAccess)
|
|
||||||
|
|
||||||
if printable == :self
|
|
||||||
if object.is_a?(ActiveSupport::TimeWithZone)
|
|
||||||
printable = :active_support_time
|
|
||||||
elsif object.is_a?(HashWithIndifferentAccess)
|
|
||||||
printable = :hash_with_indifferent_access
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
printable
|
|
||||||
end
|
|
||||||
|
|
||||||
# Format ActiveSupport::TimeWithZone as standard Time.
|
def cast_with_active_support(object, type)
|
||||||
#------------------------------------------------------------------------------
|
cast = cast_without_active_support(object, type)
|
||||||
def awesome_active_support_time(object)
|
if defined?(::ActiveSupport) && defined?(::HashWithIndifferentAccess)
|
||||||
awesome_self(object, :as => :time)
|
if object.is_a?(::ActiveSupport::TimeWithZone)
|
||||||
end
|
cast = :active_support_time
|
||||||
|
elsif object.is_a?(::HashWithIndifferentAccess)
|
||||||
|
cast = :hash_with_indifferent_access
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cast
|
||||||
|
end
|
||||||
|
|
||||||
# Format HashWithIndifferentAccess as standard Hash.
|
# Format ActiveSupport::TimeWithZone as standard Time.
|
||||||
#
|
#------------------------------------------------------------------------------
|
||||||
# NOTE: can't use awesome_self(object, :as => :hash) since awesome_self uses
|
def awesome_active_support_time(object)
|
||||||
# object.inspect internally, i.e. it would convert hash to string.
|
awesome_self(object, :as => :time)
|
||||||
#------------------------------------------------------------------------------
|
end
|
||||||
def awesome_hash_with_indifferent_access(object)
|
|
||||||
awesome_hash(object)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Format HashWithIndifferentAccess as standard Hash.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def awesome_hash_with_indifferent_access(object)
|
||||||
|
awesome_hash(object)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
AwesomePrint.send(:include, AwesomePrintActiveSupport)
|
AwesomePrint::Formatter.send(:include, AwesomePrint::ActiveSupport)
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
require File.dirname(__FILE__) + "/ap/core_ext/#{file}"
|
require File.dirname(__FILE__) + "/ap/core_ext/#{file}"
|
||||||
end
|
end
|
||||||
|
|
||||||
require File.dirname(__FILE__) + "/ap/awesome_print"
|
require File.dirname(__FILE__) + "/ap/inspector"
|
||||||
|
require File.dirname(__FILE__) + "/ap/formatter"
|
||||||
require File.dirname(__FILE__) + "/ap/core_ext/logger" if defined?(Logger)
|
require File.dirname(__FILE__) + "/ap/core_ext/logger" if defined?(Logger)
|
||||||
require File.dirname(__FILE__) + "/ap/mixin/action_view" if defined?(ActionView)
|
require File.dirname(__FILE__) + "/ap/mixin/action_view" if defined?(ActionView)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ require 'action_view'
|
||||||
require 'ap/mixin/action_view'
|
require 'ap/mixin/action_view'
|
||||||
|
|
||||||
describe "AwesomePrint ActionView extensions" do
|
describe "AwesomePrint ActionView extensions" do
|
||||||
before(:each) do
|
before do
|
||||||
@view = ActionView::Base.new
|
@view = ActionView::Base.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,53 +4,51 @@ require 'active_record'
|
||||||
require 'ap/mixin/active_record'
|
require 'ap/mixin/active_record'
|
||||||
|
|
||||||
|
|
||||||
if defined?(::ActiveRecord)
|
# Create tableless ActiveRecord model.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
def self.columns()
|
||||||
|
@columns ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.column(name, sql_type = nil, default = nil, null = true)
|
||||||
|
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
|
||||||
|
end
|
||||||
|
|
||||||
|
column :id, :integer
|
||||||
|
column :name, :string
|
||||||
|
column :rank, :integer
|
||||||
|
column :admin, :boolean
|
||||||
|
column :created_at, :datetime
|
||||||
|
|
||||||
|
def self.table_exists?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class SubUser < User
|
||||||
|
def self.columns
|
||||||
|
User.columns
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "AwesomePrint::ActiveRecord" do
|
||||||
|
before do
|
||||||
|
stub_dotfile!
|
||||||
|
end
|
||||||
|
|
||||||
# Create tableless ActiveRecord model.
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
class User < ActiveRecord::Base
|
describe "ActiveRecord instance" do
|
||||||
def self.columns()
|
before do
|
||||||
@columns ||= []
|
ActiveRecord::Base.default_timezone = :utc
|
||||||
|
@diana = User.new(:name => "Diana", :rank => 1, :admin => false, :created_at => "1992-10-10 12:30:00")
|
||||||
|
@laura = User.new(:name => "Laura", :rank => 2, :admin => true, :created_at => "2003-05-26 14:15:00")
|
||||||
|
@ap = AwesomePrint::Inspector.new(:plain => true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.column(name, sql_type = nil, default = nil, null = true)
|
it "display single record" do
|
||||||
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
|
out = @ap.send(:awesome, @diana)
|
||||||
end
|
str = <<-EOS.strip
|
||||||
|
|
||||||
column :id, :integer
|
|
||||||
column :name, :string
|
|
||||||
column :rank, :integer
|
|
||||||
column :admin, :boolean
|
|
||||||
column :created_at, :datetime
|
|
||||||
|
|
||||||
def self.table_exists?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class SubUser < User
|
|
||||||
def self.columns
|
|
||||||
User.columns
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "AwesomePrint/ActiveRecord" do
|
|
||||||
before(:each) do
|
|
||||||
stub_dotfile!
|
|
||||||
end
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
describe "ActiveRecord instance" do
|
|
||||||
before(:each) do
|
|
||||||
ActiveRecord::Base.default_timezone = :utc
|
|
||||||
@diana = User.new(:name => "Diana", :rank => 1, :admin => false, :created_at => "1992-10-10 12:30:00")
|
|
||||||
@laura = User.new(:name => "Laura", :rank => 2, :admin => true, :created_at => "2003-05-26 14:15:00")
|
|
||||||
@ap = AwesomePrint.new(:plain => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "display single record" do
|
|
||||||
out = @ap.send(:awesome, @diana)
|
|
||||||
str = <<-EOS.strip
|
|
||||||
#<User:0x01234567> {
|
#<User:0x01234567> {
|
||||||
:id => nil,
|
:id => nil,
|
||||||
:name => "Diana",
|
:name => "Diana",
|
||||||
|
@ -59,18 +57,18 @@ if defined?(::ActiveRecord)
|
||||||
:created_at => ?
|
:created_at => ?
|
||||||
}
|
}
|
||||||
EOS
|
EOS
|
||||||
if RUBY_VERSION.to_f < 1.9
|
if RUBY_VERSION.to_f < 1.9
|
||||||
str.sub!('?', 'Sat Oct 10 12:30:00 UTC 1992')
|
str.sub!('?', 'Sat Oct 10 12:30:00 UTC 1992')
|
||||||
else
|
else
|
||||||
str.sub!('?', '1992-10-10 12:30:00 UTC')
|
str.sub!('?', '1992-10-10 12:30:00 UTC')
|
||||||
end
|
|
||||||
|
|
||||||
out.gsub(/0x([a-f\d]+)/, "0x01234567").should == str
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "display multiple records" do
|
out.gsub(/0x([a-f\d]+)/, "0x01234567").should == str
|
||||||
out = @ap.send(:awesome, [ @diana, @laura ])
|
end
|
||||||
str = <<-EOS.strip
|
|
||||||
|
it "display multiple records" do
|
||||||
|
out = @ap.send(:awesome, [ @diana, @laura ])
|
||||||
|
str = <<-EOS.strip
|
||||||
[
|
[
|
||||||
[0] #<User:0x01234567> {
|
[0] #<User:0x01234567> {
|
||||||
:id => nil,
|
:id => nil,
|
||||||
|
@ -88,23 +86,23 @@ EOS
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
EOS
|
EOS
|
||||||
if RUBY_VERSION.to_f < 1.9
|
if RUBY_VERSION.to_f < 1.9
|
||||||
str.sub!('?', 'Sat Oct 10 12:30:00 UTC 1992')
|
str.sub!('?', 'Sat Oct 10 12:30:00 UTC 1992')
|
||||||
str.sub!('!', 'Mon May 26 14:15:00 UTC 2003')
|
str.sub!('!', 'Mon May 26 14:15:00 UTC 2003')
|
||||||
else
|
else
|
||||||
str.sub!('?', '1992-10-10 12:30:00 UTC')
|
str.sub!('?', '1992-10-10 12:30:00 UTC')
|
||||||
str.sub!('!', '2003-05-26 14:15:00 UTC')
|
str.sub!('!', '2003-05-26 14:15:00 UTC')
|
||||||
end
|
|
||||||
|
|
||||||
out.gsub(/0x([a-f\d]+)/, "0x01234567").should == str
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
out.gsub(/0x([a-f\d]+)/, "0x01234567").should == str
|
||||||
describe "ActiveRecord class" do
|
end
|
||||||
it "should print the class" do
|
end
|
||||||
@ap = AwesomePrint.new(:plain => true)
|
|
||||||
@ap.send(:awesome, User).should == <<-EOS.strip
|
#------------------------------------------------------------------------------
|
||||||
|
describe "ActiveRecord class" do
|
||||||
|
it "should print the class" do
|
||||||
|
@ap = AwesomePrint::Inspector.new(:plain => true)
|
||||||
|
@ap.send(:awesome, User).should == <<-EOS.strip
|
||||||
class User < ActiveRecord::Base {
|
class User < ActiveRecord::Base {
|
||||||
:id => :integer,
|
:id => :integer,
|
||||||
:name => :string,
|
:name => :string,
|
||||||
|
@ -117,7 +115,7 @@ class User < ActiveRecord::Base {
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should print the class for non-direct subclasses of AR::Base" do
|
it "should print the class for non-direct subclasses of AR::Base" do
|
||||||
@ap = AwesomePrint.new(:plain => true)
|
@ap = AwesomePrint::Inspector.new(:plain => true)
|
||||||
@ap.send(:awesome, SubUser).should == <<-EOS.strip
|
@ap.send(:awesome, SubUser).should == <<-EOS.strip
|
||||||
class SubUser < User {
|
class SubUser < User {
|
||||||
:id => :integer,
|
:id => :integer,
|
||||||
|
@ -128,7 +126,6 @@ class SubUser < User {
|
||||||
}
|
}
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
22
spec/active_support_spec.rb
Executable file
22
spec/active_support_spec.rb
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
||||||
|
|
||||||
|
require 'active_support'
|
||||||
|
require 'ap/mixin/active_support'
|
||||||
|
|
||||||
|
describe "AwesomePrint::ActiveSupport" do
|
||||||
|
before do
|
||||||
|
stub_dotfile!
|
||||||
|
@ap = AwesomePrint::Inspector.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should format ActiveSupport::TimeWithZone as regular Time" do
|
||||||
|
Time.zone = 'Eastern Time (US & Canada)'
|
||||||
|
time = Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone
|
||||||
|
@ap.send(:awesome, time).should == "\e[0;32mSat, 10 Feb 2007 15:30:45 EST -05:00\e[0m"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should format HashWithIndifferentAccess as regular Hash" do
|
||||||
|
hash = HashWithIndifferentAccess.new({ :hello => "world" })
|
||||||
|
@ap.send(:awesome, hash).should == "{\n \"hello\"\e[0;37m => \e[0m\e[0;33m\"world\"\e[0m\n}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,12 +3,12 @@ require "bigdecimal"
|
||||||
require "rational"
|
require "rational"
|
||||||
|
|
||||||
describe "AwesomePrint" do
|
describe "AwesomePrint" do
|
||||||
before(:each) do
|
before do
|
||||||
stub_dotfile!
|
stub_dotfile!
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Array" do
|
describe "Array" do
|
||||||
before(:each) do
|
before do
|
||||||
@arr = [ 1, :two, "three", [ nil, [ true, false] ] ]
|
@arr = [ 1, :two, "three", [ nil, [ true, false] ] ]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Nested Array" do
|
describe "Nested Array" do
|
||||||
before(:each) do
|
before do
|
||||||
@arr = [ 1, 2 ]
|
@arr = [ 1, 2 ]
|
||||||
@arr << @arr
|
@arr << @arr
|
||||||
end
|
end
|
||||||
|
@ -161,7 +161,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Hash" do
|
describe "Hash" do
|
||||||
before(:each) do
|
before do
|
||||||
@hash = { 1 => { :sym => { "str" => { [1, 2, 3] => { { :k => :v } => Hash } } } } }
|
@hash = { 1 => { :sym => { "str" => { [1, 2, 3] => { { :k => :v } => Hash } } } } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Nested Hash" do
|
describe "Nested Hash" do
|
||||||
before(:each) do
|
before do
|
||||||
@hash = {}
|
@hash = {}
|
||||||
@hash[:a] = @hash
|
@hash[:a] = @hash
|
||||||
end
|
end
|
||||||
|
@ -265,7 +265,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Hash with several keys" do
|
describe "Hash with several keys" do
|
||||||
before(:each) do
|
before do
|
||||||
@hash = {"b" => "b", :a => "a", :z => "z", "alpha" => "alpha"}
|
@hash = {"b" => "b", :a => "a", :z => "z", "alpha" => "alpha"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ EOS
|
||||||
end
|
end
|
||||||
|
|
||||||
it "plain multiline with sorted keys" do
|
it "plain multiline with sorted keys" do
|
||||||
@hash.ai(:plain => true, :sorted_hash_keys => true).should == <<-EOS.strip
|
@hash.ai(:plain => true, :sort_keys => true).should == <<-EOS.strip
|
||||||
{
|
{
|
||||||
:a => "a",
|
:a => "a",
|
||||||
"alpha" => "alpha",
|
"alpha" => "alpha",
|
||||||
|
@ -304,7 +304,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Negative options[:indent]" do
|
describe "Negative options[:indent]" do
|
||||||
before(:each) do
|
before do
|
||||||
@hash = { [0, 0, 255] => :yellow, :red => "rgb(255, 0, 0)", "magenta" => "rgb(255, 0, 255)" }
|
@hash = { [0, 0, 255] => :yellow, :red => "rgb(255, 0, 0)", "magenta" => "rgb(255, 0, 255)" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ EOS
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Utility methods" do
|
describe "Utility methods" do
|
||||||
it "should merge options" do
|
it "should merge options" do
|
||||||
ap = AwesomePrint.new
|
ap = AwesomePrint::Inspector.new
|
||||||
ap.send(:merge_options!, { :color => { :array => :black }, :indent => 0 })
|
ap.send(:merge_options!, { :color => { :array => :black }, :indent => 0 })
|
||||||
options = ap.instance_variable_get("@options")
|
options = ap.instance_variable_get("@options")
|
||||||
options[:color][:array].should == :black
|
options[:color][:array].should == :black
|
||||||
|
@ -374,7 +374,7 @@ EOS
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
describe "Struct" do
|
describe "Struct" do
|
||||||
before(:each) do
|
before do
|
||||||
@struct = unless defined?(Struct::SimpleStruct)
|
@struct = unless defined?(Struct::SimpleStruct)
|
||||||
Struct.new("SimpleStruct", :name, :address).new
|
Struct.new("SimpleStruct", :name, :address).new
|
||||||
else
|
else
|
||||||
|
|
|
@ -17,8 +17,8 @@ describe "AwesomePrint logging extensions" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "the log level" do
|
describe "the log level" do
|
||||||
before(:each) do
|
before do
|
||||||
AwesomePrint.defaults = { }
|
AwesomePrint.defaults = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should fallback to the default :debug log level" do
|
it "should fallback to the default :debug log level" do
|
||||||
|
|
Loading…
Reference in a new issue