1
0
Fork 0
mirror of https://github.com/awesome-print/awesome_print synced 2023-03-27 23:22:34 -04:00

New approach for awesomeprint -- this uses a factory pattern to register formatters as they go. wip.

This commit is contained in:
James Cox 2019-01-22 11:01:43 -05:00
parent 4564fd7472
commit 2105efbf51
35 changed files with 418 additions and 275 deletions

1
.gitignore vendored
View file

@ -33,3 +33,4 @@ Gemfile.lock
# PROJECT::RBENV
.ruby-gemset
.awesome-print/
.byebug_history

1
.rspec
View file

@ -1 +1,2 @@
--format Fuubar
--color

View file

@ -24,8 +24,8 @@ matrix:
include:
- rvm: ruby-head
env: RUBYOPT="--enable-frozen-string-literal"
# allow_failures:
# - rvm: ruby-head
allow_failures:
- rvm: ruby-head
addons:
code_climate:

View file

@ -1,4 +1,6 @@
## master (unreleased)
## 2.0.0
- Fixes spec suite to properly work via travis, gets a clean build [@imajes, others]
- Adds support for ActiveModel::Errors [@dshinzie] - [#301]
- removes use of `strip_heredoc` from specs as it's a rails dep [@kstephens] - [#303]

View file

@ -24,7 +24,9 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']
s.add_development_dependency 'rspec', '>= 3.0.0'
s.add_development_dependency 'fuubar'
s.add_development_dependency 'appraisal'
s.add_development_dependency 'byebug'
s.add_development_dependency 'fakefs', '>= 0.2.1'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'nokogiri', '>= 1.6.5'

View file

@ -15,27 +15,29 @@ unless defined?(AwesomePrint::Inspector)
require 'awesome_print/custom_defaults'
require 'awesome_print/inspector'
require 'awesome_print/formatter'
Dir["./lib/awesome_print/formatters/**/*.rb"].each { |f| require f }
require 'awesome_print/version'
require 'awesome_print/core_ext/logger' if defined?(Logger)
#
# Load the following under normal circumstances as well as in Rails
# console when required from ~/.irbrc or ~/.pryrc.
#
require 'awesome_print/ext/active_record' if defined?(ActiveRecord) || AwesomePrint.rails_console?
require 'awesome_print/ext/active_support' if defined?(ActiveSupport) || AwesomePrint.rails_console?
#
# Load remaining extensions.
#
if defined?(ActiveSupport.on_load)
ActiveSupport.on_load(:action_view) do
require 'awesome_print/ext/action_view'
end
end
require 'awesome_print/ext/mongo_mapper' if defined?(MongoMapper)
require 'awesome_print/ext/mongoid' if defined?(Mongoid)
require 'awesome_print/ext/nokogiri' if defined?(Nokogiri)
require 'awesome_print/ext/nobrainer' if defined?(NoBrainer)
require 'awesome_print/ext/ripple' if defined?(Ripple)
require 'awesome_print/ext/sequel' if defined?(Sequel)
require 'awesome_print/ext/ostruct' if defined?(OpenStruct)
# require 'awesome_print/ext/active_record' if defined?(ActiveRecord) || AwesomePrint.rails_console?
# require 'awesome_print/ext/active_support' if defined?(ActiveSupport) || AwesomePrint.rails_console?
# #
# # Load remaining extensions.
# #
# if defined?(ActiveSupport.on_load)
# ActiveSupport.on_load(:action_view) do
# require 'awesome_print/ext/action_view'
# end
# end
# require 'awesome_print/ext/mongo_mapper' if defined?(MongoMapper)
# require 'awesome_print/ext/mongoid' if defined?(Mongoid)
# require 'awesome_print/ext/nokogiri' if defined?(Nokogiri)
# require 'awesome_print/ext/nobrainer' if defined?(NoBrainer)
# require 'awesome_print/ext/ripple' if defined?(Ripple)
# require 'awesome_print/ext/sequel' if defined?(Sequel)
# require 'awesome_print/ext/ostruct' if defined?(OpenStruct)
end

View file

@ -6,6 +6,8 @@ module AwesomePrint
# Pick the color and apply it to the given string as necessary.
#------------------------------------------------------------------------------
def colorize(str, type)
puts "[COLORIZING] - using #{options[:color][type]} for #{type}"
str = CGI.escapeHTML(str) if options[:html]
if options[:plain] || !options[:color][type] || !inspector.colorize?
str

View file

@ -1,27 +0,0 @@
# Copyright (c) 2010-2016 Michael Dvorkin and contributors
#
# 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
module OpenStruct
def self.included(base)
base.send :alias_method, :cast_without_ostruct, :cast
base.send :alias_method, :cast, :cast_with_ostruct
end
def cast_with_ostruct(object, type)
cast = cast_without_ostruct(object, type)
if (defined?(::OpenStruct)) && (object.is_a?(::OpenStruct))
cast = :open_struct_instance
end
cast
end
def awesome_open_struct_instance(object)
"#{object.class} #{awesome_hash(object.marshal_dump)}"
end
end
end
AwesomePrint::Formatter.send(:include, AwesomePrint::OpenStruct)

View file

@ -1,126 +1,53 @@
# Copyright (c) 2010-2016 Michael Dvorkin and contributors
#
# Awesome Print is freely distributable under the terms of MIT license.
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
require 'awesome_print/formatters'
require_relative 'colorize'
module AwesomePrint
class Formatter
include Colorize
attr_reader :inspector, :options
CORE_FORMATTERS = [:array, :bigdecimal, :class, :dir, :file, :hash, :method, :rational, :set, :struct, :unboundmethod]
# Acts as a class ivar
@registered_formatters = {}
# make it accessible
def self.registered_formatters
@registered_formatters
end
# register a new formatter..
#------------------------------------------------------------------------------
def self.register(formatter)
@registered_formatters[formatter.formatted_object_type.to_sym] = formatter
end
def initialize(inspector)
@inspector = inspector
@options = inspector.options
@inspector = inspector
@options = inspector.options
end
# Main entry point to format an object.
# type is determined by Inspector#printable
#------------------------------------------------------------------------------
def format(object, type = nil)
core_class = cast(object, type)
awesome = if core_class != :self
send(:"awesome_#{core_class}", object) # Core formatters.
puts "[FORMAT] #{type.to_s.red} >>> #{object}"
format_with = active_formatter(type)
puts "[ACTIVE] using > #{format_with.to_s.blueish} < to format"
if format_with && format_with.send(:formattable?, object)
format_with.new(@inspector).format(object)
else
awesome_self(object, type) # Catch all that falls back to object.inspect.
end
awesome
end
# Hook this when adding custom formatters. Check out lib/awesome_print/ext
# directory for custom formatters that ship with awesome_print.
#------------------------------------------------------------------------------
def cast(object, type)
CORE_FORMATTERS.include?(type) ? type : :self
end
private
# Catch all method to format an arbitrary object.
#------------------------------------------------------------------------------
def awesome_self(object, type)
if @options[:raw] && object.instance_variables.any?
awesome_object(object)
elsif (hash = convert_to_hash(object))
awesome_hash(hash)
else
awesome_simple(object.inspect.to_s, type, @inspector)
puts "[FALLBACK] well darn, we're just gonna have to fb"
# in this case, formatter is missing or fails format test
AwesomePrint::Formatters::FallbackFormatter.new(@inspector).format(object)
end
end
def awesome_bigdecimal(n)
o = n.to_s('F')
type = :bigdecimal
awesome_simple(o, type, @inspector)
end
def awesome_rational(n)
o = n.to_s
type = :rational
awesome_simple(o, type, @inspector)
end
def awesome_simple(o, type, inspector = @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, @inspector).format
end
def awesome_struct(s)
Formatters::StructFormatter.new(s, @inspector).format
end
def awesome_method(m)
Formatters::MethodFormatter.new(m, @inspector).format
end
alias :awesome_unboundmethod :awesome_method
def awesome_class(c)
Formatters::ClassFormatter.new(c, @inspector).format
end
def awesome_file(f)
Formatters::FileFormatter.new(f, @inspector).format
end
def awesome_dir(d)
Formatters::DirFormatter.new(d, @inspector).format
end
# Utility methods.
#------------------------------------------------------------------------------
def convert_to_hash(object)
if !object.respond_to?(:to_hash)
return nil
end
if object.method(:to_hash).arity != 0
return nil
end
hash = object.to_hash
if !hash.respond_to?(:keys) || !hash.respond_to?('[]')
return nil
end
return hash
def active_formatter(type)
self.class.registered_formatters[type]
end
end
end

View file

@ -3,16 +3,19 @@ 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
formatter_for :array
def self.formattable?(object)
object.is_a?(Array)
end
def format
if array.length.zero?
attr_reader :array
def format(object)
@array = object
if object.length.zero?
'[]'
elsif methods_array?
methods_array

View file

@ -1,68 +1,26 @@
require_relative '../colorize'
require_relative '../limiter'
require_relative '../registrar'
module AwesomePrint
module Formatters
class BaseFormatter
include Colorize
include Registrar
include Limiter
DEFAULT_LIMIT_SIZE = 7
attr_reader :object, :inspector, :options
# 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?(Integer) and options[:limit] > 0)
def initialize(inspector)
@inspector = inspector
@options = inspector.options
end
def get_limit_size
case options[:limit]
when true
DEFAULT_LIMIT_SIZE
else
options[:limit]
end
def format(object)
raise NotImplementedError
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]
temp[head] = if is_hash
"#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
else
"#{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://readruby.chengguangnan.com/methods#method-objects-parameters
@ -104,6 +62,7 @@ module AwesomePrint
#
# Indentation related methods
# FIXME: move to Indentator?...
#-----------------------------------------
def indentation
inspector.current_indentation

