Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Source-Diving for Fun and Profit

Kevin Kuchta
November 18, 2019

Source-Diving for Fun and Profit

Ever spent hours pouring over a gem's documentation trying to figure out how to make it work? Dug through dozens of blog posts trying to understand why a library's not working? Well what if I promised you an end to all that?!

Well, ok, I'd be lying. But maybe I can save you some hair-pulling some of the time! Let me introduce you to the joys of Reading the Code. Maybe it seems obvious to you, but one of the biggest leaps I made as a ruby dev was really getting comfortable jumping into a gem's source as a debugging technique.

In an effort to get you over that hump earlier than I did, let's talk tips and tricks for getting in and out of a library's codebase as efficiently as possible. It won't solve every problem, but sometimes 5 minutes on GitHub will save you hours on StackOverflow.

Kevin Kuchta

November 18, 2019
Tweet

More Decks by Kevin Kuchta

Other Decks in Programming

Transcript

  1. @kkuchta Find the biggest $ find lib -type f |

    xargs wc -l | sort -r | head Find in ./lib Sort by line count Get the line count for each Just show the top 10
  2. @kkuchta Take me to your leader • Entry points •

    Files with lots of content • Biggest files
  3. @kkuchta Ag is cool $ ag 'something' $ ag -i

    'something' $ ag 'some.*thing'
  4. @kkuchta Ag is cool $ ag 'something' $ ag -i

    'something' $ ag 'some.*thing' $ ag --ruby 'something'
  5. @kkuchta • def stream • PaginatedListing • def _stream •

    dead end • Subreddit • def listing • define_method(:comments) Iterative Searching session.subreddit('all').comments.stream
  6. @kkuchta Iterative Searching Gotcha: metaprogramming def add_one(x) x + 1

    end define_method("add_one") do |x| x + 1 end method_name = "add" + "_" + "one" define_method(method_name) do |x| x + 1 end
  7. @kkuchta Searching: Referral • Search by full name, exact name,

    partial name • Filter by type (constant, instance_method, local var, etc) • Regexes everywhere • Sort by anything • Customize output • Filter by scope • github.com/testdouble/referral
  8. @kkuchta 3. Trace It puts "x = #{x}, y =

    #{y}" puts "some_gem = #{some_gem.inspect}" result = some_gem.do_thing(x, y, true) puts "result = #{result}"
  9. @kkuchta 3. Trace It /Users/kevin/.rvm/gems/ruby-2.0.0-p648 actionmailer-4.0.4 commander-4.1.3 jquery-rails-3.1.1 actionpack-4.0.4 diff-lcs-1.2.5

    json-1.8.1 activemodel-4.0.4 directory_watcher-1.4.1 kgio-2.9.2 activerecord-4.0.4 erubis-2.7.0 kramdown-0.14.2 activerecord-deprecated_finders-1.0.3 execjs-2.2.1 liquid-2.5.0 activesupport-4.0.4 fast-stemmer-1.0.2 listen-1.0.3 arel-4.0.2 ffi-1.8.1 lumberjack-1.0.3 builder-3.1.4 formatador-0.2.4 mail-2.5.4 byebug-9.0.6 guard-1.8.0 maruku-0.6.1 classifier-1.3.3 guard-haml-0.5 method_source-0.8.1 coderay-1.0.9 haml-3.1.4 method_source-0.9.2 coderay-1.1.2 highline-1.6.18 mime-types-1.25.1 coffee-rails-4.0.1 hike-1.2.3 minitest-4.7.5 coffee-script-2.3.0 i18n-0.6.11 multi_json-1.10.1 coffee-script-source-1.7.1 jbuilder-1.5.3 pg-0.17.1 $ echo $GEM_HOME $ ls $GEM_HOME/gems/
  10. @kkuchta 3. Trace It /Users/kevin/.rvm/gems/ruby-2.6.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/ file.rb:85:in `gets': wrong number of

    arguments (given 2, expected 0..1) stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a
  11. @kkuchta 3. Trace It 2. /Users/kevin/.rvm/gems/ruby-2.6.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/ file.rb:85:in `gets': wrong number

    of arguments (given 2, expected 0..1) stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a 1. from /Users/kevin/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/csv/parser.rb:392:in `resolve_row_separator'
  12. @kkuchta 3. Trace It stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a

    from /Users/kevin/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/csv/parser.rb:392:in `resolve_row_separator' 392
  13. @kkuchta 3. Trace It stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a

    from /Users/kevin/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/csv/parser.rb:392:in `resolve_row_separator'
  14. @kkuchta 3. Trace It stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a

    /Users/kevin/.rvm/gems/ruby-2.6.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/ file.rb:85:in `gets': wrong number of arguments (given 2, expected 0..1) from /Users/kevin/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/csv/parser.rb:392:in `resolve_row_separator'
  15. @kkuchta 3. Trace It stream = sftp.file.open(filepath) csv = CSV.new(stream).to_a

    /Users/kevin/.rvm/gems/ruby-2.6.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/ file.rb:85:in `gets': wrong number of arguments (given 2, expected 0..1)
  16. @kkuchta 3. Trace It • Print debugging • Quick, easy

    • Rerun to make changes • Debugger debugging • Steeper learning curve • Exploratory tracing
  17. @kkuchta 3. Trace It def first(x) x = x +

    3 if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end first(7)
  18. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It
  19. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It
  20. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It
  21. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)>
  22. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step
  23. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step
  24. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step
  25. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)>
  26. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x
  27. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)>
  28. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)>
  29. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)>
  30. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)> next pry(main)>
  31. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)> next pry(main)> step pry(main)>
  32. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)> next pry(main)> step pry(main)> y => 10 pry(main)>
  33. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)> next pry(main)> step pry(main)> y => 10 pry(main)> finish
  34. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It pry(main)> step pry(main)> x => 7 pry(main)> next pry(main)> x > 5 => true pry(main)> next pry(main)> step pry(main)> y => 10 pry(main)> finish
  35. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It
  36. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line
  37. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function
  38. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function
  39. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function
  40. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function continue => stop debugging + move on
  41. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function whereami => print the code around you continue => stop debugging + move on
  42. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function whereami => print the code around you backtrace => print the current stack trace continue => stop debugging + move on
  43. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function whereami => print the code around you backtrace => print the current stack trace !!! => exit the whole program continue => stop debugging + move on
  44. @kkuchta require 'pry-byebug' def first(x) x = x + 3

    if x > 5 x = second(x) end x end def second(y) y = y + 4 while y > 1 y = y / 2.0 end y end binding.pry first(7) 3. Trace It next => Go to the next line step => step into this function finish => finish this function whereami => print the code around you backtrace => print the current stack trace !!! => exit the whole program continue => stop debugging + move on help => exit the whole program
  45. @kkuchta Debuggers are great • Debugger: pry-byebug • Rebugger: Kevin

    Kuchta • Start in your code and step into gem code
  46. @kkuchta Tracing: Forwards Starting from: result = first(c) def first(x)

    second(x - 3) end def second(y) third(y * 2) end
  47. @kkuchta Tracing: Forwards Starting from: result = first(c) def first(x)

    second(x - 3) end def second(y) third(y * 2) end def third(z) z / z + 1 end
  48. @kkuchta Tracing: Forwards • Debuggers • Step into each new

    level • Print debugging • See how far the code is getting in each level • Good when you have a function call that's not doing what you'd expect
  49. @kkuchta • Start at your point of interest • Good

    for exceptions Tracing: Backwards
  50. @kkuchta • Start at your point of interest • Good

    for exceptions • Text search / referral Tracing: Backwards
  51. @kkuchta • Searching for references • Check current file •

    Check includes / extends • Metaprogramming Tracing
  52. @kkuchta • Searching for references • Check current file •

    Check includes / extends • Metaprogramming • Use ag to search the whole codebase Tracing
  53. @kkuchta • Searching for references • Check current file •

    Check includes / extends • Metaprogramming • Use ag to search the whole codebase • Check gem files Tracing
  54. @kkuchta The Kuchta 3ish-step Process©™® 1. Take me to your

    leader • Entry points • Big files 2. Text search • Ag • Repeated searching
  55. @kkuchta The Kuchta 3ish-step Process©™® 1. Take me to your

    leader • Entry points • Big files 2. Text search • Ag • Repeated searching 3. Trace • Puts statements • Debugger