Talks

Debugger Driven Developement with Pry

Debugger Driven Developement with Pry

by Joel Turnbull

In the presentation titled "Debugger Driven Development with Pry," Joel Turnbull discusses how integrating Pry into the Ruby development workflow enhances the test-driven development (TDD) process. He emphasizes that while many Ruby developers tend to avoid using debuggers, this behavior stems from the lack of effective tools prior to Pry. Joel advocates for a shift in mindset, using debuggers not just to fix software but to facilitate the software building process itself.

Key Points Discussed:
- Pry as a Game-Changer: Joel introduces Pry as a powerful alternative to the Interactive Ruby Shell (IRB), offering runtime interaction features that improve development efficiency.
- Enhanced Introspection: Pry offers advanced introspection capabilities, allowing developers to ask detailed questions about their code and object states without the burdensome effort required in IRB.
- Workflow Features: He showcases features like syntax highlighting, tab completion, and the ability to drop into a runtime at any point in the application to explore and troubleshoot code.
- Real-time Debugging: Using a demonstration of a bowling score tracking application, Joel illustrates how to use binding.pry to pause code execution and analyze live data, reactively editing and improving the code.
- Tools for TDD: Joel connects the benefits of Pry to TDD, explaining how the iterative process of writing tests, observing failures, and fixing code is well supported by Pry. He highlights a feature that allows automatic creation of method definitions and class structures in response to errors, thereby streamlining the development process.
- Conclusion: Joel emphasizes that successful programming requires embracing the runtime environment of tools like Pry. He argues that using a debugger while building software can lead to a more productive and engaging development experience, while also enhancing focus by minimizing context switching.

In conclusion, Joel asserts that developers should embrace Pry as an innovative tool in their workflow, combining it with the effectiveness of TDD to create a seamless and enjoyable coding experience. He encourages developers to leverage Pry not just to fix issues but as an integral component in their development processes, ultimately advocating for a deeper immersion in a dynamic coding environment.