View file

@ -0,0 +1,19 @@
require_relative 'base_formatter'
module AwesomePrint
module Formatters
class BigdecimalFormatter < BaseFormatter
formatter_for :bigdecimal
def self.formattable?(object)
true
end
def format(object)
colorize(object.to_s('F'), self.class.formatted_object_type)
end
end
end
end

View file

@ -4,16 +4,15 @@ module AwesomePrint
module Formatters
class ClassFormatter < BaseFormatter
attr_reader :klass, :inspector, :options
formatter_for :class
def initialize(klass, inspector)
@klass = klass
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.is_a?(Class)
end
def format
def format(klass)
superclass = klass.superclass
if superclass
colorize("#{klass.inspect} < #{superclass}", :class)
else

View file

@ -5,15 +5,13 @@ module AwesomePrint
module Formatters
class DirFormatter < BaseFormatter
attr_reader :dir, :inspector, :options
formatter_for :dir
def initialize(dir, inspector)
@dir = dir
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.is_a?(Dir)
end
def format
def format(dir)
ls = `ls -alF #{dir.path.shellescape}`
colorize(ls.empty? ? dir.inspect : "#{dir.inspect}\n#{ls.chop}", :dir)
end

View file

@ -0,0 +1,51 @@
require_relative 'base_formatter'
# this handles some fallback logic to route things we don't know what they are
module AwesomePrint
module Formatters
class FallbackFormatter < BaseFormatter
formatter_for :self
def self.formattable?(object)
true
end
def format(object)
if @options[:raw] && object.instance_variables.any?
Formatters::ObjectFormatter.new(@inspector).format(object)
elsif (hash = convert_to_hash(object))
Formatters::HashFormatter.new(@inspector).format(hash)
else
Formatters::SimpleFormatter.new(@inspector).format(object.inspect.to_s)
end
end
private
# Utility methods.
#------------------------------------------------------------------------------
# FIXME: this could be super fixed.
#
def convert_to_hash(object)
if !object.respond_to?(:to_hash)
return nil
end
if object.method(:to_hash).arity != 0
return nil
end
hash = object.to_hash
if !hash.respond_to?(:keys) || !hash.respond_to?('[]')
return nil
end
return hash
end
end
end
end

