├── .rspec ├── .github ├── CODEOWNERS └── workflows │ ├── lint.yml │ ├── rspec.yml │ └── release.yml ├── .codeclimate ├── github-api-demo.gif ├── .editorconfig ├── .codeclimate.yml ├── lib ├── amazing_print │ ├── version.rb │ ├── indentator.rb │ ├── formatters │ │ ├── simple_formatter.rb │ │ ├── method_formatter.rb │ │ ├── class_formatter.rb │ │ ├── dir_formatter.rb │ │ ├── file_formatter.rb │ │ ├── mswin_helper.rb │ │ ├── struct_formatter.rb │ │ ├── object_formatter.rb │ │ ├── array_formatter.rb │ │ ├── hash_formatter.rb │ │ └── base_formatter.rb │ ├── colorize.rb │ ├── formatters.rb │ ├── ext │ │ ├── action_view.rb │ │ ├── ostruct.rb │ │ ├── active_support.rb │ │ ├── nokogiri.rb │ │ ├── nobrainer.rb │ │ ├── sequel.rb │ │ ├── ripple.rb │ │ ├── mongoid.rb │ │ ├── mongo_mapper.rb │ │ └── active_record.rb │ ├── core_ext │ │ ├── kernel.rb │ │ ├── object.rb │ │ ├── logger.rb │ │ ├── class.rb │ │ └── awesome_method_array.rb │ ├── ext_loader.rb │ ├── json_helper.rb │ ├── colors.rb │ ├── custom_defaults.rb │ └── formatter.rb ├── ap.rb └── amazing_print.rb ├── .gitignore ├── gemfiles ├── mongoid_7.0.gemfile ├── mongoid_8.0.gemfile ├── rails_8.0.gemfile ├── rails_8.1.gemfile ├── rails_main.gemfile ├── sequel_5.0.gemfile ├── rails_7.1.gemfile ├── rails_7.2.gemfile ├── rails_6.1.gemfile └── rails_7.0.gemfile ├── spec ├── sequel_helper.rb ├── support │ ├── mongoid_versions.rb │ ├── active_record_data.rb │ ├── active_record_data │ │ ├── 3_2_diana.txt │ │ ├── 3_2_diana_legacy.txt │ │ ├── 3_2_multi.txt │ │ ├── 3_2_multi_legacy.txt │ │ ├── 4_1_diana.txt │ │ ├── 4_0_diana.txt │ │ ├── 4_2_diana.txt │ │ ├── 4_2_diana_legacy.txt │ │ ├── 5_1_diana.txt │ │ ├── 5_0_diana.txt │ │ ├── 6_0_diana.txt │ │ ├── 5_2_diana.txt │ │ ├── 6_1_diana.txt │ │ ├── 7_0_diana.txt │ │ ├── 8_0_diana.txt │ │ ├── 7_2_diana.txt │ │ └── 7_1_diana.txt │ ├── ext_verifier.rb │ └── rails_versions.rb ├── ext │ ├── ostruct_spec.rb │ ├── action_view_spec.rb │ ├── active_support_spec.rb │ ├── sequel_spec.rb │ ├── ripple_spec.rb │ ├── action_controller_spec.rb │ ├── active_model_spec.rb │ ├── nobrainer_spec.rb │ ├── nokogiri_spec.rb │ └── mongoid_spec.rb ├── active_record_helper.rb ├── core_ext │ └── logger_spec.rb ├── spec_helper.rb ├── colors_spec.rb └── objects_spec.rb ├── Rakefile ├── Gemfile ├── .rubocop.yml ├── .travis.yml ├── LICENSE ├── amazing_print.gemspec ├── Appraisals ├── CONTRIBUTING.md └── CHANGELOG.md /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @amazing-print/developers 2 | -------------------------------------------------------------------------------- /.codeclimate: -------------------------------------------------------------------------------- 1 | engines: 2 | rubocop: 3 | enabled: true 4 | reek: 5 | enabled: true 6 | -------------------------------------------------------------------------------- /github-api-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazing-print/amazing_print/HEAD/github-api-demo.gif -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | indent_style = space 5 | indent_size = 2 6 | trim_trailing_whitespace = true 7 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - ruby 8 | fixme: 9 | enabled: true 10 | rubocop: 11 | enabled: true 12 | ratings: 13 | paths: 14 | - "**.rb" 15 | exclude_paths: 16 | - spec/ 17 | -------------------------------------------------------------------------------- /lib/amazing_print/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | def self.version 10 | '2.0.0' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## MAC OS 2 | .DS_Store 3 | 4 | ## TEXTMATE 5 | *.tmproj 6 | tmtags 7 | 8 | ## EMACS 9 | *~ 10 | \#* 11 | .\#* 12 | 13 | ## VIM 14 | *.swp 15 | 16 | ## RUBYMINE 17 | .idea 18 | 19 | ## PROJECT::GENERAL 20 | coverage 21 | rdoc 22 | pkg 23 | .ruby-version 24 | gemfiles/*.gemfile.lock 25 | Gemfile.lock 26 | /tmp 27 | /.bundle 28 | /gemfiles/.bundle 29 | 30 | ## PROJECT::RVM 31 | .rvmrc 32 | 33 | # PROJECT::RBENV 34 | .ruby-gemset 35 | *.gem 36 | -------------------------------------------------------------------------------- /lib/amazing_print/indentator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AmazingPrint 4 | class Indentator 5 | attr_reader :shift_width, :indentation 6 | 7 | def initialize(indentation) 8 | @indentation = indentation 9 | @shift_width = indentation.freeze 10 | end 11 | 12 | def indent 13 | @indentation += shift_width 14 | yield 15 | ensure 16 | @indentation -= shift_width 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/ap.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | # 9 | # Keeping this for backwards compatibility to allow 10 | # require "ap" 11 | # 12 | require_relative 'amazing_print' 13 | -------------------------------------------------------------------------------- /gemfiles/mongoid_7.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "concurrent-ruby", "1.3.4" 8 | gem "fakefs", "~> 1.2" 9 | gem "logger", "~> 1.7" 10 | gem "mongoid", "~> 7.0.0" 11 | gem "nokogiri", "~> 1.18.3" 12 | gem "ostruct" 13 | gem "pry" 14 | gem "rspec", "~> 3.9" 15 | gem "rubocop", "~> 1.20" 16 | gem "rubocop-rspec", "~> 3.6" 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/mongoid_8.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "concurrent-ruby", "1.3.4" 8 | gem "fakefs", "~> 1.2" 9 | gem "logger", "~> 1.7" 10 | gem "mongoid", "~> 8.0.0" 11 | gem "nokogiri", "~> 1.18.3" 12 | gem "ostruct" 13 | gem "pry" 14 | gem "rspec", "~> 3.9" 15 | gem "rubocop", "~> 1.20" 16 | gem "rubocop-rspec", "~> 3.6" 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails_8.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "fakefs", "~> 1.2" 8 | gem "logger", "~> 1.7" 9 | gem "nokogiri", "~> 1.18.3" 10 | gem "ostruct" 11 | gem "pry" 12 | gem "rails", "~> 8.0.0" 13 | gem "rspec", "~> 3.9" 14 | gem "rubocop", "~> 1.20" 15 | gem "rubocop-rspec", "~> 3.6" 16 | gem "sqlite3", ">= 2.1", platform: :mri 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails_8.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "fakefs", "~> 1.2" 8 | gem "logger", "~> 1.7" 9 | gem "nokogiri", "~> 1.18.3" 10 | gem "ostruct" 11 | gem "pry" 12 | gem "rails", "~> 8.1.0" 13 | gem "rspec", "~> 3.9" 14 | gem "rubocop", "~> 1.20" 15 | gem "rubocop-rspec", "~> 3.6" 16 | gem "sqlite3", ">= 2.1", platform: :mri 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lints 2 | 3 | on: 4 | merge_group: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | lint: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: 3.3 20 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 21 | - name: Lint files with Rubocop 22 | run: | 23 | bundle exec rubocop 24 | -------------------------------------------------------------------------------- /spec/sequel_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if ExtVerifier.has_sequel? 4 | # Establish connection to in-memory SQLite DB 5 | DB = RUBY_PLATFORM == 'java' ? Sequel.connect('jdbc:sqlite::memory:') : Sequel.sqlite 6 | 7 | # Create the users table 8 | DB.create_table :sequel_users do 9 | primary_key :id 10 | String :first_name 11 | String :last_name 12 | TrueClass :admin 13 | DateTime :created_at 14 | end 15 | 16 | # Create models 17 | class SequelUser < Sequel::Model; end 18 | end 19 | -------------------------------------------------------------------------------- /gemfiles/rails_main.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "fakefs", "~> 1.2" 8 | gem "logger", "~> 1.7" 9 | gem "nokogiri", "~> 1.18.3" 10 | gem "ostruct" 11 | gem "pry" 12 | gem "rails", github: "rails/rails" 13 | gem "rspec", "~> 3.9" 14 | gem "rubocop", "~> 1.20" 15 | gem "rubocop-rspec", "~> 3.6" 16 | gem "sqlite3", ">= 2.1", platform: :mri 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/sequel_5.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 6 | gem "bigdecimal" 7 | gem "fakefs", "~> 1.2" 8 | gem "jdbc-sqlite3", platform: :jruby 9 | gem "logger", "~> 1.7" 10 | gem "nokogiri", "~> 1.18.3" 11 | gem "ostruct" 12 | gem "pry" 13 | gem "rspec", "~> 3.9" 14 | gem "rubocop", "~> 1.20" 15 | gem "rubocop-rspec", "~> 3.6" 16 | gem "sequel", "~> 5.0" 17 | gem "sqlite3", "~> 1.4", platform: :mri 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rubygems' 4 | require 'bundler/setup' 5 | 6 | require 'bundler' 7 | Bundler::GemHelper.install_tasks 8 | 9 | task :default do 10 | if ENV['BUNDLE_GEMFILE'] =~ /gemfiles/ 11 | Rake::Task['spec'].invoke 12 | else 13 | Rake::Task['appraise'].invoke 14 | end 15 | end 16 | 17 | task :appraise do 18 | exec 'appraisal install && appraisal rake' 19 | end 20 | 21 | desc 'Run all amazing_print gem specs' 22 | task :spec do 23 | # Run plain rspec command without RSpec::Core::RakeTask overrides. 24 | exec 'rspec -c spec' 25 | end 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in amazing_print.gemspec 6 | gemspec 7 | 8 | # Development dependencies 9 | # Update this once https://github.com/thoughtbot/appraisal/pull/202 is released 10 | gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git', branch: :main 11 | 12 | gem 'bigdecimal' 13 | gem 'fakefs', '~> 1.2' 14 | gem 'logger', '~> 1.7' 15 | gem 'nokogiri', '~> 1.18.3' 16 | gem 'ostruct' 17 | gem 'pry' 18 | gem 'rspec', '~> 3.9' 19 | gem 'rubocop', '~> 1.20' 20 | gem 'rubocop-rspec', '~> 3.6' 21 | -------------------------------------------------------------------------------- /gemfiles/rails_7.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord-jdbcsqlite3-adapter", "~> 71.0", platform: :jruby 6 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 7 | gem "bigdecimal" 8 | gem "fakefs", "~> 1.2" 9 | gem "logger", "~> 1.7" 10 | gem "nokogiri", "~> 1.18.3" 11 | gem "ostruct" 12 | gem "pry" 13 | gem "rails", "~> 7.1.0" 14 | gem "rspec", "~> 3.9" 15 | gem "rubocop", "~> 1.20" 16 | gem "rubocop-rspec", "~> 3.6" 17 | gem "sqlite3", "~> 2.6", platform: :mri 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /gemfiles/rails_7.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord-jdbcsqlite3-adapter", "~> 72.0", platform: :jruby 6 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 7 | gem "bigdecimal" 8 | gem "fakefs", "~> 1.2" 9 | gem "logger", "~> 1.7" 10 | gem "nokogiri", "~> 1.18.3" 11 | gem "ostruct" 12 | gem "pry" 13 | gem "rails", "~> 7.2.0" 14 | gem "rspec", "~> 3.9" 15 | gem "rubocop", "~> 1.20" 16 | gem "rubocop-rspec", "~> 3.6" 17 | gem "sqlite3", "~> 2.6", platform: :mri 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/simple_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class SimpleFormatter < BaseFormatter 8 | attr_reader :string, :type, :inspector, :options 9 | 10 | def initialize(string, type, inspector) 11 | super() 12 | @string = string 13 | @type = type 14 | @inspector = inspector 15 | @options = inspector.options 16 | end 17 | 18 | def format 19 | colorize(string, type) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /gemfiles/rails_6.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platform: :jruby 6 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 7 | gem "bigdecimal" 8 | gem "concurrent-ruby", "1.3.4" 9 | gem "fakefs", "~> 1.2" 10 | gem "logger", "~> 1.7" 11 | gem "nokogiri", "~> 1.18.3" 12 | gem "ostruct" 13 | gem "pry" 14 | gem "rails", "~> 6.1.0" 15 | gem "rspec", "~> 3.9" 16 | gem "rubocop", "~> 1.20" 17 | gem "rubocop-rspec", "~> 3.6" 18 | gem "sqlite3", "~> 1.4", platform: :mri 19 | 20 | gemspec path: "../" 21 | -------------------------------------------------------------------------------- /gemfiles/rails_7.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platform: :jruby 6 | gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", branch: :main 7 | gem "bigdecimal" 8 | gem "concurrent-ruby", "1.3.4" 9 | gem "fakefs", "~> 1.2" 10 | gem "logger", "~> 1.7" 11 | gem "nokogiri", "~> 1.18.3" 12 | gem "ostruct" 13 | gem "pry" 14 | gem "rails", "~> 7.0.0" 15 | gem "rspec", "~> 3.9" 16 | gem "rubocop", "~> 1.20" 17 | gem "rubocop-rspec", "~> 3.6" 18 | gem "sqlite3", "~> 1.4", platform: :mri 19 | 20 | gemspec path: "../" 21 | -------------------------------------------------------------------------------- /spec/support/mongoid_versions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module MongoidVersions 4 | def mongoid_version 5 | Gem::Version.new(Mongoid::VERSION) 6 | end 7 | 8 | def mongoid_4_0? 9 | Gem::Requirement.new('~> 4.0.0').satisfied_by?(mongoid_version) 10 | end 11 | 12 | def mongoid_5_0? 13 | Gem::Requirement.new('~> 5.0.0').satisfied_by?(mongoid_version) 14 | end 15 | 16 | def mongoid_6_0? 17 | Gem::Requirement.new('~> 6.0.0').satisfied_by?(mongoid_version) 18 | end 19 | end 20 | 21 | RSpec.configure do |config| 22 | config.include(MongoidVersions) 23 | config.extend(MongoidVersions) 24 | end 25 | -------------------------------------------------------------------------------- /lib/amazing_print/colorize.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | autoload :CGI, 'cgi' 4 | 5 | require_relative 'colors' 6 | 7 | module AmazingPrint 8 | module Colorize 9 | # Pick the color and apply it to the given string as necessary. 10 | #------------------------------------------------------------------------------ 11 | def colorize(str, type) 12 | str = CGI.escapeHTML(str) if options[:html] 13 | return str if options[:colors] == :none || !options[:color][type] || !inspector.colorize? 14 | 15 | AmazingPrint::Colors.public_send(options[:color][type], str, options[:html]) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AmazingPrint 4 | module Formatters 5 | require_relative 'formatters/object_formatter' 6 | require_relative 'formatters/struct_formatter' 7 | require_relative 'formatters/hash_formatter' 8 | require_relative 'formatters/array_formatter' 9 | require_relative 'formatters/simple_formatter' 10 | require_relative 'formatters/method_formatter' 11 | require_relative 'formatters/class_formatter' 12 | require_relative 'formatters/dir_formatter' 13 | require_relative 'formatters/file_formatter' 14 | require_relative 'colorize' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/ext/ostruct_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe 'AmazingPrint Ostruct extension' do 6 | before do 7 | @ap = AmazingPrint::Inspector.new(colors: :none, sort_keys: true) 8 | end 9 | 10 | it 'empty hash' do 11 | struct = OpenStruct.new 12 | expect(@ap.send(:awesome, struct)).to eq('OpenStruct {}') 13 | end 14 | 15 | it 'plain multiline' do 16 | struct = OpenStruct.new name: 'Foo', address: 'Bar' 17 | expect(@ap.send(:awesome, struct)).to eq <<~EOS.strip 18 | OpenStruct { 19 | address: "Bar", 20 | name: "Foo" 21 | } 22 | EOS 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - rubocop-rspec 3 | 4 | inherit_from: .rubocop_todo.yml 5 | 6 | AllCops: 7 | NewCops: enable 8 | TargetRubyVersion: 3.1 9 | 10 | Lint/RaiseException: 11 | Enabled: true 12 | 13 | Lint/StructNewOverride: 14 | Enabled: true 15 | 16 | Metrics/ClassLength: 17 | Max: 250 18 | 19 | Style/FrozenStringLiteralComment: 20 | Exclude: 21 | - gemfiles/* 22 | 23 | Style/HashEachMethods: 24 | Enabled: true 25 | 26 | Style/HashTransformKeys: 27 | Enabled: true 28 | 29 | Style/HashTransformValues: 30 | Enabled: true 31 | 32 | Style/StringLiterals: 33 | Exclude: 34 | - gemfiles/* 35 | 36 | Style/FetchEnvVar: 37 | Enabled: false 38 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/method_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class MethodFormatter < BaseFormatter 8 | attr_reader :method, :inspector, :options 9 | 10 | def initialize(method, inspector) 11 | super() 12 | @method = method 13 | @inspector = inspector 14 | @options = inspector.options 15 | end 16 | 17 | def format 18 | name, args, owner = method_tuple(method) 19 | 20 | "#{colorize(owner, :class)}##{colorize(name, :method)}#{colorize(args, :args)}" 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/support/active_record_data.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | 5 | class ActiveRecordData 6 | class << self 7 | data_file_selector = Pathname(File.dirname(__FILE__)).join('active_record_data', '*.txt') 8 | 9 | # Example generated method 10 | # data_filename = '/path/to/ap/spec/support/active_record_data/4_2_diana.txt' 11 | # 12 | # def self.raw_4_2_dana 13 | # File.read(data_filename).strip 14 | # end 15 | Dir[data_file_selector].each do |data_filename| 16 | method_name = Pathname(data_filename).basename('.txt') 17 | define_method(:"raw_#{method_name}") do 18 | File.read(data_filename).strip 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/class_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class ClassFormatter < BaseFormatter 8 | attr_reader :klass, :inspector, :options 9 | 10 | def initialize(klass, inspector) 11 | super() 12 | @klass = klass 13 | @inspector = inspector 14 | @options = inspector.options 15 | end 16 | 17 | def format 18 | superclass = klass.superclass 19 | if superclass 20 | colorize("#{klass.inspect} < #{superclass}", :class) 21 | else 22 | colorize(klass.inspect, :class) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/support/active_record_data/3_2_diana.txt: -------------------------------------------------------------------------------- 1 | # false, 12 | "created_at" => "1992-10-10 12:30:00", 13 | "id" => nil, 14 | "name" => "Diana", 15 | "rank" => 1 16 | }, 17 | attr_reader :association_cache = {}, 18 | attr_reader :changed_attributes = { 19 | "admin" => nil, 20 | "created_at" => nil, 21 | "name" => nil, 22 | "rank" => nil 23 | } 24 | > 25 | -------------------------------------------------------------------------------- /spec/support/active_record_data/3_2_diana_legacy.txt: -------------------------------------------------------------------------------- 1 | # false, 11 | "created_at" => "1992-10-10 12:30:00", 12 | "id" => nil, 13 | "name" => "Diana", 14 | "rank" => 1 15 | }, 16 | attr_reader :association_cache = {}, 17 | attr_reader :changed_attributes = { 18 | "admin" => nil, 19 | "created_at" => nil, 20 | "name" => nil, 21 | "rank" => nil 22 | }, 23 | attr_reader :mass_assignment_options = nil 24 | > 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | 4 | rvm: 5 | - 2.5 6 | - 2.6 7 | - 2.7 8 | - ruby-head 9 | - jruby-9.2 10 | - jruby-head 11 | 12 | before_install: 13 | - gem install bundler 14 | 15 | gemfile: 16 | - gemfiles/rails_5.1.gemfile 17 | - gemfiles/rails_5.2.gemfile 18 | - gemfiles/rails_6.0.gemfile 19 | - gemfiles/mongoid_4.0.gemfile 20 | - gemfiles/mongoid_5.0.gemfile 21 | - gemfiles/mongoid_6.0.gemfile 22 | - gemfiles/sequel_5.0.gemfile 23 | - gemfiles/mongo_mapper.gemfile 24 | 25 | matrix: 26 | include: 27 | - rvm: ruby-head 28 | env: RUBYOPT="--enable-frozen-string-literal" 29 | allow_failures: 30 | - rvm: jruby-head 31 | - rvm: ruby-head 32 | 33 | addons: 34 | code_climate: 35 | repo_token: b84a33c35c698270ad54261655bc25161a0853386825a2d54fb1c7a11c2b2785 36 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/action_view.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module ActionView 10 | # Use HTML colors and add default "debug_dump" class to the resulting HTML. 11 | def ap_debug(object, options = {}) 12 | object.ai( 13 | options.merge(html: true) 14 | ).sub( 15 | /^])/, 16 | '
 { !ExtVerifier.has_rails? || ActiveRecord::VERSION::STRING >= '6.1' }.call do
 7 |   before do
 8 |     @view = ActionView::Base.new
 9 |   end
