pry notes
Ruby console / pry tricks
History
hist (history)
hist --all (-a)
hist -T (--tail) 20
hist -G (--grep) (REGEX)
hist -r (--replay) 14..16
hist -n (--no-numbers)
# You could define aliases like these ones
# "hh", "hist -a -T30"
# "hn", "hist -a -T30 -n"
# "hg", "hist -a -G"
# "hr", "hist -a -r"
#
# To support CTRL + R
# https://github.com/pry/pry/wiki/FAQ#how-can-i-get-readline-support-ctrlr-etc-for-pry-in-osx
#
# .cat #{Pry.config.history.file}
# cat ~/.pry_history
# Pry.history.to_a
# You can load the previous history to avoid the --all flag and use CTRL + R and the up key
# Pry.load_history # same as Pry.history.load
General tips
# Shell
.open #{Shop.first.image.path}
.ls #{output_file_path_for_upload}
# Name conflicts
# ls = 1
# => SyntaxError: syntax error, unexpected '='
# ;ls =1
# skip dump
(1..100).to_a;
# latest output
_
Copy & Paste
Quick Trick : Make the font smaller
![]() |
![]() |
with pbcopy, pbpaste for mac
foo = '123'
`echo #{foo} | pbcopy`
foo = {a: "test\n", b:2}
`echo #{foo} | pbcopy` # does not work
`echo #{Shellwords.escape(foo)} | pbcopy`
# better
IO.popen('pbcopy', 'w') { |f| f << foo }
copied_foo = eval(`pbpaste`)
with the pry-clipboard
gem
# multiplatform
Clipboard.copy Array.methods
foo = Clipboard.paste
copy-result
copy-history
# Aliases
"cr", "copy-result"
"cc", "copy-history -T -2 -l" # copy input und output, at least in my case
Extra Gems
They can be useful, but some of them have not been updated in a long time, may not work with your rails version or be too unstable.
- pry-clipboard Latest commit - Apr 2012
- jazz-hands Latest commit - Nov 2013, includes pry-debugger which has been replaced with pry-byebug. An alternative might be jazz-fingers. Other gems pry-stack_explorer or pry-nav should also be replaced with pry-byebug.
- pry-coolio latest commit - Dec 2014
- hirb latest commit Mar 2015
Output
array_sample = (1..100).to_a
hash_sample = (1..200).to_a.in_groups_of(2).to_h
# to play with more realistic examples, using the countries gem:
array_sample = ISO3166::Country.all_names_with_codes
hash_sample = ISO3166::Country.search('de')
print array_sample;
puts #foo.to_s + newline after each argument
p # puts with foo.inspect
pp # pretty print - indentation
j/jj # json / pretty json
# you can get a simpler kind of output even without changing the inspector
array_sample.inspect
Pry.config.pager = false
# IMPORTANT: Use Pry.xxx in your .pryrc, use _pry_.xxx in your repl session
_pry_.config.pager = false
# pager is Less by default, you can change it
# https://github.com/pry/pry/wiki/FAQ#how-do-i-use-other-pager-than-less
#
# Some Less shortcuts
# h show help
# q quit
# G end of file
# g start of file
# * mac: fn left right top down
# / search forward
# ? search backwards
# n next match (forwards/backwards)
# N previous match (forwards/backwards)
Tabular Data
poor man’s table
# First: ways of string interpolation, and what they ruby style guide says about it (https://github.com/bbatsov/ruby-style-guide)
# sprintf (alias: format) better than String#%
# sprinft saves to variable, printf prints
format('%-5d %-5d', 20, 10)
format('%<first>-5d %<second>-5d', first: 20, second: 10)
# => "20 10 " (5 chars in total for every part)
def table_example
printf("%-10s %-20s\n", 'id', 'text')
Question.limit(4).pluck(:id, :text).each do |attrs|
printf("%-10s %-20s\n", *attrs)
end
nil
end
# =>
id text
1 Iste sint a cumque c
3 Omnis animi ea archi
4 Facere ut nihil nisi
9 Eum et incidunt veri
With Hirb
def _table(*args)
print Hirb::Helpers::AutoTable.render(*args)
end
_table Question.limit(5), fields: %i(id questionnaire_id text)
# =>
+----+------------------+-------------------------------------+
| id | questionnaire_id | text |
+----+------------------+-------------------------------------+
| 1 | 6 | Iste sint a cumque consectetur. |
| 2 | 7 | Omnis animi ea architecto reicie |
| 3 | 8 | Facere ut nihil nisi eaque praes... |
| 4 | 9 | Eum et incidunt veritatis ut. |
| 5 | 10 | Non voluptatem praesentium sint ... |
+----+------------------+-------------------------------------+
_table [['foo', 'bar', 'baz'], [1,2,3], [4,5,6]]
_table [{foo: '1', bar: '2'}, {foo: '3', bar: '4'}]
_table (1..3).map {|i| i.days.from_now }, :fields=>[:to_s, :year, :month, :day]
Inspectors
list-inspectors
change-inspector # default, simple, clipped
# custom: awesome, hirb, special_cases....
* awesome_print
ap User.all
Custom Inspectors
from https://github.com/pry/pry/blob/v0.10.4/lib/pry/commands/change_inspector.rb
if inspector_map.key?(inspector)
_pry_.print = inspector_map[inspector][:value]
..
from https://github.com/pry/pry/blob/v0.10.4/lib/pry/inspector.rb
class Pry::Inspector
MAP = {
...
"simple" => {
value: Pry::SIMPLE_PRINT,
description: <<-DESCRIPTION.each_line.map(&:lstrip)
A simple inspector that uses #puts and #inspect when printing an
object. It has no pager, color, or pretty_inspect support.
DESCRIPTION
},
from https://github.com/pry/pry/blob/v0.10.4/lib/pry.rb
SIMPLE_PRINT = proc do |output, value|
begin
output.puts value.inspect
rescue RescuableException
output.puts "unknown"
end
end
We can extend them with our own, to add awesome_print or hirb for example (see Config File) or special logic:
# Like default, but if the value is a User, it highlights the email and role
Pry::Inspector::MAP['default_with_special_cases'] = {
description: 'provides extra info for some models',
value: proc do |output, value, _pry_|
case value
when User
puts "\e[34m #{ value.role } #{ value.email } \e[0m\n -------- \n"
end
Pry::DEFAULT_PRINT.call(output, value, _pry_)
end
}
Prompts
list-prompts
change-prompt # simple, nav, none
# custom: rails_app, memory, ...
Custom Prompts
Analogous to the inspectors
- https://github.com/pry/pry/blob/v0.10.4/lib/pry/commands/change_prompt.rb
- https://github.com/pry/pry/blob/v0.10.4/lib/pry/prompt.rb
- https://github.com/pry/pry/blob/v0.10.4/lib/pry.rb
the default prompt has a variable to configure the project name
Pry.config.prompt_name = File.basename(Dir.pwd)
Pry.config.prompt_name = File.basename(Dir.pwd)
Pry::Prompt::MAP['rails_app'] = {
description: 'prompt with app name and environment',
value: proc do |obj, nest_level, _pry_|
env_warning = case Rails.env
when 'production' then " \e[41mproduction\e[0m "
when 'staging' then " \e[42mstaging\e[0m "
end
"#{_pry_.config.prompt_name}#{env_warning}[pry@#{obj}]> "
end
}
# Set rails_app as default prompt
Pry.config.prompt = Pry::Prompt::MAP['rails_app'][:value]
# Other example usign pry text helpers: http://phansch.net/2017/02/12/a-better-pry-prompt-for-rails-console/
Pry::Prompt::MAP['memory'] = {
description: 'shows PID and memory',
value: proc do |obj, nest_level, _pry_|
pid, size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)
"PID:#{pid} MEM:#{size}KB(#{helper.number_to_human_size(size * 1000)}) [pry@#{obj}] > "
end
}
Colors
adapted from http://stackoverflow.com/questions/1489183/colorized-ruby-output
module PryColorize
module_function
def black(str); "\e[30m#{str}\e[0m" end
def red(str); "\e[31m#{str}\e[0m" end
def green(str); "\e[32m#{str}\e[0m" end
def brown(str); "\e[33m#{str}\e[0m" end
def blue(str); "\e[34m#{str}\e[0m" end
def magenta(str); "\e[35m#{str}\e[0m" end
def cyan(str); "\e[36m#{str}\e[0m" end
def gray(str); "\e[37m#{str}\e[0m" end
def bg_black(str); "\e[40m#{str}\e[0m" end
def bg_red(str); "\e[41m#{str}\e[0m" end
def bg_green(str); "\e[42m#{str}\e[0m" end
def bg_brown(str); "\e[43m#{str}\e[0m" end
def bg_blue(str); "\e[44m#{str}\e[0m" end
def bg_magenta(str); "\e[45m#{str}\e[0m" end
def bg_cyan(str); "\e[46m#{str}\e[0m" end
def bg_gray(str); "\e[47m#{str}\e[0m" end
def bold(str); "\e[1m#{str}\e[22m" end
def italic(str); "\e[3m#{str}\e[23m" end
def underline(str); "\e[4m#{str}\e[24m" end
def blink(str); "\e[5m#{str}\e[25m" end
def reverse_color(str); "\e[7m#{str}\e[27m" end
def list
methods = singleton_methods(false) - [:list]
methods.each { |m| puts send(m, format('%-20s', m)) }
end
end
There are also some pry helpers (http://www.rubydoc.info/github/pry/pry/Pry/Helpers/Text)
Pry::Helpers::Text.black
Pry::Helpers::Text.red
Pry::Helpers::Text.green
Pry::Helpers::Text.yellow
Pry::Helpers::Text.blue
Pry::Helpers::Text.purple
Pry::Helpers::Text.magenta
Pry::Helpers::Text.cyan
Pry::Helpers::Text.white
--
Pry::Helpers::Text.bold
Pry::Helpers::Text.indent
Cool use: giving a warning to other devs while initializing:
# config/initializers/meta_request_warning.rb
# the meta-request gem conflicts with the activerecord-postgis-adapter
# doing a Shop.first.update_attributes({}) on the console leads to a SystemStackError
if defined? MetaRequest
logger = Logger.new(STDOUT)
logger.warn "\e[31mThe meta-request gem can cause a SystemStackError: stack level too deep when saving a shop record. See https://github.com/rgeo/activerecord-postgis-adapter/issues/81 \e[0m"
end
bundle exec rails c
The meta-request gem can cause a SystemStackError: stack level
too deep when saving a shop record.
See
https://github.com/rgeo/activerecord-postgis-adapter/issues/81
Loading development environment (Rails 4.2.6)
Emoji! 🦄
Images
def see(image_path)
name = inline_base64(File.basename(image_path))
image = inline_base64(File.read(image_path))
puts "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
end
def inline_base64(path)
Base64.encode64(path).gsub("\n", "")
end
Exploring/Debugging/Editing
binding.pry
whereami # @ -n
show-source
ls
cd # cd..
show-source # @ whereami
show-source Foo -a # Found 2 candidates for `Foo` definition: ...
find-method api_user?
find-method location Uberall::Client
find-method -c business_id Uberall::Client # will grep the source code
Foo._tab_autocomplete
edit -p Foo # $EDITOR or Pry.config.editor # reload
Exiting / Escaping the loop
binding.pry if x
binding.pry if $x
binding.pry unless @once; @once = true
exit
!!! (exit-program)
disable-pry
* enable-pry
docs
# gem 'pry-doc'
? Array#reduce (alias show-doc)
? Shop.belongs_to
rails info
# gem 'pry-rails'
show-middleware
show-model
show-models
show-routes
byebug
# gem 'pry-byebug'
# break, step, next, finish, continue
# backtrace, up, down, frame
class A
def foo
B.new.bar
end
end
class B
def bar
binding.pry
C.new.baz
end
end
class C
def baz
1+1
end
end
A.new.foo
more
# Calling binding.pry within a method of a class inheriting from SimpleDelegator
::Kernel.binding.pry
# looking into a gem
bundle show devise
bundle open devise
gem pristine devise # undo any edits (try edit -p also to avoid this problem)
# http://guides.rubygems.org/command-reference/#gem-pristine
# using regular ruby
method(:foo).source_location
Movie.instance_method(:average_stars).source_location
Movie.instance_method(:average_stars).super_method.source_location # find where super is pointing to
caller # caller(1,4)
notifications / sounds
# gem 'terminal-notifier'
def ping_when_finished(&block)
block.call if block_given?
`say fertig`
`terminal-notifier -message "finished" -sound default -timeout 5`
end
waiting for iterations to finish
# gem 'tqdm'
module Enumerable
def measure(&block)
Benchmark.measure do
ActiveRecord::Base.logger.silence do
self.tqdm.each(&block)
end
end
end
end
Foreman + pry / Remote Debugging
gem 'pry-remote' # latest comit August 2014
binding.remote_pry
#=> [pry-remote] Waiting for client on drb://localhost:9876 # at least in theory
# from other terminal
bundle exec pry-remote
binding.remote_pry('127.0.0.1', 9888)
bundle exec pry-remote -s 127.0.0.1 -p 9888 -w # -w = wait
# Address already in use - bind(2) for "127.0.0.1" port 9876
# restart or kill -9 $(lsof -ti tcp:9876)
Using byebyebug
# config/initializers/byebug.rb
if Rails.env.development?
require 'byebug/core'
Byebug.wait_connection = true
Byebug.start_server 'localhost', ENV.fetch("BYEBUG_SERVER_PORT", 8989).to_i
end
# somewhere
byebug
bundle exec byebug -R localhost:8989
- With better errors you could force an exception to get a little insight.
- With web-console you get a console after rendering the view
Config - .pryrc
- Isolate from project / other devs
load '.pryrc_custom' if File.exist?('.pryrc_custom')
pry_manual_gem_require
- Cheatsheet
# Method to try to load gems outside of the gemfile context
# pry_manual_gem_require('my_gem')
# pry_manual_gem_require('my_gem', 'other_dependency')
#
# Check the corresponding .gemspec to add more explicit dependencies if needed
def pry_manual_gem_require(*gem_names)
gem_names.flatten.each do |gem_name|
path = Bundler.with_clean_env { %x[gem which #{gem_name} 2>/dev/null] }.strip
next if path.blank?
$LOAD_PATH.unshift(File.dirname(path))
end
gem_names.flatten.each { |gem_name| require gem_name }
yield if block_given?
rescue LoadError
end
pry_manual_gem_require(%w(pry-clipboard clipboard))
### COLORS #####################################################################
# See all colors with PryColorize.list
module PryColorize
module_function
def black(str); "\e[30m#{str}\e[0m" end
def red(str); "\e[31m#{str}\e[0m" end
def green(str); "\e[32m#{str}\e[0m" end
def brown(str); "\e[33m#{str}\e[0m" end
def blue(str); "\e[34m#{str}\e[0m" end
def magenta(str); "\e[35m#{str}\e[0m" end
def cyan(str); "\e[36m#{str}\e[0m" end
def gray(str); "\e[37m#{str}\e[0m" end
def bg_black(str); "\e[40m#{str}\e[0m" end
def bg_red(str); "\e[41m#{str}\e[0m" end
def bg_green(str); "\e[42m#{str}\e[0m" end
def bg_brown(str); "\e[43m#{str}\e[0m" end
def bg_blue(str); "\e[44m#{str}\e[0m" end
def bg_magenta(str); "\e[45m#{str}\e[0m" end
def bg_cyan(str); "\e[46m#{str}\e[0m" end
def bg_gray(str); "\e[47m#{str}\e[0m" end
def bold(str); "\e[1m#{str}\e[22m" end
def italic(str); "\e[3m#{str}\e[23m" end
def underline(str); "\e[4m#{str}\e[24m" end
def blink(str); "\e[5m#{str}\e[25m" end
def reverse_color(str); "\e[7m#{str}\e[27m" end
def list
methods = singleton_methods(false) - [:list]
methods.each { |m| puts send(m, format('%-20s', m)) }
end
end
### INSPECTORS #################################################################
pry_manual_gem_require('awesome_print') do
Pry::Inspector::MAP['awesome'] = {
value: proc { |output, value| output.puts value.ai },
description: 'Awesome Print'
}
end
pry_manual_gem_require('hirb') do
Hirb.enable
Pry::Inspector::MAP['hirb'] = {
value: proc do |*args|
Hirb::View.view_or_page_output(args[1]) || Pry::DEFAULT_PRINT.call(*args)
end,
description: 'Hirb (tabular data)'
}
# _table [['foo', 'bar', 'baz'], [1,2,3], [4,5,6]]
# _table [{foo: '1', bar: '2'}, {foo: '3', bar: '4'}]
# _table (1..3).map {|i| i.days.from_now }, :fields=>[:to_s, :year, :month, :day]
def _table(*args)
print Hirb::Helpers::AutoTable.render(*args)
end
end
# Example custom Inspector
# Pry::Inspector::MAP['default_with_special_cases'] = {
# description: 'provides extra info for some models',
# value: proc do |output, value, pry_|
# case value
# when User
# puts "\e[34m #{value.role.name} #{value.email} \e[0m"
# end
# Pry::DEFAULT_PRINT.call(output, value, pry_)
# end
# }
### PROMPTS ####################################################################
Pry.config.prompt_name = File.basename(Dir.pwd)
Pry::Prompt::MAP['rails_app'] = {
description: 'prompt with app name and environment',
value: proc do |obj, _, pry_|
env_warning =
case Rails.env
when 'production' then " #{PryColorize.bg_red('production')} "
when 'staging' then " #{PryColorize.bg_green('staging')} "
end
"#{pry_.config.prompt_name}#{env_warning}[pry@#{obj}]> "
end
}
Pry::Prompt::MAP['memory'] = {
description: 'shows PID and memory',
value: proc do |obj, _, _|
pid, size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)
"PID:#{pid} MEM:#{size}KB(#{helper.number_to_human_size(size * 1000)}) [pry@#{obj}] > "
end
}
# Set rails_app as default prompt
Pry.config.prompt = Pry::Prompt::MAP['rails_app'][:value]
### COMMANDS AND ALIASES #######################################################
Pry::Commands.block_command 'hl', 'Pry.history.load' do
Pry.history.load
end
Pry.commands.alias_command 'hh', 'hist -a -T20'
Pry.commands.alias_command 'hn', 'hist -a -T20 -n'
Pry.commands.alias_command 'hg', 'hist -a -G'
Pry.commands.alias_command 'hr', 'hist -a -r'
Pry::Commands.block_command('cheat', 'Display Cheatsheet') do
puts 'Shortcuts:'
puts '@ : whereami'
puts '? : show-doc'
puts 'hl : Pry.history.load load whole history'
puts 'hh : hist -a -T20 Last 20 commands'
puts 'hn : hist -a -T20 -n Last 20 command no numbers'
puts 'hg : hist -a -G Commands matching expression'
puts 'hr : hist -a -r hist -r <command number> to run a command'
puts 'help alias See all the aliases available'
end
### PROJECT HISTORY FILE #######################################################
Pry.config.history.file = Rails.root.join('.pry_history')
### CUSTOM #####################################################################
custom_config_path = Rails.root.join('.pryrc_custom')
load custom_config_path if File.exist?(custom_config_path)
Other
-
byebug Rails default debugger
-
binding.irb
in ruby 2.4
Edits:
- In my case I need the to require
rb-readline
to load the history:
# .pryrc_custom
pry_manual_gem_require('rb-readline')
- In order to highlight the environment in production/staging we need to load pry, you might not want to do that and
use
.irbrc
instead:
# .irbrc
if defined?(Rails) && Rails.env
reset = "\e[0m"
color = case Rails.env
when 'production' then "\e[41m" # red background
when 'staging' then "\e[42m" # green background
end
app_name_env = Rails.application.class.parent_name.underscore
app_name_env << "#{reset}:#{color}#{Rails.env}#{reset}"
IRB.conf[:PROMPT][:RAILS_ENV] = {
PROMPT_I: "%N(#{app_name_env}):%03n:%i> ",
PROMPT_N: "%N(#{app_name_env}):%03n:%i> ",
PROMPT_S: "%N(#{app_name_env}):%03n:%i%l ",
PROMPT_C: "%N(#{app_name_env}):%03n:%i* ",
RETURN: "=> %s\n"
}
IRB.conf[:PROMPT_MODE] = :RAILS_ENV
end