View file

@ -0,0 +1,15 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class FalseClassFormatter < SimpleFormatter
formatter_for :falseclass
def self.formattable?(object)
object == false
end
end
end
end

View file

@ -5,15 +5,13 @@ module AwesomePrint
module Formatters
class FileFormatter < BaseFormatter
attr_reader :file, :inspector, :options
formatter_for :file
def initialize(file, inspector)
@file = file
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.is_a?(File)
end
def format
def format(file)
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

View file

@ -3,15 +3,20 @@ 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
attr_reader :hash
formatter_for :hash
def self.formattable?(object)
object.is_a?(Hash)
end
def format
# INSTANCE METHODS BELOW
def format(hash)
@hash = hash
if hash.empty?
empty_hash
elsif multiline_hash?

View file

@ -0,0 +1,11 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class IntegerFormatter < SimpleFormatter
formatter_for :integer
end
end
end

View file

@ -4,15 +4,14 @@ module AwesomePrint
module Formatters
class MethodFormatter < BaseFormatter
attr_reader :method, :inspector, :options
formatter_for :method
def initialize(method, inspector)
@method = method
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
puts "formattable? for METHOD..."
true
end
def format
def format(method)
name, args, owner = method_tuple(method)
"#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}"

View file

@ -0,0 +1,19 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class NilClassFormatter < SimpleFormatter
formatter_for :nilclass
def self.formattable?(object)
object == nil
end
def format(object)
colorize('nil', self.class.formatted_object_type)
end
end
end
end

View file

@ -4,16 +4,18 @@ module AwesomePrint
module Formatters
class ObjectFormatter < BaseFormatter
attr_reader :object, :variables, :inspector, :options
formatter_for :object
def initialize(object, inspector)
@object = object
@variables = object.instance_variables
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.is_a?(Object)
end
def format
attr_reader :variables
def format(object)
@object = object
@variables = object.instance_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 object.respond_to?(:"#{property}=")

