$30 off During Our Annual Pro Sale. View Details »

RubyConf Brazil 2015: Deep Diving: How to Explore a New Code Base

RubyConf Brazil 2015: Deep Diving: How to Explore a New Code Base

As a developer, diving in a new code base is not uncommon: you've just been hired, you change of project, you want to help an open source project, the open source library your project depends on is buggy, etc. It's like entering in a underwater cave, you don't know the treasures or the monsters you'll find there, neither if the path is treacherous or if it's a true labyrinth where you'll get lost. You can prepare yourself, you can plan your visit, you can equip yourself, you can survive and find the gem you're looking for...

The gem trace_calls https://github.com/toch/trace_calls
The original snippet of code using TracePoint API https://gist.github.com/toch/82fd93ca449500d21f00

Christophe Philemotte

September 19, 2015
Tweet

More Decks by Christophe Philemotte

Other Decks in Programming

Transcript

  1. Deep Diving:
    How to Explore a
    New Codebase
    Andy Spearing © 2009

    View Slide

  2. _toch
    toch
    Hi, I'm
    Christophe

    View Slide

  3. @matylda © 2011
    Intro

    View Slide

  4. @matylda © 2011
    The most
    common
    task

    View Slide

  5. continuous typing

    View Slide

  6. View Slide

  7. but
    continuous reading
    Jono Witts © 2008

    View Slide

  8. Each written line
    is read
    at least 1 time
    Gavin St. Ours © 20013

    View Slide

  9. ● 300 dev days
    ● 14633 LOC
    ● 634595 Changed lines
    ● → 634595 / 14633 ~ 43 changed LOC / LOC

    View Slide

  10. For 1 final line,
    I read at least
    43 lines.

    View Slide

  11. View Slide

  12. How to
    Dive
    into a project
    Stuart Hamilton © 2008

    View Slide

  13. DO NOT
    Wander
    without a plan
    Felix Esteban © 2004

    View Slide

  14. Journey
    Needs
    Plan
    Peter Southwood © 20011

    View Slide

  15. Plan
    1. Goal
    2. Map
    3. Equipment & Dive
    4. Next?
    Peter Southwood © 20011

    View Slide

  16. 1.
    Goal
    Peter Southwood © 20011

    View Slide

  17. ➔ fix a bug
    ➔ implement a feature
    ➔ write some doc
    ➔ style the code
    ➔ refactor a small piece of code
    ➔ simply use it

    View Slide

  18. View Slide

  19. http_proxy=http://127.0.0.1 \
    ruby -e "
    require 'open-uri'
    open(
    'http://google.com',
    proxy: nil
    )
    "

    View Slide

  20. ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `initialize': Connection
    refused - connect(2) (Errno::ECONNREFUSED)
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `block in connect'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/timeout.rb:52:in `timeout'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:877:in `connect'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:862:in `do_start'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:851:in `start'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:313:in `open_http'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:708:in `buffer_open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:210:in `block in open_loo
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:208:in `catch'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:208:in `open_loop'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:149:in `open_uri'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:688:in `open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:34:in `open'
    from -e:1:in `'

    View Slide

  21. 2.
    Map
    Peter Southwood © 20011

    View Slide

  22. Find the Map
    ➔ Repository URI
    ➔ Project URI
    ➔ Intro Doc: README

    View Slide

  23. Find the Map
    ● https://github.com/ruby/ruby
    ● https://bugs.ruby-lang.org/
    ● https://github.com/ruby/ruby/blob/trunk/
    README.md

    View Slide

  24. Find the Legend

    View Slide

  25. a
    Legend

    View Slide

  26. Find the Legend
    ➔ CONTRIBUTING Guideline

    View Slide

  27. Find the Legend
    ➔ CONTRIBUTING Guideline
    https://github.com/ruby/ruby/blob/trunk/
    CONTRIBUTING.md

    View Slide

  28. Find the Legend
    ➔ CONTRIBUTING Guideline
    https://github.com/ruby/ruby/blob/trunk/
    CONTRIBUTING.md

    View Slide

  29. Find the Legend
    ➔ CONTRIBUTING Guideline
    ➔ Directory Structure

    View Slide

  30. Find the Legend
    ➔ CONTRIBUTING Guideline
    ➔ Directory Structure

    View Slide

  31. Find the Legend
    ➔ CONTRIBUTING Guideline
    ➔ Directory Structure
    ➔ Talk to other contributors, maintainers

    View Slide

  32. Find the Legend
    ➔ CONTRIBUTING Guideline
    ➔ Directory Structure
    ➔ Talk to other contributors, maintainers
    ➔ Participate to the dev meeting

    View Slide

  33. If blocked?
    ➔ ASK!

    View Slide

  34. Boris Dimitrov © 20009
    3.
    Equipment
    & Dive

    View Slide

  35. Get a Toolbelt
    ➔ Pick an Editor

    View Slide

  36. xkcd

    View Slide

  37. Get a Toolbelt
    ➔ Pick an Editor
    ➔ Bundle

    View Slide

  38. $ cd GitLab
    $ bundle viz

    View Slide

  39. $ cd GitLab
    $ bundle viz

    View Slide

  40. $ cd GitLab
    $ bundle viz

    View Slide

  41. Get a Toolbelt
    ➔ Pick an Editor
    ➔ Bundle
    ➔ Run Tests

    View Slide

  42. Get a Toolbelt
    ➔ Pick an Editor
    ➔ Bundle
    ➔ Run Tests
    ➔ Others

    View Slide

  43. Get a Toolbelt
    ➔ ST3
    ➔ Apply a Patch & Use it
    ➔ MRI WIKI DeveloperHowto
    ➔ Sign up to Ruby Redmine

    View Slide

  44. Use your Toolbelt
    ➔ Run the App & Use it

    View Slide

  45. http_proxy=http://127.0.0.1 \
    ruby -e "
    require 'open-uri'
    open(
    'http://google.com',
    proxy: nil
    )
    "

    View Slide

  46. Use your Toolbelt
    ➔ Run the App & Use it
    ➔ Read the Code

    View Slide

  47. $ wc -l *.rb
    1001 common.rb
    257 ftp.rb
    1676 generic.rb
    106 http.rb
    22 https.rb
    260 ldap.rb
    20 ldaps.rb
    280 mailto.rb
    3622 total
    open-uri

    View Slide

  48. ➔ Find proxy management
    ➔ Understand how open_http works
    ➔ Spot the bug
    URI

    View Slide

  49. ➔ Trace the Calls
    Dive into URI

    View Slide

  50. ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `initialize': Connection
    refused - connect(2) (Errno::ECONNREFUSED)
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `block in connect'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/timeout.rb:52:in `timeout'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:877:in `connect'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:862:in `do_start'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:851:in `start'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:313:in `open_http'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:708:in `buffer_open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:210:in `block in open_loo
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:208:in `catch'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:208:in `open_loop'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:149:in `open_uri'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:688:in `open'
    from ~/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/open-uri.rb:34:in `open'
    from -e:1:in `'

    View Slide

  51. def get_call_graph_on
    scope = {}
    trace = TracePoint.new(:call, :line) do |tp|
    case tp.event
    when :call then puts "#{tp.path}:#{tp.lineno} #{tp.defined_class}::#{tp.method_id} called from " \
    "#{scope[:path]}:#{scope[:lineno]} #{scope[:class]}::#{scope[:method_id]}"
    when :line then scope = {
    event: :line,
    lineno: tp.lineno,
    path: tp.path,
    class: tp.defined_class,
    method_id: tp.method_id
    }
    end
    end
    trace.enable
    yield
    trace.disable
    end

    View Slide

  52. scope = {}
    trace = TracePoint.new(:call, :line) do |tp|
    case tp.event
    # ...
    end
    end

    View Slide

  53. trace = TracePoint.new(:call, :line) do |tp|
    case tp.event
    when :call then puts
    "#{tp.path}:#{tp.lineno} ..." \
    "#{scope[:path]}:#{scope[:lineno]} ..."
    # ...
    end
    end

    View Slide

  54. # ...
    when :line then scope = {
    event: :line,
    lineno: tp.lineno,
    path: tp.path,
    class: tp.defined_class,
    method_id: tp.method_id
    }

    View Slide

  55. trace.enable
    yield
    trace.disable

    View Slide

  56. require 'open-uri'
    get_call_graph_on do
    open(
    'http://google.com',
    proxy: nil
    )
    end

    View Slide

  57. View Slide

  58. require 'open-uri'
    require 'trace_calls'
    TraceCalls::on do
    open('http://google.com', proxy: nil)
    end
    puts TraceCalls::root

    View Slide

  59. +-Kernel::open
    +-#::parse called from Kernel::open at 33
    | \-URI::Parser::parse called from #::parse at 747
    | | +-URI::Parser::split called from URI::Parser::parse at 211
    | | +-#::scheme_list called from URI::Parser::parse at 213
    | | +-#::scheme_list called from URI::Parser::parse at 214
    | | \-URI::HTTP::initialize called from URI::Parser::parse at 660
    | | | \-URI::Generic::initialize called from URI::HTTP::initialize at 84
    | | | | +-URI::Generic::set_scheme called from URI::Generic::initialize at 203
    | | | | +-URI::Generic::set_userinfo called from URI::Generic::initialize at 204
    | | | | | \-URI::Generic::split_userinfo called from URI::Generic::set_userinfo at 525
    | | | | +-URI::Generic::set_host called from URI::Generic::initialize at 205
    | | | | +-URI::Generic::set_port called from URI::Generic::initialize at 206
    | | | | +-URI::Generic::set_path called from URI::Generic::initialize at 207
    | | | | +-URI::Generic::set_query called from URI::Generic::initialize at 208
    | | | | +-URI::Generic::set_opaque called from URI::Generic::initialize at 209
    | | | | +-URI::Generic::set_registry called from URI::Generic::initialize at 210
    | | | | +-URI::Generic::set_fragment called from URI::Generic::initialize at 211
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | \-URI::Generic::set_port called from URI::Generic::initialize at 30

    View Slide

  60. +-Kernel::open
    +-#::parse called from Kernel::open at 33
    | \-URI::Parser::parse called from #::parse at 747
    | | +-URI::Parser::split called from URI::Parser::parse at 211
    | | +-#::scheme_list called from URI::Parser::parse at 213
    | | +-#::scheme_list called from URI::Parser::parse at 214
    | | \-URI::HTTP::initialize called from URI::Parser::parse at 660
    | | | \-URI::Generic::initialize called from URI::HTTP::initialize at 84
    | | | | +-URI::Generic::set_scheme called from URI::Generic::initialize at 203
    | | | | +-URI::Generic::set_userinfo called from URI::Generic::initialize at 204
    | | | | | \-URI::Generic::split_userinfo called from URI::Generic::set_userinfo at 525
    | | | | +-URI::Generic::set_host called from URI::Generic::initialize at 205
    | | | | +-URI::Generic::set_port called from URI::Generic::initialize at 206
    | | | | +-URI::Generic::set_path called from URI::Generic::initialize at 207
    | | | | +-URI::Generic::set_query called from URI::Generic::initialize at 208
    | | | | +-URI::Generic::set_opaque called from URI::Generic::initialize at 209
    | | | | +-URI::Generic::set_registry called from URI::Generic::initialize at 210
    | | | | +-URI::Generic::set_fragment called from URI::Generic::initialize at 211
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | \-URI::Generic::set_port called from URI::Generic::initialize at 30

    View Slide

  61. +-Kernel::open
    +-#::parse called from Kernel::open at 33
    | \-URI::Parser::parse called from #::parse at 747
    | | +-URI::Parser::split called from URI::Parser::parse at 211
    | | +-#::scheme_list called from URI::Parser::parse at 213
    | | +-#::scheme_list called from URI::Parser::parse at 214
    | | \-URI::HTTP::initialize called from URI::Parser::parse at 660
    | | | \-URI::Generic::initialize called from URI::HTTP::initialize at 84
    | | | | +-URI::Generic::set_scheme called from URI::Generic::initialize at 203
    | | | | +-URI::Generic::set_userinfo called from URI::Generic::initialize at 204
    | | | | | \-URI::Generic::split_userinfo called from URI::Generic::set_userinfo at 525
    | | | | +-URI::Generic::set_host called from URI::Generic::initialize at 205
    | | | | +-URI::Generic::set_port called from URI::Generic::initialize at 206
    | | | | +-URI::Generic::set_path called from URI::Generic::initialize at 207
    | | | | +-URI::Generic::set_query called from URI::Generic::initialize at 208
    | | | | +-URI::Generic::set_opaque called from URI::Generic::initialize at 209
    | | | | +-URI::Generic::set_registry called from URI::Generic::initialize at 210
    | | | | +-URI::Generic::set_fragment called from URI::Generic::initialize at 211
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | +-URI::Generic::default_port called from URI::Generic::initialize at 220
    | | | | | \-#::default_port called from URI::Generic::default_port at
    | | | | \-URI::Generic::set_port called from URI::Generic::initialize at 30

    View Slide

  62. \-OpenURI::OpenRead::open called from Kernel::open at 34
    | \-#::open_uri called from OpenURI::OpenRead::open at 688
    | | +-#::scan_open_optional_arguments called from #::open_ur
    | | +-#::check_options called from #::open_uri at 136
    | | \-#::open_loop called from #::open_uri at 149
    | | | +-OpenURI::Buffer::initialize called from #::open_loop at 209
    | | | +-URI::HTTP::buffer_open called from #::open_loop at 195
    | | | | \-#::open_http called from URI::HTTP::buffer_open at 708
    | | | | | +-URI::Generic::userinfo called from #::open_http at 259
    | | | | | +-Kernel::require called from #::open_http at 267
    | | | | | | +-MonitorMixin::mon_enter called from Kernel::require at 39
    | | | | | | +-#::find_unresolved_default_spec called from Kernel::require at 43
    | | | | | | | \-#::suffixes called from #::find_unresolved_default_s
    | | | | | | +-#::unresolved_deps called from Kernel::require at
    | | | | | | +-#::stubs called from Kernel::require at 63
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64

    View Slide

  63. \-OpenURI::OpenRead::open called from Kernel::open at 34
    | \-#::open_uri called from OpenURI::OpenRead::open at 688
    | | +-#::scan_open_optional_arguments called from #::open_ur
    | | +-#::check_options called from #::open_uri at 136
    | | \-#::open_loop called from #::open_uri at 149
    | | | +-OpenURI::Buffer::initialize called from #::open_loop at 209
    | | | +-URI::HTTP::buffer_open called from #::open_loop at 195
    | | | | \-#::open_http called from URI::HTTP::buffer_open at 708
    | | | | | +-URI::Generic::userinfo called from #::open_http at 259
    | | | | | +-Kernel::require called from #::open_http at 267
    | | | | | | +-MonitorMixin::mon_enter called from Kernel::require at 39
    | | | | | | +-#::find_unresolved_default_spec called from Kernel::require at 43
    | | | | | | | \-#::suffixes called from #::find_unresolved_default_s
    | | | | | | +-#::unresolved_deps called from Kernel::require at
    | | | | | | +-#::stubs called from Kernel::require at 63
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64

    View Slide

  64. \-OpenURI::OpenRead::open called from Kernel::open at 34
    | \-#::open_uri called from OpenURI::OpenRead::open at 688
    | | +-#::scan_open_optional_arguments called from #::open_ur
    | | +-#::check_options called from #::open_uri at 136
    | | \-#::open_loop called from #::open_uri at 149
    | | | +-OpenURI::Buffer::initialize called from #::open_loop at 209
    | | | +-URI::HTTP::buffer_open called from #::open_loop at 195
    | | | | \-#::open_http called from URI::HTTP::buffer_open at 708
    | | | | | +-URI::Generic::userinfo called from #::open_http at 259
    | | | | | +-Kernel::require called from #::open_http at 267
    | | | | | | +-MonitorMixin::mon_enter called from Kernel::require at 39
    | | | | | | +-#::find_unresolved_default_spec called from Kernel::require at 43
    | | | | | | | \-#::suffixes called from #::find_unresolved_default_s
    | | | | | | +-#::unresolved_deps called from Kernel::require at
    | | | | | | +-#::stubs called from Kernel::require at 63
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64
    | | | | | | | \-Gem::StubSpecification::name called from Gem::StubSpecification::activated
    | | | | | | +-Gem::StubSpecification::activated? called from Kernel::require at 64

    View Slide

  65. | | | | | +-URI::Generic::hostname called from #::open_http at 278
    | | | | | +-URI::HTTP::request_uri called from #::open_http at 280
    | | | | | | \-URI::Generic::path_query called from URI::HTTP::request_uri at 96
    | | | | | +-#::new called from #::open_http at 291
    | | | | | | +-Net::HTTP::initialize called from #::new at 609
    | | | | | | \-#::proxy_class? called from #::new at 611
    | | | | | +-Net::HTTP::start called from #::open_http at 313
    | | | | | | +-Net::HTTP::do_start called from Net::HTTP::start at 851
    | | | | | | | \-Net::HTTP::connect called from Net::HTTP::do_start at 862
    | | | | | | | | +-Net::HTTP::proxy? called from Net::HTTP::connect at 868
    | | | | | | | | | \-Net::HTTP::proxy_uri called from Net::HTTP::proxy? at 1014
    | | | | | | | | | | +-Kernel::URI called from Net::HTTP::proxy_uri at 1027
    | | | | | | | | | | | \-#::parse called from Kernel::URI at 994
    | | | | | | | | | | | | \-URI::Parser::parse called from #::parse at 747
    | | | | | | | | | | | | | +-URI::Parser::split called from URI::Parser::parse at 211
    | | | | | | | | | | | | | +-#::scheme_list called from URI::Parser::parse at 21
    | | | | | | | | | | | | | +-#::scheme_list called from URI::Parser::parse at 21
    | | | | | | | | | | | | | \-URI::HTTP::initialize called from URI::Parser::parse at 660
    | | | | | | | | | | | | | | \-URI::Generic::initialize called from URI::HTTP::initialize a
    | | | | | | | | | | | | | | | +-URI::Generic::set_scheme called from URI::Generic::initial
    | | | | | | | | | | | | | | | +-URI::Generic::set_userinfo called from URI::Generic::initi

    View Slide

  66. http = klass.new(
    target_host,
    target_port
    )

    View Slide

  67. ➔ Trace the Calls
    ➔ Search the Source
    Dive into URI

    View Slide

  68. Search the Source
    $ grep -nr "open_http".
    ./open-uri.rb:253: def OpenURI.open_http(buf, target, proxy, option
    ./open-uri.rb:708: OpenURI.open_http(buf, self, proxy, options)
    ./open-uri.rb:717: OpenURI.open_http(buf, self, proxy, option

    View Slide

  69. Search the Source
    $ grep -rn "class HTTP " .
    ./net/http.rb:384: class HTTP < Protocol
    ./uri/http.rb:22: class HTTP < Generic

    View Slide

  70. Search the Source
    $ EDITOR=subl \
    bundle open gitlab_omniauth-ldap

    View Slide

  71. ➔ Trace the Calls
    ➔ Search the Source
    ➔ Get Context with a Test
    Dive into URI

    View Slide

  72. Get Context with a Test
    context "without params['markdown_img']" do
    it "returns an error" do
    TraceCalls::on do
    post :upload_image, id: project.to_param, format: :json
    end
    puts TraceCalls::root
    expect(response.status).to eq(422)
    end
    end

    View Slide

  73. Use your Toolbelt
    ➔ Run the App & Use it
    ➔ Read the Code
    ➔ Edit the Code

    View Slide

  74. http = proxy ?
    klass.new(target_host, target_port) :
    klass.new(target_host, target_port, nil)

    View Slide

  75. Use your Toolbelt
    ➔ Run the App & Use it
    ➔ Read & Edit the Code
    ➔ Report a bug
    ➔ Submit a contribution

    View Slide

  76. View Slide

  77. If blocked?
    ➔ ASK!

    View Slide

  78. 4.
    Next?
    Peter Southwood © 20011

    View Slide

  79. How long?
    ➔ Keep it reasonable at first
    ➔ From ½ to 1 day

    View Slide

  80. Find Next Goal
    ➔ Fix a bug
    ➔ Develop a feature
    ➔ Run Static Analysis Tools
    ➔ Review a PullRequest

    View Slide

  81. Iterate
    ➔ :)
    Jean-Marc Kuffer © 2007

    View Slide

  82. If blocked?
    ➔ ASK!

    View Slide

  83. Outro
    1. Goal
    2. Map
    3. Equipment & Dive

    View Slide

  84. ?
    _toch
    toch

    View Slide