00:00:17.720 Thanks for coming. My name is Joel Turnbull, and I’m a developer for Gaslight in Cincinnati, Ohio. I also head up and coordinate a blog there, in case anybody wants to talk about that. But today, I’m here to talk about debugger-driven development with Pry.
00:00:35.040 Most Rubyists I know don’t use debuggers. When faced with a problem, they would prefer to ponder code rather than pop open a debugger and poke around. To me, trying to solve a problem by pondering code is like trying to find your keys in the dark while holding a flashlight but consciously deciding not to use it. Why is this the case? I used to think it might be about egos or culture, but really, it’s pretty simple: I think we have had a lack of really good tools up to this point.
00:01:22.439 Ultimately, my talk isn’t about using debuggers in a traditional sense to fix software, but rather using debuggers as a tool in your workflow to build software. So why do I think we can do this right now? I feel like we finally have a tool that we can use to explore this debugger-driven workflow, and that tool is Pry.
00:01:50.799 Can I get a show of hands of who uses Pry? Awesome—like, everybody! Alright, cool. So, I’m talking about debugger-driven development with Pry. Conrad Irwin gave a talk not even six months ago called REPL-driven development with Pry. I swear to God, I had no idea, but I think both terms somewhat undersell the power of Pry. Here’s my favorite definition: Pry is an IRB alternative and a runtime developer console.
00:02:15.959 If we think about Pry as an IRB alternative, anything you can do with both is a REPL, and a debugger is a REPL, too. Anything you can do in Ruby, you can do in IRB; anything you can do in IRB, you can do in Pry. What makes both powerful is that they leverage the notion of runtime. To me, runtime is all about immersion—being immersed in a live system where you can play with code, look at your objects, validate your implementations, and do everything you need.
00:03:04.920 It’s like looking for your keys with a flashlight. Given that Pry and IRB are both REPLs and share the idea of runtime, why should you use Pry instead of IRB? Pry provides a couple of vital workflow features right out of the box, such as syntax highlighting and tab completion, both of which are super handy.
00:03:30.560 But what I want to talk about today are some of the bigger, game-changing features of Pry. The first one is enhanced introspection. Introspection is the ability of a programming language to ask questions about itself, and it is built into Ruby. If you’ve ever asked a class what methods you can call on it or what class an instance belongs to, you are doing introspection.
00:04:03.519 What if you want to go deeper? What if you want to know what the class methods are versus the instance methods? Want to know what methods are inherited and from where? What if you want to know what state an instance holds onto during its lifecycle? You can answer all these questions with plain Ruby and IRB, but the problem is the effort involved is non-trivial. I would classify it as daunting.
00:05:07.919 So, given that, I would classify this as DTFM—this is the workflow I prefer. I like to take the pieces out of the box, get a feel for them, play around with them, and if I get stuck, then I read the manual. The second really game-changing feature of Pry, to me, is its extendability through plugins. The best way I can show this is to demo some of my favorites. So, I’m going to demonstrate using a Rails app. Instead of starting it normally with ‘rails s,’ I’ll run it under the umbrella of Pry Rescue. I’ll show you why in a minute. Here we are—this is an app I’ve been working on during late nights. Please don’t steal this; it’s a bowling score tracker. You can push any number after you bowl, and it will record how many pins you knock down.
00:06:39.920 The first thing I want to show you about Pry, for those who aren’t familiar, is how to invoke a runtime at any point in your application where you’re running Ruby. Right here, I’ve inserted a couple of ‘binding.pry’ statements—one into my controller action and one into my template. On the lower left, we have our model. Let’s rerun it with that in mind; let’s go back to our running server.
00:08:11.679 We see that we’ve halted execution here, and we’ve dropped into a runtime. We can look around and play lines of code. Let’s run the line that sets the bowling game and then look again. When we exit from this, we’ll return to our controller starting to render our template, and we’ve hit our next ‘binding.pry.’ You can also put ‘binding.pry’ inside your ERB tags. Same drill—we can inspect bowling games, step into our method implementations, and look around.
00:08:44.360 We can go to the next, or we can continue. If things aren’t blowing up, but something isn’t quite right, you can see how handy that will be. But what if things do blow up? I’m going to spark an exception here by trying to bowl my next frame. I’m going to bowl a big nine, and I’m going to come back to the app. We’ve been dropped into a runtime because an exception was thrown. That’s what being under the watchful eye of Pry Rescue will do for us.
00:10:31.200 Some interesting things we can do here: we can call the ‘show-stack’ command from the Pry Stack Explorer plugin, and we see the whole stack here; this is like a call stack, but it’s alive, right? We can move up and down the stack and examine the state of any object at any level of our stack trace. We can call ‘CD cause’ and see if it can track down what caused this problem in the first place.
00:12:19.880 There it is; we’re right where we expect to be. The problem with frame one is true. We need to add a guard here—if we have pins on frame one, let’s render that; otherwise, let’s just render a dash.
00:12:56.110 We get feedback that this is a working implementation. Let’s copy our history using another plugin called Pry Clipboard, which copies that implementation to my clipboard. Now, I’ll edit the file where the last exception was raised.
00:14:29.460 It drops me right where I need to be. I’ll paste in that implementation, drop back in, and I’ll ask Pry Rescue to try it again. We’re back, and we’ve got our dash, so we can continue to bowl. Right? That’s legit! A five and a six, okay?
00:15:03.600 So what have we done here? We used ‘binding.pry’ to invoke a runtime anywhere in our app where we’re writing Ruby code. We used the Pry Debugger gem to give us the step, next, and continue functionalities we expect from our debugging tools. We used Pry Rescue to run our Rails app under the Pry Rescue umbrella to drop into a runtime when things went wrong so that we can poke around.
00:16:51.560 We used the Pry Stack Explorer gem to navigate the stack and explore state at any level. The few commands we saw were ‘CD cause’ and ‘Pry Rescue,’ which took us to the root of our problem. We also used ‘play’ and ‘copy history’ to avoid the hassle of copying things with our mouse and pasting them into our REPL. We used the ‘edit’ command with the ‘E’ flag to navigate to the file where the exception occurred so that we could fix it.
00:18:06.720 Then we used Pry Rescue to try again, replaying our request without having to reload our page or environment, which was fast. While I’ve demonstrated Pry in a debugger context, I find the most interesting aspect to be the idea of Pry as a runtime developer console. Ruby has many awesome aspects: it’s introspective, dynamic, and reflective, meaning we don’t have to compile, and we can change things on the fly.
00:19:40.760 I think we can take advantage of these capabilities a lot in the code we write, whether through metaprogramming, monkey patching, or otherwise. Yet, we haven’t effectively leveraged these features in our tools and workflows.
00:20:30.600 When I talk about workflow issues, I see big problems in the traditional Ruby development workflow. By that, I mean we write some code in an editor, save it, pop over to a terminal or web page to reload it, and check if it worked. If it worked, we go back to coding; if not, we fix it and repeat the process. What problems do I see with this? First, it’s disruptive and distracting to keep switching back and forth.
00:21:55.920 Second, it’s guesswork. We write code, thinking it will work, then cross our fingers and check if it does—taking shots in the dark. This seems backward; our codebase should record what we have already validated rather than the other way around. We take these things for granted, but other languages have integrated the concept of a runtime into their workflow, blurring the line between static and running code.
00:23:09.279 While nothing in Ruby restricts us from doing the same, we haven’t had the tools until recently. So, how does Pry solve these workflow problems? The introspection, documentation, and source code browsing integrated into Pry give us 90% of the information we need to write code effectively. Pry offers a runtime against which you can validate your code and receive immediate feedback.
00:24:48.050 It’s smart about editing; you don’t have to think about what file you need to open. Pry usually knows which file you want to edit and where in that file you want to make changes. When I talk about runtime development, I mean being immersed in an environment that gives feedback, is alive, and makes us feel welcome. We want to spend the majority of our development time there and consider our editor simply as a tool to push in finished code.
00:25:37.440 Let me demonstrate how I envision that working. Our mission is to write an empty class definition. This is just a script, no Rails involved. Given a class name like BowlingGame, we create a file called ‘bowling_game.rb’ and write a class definition into it: ‘class BowlingGame; end.’ I’ve created a skeleton for how this might operate. We’ll read a string from the command line and pass it into a method called ‘file_name_for_class’ to get that file name.
00:27:57.600 Then, we’ll pass the string into a method called ‘class_definition_for_class’ to get the class definition, and finally create the class by writing the class definition to that file. Let’s see what this is like. When we run it, we get an expected error because we haven’t implemented anything called ‘file_name_for_class’ yet. Instead of jumping into the editor right away, let’s use Pry Rescue to leverage that exception and drop us into a runtime. We get the same error, but the difference is we’re inside a runtime now.
00:29:44.080 The first problem is easy—we need to define ‘file_name_for_class,’ and we do it right here. I’ll raise in the implementation of this class and come back out to show you why. When we ask Pry to try again, we find ourselves right where we need to be to implement that class properly. Although I’ve neglected to pass in the ‘bowling_game’ string as I always do, now we have all we need.
00:30:43.440 We’re looking to create something like ‘bowling_game.rb’. So I’ll preview my favorite Pry command of all time—‘ls’. When I call ‘ls’ and give it the class or object I’m working with, I know immediately I’m working with a string and can see all the methods available to me on that string. Importantly, only the specific methods are shown, excluding object methods, which is great.
00:32:38.560 Now, we need to use ‘underscore’ to convert our string to the correct format for the filename. We include ActiveSupport and call ‘ls’ again on our class, and we have some extra methods available. We’ll use tab completion for ‘underscore,’ and once we add our file extension, we should be done. Let’s copy that, drop back into our file, and place the implementation where we need it—let’s save and keep moving.
00:33:59.040 In the interest of time, I won’t continue through this entire process, but I want to implement the last method inside ‘create_class’ to discuss what I love about introspection. Essentially, we leveraged Pry to assist our development process, using ‘Pry Rescue’ to navigate through raised exceptions. We raised an exception inside our ‘create_class’ method; when we tried again, we acquired our file name and class definition, creating the file class.
00:34:55.680 I’m aware of this file class, and I know it’s precisely what I need. In moments of uncertainty, I can pop out to Stack Overflow or Ruby docs, or I can perform ‘ls’ on the file class to review all the methods available to me. In a matter of seconds, I can judge what I need to know through Ruby’s introspection capabilities.
00:36:12.480 After exploring the write method and realizing it might not fit my current needs, I check the documentation to understand it better. I see how the write method functions. Let's give it a try by calling ‘file.write(file_name, class_definition),’ and we’ll check if it works. Following that, I’ll run the test suite again to make sure everything’s functioning as expected.
00:37:21.040 We’ve successfully implemented all components of the application—utilizing Pry once again to streamline our development process. We explored, ensuring everything was functioning as intended without the headache of context switching or reloading environments, allowing our workflow to flow seamlessly.
00:39:29.680 In conclusion, I encourage you to embrace your runtime in all things: don’t read the manual, use a great flashlight for debugging, utilize a debugging tool when building software, and most importantly, use Pry.