Seeing Metaprogramming and Lambda Function Patterns in Ruby
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Nimmo, Lukas
Metaprogramming and lambda functions in Ruby should be explained within their rightful place: living examples. You may have read tutorials on what these concepts are, but still do not understand when or why to use them. I dredged through over 50 prominent Open Source Ruby projects to bring you ten successful patterns that are used time and time again to create some of the most expressive and popular Ruby DSLs. Join me as we cover these patterns so that you can immediately begin using them in your own code to implement powerful DSLs. No vacuum-living, esoteric concepts here.
 Hello everyone, my name is Lucas Nimo and if you have any comments or questions and you want to contact me afterwards, I have my email up here. So I'm actually a geologist by education and as a geologist we study minerals and assuming that we study minerals, so I'm here at RubyConf. But usually when I meet people and they tend to have this very romantic view of geology and that you're climbing these highest heights and grabbing some samples along the way, check out these awesome rocks or check out these awesome formations. But in reality it looks a lot like this. It's very exciting I know, looking under a microscope. And also very, in one particular area of geology you also tend to study a lot of these. Does anybody actually know what these are? Core samples, right. Exactly, and what they do is if you study these enough you get a nice little graph or chart that looks like this. It gives you an idea into the past environment of what's going on. And it might look something like this. So right now you're probably wondering well I'm at a RubyConf in Swire you're talking about rocks. And that's because there is a very nice parallel between programming and what's going on in geology right here. If you were to only look at about five meters of this chart you're not going to be able to see the whole picture. And the same thing in Ruby and metaprogramming and Lambda functions we have all these techniques we can use but we tend to get lost in the trees and we can't see the forest. We can't really see what's going on. And hopefully I can help with that. And I want to start off by saying Ruby is beautiful so our DSL should be beautiful. This is not a rip off of many swans. This has a very ugly acronym I promise. But I think that our DSLs are Ruby in general is known for being a very human readable language. And I think our DSLs should live up to this reputation or even match this reputation of being readable yet extremely powerful. And this talk is going to go over some of the common ways developers go about using the dynamic nature of Ruby to achieve these DSLs. And I went through many popular libraries and I categorized or listed each time they would use metaprogramming or Lambda functions and then I categorized them even further into use cases or best practices. And the libraries that you're going to see here are representative of the other libraries that tended to use that same pattern. And when we design interfaces in Ruby we tend to ask a few questions like who are we designing for or what do our users want out of our library. Well they want to be able to use what it's meant for and they want it to be very easy to use. And that's not to say that the implementers love paying it's just the Ruby way of making the user experience very happy. And one last stop before we move on. I want to talk about paying for a second. Everyone knows what the paying in the middle is right. That's Starry Night by Van Gogh. But the question I want to pose to you is would this painting have been as famous or as influential as Van Gogh had used a style on the left which is a Monet or the style on the right which is a $300 million painting believe it or not. And probably not, right? He used a very specific style for a very specific reason. And what you're going to see throughout this talk is that many of the patterns or the techniques that are used by different libraries were used to achieve a certain aesthetic design. It's not required. Your interface can actually be very ugly if you want it to. But that's not the Ruby way, is it? And the roadmap we're going to choose to go through this problem is we're going to talk about some goals that we'd like and then we're going to talk about the problems associated with reaching those goals. And then we're going to look at the public interface that actually satisfies that. And then we're going to dive deep a little bit and look at the implementation. So code heavy, beware. And the first case we're going to talk about is trust issues. I don't mean to bring up any emotions with you guys, but developers tend to have trust issues, right? We tend to code defensively in such a way that we want to prevent others from making mistakes and from ourselves making those same mistakes, right? And one way we can approach this is through configurations, right? We want to be able to provide configuration options while handling quality assurance. And a way you can do this is a singleton. That means every time that a user interacts with an object, it will have this global effect versus a very localized mess. And moving on from this, we can look at some typical ways that you would actually set up an object or library, right? Every time you interact with a library, there's always some interaction involved. And this is probably the simplest way you can do it. You have a config object,.new, very explicit, very simple, it's very obvious what's going on, and you pass in five values. Now one of the problems here is that five values is quite a lot to remember. And it could be even more depending on the amount of options that you provide. And I as a user don't want to have to flip back and forth between documentation every time I mess with this stuff. And not only that, we can't rely on defaults because we have to pass in a value every time. And moving forward again, this actually solves both of those problems. You don't have to provide a value for every single configuration option, and you don't have to rely on order. But a problem that you run into here is that a user or someone else you're developing with can set these values up pretty much anywhere they want. So let's look at a way, before we review this, is we are just looking at providing configuration options but still handling the quality assurance for the user. And Rails actually does this with config. Config sets up configurations for multiple environments, and all you do is you pass in a block, and you evaluate in that block this config variable that's passed in. And the magic that happens here is you have a config module, but in it, it's this yield self part. Yield self is actually the singleton class of the config. So now, every time that you operate on this variable in this block, it's actually operating on the singleton. So no matter where the user sets this up or when this set this up, it's always going to revolve around the singleton. You're saving them from future mistakes. And here's another way. Here's another version. This is actually a hash, and Geocoder actually takes addresses and converts them to longitude and latitude. And you just pass in this hash, but underneath, as we'll see, it gets passed to this instance. And this instance is really just a singleton, right? You're just passing in this hash, it merges those options onto the singleton. And it's a way of secretly forcing the user to use a certain interface. And you're probably wondering, well, which one should I use, right? If both of these satisfy our conditions, we have a block and a hash. I'm of the opinion that the block is more Ruby-like. You pass in an object, you work on that object, you do some stuff, right? But on the right, you're passing in a hash. And there's a lot of redundancy that doesn't really add to the intention of what's going on. You have these colons, these hash rockets, these commas. It doesn't really add anything. But when you look at the config.setup on the left, every time you operate on that config variable, it's intentional as to what's going on. You're explicitly saying, okay, I have this object, and I'm setting a configuration value on it. And moving on to case number two, which is situational language. I'm going to define, offering situational language, I'm going to define it as context-specific method names that rely on one implementation. We want a flexible interface for our library. So we may want to offer multiple method names, but they still rely on one single implementation. Here's what I'm talking about. Here's a typical way you would create flexibility. It's very iterative, you define a path, or define a pinpath method, passes in a path, and then it pushes that onto a pass variable. But if you have several paths, it makes sense to pass in an array, and then you smash up that array, and then you push it onto the pass variable. Now the problem with, there really isn't anything wrong with this, is there, but it's repetitive. And it would be nice if we could actually get both of these things at the same time. And yet, still offer this situational language. One implementation, and here's two libraries that do this, Hike, allows you to find a file within a set of paths, and RSpec, obviously testing framework. But if you see here, both of these instances actually, behind the scenes, rely on the same method implementation. So a pinpath and a pinpath relies on the same implementation. And so does a hash including and a string including. And this is not Hike right here, this is, they actually offer a condensed version. And this is what it looks like. They define one method name, but then they alias that method name to say, hey, I want to give the user the opportunity if they want to pass in, you know, one value and use a pinpath if they're like. It makes more sense. It's context specific, it's situational language. And some common situations in which you're going to see this are singular versus plural method names like we just saw, the path and paths. And another example of this is synonyms with RSpec. So RSpec's alias matcher, alias is the method and it does a few other extra stuff for them, documentation wise. And, but as you see here, you can actually use a string including on a hash because it relies on the same implementation, but they're allowing you or providing this flexibility to use it if you want to. Which, and one more option is RSpec has a very human like readable interface. So it may make sense to make verb tenses different, satisfy versus satisfying, depending on the case. But one of the cons is cognitive load. Yeah, and you may feel, or if you're using this library especially for the first time, that you may feel like you need to know all of this information and it may feel overwhelming, but that's not really the intent of the developer. The intent is that it's there if you need it. And as you get used to it, it actually becomes very nice as sort of a documentation tool as to what the object you're operating on or the amount or number of stuff you're passing in. And case number three is going to be leveraging APIs. And so our goal is going to be leveraging the generally accepted meaning of existing APIs. What do I mean by this? Every time you use Ruby, you are using the public interface that Matz created to solve problems. And in a way, you're inheriting his design. And so whenever you inherit code elsewhere, you're inheriting their design, no matter what. And you're going to decide whether or not you're going to change the meaning of the design that you've inherited, whether you're going to keep the generally accepted meaning or if you're going to leverage it. And Polyglot is a library that allows you to load Ruby files with non-Ruby extensions. So it's a custom loader. And here's how they might do that. They open up the kernel module, they define a Polyglot require method, and they do some awesome Polyglot stuff, and then they wrap that around the require method to handle normal cases. So in your code, it looks something like this. Polyglot require stuff.weird. Again, they are adding to the existing API. Require means something to a developer, right? It has a generally accepted meaning within the Ruby community. But what they've done here is they've just added to it. They've just wrapped behavior around it. They didn't actually change what it means to require a file. And here's their implementation. Actually, this isn't around alias. What's going on here is the alias, the original require, with Polyglot original require. And then in their require method, they rely on the original implementation. And this is what it looks like. You can require stuff.weird with these strange non-Ruby extensions. But the older version is still directly available through Polyglot original require. And even though in Polyglot, they actually do handle normal use cases, so they actually did not change the meaning. And you're probably wondering, well, does this have some drawbacks? And the answer is yes. This was actually common in Rails for a long time, and you actually got it through something like this, alias method chain. And what it does is it takes the original method called foo, and this is going to be the new method that doesn't have a certain feature. And foo is actually going to have take on some extra feature. And so that method is going to be foo, and you do that through alias method chain. The problem is that if foo with feature relies at all on the original method name, you're going to get infinite recursion. And not only that, you're going to get extra method names in the namespace that you're probably not going to use. And if alias method chain actually gets called twice, the original implementation is essentially cease to exist. You can't call it anymore. And this has actually been deprecated in Rails in favor of it being offered in Ruby. And you do that by defining some behavior in a module. I have a method called foo, and it relies on some method that I am going to end up inheriting. But the magic is here, prepend. What it does is it takes module, new behavior, and it inserts it in the ancestor chain below my class. So now foo calls the foo, sorry, the foo and new behavior relies on the foo in my class. So now you don't have all these extra method names, and you can just rely on Ruby's object model to accomplish the same thing. And case number four, declarative DSLs. This is going to seem not meta-program E at all. And our goal is actually going to be declarative DSLs for setting up classes in your domain. It's going to look like normal Ruby, and it should, right? We use these every day. It's called adder accessor, adder reader, adder writer. And it feels like it's part of the Ruby language. But it's not. It's just a method, right? It just feels like it is, because it fits so well within the domain of the problem that you're trying to solve. And not only that, but we want to automate some sort of common functionality for class setup, and we'll see what that means right here. Rails does this with ActiveRecordBase. A user is a model, but your model can actually be anything, right? It can be a book. It can be a user. It can be anything. But what ActiveRecordBase does is think of it like a platform, sort of like a Raspberry Pi, where you have this base that you build up on top of, to where you can actually have a T-brewer or a video game emulator. Essentially, just anything you want, just take your pick. But that's what ActiveRecordBase is doing, and then it offers optional functionality to build on top of that, so that when you interact with ActiveRecordBase, you just say, okay, I want this, and then it executes that functionality. I don't care how it does it. We're very familiar with Hasmany, right? It's very common. And it's a very decorative statement, right? You say, I want this, and it gives you that. But there's a lot under the hood about what's going on. But it's also so simple at the same time, because class macros are just class methods. They're just executing a problem that feels seamless within the domain in which you're executing. ActiveRecordBase deals with databases. So it feels natural that Hasmany should express a very natural one-to-many relationship. And it does that with belongs to, Hasmany, several cases. And the implementation is actually not so difficult, right? It's just defining a Hasmany method way down here, and up in the code. This is in the netters of Rails. But this functionality is defined on a singleton. It's that simple. It's just building up the language of your DSL. And anyone that inherits from your base class, like a framework, like Rails does, that class now inherits and can use some of that functionality. In case five, domain switching. Our goal here is going to be defining behavior in a familiar domain so that you can execute that behavior elsewhere. And a better way to look at this is right here, Sinatra. So this helper's method is actually defined on the top-level namespace. But you can use this bar method very, very far away from that, right? You can use these in views. And this is what we're talking about. Underneath Sinatra, somehow it's allowing you to define behavior on this top-level somewhere familiar and execute that elsewhere that is less familiar. You're making it easier on the user. It's the Ruby way, right? And every time you call a method on the top-level, it gets sent to base, which gets sent to templates. But the magic is here, really. Class eval, that block that you send to helpers gets executed inside of some sort of context, class eval, which is executed on base so that when your view calls render, you now have access to these methods so that each time you define these helper methods, you don't have to define them specifically in a view context. You can define them on a more familiar one in a top-level domain. In opening doorways, we want to give the user, if we think of a library as a house, and as that if you build a house, I've never built one. But if you would build a house, you would not put a door on the second story because the only way you can access it is through a ladder. We want to allow many entries around the same structure of our library. We want to build a big, beautiful door. We want to allow people to easily enter. And here's clockwork that executes applications at specific times. It all does. And here's a class that includes clockwork. And here's a way that you can access this manager of clockwork by scoping in and accessing it. But the problem is, actually, that's a little wrong. Sorry. It should be directly through the class. What we want is to be able to execute manager, access manager, directly off the instance. We don't want to have to go through this roundabout way of accessing something that a user is going to commonly want to access. And here's Ferde. Ferde is an HTTP client library. So it makes sense that we're going to often be making Git requests. And here's a way you can do it. You have a Ferde module. You scope into this connection. You create a new connection. And you make a Git request. And you have a Ferde module.com. But again, you're going to be making Git requests all the time. So it should not be necessary that the user has to scope in to some sort of class in order to access it. We want something like this, directly off the main module. And if you notice, in each of these examples, we're trying to avoid scoping in somewhere else within our namespace to execute. We want to make it friendly and easily accessible. And so again, this is where the many entries comes from around the same structure of our library. And here's Clockwork. This is what happens when you include Clockwork. First you can access it through the module itself. But you can also access it through the class and through the instance. It's pretty nifty. And here's how it works. It's like a lot of code. But this is where it happens, really. There's an included hook so that whenever a class inherits Clockwork, that class comes into the hook and it includes the methods on the right. So we get the instance methods. And then it extends those methods so we get the class methods. But also, Clockwork itself extends these methods. And that's where we get the instance method, the class method, and directly off the module itself. And if you look here, what we're actually doing is we're taking a singleton or a class method and we're trying to execute it in many different ways. So that now everything we realize, everything still is around this manager. Everything still revolves around the Clockwork module. And here's Fairday. This is what we were just talking about earlier. So two different ways you can do the same thing. But they also offer a simpler version, or here's how they get the simpler version, is instead of creating a new connection every single time, we just want to say, Fairday, get me this request and do it for me. And how it does that is there is a method missing on Fairday, Fairday module. So when you send a method there, that method then gets delegated to this new connection here. So every time that you send a request through method missing or executed method on Fairday, it's actually sent to this default connection. It creates a new connection for you. Again, this just is all about making the user happy. And case number seven, dynamic building. We want to be able to dynamically build objects with the broadest interface possible. I know it's a very broad definition, but here's an example. So when you build objects, this is an example or a way that you could do that. You have a library. You create an attribute called name, and then I add a value onto my name. I have to pass in that attribute every time if I'm going to associate a value with it. And then I can yield some sort of value off of that. The problem with this, I mean, there really isn't any problem. It's pretty explicit. But if you're going to have 10 or 20 of these things, it's going to get pretty repetitive, right? And not only that, you're going to have to associate each one of these attributes with every single value that you pass in. It's not dry. And the goal then, and this is where we're coming up from, we want to avoid the explicit method calls by mimicking Ruby. If we mimic Ruby, we can already leverage Ruby's existing interface. Sorry, and which I think is good enough. And here's an example of that actually happening. Here's JBuilder, and it revolves around this JSON object. And each time that you want to create a name, you send it a method call. And then you get something like this, right? You send in a block, and then you send in a value. It just depends on what type of JSON object you're building up. And the internals look like this, though. So every time you send a method call to this JSON object, it gets sent to method missing. And if there's a block, it executes a certain statement. But if there's a value, it executes that. It's mimicking Ruby. You're just mimicking Ruby. It looks like a method call. And here's Paint. Paint manages terminal colors for you. But you can actually create your own schemes. And how you do that, and this is a pretty cool trick, I thought, kind of clever, you scope into the shortcuts constant, and you create your scheme as a symbol name, and then you pass in these symbols that are associated with some values. But now you can access your scheme like this as a module and as a singleton method. What happens behind the scenes is my scheme is now a module. It gets converted at runtime into a module. And here's Paint's implementation. It all has to do with const missing because your scheme does not exist until runtime. And when it's sent to const missing, it takes that module and it evaluates that module on Paint. So it creates a module for you at runtime. And it's not only that, but it takes each one of those symbols that you passed in as a hash, and it makes some singleton methods. So now you can easily access your scheme like in the top right. But what is the pattern, really? Why are these so easily able to understand what's going on, even though they have probably the broadest interface possible? And that's because they look like Ruby. I know I've said that a million times, but I really want to hammer that through. And we're just leveraging an existing interface for our own. We could have used explicit method names like create attribute, add value to that attribute, but it's very redundant. We can just use Ruby. And this brings us full circle. And we've examined several patterns that have to do with metaprogramming and lambda functions. So hopefully you've been able to see how these are actually used in real world examples like libraries, and that they don't actually live in a vacuum. They achieve a certain interface, and clean interface is at that, but they also reduce redundancy. I've never heard someone say, man, I wish Sinatra made me write more code, right? It never happens. But also another thing about these patterns, you shouldn't just wake up one day and think, okay, I'm going to use the observer pattern. It doesn't work like that, right? You wake up and you wait till you have that problem. And later it arrives organically, and then you think, wow, I watched this guy, this geology guy, he's wearing all blue, strangely enough. And think about one of these patterns. And they say that you only remember the first, like the first five minutes and the last five minutes. So if there's one thing I want to leave you with, it's the situations, I just want to go through these situations again, in which you should recognize and think, yes, this slide, this is what I want to remember. And you're going to start off with trust issues. If you're trying to force a certain type of public interface underneath and make sure that it's global in its state, think singleton. And situational language, if you want to create a lot of flexibility for a certain context, think alias method, if you want to leverage existing APIs again around alias, don't know how much you're actually going to use that. But since we have module pre-pen now, but it's still out there. And declarative DSLs, think class macros, think I want to allow my user to use some sort of functionality that's predefined. And they just say, I want this to happen, and I provide that for them. And switching, allow your user to create some sort of functionality and a domain that's familiar for them, like the top level namespace, and then allow them to execute that elsewhere in a less familiar domain, like a few. And opening doorways, think hooks and method delegation, think about making your interface seamless in a way that they commonly interact around the namespace library. And lastly, dynamic building. If you ever want to build an object, it's probably the way you want to go. You're probably always going to use method missing and const missing. And that's it. Questions. All right. Thank you.
 Hello everyone, my name is Lucas Nimo and if you have any comments or questions and you  want to contact me afterwards, I have my email up here.  So I'm actually a geologist by education and as a geologist we study minerals and assuming  that we study minerals, so I'm here at RubyConf.  But usually when I meet people and they tend to have this very romantic view of geology  and that you're climbing these highest heights and grabbing some samples along the way, check  out these awesome rocks or check out these awesome formations.  But in reality it looks a lot like this.  It's very exciting I know, looking under a microscope.  And also very, in one particular area of geology you also tend to study a lot of these.  Does anybody actually know what these are?  Core samples, right.  Exactly, and what they do is if you study these enough you get a nice little graph or  chart that looks like this.  It gives you an idea into the past environment of what's going on.  And it might look something like this.  So right now you're probably wondering well I'm at a RubyConf in Swire you're talking  about rocks.  And that's because there is a very nice parallel between programming and what's going on in  geology right here.  If you were to only look at about five meters of this chart you're not going to be able  to see the whole picture.  And the same thing in Ruby and metaprogramming and Lambda functions we have all these techniques  we can use but we tend to get lost in the trees and we can't see the forest.  We can't really see what's going on.  And hopefully I can help with that.  And I want to start off by saying Ruby is beautiful so our DSL should be beautiful.  This is not a rip off of many swans.  This has a very ugly acronym I promise.  But I think that our DSLs are Ruby in general is known for being a very human readable language.  And I think our DSLs should live up to this reputation or even match this reputation of  being readable yet extremely powerful.  And this talk is going to go over some of the common ways developers go about using the  dynamic nature of Ruby to achieve these DSLs.  And I went through many popular libraries and I categorized or listed each time they  would use metaprogramming or Lambda functions and then I categorized them even further into  use cases or best practices.  And the libraries that you're going to see here are representative of the other libraries  that tended to use that same pattern.  And when we design interfaces in Ruby we tend to ask a few questions like who are we designing  for or what do our users want out of our library.  Well they want to be able to use what it's meant for and they want it to be very easy  to use.  And that's not to say that the implementers love paying it's just the Ruby way of making  the user experience very happy.  And one last stop before we move on.  I want to talk about paying for a second.  Everyone knows what the paying in the middle is right.  That's Starry Night by Van Gogh.  But the question I want to pose to you is would this painting have been as famous or  as influential as Van Gogh had used a style on the left which is a Monet or the style  on the right which is a $300 million painting believe it or not.  And probably not, right?  He used a very specific style for a very specific reason.  And what you're going to see throughout this talk is that many of the patterns or the techniques  that are used by different libraries were used to achieve a certain aesthetic design.  It's not required.  Your interface can actually be very ugly if you want it to.  But that's not the Ruby way, is it?  And the roadmap we're going to choose to go through this problem is we're going to talk  about some goals that we'd like and then we're going to talk about the problems associated  with reaching those goals.  And then we're going to look at the public interface that actually satisfies that.  And then we're going to dive deep a little bit and look at the implementation.  So code heavy, beware.  And the first case we're going to talk about is trust issues.  I don't mean to bring up any emotions with you guys, but developers tend to have trust  issues, right?  We tend to code defensively in such a way that we want to prevent others from making  mistakes and from ourselves making those same mistakes, right?  And one way we can approach this is through configurations, right?  We want to be able to provide configuration options while handling quality assurance.  And a way you can do this is a singleton.  That means every time that a user interacts with an object, it will have this global effect  versus a very localized mess.  And moving on from this, we can look at some typical ways that you would actually set up  an object or library, right?  Every time you interact with a library, there's always some interaction involved.  And this is probably the simplest way you can do it.  You have a config object,.new, very explicit, very simple, it's very obvious what's going  on, and you pass in five values.  Now one of the problems here is that five values is quite a lot to remember.  And it could be even more depending on the amount of options that you provide.  And I as a user don't want to have to flip back and forth between documentation every  time I mess with this stuff.  And not only that, we can't rely on defaults because we have to pass in a value every time.  And moving forward again, this actually solves both of those problems.  You don't have to provide a value for every single configuration option, and you don't  have to rely on order.  But a problem that you run into here is that a user or someone else you're developing with  can set these values up pretty much anywhere they want.  So let's look at a way, before we review this, is we are just looking at providing configuration  options but still handling the quality assurance for the user.  And Rails actually does this with config.  Config sets up configurations for multiple environments, and all you do is you pass in  a block, and you evaluate in that block this config variable that's passed in.  And the magic that happens here is you have a config module, but in it, it's this yield  self part.  Yield self is actually the singleton class of the config.  So now, every time that you operate on this variable in this block, it's actually operating  on the singleton.  So no matter where the user sets this up or when this set this up, it's always going to  revolve around the singleton.  You're saving them from future mistakes.  And here's another way.  Here's another version.  This is actually a hash, and Geocoder actually takes addresses and converts them to longitude  and latitude.  And you just pass in this hash, but underneath, as we'll see, it gets passed to this instance.  And this instance is really just a singleton, right?  You're just passing in this hash, it merges those options onto the singleton.  And it's a way of secretly forcing the user to use a certain interface.  And you're probably wondering, well, which one should I use, right?  If both of these satisfy our conditions, we have a block and a hash.  I'm of the opinion that the block is more Ruby-like.  You pass in an object, you work on that object, you do some stuff, right?  But on the right, you're passing in a hash.  And there's a lot of redundancy that doesn't really add to the intention of what's going  on.  You have these colons, these hash rockets, these commas.  It doesn't really add anything.  But when you look at the config.setup on the left, every time you operate on that config  variable, it's intentional as to what's going on.  You're explicitly saying, okay, I have this object, and I'm setting a configuration value  on it.  And moving on to case number two, which is situational language.  I'm going to define, offering situational language, I'm going to define it as context-specific  method names that rely on one implementation.  We want a flexible interface for our library.  So we may want to offer multiple method names, but they still rely on one single implementation.  Here's what I'm talking about.  Here's a typical way you would create flexibility.  It's very iterative, you define a path, or define a pinpath method, passes in a path,  and then it pushes that onto a pass variable.  But if you have several paths, it makes sense to pass in an array, and then you smash up  that array, and then you push it onto the pass variable.  Now the problem with, there really isn't anything wrong with this, is there, but it's repetitive.  And it would be nice if we could actually get both of these things at the same time.  And yet, still offer this situational language.  One implementation, and here's two libraries that do this, Hike, allows you to find a file  within a set of paths, and RSpec, obviously testing framework.  But if you see here, both of these instances actually, behind the scenes, rely on the same  method implementation.  So a pinpath and a pinpath relies on the same implementation.  And so does a hash including and a string including.  And this is not Hike right here, this is, they actually offer a condensed version.  And this is what it looks like.  They define one method name, but then they alias that method name to say, hey, I want  to give the user the opportunity if they want to pass in, you know, one value and use a  pinpath if they're like.  It makes more sense.  It's context specific, it's situational language.  And some common situations in which you're going to see this are singular versus plural  method names like we just saw, the path and paths.  And another example of this is synonyms with RSpec.  So RSpec's alias matcher, alias is the method and it does a few other extra stuff for them,  documentation wise.  And, but as you see here, you can actually use a string including on a hash because it  relies on the same implementation, but they're allowing you or providing this flexibility  to use it if you want to.  Which, and one more option is RSpec has a very human like readable interface.  So it may make sense to make verb tenses different, satisfy versus satisfying, depending on the  case.  But one of the cons is cognitive load.  Yeah, and you may feel, or if you're using this library especially for the first time,  that you may feel like you need to know all of this information and it may feel overwhelming,  but that's not really the intent of the developer.  The intent is that it's there if you need it.  And as you get used to it, it actually becomes very nice as sort of a documentation tool  as to what the object you're operating on or the amount or number of stuff you're passing  in.  And case number three is going to be leveraging APIs.  And so our goal is going to be leveraging the generally accepted meaning of existing  APIs.  What do I mean by this?  Every time you use Ruby, you are using the public interface that Matz created to solve  problems.  And in a way, you're inheriting his design.  And so whenever you inherit code elsewhere, you're inheriting their design, no matter  what.  And you're going to decide whether or not you're going to change the meaning of the  design that you've inherited, whether you're going to keep the generally accepted meaning  or if you're going to leverage it.  And Polyglot is a library that allows you to load Ruby files with non-Ruby extensions.  So it's a custom loader.  And here's how they might do that.  They open up the kernel module, they define a Polyglot require method, and they do some  awesome Polyglot stuff, and then they wrap that around the require method to handle normal  cases.  So in your code, it looks something like this.  Polyglot require stuff.weird.  Again, they are adding to the existing API.  Require means something to a developer, right?  It has a generally accepted meaning within the Ruby community.  But what they've done here is they've just added to it.  They've just wrapped behavior around it.  They didn't actually change what it means to require a file.  And here's their implementation.  Actually, this isn't around alias.  What's going on here is the alias, the original require, with Polyglot original require.  And then in their require method, they rely on the original implementation.  And this is what it looks like.  You can require stuff.weird with these strange non-Ruby extensions.  But the older version is still directly available through Polyglot original require.  And even though in Polyglot, they actually do handle normal use cases, so they actually  did not change the meaning.  And you're probably wondering, well, does this have some drawbacks?  And the answer is yes.  This was actually common in Rails for a long time, and you actually got it through something  like this, alias method chain.  And what it does is it takes the original method called foo, and this is going to be the new  method that doesn't have a certain feature.  And foo is actually going to have take on some extra feature.  And so that method is going to be foo, and you do that through alias method chain.  The problem is that if foo with feature relies at all on the original method name, you're  going to get infinite recursion.  And not only that, you're going to get extra method names in the namespace that you're  probably not going to use.  And if alias method chain actually gets called twice, the original implementation is essentially  cease to exist.  You can't call it anymore.  And this has actually been deprecated in Rails in favor of it being offered in Ruby.  And you do that by defining some behavior in a module.  I have a method called foo, and it relies on some method that I am going to end up inheriting.  But the magic is here, prepend.  What it does is it takes module, new behavior, and it inserts it in the ancestor chain below  my class.  So now foo calls the foo, sorry, the foo and new behavior relies on the foo in my class.  So now you don't have all these extra method names, and you can just rely on Ruby's object  model to accomplish the same thing.  And case number four, declarative DSLs.  This is going to seem not meta-program E at all.  And our goal is actually going to be declarative DSLs for setting up classes in your domain.  It's going to look like normal Ruby, and it should, right?  We use these every day.  It's called adder accessor, adder reader, adder writer.  And it feels like it's part of the Ruby language.  But it's not.  It's just a method, right?  It just feels like it is, because it fits so well within the domain of the problem that  you're trying to solve.  And not only that, but we want to automate some sort of common functionality for class  setup, and we'll see what that means right here.  Rails does this with ActiveRecordBase.  A user is a model, but your model can actually be anything, right?  It can be a book.  It can be a user.  It can be anything.  But what ActiveRecordBase does is think of it like a platform, sort of like a Raspberry  Pi, where you have this base that you build up on top of, to where you can actually have  a T-brewer or a video game emulator.  Essentially, just anything you want, just take your pick.  But that's what ActiveRecordBase is doing, and then it offers optional functionality  to build on top of that, so that when you interact with ActiveRecordBase, you just say,  okay, I want this, and then it executes that functionality.  I don't care how it does it.  We're very familiar with Hasmany, right?  It's very common.  And it's a very decorative statement, right?  You say, I want this, and it gives you that.  But there's a lot under the hood about what's going on.  But it's also so simple at the same time, because class macros are just class methods.  They're just executing a problem that feels seamless within the domain in which you're  executing.  ActiveRecordBase deals with databases.  So it feels natural that Hasmany should express a very natural one-to-many relationship.  And it does that with belongs to, Hasmany, several cases.  And the implementation is actually not so difficult, right?  It's just defining a Hasmany method way down here, and up in the code.  This is in the netters of Rails.  But this functionality is defined on a singleton.  It's that simple.  It's just building up the language of your DSL.  And anyone that inherits from your base class, like a framework, like Rails does, that class  now inherits and can use some of that functionality.  In case five, domain switching.  Our goal here is going to be defining behavior in a familiar domain so that you can execute  that behavior elsewhere.  And a better way to look at this is right here, Sinatra.  So this helper's method is actually defined on the top-level namespace.  But you can use this bar method very, very far away from that, right?  You can use these in views.  And this is what we're talking about.  Underneath Sinatra, somehow it's allowing you to define behavior on this top-level somewhere  familiar and execute that elsewhere that is less familiar.  You're making it easier on the user.  It's the Ruby way, right?  And every time you call a method on the top-level, it gets sent to base, which gets sent to templates.  But the magic is here, really.  Class eval, that block that you send to helpers gets executed inside of some sort of context,  class eval, which is executed on base so that when your view calls render, you now have  access to these methods so that each time you define these helper methods, you don't  have to define them specifically in a view context.  You can define them on a more familiar one in a top-level domain.  In opening doorways, we want to give the user, if we think of a library as a house, and as  that if you build a house, I've never built one.  But if you would build a house, you would not put a door on the second story because  the only way you can access it is through a ladder.  We want to allow many entries around the same structure of our library.  We want to build a big, beautiful door.  We want to allow people to easily enter.  And here's clockwork that executes applications at specific times.  It all does.  And here's a class that includes clockwork.  And here's a way that you can access this manager of clockwork by scoping in and accessing  it.  But the problem is, actually, that's a little wrong.  Sorry.  It should be directly through the class.  What we want is to be able to execute manager, access manager, directly off the instance.  We don't want to have to go through this roundabout way of accessing something that a user is  going to commonly want to access.  And here's Ferde.  Ferde is an HTTP client library.  So it makes sense that we're going to often be making Git requests.  And here's a way you can do it.  You have a Ferde module.  You scope into this connection.  You create a new connection.  And you make a Git request.  And you have a Ferde module.com.  But again, you're going to be making Git requests all the time.  So it should not be necessary that the user has to scope in to some sort of class in order  to access it.  We want something like this, directly off the main module.  And if you notice, in each of these examples, we're trying to avoid scoping in somewhere  else within our namespace to execute.  We want to make it friendly and easily accessible.  And so again, this is where the many entries comes from around the same structure of our  library.  And here's Clockwork.  This is what happens when you include Clockwork.  First you can access it through the module itself.  But you can also access it through the class and through the instance.  It's pretty nifty.  And here's how it works.  It's like a lot of code.  But this is where it happens, really.  There's an included hook so that whenever a class inherits Clockwork, that class comes  into the hook and it includes the methods on the right.  So we get the instance methods.  And then it extends those methods so we get the class methods.  But also, Clockwork itself extends these methods.  And that's where we get the instance method, the class method, and directly off the module  itself.  And if you look here, what we're actually doing is we're taking a singleton or a class method  and we're trying to execute it in many different ways.  So that now everything we realize, everything still is around this manager.  Everything still revolves around the Clockwork module.  And here's Fairday.  This is what we were just talking about earlier.  So two different ways you can do the same thing.  But they also offer a simpler version, or here's how they get the simpler version, is  instead of creating a new connection every single time, we just want to say, Fairday,  get me this request and do it for me.  And how it does that is there is a method missing on Fairday, Fairday module.  So when you send a method there, that method then gets delegated to this new connection  here.  So every time that you send a request through method missing or executed method on Fairday,  it's actually sent to this default connection.  It creates a new connection for you.  Again, this just is all about making the user happy.  And case number seven, dynamic building.  We want to be able to dynamically build objects with the broadest interface possible.  I know it's a very broad definition, but here's an example.  So when you build objects, this is an example or a way that you could do that.  You have a library.  You create an attribute called name, and then I add a value onto my name.  I have to pass in that attribute every time if I'm going to associate a value with it.  And then I can yield some sort of value off of that.  The problem with this, I mean, there really isn't any problem.  It's pretty explicit.  But if you're going to have 10 or 20 of these things, it's going to get pretty repetitive,  right?  And not only that, you're going to have to associate each one of these attributes with  every single value that you pass in.  It's not dry.  And the goal then, and this is where we're coming up from, we want to avoid the explicit  method calls by mimicking Ruby.  If we mimic Ruby, we can already leverage Ruby's existing interface.  Sorry, and which I think is good enough.  And here's an example of that actually happening.  Here's JBuilder, and it revolves around this JSON object.  And each time that you want to create a name, you send it a method call.  And then you get something like this, right?  You send in a block, and then you send in a value.  It just depends on what type of JSON object you're building up.  And the internals look like this, though.  So every time you send a method call to this JSON object, it gets sent to method missing.  And if there's a block, it executes a certain statement.  But if there's a value, it executes that.  It's mimicking Ruby.  You're just mimicking Ruby.  It looks like a method call.  And here's Paint.  Paint manages terminal colors for you.  But you can actually create your own schemes.  And how you do that, and this is a pretty cool trick, I thought, kind of clever, you  scope into the shortcuts constant, and you create your scheme as a symbol name, and then  you pass in these symbols that are associated with some values.  But now you can access your scheme like this as a module and as a singleton method.  What happens behind the scenes is my scheme is now a module.  It gets converted at runtime into a module.  And here's Paint's implementation.  It all has to do with const missing because your scheme does not exist until runtime.  And when it's sent to const missing, it takes that module and it evaluates that module on  Paint.  So it creates a module for you at runtime.  And it's not only that, but it takes each one of those symbols that you passed in as  a hash, and it makes some singleton methods.  So now you can easily access your scheme like in the top right.  But what is the pattern, really?  Why are these so easily able to understand what's going on, even though they have probably  the broadest interface possible?  And that's because they look like Ruby.  I know I've said that a million times, but I really want to hammer that through.  And we're just leveraging an existing interface for our own.  We could have used explicit method names like create attribute, add value to that attribute,  but it's very redundant.  We can just use Ruby.  And this brings us full circle.  And we've examined several patterns that have to do with metaprogramming and lambda functions.  So hopefully you've been able to see how these are actually used in real world examples  like libraries, and that they don't actually live in a vacuum.  They achieve a certain interface, and clean interface is at that, but they also reduce  redundancy.  I've never heard someone say, man, I wish Sinatra made me write more code, right?  It never happens.  But also another thing about these patterns, you shouldn't just wake up one day and think,  okay, I'm going to use the observer pattern.  It doesn't work like that, right?  You wake up and you wait till you have that problem.  And later it arrives organically, and then you think, wow, I watched this guy, this geology  guy, he's wearing all blue, strangely enough.  And think about one of these patterns.  And they say that you only remember the first, like the first five minutes and the last five  minutes.  So if there's one thing I want to leave you with, it's the situations, I just want to  go through these situations again, in which you should recognize and think, yes, this  slide, this is what I want to remember.  And you're going to start off with trust issues.  If you're trying to force a certain type of public interface underneath and make sure  that it's global in its state, think singleton.  And situational language, if you want to create a lot of flexibility for a certain context,  think alias method, if you want to leverage existing APIs again around alias, don't know  how much you're actually going to use that.  But since we have module pre-pen now, but it's still out there.  And declarative DSLs, think class macros, think I want to allow my user to use some sort  of functionality that's predefined.  And they just say, I want this to happen, and I provide that for them.  And switching, allow your user to create some sort of functionality and a domain that's  familiar for them, like the top level namespace, and then allow them to execute that elsewhere  in a less familiar domain, like a few.  And opening doorways, think hooks and method delegation, think about making your interface  seamless in a way that they commonly interact around the namespace library.  And lastly, dynamic building.  If you ever want to build an object, it's probably the way you want to go.  You're probably always going to use method missing and const missing.  And that's it.  Questions.  All right.  Thank you.