10 | 
11 |   it "uses HTML and adds 'debug_dump' class to plain 
 tag" do
12 |     markup = rand
13 |     expect(@view.ap(markup, colors: :none)).to eq(%(
#{markup}
)) 14 | end 15 | 16 | it "uses HTML and adds 'debug_dump' class to colorized
 tag" do
17 |     markup = ' &'
18 |     expect(@view.ap(markup)).to eq('
" &<hello>"
') 19 | end 20 | 21 | it 'uses HTML and does set output to HTML safe' do 22 | expect(@view.ap('

Hello World

')).to be_html_safe 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/dir_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'shellwords' 4 | 5 | require_relative 'base_formatter' 6 | require_relative 'mswin_helper' if RUBY_PLATFORM.include?('mswin') 7 | 8 | module AmazingPrint 9 | module Formatters 10 | class DirFormatter < BaseFormatter 11 | attr_reader :dir, :inspector, :options 12 | 13 | def initialize(dir, inspector) 14 | super() 15 | @dir = dir 16 | @inspector = inspector 17 | @options = inspector.options 18 | end 19 | 20 | def format 21 | ls = info 22 | colorize(ls.empty? ? dir.inspect : "#{dir.inspect}\n#{ls.chop}", :dir) 23 | end 24 | 25 | def info 26 | if RUBY_PLATFORM.include?('mswin') 27 | "#{GetChildItem.new(@dir.path)}\n" 28 | else 29 | `ls -alF #{dir.path.shellescape}` 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/file_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'shellwords' 4 | 5 | require_relative 'base_formatter' 6 | require_relative 'mswin_helper' if RUBY_PLATFORM.include?('mswin') 7 | 8 | module AmazingPrint 9 | module Formatters 10 | class FileFormatter < BaseFormatter 11 | attr_reader :file, :inspector, :options 12 | 13 | def initialize(file, inspector) 14 | super() 15 | @file = file 16 | @inspector = inspector 17 | @options = inspector.options 18 | end 19 | 20 | def format 21 | ls = info 22 | colorize(ls.empty? ? file.inspect : "#{file.inspect}\n#{ls.chop}", :file) 23 | end 24 | 25 | def info 26 | if RUBY_PLATFORM.include?('mswin') 27 | "#{GetChildItem.new(@file.path)}\n" 28 | else 29 | File.directory?(file) ? `ls -adlF #{file.path.shellescape}` : `ls -alF #{file.path.shellescape}` 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/amazing_print/ext_loader.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AmazingPrint 4 | ## 5 | # Attempt to load extensions up to 3 times since this library may be required 6 | # before dependencies that we have extensions for. 7 | # 8 | class ExtLoader 9 | EXT_LOAD_ATTEMPT_LIMIT = 3 10 | 11 | @load_attemps = 0 12 | 13 | def self.call 14 | return if @load_attemps >= EXT_LOAD_ATTEMPT_LIMIT 15 | 16 | require_relative 'ext/mongo_mapper' if defined?(MongoMapper) 17 | require_relative 'ext/mongoid' if defined?(Mongoid) 18 | require_relative 'ext/nobrainer' if defined?(NoBrainer) 19 | require_relative 'ext/nokogiri' if defined?(Nokogiri) 20 | require_relative 'ext/ostruct' if defined?(OpenStruct) # rubocop:disable Style/OpenStructUse 21 | require_relative 'ext/ripple' if defined?(Ripple) 22 | require_relative 'ext/sequel' if defined?(Sequel) 23 | 24 | @load_attemps += 1 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/ostruct.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module OpenStruct 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_ostruct, :cast 12 | base.send :alias_method, :cast, :cast_with_ostruct 13 | end 14 | 15 | def cast_with_ostruct(object, type) 16 | cast = cast_without_ostruct(object, type) 17 | cast = :open_struct_instance if defined?(::OpenStruct) && object.is_a?(::OpenStruct) 18 | cast 19 | end 20 | 21 | def awesome_open_struct_instance(object) 22 | "#{object.class} #{awesome_hash(object.marshal_dump)}" 23 | end 24 | end 25 | end 26 | 27 | AmazingPrint::Formatter.include AmazingPrint::OpenStruct 28 | -------------------------------------------------------------------------------- /spec/support/ext_verifier.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ExtVerifier 4 | def require_dependencies!(dependencies) 5 | dependencies.each do |dependency| 6 | require dependency 7 | rescue LoadError 8 | end 9 | end 10 | module_function :require_dependencies! 11 | 12 | def has_rails? 13 | defined?(Rails) 14 | end 15 | module_function :has_rails? 16 | 17 | def has_mongoid? 18 | defined?(Mongoid) 19 | end 20 | module_function :has_mongoid? 21 | 22 | def has_mongo_mapper? 23 | defined?(MongoMapper) 24 | end 25 | module_function :has_mongo_mapper? 26 | 27 | def has_ripple? 28 | defined?(Ripple) 29 | end 30 | module_function :has_ripple? 31 | 32 | def has_nobrainer? 33 | defined?(NoBrainer) 34 | end 35 | module_function :has_nobrainer? 36 | 37 | def has_sequel? 38 | defined?(::Sequel::Model) 39 | end 40 | module_function :has_sequel? 41 | end 42 | 43 | RSpec.configure do |config| 44 | config.include(ExtVerifier) 45 | config.extend(ExtVerifier) 46 | end 47 | -------------------------------------------------------------------------------- /lib/amazing_print/core_ext/object.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | class Object # :nodoc: 9 | # 10 | # Intercept methods below to inject @__amazing_print__ instance variable 11 | # so we know it is the *methods* array when formatting an array. 12 | # 13 | # Remaining instance '_methods' are handled in core_ext/class.rb. 14 | # 15 | %w[methods private_methods protected_methods public_methods singleton_methods].each do |name| 16 | original_method = instance_method(name) 17 | 18 | define_method name do |*args| 19 | methods = original_method.bind(self).call(*args) 20 | methods.instance_variable_set(:@__awesome_methods__, self) 21 | methods.extend(AwesomeMethodArray) 22 | methods 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/amazing_print/core_ext/logger.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module Logger 10 | # Add ap method to logger 11 | #------------------------------------------------------------------------------ 12 | def ap(object, options = {}) 13 | if options.is_a?(Hash) 14 | level = options.delete(:level) 15 | else 16 | level = options 17 | options = {} 18 | end 19 | 20 | level ||= AmazingPrint.defaults[:log_level] if AmazingPrint.defaults 21 | level ||= :debug 22 | send level, object.ai(options) 23 | end 24 | end 25 | end 26 | 27 | Logger.include AmazingPrint::Logger 28 | ActiveSupport::BufferedLogger.include AmazingPrint::Logger if defined?(ActiveSupport::BufferedLogger) 29 | -------------------------------------------------------------------------------- /lib/amazing_print/core_ext/class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | class Class # :nodoc: 9 | # 10 | # Intercept methods below to inject @__amazing_print__ instance variable 11 | # so we know it is the *methods* array when formatting an array. 12 | # 13 | # Remaining public/private etc. '_methods' are handled in core_ext/object.rb. 14 | # 15 | %w[instance_methods private_instance_methods protected_instance_methods public_instance_methods].each do |name| 16 | original_method = instance_method(name) 17 | 18 | define_method name do |*args| 19 | methods = original_method.bind(self).call(*args) 20 | methods.instance_variable_set(:@__awesome_methods__, self) 21 | methods.extend(AwesomeMethodArray) 22 | methods 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /.github/workflows/rspec.yml: -------------------------------------------------------------------------------- 1 | name: Specs 2 | 3 | on: 4 | merge_group: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | test: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: 16 | - ubuntu 17 | ruby: 18 | - "3.1" 19 | - "3.2" 20 | - "3.3" 21 | - "3.4" 22 | - "head" 23 | - "jruby-9.4" 24 | runs-on: ${{ matrix.os }}-latest 25 | continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Set up Ruby ${{ matrix.ruby }} 29 | uses: ruby/setup-ruby@v1 30 | with: 31 | ruby-version: ${{ matrix.ruby }} 32 | - name: Install Dependencies 33 | run: | 34 | sudo apt-get update 35 | sudo apt-get -y install libsqlite3-dev 36 | bundle install --retry 3 37 | - name: Test with all appraisals 38 | if : ${{ matrix.ruby != 'head' }} 39 | run: bundle exec rake 40 | - name: Test without appraisals 41 | if : ${{ matrix.ruby == 'head' }} 42 | run: bundle exec rspec 43 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | push: 8 | name: Push gem to RubyGems.org 9 | runs-on: ubuntu-latest 10 | 11 | permissions: 12 | id-token: write # IMPORTANT: this permission is mandatory for trusted publishing 13 | contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag 14 | 15 | steps: 16 | # Set up 17 | - uses: actions/checkout@v4 18 | - name: Set up Ruby 19 | uses: ruby/setup-ruby@v1 20 | with: 21 | bundler-cache: true 22 | ruby-version: ruby 23 | - name: Set Environment Variables 24 | run: | 25 | version=$(ruby -e 'require_relative "lib/amazing_print/version.rb"; puts AmazingPrint.version') 26 | echo "APP_VERSION=$version" >> $GITHUB_ENV 27 | 28 | # Release 29 | - uses: rubygems/release-gem@v1 30 | 31 | # GitHub release 32 | - uses: ncipollo/release-action@v1 33 | with: 34 | artifacts: "pkg/*.gem" 35 | generateReleaseNotes: true 36 | skipIfReleaseExists: true 37 | tag: ${{ env.APP_VERSION }} 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2010-2019 Michael Dvorkin 4 | Copyright (c) 2020 AmazingPrint 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/amazing_print/json_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ## 4 | # Helper module for hash_format: :json 5 | # 6 | module AmazingPrint 7 | module JSONHelper 8 | VALUE_CLASSES_NOT_TO_CONVERT = %w[Array BigDecimal Float Hash Integer String].freeze 9 | 10 | def json_awesome(object, is_key: false) 11 | return inspector.awesome(object) unless options[:hash_format] == :json && object.respond_to?(:to_json) 12 | 13 | if object.nil? 14 | # Color null like we do nil 15 | colorize(object.to_json, :nilclass) 16 | elsif is_key && object.is_a?(Numeric) 17 | # JSON keys should be a string 18 | inspector.awesome(object.to_s) 19 | elsif VALUE_CLASSES_NOT_TO_CONVERT.include?(object.class.name) || !object.respond_to?(:to_json) 20 | # These objects should not be converted to strings with #to_json so we can treat them normally 21 | inspector.awesome(object) 22 | else 23 | # Remove surrounding quotes added by #to_json from the standard library since 24 | # inspector.awesome is going to add those for us for strings. 25 | inspector.awesome(object.to_json.gsub(/\A"/, '').gsub(/"\z/, '')) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/amazing_print/colors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AmazingPrint 4 | module Colors 5 | module_function 6 | 7 | # 8 | # ANSI color codes: 9 | # \e => escape 10 | # 30 => color base 11 | # 1 => bright 12 | # 0 => normal 13 | # 14 | # For HTML coloring we use tag instead of to require monospace 15 | # font. Note that beloved has been removed from HTML5. 16 | # 17 | %w[gray red green yellow blue purple cyan white].zip( 18 | %w[black darkred darkgreen brown navy darkmagenta darkcyan slategray] 19 | ).each_with_index do |(color, shade), i| 20 | # NOTE: Format strings are created once only, for performance, and remembered by closures. 21 | 22 | term_bright_seq = "\e[1;#{i + 30}m%s\e[0m" 23 | html_bright_seq = %(%s) 24 | 25 | define_method color do |str, html = false| 26 | (html ? html_bright_seq : term_bright_seq) % str 27 | end 28 | 29 | term_normal_seq = "\e[0;#{i + 30}m%s\e[0m" 30 | html_normal_seq = %(%s) 31 | 32 | define_method "#{color}ish" do |str, html = false| 33 | (html ? html_normal_seq : term_normal_seq) % str 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/ext/active_support_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe 'AmazingPrint::ActiveSupport', skip: -> { !ExtVerifier.has_rails? }.call do 6 | before do 7 | @ap = AmazingPrint::Inspector.new 8 | end 9 | 10 | it 'formats ActiveSupport::TimeWithZone as regular Time' do 11 | Time.zone = 'Eastern Time (US & Canada)' 12 | time = Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone 13 | 14 | expected_date_str = (Rails.version.to_f >= 8.0 ? '2007-02-10' : 'Sat, 10 Feb 2007') 15 | expect(@ap.send(:awesome, time)) 16 | .to eq("\e[0;32m#{expected_date_str} 15:30:45.000000000 EST -05:00\e[0m") 17 | end 18 | 19 | it 'formats HashWithIndifferentAccess as regular Hash' do 20 | hash = HashWithIndifferentAccess.new({ hello: 'world' }) 21 | expect(@ap.send(:awesome, hash)).to eq("{\n \e[0;33m\"hello\"\e[0m\e[0;37m => \e[0m\e[0;33m\"world\"\e[0m\n}") 22 | end 23 | 24 | # ActiveSupport sticks in instance variables to the date object. Make sure 25 | # we ignore that and format Date instance as regular date. 26 | it 'formates Date object as date' do 27 | date = Date.new(2003, 5, 26) 28 | expect(date.ai(colors: :none)).to eq('Mon, 26 May 2003') 29 | expect(date.ai).to eq("\e[0;32mMon, 26 May 2003\e[0m") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/ext/sequel_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'sequel_helper' 5 | 6 | RSpec.describe 'AmazingPrint/Sequel', skip: -> { !ExtVerifier.has_sequel? }.call do 7 | before do 8 | @ap = AmazingPrint::Inspector.new colors: :none, sort_keys: true 9 | @user1 = SequelUser.new first_name: 'Jeremy', last_name: 'Evans' 10 | @user2 = SequelUser.new first_name: 'Sequel', last_name: 'Five' 11 | end 12 | 13 | it 'display single record' do 14 | out = @ap.awesome(@user1) 15 | str = <<~EOS.strip 16 | # { 17 | first_name: "Jeremy", 18 | last_name: "Evans" 19 | } 20 | EOS 21 | expect(out).to be_similar_to(str) 22 | end 23 | 24 | it 'display multiple records' do 25 | out = @ap.awesome([@user1, @user2]) 26 | str = <<~EOS.strip 27 | [ 28 | [0] # { 29 | first_name: "Jeremy", 30 | last_name: "Evans" 31 | }, 32 | [1] # { 33 | first_name: "Sequel", 34 | last_name: "Five" 35 | } 36 | ] 37 | EOS 38 | expect(out).to be_similar_to(str) 39 | end 40 | 41 | it 'does not crash if on Sequel::Model' do 42 | out = @ap.awesome(Sequel::Model) 43 | expect(out).to be_similar_to('Sequel::Model < Object') 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /amazing_print.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2020 AmazingPrint and contributors 4 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 5 | # 6 | # AmazingPrint is freely distributable under the terms of MIT license. 7 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 8 | #------------------------------------------------------------------------------ 9 | 10 | $LOAD_PATH.push File.expand_path('lib', __dir__) 11 | require 'amazing_print/version' 12 | 13 | Gem::Specification.new do |s| 14 | s.name = 'amazing_print' 15 | s.version = AmazingPrint.version 16 | s.required_ruby_version = '>= 3.1.0' 17 | s.authors = ['Michael Dvorkin', 'Kevin McCormack', 'Patrik Wenger'] 18 | s.email = 'harlemsquirrel@gmail.com' 19 | s.homepage = 'https://github.com/amazing-print/amazing_print' 20 | s.summary = 'Pretty print Ruby objects with proper indentation and colors' 21 | s.description = 'Great Ruby debugging companion: pretty print Ruby objects to visualize their structure. Supports custom object formatting via plugins' 22 | s.license = 'MIT' 23 | s.metadata = { 24 | 'changelog_uri' => 'https://github.com/amazing-print/amazing_print/blob/master/CHANGELOG.md' 25 | } 26 | 27 | s.files = Dir['lib/**/*', 'rails/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md'] 28 | s.executables = [] 29 | s.require_paths = ['lib'] 30 | end 31 | -------------------------------------------------------------------------------- /spec/ext/ripple_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Lint/ConstantDefinitionInBlock 4 | 5 | require 'spec_helper' 6 | 7 | RSpec.describe 'AmazingPrint/Ripple', skip: -> { !ExtVerifier.has_ripple? }.call do 8 | if ExtVerifier.has_ripple? 9 | before :all do 10 | class RippleUser 11 | include Ripple::Document 12 | 13 | key_on :_id 14 | property :_id, String 15 | property :first_name, String 16 | property :last_name, String 17 | end 18 | end 19 | 20 | after :all do 21 | Object.instance_eval { remove_const :RippleUser } 22 | end 23 | end 24 | 25 | before do 26 | @ap = AmazingPrint::Inspector.new colors: :none, sort_keys: true 27 | end 28 | 29 | it 'prints class instance' do 30 | user = RippleUser.new _id: '12345', first_name: 'Al', last_name: 'Capone' 31 | out = @ap.send :awesome, user 32 | 33 | expect(out).to be_similar_to <<~EOS.strip 34 | # { 35 | :_id => "12345", 36 | :first_name => "Al", 37 | :last_name => "Capone" 38 | } 39 | EOS 40 | end 41 | 42 | it 'prints the class' do 43 | expect(@ap.send(:awesome, RippleUser)).to eq <<~EOS.strip 44 | class RippleUser < Object { 45 | :_id => :string, 46 | :first_name => :string, 47 | :last_name => :string 48 | } 49 | EOS 50 | end 51 | end 52 | 53 | # rubocop:enable Lint/ConstantDefinitionInBlock 54 | -------------------------------------------------------------------------------- /spec/ext/action_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe 'AmazingPrint::ActionController', skip: -> { !ExtVerifier.has_rails? }.call do 6 | let(:inspector) { AmazingPrint::Inspector.new } 7 | 8 | context 'with unpermitted ActionController::Parameters' do 9 | let(:parameters) do 10 | ActionController::Parameters.new post: { id: 1, content: 'Some' } 11 | end 12 | 13 | it 'formats as an object' do 14 | expect(inspector.send(:awesome, parameters)).to match( 15 | if RUBY_VERSION < '3.4.0' 16 | /\A#?{"id"=>1, "content"=>"Some"}} permitted: false>\z/ 17 | else 18 | /\A#? {"id" => 1, "content" => "Some"}} permitted: false>\z/ 19 | end 20 | ) 21 | end 22 | end 23 | 24 | context 'with permitted ActionController::Parameters' do 25 | let(:expected_output) do 26 | <<~OUTPUT 27 | { 28 | \e[0;33m"post"\e[0m\e[0;37m => \e[0m{ 29 | \e[0;33m"id"\e[0m\e[0;37m => \e[0m\e[1;34m1\e[0m, 30 | \e[0;33m"content"\e[0m\e[0;37m => \e[0m\e[0;33m"Some"\e[0m 31 | } 32 | } 33 | OUTPUT 34 | .chomp 35 | end 36 | let(:parameters) do 37 | ActionController::Parameters.new post: { id: 1, content: 'Some' } 38 | end 39 | 40 | it 'formats as a hash' do 41 | expect(inspector.send(:awesome, parameters.permit!)).to eq expected_output 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/amazing_print.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | # 9 | # AmazingPrint might be loaded implicitly through ~/.irbrc or ~/.pryrc 10 | # so do nothing for subsequent requires. 11 | # 12 | unless defined?(AmazingPrint::Inspector) 13 | %w[awesome_method_array object class kernel].each do |file| 14 | require_relative "amazing_print/core_ext/#{file}" 15 | end 16 | 17 | require_relative 'amazing_print/custom_defaults' 18 | require_relative 'amazing_print/inspector' 19 | require_relative 'amazing_print/formatter' 20 | require_relative 'amazing_print/version' 21 | require_relative 'amazing_print/core_ext/logger' if defined?(Logger) 22 | # 23 | # Load the following under normal circumstances as well as in Rails 24 | # console when required from ~/.irbrc or ~/.pryrc. 25 | # 26 | require_relative 'amazing_print/ext/active_record' if defined?(ActiveRecord) || AmazingPrint.rails_console? 27 | require_relative 'amazing_print/ext/active_support' if defined?(ActiveSupport) || AmazingPrint.rails_console? 28 | # 29 | # Load remaining extensions. 30 | # 31 | if defined?(ActiveSupport.on_load) 32 | ActiveSupport.on_load(:action_view) do 33 | require_relative 'amazing_print/ext/action_view' 34 | end 35 | end 36 | 37 | AmazingPrint::ExtLoader.call 38 | end 39 | -------------------------------------------------------------------------------- /spec/ext/active_model_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'active_record_helper' 5 | 6 | RSpec.describe 'ActiveModel formatting', skip: -> { !ExtVerifier.has_rails? }.call do 7 | before do 8 | @ap = AmazingPrint::Inspector.new(colors: :none) 9 | end 10 | 11 | describe 'ActiveModel class' do 12 | it 'prints the class' do 13 | expect(@ap.awesome(ActiveModelModel)).to eq <<~PRINT.strip 14 | class ActiveModelModel < Object { 15 | name: :string, 16 | rank: :integer 17 | } 18 | PRINT 19 | end 20 | end 21 | 22 | describe 'ActiveModel instance' do 23 | it 'prints the instance' do 24 | model = ActiveModelModel.new(name: 'John', rank: 1) 25 | expect(@ap.awesome(model)).to be_similar_to <<~PRINT.strip 26 | # { 27 | name: "John", 28 | rank: 1 29 | } 30 | PRINT 31 | end 32 | end 33 | 34 | it 'formats active_model_errors properly' do 35 | model = TableFreeModel.new 36 | model.errors.add(:name, "can't be blank") 37 | 38 | out = @ap.awesome(model.errors) 39 | 40 | str = <<~ERRORS.strip 41 | # { 42 | "name" => nil, 43 | details: { 44 | name: [ 45 | [0] { 46 | error: "can't be blank" 47 | } 48 | ] 49 | }, 50 | messages: { 51 | name: [ 52 | [0] "can't be blank" 53 | ] 54 | } 55 | } 56 | ERRORS 57 | 58 | expect(out).to be_similar_to(str) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/ext/nobrainer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Lint/ConstantDefinitionInBlock 4 | 5 | require 'spec_helper' 6 | 7 | RSpec.describe 'AmazingPrint/NoBrainer', skip: -> { !ExtVerifier.has_nobrainer? }.call do 8 | if ExtVerifier.has_nobrainer? 9 | before :all do 10 | NoBrainer.configure do |config| 11 | config.app_name = 'ap_test' 12 | config.environment = :test 13 | end 14 | end 15 | 16 | before :all do 17 | class SomeModel 18 | include NoBrainer::Document 19 | 20 | field :first_name, type: String 21 | field :last_name, type: String 22 | field :some_field 23 | end 24 | end 25 | 26 | after :all do 27 | Object.instance_eval { remove_const :SomeModel } 28 | end 29 | end 30 | 31 | before do 32 | @ap = AmazingPrint::Inspector.new colors: :none 33 | end 34 | 35 | it 'prints class instance' do 36 | user = SomeModel.new first_name: 'Al', last_name: 'Capone' 37 | out = @ap.send :awesome, user 38 | 39 | object_id = user.id.inspect 40 | str = <<~EOS.strip 41 | # { 42 | :id => #{object_id}, 43 | :first_name => "Al", 44 | :last_name => "Capone" 45 | } 46 | EOS 47 | expect(out).to eq(str) 48 | end 49 | 50 | it 'prints the class' do 51 | class_spec = <<~EOS.strip 52 | class SomeModel < Object { 53 | :id => :string, 54 | :first_name => :string, 55 | :last_name => :string, 56 | :some_field => :object 57 | } 58 | EOS 59 | 60 | expect(@ap.send(:awesome, SomeModel)).to eq class_spec 61 | end 62 | end 63 | 64 | # rubocop:enable Lint/ConstantDefinitionInBlock 65 | -------------------------------------------------------------------------------- /spec/support/active_record_data/3_2_multi.txt: -------------------------------------------------------------------------------- 1 | [ 2 | [0] # false, 13 | "created_at" => "1992-10-10 12:30:00", 14 | "id" => nil, 15 | "name" => "Diana", 16 | "rank" => 1 17 | }, 18 | attr_reader :association_cache = {}, 19 | attr_reader :changed_attributes = { 20 | "admin" => nil, 21 | "created_at" => nil, 22 | "name" => nil, 23 | "rank" => nil 24 | } 25 | >, 26 | [1] # true, 37 | "created_at" => "2003-05-26 14:15:00", 38 | "id" => nil, 39 | "name" => "Laura", 40 | "rank" => 2 41 | }, 42 | attr_reader :association_cache = {}, 43 | attr_reader :changed_attributes = { 44 | "admin" => nil, 45 | "created_at" => nil, 46 | "name" => nil, 47 | "rank" => nil 48 | } 49 | > 50 | ] 51 | -------------------------------------------------------------------------------- /spec/support/active_record_data/3_2_multi_legacy.txt: -------------------------------------------------------------------------------- 1 | [ 2 | [0] # false, 12 | "created_at" => "1992-10-10 12:30:00", 13 | "id" => nil, 14 | "name" => "Diana", 15 | "rank" => 1 16 | }, 17 | attr_reader :association_cache = {}, 18 | attr_reader :changed_attributes = { 19 | "admin" => nil, 20 | "created_at" => nil, 21 | "name" => nil, 22 | "rank" => nil 23 | }, 24 | attr_reader :mass_assignment_options = nil 25 | >, 26 | [1] # true, 36 | "created_at" => "2003-05-26 14:15:00", 37 | "id" => nil, 38 | "name" => "Laura", 39 | "rank" => 2 40 | }, 41 | attr_reader :association_cache = {}, 42 | attr_reader :changed_attributes = { 43 | "admin" => nil, 44 | "created_at" => nil, 45 | "name" => nil, 46 | "rank" => nil 47 | }, 48 | attr_reader :mass_assignment_options = nil 49 | > 50 | ] 51 | -------------------------------------------------------------------------------- /spec/active_record_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if ExtVerifier.has_rails? 4 | # Required to use the column support 5 | module Rails 6 | def self.env 7 | {} 8 | end 9 | end 10 | 11 | # Establish connection to in-memory SQLite DB 12 | ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' 13 | 14 | # Create the users table 15 | ActiveRecord::Migration.verbose = false 16 | ActiveRecord::Migration.create_table :users do |t| 17 | t.string :name 18 | t.integer :rank 19 | t.boolean :admin 20 | t.datetime :created_at 21 | end 22 | 23 | ActiveRecord::Migration.create_table :emails do |t| 24 | t.references :user 25 | t.string :email_address 26 | end 27 | 28 | ActiveRecord::Migration.create_table :wizards do |t| 29 | t.string :name 30 | t.datetime :created_at 31 | t.datetime :updated_at 32 | t.integer :spells_count 33 | end 34 | 35 | ActiveRecord::Migration.create_table :spells do |t| 36 | t.references :wizard 37 | t.string :name 38 | end 39 | 40 | # Create models 41 | class User < ActiveRecord::Base; has_many :emails; end 42 | 43 | class SubUser < User; end 44 | 45 | class Email < ActiveRecord::Base 46 | belongs_to :user 47 | end 48 | 49 | class Wizard < ActiveRecord::Base 50 | has_many :spells 51 | end 52 | 53 | class Spell < ActiveRecord::Base 54 | belongs_to :wizard, counter_cache: true 55 | end 56 | 57 | class TableFreeModel 58 | include ::ActiveModel::Validations 59 | 60 | attr_reader(:name) 61 | 62 | def attributes 63 | { 'name' => name } 64 | end 65 | end 66 | 67 | class ActiveModelModel 68 | include ::ActiveModel::Model 69 | include ::ActiveModel::Attributes 70 | 71 | attribute :name, :string 72 | attribute :rank, :integer, default: 0 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/active_support.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module ActiveSupport 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_active_support, :cast 12 | base.send :alias_method, :cast, :cast_with_active_support 13 | end 14 | 15 | def cast_with_active_support(object, type) 16 | cast = cast_without_active_support(object, type) 17 | if defined?(::ActiveSupport) && defined?(::HashWithIndifferentAccess) 18 | if (defined?(::ActiveSupport::TimeWithZone) && object.is_a?(::ActiveSupport::TimeWithZone)) || object.is_a?(::Date) 19 | cast = :active_support_time 20 | elsif object.is_a?(::HashWithIndifferentAccess) 21 | cast = :hash_with_indifferent_access 22 | end 23 | end 24 | cast 25 | end 26 | 27 | # Format ActiveSupport::TimeWithZone as standard Time. 28 | #------------------------------------------------------------------------------ 29 | def awesome_active_support_time(object) 30 | colorize(object.inspect, :time) 31 | end 32 | 33 | # Format HashWithIndifferentAccess as standard Hash. 34 | #------------------------------------------------------------------------------ 35 | def awesome_hash_with_indifferent_access(object) 36 | awesome_hash(object) 37 | end 38 | end 39 | end 40 | 41 | AmazingPrint::Formatter.include AmazingPrint::ActiveSupport 42 | # 43 | # Colorize Rails logs. 44 | # 45 | AmazingPrint.force_colors!( 46 | colors: ActiveSupport.try(:colorize_logging) || ActiveSupport::LogSubscriber.colorize_logging 47 | ) 48 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/nokogiri.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module Nokogiri 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_nokogiri, :cast 12 | base.send :alias_method, :cast, :cast_with_nokogiri 13 | end 14 | 15 | # Add Nokogiri XML Node and NodeSet names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_nokogiri(object, type) 18 | cast = cast_without_nokogiri(object, type) 19 | if (defined?(::Nokogiri::XML::Node) && object.is_a?(::Nokogiri::XML::Node)) || 20 | (defined?(::Nokogiri::XML::NodeSet) && object.is_a?(::Nokogiri::XML::NodeSet)) 21 | cast = :nokogiri_xml_node 22 | end 23 | cast 24 | end 25 | 26 | #------------------------------------------------------------------------------ 27 | def awesome_nokogiri_xml_node(object) 28 | return '[]' if object.is_a?(::Nokogiri::XML::NodeSet) && object.empty? 29 | 30 | xml = object.to_xml(indent: 2) 31 | # 32 | # Colorize tag, id/class name, and contents. 33 | # 34 | xml.gsub!(%r{(<)(/?[A-Za-z1-9]+)}) { |_tag| "#{Regexp.last_match(1)}#{colorize(Regexp.last_match(2), :keyword)}" } 35 | xml.gsub!(/(id|class)="[^"]+"/i) { |id| colorize(id, :class) } 36 | xml.gsub!(/>([^<]+)#{contents}<" 39 | end 40 | xml 41 | end 42 | end 43 | end 44 | 45 | AmazingPrint::Formatter.include AmazingPrint::Nokogiri 46 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/mswin_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'fiddle' 4 | require 'fiddle/import' 5 | 6 | module AmazingPrint 7 | module Formatters 8 | module Kernel32 9 | extend Fiddle::Importer 10 | 11 | dlload 'kernel32' 12 | extern 'unsigned long GetFileAttributesA(const char*)' 13 | end 14 | 15 | class GetChildItem 16 | def initialize(fname) 17 | @fname = fname 18 | @stat = File.send(File.symlink?(@fname) ? :lstat : :stat, @fname) 19 | @attrs = Kernel32::GetFileAttributesA @fname 20 | end 21 | 22 | # docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants 23 | FILE_ATTRIBUTE_ARCHIVE = 0x20 24 | FILE_ATTRIBUTE_READONLY = 0x1 25 | FILE_ATTRIBUTE_HIDDEN = 0x2 26 | FILE_ATTRIBUTE_SYSTEM = 0x4 27 | 28 | def mode 29 | r = ['-'] * 6 30 | r[0] = 'd' if @stat.directory? 31 | r[1] = 'a' unless @attrs.nobits?(FILE_ATTRIBUTE_ARCHIVE) 32 | r[2] = 'r' unless @attrs.nobits?(FILE_ATTRIBUTE_READONLY) 33 | r[3] = 'h' unless @attrs.nobits?(FILE_ATTRIBUTE_HIDDEN) 34 | r[4] = 's' unless @attrs.nobits?(FILE_ATTRIBUTE_SYSTEM) 35 | r[5] = 'l' if File.symlink? @fname 36 | r.join 37 | end 38 | 39 | def last_write_time 40 | @stat.mtime.strftime '%Y-%m-%d %H:%M' 41 | end 42 | 43 | def length 44 | @stat.file? ? @stat.size.to_s : '' 45 | end 46 | 47 | def name 48 | @fname 49 | end 50 | 51 | def to_s 52 | format '%-12s %s %14s %s', 53 | { 54 | Mode: mode, 55 | LastWriteTime: last_write_time, 56 | Length: length, 57 | Name: name 58 | } 59 | end 60 | end 61 | end 62 | end 63 | 64 | puts AmazingPrint::Formatters::GetChildItem.new ARGV[0] if __FILE__ == $PROGRAM_NAME 65 | -------------------------------------------------------------------------------- /spec/ext/nokogiri_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe 'AmazingPrint/Nokogiri' do 6 | it 'colorizes tags' do 7 | xml = Nokogiri::XML('

') 8 | # FIXME: Due to something strange with Nokogiri and JRuby, we need to remove extra blank lines. 9 | output = xml.ai.gsub("\n\n", "\n") 10 | expect(output).to eq <<~EOS 11 | \e[1;32m 12 | \e[0m<\e[1;36mhtml\e[0m>\e[1;32m 13 | \e[0m<\e[1;36mbody\e[0m>\e[1;32m 14 | \e[0m<\e[1;36mh1\e[0m/>\e[1;32m 15 | \e[0m<\e[1;36m/body\e[0m>\e[1;32m 16 | \e[0m<\e[1;36m/html\e[0m> 17 | EOS 18 | end 19 | 20 | it 'colorizes contents' do 21 | xml = Nokogiri::XML('

Hello

') 22 | expect(xml.ai).to eq <<~EOS 23 | \e[1;32m 24 | \e[0m<\e[1;36mhtml\e[0m>\e[1;32m 25 | \e[0m<\e[1;36mbody\e[0m>\e[1;32m 26 | \e[0m<\e[1;36mh1\e[0m>\e[1;32mHello\e[0m<\e[1;36m/h1\e[0m>\e[1;32m 27 | \e[0m<\e[1;36m/body\e[0m>\e[1;32m 28 | \e[0m<\e[1;36m/html\e[0m> 29 | EOS 30 | end 31 | 32 | it 'colorizes class and id' do 33 | xml = Nokogiri::XML('

') 34 | # FIXME: Due to something strange with Nokogiri and JRuby, we need to remove extra blank lines. 35 | output = xml.ai.gsub("\n\n", "\n") 36 | expect(output).to eq <<~EOS 37 | \e[1;32m 38 | \e[0m<\e[1;36mhtml\e[0m>\e[1;32m 39 | \e[0m<\e[1;36mbody\e[0m>\e[1;32m 40 | \e[0m<\e[1;36mh1\e[0m>\e[1;32m 41 | \e[0m<\e[1;36mspan\e[0m \e[1;33mclass="world"\e[0m \e[1;33mid="hello"\e[0m/>\e[1;32m 42 | \e[0m<\e[1;36m/h1\e[0m>\e[1;32m 43 | \e[0m<\e[1;36m/body\e[0m>\e[1;32m 44 | \e[0m<\e[1;36m/html\e[0m> 45 | EOS 46 | end 47 | 48 | it 'handle empty NodeSet' do 49 | xml = Nokogiri::XML::NodeSet.new(Nokogiri::XML('')) 50 | expect(xml.ai).to eq('[]') 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/core_ext/logger_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | require 'logger' 6 | require 'amazing_print/core_ext/logger' 7 | 8 | RSpec.describe 'AmazingPrint logging extensions' do 9 | subject(:logger) do 10 | Logger.new(File::NULL) 11 | rescue Errno::ENOENT 12 | Logger.new(File::NULL) 13 | end 14 | 15 | let(:object) { double } 16 | let(:options) { { sort_keys: true } } 17 | 18 | describe 'ap method' do 19 | it 'awesome_inspects the given object' do 20 | expect(object).to receive(:ai) 21 | logger.ap object 22 | end 23 | 24 | it 'passes options to `ai`' do 25 | expect(object).to receive(:ai).with(options) 26 | logger.ap object, options 27 | end 28 | 29 | describe 'the log level' do 30 | before do 31 | AmazingPrint.defaults = {} 32 | end 33 | 34 | it 'fallbacks to the default :debug log level' do 35 | expect(logger).to receive(:debug) 36 | logger.ap nil 37 | end 38 | 39 | it 'uses the global user default if no level passed' do 40 | AmazingPrint.defaults = { log_level: :info } 41 | expect(logger).to receive(:info) 42 | logger.ap nil 43 | end 44 | 45 | it 'uses the passed in level' do 46 | expect(logger).to receive(:warn) 47 | logger.ap nil, :warn 48 | end 49 | 50 | it 'makes no difference if passed as a hash or a part of options' do 51 | expect(logger).to receive(:warn) 52 | logger.ap nil, { level: :warn } 53 | end 54 | 55 | context 'when given options' do 56 | it 'uses the default log level with the options' do 57 | expect(logger).to receive(:debug) 58 | expect(object).to receive(:ai).with(options) 59 | logger.ap object, options 60 | end 61 | 62 | it 'still uses the passed in level with options' do 63 | expect(logger).to receive(:warn) 64 | expect(object).to receive(:ai).with(options) 65 | logger.ap object, options.merge({ level: :warn }) 66 | end 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if RUBY_PLATFORM != 'java' && RUBY_VERSION >= '3.2.0' 4 | appraise 'rails-main' do 5 | gem 'rails', github: 'rails/rails' 6 | gem 'sqlite3', '>= 2.1', platform: :mri 7 | end 8 | 9 | appraise 'rails-8.1' do 10 | # Waiting on ActiveRecord 8.1 support 11 | # gem 'activerecord-jdbcsqlite3-adapter', '~> 81.0', platform: :jruby 12 | gem 'rails', '~> 8.1.0' 13 | gem 'sqlite3', '>= 2.1', platform: :mri 14 | end 15 | 16 | appraise 'rails-8.0' do 17 | # Waiting on ActiveRecord 8.0 support 18 | # gem 'activerecord-jdbcsqlite3-adapter', '~> 80.0', platform: :jruby 19 | gem 'rails', '~> 8.0.0' 20 | gem 'sqlite3', '>= 2.1', platform: :mri 21 | end 22 | end 23 | 24 | appraise 'rails-7.2' do 25 | # Waiting on ActiveRecord 7.2 support 26 | gem 'activerecord-jdbcsqlite3-adapter', '~> 72.0', platform: :jruby 27 | gem 'rails', '~> 7.2.0' 28 | gem 'sqlite3', '~> 2.6', platform: :mri 29 | end 30 | 31 | appraise 'rails-7.1' do 32 | gem 'activerecord-jdbcsqlite3-adapter', '~> 71.0', platform: :jruby 33 | gem 'rails', '~> 7.1.0' 34 | gem 'sqlite3', '~> 2.6', platform: :mri 35 | end 36 | 37 | if RUBY_VERSION < '3.4.0' 38 | appraise 'rails-7.0' do 39 | gem 'activerecord-jdbcsqlite3-adapter', '~> 70.0', platform: :jruby 40 | gem 'concurrent-ruby', '1.3.4' # https://stackoverflow.com/a/79361034/3446655 41 | gem 'rails', '~> 7.0.0' 42 | gem 'sqlite3', '~> 1.4', platform: :mri 43 | end 44 | 45 | appraise 'mongoid-7.0' do 46 | gem 'mongoid', '~> 7.0.0' 47 | gem 'concurrent-ruby', '1.3.4' # https://stackoverflow.com/a/79361034/3446655 48 | end 49 | 50 | appraise 'mongoid-8.0' do 51 | gem 'mongoid', '~> 8.0.0' 52 | gem 'concurrent-ruby', '1.3.4' # https://stackoverflow.com/a/79361034/3446655 53 | end 54 | 55 | appraise 'rails-6.1' do 56 | gem 'activerecord-jdbcsqlite3-adapter', '~> 61.0', platform: :jruby 57 | gem 'concurrent-ruby', '1.3.4' # https://stackoverflow.com/a/79361034/3446655 58 | gem 'rails', '~> 6.1.0' 59 | gem 'sqlite3', '~> 1.4', platform: :mri 60 | end 61 | 62 | appraise 'sequel-5.0' do 63 | gem 'jdbc-sqlite3', platform: :jruby 64 | gem 'sequel', '~> 5.0' 65 | gem 'sqlite3', '~> 1.4', platform: :mri 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/nobrainer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Style/HashTransformValues 4 | 5 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 6 | # 7 | # AmazingPrint is freely distributable under the terms of MIT license. 8 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 9 | #------------------------------------------------------------------------------ 10 | module AmazingPrint 11 | module NoBrainer 12 | def self.included(base) 13 | base.send :alias_method, :cast_without_nobrainer, :cast 14 | base.send :alias_method, :cast, :cast_with_nobrainer 15 | end 16 | 17 | # Add NoBrainer class names to the dispatcher pipeline. 18 | #------------------------------------------------------------------------------ 19 | def cast_with_nobrainer(object, type) 20 | cast = cast_without_nobrainer(object, type) 21 | if defined?(::NoBrainer::Document) 22 | if object.is_a?(Class) && object < ::NoBrainer::Document 23 | cast = :nobrainer_class 24 | elsif object.is_a?(::NoBrainer::Document) 25 | cast = :nobrainer_document 26 | end 27 | end 28 | cast 29 | end 30 | 31 | # Format NoBrainer class object. 32 | #------------------------------------------------------------------------------ 33 | def awesome_nobrainer_class(object) 34 | name = "#{awesome_simple(object, :class)} < #{awesome_simple(object.superclass, :class)}" 35 | data = object.fields.to_h do |field, options| 36 | [field, (options[:type] || Object).to_s.underscore.to_sym] 37 | end 38 | 39 | name = "class #{awesome_simple(object.to_s, :class)}" 40 | base = "< #{awesome_simple(object.superclass.to_s, :class)}" 41 | 42 | [name, base, awesome_hash(data)].join(' ') 43 | end 44 | 45 | # Format NoBrainer Document object. 46 | #------------------------------------------------------------------------------ 47 | def awesome_nobrainer_document(object) 48 | data = object.inspectable_attributes.symbolize_keys 49 | data = { errors: object.errors, attributes: data } if object.errors.present? 50 | "#{object} #{awesome_hash(data)}" 51 | end 52 | end 53 | end 54 | 55 | AmazingPrint::Formatter.include AmazingPrint::NoBrainer 56 | 57 | # rubocop:enable Style/HashTransformValues 58 | -------------------------------------------------------------------------------- /spec/support/rails_versions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RailsVersions 4 | def rails_version 5 | Gem::Version.new(Rails::VERSION::STRING) 6 | end 7 | 8 | def rails_main? 9 | Rails.version.end_with?('.alpha') 10 | end 11 | alias activerecord_main? rails_main? 12 | 13 | def rails_8_1? 14 | Gem::Requirement.new('~> 8.1.0').satisfied_by?(rails_version) 15 | end 16 | alias activerecord_8_1? rails_8_1? 17 | 18 | def rails_8_0? 19 | Gem::Requirement.new('~> 8.0.0').satisfied_by?(rails_version) 20 | end 21 | alias activerecord_8_0? rails_8_0? 22 | 23 | def rails_7_2? 24 | Gem::Requirement.new('~> 7.2.0').satisfied_by?(rails_version) 25 | end 26 | alias activerecord_7_2? rails_7_2? 27 | 28 | def rails_7_1? 29 | Gem::Requirement.new('~> 7.1.0').satisfied_by?(rails_version) 30 | end 31 | alias activerecord_7_1? rails_7_1? 32 | 33 | def rails_7_0? 34 | Gem::Requirement.new('~> 7.0.0').satisfied_by?(rails_version) 35 | end 36 | alias activerecord_7_0? rails_7_0? 37 | 38 | def rails_6_1? 39 | Gem::Requirement.new('~> 6.1.0').satisfied_by?(rails_version) 40 | end 41 | alias activerecord_6_1? rails_6_1? 42 | 43 | def rails_6_0? 44 | Gem::Requirement.new('~> 6.0.0').satisfied_by?(rails_version) 45 | end 46 | alias activerecord_6_0? rails_6_0? 47 | 48 | def rails_5_2? 49 | Gem::Requirement.new('~> 5.2.0').satisfied_by?(rails_version) 50 | end 51 | alias activerecord_5_2? rails_5_2? 52 | 53 | def rails_5_1? 54 | Gem::Requirement.new('~> 5.1.0').satisfied_by?(rails_version) 55 | end 56 | alias activerecord_5_1? rails_5_1? 57 | 58 | def rails_5_0? 59 | Gem::Requirement.new('~> 5.0.0.racecar1').satisfied_by?(rails_version) 60 | end 61 | alias activerecord_5_0? rails_5_0? 62 | 63 | def rails_4_2? 64 | Gem::Requirement.new('~> 4.2.0').satisfied_by?(rails_version) 65 | end 66 | alias activerecord_4_2? rails_4_2? 67 | 68 | def rails_4_1? 69 | Gem::Requirement.new('~> 4.1.0').satisfied_by?(rails_version) 70 | end 71 | alias activerecord_4_1? rails_4_1? 72 | 73 | def rails_4_0? 74 | Gem::Requirement.new('~> 4.0.0').satisfied_by?(rails_version) 75 | end 76 | alias activerecord_4_0? rails_4_0? 77 | 78 | def rails_3_2? 79 | Gem::Requirement.new('~> 3.2.0').satisfied_by?(rails_version) 80 | end 81 | alias activerecord_3_2? rails_3_2? 82 | end 83 | 84 | RSpec.configure do |config| 85 | config.include(RailsVersions) 86 | config.extend(RailsVersions) 87 | end 88 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/sequel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module Sequel 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_sequel, :cast 12 | base.send :alias_method, :cast, :cast_with_sequel 13 | end 14 | 15 | # Add Sequel class names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_sequel(object, type) 18 | cast = cast_without_sequel(object, type) 19 | if defined?(::Sequel::Model) && object.is_a?(::Sequel::Model) 20 | cast = :sequel_document 21 | elsif defined?(::Sequel::Model) && object.is_a?(Class) && object.ancestors.include?(::Sequel::Model) && object&.name != 'Sequel::Model' 22 | cast = :sequel_model_class 23 | elsif defined?(::Sequel::Mysql2::Dataset) && object.class.ancestors.include?(::Sequel::Mysql2::Dataset) 24 | cast = :sequel_dataset 25 | end 26 | cast 27 | end 28 | 29 | # Format Sequel Document object. 30 | #------------------------------------------------------------------------------ 31 | def awesome_sequel_document(object) 32 | data = object.values.sort_by(&:to_s).each_with_object({}) do |c, hash| 33 | hash[c[0].to_sym] = c[1] 34 | end 35 | data = { errors: object.errors, values: data } unless object.errors.empty? 36 | "#{object} #{awesome_hash(data)}" 37 | end 38 | 39 | # Format Sequel Dataset object. 40 | #------------------------------------------------------------------------------ 41 | def awesome_sequel_dataset(dataset) 42 | [awesome_array(dataset.to_a), amazing_print(dataset.sql)].join("\n") 43 | end 44 | 45 | # Format Sequel Model class. 46 | #------------------------------------------------------------------------------ 47 | def awesome_sequel_model_class(object) 48 | data = object.db_schema.inject({}) { |h, (prop, defn)| h.merge(prop => defn[:db_type]) } 49 | name = "class #{awesome_simple(object.to_s, :class)}" 50 | base = "< #{awesome_simple(object.superclass.to_s, :class)}" 51 | 52 | [name, base, awesome_hash(data)].join(' ') 53 | end 54 | end 55 | end 56 | 57 | AmazingPrint::Formatter.include AmazingPrint::Sequel 58 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/struct_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class StructFormatter < BaseFormatter 8 | attr_reader :struct, :variables, :inspector, :options 9 | 10 | def initialize(struct, inspector) 11 | super() 12 | @struct = struct 13 | @variables = struct.members 14 | @inspector = inspector 15 | @options = inspector.options 16 | end 17 | 18 | def format 19 | vars = variables.map do |var| 20 | property = var.to_s[1..].to_sym # to_s because of some monkey patching done by Puppet. 21 | accessor = if struct.respond_to?(:"#{property}=") 22 | struct.respond_to?(property) ? :accessor : :writer 23 | else 24 | struct.respond_to?(property) ? :reader : nil 25 | end 26 | if accessor 27 | ["attr_#{accessor} :#{property}", var] 28 | else 29 | [var.to_s, var] 30 | end 31 | end 32 | 33 | data = vars.sort.map do |declaration, var| 34 | key = left_aligned do 35 | align(declaration, declaration.size) 36 | end 37 | 38 | if options[:colors] != :none 39 | key = if key =~ /(@\w+)/ 40 | key.sub(Regexp.last_match(1), colorize(Regexp.last_match(1), :variable)) 41 | else 42 | key.sub(/(attr_\w+)\s(:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}") 43 | end 44 | end 45 | 46 | indented do 47 | key + colorize(' = ', :hash) + inspector.awesome(struct.send(var)) 48 | end 49 | end 50 | 51 | if options[:multiline] 52 | "#<#{awesome_instance}\n#{data.join(%(,\n))}\n#{outdent}>" 53 | else 54 | "#<#{awesome_instance} #{data.join(', ')}>" 55 | end 56 | end 57 | 58 | private 59 | 60 | def awesome_instance 61 | # We need to ensure that the original Kernel#format is used here instead of the one defined 62 | # above. 63 | # rubocop:disable Style/ColonMethodCall 64 | if defined?(Data) && struct.is_a?(Data) 65 | Kernel::format("data #{struct.class}:0x%08x", struct.__id__ * 2) 66 | else 67 | Kernel::format("#{struct.class.superclass}:#{struct.class}:0x%08x", struct.__id__ * 2) 68 | end 69 | # rubocop:enable Style/ColonMethodCall 70 | end 71 | 72 | def left_aligned 73 | current = options[:indent] 74 | options[:indent] = 0 75 | yield 76 | ensure 77 | options[:indent] = current 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/object_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class ObjectFormatter < BaseFormatter 8 | attr_reader :object, :variables, :inspector, :options 9 | 10 | def initialize(object, inspector) 11 | super() 12 | @object = object 13 | @variables = object.instance_variables 14 | @inspector = inspector 15 | @options = inspector.options 16 | end 17 | 18 | def format 19 | vars = variables.map do |var| 20 | property = var.to_s[1..].to_sym # to_s because of some monkey patching done by Puppet. 21 | accessor = if object.respond_to?(:"#{property}=") 22 | object.respond_to?(property) ? :accessor : :writer 23 | else 24 | object.respond_to?(property) ? :reader : nil 25 | end 26 | if accessor 27 | ["attr_#{accessor} :#{property}", var] 28 | else 29 | [var.to_s, var] 30 | end 31 | end 32 | 33 | data = (options[:sort_vars] ? vars.sort : vars).map do |declaration, var| 34 | key = left_aligned do 35 | align(declaration, declaration.size) 36 | end 37 | 38 | if options[:colors] != :none 39 | key = if key =~ /(@\w+)/ 40 | key.sub(Regexp.last_match(1), colorize(Regexp.last_match(1), :variable)) 41 | else 42 | key.sub(/(attr_\w+)\s(:\w+)/, "#{colorize('\\1', :keyword)} #{colorize('\\2', :method)}") 43 | end 44 | end 45 | 46 | indented do 47 | key + colorize(' = ', :hash) + inspector.awesome(object.instance_variable_get(var)) 48 | end 49 | end 50 | 51 | if options[:multiline] 52 | "#<#{awesome_instance}\n#{data.join(%(,\n))}\n#{outdent}>" 53 | else 54 | "#<#{awesome_instance} #{data.join(', ')}>" 55 | end 56 | end 57 | 58 | private 59 | 60 | def valid_instance_var?(variable_name) 61 | variable_name.to_s.start_with?('@') 62 | end 63 | 64 | def awesome_instance 65 | str = object.send(options[:class_name]).to_s 66 | return str unless options[:object_id] 67 | 68 | # We need to ensure that the original Kernel#format is used here instead of the one 69 | # defined above. 70 | # rubocop:disable Style/ColonMethodCall 71 | str + Kernel::format(':0x%08x', object.__id__ * 2) 72 | # rubocop:enable Style/ColonMethodCall 73 | end 74 | 75 | def left_aligned 76 | current = options[:indent] 77 | options[:indent] = 0 78 | yield 79 | ensure 80 | options[:indent] = current 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/ripple.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module Ripple 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_ripple, :cast 12 | base.send :alias_method, :cast, :cast_with_ripple 13 | end 14 | 15 | # Add Ripple class names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_ripple(object, type) 18 | cast = cast_without_ripple(object, type) 19 | return cast unless defined?(::Ripple) 20 | 21 | case object 22 | when ::Ripple::AttributeMethods # Module used to access attributes across documents and embedded documents 23 | cast = :ripple_document_instance 24 | when ::Ripple::Properties # Used to access property metadata on Ripple classes 25 | cast = :ripple_document_class 26 | end 27 | cast 28 | end 29 | 30 | private 31 | 32 | # Format Ripple instance object. 33 | # 34 | # NOTE: by default only instance attributes are shown. To format a Ripple document instance 35 | # as a regular object showing its instance variables and accessors use :raw => true option: 36 | # 37 | # ap document, :raw => true 38 | # 39 | #------------------------------------------------------------------------------ 40 | def awesome_ripple_document_instance(object) 41 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 42 | return awesome_object(object) if @options[:raw] 43 | 44 | (exclude_assoc = @options[:exclude_assoc]) || @options[:exclude_associations] 45 | 46 | data = object.attributes.each_with_object(::ActiveSupport::OrderedHash.new) do |(name, _value), hash| 47 | hash[name.to_sym] = object.send(name) 48 | end 49 | 50 | unless exclude_assoc 51 | data = object.class.embedded_associations.each_with_object(data) do |assoc, hash| 52 | hash[assoc.name] = object.get_proxy(assoc) # Should always be array or Ripple::EmbeddedDocument for embedded associations 53 | end 54 | end 55 | 56 | "#{object} " << awesome_hash(data) 57 | end 58 | 59 | # Format Ripple class object. 60 | #------------------------------------------------------------------------------ 61 | def awesome_ripple_document_class(object) 62 | return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:properties) 63 | 64 | name = "class #{awesome_simple(object.to_s, :class)}" 65 | base = "< #{awesome_simple(object.superclass.to_s, :class)}" 66 | 67 | [name, base, awesome_hash(data)].join(' ') 68 | end 69 | end 70 | end 71 | 72 | AmazingPrint::Formatter.include AmazingPrint::Ripple 73 | -------------------------------------------------------------------------------- /lib/amazing_print/custom_defaults.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AmazingPrint 4 | class << self 5 | attr_accessor :defaults, :force_colors 6 | 7 | # Class accessor to force colorized output (ex. forked subprocess where TERM 8 | # might be dumb). 9 | #--------------------------------------------------------------------------- 10 | def force_colors!(colors: true) 11 | @force_colors = colors 12 | end 13 | 14 | def console? 15 | boolean(defined?(IRB) || defined?(Pry)) 16 | end 17 | 18 | def rails_console? 19 | console? && boolean(defined?(Rails::Console) || ENV['RAILS_ENV']) 20 | end 21 | 22 | def diet_rb 23 | IRB.formatter = Class.new(IRB::Formatter) do 24 | def inspect_object(object) 25 | object.ai 26 | end 27 | end.new 28 | end 29 | 30 | def usual_rb 31 | IRB::Irb.class_eval do 32 | def output_value(_omit = false) # rubocop:disable Style/OptionalBooleanParameter 33 | ap @context.last_value 34 | rescue NoMethodError 35 | puts "(Object doesn't support #ai)" 36 | end 37 | end 38 | end 39 | 40 | def irb! 41 | return unless defined?(IRB) 42 | 43 | IRB.version.include?('DietRB') ? diet_rb : usual_rb 44 | end 45 | 46 | def pry! 47 | return unless defined?(Pry) 48 | 49 | Pry.print = proc do |_output, value, pry_instance| 50 | pry_instance.pager.open do |pager| 51 | pager.print "#{value.ai}\n" 52 | end 53 | end 54 | end 55 | 56 | def rdbg! 57 | return unless defined?(::DEBUGGER__::SESSION) 58 | 59 | ::DEBUGGER__::SESSION.extend_feature( 60 | thread_client: Module.new do 61 | # rdbg calls this for both p and pp printing paths 62 | def color_pp(obj, width) 63 | opts = { 64 | multiline: true, 65 | index: false, 66 | indent: 2 67 | } 68 | 69 | opts[:colors] = :none if ::DEBUGGER__::CONFIG[:no_color] 70 | 71 | if obj.respond_to?(:ai) 72 | obj.ai(opts) 73 | else 74 | # Fallback if AP isn't mixed into the object for some reason 75 | super 76 | end 77 | rescue StandardError 78 | # Never break the debugger; fall back to rdbg default 79 | super 80 | end 81 | end 82 | ) 83 | end 84 | 85 | ## 86 | # Reload the cached custom configurations. 87 | # 88 | def reload! 89 | AmazingPrint::Inspector.reload_dotfile 90 | end 91 | 92 | private 93 | 94 | # Takes a value and returns true unless it is false or nil 95 | # This is an alternative to the less readable !!(value) 96 | # https://github.com/bbatsov/ruby-style-guide#no-bang-bang 97 | def boolean(value) # rubocop:todo Naming/PredicateMethod 98 | value ? true : false 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests. Here's a quick guide: 4 | 5 | 1. Fork the repo. 6 | 7 | 1. Create your feature branch (`git checkout -b my-new-feature`) 8 | 9 | 1. Update CHANGELOG.md with a brief description of your changes under the `unreleased` heading. 10 | 11 | 1. Add/Update tests were appropriate 12 | 13 | 1. Commit your changes (`git commit -am 'Added some feature'`) 14 | 15 | 1. Push to the branch (`git push origin my-new-feature`) 16 | 17 | 1. Create new Pull Request 18 | 19 | At this point you're waiting on us. We are not super fast at responding, but we will do our best to get to your PR as soon as time permits. We may suggest some changes, improvements or alternatives. 20 | 21 | Some things that will increase the chance that your pull request is accepted is to follow the practices described on [Ruby style guide](https://github.com/bbatsov/ruby-style-guide), [Rails style guide](https://github.com/bbatsov/rails-style-guide) and [Better Specs](http://betterspecs.org/). 22 | 23 | ## Specs 24 | 25 | To run all the specs in all gemfiles just run: 26 | 27 | ``` 28 | $ rake 29 | ``` 30 | 31 | To run specs of a single gemfile run: 32 | 33 | ``` 34 | $ appraisal rails-3.2 rake 35 | ``` 36 | 37 | If you want to run a specific spec in a gemfile run: 38 | 39 | ``` 40 | $ appraisal rails-3.2 rspec spec/colors_spec.rb 41 | ``` 42 | 43 | ## Contributor Rolecall 44 | 45 | Special thanks goes to amazing team of contributors, namely: 46 | 47 | * 6fusion.com -- https://github.com/6fusion 48 | * Adam Doppelt -- https://github.com/gurgeous 49 | * Andrew O'Brien -- https://github.com/AndrewO 50 | * Andrew Horsman -- https://github.com/basicxman 51 | * Barry Allard -- https://github.com/steakknife 52 | * Benoit Daloze -- http://github.com/eregon 53 | * Brandon Zylstra -- https://github.com/brandondrew 54 | * Dan Lynn -- https://github.com/danlynn 55 | * Daniel Johnson -- https://github.com/adhd360 56 | * Daniel Bretoi -- http://github.com/danielb2 57 | * Eloy Duran -- http://github.com/alloy 58 | * Elpizo Choi -- https://github.com/fuJiin 59 | * Evan Senter -- https://github.com/evansenter 60 | * George . -- https://github.com/gardelea 61 | * Greg Weber -- https://github.com/gregwebs 62 | * Jan Vansteenkiste -- https://github.com/vStone 63 | * Jeff Felchner -- https://github.com/jfelchner 64 | * Jonathan Davies -- send your Github URL ;-) 65 | * Kevin Olbrich -- https://github.com/olbrich 66 | * Matthew Schulkind -- https://github.com/mschulkind 67 | * Mike McQuaid -- https://github.com/mikemcquaid 68 | * Nami-Doc -- https://github.com/Nami-Doc 69 | * Nicolas Viennot -- https://github.com/nviennot 70 | * Nikolaj Nikolajsen -- https://github.com/nikolajsen 71 | * Richard Hall -- https://github.com/richardardrichard 72 | * Ryan Schlesinger -- https://github.com/ryansch 73 | * Scott Hyndman -- https://github.com/shyndman 74 | * Sean Gallagher -- http://github.com/torandu 75 | * Stephan Hagemann -- https://github.com/shageman 76 | * Tim Harper -- http://github.com/timcharper 77 | * Tobias Crawley -- http://github.com/tobias 78 | * Thibaut Barrère -- https://github.com/thbar 79 | * Trevor Wennblom -- https://github.com/trevor 80 | * vfrride -- https://github.com/vfrride 81 | * Viktar Basharymau -- https://github.com/DNNX 82 | -------------------------------------------------------------------------------- /lib/amazing_print/core_ext/awesome_method_array.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | # 9 | # The following makes it possible to invoke amazing_print while performing 10 | # operations on method arrays, ex: 11 | # 12 | # ap [].methods - Object.methods 13 | # ap ''.methods.grep(/!|\?/) 14 | # 15 | # If you could think of a better way please let me know :-) 16 | # 17 | module AwesomeMethodArray # :nodoc: 18 | def -(other) 19 | super.tap do |arr| 20 | arr.instance_variable_set(:@__awesome_methods__, instance_variable_get(:@__awesome_methods__)) 21 | end 22 | end 23 | 24 | def &(other) 25 | super.tap do |arr| 26 | arr.instance_variable_set(:@__awesome_methods__, instance_variable_get(:@__awesome_methods__)) 27 | end 28 | end 29 | 30 | # 31 | # Intercepting Array#grep needs a special treatment since grep accepts 32 | # an optional block. 33 | # 34 | def grep(pattern, &blk) 35 | # 36 | # The following looks rather insane and I've sent numerous hours trying 37 | # to figure it out. The problem is that if grep gets called with the 38 | # block, for example: 39 | # 40 | # [].methods.grep(/(.+?)_by/) { $1.to_sym } 41 | # 42 | # ...then simple: 43 | # 44 | # original_grep(pattern, &blk) 45 | # 46 | # doesn't set $1 within the grep block which causes nil.to_sym failure. 47 | # The workaround below has been tested with Ruby 1.8.7/Rails 2.3.8 and 48 | # Ruby 1.9.2/Rails 3.0.0. For more info see the following thread dating 49 | # back to 2003 when Ruby 1.8.0 was as fresh off the grill as Ruby 1.9.2 50 | # is in 2010 :-) 51 | # 52 | # http://www.justskins.com/forums/bug-when-rerouting-string-52852.html 53 | # 54 | # BTW, if you figure out a better way of intercepting Array#grep please 55 | # let me know: twitter.com/mid -- or just say hi so I know you've read 56 | # the comment :-) 57 | # 58 | arr = if blk 59 | super do |match| 60 | # 61 | # The binding can only be used with Ruby-defined methods, therefore 62 | # we must rescue potential "ArgumentError: Can't create Binding from 63 | # C level Proc" error. 64 | # 65 | # For example, the following raises ArgumentError since #succ method 66 | # is defined in C. 67 | # 68 | # [ 0, 1, 2, 3, 4 ].grep(1..2, &:succ) 69 | # 70 | begin 71 | eval("%Q/#{match.to_s.gsub('/', '\/')}/ =~ #{pattern.inspect}", blk.binding, __FILE__, __LINE__) 72 | rescue StandardError 73 | ArgumentError 74 | end 75 | yield match 76 | end 77 | else 78 | super 79 | end 80 | arr.instance_variable_set(:@__awesome_methods__, instance_variable_get(:@__awesome_methods__)) 81 | arr.select! { |item| item.is_a?(Symbol) || item.is_a?(String) } # grep block might return crap. 82 | arr 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/mongoid.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module Mongoid 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_mongoid, :cast 12 | base.send :alias_method, :cast, :cast_with_mongoid 13 | end 14 | 15 | # Add Mongoid class names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_mongoid(object, type) 18 | cast = cast_without_mongoid(object, type) 19 | if defined?(::Mongoid::Document) 20 | if object.is_a?(Class) && object.ancestors.include?(::Mongoid::Document) 21 | cast = :mongoid_class 22 | elsif object.class.ancestors.include?(::Mongoid::Document) 23 | cast = :mongoid_document 24 | elsif (defined?(::BSON) && object.is_a?(::BSON::ObjectId)) || (defined?(::Moped::BSON) && object.is_a?(::Moped::BSON::ObjectId)) 25 | cast = :mongoid_bson_id 26 | end 27 | end 28 | cast 29 | end 30 | 31 | # Format Mongoid class object. 32 | #------------------------------------------------------------------------------ 33 | def awesome_mongoid_class(object) 34 | return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:fields) 35 | 36 | aliases = object.aliased_fields.invert 37 | data = object.fields.sort.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 38 | name = c[1].name 39 | alias_name = aliases[name] unless name == '_id' 40 | printed_name = alias_name ? "#{alias_name}(#{name})" : name 41 | 42 | hash[printed_name.to_sym] = (c[1].type || 'undefined').to_s.underscore.intern 43 | hash 44 | end 45 | 46 | name = "class #{awesome_simple(object.to_s, :class)}" 47 | base = "< #{awesome_simple(object.superclass.to_s, :class)}" 48 | 49 | [name, base, awesome_hash(data)].join(' ') 50 | end 51 | 52 | # Format Mongoid Document object. 53 | #------------------------------------------------------------------------------ 54 | def awesome_mongoid_document(object) 55 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 56 | 57 | aliases = object.aliased_fields.invert 58 | data = (object.attributes || {}).sort.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 59 | name = c[0] 60 | alias_name = aliases[name] unless name == '_id' 61 | printed_name = alias_name ? "#{alias_name}(#{name})" : name 62 | 63 | hash[printed_name.to_sym] = c[1] 64 | hash 65 | end 66 | data = { errors: object.errors, attributes: data } unless object.errors.empty? 67 | "#{object} #{awesome_hash(data)}" 68 | end 69 | 70 | # Format BSON::ObjectId 71 | #------------------------------------------------------------------------------ 72 | def awesome_mongoid_bson_id(object) 73 | object.inspect 74 | end 75 | end 76 | end 77 | 78 | AmazingPrint::Formatter.include AmazingPrint::Mongoid 79 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Unreleased 4 | 5 | - Introduce new `colors` option that can have the following values: 6 | - `:all`. Colors everything. Default. 7 | - `:values_only`. Only colors hash values, not hash keys. 8 | - `:none`. Disables colors. 9 | - Deprecate `plain` option which was used to disable colors. The new `colors` 10 | option makes it redundant. 11 | 12 | ## v2.0.0 13 | 14 | - Rework hash formats and add JSON format (#83) 15 | - Format Data objects using StructFormatter (#138) 16 | 17 | ## v1.8.1 18 | 19 | - Fix authors list in gemspec #128 20 | - Use array.length.zero? to avoid issues with ActiveRecord collection proxy #129 21 | - grammar glowup in README.md #130 22 | 23 | ## v1.8.0 24 | 25 | - Drop Ruby 3.0 and add Ruby 3.4 #125 26 | 27 | ## v1.7.2 28 | 29 | - update the version string in `AmazingPrint.version` 30 | 31 | ## v1.7.1 32 | 33 | - no changes, only linting 34 | 35 | ## v1.7.0 36 | 37 | - Require Ruby >= 3.0 38 | - Add support for `::ActiveModel::Attributes` 39 | - Preserve user defined color defaults 40 | - Micro-optimization to `AmazingPrint::ActiveRecord#cast` (avoid calls to `#ancestors`) 41 | 42 | ## v1.6.0 43 | 44 | - Use pager with Pry #96 45 | - Add Rails 7 appraisal #98 46 | - Allow Hash keys to be colorized #99 47 | - Use CI merge queue #104 48 | - Add support for Ruby 3.3 #105 49 | - Add Mongoid field aliases #106 50 | - Add bigdecimal gem #109 51 | - Add ExtLoader to help with require order issues #110 52 | 53 | ## v1.5.0 54 | 55 | - Drop support for Ruby 2.3 and 2.4 as well as JRuby 9.1 56 | - Add File/Dir formatters for mswin platform #48 57 | - Don't monkey patch String class #91 58 | - Fix ruby19 hash syntax so it can be copy-pasted #94 59 | 60 | ## v1.4.0 61 | 62 | - Support loading config from `$XDG_CONFIG_HOME/aprc` - #63 63 | - Remove support for Rails 5.1 #75 64 | - Update AR specs for Ruby 2.6.7 #76 65 | - Load .aprc configs only once. #74 66 | - Add XDG config support #77 67 | - Rubocop updates #79 68 | - Update Irb integration for v1.2.6+ #81 69 | 70 | ## v1.3.0 71 | 72 | - Fix HTML escaping problems #53 73 | - Update test suite for Ruby 2.7.2 and JRuby #61 74 | - Add ActionView spec for html_safe #65 75 | - Add support for Rails 6.1 #68 76 | - Update specs for Ruby 3.0 #69 77 | 78 | ## v1.2.2 79 | 80 | - Support Ruby 3.0 / IRB 1.2.6 - #57 81 | - Fix FrozenError - #51 82 | - Drop support for Ruby 2.3 and 2.4 as well as JRuby 9.1 - #46 83 | - Add passing of `options` to `Logger#ap` - #55 84 | 85 | ## v1.2.1 86 | 87 | - Correctly print active_model_errors for models that don't have tables - #42 by sahglie 88 | - Update AmazingPrint::MongoMapper for frozen strings - #44 89 | 90 | ## v1.2.0 91 | 92 | - Fix frozen string literal issue with ActiveRecord 93 | - Add uncolor String method to remove ANSI color codes - #30 by duffyjp 94 | - Restore original copyright - #33 by amarshall 95 | - Remove method core extension since it is not needed since ruby 1.9 - #37 by grosser 96 | - Remove pale and black string color aliases - #38 97 | - Fix formatting ActionController::Parameters - #29 98 | 99 | ## v1.1.0 100 | 101 | - Print out method keyword arguments 102 | - Fix NoMethodError with Sequel 103 | - Code cleanups 104 | 105 | Thanks for the great contributions from: 106 | 107 | - andydna 108 | - beanieboi 109 | 110 | ## v1.0.0 111 | 112 | - Initial Release. 113 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | # 9 | # Running specs from the command line: 10 | # $ rake spec # Entire spec suite. 11 | # $ rspec spec/objects_spec.rb # Individual spec file. 12 | # 13 | # NOTE: To successfully run specs with Ruby 1.8.6 the older versions of 14 | # Bundler and RSpec gems are required: 15 | # 16 | # $ gem install bundler -v=1.0.2 17 | # $ gem install rspec -v=2.6.0 18 | # 19 | 20 | # require 'simplecov' 21 | # SimpleCov.start 22 | 23 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 24 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 25 | 26 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |file| 27 | require file 28 | end 29 | 30 | ExtVerifier.require_dependencies!( 31 | %w[ 32 | rails 33 | active_record 34 | action_controller 35 | action_view 36 | active_support/all 37 | mongoid 38 | mongo_mapper 39 | ripple nobrainer 40 | ostruct 41 | sequel 42 | ] 43 | ) 44 | require 'nokogiri' unless RUBY_PLATFORM.include?('mswin') 45 | require 'amazing_print' 46 | 47 | RSpec.configure do |config| 48 | config.disable_monkey_patching! 49 | # TODO: Make specs not order dependent 50 | # config.order = :random 51 | Kernel.srand config.seed 52 | config.filter_run focus: true 53 | config.run_all_when_everything_filtered = true 54 | config.expect_with :rspec do |expectations| 55 | expectations.syntax = :expect 56 | end 57 | config.mock_with :rspec do |mocks| 58 | mocks.syntax = :expect 59 | mocks.verify_partial_doubles = true 60 | end 61 | 62 | config.default_formatter = 'doc' if config.files_to_run.one? 63 | 64 | # Run before all examples. Using suite or all will not work as stubs are 65 | # killed after each example ends. 66 | config.before do |_example| 67 | stub_dotfile! 68 | end 69 | 70 | if RUBY_PLATFORM.include?('mswin') 71 | config.filter_run_excluding unix: true 72 | else 73 | config.filter_run_excluding mswin: true 74 | end 75 | end 76 | 77 | # This matcher handles the normalization of objects to replace non deterministic 78 | # parts (such as object IDs) with simple placeholder strings before doing a 79 | # comparison with a given string. It's important that this method only matches 80 | # a string which strictly conforms to the expected object ID format. 81 | RSpec::Matchers.define :be_similar_to do |expected, options| 82 | match do |actual| 83 | options ||= {} 84 | @actual = normalize_object_id_strings(actual, options) 85 | values_match? expected, @actual 86 | end 87 | 88 | diffable 89 | end 90 | 91 | # Override the Object IDs with a placeholder so that we are only checking 92 | # that an ID is present and not that it matches a certain value. This is 93 | # necessary as the Object IDs are not deterministic. 94 | def normalize_object_id_strings(str, options) 95 | str = str.gsub(/#<(.*?):0x[a-f\d]+/, '#<\1:placeholder_id') unless options[:skip_standard] 96 | str = str.gsub(/BSON::ObjectId\('[a-f\d]{24}'\)/, 'placeholder_bson_id') unless options[:skip_bson] 97 | str 98 | end 99 | 100 | def stub_dotfile! 101 | allow_any_instance_of(AmazingPrint::Inspector) 102 | .to receive(:load_dotfile) 103 | .and_return(true) 104 | end 105 | 106 | def capture! 107 | standard = $stdout 108 | $stdout = StringIO.new 109 | yield 110 | ensure 111 | $stdout = standard 112 | end 113 | -------------------------------------------------------------------------------- /spec/colors_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Lint/ConstantDefinitionInBlock, Style/OptionalBooleanParameter 4 | 5 | require 'spec_helper' 6 | 7 | RSpec.describe 'AmazingPrint' do 8 | def stub_tty!(output = true, stream = $stdout) 9 | if output 10 | stream.instance_eval do 11 | def tty? 12 | true 13 | end 14 | end 15 | else 16 | stream.instance_eval do 17 | def tty? 18 | false 19 | end 20 | end 21 | end 22 | end 23 | 24 | describe 'colorization' do 25 | PLAIN = '[ 1, :two, "three", [ nil, [ true, false ] ] ]' 26 | COLORIZED = "[ \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 ] ] ]" 27 | 28 | before do 29 | ENV['TERM'] = 'xterm-colors' 30 | ENV.delete('ANSICON') 31 | @arr = [1, :two, 'three', [nil, [true, false]]] 32 | end 33 | 34 | describe 'default settings (no forced colors)' do 35 | before do 36 | AmazingPrint.force_colors! colors: false 37 | end 38 | 39 | it 'colorizes tty processes by default' do 40 | stub_tty! 41 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 42 | end 43 | 44 | it "colorizes processes with ENV['ANSICON'] by default" do 45 | stub_tty! 46 | term = ENV['ANSICON'] 47 | ENV['ANSICON'] = '1' 48 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 49 | ensure 50 | ENV['ANSICON'] = term 51 | end 52 | 53 | it 'does not colorize tty processes running in dumb terminals by default' do 54 | stub_tty! 55 | term = ENV['TERM'] 56 | ENV['TERM'] = 'dumb' 57 | expect(@arr.ai(multiline: false)).to eq(PLAIN) 58 | ensure 59 | ENV['TERM'] = term 60 | end 61 | 62 | it 'does not colorize subprocesses by default' do 63 | stub_tty! false 64 | expect(@arr.ai(multiline: false)).to eq(PLAIN) 65 | ensure 66 | stub_tty! 67 | end 68 | end 69 | 70 | describe 'forced colors override' do 71 | before do 72 | AmazingPrint.force_colors! 73 | end 74 | 75 | it 'still colorizes tty processes' do 76 | stub_tty! 77 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 78 | end 79 | 80 | it "colorizes processes with ENV['ANSICON'] set to 0" do 81 | stub_tty! 82 | term = ENV['ANSICON'] 83 | ENV['ANSICON'] = '1' 84 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 85 | ensure 86 | ENV['ANSICON'] = term 87 | end 88 | 89 | it 'colorizes dumb terminals' do 90 | stub_tty! 91 | term = ENV['TERM'] 92 | ENV['TERM'] = 'dumb' 93 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 94 | ensure 95 | ENV['TERM'] = term 96 | end 97 | 98 | it 'colorizes subprocess' do 99 | stub_tty! false 100 | expect(@arr.ai(multiline: false)).to eq(COLORIZED) 101 | ensure 102 | stub_tty! 103 | end 104 | end 105 | end 106 | 107 | describe 'AmazingPrint::Colors' do 108 | %i[gray red green yellow blue purple cyan white].each_with_index do |color, i| 109 | it "has #{color} color" do 110 | expect(AmazingPrint::Colors.public_send(color, color.to_s)).to eq("\e[1;#{i + 30}m#{color}\e[0m") 111 | end 112 | 113 | it "has #{color}ish color" do 114 | expect(AmazingPrint::Colors.public_send(:"#{color}ish", color.to_s)).to eq("\e[0;#{i + 30}m#{color}\e[0m") 115 | end 116 | end 117 | end 118 | end 119 | 120 | # rubocop:enable Lint/ConstantDefinitionInBlock, Style/OptionalBooleanParameter 121 | -------------------------------------------------------------------------------- /spec/support/active_record_data/4_1_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 19 | "created_at" => #, 32 | "id" => #, 45 | "name" => #, 58 | "rank" => # 71 | }, 72 | @column_types_override = nil, 73 | @destroyed = false, 74 | @marked_for_destruction = false, 75 | @new_record = true, 76 | @readonly = false, 77 | @reflects_state = [ 78 | [0] false 79 | ], 80 | @transaction_state = nil, 81 | @txn = nil, 82 | attr_accessor :attributes = { 83 | "admin" => false, 84 | "created_at" => "1992-10-10 12:30:00", 85 | "id" => nil, 86 | "name" => "Diana", 87 | "rank" => 1 88 | }, 89 | attr_accessor :destroyed_by_association = nil, 90 | attr_reader :association_cache = {}, 91 | attr_reader :changed_attributes = { 92 | "admin" => nil, 93 | "created_at" => nil, 94 | "name" => nil, 95 | "rank" => nil 96 | } 97 | > 98 | -------------------------------------------------------------------------------- /spec/support/active_record_data/4_0_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 19 | "created_at" => #, 32 | "id" => #, 45 | "name" => #, 58 | "rank" => # 71 | }, 72 | @column_types_override = nil, 73 | @destroyed = false, 74 | @marked_for_destruction = false, 75 | @new_record = true, 76 | @previously_changed = {}, 77 | @readonly = false, 78 | @reflects_state = [ 79 | [0] false 80 | ], 81 | @transaction_state = nil, 82 | @txn = nil, 83 | attr_accessor :attributes = { 84 | "admin" => false, 85 | "created_at" => "1992-10-10 12:30:00", 86 | "id" => nil, 87 | "name" => "Diana", 88 | "rank" => 1 89 | }, 90 | attr_accessor :destroyed_by_association = nil, 91 | attr_reader :association_cache = {}, 92 | attr_reader :changed_attributes = { 93 | "admin" => nil, 94 | "created_at" => nil, 95 | "name" => nil, 96 | "rank" => nil 97 | } 98 | > 99 | -------------------------------------------------------------------------------- /spec/ext/mongoid_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Lint/ConstantDefinitionInBlock 4 | 5 | require 'spec_helper' 6 | 7 | RSpec.describe 'AmazingPrint/Mongoid', skip: -> { !ExtVerifier.has_mongoid? }.call do 8 | before do 9 | @ap = AmazingPrint::Inspector.new colors: :none, sort_keys: true 10 | end 11 | 12 | describe 'Document' do 13 | if ExtVerifier.has_mongoid? 14 | before :all do 15 | class MongoUser 16 | include Mongoid::Document 17 | 18 | field :first_name, type: String 19 | field :last_name, type: String 20 | end 21 | end 22 | 23 | after :all do 24 | Object.instance_eval { remove_const :MongoUser } 25 | Object.instance_eval { remove_const :Chamelion } 26 | end 27 | end 28 | 29 | it 'prints class instance' do 30 | user = MongoUser.new first_name: 'Al', last_name: 'Capone' 31 | out = @ap.send :awesome, user 32 | 33 | object_id = user.id.inspect 34 | str = <<~EOS.strip 35 | # { 36 | _id: #{object_id}, 37 | first_name: "Al", 38 | last_name: "Capone" 39 | } 40 | EOS 41 | expect(out).to be_similar_to(str, { skip_bson: true }) 42 | end 43 | 44 | it 'prints the class' do 45 | class_spec = <<~EOS.strip 46 | class MongoUser < Object { 47 | _id: :"bson/object_id", 48 | first_name: :string, 49 | last_name: :string 50 | } 51 | EOS 52 | 53 | expect(@ap.send(:awesome, MongoUser)).to eq class_spec 54 | end 55 | 56 | it 'prints the class when type is undefined' do 57 | class Chamelion 58 | include Mongoid::Document 59 | 60 | field :last_attribute 61 | end 62 | 63 | class_spec = <<~EOS.strip 64 | class Chamelion < Object { 65 | _id: :"bson/object_id", 66 | last_attribute: :object 67 | } 68 | EOS 69 | 70 | expect(@ap.send(:awesome, Chamelion)).to eq class_spec 71 | end 72 | end 73 | 74 | describe 'Document with aliased fields' do 75 | if ExtVerifier.has_mongoid? 76 | before :all do 77 | class MongoUser 78 | include Mongoid::Document 79 | 80 | field :fn, as: :first_name, type: String 81 | field :ln, as: :last_name, type: String 82 | end 83 | end 84 | 85 | after :all do 86 | Object.instance_eval { remove_const :MongoUser } 87 | Object.instance_eval { remove_const :Chamelion } 88 | end 89 | end 90 | 91 | it 'prints class instance' do 92 | user = MongoUser.new first_name: 'Al', last_name: 'Capone' 93 | out = @ap.send :awesome, user 94 | 95 | object_id = user.id.inspect 96 | str = <<~EOS.strip 97 | # { 98 | _id: #{object_id}, 99 | "first_name(fn)": "Al", 100 | "last_name(ln)": "Capone" 101 | } 102 | EOS 103 | expect(out).to be_similar_to(str, { skip_bson: true }) 104 | end 105 | 106 | it 'prints the class' do 107 | class_spec = <<~EOS.strip 108 | class MongoUser < Object { 109 | _id: :"bson/object_id", 110 | "first_name(fn)": :string, 111 | "last_name(ln)": :string 112 | } 113 | EOS 114 | 115 | expect(@ap.send(:awesome, MongoUser)).to eq class_spec 116 | end 117 | 118 | it 'prints the class when type is undefined' do 119 | class Chamelion 120 | include Mongoid::Document 121 | 122 | field :la, as: :last_attribute 123 | end 124 | 125 | class_spec = <<~EOS.strip 126 | class Chamelion < Object { 127 | _id: :"bson/object_id", 128 | "last_attribute(la)": :object 129 | } 130 | EOS 131 | 132 | expect(@ap.send(:awesome, Chamelion)).to eq class_spec 133 | end 134 | end 135 | end 136 | 137 | # rubocop:enable Lint/ConstantDefinitionInBlock 138 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/array_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | require_relative '../json_helper' 5 | 6 | module AmazingPrint 7 | module Formatters 8 | class ArrayFormatter < BaseFormatter 9 | include AmazingPrint::JSONHelper 10 | 11 | attr_reader :array, :inspector, :options 12 | 13 | def initialize(array, inspector) 14 | super() 15 | @array = array 16 | @inspector = inspector 17 | @options = inspector.options 18 | end 19 | 20 | def format 21 | if array.length.zero? # rubocop:disable Style/ZeroLengthPredicate 22 | '[]' 23 | elsif methods_array? 24 | methods_array 25 | else 26 | simple_array 27 | end 28 | end 29 | 30 | private 31 | 32 | def methods_array? 33 | array.instance_variable_defined?('@__awesome_methods__') 34 | end 35 | 36 | def simple_array 37 | if options[:multiline] 38 | multiline_array 39 | else 40 | "[ #{array.map { |item| inspector.awesome(item) }.join(', ')} ]" 41 | end 42 | end 43 | 44 | def multiline_array 45 | data = if should_be_limited? 46 | limited(generate_printable_array, width(array)) 47 | else 48 | generate_printable_array 49 | end 50 | 51 | %([\n#{data.join(",\n")}\n#{outdent}]) 52 | end 53 | 54 | def generate_printable_array 55 | array.map.with_index do |item, index| 56 | array_prefix(index, width(array)).tap do |temp| 57 | indented do 58 | temp << json_awesome(item) 59 | end 60 | end 61 | end 62 | end 63 | 64 | def array_prefix(iteration, width) 65 | generic_prefix(iteration, width) 66 | end 67 | 68 | def methods_array 69 | array.map!(&:to_s).sort! 70 | 71 | data = generate_printable_tuples.join("\n") 72 | 73 | "[\n#{data}\n#{outdent}]" 74 | end 75 | 76 | def generate_printable_tuples 77 | tuples.map.with_index do |item, index| 78 | tuple_prefix(index, width(tuples)).tap do |temp| 79 | indented { temp << tuple_template(item) } 80 | end 81 | end 82 | end 83 | 84 | def tuple_template(item) 85 | name_width, args_width = name_and_args_width 86 | 87 | [ 88 | colorize(item[0].rjust(name_width), :method), 89 | colorize(item[1].ljust(args_width), :args), 90 | ' ', 91 | colorize(item[2], :class) 92 | ].join 93 | end 94 | 95 | def tuples 96 | @tuples ||= array.map { |name| generate_tuple(name) } 97 | end 98 | 99 | def name_and_args_width 100 | name_and_args = tuples.transpose 101 | 102 | [name_and_args[0].map(&:size).max, name_and_args[1].map(&:size).max] 103 | end 104 | 105 | def tuple_prefix(iteration, width) 106 | generic_prefix(iteration, width, ' ') 107 | end 108 | 109 | def generate_tuple(name) 110 | meth = case name 111 | when Symbol, String 112 | find_method(name) 113 | end 114 | 115 | meth ? method_tuple(meth) : [name.to_s, '(?)', '?'] 116 | end 117 | 118 | def find_method(name) 119 | object = array.instance_variable_get('@__awesome_methods__') 120 | 121 | meth = begin 122 | object.method(name) 123 | rescue NameError, ArgumentError 124 | nil 125 | end 126 | 127 | meth || begin 128 | object.instance_method(name) 129 | rescue NameError 130 | nil 131 | end 132 | end 133 | 134 | def generic_prefix(iteration, width, padding = '') 135 | if options[:index] && options[:hash_format] != :json 136 | indent + colorize("[#{iteration.to_s.rjust(width)}] ", :array) 137 | else 138 | indent + padding 139 | end 140 | end 141 | 142 | def width(items) 143 | (items.size - 1).to_s.size 144 | end 145 | end 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /lib/amazing_print/formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | require_relative 'formatters' 9 | 10 | module AmazingPrint 11 | class Formatter 12 | include Colorize 13 | 14 | attr_reader :inspector, :options 15 | 16 | CORE_FORMATTERS = %i[array bigdecimal class dir file hash method rational set struct unboundmethod].freeze 17 | 18 | def initialize(inspector) 19 | @inspector = inspector 20 | @options = inspector.options 21 | end 22 | 23 | # Main entry point to format an object. 24 | #------------------------------------------------------------------------------ 25 | def format(object, type = nil) 26 | core_class = cast(object, type) 27 | if core_class == :self 28 | awesome_self(object, type) # Catch all that falls back to object.inspect. 29 | else 30 | send(:"awesome_#{core_class}", object) # Core formatters. 31 | end 32 | end 33 | 34 | # Hook this when adding custom formatters. Check out lib/amazing_print/ext 35 | # directory for custom formatters that ship with amazing_print. 36 | #------------------------------------------------------------------------------ 37 | def cast(_object, type) 38 | CORE_FORMATTERS.include?(type) ? type : :self 39 | end 40 | 41 | private 42 | 43 | # Catch all method to format an arbitrary object. 44 | #------------------------------------------------------------------------------ 45 | def awesome_self(object, type) 46 | if @options[:raw] && object.instance_variables.any? 47 | awesome_object(object) 48 | elsif (hash = convert_to_hash(object)) 49 | awesome_hash(hash) 50 | else 51 | awesome_simple(object.inspect.to_s, type, @inspector) # rubocop:disable Lint/RedundantTypeConversion 52 | end 53 | end 54 | 55 | def awesome_bigdecimal(n) 56 | o = n.to_s('F') 57 | type = :bigdecimal 58 | awesome_simple(o, type, @inspector) 59 | end 60 | 61 | def awesome_rational(n) 62 | o = n.to_s 63 | type = :rational 64 | awesome_simple(o, type, @inspector) 65 | end 66 | 67 | def awesome_simple(o, type, inspector = @inspector) 68 | AmazingPrint::Formatters::SimpleFormatter.new(o, type, inspector).format 69 | end 70 | 71 | def awesome_array(a) 72 | Formatters::ArrayFormatter.new(a, @inspector).format 73 | end 74 | 75 | def awesome_set(s) 76 | Formatters::ArrayFormatter.new(s.to_a, @inspector).format 77 | end 78 | 79 | def awesome_hash(h) 80 | Formatters::HashFormatter.new(h, @inspector).format 81 | end 82 | 83 | def awesome_object(o) 84 | Formatters::ObjectFormatter.new(o, @inspector).format 85 | end 86 | 87 | def awesome_struct(s) 88 | Formatters::StructFormatter.new(s, @inspector).format 89 | end 90 | 91 | def awesome_method(m) 92 | Formatters::MethodFormatter.new(m, @inspector).format 93 | end 94 | alias awesome_unboundmethod awesome_method 95 | 96 | def awesome_class(c) 97 | Formatters::ClassFormatter.new(c, @inspector).format 98 | end 99 | 100 | def awesome_file(f) 101 | Formatters::FileFormatter.new(f, @inspector).format 102 | end 103 | 104 | def awesome_dir(d) 105 | Formatters::DirFormatter.new(d, @inspector).format 106 | end 107 | 108 | # Utility methods. 109 | #------------------------------------------------------------------------------ 110 | def convert_to_hash(object) 111 | return nil unless object.respond_to?(:to_hash) 112 | 113 | return nil if object.method(:to_hash).arity != 0 114 | 115 | # ActionController::Parameters will raise if they are not yet permitted and 116 | # we try to convert to hash. 117 | # https://api.rubyonrails.org/classes/ActionController/Parameters.html 118 | return nil if object.respond_to?(:permitted?) && !object.permitted? 119 | 120 | hash = object.to_hash 121 | return nil if !hash.respond_to?(:keys) || !hash.respond_to?(:[]) 122 | 123 | hash 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /spec/support/active_record_data/4_2_diana.txt: -------------------------------------------------------------------------------- 1 | # false, 9 | "created_at" => 1992-10-10 12:30:00 UTC, 10 | "name" => "Diana", 11 | "rank" => 1 12 | }, 13 | @readonly = false, 14 | @transaction_state = nil, 15 | @txn = nil, 16 | attr_accessor :attributes = # #, 27 | attr_reader :value = false, 28 | attr_reader :value_before_type_cast = false 29 | >, 30 | "created_at" => #, 37 | attr_reader :value = 1992-10-10 12:30:00 UTC, 38 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 39 | >, 40 | "name" => #, 47 | attr_reader :value = "Diana", 48 | attr_reader :value_before_type_cast = "Diana" 49 | >, 50 | "rank" => #, 58 | attr_reader :value = 1, 59 | attr_reader :value_before_type_cast = 1 60 | > 61 | }, 62 | @materialized = false, 63 | @types = { 64 | "admin" => #, 69 | "created_at" => #, 74 | "id" => #, 80 | "name" => #, 85 | "rank" => # 91 | }, 92 | @values = { 93 | "admin" => nil, 94 | "created_at" => nil, 95 | "id" => nil, 96 | "name" => nil, 97 | "rank" => nil 98 | } 99 | > 100 | >, 101 | attr_accessor :destroyed_by_association = nil, 102 | attr_reader :association_cache = {}, 103 | attr_reader :changed_attributes = { 104 | "admin" => nil, 105 | "created_at" => nil, 106 | "name" => nil, 107 | "rank" => nil 108 | } 109 | > 110 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/hash_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'base_formatter' 4 | require_relative '../json_helper' 5 | 6 | module AmazingPrint 7 | module Formatters 8 | class HashFormatter < BaseFormatter 9 | include AmazingPrint::JSONHelper 10 | 11 | VALID_HASH_FORMATS = %i[json rocket symbol].freeze 12 | 13 | class InvalidHashFormatError < StandardError; end 14 | 15 | attr_reader :hash, :inspector, :options 16 | 17 | def initialize(hash, inspector) 18 | super() 19 | @hash = hash 20 | @inspector = inspector 21 | @options = inspector.options 22 | 23 | return if VALID_HASH_FORMATS.include?(options[:hash_format]) 24 | 25 | raise(InvalidHashFormatError, "Invalid hash_format: #{options[:hash_format].inspect}. " \ 26 | "Must be one of #{VALID_HASH_FORMATS}") 27 | end 28 | 29 | def format 30 | if hash.empty? 31 | empty_hash 32 | elsif multiline_hash? 33 | multiline_hash 34 | else 35 | simple_hash 36 | end 37 | end 38 | 39 | private 40 | 41 | def empty_hash 42 | '{}' 43 | end 44 | 45 | def multiline_hash? 46 | options[:multiline] 47 | end 48 | 49 | def multiline_hash 50 | ["{\n", printable_hash.join(",\n"), "\n#{outdent}}"].join 51 | end 52 | 53 | def simple_hash 54 | "{ #{printable_hash.join(', ')} }" 55 | end 56 | 57 | def printable_hash 58 | data = printable_keys 59 | width = left_width(data) 60 | 61 | data.map! do |key, value| 62 | indented do 63 | case options[:hash_format] 64 | when :json 65 | json_syntax(key, value, width) 66 | when :rocket 67 | pre_ruby19_syntax(key, value, width) 68 | when :symbol 69 | ruby19_syntax(key, value, width) 70 | end 71 | end 72 | end 73 | 74 | should_be_limited? ? limited(data, width, is_hash: true) : data 75 | end 76 | 77 | def left_width(keys) 78 | result = max_key_width(keys) 79 | result += indentation if options[:indent].positive? 80 | result 81 | end 82 | 83 | def key_size(key) 84 | return key.inspect.size if symbol?(key) 85 | 86 | if options[:html] 87 | single_line { inspector.awesome(key) }.size 88 | else 89 | no_colors { single_line { inspector.awesome(key) }.size } 90 | end 91 | end 92 | 93 | def max_key_width(keys) 94 | keys.map { |key, _value| key_size(key) }.max || 0 95 | end 96 | 97 | def printable_keys 98 | keys = hash.keys 99 | 100 | keys.sort! { |a, b| a.to_s <=> b.to_s } if options[:sort_keys] 101 | 102 | keys.map! do |key| 103 | single_line do 104 | [key, hash[key]] 105 | end 106 | end 107 | end 108 | 109 | def string?(key) 110 | key[0] == '"' && key[-1] == '"' 111 | end 112 | 113 | def symbol?(key) 114 | key.is_a?(Symbol) 115 | end 116 | 117 | def json_format? 118 | options[:hash_format] == :json 119 | end 120 | 121 | def json_syntax(key, value, width) 122 | unless defined?(JSON) 123 | warn 'JSON is not defined. Defaulting hash format to symbol' 124 | return ruby19_syntax(key, value, width) 125 | end 126 | 127 | formatted_key = json_awesome(key, is_key: true) 128 | formatted_value = json_awesome(value) 129 | 130 | "#{align(formatted_key, width)}#{colorize(': ', :hash)}#{formatted_value}" 131 | end 132 | 133 | def ruby19_syntax(key, value, width) 134 | return pre_ruby19_syntax(key, value, width) unless symbol?(key) 135 | 136 | # Move the colon to the right side of the symbol 137 | key_string = key.inspect.include?('"') ? key.inspect.sub(':', '') : key.to_s 138 | awesome_key = format_key(key).sub(/#{Regexp.escape(key.inspect)}/, "#{key_string}:") 139 | 140 | "#{align(awesome_key, width)} #{inspector.awesome(value)}" 141 | end 142 | 143 | def pre_ruby19_syntax(key, value, width) 144 | awesome_key = single_line { format_key(key) } 145 | 146 | "#{align(awesome_key, width)}#{colorize(' => ', :hash)}#{inspector.awesome(value)}" 147 | end 148 | 149 | def format_key(key) 150 | if options[:colors] == :values_only 151 | no_colors { inspector.awesome(key) } 152 | else 153 | inspector.awesome(key) 154 | end 155 | end 156 | 157 | def no_colors 158 | old_colors = options[:colors] 159 | options[:colors] = :none 160 | yield 161 | ensure 162 | options[:colors] = old_colors 163 | end 164 | 165 | def single_line 166 | multiline = options[:multiline] 167 | options[:multiline] = false 168 | yield 169 | ensure 170 | options[:multiline] = multiline 171 | end 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /spec/support/active_record_data/4_2_diana_legacy.txt: -------------------------------------------------------------------------------- 1 | # false, 9 | "created_at" => 1992-10-10 12:30:00 UTC, 10 | "name" => "Diana", 11 | "rank" => 1 12 | }, 13 | @readonly = false, 14 | @transaction_state = nil, 15 | @txn = nil, 16 | attr_accessor :attributes = # #, 28 | attr_reader :value = false, 29 | attr_reader :value_before_type_cast = false 30 | >, 31 | "created_at" => #, 38 | attr_reader :value = 1992-10-10 12:30:00 UTC, 39 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 40 | >, 41 | "name" => #, 48 | attr_reader :value = "Diana", 49 | attr_reader :value_before_type_cast = "Diana" 50 | >, 51 | "rank" => #, 59 | attr_reader :value = 1, 60 | attr_reader :value_before_type_cast = 1 61 | > 62 | }, 63 | attr_reader :types = { 64 | "admin" => #, 69 | "created_at" => #, 74 | "id" => #, 80 | "name" => #, 85 | "rank" => # 91 | }, 92 | attr_reader :values = { 93 | "admin" => nil, 94 | "created_at" => nil, 95 | "id" => nil, 96 | "name" => nil, 97 | "rank" => nil 98 | } 99 | > 100 | >, 101 | attr_accessor :destroyed_by_association = nil, 102 | attr_reader :association_cache = {}, 103 | attr_reader :changed_attributes = { 104 | "admin" => nil, 105 | "created_at" => nil, 106 | "name" => nil, 107 | "rank" => nil 108 | } 109 | > 110 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/mongo_mapper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module MongoMapper 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_mongo_mapper, :cast 12 | base.send :alias_method, :cast, :cast_with_mongo_mapper 13 | end 14 | 15 | # Add MongoMapper class names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_mongo_mapper(object, type) 18 | apply_default_mongo_mapper_options 19 | cast = cast_without_mongo_mapper(object, type) 20 | 21 | if defined?(::MongoMapper::Document) 22 | if object.is_a?(Class) && !object.ancestors.intersect?([::MongoMapper::Document, 23 | ::MongoMapper::EmbeddedDocument]).nil? 24 | cast = :mongo_mapper_class 25 | elsif object.is_a?(::MongoMapper::Document) || object.is_a?(::MongoMapper::EmbeddedDocument) 26 | cast = :mongo_mapper_instance 27 | elsif object.is_a?(::MongoMapper::Plugins::Associations::Base) 28 | cast = :mongo_mapper_association 29 | elsif object.is_a?(::BSON::ObjectId) 30 | cast = :mongo_mapper_bson_id 31 | end 32 | end 33 | 34 | cast 35 | end 36 | 37 | # Format MongoMapper class object. 38 | #------------------------------------------------------------------------------ 39 | def awesome_mongo_mapper_class(object) 40 | return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:keys) 41 | 42 | data = object.keys.sort.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 43 | hash[c.first] = (c.last.type || 'undefined').to_s.underscore.intern 44 | end 45 | 46 | # Add in associations 47 | if @options[:mongo_mapper][:show_associations] 48 | object.associations.each do |name, assoc| 49 | data[name.to_s] = assoc 50 | end 51 | end 52 | 53 | name = "class #{awesome_simple(object.to_s, :class)}" 54 | base = "< #{awesome_simple(object.superclass.to_s, :class)}" 55 | 56 | [name, base, awesome_hash(data)].join(' ') 57 | end 58 | 59 | # Format MongoMapper instance object. 60 | # 61 | # NOTE: by default only instance attributes (i.e. keys) are shown. To format 62 | # MongoMapper instance as regular object showing its instance variables and 63 | # accessors use :raw => true option: 64 | # 65 | # ap record, :raw => true 66 | # 67 | #------------------------------------------------------------------------------ 68 | def awesome_mongo_mapper_instance(object) 69 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 70 | return awesome_object(object) if @options[:raw] 71 | 72 | data = object.keys.keys.sort.each_with_object(::ActiveSupport::OrderedHash.new) do |name, hash| 73 | hash[name] = object[name] 74 | end 75 | 76 | # Add in associations 77 | if @options[:mongo_mapper][:show_associations] 78 | object.associations.each do |name, assoc| 79 | data[name.to_s] = if @options[:mongo_mapper][:inline_embedded] && assoc.embeddable? 80 | object.send(name) 81 | else 82 | assoc 83 | end 84 | end 85 | end 86 | 87 | label = object.to_s 88 | label = "#{colorize('embedded', :assoc)} #{label}" if object.is_a?(::MongoMapper::EmbeddedDocument) 89 | 90 | [label, awesome_hash(data)].join(' ') 91 | end 92 | 93 | # Format MongoMapper association object. 94 | #------------------------------------------------------------------------------ 95 | def awesome_mongo_mapper_association(object) 96 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 97 | return awesome_object(object) if @options[:raw] 98 | 99 | association = object.class.name.split('::').last.titleize.downcase.sub(/ association$/, '') 100 | association = "embeds #{association}" if object.embeddable? 101 | class_name = object.class_name 102 | 103 | "#{colorize(association, :assoc)} #{colorize(class_name, :class)}" 104 | end 105 | 106 | # Format BSON::ObjectId 107 | #------------------------------------------------------------------------------ 108 | def awesome_mongo_mapper_bson_id(object) 109 | object.inspect 110 | end 111 | 112 | private 113 | 114 | def apply_default_mongo_mapper_options 115 | @options[:color][:assoc] ||= :greenish 116 | @options[:mongo_mapper] ||= { 117 | show_associations: false, # Display association data for MongoMapper documents and classes. 118 | inline_embedded: false # Display embedded associations inline with MongoMapper documents. 119 | } 120 | end 121 | end 122 | end 123 | 124 | AmazingPrint::Formatter.include AmazingPrint::MongoMapper 125 | -------------------------------------------------------------------------------- /spec/support/active_record_data/5_1_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 21 | attr_reader :value_before_type_cast = nil 22 | >, 23 | attr_reader :name = "admin", 24 | attr_reader :type = #, 29 | attr_reader :value_before_type_cast = false 30 | >, 31 | "created_at" => #, 40 | attr_reader :value_before_type_cast = nil 41 | >, 42 | attr_reader :name = "created_at", 43 | attr_reader :type = #, 48 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 49 | >, 50 | "id" => #, 59 | attr_reader :value_before_type_cast = nil 60 | >, 61 | "name" => #, 70 | attr_reader :value_before_type_cast = nil 71 | >, 72 | attr_reader :name = "name", 73 | attr_reader :type = #, 78 | attr_reader :value_before_type_cast = "Diana" 79 | >, 80 | "rank" => #, 90 | attr_reader :value_before_type_cast = nil 91 | >, 92 | attr_reader :name = "rank", 93 | attr_reader :type = #, 99 | attr_reader :value_before_type_cast = 1 100 | > 101 | } 102 | >, 103 | attr_accessor :destroyed_by_association = nil 104 | > 105 | -------------------------------------------------------------------------------- /lib/amazing_print/formatters/base_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../colorize' 4 | 5 | module AmazingPrint 6 | module Formatters 7 | class BaseFormatter 8 | include Colorize 9 | 10 | DEFAULT_LIMIT_SIZE = 7 11 | 12 | # To support limited output, for example: 13 | # 14 | # ap ('a'..'z').to_a, :limit => 3 15 | # [ 16 | # [ 0] "a", 17 | # [ 1] .. [24], 18 | # [25] "z" 19 | # ] 20 | # 21 | # ap (1..100).to_a, :limit => true # Default limit is 7. 22 | # [ 23 | # [ 0] 1, 24 | # [ 1] 2, 25 | # [ 2] 3, 26 | # [ 3] .. [96], 27 | # [97] 98, 28 | # [98] 99, 29 | # [99] 100 30 | # ] 31 | #------------------------------------------------------------------------------ 32 | def should_be_limited? 33 | options[:limit] || (options[:limit].is_a?(Integer) && options[:limit].positive?) 34 | end 35 | 36 | def get_limit_size 37 | case options[:limit] 38 | when true 39 | DEFAULT_LIMIT_SIZE 40 | else 41 | options[:limit] 42 | end 43 | end 44 | 45 | def limited(data, width, is_hash: false) 46 | limit = get_limit_size 47 | if data.length <= limit 48 | data 49 | else 50 | # Calculate how many elements to be displayed above and below the separator. 51 | head = limit / 2 52 | tail = head - ((limit - 1) % 2) 53 | 54 | # Add the proper elements to the temp array and format the separator. 55 | temp = data[0, head] + [nil] + data[-tail, tail] 56 | 57 | temp[head] = if is_hash 58 | "#{indent}#{data[head].strip} .. #{data[data.length - tail - 1].strip}" 59 | else 60 | "#{indent}[#{head.to_s.rjust(width)}] .. [#{data.length - tail - 1}]" 61 | end 62 | 63 | temp 64 | end 65 | end 66 | 67 | def method_tuple(method) 68 | # See http://readruby.chengguangnan.com/methods#method-objects-parameters 69 | # (mirror: http://archive.is/XguCA#selection-3381.1-3381.11) 70 | args = method.parameters.inject([]) do |arr, (type, name)| 71 | name ||= (type == :block ? 'block' : "arg#{arr.size + 1}") 72 | arr << case type 73 | when :req then name.to_s 74 | when :keyreq then "#{name}:" 75 | when :key then "*#{name}:" 76 | when :opt, :rest then "*#{name}" 77 | when :block then "&#{name}" 78 | else '?' 79 | end 80 | end 81 | 82 | # method.to_s formats to handle: 83 | # 84 | # # 85 | # # 86 | # #)#_username> 87 | # # 88 | # # 89 | # #(ActiveRecord::Querying)#first> 90 | # # 91 | # # 92 | # 93 | if method.to_s =~ %r{(Unbound)*Method: ((#<)?[^/#]*)[#.]} 94 | unbound = Regexp.last_match(1) && '(unbound)' 95 | klass = Regexp.last_match(2) 96 | if klass && klass =~ /(\(\w+:\s.*?\))/ # Is this ActiveRecord-style class? 97 | klass.sub!(Regexp.last_match(1), '') # Yes, strip the fields leaving class name only. 98 | end 99 | 100 | owner = "#{klass}#{unbound}".gsub('(', ' (') 101 | end 102 | 103 | [method.name.to_s, "(#{args.join(', ')})", owner.to_s] 104 | end 105 | 106 | # 107 | # Indentation related methods 108 | #----------------------------------------- 109 | def indentation 110 | inspector.current_indentation 111 | end 112 | 113 | def indented(&) 114 | inspector.increase_indentation(&) 115 | end 116 | 117 | # precompute common indentations 118 | INDENT_CACHE = (0..100).map { |i| ' ' * i }.map(&:freeze).freeze 119 | 120 | def indent(n = indentation) 121 | INDENT_CACHE[n] || (' ' * n) 122 | end 123 | 124 | def outdent 125 | i = indentation - options[:indent].abs 126 | 127 | INDENT_CACHE[i] || (' ' * i) 128 | end 129 | 130 | def align(value, width) 131 | if options[:multiline] 132 | indent_option = options[:indent] 133 | effective_width = width + value.size - colorless_size(value) 134 | if indent_option.positive? 135 | value.rjust(effective_width) 136 | elsif indent_option.zero? 137 | "#{indent}#{value.ljust(effective_width)}" 138 | else 139 | "#{indent(indentation + indent_option)}#{value.ljust(effective_width)}" 140 | end 141 | else 142 | value 143 | end 144 | end 145 | 146 | def colorless(string) 147 | string.gsub(/\e\[[\d;]+m/, '') 148 | end 149 | 150 | def colorless_size(string) 151 | colorless(string).size 152 | end 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /spec/support/active_record_data/5_0_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 22 | attr_reader :value_before_type_cast = nil 23 | >, 24 | attr_reader :name = "admin", 25 | attr_reader :type = #, 30 | attr_reader :value_before_type_cast = false 31 | >, 32 | "created_at" => #, 41 | attr_reader :value_before_type_cast = nil 42 | >, 43 | attr_reader :name = "created_at", 44 | attr_reader :type = #, 49 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 50 | >, 51 | "id" => #, 60 | attr_reader :value_before_type_cast = nil 61 | >, 62 | "name" => #, 71 | attr_reader :value_before_type_cast = nil 72 | >, 73 | attr_reader :name = "name", 74 | attr_reader :type = #, 79 | attr_reader :value_before_type_cast = "Diana" 80 | >, 81 | "rank" => #, 91 | attr_reader :value_before_type_cast = nil 92 | >, 93 | attr_reader :name = "rank", 94 | attr_reader :type = #, 100 | attr_reader :value_before_type_cast = 1 101 | > 102 | } 103 | >, 104 | attr_accessor :destroyed_by_association = nil 105 | > 106 | -------------------------------------------------------------------------------- /spec/support/active_record_data/6_0_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 21 | attr_reader :value_before_type_cast = nil 22 | >, 23 | attr_reader :name = "admin", 24 | attr_reader :type = #, 29 | attr_reader :value_before_type_cast = false 30 | >, 31 | "created_at" => #, 40 | attr_reader :value_before_type_cast = nil 41 | >, 42 | attr_reader :name = "created_at", 43 | attr_reader :type = #, 48 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 49 | >, 50 | "id" => #, 59 | attr_reader :value_before_type_cast = nil 60 | >, 61 | "name" => #, 70 | attr_reader :value_before_type_cast = nil 71 | >, 72 | attr_reader :name = "name", 73 | attr_reader :type = #, 78 | attr_reader :value_before_type_cast = "Diana" 79 | >, 80 | "rank" => #, 90 | attr_reader :value_before_type_cast = nil 91 | >, 92 | attr_reader :name = "rank", 93 | attr_reader :type = #, 99 | attr_reader :value_before_type_cast = 1 100 | > 101 | } 102 | >, 103 | attr_accessor :destroyed_by_association = nil 104 | > 105 | -------------------------------------------------------------------------------- /spec/support/active_record_data/5_2_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 21 | attr_reader :value_before_type_cast = nil 22 | >, 23 | attr_reader :name = "admin", 24 | attr_reader :type = #, 29 | attr_reader :value_before_type_cast = false 30 | >, 31 | "created_at" => #, 40 | attr_reader :value_before_type_cast = nil 41 | >, 42 | attr_reader :name = "created_at", 43 | attr_reader :type = #, 48 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 49 | >, 50 | "id" => #, 59 | attr_reader :value_before_type_cast = nil 60 | >, 61 | "name" => #, 70 | attr_reader :value_before_type_cast = nil 71 | >, 72 | attr_reader :name = "name", 73 | attr_reader :type = #, 78 | attr_reader :value_before_type_cast = "Diana" 79 | >, 80 | "rank" => #, 90 | attr_reader :value_before_type_cast = nil 91 | >, 92 | attr_reader :name = "rank", 93 | attr_reader :type = #, 99 | attr_reader :value_before_type_cast = 1 100 | > 101 | } 102 | >, 103 | attr_accessor :destroyed_by_association = nil 104 | > 105 | -------------------------------------------------------------------------------- /lib/amazing_print/ext/active_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (c) 2010-2016 Michael Dvorkin and contributors 4 | # 5 | # AmazingPrint is freely distributable under the terms of MIT license. 6 | # See LICENSE file or http://www.opensource.org/licenses/mit-license.php 7 | #------------------------------------------------------------------------------ 8 | module AmazingPrint 9 | module ActiveRecord 10 | def self.included(base) 11 | base.send :alias_method, :cast_without_active_record, :cast 12 | base.send :alias_method, :cast, :cast_with_active_record 13 | end 14 | 15 | # Add ActiveRecord class names to the dispatcher pipeline. 16 | #------------------------------------------------------------------------------ 17 | def cast_with_active_record(object, type) 18 | cast = cast_without_active_record(object, type) 19 | return cast unless defined?(::ActiveRecord::Base) 20 | 21 | if object.is_a?(::ActiveRecord::Base) 22 | cast = :active_record_instance 23 | elsif object.is_a?(Class) && object.include?(::ActiveModel::Attributes) 24 | cast = :active_model_class 25 | elsif object.is_a?(::ActiveModel::Attributes) 26 | cast = :active_model_instance 27 | elsif object.is_a?(::ActiveModel::Errors) 28 | cast = :active_model_error 29 | elsif object.is_a?(Class) && object <= ::ActiveRecord::Base 30 | cast = :active_record_class 31 | elsif type == :activerecord_relation || object.is_a?(::ActiveRecord::Relation) 32 | cast = :array 33 | end 34 | cast 35 | end 36 | 37 | private 38 | 39 | # Format ActiveRecord instance object. 40 | # 41 | # NOTE: by default only instance attributes (i.e. columns) are shown. To format 42 | # ActiveRecord instance as regular object showing its instance variables and 43 | # accessors use :raw => true option: 44 | # 45 | # ap record, :raw => true 46 | # 47 | #------------------------------------------------------------------------------ 48 | def awesome_active_record_instance(object) 49 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 50 | return awesome_object(object) if @options[:raw] 51 | 52 | data = if object.class.column_names == object.attributes.keys 53 | object.class.column_names.each_with_object(::ActiveSupport::OrderedHash.new) do |name, hash| 54 | if object.has_attribute?(name) || object.new_record? 55 | value = object.respond_to?(name) ? object.send(name) : object.read_attribute(name) 56 | hash[name.to_sym] = value 57 | end 58 | end 59 | else 60 | object.attributes 61 | end 62 | [awesome_simple(object.to_s, :active_record_instance), awesome_hash(data)].join(' ') 63 | end 64 | 65 | # Format ActiveRecord class object. 66 | #------------------------------------------------------------------------------ 67 | def awesome_active_record_class(object) 68 | return object.inspect if !defined?(::ActiveSupport::OrderedHash) || !object.respond_to?(:columns) || object.to_s == 'ActiveRecord::Base' 69 | return awesome_class(object) if object.respond_to?(:abstract_class?) && object.abstract_class? 70 | 71 | data = object.columns.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 72 | hash[c.name.to_sym] = c.type 73 | end 74 | 75 | [awesome_simple("class #{object} < #{object.superclass}", :class), awesome_hash(data)].join(' ') 76 | end 77 | 78 | # Format ActiveModel instance object. 79 | #------------------------------------------------------------------------------ 80 | def awesome_active_model_instance(object) 81 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 82 | return awesome_object(object) if @options[:raw] 83 | 84 | data = object.attributes.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 85 | hash[c.first.to_sym] = c.last 86 | end 87 | 88 | [awesome_simple(object.to_s, :active_model_instance), awesome_hash(data)].join(' ') 89 | end 90 | 91 | # Format ActiveModel class object. 92 | #------------------------------------------------------------------------------ 93 | def awesome_active_model_class(object) 94 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 95 | return awesome_class(object) if @options[:raw] 96 | 97 | data = object.attribute_types.each_with_object(::ActiveSupport::OrderedHash.new) do |c, hash| 98 | hash[c.first.to_sym] = c.last.type 99 | end 100 | 101 | [awesome_simple("class #{object} < #{object.superclass}", :class), awesome_hash(data)].join(' ') 102 | end 103 | 104 | # Format ActiveModel error object. 105 | #------------------------------------------------------------------------------ 106 | def awesome_active_model_error(object) 107 | return object.inspect unless defined?(::ActiveSupport::OrderedHash) 108 | return awesome_object(object) if @options[:raw] 109 | 110 | data = object.instance_variable_get('@base') 111 | .attributes 112 | .merge(details: object.details.to_h, 113 | messages: object.messages.to_h.transform_values(&:to_a)) 114 | 115 | [awesome_simple(object.to_s, :active_model_error), awesome_hash(data)].join(' ') 116 | end 117 | end 118 | end 119 | 120 | AmazingPrint::Formatter.include AmazingPrint::ActiveRecord 121 | -------------------------------------------------------------------------------- /spec/support/active_record_data/6_1_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 22 | attr_reader :value_before_type_cast = nil 23 | >, 24 | attr_reader :name = "admin", 25 | attr_reader :type = #, 30 | attr_reader :value_before_type_cast = false 31 | >, 32 | "created_at" => #, 41 | attr_reader :value_before_type_cast = nil 42 | >, 43 | attr_reader :name = "created_at", 44 | attr_reader :type = #, 49 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 50 | >, 51 | "id" => #, 60 | attr_reader :value_before_type_cast = nil 61 | >, 62 | "name" => #, 73 | attr_reader :value_before_type_cast = nil 74 | >, 75 | attr_reader :name = "name", 76 | attr_reader :type = #, 83 | attr_reader :value_before_type_cast = "Diana" 84 | >, 85 | "rank" => #, 95 | attr_reader :value_before_type_cast = nil 96 | >, 97 | attr_reader :name = "rank", 98 | attr_reader :type = #, 104 | attr_reader :value_before_type_cast = 1 105 | > 106 | } 107 | >, 108 | attr_accessor :destroyed_by_association = nil 109 | > 110 | -------------------------------------------------------------------------------- /spec/support/active_record_data/7_0_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 22 | attr_reader :value_before_type_cast = nil 23 | >, 24 | attr_reader :name = "admin", 25 | attr_reader :type = #, 30 | attr_reader :value_before_type_cast = false 31 | >, 32 | "created_at" => #, 41 | attr_reader :value_before_type_cast = nil 42 | >, 43 | attr_reader :name = "created_at", 44 | attr_reader :type = #, 49 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 50 | >, 51 | "id" => #, 60 | attr_reader :value_before_type_cast = nil 61 | >, 62 | "name" => #, 73 | attr_reader :value_before_type_cast = nil 74 | >, 75 | attr_reader :name = "name", 76 | attr_reader :type = #, 83 | attr_reader :value_before_type_cast = "Diana" 84 | >, 85 | "rank" => #, 95 | attr_reader :value_before_type_cast = nil 96 | >, 97 | attr_reader :name = "rank", 98 | attr_reader :type = #, 104 | attr_reader :value_before_type_cast = 1 105 | > 106 | } 107 | >, 108 | attr_accessor :destroyed_by_association = nil, 109 | attr_reader :strict_loading_mode = :all 110 | > 111 | -------------------------------------------------------------------------------- /spec/support/active_record_data/8_0_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 34 | attr_reader :value_before_type_cast = nil 35 | >, 36 | attr_reader :name = "admin", 37 | attr_reader :type = #, 42 | attr_reader :value_before_type_cast = false 43 | >, 44 | "created_at" => #, 54 | attr_reader :value_before_type_cast = nil 55 | >, 56 | attr_reader :name = "created_at", 57 | attr_reader :type = #, 63 | attr_reader :value_before_type_cast = "?" 64 | >, 65 | "id" => #, 74 | attr_reader :value_before_type_cast = nil 75 | >, 76 | "name" => #, 87 | attr_reader :value_before_type_cast = nil 88 | >, 89 | attr_reader :name = "name", 90 | attr_reader :type = #, 97 | attr_reader :value_before_type_cast = "Diana" 98 | >, 99 | "rank" => #, 109 | attr_reader :value_before_type_cast = nil 110 | >, 111 | attr_reader :name = "rank", 112 | attr_reader :type = #, 118 | attr_reader :value_before_type_cast = 1 119 | > 120 | } 121 | >, 122 | attr_accessor :destroyed_by_association = nil, 123 | attr_reader :strict_loading_mode = :all 124 | > -------------------------------------------------------------------------------- /spec/support/active_record_data/7_2_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 34 | attr_reader :value_before_type_cast = nil 35 | >, 36 | attr_reader :name = "admin", 37 | attr_reader :type = #, 42 | attr_reader :value_before_type_cast = false 43 | >, 44 | "created_at" => #, 54 | attr_reader :value_before_type_cast = nil 55 | >, 56 | attr_reader :name = "created_at", 57 | attr_reader :type = #, 63 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 64 | >, 65 | "id" => #, 74 | attr_reader :value_before_type_cast = nil 75 | >, 76 | "name" => #, 87 | attr_reader :value_before_type_cast = nil 88 | >, 89 | attr_reader :name = "name", 90 | attr_reader :type = #, 97 | attr_reader :value_before_type_cast = "Diana" 98 | >, 99 | "rank" => #, 109 | attr_reader :value_before_type_cast = nil 110 | >, 111 | attr_reader :name = "rank", 112 | attr_reader :type = #, 118 | attr_reader :value_before_type_cast = 1 119 | > 120 | } 121 | >, 122 | attr_accessor :destroyed_by_association = nil, 123 | attr_reader :strict_loading_mode = :all 124 | > 125 | -------------------------------------------------------------------------------- /spec/support/active_record_data/7_1_diana.txt: -------------------------------------------------------------------------------- 1 | # #, 33 | attr_reader :value_before_type_cast = nil 34 | >, 35 | attr_reader :name = "admin", 36 | attr_reader :type = #, 41 | attr_reader :value_before_type_cast = false 42 | >, 43 | "created_at" => #, 53 | attr_reader :value_before_type_cast = nil 54 | >, 55 | attr_reader :name = "created_at", 56 | attr_reader :type = #, 62 | attr_reader :value_before_type_cast = "1992-10-10 12:30:00" 63 | >, 64 | "id" => #, 73 | attr_reader :value_before_type_cast = nil 74 | >, 75 | "name" => #, 86 | attr_reader :value_before_type_cast = nil 87 | >, 88 | attr_reader :name = "name", 89 | attr_reader :type = #, 96 | attr_reader :value_before_type_cast = "Diana" 97 | >, 98 | "rank" => #, 108 | attr_reader :value_before_type_cast = nil 109 | >, 110 | attr_reader :name = "rank", 111 | attr_reader :type = #, 117 | attr_reader :value_before_type_cast = 1 118 | > 119 | } 120 | >, 121 | attr_accessor :destroyed_by_association = nil, 122 | attr_reader :errors = nil, 123 | attr_reader :strict_loading_mode = :all, 124 | attr_reader :validation_context = nil 125 | > 126 | -------------------------------------------------------------------------------- /spec/objects_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Lint/ConstantDefinitionInBlock 4 | 5 | require 'spec_helper' 6 | 7 | RSpec.describe 'Objects' do 8 | after do 9 | Object.instance_eval { remove_const :Hello } if defined?(Hello) 10 | end 11 | 12 | describe 'Formatting an object' do 13 | it 'attributes' do 14 | class Hello 15 | attr_reader :abra 16 | attr_writer :ca 17 | attr_accessor :dabra 18 | 19 | def initialize 20 | @abra = 1 21 | @ca = 2 22 | @dabra = 3 23 | end 24 | end 25 | 26 | hello = Hello.new 27 | out = hello.ai(colors: :none, raw: true) 28 | str = <<~EOS.strip 29 | # 34 | EOS 35 | expect(out).to be_similar_to(str) 36 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 37 | end 38 | 39 | it 'instance variables' do 40 | class Hello 41 | def initialize 42 | @abra = 1 43 | @ca = 2 44 | @dabra = 3 45 | end 46 | end 47 | 48 | hello = Hello.new 49 | out = hello.ai(colors: :none, raw: true) 50 | str = <<~EOS.strip 51 | # 56 | EOS 57 | expect(out).to be_similar_to(str) 58 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 59 | end 60 | 61 | it 'attributes and instance variables' do 62 | class Hello 63 | attr_reader :abra 64 | attr_writer :ca 65 | attr_accessor :dabra 66 | 67 | def initialize 68 | @abra = 1 69 | @ca = 2 70 | @dabra = 3 71 | @scooby = 3 72 | @dooby = 2 73 | @doo = 1 74 | end 75 | end 76 | 77 | hello = Hello.new 78 | out = hello.ai(colors: :none, raw: true) 79 | str = <<~EOS.strip 80 | # 88 | EOS 89 | expect(out).to be_similar_to(str) 90 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 91 | end 92 | 93 | it 'without the plain options print the colorized values' do 94 | class Hello 95 | attr_reader :abra 96 | attr_writer :ca 97 | 98 | def initialize 99 | @abra = 1 100 | @ca = 2 101 | @dabra = 3 102 | end 103 | end 104 | 105 | hello = Hello.new 106 | out = hello.ai(raw: true) 107 | str = <<~EOS.strip 108 | # 113 | EOS 114 | expect(out).to be_similar_to(str) 115 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 116 | end 117 | 118 | it 'with multine as false show inline values' do 119 | class Hello 120 | attr_reader :abra 121 | attr_writer :ca 122 | 123 | def initialize 124 | @abra = 1 125 | @ca = 2 126 | @dabra = 3 127 | end 128 | end 129 | 130 | hello = Hello.new 131 | out = hello.ai(multiline: false, colors: :none, raw: true) 132 | str = <<~EOS.strip 133 | # 134 | EOS 135 | expect(out).to be_similar_to(str) 136 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 137 | end 138 | 139 | it 'without the sort_vars option does not sort instance variables' do 140 | class Hello 141 | attr_reader :abra 142 | attr_writer :ca 143 | attr_accessor :dabra 144 | 145 | def initialize 146 | @abra = 1 147 | @ca = 2 148 | @dabra = 3 149 | @scooby = 3 150 | @dooby = 2 151 | @doo = 1 152 | end 153 | 154 | def instance_variables 155 | %i[@scooby @dooby @doo @abra @ca @dabra] 156 | end 157 | end 158 | 159 | hello = Hello.new 160 | out = hello.ai(colors: :none, raw: true, sort_vars: false) 161 | str = <<~EOS.strip 162 | # 170 | EOS 171 | expect(out).to be_similar_to(str) 172 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 173 | end 174 | 175 | it 'object_id' do 176 | class Hello 177 | def initialize 178 | @abra = 1 179 | @ca = 2 180 | @dabra = 3 181 | end 182 | end 183 | 184 | hello = Hello.new 185 | out = hello.ai(colors: :none, raw: true, object_id: false) 186 | str = <<~EOS.strip 187 | # 192 | EOS 193 | expect(out).to be_similar_to(str) 194 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 195 | end 196 | 197 | it 'class_name' do 198 | class Hello 199 | def initialize 200 | @abra = 1 201 | @ca = 2 202 | @dabra = 3 203 | end 204 | 205 | def to_s 206 | 'CustomizedHello' 207 | end 208 | end 209 | 210 | hello = Hello.new 211 | out = hello.ai(colors: :none, raw: true, class_name: :to_s) 212 | str = <<~EOS.strip 213 | # 218 | EOS 219 | expect(out).to be_similar_to(str) 220 | expect(hello.ai(colors: :none, raw: false)).to eq(hello.inspect) 221 | end 222 | end 223 | end 224 | 225 | # rubocop:enable Lint/ConstantDefinitionInBlock 226 | --------------------------------------------------------------------------------