pry notes
June 15th, 2018 - Bonn
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
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.
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
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
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