diff --git a/lib/awesome_print/colorize.rb b/lib/awesome_print/colorize.rb new file mode 100644 index 0000000..33f438e --- /dev/null +++ b/lib/awesome_print/colorize.rb @@ -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|#{str}| if options[:html] + str.send(options[:color][type]) + end + end + end +end diff --git a/lib/awesome_print/formatter.rb b/lib/awesome_print/formatter.rb index cff0c55..94dccbf 100644 --- a/lib/awesome_print/formatter.rb +++ b/lib/awesome_print/formatter.rb @@ -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|#{str}| 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: - # - # # - # # - # #)#_username> - # # - # # - # # - # - 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 diff --git a/lib/awesome_print/formatters/array_formatter.rb b/lib/awesome_print/formatters/array_formatter.rb new file mode 100644 index 0000000..eda390d --- /dev/null +++ b/lib/awesome_print/formatters/array_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/base_formatter.rb b/lib/awesome_print/formatters/base_formatter.rb new file mode 100644 index 0000000..802a90b --- /dev/null +++ b/lib/awesome_print/formatters/base_formatter.rb @@ -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: + # + # # + # # + # #)#_username> + # # + # # + # # + # + 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 diff --git a/lib/awesome_print/formatters/class_formatter.rb b/lib/awesome_print/formatters/class_formatter.rb new file mode 100644 index 0000000..8cc1089 --- /dev/null +++ b/lib/awesome_print/formatters/class_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/dir_formatter.rb b/lib/awesome_print/formatters/dir_formatter.rb new file mode 100644 index 0000000..d773520 --- /dev/null +++ b/lib/awesome_print/formatters/dir_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/file_formatter.rb b/lib/awesome_print/formatters/file_formatter.rb new file mode 100644 index 0000000..3bf62b6 --- /dev/null +++ b/lib/awesome_print/formatters/file_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/hash_formatter.rb b/lib/awesome_print/formatters/hash_formatter.rb new file mode 100644 index 0000000..1435e43 --- /dev/null +++ b/lib/awesome_print/formatters/hash_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/method_formatter.rb b/lib/awesome_print/formatters/method_formatter.rb new file mode 100644 index 0000000..26c398d --- /dev/null +++ b/lib/awesome_print/formatters/method_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/object_formatter.rb b/lib/awesome_print/formatters/object_formatter.rb new file mode 100644 index 0000000..c5a8d91 --- /dev/null +++ b/lib/awesome_print/formatters/object_formatter.rb @@ -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 diff --git a/lib/awesome_print/formatters/simple_formatter.rb b/lib/awesome_print/formatters/simple_formatter.rb new file mode 100644 index 0000000..bdd274f --- /dev/null +++ b/lib/awesome_print/formatters/simple_formatter.rb @@ -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 diff --git a/lib/awesome_print/indentator.rb b/lib/awesome_print/indentator.rb new file mode 100644 index 0000000..73e876b --- /dev/null +++ b/lib/awesome_print/indentator.rb @@ -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 diff --git a/lib/awesome_print/inspector.rb b/lib/awesome_print/inspector.rb index 0893a90..c519322 100644 --- a/lib/awesome_print/inspector.rb +++ b/lib/awesome_print/inspector.rb @@ -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. #------------------------------------------------------------------------------