View file

@ -0,0 +1,19 @@
require_relative 'base_formatter'
module AwesomePrint
module Formatters
class OpenStruct < BaseFormatter
formatter_for :openstruct
def self.formattable?(object)
defined?(::OpenStruct) && object.is_a?(::OpenStruct)
end
def format(object)
"#{object.class} #{HashFormatter.new(inspector).format(object.marshal_dump)}"
end
end
end
end

View file

@ -0,0 +1,11 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class RationalFormatter < SimpleFormatter
formatter_for :rational
end
end
end

View file

@ -4,18 +4,16 @@ module AwesomePrint
module Formatters
class SimpleFormatter < BaseFormatter
attr_reader :string, :type, :inspector, :options
formatter_for :simple
def initialize(string, type, inspector)
@string = string
@type = type
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.respond_to?(:to_s)
end
def format
colorize(string, type)
def format(object)
colorize(object.to_s, self.class.formatted_object_type)
end
end
end
end

View file

@ -0,0 +1,11 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class StringFormatter < SimpleFormatter
formatter_for :string
end
end
end

View file

@ -4,16 +4,18 @@ module AwesomePrint
module Formatters
class StructFormatter < BaseFormatter
attr_reader :struct, :variables, :inspector, :options
formatter_for :struct
def initialize(struct, inspector)
@struct = struct
@variables = struct.members
@inspector = inspector
@options = inspector.options
def self.formattable?(object)
object.is_a?(Struct)
end
def format
attr_reader :struct, :variables
def format(struct)
@struct = struct
@variables = struct.members
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 struct.respond_to?(:"#{property}=")

View file

@ -0,0 +1,18 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class SymbolFormatter < SimpleFormatter
formatter_for :symbol
def self.formattable?(object)
object.respond_to?(:to_s)
end
def format(object)
colorize(object.to_s, self.class.formatted_object_type)
end
end
end
end

View file

@ -0,0 +1,15 @@
require_relative 'simple_formatter'
module AwesomePrint
module Formatters
class TrueClassFormatter < SimpleFormatter
formatter_for :trueclass
def self.formattable?(object)
object == true
end
end
end
end

View file

@ -42,6 +42,7 @@ module AwesomePrint
rational: :blue,
string: :yellowish,
struct: :pale,
openstruct: :pale,
symbol: :cyanish,
time: :greenish,
trueclass: :green,
@ -106,9 +107,10 @@ module AwesomePrint
#---------------------------------------------------------------------------
def nested(object)
case printable(object)
when :array then @formatter.colorize('[...]', :array)
when :hash then @formatter.colorize('{...}', :hash)
when :struct then @formatter.colorize('{...}', :struct)
when :array then @formatter.colorize('[...]', :array)
when :hash then @formatter.colorize('{...}', :hash)
when :struct then @formatter.colorize('{...}', :struct)
when :openstruct then @formatter.colorize('{...}', :openstruct)
else @formatter.colorize("...#{object.class}...", :class)
end
end
@ -123,14 +125,7 @@ module AwesomePrint
# 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
object.class.to_s.gsub(/:+/, '_').downcase.to_sym
end
# Update @options by first merging the :color hash and then the remaining

View file

@ -0,0 +1,63 @@
module AwesomePrint
module Limiter
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?(Integer) 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]
temp[head] = if is_hash
"#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}"
else
"#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]"
end
temp
end
end
end
end

View file

@ -0,0 +1,20 @@
module AwesomePrint
module Registrar
def self.included(base)
base.send(:extend, ClassMethods)
end
module ClassMethods
attr_accessor :formatted_object_type
def formatter_for(type)
self.formatted_object_type = type
AwesomePrint::Formatter.register(self)
end
end
end
end

View file

@ -1,4 +1,5 @@
require 'spec_helper'
require 'ostruct'
RSpec.describe 'AwesomePrint Ostruct extension' do
before do

View file

@ -119,7 +119,7 @@ EOS
EOS
end
it 'colored single line' do
it 'colored single line', focus: true do
expect(@arr.ai(multiline: false)).to eq("[ \e[1;34m1\e[0m, \e[0;36m:two\e[0m, \e[0;33m\"three\"\e[0m, [ \e[1;31mnil\e[0m, [ \e[1;32mtrue\e[0m, \e[1;31mfalse\e[0m ] ] ]")
end
end

View file

@ -25,6 +25,8 @@ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each do |file|
require file
end
require 'byebug'
ExtVerifier.require_dependencies!(
%w(
rails