Last active
August 24, 2023 13:37
-
-
Save jordan-brough/15d1bfc2378a36a1a2a1 to your computer and use it in GitHub Desktop.
Ruby Examine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# to always include this in IRB save this to ~/.irbrc.examine and add this to your ~/.irbrc: | |
# | |
# load File.expand_path('../.irbrc.examine', __FILE__) | |
# | |
module JordanBrough | |
module Examine | |
module InstanceMethods | |
def examine(*attrs, printer: :default) | |
JordanBrough::Examine.examine(self, *attrs, printer: printer) | |
end | |
end | |
module_function | |
# for console usage | |
# outputs columnized + justified inspection of selected attributes. | |
# see Object#pluck for valid attrs | |
# | |
# examples: | |
# | |
# User.all.random(2).examine(:name, :email) | |
# name email | |
# Jordan Brough jordan+a4c@animoto.com | |
# Jordan Brough jordan+sep30_2@animoto.com | |
# => nil | |
# | |
# User.all.random(5).examine(:name, :email, { subscription_count: -> { _1.subscriptions.count } }]) | |
# name email subscription_count | |
# Jordan Brough jordan+oct21@animoto.com 1 | |
# John Doe john@john.com 0 | |
# Tom Clifton tom@animoto.com 0 | |
# Jordan Brough jordan+a4c@animoto.com 2 | |
# Jordan Brough jordan+b2b@animoto.com 0 | |
# => nil | |
# def examine(object, *attrs, &block) | |
# require 'date' | |
# array = object.respond_to?(:to_ary) ? object.to_ary : [object] | |
# printer = block || lambda do |keys, hashes, val_to_s| | |
# max_widths = {} | |
# keys.each do |key| | |
# max_widths[key] = ([key.to_s.size] + hashes.map { |hash| val_to_s.call(hash[key]).size }).max | |
# end | |
# keys_str = keys.map { |key| key.to_s.ljust(max_widths[key]) }.join(' ') + "\n" | |
# keys_str + hashes.map do |hash| | |
# hash.map { |key, val| val_to_s.call(val).ljust(max_widths[key]) }.join(' ') | |
# end.join("\n") | |
# end | |
# val_to_s = lambda do |val| | |
# case val | |
# when Time, DateTime then val.strftime('%Y-%m-%d %H:%M:%S') | |
# when Date then val.strftime('%Y-%m-%d') | |
# else val.to_s | |
# end | |
# end | |
# keys = attrs.map { |attr| attr.is_a?(Hash) ? attr.keys.first : attr } | |
# hashes = array.map { |item| pluck(item, attrs) } # note: pluck returns an ActiveSupport::OrderedHash | |
# printer.call(keys, hashes, val_to_s) | |
# end | |
# def pluck(object, attrs) | |
# key_vals = attrs.map do |item| | |
# key = item.is_a?(Hash) ? item.keys.first : item | |
# val = item.is_a?(Hash) ? item.values.first : item | |
# val = if val.respond_to?(:call) | |
# if val.respond_to?(:arity) && val.arity.abs > 0 | |
# val.call(object) | |
# else | |
# val.call | |
# end | |
# else | |
# object.instance_eval(val.to_s) | |
# end | |
# [key, val] | |
# end | |
# Hash[key_vals] | |
# end | |
def examine(object, *attrs, printer:) | |
require 'date' | |
array = object.respond_to?(:to_ary) ? object.to_ary : [object] | |
keys = extract_keys(attrs) | |
hashes = array.map { |item| pluck(item, attrs) } | |
case printer | |
when :default | |
default_print(keys, hashes) | |
when :csv | |
csv_print(keys, hashes) | |
else | |
raise "unknown printer #{printer}" | |
end | |
end | |
def default_print(keys, hashes) | |
max_widths = calculate_max_widths(keys, hashes) | |
format_keys(keys, max_widths) + format_hashes(hashes, max_widths).join("\n") | |
end | |
def csv_print(keys, hashes) | |
require 'csv' | |
keys.to_csv + hashes.map do |hash| | |
hash.map { |key, val| value_to_string(val) }.to_csv | |
end.join | |
end | |
def calculate_max_widths(keys, hashes) | |
keys.each_with_object({}) { |key, max_widths| max_widths[key] = max_width_for_key(key, hashes) } | |
end | |
def max_width_for_key(key, hashes) | |
([key.to_s.size] + hashes.map { |hash| value_to_string(hash[key]).size }).max | |
end | |
def format_keys(keys, max_widths) | |
keys.map { |key| key.to_s.ljust(max_widths[key]) }.join(' ') + "\n" | |
end | |
def format_hashes(hashes, max_widths) | |
hashes.map { |hash| format_hash(hash, max_widths) } | |
end | |
def format_hash(hash, max_widths) | |
hash.map { |key, val| value_to_string(val).ljust(max_widths[key]) }.join(' ') | |
end | |
def value_to_string(val) | |
case val | |
when Time, DateTime then val.strftime('%Y-%m-%d %H:%M:%S') | |
when Date then val.strftime('%Y-%m-%d') | |
else val.to_s | |
end | |
end | |
def extract_keys(attrs) | |
attrs.flat_map { |attr| attr.is_a?(Hash) ? attr.keys : attr } | |
end | |
def pluck(object, attrs) | |
Hash[attrs.flat_map { |item| extract_key_values(item, object) }] | |
end | |
def extract_key_values(__item__, __object__) | |
__item__.is_a?(Hash) ? __item__.map { |key, val| [key, evaluate_value(val, __object__)] } : [[__item__, __object__.instance_eval(__item__.to_s)]] | |
end | |
def evaluate_value(__val__, __object__) | |
__val__.respond_to?(:call) ? (__val__.respond_to?(:arity) && __val__.arity.abs > 0 ? __val__.call(__object__) : __val__.call) : __object__.instance_eval(__val__.to_s) | |
end | |
end | |
end | |
Object.send(:include, JordanBrough::Examine::InstanceMethods) # using "send" for Ruby < 2.1 compatibility |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment