 Right. This is extremely defensive coding. Let's get started. Phil introduced me, but to introduce myself, my name is Sam Phippin. I'm Sam Phippin on Twitter, Sam Phippin on GitHub. You can check out my profiles there if you're interested in finding out more about me. If you do check out my GitHub profile, you'll find that I spend most of my time on GitHub committing as a member of the RSpec core team. And that's one of my driving motivations why I come to conferences like this. I think it's really important that RSpec is well represented in the community at large. And I love hearing questions and taking feedback. So if you have anything you'd like to say about RSpec to someone who spends a lot of time working on RSpec, you should take this opportunity to do so. If you see me walking around the conference, just feel free to grab me and let's have a chat. I love talking about this stuff. I work for a company called fun and plausible solutions. We're a consultancy based in the UK. We focus on testing and refactoring Rails apps, doing object-oriented design and testing training, machine learning, stuff like that. And if that's interesting to you, please do come and have a chat with me. I'm a little bit ill. Maybe you can hear it in my voice. And so this talk might sound a bit spaced out. It's just because I've had a lot of medicine, but I'm sure it'll be fine. Like a lot. So with all of that sort of front matter sorted, let's actually talk about stuff. So I like to start this talk with a story. And the story actually comes from a Q&A session from one of my other talks about the internal mocking architecture of RSpec. As the questions came and I answered the deeper technical details that the senior developers in the room were interested in finding out about, I was left with a few hands up. And I got a question that absolutely floored me, that it wasn't easy for me to answer. And this question came from one of the junior developers in the room. Now, the thing that I found interesting about this is that it was relatively easy for me to dispatch with the questions from the more experienced developers, but a more sort of genuine question from someone who is newer in our community genuinely stopped me. And this is one of the reasons I absolutely love working with junior developers, because they force you to make explicit things that you have in your mind that you may not be able to fully articulate until they ask your question. And the question was this. It was when is it okay to override methods that Ruby provides on object? The basic thesis of my talk is that RSpec is complicated because we have to do things to defend against changes users make to object. And I basically said that this is why it was hard and that you really shouldn't do this most of the time, except most is like a wiggle word. It doesn't give you a useful definition of when to do it and when to not. And when I was asked this question, I couldn't immediately provide an answer. I sort of ummed and awed. I walked around. I gave half explanations. I wasn't really sure I was doing the explanation justice. And so what I did is I stopped and I took a breath and I really thought about it. I tried to come up with an answer that unified all of the examples and half explanations I'd given. This is a really useful exercise. And again, it's one of the reasons I like working with junior developers, because they force me to explain what I'm talking about. The answer that I gave came to do with consistency. The idea being that you should override methods on objects precisely when they make the object more consistent with Ruby, not less. And to give an example of this idea, what I mean when I say more consistent is that there are things in the Ruby standard library that override methods provided by kernel that do so to make them behave more as you would expect them to do. A good example of this is equals equals on a data object, like a string or a collection. Because in that case, comparing object identities, which is the default behavior on object, doesn't really make sense. And instead, comparing the values inside the collection is going to be a lot more beneficial for everyone involved. The basic idea here is that by overriding equal equal, you've changed some behavior on object, but you've done so in a way that makes the object behave more obviously with all the other objects in the Ruby system. And I'd like for you to hold on to this story as I go through the rest of the talk, and we'll come back and visit it at the end. So I want to pose it the question, what makes a gem good? Well, immediately, your mind might spring towards the internals of a gem, the core pieces of functionality that it provides that actually make it do what it's going to do. But as I think about this more and more, I find that the internals of a gem don't really matter, and it's the interface that a gem provides that really makes me care about what the gem is going to do. And the reason for this is that I would like to think I'm a pretty capable Ruby developer. And if that's the case, then I can basically implement anything a gem is going to provide me given enough time and resources. And so if that's true, a gem has to be more convenient than if I'm simply going to implement the same thing myself. If I do an implementation myself, the code is going to fit in nicely with the existing architecture of my application. It's going to work how I want it to, and I'm going to understand what the API is. In order for a gem to be more convenient than if I did it myself, it needs to meet a number of criteria. One of them is that it's going to stay convenient as my application continues to grow and continues to get built upon. If, like, a gem fits in immediately when I use it to get something done, that's great. But if in six months I can't really modify my application anymore, because I've got to get the code on to a gem, that's going to be highly problematic. It's reasonable to assume that most of us work on teams made up of diverse skill ranges. And if that's the case, then if a gem can only be used by developers who really, really understand what's going on, it's not going to be anywhere near as convenient as if it's a gem that can be used by everyone on a team. And so that's a really important feature for a gem to have. I also think gems need to work with a wide range of Ruby code bases. Not everyone in Ruby is just building Rails applications. Not everyone in Ruby is just running on MRI. And so, really, a gem needs to fit, like, with all of these different crazy things that I'm supposed to do. Now, that's a fairly large shopping list. And so it's only fair that I apply that to something that I work on and use every day. Now, let's talk about RSpec. Well, I could tell you that RSpec is definitely always convenient, but that would be an abject lie. I'm sure that everybody in this room has at some point encountered something in RSpec that has made them absolutely frustrated, angry, want to smash the computer, log off, and go home for the day. I know that has happened to me. And so, if that's the case, then, well, we actually need to look in some detail at some pieces of RSpec to see how they fit in with this story. And in order to do that, I'd like to explore a sort of user story that we have from inside RSpec. Now, I'm sorry. I know it's only 9.49 in the morning, and I'm about to get really agile on all of you, but I hope that's okay. So, my user story goes like this. As an RSpec user, when I stuff an object, I want the original method to be on that object after the example so that my objects aren't broken by my test suite. Now, that was a little bit fast. I'm sure not all of you are experts in the language of mocking and stubbing inside RSpec. So, let's go through that one more time, just a little bit more slowly. As an RSpec user, that's about 70% of you in this room if the Ruby survey is to be believed. When I stub an object, that's taking a method on an object and replacing that method's implementation with a simple RSpec stub implementation to make it easier to reason about what that method is doing in your test. I want the original method to be on that object after the example. So, this is when a test finishes executing, RSpec puts the method back on the object so that the object is complete and as it was before the test began executing. And the reason for this is that nowhere in your production system are you going to have RSpec stubs floating around objects so it doesn't make sense in the beginning of execution of a second, third or fourth test to also have RSpec stubs present on your object. So, basically what we're talking about here is RSpec moving methods around the system in order to provide you convenience when you do stubbing or mocking. Well, you might reasonably ask how does that work and just to clarify, we're talking about this syntax from RSpec, the allow some object to receive some method syntax. In this case, we're talking about allow cat to receive meow. Well, what this is going to do inside of RSpec is it's going to take the meow method off cat and save it somewhere else in the RSpec system. It doesn't really matter where. That's not important. Your test is then going to execute and the method is going to be put back on the object when the test is done executing. And in this case that means that the meow method will have its original implementation on the cat object when your test is done executing. But the question then becomes how do you save a method in Ruby? I suspect not all of you spend all of your time moving methods around the system as if they were data. That's a fairly odd Ruby use case that one doesn't usually encounter when building a Rails application. If you look in the standard library documentation for object, you'll find this piece of lovingly crafted HTML on the Ruby website. It says method is a method which takes a symbol and returns a method. The literal doc string reads looks up the named method as a receiver in obj returning a method object or raising name error. The method object acts as a closure in object instance so instance variables in the value of self remain available. Now that's not totally clear. But what we're talking about here is something called a method object. In Ruby, every object that inherits from kernel has a method called method on it. When you invoke the method method with a symbol, it returns a method object. Method objects represent a single method on a single object somewhere inside the Ruby system. You can move them around as if they were any other object and this basically allows you to treat a method as data. Method objects have a single public method called call. When you execute the call method, it's like directly invoking that method on the object. This basically gives us exactly what we need in order to implement our feature in RSpec because we can take the method object, we can put it somewhere else and we can put it back at the end of the test. Now, I could tell you that that's absolutely everything you need in order to implement method saving in RSpec. Unfortunately, that's not true. And in order to tell you the truth of the matter, we need to talk about something that I'm going to label defensive coding for the rest of this talk. We're going to be talking about a single method from inside RSpec called RSpec support method handle four. This is basically our equivalent of the method method except that it actually works. Invoking the method method isn't good enough in a lot of cases and what we're going to do is quickly go through the get history of the evolution of this method. Now, some scenes have been altered and sequences have been chopped and changed but this is a approximately true representation given that I have slides and not the ability to travel in time. So this is the first implementation of RSpec support method handle four. You'll find it anywhere inside the RSpec get history. It takes an object and a method name and it directly invokes the method method on that object with the method name. This is just calling the method method and as I said is unfortunately not good enough. One of the reasons it's not good enough is that users lie. How many of you have ever had to do anything to do with HTTP in Ruby? I know that's a bit out there but bear with me because you see, if you look inside basically any HTTP library in Ruby, you'll find a definition that looks something like this where we define a method method that returns like get or post or put as a string or a symbol. And that's fine. The word method means something in HTTP and people should be allowed to define that method. Unfortunately, users can and will redefine anything out from under your feet at any time for any reason in Ruby. This is a problem because the method method no longer actually returns method objects and we're screwed. In order to solve this problem, RSpec evolves to look like this. We create a constant called kernel method method and into it we assign the result of the expression kernel.instance method method. The instance method method is like the method method except that instead of returning method object, it returns instance method objects. Instance method objects are like method objects except that instead of having a particular instance bound to them, they're the pure implementation of the method from the class. Basically it's like a method object but it doesn't yet have an instance or instance variables to be applied to. It's the method object without the target. Instance method objects come from classes or modules, not object instances. Basically what the instance method method allows you to do is grab the implementation of any method from any class without having a particular target in mind yet. Basically what we've done here is we've got the kernel implementation of the method method and we saved it. The reason for this is that when we invoke method handle four, we can rebind the kernel implementation of the method method to the object, call it with the method name and get our result. Basically we're using kernel's implementation of method which always works. The key principle to take away here is that users can and will redefine core methods at any time for any particular reason in Ruby. This is a thing that Ruby explicitly allows. So instead we're going to use kernel's implementation of stuff because nobody screws with kernel. The problem here is that Ruby interpreters lie. Now this might be a slight stretch but some objects don't have kernel in their inheritance chain. Now like the hardcore Ruby object system people in the room will have immediately jumped to this idea of basic object but that's not exactly what I'm talking about because you see in the Ruby standard library some objects inherit from basic object and then instead of including kernel, include a jupe of kernel. What this does is give you all of the methods off of kernel without actually having the thing kernel in your inheritance chain. If we take our existing implementation of method handle 4 and we invoke it, this all seems fine until we switch to JRuby. When we switch to JRuby we get this exception which is bind argument must be an instance of kernel. What we're doing here is called rebinding module methods and this is a thing that is meant to be allowed in all Ruby interpreters greater than or equal to version 2 except sometimes that just doesn't work and the thing about RSpec is we have to support basically every Ruby interpreter on the planet. We support 187 and up all versions of JRuby and even REE. How many of you are still running REE in production? The answer should be zero. I can't see any one, I can't see any hands because of the lights but it's zero. RSpec does not support Rubinius and that's basically because Rubinius is not a working implementation of Ruby. That's not meant to be a slight on the Rubinius team, implementing Ruby is hard and RSpec is a stress test for Ruby, so whatever. But if you want to make RSpec work on Rubinius, that would be a cool thing for you to do as an open source person. We also support Windows. This is a Windows CI on the cloud that's a bit like Travis. It's kind of janky but it works and so I just want to show that we're awesome and we support Windows. The point is we try really, really hard to work with every Ruby interpreter there is and that's great but this comes to be a problem when you have to support things like rebinding module methods because in order to do that, you have to do this. Basically what we have here is a conditional definition of methods depending on something that we know at Ruby interpreter boot time. This is a performance optimization. We could put the conditional inside the method but we don't need to. Basically this Ruby features support rebinding module methods tells us whether or not we can actually do the thing I just said and the answer is if we're running on an MRI, the answer is the Ruby version is greater than or equal to 2, otherwise we create a module, pull an instance method object off it and try and bind it to an object that doesn't include that module. If that doesn't blow up, then we know the answer is true, otherwise the answer is false. Coming back to our definition, if we do support rebinding module methods, then we can simply use the kernel rebinding trick, otherwise we have to check if kernel is in the inheritance chain of the object and then if it is we can do the kernel rebinding trick, otherwise we have to call the method method. Ruby interpreters behave differently and this is kind of a problem when you write a gem that has to support literally everyone. Just to show you this is real, if you look up simple delegator out of the standard library, it totally doesn't include kernel, it totally includes a dupe of kernel. Sometimes users don't lie. This is where it gets a bit ridiculous because what we have here is a class that has a single public method and another class which says that basically it responds to everything instances of that other class respond to and it returns method handles from everything that other class responds to. The important part here is that that definition of the method method is valid. It always returns a method handle, just sometimes not from its own class, sometimes from a different class and that's fine. That's the thing you're totally allowed to do in Ruby and should be supported. Unfortunately, the kernel implementation of the method method doesn't know about this conditional and so if we invoke our existing definition of aspect support method handle 4, we get this exception. The problem here is we can't use the user method and the kernel method doesn't work. It's a catch 22. So the solution that we came up with here is what I like to call trust but verify. It looks like this. Basically, we start by trying our kernel method rebinding trick and if that doesn't work, we invoke the method method, we check if the method handle returned is actually a method handle. If it is, we return it and otherwise we raise the original exception. Putting this all together, it looks like this. This is the implementation of aspect support method handle 4 as it exists in aspect today. It is the most reliable way to pull a method object, often object in Ruby I've ever seen. It supports basically any user object that has ever been thrown at aspect from any gem anywhere in the ecosystem. And it works. To wrap up, this is one of the reasons why I love aspect. Users who do defining the method method, defining it to return method handles and working with objects that have broken inheritance chains are all supported. We will go to incredible, painful, extreme lengths to support your Ruby code with our test framework. I like to say that if you can express it in the aspect DSL, it should work for you. That is an incredibly high threshold for us to work towards, but it is one that I want to. So please file bugs. If you think aspect is not working for you, it may be our fault. It may be like some arcane weird bug somewhere deep in our engines. And even if it's not, it probably means there's a problem with our documentation, which is also something we should fix. If you're going to write a gem, it needs to be defensive. Users lie. Redefinitions will come from your feet. People will stub, respond to and do even more mad, mad stuff. But we can support it. Your code is going to run on every Ruby interpreter under the sun on platforms you've never even dreamed of. Be ready. Ruby uses a strange, strange people. I think that should be obvious to you. But that's fine. That's what makes us great. We're weird, but we do weird things with our software. Making a gem that works with all that stuff is really, really important. At the start of this talk, I posited the question, what makes a gem good? Well, what about the internals? I would argue the explanation that I just don't really make sense. And that's fine. It doesn't matter. The internals aren't what's important to a gem. Let's talk about the interface. You just type allow cat to receive meow. And you get all of that code springing into life basically for free and more beyond it. That is not the most complicated part of RSpec I could have shown to you. It's the complexity of RSpec working with basically any Ruby object anyone can define for free. It's hidden behind our well-defined interface. You probably didn't even know all of that nonsense was there until I just showed you. It's the interface to a gem that really, really matters in order for it to be really good. Let's talk about gems. If you can hide code in order to provide some useful abstraction, then a gem is like a super object. It's like a really big barrier that you can use to hide lots and lots of code behind to provide really big useful abstractions. Sandy Mets likes to say that the wrong abstraction is one of the most expensive things you can have. And so done badly, that means that a gem is going to cause an extreme mess in your application. You can hide huge amounts of complexity behind really well-defined barriers, and defending against the crazy stuff that your users are going to do is going to help your gem be really convenient for them. Basically, make it so that your users never have a reason to look what's inside the box, to never go diving into the source code of RSpec Support Method Handle 4 and you're doing a really good job. If you remember the story at the beginning of this talk, well, that question that beginner asked me is like born out of Ruby's power. Given that users can and will redefine any core method out from under your feet at any time for any reason, we need to think about the implications of that in the software that we write. We need to oh, that's to make sense. We need to change our code to make good against the mistakes we're going to make tomorrow. So that's all I've got. Thank you very much. Let's have some questions.