< Return to Video

RailsConf 2014 - Debugger Driven Developement with Pry by Joel Turnbull

  • 0:18 - 0:22
    JOEL TURNBULL: Thanks for coming.
  • 0:22 - 0:24
    My name is Joel Turnbull.
  • 0:24 - 0:26
    I'm a developer for Gaslight in Cincinnatti,
    Ohio.
  • 0:26 - 0:30
    I also head up and coordinate a blog over
    there,
  • 0:30 - 0:33
    in case anybody ever wants to talk about that.
  • 0:33 - 0:37
    But today, I'm here to talk about debugger
    driven
  • 0:37 - 0:44
    development with Pry. Most Rubyists I know
    don't use
  • 0:44 - 0:51
    debuggers. Rubyists I know, when faced with
    a problem,
  • 0:51 - 0:57
    would prefer to ponder code than pop open
    a
  • 0:57 - 0:59
    debugger and poke around.
  • 0:59 - 1:03
    This is crazy to me. I've always thought it
  • 1:03 - 1:06
    was, because to me, trying to solve a problem
  • 1:06 - 1:10
    by pondering code is like trying to find your
  • 1:10 - 1:14
    keys in the dark, when you're holding a flashlight,
  • 1:14 - 1:20
    but you're consciously deciding not to use
    it.
  • 1:20 - 1:22
    So why is this? I, I used to think
  • 1:22 - 1:25
    it might be about egos or culture or something
  • 1:25 - 1:28
    like that, but really it's pretty simple.
    I think
  • 1:28 - 1:30
    we've had a lack of really good tools up
  • 1:30 - 1:37
    to this point. But ultimately, my talk isn't
    about
  • 1:37 - 1:41
    using debuggers in a traditional sense to
    fix software,
  • 1:41 - 1:44
    but using debuggers as a tool in your workflow
  • 1:44 - 1:47
    to build software.
  • 1:47 - 1:51
    And, so why do I think we can do
  • 1:51 - 1:55
    this right now? I feel like we finally have
  • 1:55 - 2:00
    a tool that we can use to explore this
  • 2:00 - 2:06
    debugger driven, debugger driven workflow.
    And that tool is
  • 2:06 - 2:07
    Pry.
  • 2:07 - 2:09
    Can I get a show of hands of who
  • 2:09 - 2:16
    uses Pry? Awesome. Like, everybody. All right.
    Cool. So
  • 2:17 - 2:21
    I'm talking about debugger driven development
    with Pry. Conrad
  • 2:21 - 2:24
    Erwin gave a talk not even like six months
  • 2:24 - 2:27
    ago called REPL Driven Development with Pry.
    I swear
  • 2:27 - 2:29
    to god I had no idea.
  • 2:29 - 2:36
    But, and both are accurate, I think. You know,
  • 2:37 - 2:40
    but I think both terms kind of undersell what
  • 2:40 - 2:42
    the power of Pry is. So here's my favorite
  • 2:42 - 2:47
    definition. Pry is an IRB alternative and
    runtime developer
  • 2:47 - 2:50
    console. So if we take the first part of
  • 2:50 - 2:54
    that and we think about Pry as an IRB
  • 2:54 - 2:58
    alternative, you know, anything that you can
    do with,
  • 2:58 - 3:03
    with, both, both are REPLs, right. And, and
    a
  • 3:03 - 3:05
    debugger is a REPL, too. Anything you can
    do
  • 3:05 - 3:08
    in Ruby, you can do in IRB. Anything you
  • 3:08 - 3:12
    can do in IRB, you can do in Pry.
  • 3:12 - 3:15
    What makes both of them powerful, is they
    both
  • 3:15 - 3:21
    leverage this idea of runtime. And, to me,
    runtime
  • 3:21 - 3:24
    is all about immersion. It's about being immersed
    in
  • 3:24 - 3:29
    a live system, where you can play with code
  • 3:29 - 3:31
    and you can look at your objects and, and
  • 3:31 - 3:34
    all that kind of thing. You can validate your
  • 3:34 - 3:41
    implementations. Everything you need to do.
    And it's like,
  • 3:42 - 3:45
    looking for your keys with a flashlight.
  • 3:45 - 3:49
    So, given that Pry and IRB are both REPLs
  • 3:49 - 3:51
    and they both have this idea of runtime, why
  • 3:51 - 3:57
    should you use Pry instead of IRB? It's got
  • 3:57 - 3:59
    a couple vital workflow features right out
    of the
  • 3:59 - 4:04
    box. Syntax highlighting and tab completion.
    Both super handy.
  • 4:04 - 4:07
    But, what I want to talk about today is
  • 4:07 - 4:11
    some of the bigger, game-changing features
    of Pry. The
  • 4:11 - 4:18
    first one is enhanced introspection. Here's
    our friend again.
  • 4:19 - 4:22
    Introspection is the ability to, of, of a
    language,
  • 4:22 - 4:25
    where you can ask a language questions about
    itself.
  • 4:25 - 4:29
    And it's built into Ruby, and that's awesome.
  • 4:29 - 4:32
    If you've ever asked a class what method you
  • 4:32 - 4:35
    can call on it, or you've asked an instance
  • 4:35 - 4:41
    what class it is, you're doing introspection.
  • 4:41 - 4:43
    What if you want to go deeper? Like, what
  • 4:43 - 4:45
    if you want to know what the class methods
  • 4:45 - 4:47
    are versus the instance methods? You know,
    what if
  • 4:47 - 4:51
    you want to know what methods are inherited,
    and
  • 4:51 - 4:53
    from where? What if you want to know what
  • 4:53 - 4:58
    state an instance holds onto during its life
    cycle?
  • 4:58 - 5:01
    You can answer all these questions with plain
    Ruby
  • 5:01 - 5:05
    and IRB. But the problem is, is that, it's,
  • 5:05 - 5:09
    the, the amount of effort involved is non-trivial.
    I
  • 5:09 - 5:14
    would classify it as daunting.
  • 5:14 - 5:21
    So. Given that. You know. I would, I would
  • 5:29 - 5:35
    classify this as DRTFM. This is what I, this
  • 5:35 - 5:41
    is what I point to. I mean, this is
  • 5:41 - 5:44
    the workflow that I like.
  • 5:44 - 5:47
    I like to take the things out of the
  • 5:47 - 5:49
    box. I like to get a feel for what
  • 5:49 - 5:52
    the pieces are. I like to play around with
  • 5:52 - 5:54
    them. I like to try to put it together
  • 5:54 - 5:57
    without reading the manual, and if I get stuck,
  • 5:57 - 6:01
    then I read the manual, right.
  • 6:01 - 6:06
    The second really game-changing feature of
    Pry, to me,
  • 6:06 - 6:10
    is extendability through plugins. And the
    best way that
  • 6:10 - 6:14
    I can show this is to just demo some
  • 6:14 - 6:18
    of my favorites for you.
  • 6:18 - 6:24
    So, I'm gonna show you a Rails app. Instead
  • 6:24 - 6:28
    of calling it in a normal way, like rails
  • 6:28 - 6:32
    s, I'm gonna call it like this. Under the
  • 6:32 - 6:35
    umbrella of Pry rescue. And I'll show you
    why
  • 6:35 - 6:40
    in a minute.
  • 6:40 - 6:43
    But here we are. This is an app I've
  • 6:43 - 6:46
    been working on late, late nights, you know.
    Please
  • 6:46 - 6:50
    don't steal this. This is a bowling score
    count,
  • 6:50 - 6:53
    tracker, right. You can push any number after
    you
  • 6:53 - 6:56
    bowl and it will record what you, what, how
  • 6:56 - 6:59
    many pins you knocked down, right.
  • 6:59 - 7:01
    So, the first thing I'm gonna show you about
  • 7:01 - 7:08
    Pry, for those who aren't necessarily familiar,
    is how
  • 7:10 - 7:15
    you invoke a runtime at any point in your
  • 7:15 - 7:19
    application, where you're running Ruby, right.
  • 7:19 - 7:22
    Here, I've inserted a couple binding dot prys.
    One
  • 7:22 - 7:25
    into my controller action and one into my
    template.
  • 7:25 - 7:28
    On the lower left we have our model. OK,
  • 7:28 - 7:32
    so let's rerun it with that in mind.
  • 7:32 - 7:34
    Let's go back to our running server, and we'll
  • 7:34 - 7:38
    see that we've halted our execution here,
    and we've
  • 7:38 - 7:41
    been dropped into a run time. And we can
  • 7:41 - 7:46
    do things in here like look around. We can
  • 7:46 - 7:48
    play lines of code. Let's play the line that
  • 7:48 - 7:52
    sets the bowling game. And then we can look
  • 7:52 - 7:55
    again.
  • 7:55 - 7:57
    When we exit from this, we've returned from
    our
  • 7:57 - 8:00
    controller. We're starting to render our template,
    and we've
  • 8:00 - 8:02
    hit our next binding dot pry. You can put
  • 8:02 - 8:07
    binding dot prys inside your erb tags, right.
  • 8:07 - 8:10
    Same drill. We can look at bowling games.
    We
  • 8:10 - 8:17
    can, we can step into our next, our, our,
  • 8:18 - 8:21
    our implementations of our methods. Here we've
    stepped into
  • 8:21 - 8:25
    the frames method of our bowling game model.
  • 8:25 - 8:29
    Same drill. We can look around here. We can
  • 8:29 - 8:34
    look at ourself. We can go to the next,
  • 8:34 - 8:39
    or we can continue.
  • 8:39 - 8:44
    So, you can see how handy that would be
  • 8:44 - 8:49
    if things aren't necessarily blowing up, but,
    you know,
  • 8:49 - 8:56
    something's not quite right, either. So what
    if things
  • 8:57 - 9:01
    do blow up, though? Right.
  • 9:01 - 9:05
    So, here I am. I'm gonna, I'm gonna spark
  • 9:05 - 9:08
    an exception here by bullet, trying to bowl
    the
  • 9:08 - 9:11
    next ball of my next frame. I'm gonna bowl
  • 9:11 - 9:15
    a big nine. I'm gonna come back here and
  • 9:15 - 9:19
    we've been dropped into a runtime, because
    an exception
  • 9:19 - 9:22
    was thrown. That's what being under the watchful
    eye
  • 9:22 - 9:25
    of Pry rescue is gonna do for us within
  • 9:25 - 9:28
    this context here.
  • 9:28 - 9:33
    So, some interesting, interesting things we
    can do here.
  • 9:33 - 9:36
    We can call the show-stack command of the
    Pry
  • 9:36 - 9:41
    stack explorer plugin. And we're seeing the
    whole stack
  • 9:41 - 9:45
    here. this is like caller, but it's alive,
    right.
  • 9:45 - 9:49
    We can move up the stack. We can move
  • 9:49 - 9:52
    up the stack nine frames. We can move down
  • 9:52 - 9:54
    the stack. We can take a look at the
  • 9:54 - 9:59
    state of any object here at any level of
  • 9:59 - 10:00
    our stack trace.
  • 10:00 - 10:02
    We can look at our stack again just to
  • 10:02 - 10:05
    see where we're at. We're on frame ten. But
  • 10:05 - 10:08
    this is a big hairy kind of Rails stack
  • 10:08 - 10:09
    trace, and we're not getting a whole lot of
  • 10:09 - 10:11
    value out of that right now.
  • 10:11 - 10:15
    So let's call the cd-cause command of Pry
    rescue.
  • 10:15 - 10:19
    And see if it can track down what caused
  • 10:19 - 10:23
    this problem in the first place.
  • 10:23 - 10:25
    There we go. This looks more familiar, right.
    Here
  • 10:25 - 10:29
    we are in our template. And I will guess
  • 10:29 - 10:32
    that on line thirteen we have a problem with
  • 10:32 - 10:35
    frame one. And that's true. So we just need
  • 10:35 - 10:37
    to add a little bit of a guard in
  • 10:37 - 10:40
    here, and say, you know, if we have pins
  • 10:40 - 10:44
    on frame one, let's render that. Otherwise
    let's just
  • 10:44 - 10:46
    render a dash.
  • 10:46 - 10:49
    And we get feedback that that's going, that's
    what's
  • 10:49 - 10:52
    gonna happen, right. We know that this is
    a
  • 10:52 - 10:54
    working implementation.
  • 10:54 - 10:57
    So let's copy our history. This is another
    plugin
  • 10:57 - 11:02
    called Pry clipboard. That's gonna copy that
    implementation into
  • 11:02 - 11:06
    my clipboard. I'm gonna edit a file where
    the
  • 11:06 - 11:11
    last exception was raised. It drops me right
    where
  • 11:11 - 11:13
    I need to be. I'm gonna paste in that
  • 11:13 - 11:20
    implementation, drop back in, and I'm gonna
    ask Pry
  • 11:23 - 11:25
    rescue to try it again.
  • 11:25 - 11:32
    And we're back. And we've got our dash. So,
  • 11:32 - 11:36
    and we can continue to bowl, right. That's
    legit,
  • 11:36 - 11:43
    right. A five and a six. OK, cool.
  • 11:45 - 11:51
    So, what do we do here? We used binding
  • 11:51 - 11:54
    dot pry to invoke a runtime anywhere in our
  • 11:54 - 11:58
    app where we're doing Ruby code. We used the
  • 11:58 - 12:03
    pry-debugger gem to give us our step, next,
    and
  • 12:03 - 12:06
    continue functionality that we expect out
    of our debugging
  • 12:06 - 12:09
    tools. We used pry-rescue and ran our Rails
    app
  • 12:09 - 12:12
    under the umbrella of pry-rescue to drop us
    into
  • 12:12 - 12:14
    a runtime when things go wrong, so that we
  • 12:14 - 12:18
    can poke around. We used the pry-stack_explorer
    gem to
  • 12:18 - 12:22
    navigate the stack and explore state at any
    level.
  • 12:22 - 12:27
    A few commands we saw were cd-cause in pry-rescue
  • 12:27 - 12:30
    that took us to the, the root of our
  • 12:30 - 12:37
    problem. We used play and we used copy-history
    to
  • 12:37 - 12:41
    not mess around with, you know, copying things
    with
  • 12:41 - 12:44
    our mouse and pasting them into our REPL,
    which
  • 12:44 - 12:47
    can be a pain. We used the edit command
  • 12:47 - 12:49
    with the e flag to take us to the
  • 12:49 - 12:51
    file where the exception occurred so that
    we could
  • 12:51 - 12:57
    fix it. And then we used pry-rescue, try-again,
    which
  • 12:57 - 13:02
    under this, under the context of Rails, just
    replayed
  • 13:02 - 13:05
    our request. We didn't have to reload our,
    our
  • 13:05 - 13:08
    page, or do any of that nonsense. Reload the
  • 13:08 - 13:12
    whole environment or anything like that, right.
    So it
  • 13:12 - 13:13
    was fast.
  • 13:13 - 13:19
    So, the, that's, that's great, and having
    demoed Pry
  • 13:19 - 13:21
    in a debugger context, you know, I can show
  • 13:21 - 13:24
    some of those things. But, really, what I
    find
  • 13:24 - 13:27
    interesting is this idea of Pry as a runtime
  • 13:27 - 13:31
    developer console, right.
  • 13:31 - 13:35
    So there's some really awspect- really awesome
    aspects of
  • 13:35 - 13:37
    Ruby, right. It's introspective, which we've
    talked about a
  • 13:37 - 13:39
    little bit and we'll talk about more. And
    it's
  • 13:39 - 13:42
    a dynamic and it's reflective, which means,
    you know,
  • 13:42 - 13:43
    we don't have to compile it and we can
  • 13:43 - 13:47
    change things on the fly in its runtime.
  • 13:47 - 13:49
    And I think we, we take advantage of these
  • 13:49 - 13:52
    things, you know, a lot in the code that
  • 13:52 - 13:56
    we write, whether we're doing metaprogramming
    or monkeypatching or,
  • 13:56 - 13:59
    or anything like that. But we, we haven't
    really
  • 13:59 - 14:02
    taken advantage of, of these things in the
    tools
  • 14:02 - 14:05
    and in our workflow yet.
  • 14:05 - 14:09
    So, when I talk about workflow and our problems
  • 14:09 - 14:12
    with our workflow, I see big problems with
    the
  • 14:12 - 14:16
    traditional workflow in, in Ruby development
    that I, that
  • 14:16 - 14:19
    I see. Right, I mean. By that I mean
  • 14:19 - 14:21
    we write some code in an editor. We save
  • 14:21 - 14:24
    it. We hop over to a terminal or we
  • 14:24 - 14:26
    pop over to a webpage and reload it and
  • 14:26 - 14:29
    run it to see if it worked and, if
  • 14:29 - 14:31
    it worked, we go back and continue on. If
  • 14:31 - 14:33
    it didn't, we fix it, we save. We go
  • 14:33 - 14:39
    back. We reload it, rerun it. Rinse, repeat,
    right?
  • 14:39 - 14:43
    What, what are, what problems do I see with
  • 14:43 - 14:47
    this? First of all, it's disruptive and it's
    distracting
  • 14:47 - 14:50
    to keep switching back and forth, right. Some
    amount
  • 14:50 - 14:54
    of context-switching is always gonna be imminent,
    right. But
  • 14:54 - 14:56
    any effort you can make to reduce that is
  • 14:56 - 14:59
    just gonna be a huge win for your flow,
  • 14:59 - 15:02
    you know, and your focus.
  • 15:02 - 15:04
    The second problem I see with that is it's
  • 15:04 - 15:06
    just guess work. We just write some code.
    Oh,
  • 15:06 - 15:08
    yeah, I think it's gonna work. We save it.
  • 15:08 - 15:11
    We go over. We run it to see if
  • 15:11 - 15:13
    it works. We're just taking shots in the dark,
  • 15:13 - 15:16
    you know. You can ask this guy.
  • 15:16 - 15:20
    Taking shots with a flashlight is much more
    accurate.
  • 15:20 - 15:23
    And recommended, apparently. So do that.
  • 15:23 - 15:27
    And, to me, it just seems backwards, right.
    We,
  • 15:27 - 15:31
    we're like solidifying and codifying our code
    into our
  • 15:31 - 15:34
    code base just in an attempt to see if
  • 15:34 - 15:38
    it works. It should be the opposite. Our code
  • 15:38 - 15:42
    base, it should be the, the record of code
  • 15:42 - 15:47
    that we've, we've already validated, right.
    And I, I
  • 15:47 - 15:50
    mean these things are just kind of things
    that
  • 15:50 - 15:53
    we accept and, and we, we take for granted,
  • 15:53 - 15:53
    right.
  • 15:53 - 15:57
    But I mean, I think other languages have,
    have
  • 15:57 - 16:01
    been more effective and intentional about
    integrating this idea
  • 16:01 - 16:06
    of a runtime into the, their workflow, you
    know.
  • 16:06 - 16:10
    And blurring this line between static and
    running code.
  • 16:10 - 16:12
    Clojure comes to mind, right.
  • 16:12 - 16:15
    But, we've talked about the awesome aspects
    of Ruby,
  • 16:15 - 16:18
    and there's really nothing that restricts
    or limits us
  • 16:18 - 16:20
    from doing the same, right. I mean, I think
  • 16:20 - 16:24
    the Ruby language is, the Ruby language enables
    it,
  • 16:24 - 16:26
    you know. It's just that up to this point,
  • 16:26 - 16:30
    we really haven't had the tools to do so.
  • 16:30 - 16:34
    So, we talked about these workflow problems.
    How does
  • 16:34 - 16:39
    Pry solve these workflow problems, right?
    I see that,
  • 16:39 - 16:42
    you know, the introspection and the documentation
    and the
  • 16:42 - 16:46
    source code browsing, which we'll see in a
    minute,
  • 16:46 - 16:51
    that's, that's baked into Pry, gives us ninety
    percent
  • 16:51 - 16:54
    of the information that we need to write code
  • 16:54 - 16:58
    right now, you know, in most cases.
  • 16:58 - 16:59
    It has a runtime that you can throw your
  • 16:59 - 17:02
    code against and validate it and get feedback
    on
  • 17:02 - 17:05
    whether it, whether it works or not, immediately.
    And
  • 17:05 - 17:07
    it's smart about editing. You don't have to
    think
  • 17:07 - 17:11
    about what file you need to open. Pry usually
  • 17:11 - 17:13
    knows what file you want to edit and usually
  • 17:13 - 17:16
    knows exactly where you want to edit it. Which
  • 17:16 - 17:18
    is really nice.
  • 17:18 - 17:25
    So, when I talk about this idea of runtime
  • 17:27 - 17:32
    development, I'm really talking about being
    immersed in this,
  • 17:32 - 17:37
    in this environment that, you know, loves
    us and
  • 17:37 - 17:41
    can give us feedback, and, and is alive, right.
  • 17:41 - 17:42
    So we want to spend the majority of our
  • 17:42 - 17:46
    development time there, and we want our editor
    to
  • 17:46 - 17:47
    just be an after thought. And that's just
    a
  • 17:47 - 17:50
    place where we just push working code when
    it's
  • 17:50 - 17:51
    done, right.
  • 17:51 - 17:55
    So, let me demo to you a little bit
  • 17:55 - 18:02
    about how I see that working, right.
  • 18:03 - 18:08
    So. Our mission is to write an empty class
  • 18:08 - 18:11
    definition, OK. This is just a script. No
    Rails
  • 18:11 - 18:15
    involved here or anything. But given a class
    like
  • 18:15 - 18:18
    name like BowlingGame, we want to create a
    file,
  • 18:18 - 18:22
    bowling_game dot rb, and write a class definition
    to
  • 18:22 - 18:27
    it, like class BowlingGame empty space end.
    Right, and
  • 18:27 - 18:30
    I've created a little skeleton here of, of
    how
  • 18:30 - 18:32
    I think that might work, right.
  • 18:32 - 18:34
    We're gonna, we're gonna read in a string
    from
  • 18:34 - 18:37
    the command line, we're gonna pass that string
    into
  • 18:37 - 18:40
    a method called file_name_for_class and get
    a file name.
  • 18:40 - 18:42
    We're gonna pass that string into a method
    called
  • 18:42 - 18:46
    class_definition for class and get a class
    definition, and
  • 18:46 - 18:48
    then we're gonna create that class by writing
    that
  • 18:48 - 18:53
    class definition to that file. Right.
  • 18:53 - 18:55
    So let's step out here and let's just run
  • 18:55 - 18:58
    that. So, we get an expected error, right.
    We
  • 18:58 - 19:03
    haven't implemented anything called file_name_for_class,
    yet. So we could
  • 19:03 - 19:05
    jump into our editor and start coding and
    all
  • 19:05 - 19:10
    that, but why don't we see what this is
  • 19:10 - 19:13
    like.
  • 19:13 - 19:17
    Why don't we use Pry rescue and leverage that
  • 19:17 - 19:20
    exception to drop us into a runtime and start
  • 19:20 - 19:26
    this runtime development process. So we do
    that. We
  • 19:26 - 19:28
    get the same error, but the difference is,
    we're
  • 19:28 - 19:33
    inside of a runtime now, OK. So, the first
  • 19:33 - 19:35
    problem is pretty easy. We know that we need
  • 19:35 - 19:41
    to define file_name_for_class. And we do so,
    right.
  • 19:41 - 19:44
    The difference is in here, I'm gonna raise,
    in
  • 19:44 - 19:48
    the implementation of this class, and drop
    back out.
  • 19:48 - 19:50
    And I'll show you why. When we ask Pry
  • 19:50 - 19:55
    to try again, we get in here. We're right
  • 19:55 - 19:58
    where we need to be to define the implementa-
  • 19:58 - 20:02
    working implementation of this class. Although,
    I already know
  • 20:02 - 20:06
    that I have forgotten to pass in the BowlingGame
  • 20:06 - 20:08
    string, like I always do.
  • 20:08 - 20:12
    OK. Now we have everything we need. So we
  • 20:12 - 20:15
    have something like BowlingGame, right, and
    we're looking for
  • 20:15 - 20:21
    something like bowling_game dot rb, OK. So
    I'm just
  • 20:21 - 20:25
    gonna preview my favorite Pry command of all
    time
  • 20:25 - 20:28
    here. ls.
  • 20:28 - 20:31
    When I call ls, and give it the class
  • 20:31 - 20:35
    or the object I'm working with, I immediately
    know
  • 20:35 - 20:37
    I'm working with a string and I'm seeing all
  • 20:37 - 20:40
    of the methods that are available to me on
  • 20:40 - 20:42
    that string. And when I say all of the
  • 20:42 - 20:45
    methods, I don't mean all of them. Notice
    that
  • 20:45 - 20:50
    the object methods are not in here. And, you
  • 20:50 - 20:54
    know that if you've ever used IRB to get
  • 20:54 - 20:57
    the method, you always do, thing dot method
    minus
  • 20:57 - 21:00
    object dot methods blah, blah, blah.
  • 21:00 - 21:02
    The, that's just noise, right, in most cases.
    So
  • 21:02 - 21:07
    the default is just to leave that out. The
  • 21:07 - 21:10
    developers of Pry have thought of that, right.
    So
  • 21:10 - 21:16
    we can try to class downcase, which gets us
  • 21:16 - 21:18
    so close, but yet so far away. We don't
  • 21:18 - 21:21
    really know where to put that underscore.
  • 21:21 - 21:26
    But, as Ruby devs, Rails devs, we know we
  • 21:26 - 21:29
    have things like ActiveSupport that can help
    us out
  • 21:29 - 21:35
    with that.
  • 21:35 - 21:37
    So we include that, and now when we ls
  • 21:37 - 21:40
    our class, we've got some extra stuff in here,
  • 21:40 - 21:44
    right. Demodulalize, deconstantize, right.
    That kind of stuff. And
  • 21:44 - 21:46
    if it, that's hard to see, we can just
  • 21:46 - 21:51
    ls ActiveSupport::Inflector itself and see
    what's available to us
  • 21:51 - 21:53
    there, right.
  • 21:53 - 21:56
    So we need an und, we needed an underscore.
  • 21:56 - 22:00
    I see a method called underscore there. Tab
    completion
  • 22:00 - 22:02
    is nice. All right. So now we're getting a
  • 22:02 - 22:06
    lot closer. All we need to do is stick
  • 22:06 - 22:11
    on our file exception, and I think we're done,
  • 22:11 - 22:12
    right.
  • 22:12 - 22:16
    So that, there's our filename. So let's copy
    that.
  • 22:16 - 22:23
    Let's drop back into our file. Right here,
    exactly
  • 22:23 - 22:26
    where we need to put the implementation, let's
    put
  • 22:26 - 22:30
    it in there. And let's save it and continue
  • 22:30 - 22:33
    on. OK, I know, I'm not gonna continue through
  • 22:33 - 22:37
    this whole thing, in the interest of time,
    but
  • 22:37 - 22:42
    I do want to, to implement this last method
  • 22:42 - 22:44
    inside of the create_class, so I can talk
    a
  • 22:44 - 22:47
    little bit more about what I love about introspection,
  • 22:47 - 22:49
    right.
  • 22:49 - 22:51
    Let's just reflect on what we did there, though,
  • 22:51 - 22:54
    with the workflow, OK. So we used, the, we
  • 22:54 - 22:57
    used pry-rescue to leverage the power, you
    know, to
  • 22:57 - 23:00
    leverage that exception, to pop us into a
    runtime,
  • 23:00 - 23:03
    and we used the runtime of the debugger not
  • 23:03 - 23:10
    to fix code, but to drive our development
    process,
  • 23:10 - 23:12
    right.
  • 23:12 - 23:17
    So. We're in here. We've raised inside of
    our
  • 23:17 - 23:21
    create_class method. We try again. We've got
    our file
  • 23:21 - 23:28
    name, and we've got our class definition.
    And we
  • 23:29 - 23:32
    have this file class, right. And, and I know
  • 23:32 - 23:34
    about this file class, and I know this is
  • 23:34 - 23:37
    exactly what I need to get the job done.
  • 23:37 - 23:39
    But, I mean, this is exactly what I, what
  • 23:39 - 23:42
    happened to me. I don't really remember exactly
    what
  • 23:42 - 23:44
    I need to do here, OK.
  • 23:44 - 23:48
    So I can pop out to StackOverflow or I
  • 23:48 - 23:51
    can pop out to, to Ruby-docs or something
    and
  • 23:51 - 23:55
    take a look. Or I can just ls file,
  • 23:55 - 24:01
    right here. And just take a minute to look
  • 24:01 - 24:04
    at this, and think about all of the low-level
  • 24:04 - 24:08
    Ruby instrospection acrobatics you would have
    to go through
  • 24:08 - 24:12
    to get this brain dump of information right
    here.
  • 24:12 - 24:14
    We've got all of the methods that you can
  • 24:14 - 24:16
    call on instances of file. We've got all the
  • 24:16 - 24:19
    methods you can call on the file class. We've
  • 24:19 - 24:23
    got all of the methods that file inherits,
    and,
  • 24:23 - 24:26
    from its superclass, and we know what that
    superclass
  • 24:26 - 24:27
    is. IO.
  • 24:27 - 24:29
    We also know all the constants involved and
    if
  • 24:29 - 24:33
    there was state involved in class or instance
    variables.
  • 24:33 - 24:38
    We would see that here as well. So, all
  • 24:38 - 24:43
    of a sudden, I'm informed about, about everything
    I
  • 24:43 - 24:45
    need to know about this class, and I have
  • 24:45 - 24:48
    a pretty good idea, just from a, from a
  • 24:48 - 24:51
    moment's notice, of how I can use it, or
  • 24:51 - 24:54
    what I can do to play around with it.
  • 24:54 - 25:00
    So let's. I see the right method. And I
  • 25:00 - 25:02
    don't really want to write anything or mess
    with
  • 25:02 - 25:05
    anything right now, so I'm just gonna look
    at
  • 25:05 - 25:09
    the documentation for it. OK. It opens the
    file.
  • 25:09 - 25:11
    It optimally seeks to the given offset. Writes
    a
  • 25:11 - 25:15
    string, then returns the length written. Write
    ensures that
  • 25:15 - 25:17
    the files close before returning. This sounds
    like what
  • 25:17 - 25:18
    I want.
  • 25:18 - 25:22
    I page down. I see some examples there. Great.
  • 25:22 - 25:27
    I'm ready to go.
  • 25:27 - 25:34
    Alternatively, I can do the same and call
    show-source,
  • 25:34 - 25:36
    OK. Which isn't doing a lot for me here,
  • 25:36 - 25:40
    but when I'm in my, my own code base,
  • 25:40 - 25:43
    this is what I lean on more often, right.
  • 25:43 - 25:50
    So, let's get our bearings again.
  • 25:50 - 25:57
    And let's give it a try, right. File.write(file_name.class_definition).
    OK.
  • 26:02 - 26:05
    Something happened. It seemed to work, right.
    How do
  • 26:05 - 26:10
    I know? Really easy to shell out in Pry.
  • 26:10 - 26:13
    You get really nice output. Exactly what you
    would
  • 26:13 - 26:15
    expect, which is, I can't say the same for
  • 26:15 - 26:18
    IRB in that, in that case.
  • 26:18 - 26:21
    I have this thing called bowling_game in there.
    Let's
  • 26:21 - 26:28
    take a look at it. That looks correct to
  • 26:28 - 26:30
    me. So let's just do that again. I'm gonna
  • 26:30 - 26:34
    copy the history again. And then I'm gonna
    remove
  • 26:34 - 26:39
    bowling_game, just to make sure that my script
    is
  • 26:39 - 26:45
    doing what it promises, right. Oops.
  • 26:45 - 26:51
    And it's not there anymore. So let's go back.
  • 26:51 - 26:58
    Let's put in that, let's put in that implementation.
  • 27:02 - 27:06
    And drop back in. Let's ask Pry to try
  • 27:06 - 27:12
    again. No more exceptions. Do we have our
    bowling_game?
  • 27:12 - 27:14
    We do.
  • 27:14 - 27:21
    OK. So, what happened there, right? We used
    the
  • 27:22 - 27:28
    debugger and the runtime as, as, as a tool
  • 27:28 - 27:32
    for driving our development process and build
    our implementation.
  • 27:32 - 27:35
    Not just to fix software, right.
  • 27:35 - 27:38
    We validated our implementation before codifying
    it, and we
  • 27:38 - 27:42
    reversed this traditional workflow that I've
    got so many
  • 27:42 - 27:47
    problems with. We explored and we informed
    ourselves about
  • 27:47 - 27:53
    how to, how to use our classes without context
  • 27:53 - 27:56
    switching. Right, we stayed focused in one
    tool. And
  • 27:56 - 28:00
    then we didn't have to reload our, our libraries
  • 28:00 - 28:02
    and our environment every time we wanted to
    run
  • 28:02 - 28:06
    code, so it was fast.
  • 28:06 - 28:11
    So, and, you know. We're in the testing track
  • 28:11 - 28:13
    here, so I should probably talk about testing
    a
  • 28:13 - 28:18
    little bit. And, that might have felt a little
  • 28:18 - 28:20
    bit like TDD to you. I mean, it does
  • 28:20 - 28:26
    to me, right. As, as practitioners of TDD,
    you
  • 28:26 - 28:30
    know, I may not, I'm, I'm, I love TDD.
  • 28:30 - 28:35
    I think most, many Rubyists do.
  • 28:35 - 28:38
    But I mean, what I love about it most
  • 28:38 - 28:41
    is it keeps me focused and, and, and, having
  • 28:41 - 28:47
    a test suite allows me to aggressively refactor
    my
  • 28:47 - 28:48
    code, you know, and have the confidence to
    do
  • 28:48 - 28:51
    that. You know, and I'd rather do that upfront
  • 28:51 - 28:54
    than after the fact, right. But, as practitioners
    of
  • 28:54 - 28:59
    TDD, you know, we've learned to love failure,
    you
  • 28:59 - 28:59
    know.
  • 28:59 - 29:02
    TDD is all about making something fail, then
    writing
  • 29:02 - 29:04
    the code to make it pass. When we have
  • 29:04 - 29:08
    bugs, we want to exercise that exact piece
    of
  • 29:08 - 29:11
    code that's giving us the error and get that
  • 29:11 - 29:13
    error, you know, before we then go, cause
    we
  • 29:13 - 29:15
    know we're about to do something cool and
    fix
  • 29:15 - 29:17
    it, right.
  • 29:17 - 29:22
    So, we love to see red. And so a,
  • 29:22 - 29:26
    a practice that's, that's centered around
    failure is naturally
  • 29:26 - 29:29
    suited to a debugging tool, right. I mean,
    that's
  • 29:29 - 29:33
    what a debugging tool does. It's meant to
    handle
  • 29:33 - 29:36
    failures, catch failures, and then give you
    the tools
  • 29:36 - 29:39
    you need and enable you to fix them as
  • 29:39 - 29:42
    quickly as you can. And we've seen how awesome,
  • 29:42 - 29:45
    you know, a debugger Pry can be.
  • 29:45 - 29:49
    And, so, the promise of this talk is that
  • 29:49 - 29:53
    I'm going to, you know, deliver a, a, a
  • 29:53 - 29:57
    Pry-enabled TDD workflow. But before I do
    that, I
  • 29:57 - 30:00
    mean, I mentioned that, you know, I've had
    a,
  • 30:00 - 30:04
    kind of specific experience that made me a
    believer
  • 30:04 - 30:07
    that this workflow even happens. I mean, I've
    actually
  • 30:07 - 30:10
    done this before. I'm not creating this, right.
  • 30:10 - 30:15
    And, and that specific experience was this.
    Does anybody
  • 30:15 - 30:20
    know what this is? Smalltalk. Yeah. This is
    a
  • 30:20 - 30:25
    screenshot of a Farrow Smalltalk IDE, right.
    And I
  • 30:25 - 30:29
    spent about a year or so in the 2000s
  • 30:29 - 30:32
    writing a web application in the C-side framework
    in
  • 30:32 - 30:35
    Smalltalk for a company in New York City with,
  • 30:35 - 30:37
    with a team of guys.
  • 30:37 - 30:42
    And, really delving into object-oriented programming,
    and learning so
  • 30:42 - 30:45
    much about it, I really learned to love this,
  • 30:45 - 30:49
    right. But what first, what first struck me
    when
  • 30:49 - 30:52
    I saw Pry and I saw the ls command
  • 30:52 - 30:57
    was it reminded me of this, right. Right here,
  • 30:57 - 31:01
    we are looking at a ZnClient class in a
  • 31:01 - 31:05
    Zinc HTTP package. We see the class definition
    right
  • 31:05 - 31:08
    there at the bottom, with all of the instance
  • 31:08 - 31:12
    variables that this class makes use of listed
    out.
  • 31:12 - 31:14
    On the right hand side, we see all the
  • 31:14 - 31:17
    methods that we can call on this, on a,
  • 31:17 - 31:20
    on an instance of class, and we can click
  • 31:20 - 31:23
    on any of those to see the source code
  • 31:23 - 31:27
    of that method. I mean, that's all, that's
    all
  • 31:27 - 31:31
    you need, as far as I'm concerned. I mean,
  • 31:31 - 31:35
    that gets you ninety percent of the way there
  • 31:35 - 31:39
    almost every, like, almost every time, right.
  • 31:39 - 31:43
    So, I wanted to, I wanted to demo, like
  • 31:43 - 31:46
    a, a Smalltalk TDD workflow, cause that's
    the other
  • 31:46 - 31:50
    really cool thing about Smalltalk and different
    thing about
  • 31:50 - 31:53
    Smalltalk is that it really, like, enables
    this awesome
  • 31:53 - 31:55
    TDD workflow. And I don't think I'm gonna
    have
  • 31:55 - 31:58
    the time to do that unfortunately.
  • 31:58 - 32:05
    But the TL;DR is this, basically. OK. So if
  • 32:05 - 32:08
    I write a test, and I try to exercise
  • 32:08 - 32:14
    a class that doesn't exist, like BowlingGame
    here, Smalltalk's,
  • 32:14 - 32:18
    when I save this, Smalltalk's gonna say, that
    doesn't
  • 32:18 - 32:19
    exist. Do you wanna, what do you want to
  • 32:19 - 32:20
    do?
  • 32:20 - 32:24
    And the first option there is define new class.
  • 32:24 - 32:28
    You just click it and it does it, right.
  • 32:28 - 32:30
    When I try to call a method on that
  • 32:30 - 32:36
    class that doesn't exist, Smalltalk asks me
    if I
  • 32:36 - 32:40
    want to implement that method, and when I
    say
  • 32:40 - 32:44
    yes, it drops me into a debugger, right. A
  • 32:44 - 32:48
    runtime, where I have everything available
    I need to
  • 32:48 - 32:50
    create the implementation of that class, and
    I can
  • 32:50 - 32:53
    expect all the things I need to do so.
  • 32:53 - 32:56
    Right, I'm not in a static code editor here.
  • 32:56 - 33:01
    This is like, living system, right. So I can
  • 33:01 - 33:04
    do so. Just want to return zero. And when
  • 33:04 - 33:09
    I save, my tests pass. So that's. That's,
    that's
  • 33:09 - 33:13
    the idea, right. And, and it, it's small,
    but,
  • 33:13 - 33:17
    you know, small changes in your user interface,
    you
  • 33:17 - 33:21
    know, can really turn, like a daunting experience
    into
  • 33:21 - 33:23
    a really motivating flow, right.
  • 33:23 - 33:26
    It doesn't take much.
  • 33:26 - 33:30
    So, when I talk about, you know, this power
  • 33:30 - 33:32
    of Pry and, and we've talked about some of
  • 33:32 - 33:36
    the plugins and how powerful they are and
    how,
  • 33:36 - 33:40
    you know, Pry is so easy to extend, you
  • 33:40 - 33:44
    know, even I can do it. And, and so,
  • 33:44 - 33:46
    I've kind of gotten the ball rolling.
  • 33:46 - 33:52
    I pushed a little code up to GitHub. It
  • 33:52 - 33:55
    defines a new command called define-it in
    Pry. Right
  • 33:55 - 33:58
    now, it's just a PryRC, and you can define
  • 33:58 - 34:00
    Pry commands in your PryRC or you can do
  • 34:00 - 34:03
    gems or whatever, and that's, that's where
    I want
  • 34:03 - 34:05
    to move it.
  • 34:05 - 34:09
    But, this is my attempt to kind of get
  • 34:09 - 34:16
    this workflow going in, in Ruby, right. So,
    all
  • 34:22 - 34:28
    right. So here it is. Here's my PryRC. I'm
  • 34:28 - 34:33
    creating a command called define-it, and then
    I'm implementing
  • 34:33 - 34:38
    - every Pry command implements this method
    called process,
  • 34:38 - 34:40
    and that is what is going to happen when
  • 34:40 - 34:43
    you call the command from within the Pry REPL,
  • 34:43 - 34:44
    right.
  • 34:44 - 34:46
    And so I'm kind of using this idea that
  • 34:46 - 34:50
    we demoed in the last demo of, when I
  • 34:50 - 34:53
    hit a name error, I just want to automatically
  • 34:53 - 34:58
    generate an empty class definition and move
    on. What
  • 34:58 - 34:59
    it will also do is when I hit a
  • 34:59 - 35:05
    NoMethodError, it's gonna generate an empty
    method definition and
  • 35:05 - 35:07
    put you right in there where you need to
  • 35:07 - 35:12
    be to implement it, right.
  • 35:12 - 35:19
    So let's take a look. So, once again. Oh,
  • 35:19 - 35:23
    let me just show you this real quick, though.
  • 35:23 - 35:26
    So here is a spec and a set of
  • 35:26 - 35:30
    tests that exercise this BowlingGame class,
    right. The first
  • 35:30 - 35:35
    test just verifies that BowlingGame is a thing.
    The
  • 35:35 - 35:38
    second test says if I ask a new BowlingGame
  • 35:38 - 35:41
    for its score, it should return zero. The
    third
  • 35:41 - 35:43
    test says, if I bowl a four then the
  • 35:43 - 35:46
    game's score should reflect that.
  • 35:46 - 35:51
    And the last test says, if I bowl twice,
  • 35:51 - 35:54
    you know, the, the score should reflect that,
    that
  • 35:54 - 36:01
    sum. So, when I run rSpec under pry-rescue,
    pry-rescue,
  • 36:07 - 36:11
    you know, it does different things in different
    contexts,
  • 36:11 - 36:15
    right. So just like before, pry-rescue's gonna
    break on
  • 36:15 - 36:20
    unhandled exceptions, just like it always
    has done, but
  • 36:20 - 36:23
    in the context of rSpec, it's also gonna break
  • 36:23 - 36:26
    on assertion failures, all right. So you can
    poke
  • 36:26 - 36:28
    around and get those fixed.
  • 36:28 - 36:34
    So, here we are. We. We've got our first
  • 36:34 - 36:41
    exception, right. It's the name error. Uninitialized
    constant BowlingGame.
  • 36:42 - 36:46
    So let's define it.
  • 36:46 - 36:53
    All right. Didn't see it, but it did it.
  • 36:53 - 36:56
    Next. Believe me.
  • 36:56 - 37:01
    All right. So next. Next, we've got our next
  • 37:01 - 37:05
    exception here right. Undefined method 'score'
    for BowlingGame. Our
  • 37:05 - 37:10
    NoMethodError. So let's define-it. You guys
    didn't believe me,
  • 37:10 - 37:14
    but there it is. All right. There we go.
  • 37:14 - 37:18
    We're in our empty method. Let's TDD, right.
    We
  • 37:18 - 37:20
    just need to make the test pass so we
  • 37:20 - 37:24
    return zero out of there. Ask pry-rescue to
    try
  • 37:24 - 37:29
    again. We got another exception here. NoMethodError:
    undefined method
  • 37:29 - 37:30
    'bowl' for BowlingGame.
  • 37:30 - 37:34
    So, let's define-it. Now I feel like we're
    gonna
  • 37:34 - 37:38
    need to keep track of a little state, so
  • 37:38 - 37:45
    let's. We want to make that the fixnum.
  • 37:46 - 37:50
    We want to return @score out of here now,
  • 37:50 - 37:56
    instead. And let's initialize @score to be
    zero when
  • 37:56 - 38:03
    you create a new BowlingGame. Let's save that
    and
  • 38:03 - 38:04
    let's try again.
  • 38:04 - 38:08
    All right. That worked. We're in our last
    test
  • 38:08 - 38:12
    here, and we've hit our first assertion error,
    right.
  • 38:12 - 38:15
    It expected nine and it got five, cause we're
  • 38:15 - 38:19
    not doing any of the, the totaling yet. So
  • 38:19 - 38:24
    let's, this is another way you can call edit.
  • 38:24 - 38:27
    You can just ask it to edit the class,
  • 38:27 - 38:31
    and it'll know what file to drop into. So
  • 38:31 - 38:32
    I feel like we're gonna want to keep track
  • 38:32 - 38:35
    of our scores now. So let's just push those
  • 38:35 - 38:41
    fixnums on a scores array. Let's get our score
  • 38:41 - 38:44
    by reducing those values. That's how you keep
    score
  • 38:44 - 38:47
    in bowling, right. You just add everything
    up. All
  • 38:47 - 38:49
    right. Cool.
  • 38:49 - 38:53
    And let's, let's initialize it with an array
    with
  • 38:53 - 38:59
    initial value of zero as well. Let's try again.
  • 38:59 - 39:06
    Something's wrong there. There it is.
  • 39:07 - 39:14
    All right. Four examples, zero failures. rSpec
    thought we
  • 39:15 - 39:22
    didn't fail at all, you know. It's pretty
    cool.
  • 39:25 - 39:32
    So. So what happened there? All right. I mean,
  • 39:34 - 39:40
    basically, you know, we. We, again, we used
    Pry
  • 39:40 - 39:45
    to drive our development process. We used
    our own
  • 39:45 - 39:48
    Pry plugin there to kind of make that a
  • 39:48 - 39:52
    little bit easier, and within one runtime,
    we fixed
  • 39:52 - 39:57
    all of our assertions, exercised our class
    and implemented
  • 39:57 - 39:59
    the whole solution, right.
  • 39:59 - 40:02
    No reloading, no nothing like that.
  • 40:02 - 40:09
    So, in conclusion, embrace your runtime and
    all things.
  • 40:10 - 40:15
    Don't read the effing manual.
  • 40:15 - 40:21
    Use a kickass flashlight.
  • 40:21 - 40:24
    If you're fixing software, use a debugger.
    If you're
  • 40:24 - 40:28
    building software, use a debugger. And when
    you use
  • 40:28 - 40:30
    a debugger, use Pry.
  • 40:30 - 40:32
    Thank you very much.
Title:
RailsConf 2014 - Debugger Driven Developement with Pry by Joel Turnbull
Description:

more » « less
Duration:
40:59

English subtitles

Revisions