Seeing_Metaprogramming_0_47.png Seeing_Metaprogramming_48_95.png Seeing_Metaprogramming_96_143.png Seeing_Metaprogramming_144_191.png Seeing_Metaprogramming_192_395.png Seeing_Metaprogramming_396_671.png Seeing_Metaprogramming_672_1091.png Seeing_Metaprogramming_1092_1427.png Seeing_Metaprogramming_1428_1559.png Seeing_Metaprogramming_1560_1811.png Seeing_Metaprogramming_1812_2063.png Seeing_Metaprogramming_2064_3287.png Seeing_Metaprogramming_3288_4667.png Seeing_Metaprogramming_4668_5303.png Seeing_Metaprogramming_5304_6587.png Seeing_Metaprogramming_6588_7115.png Seeing_Metaprogramming_7116_7655.png Seeing_Metaprogramming_7656_8267.png Seeing_Metaprogramming_8268_9863.png Seeing_Metaprogramming_9864_10403.png Seeing_Metaprogramming_10404_11243.png Seeing_Metaprogramming_11244_11531.png Seeing_Metaprogramming_11532_12059.png Seeing_Metaprogramming_12060_13139.png Seeing_Metaprogramming_13140_13259.png Seeing_Metaprogramming_13260_14747.png Seeing_Metaprogramming_14748_18083.png Seeing_Metaprogramming_18084_20243.png Seeing_Metaprogramming_20244_21227.png Seeing_Metaprogramming_21228_23687.png Seeing_Metaprogramming_23688_27671.png Seeing_Metaprogramming_27672_27755.png Seeing_Metaprogramming_27756_29831.png Seeing_Metaprogramming_29832_32675.png Seeing_Metaprogramming_32676_35879.png Seeing_Metaprogramming_35880_35975.png Seeing_Metaprogramming_35976_36203.png Seeing_Metaprogramming_36204_40643.png Seeing_Metaprogramming_40644_41495.png Seeing_Metaprogramming_41496_42935.png Seeing_Metaprogramming_42936_45491.png Seeing_Metaprogramming_45492_45719.png Seeing_Metaprogramming_45720_45767.png Seeing_Metaprogramming_45768_45863.png Seeing_Metaprogramming_45864_45909.png