My Meta Moments
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 67 | |
Author | ||
License | 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 | |
Identifiers | 10.5446/37692 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer | ||
Production Place | Cincinnati |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Ruby Conference 201629 / 67
3
5
9
10
14
18
19
20
21
22
23
27
28
29
32
33
34
36
38
40
41
46
51
56
57
58
59
62
66
00:00
Moment (mathematics)Meta elementProjective planeCodeSoftware developerMultiplication signGreatest elementProcess (computing)Different (Kate Ryan album)Meta elementMetaprogrammierungMoment (mathematics)Slide ruleProbability density functionComputer animationEngineering drawingXML
01:16
Inclusion mapView (database)Presentation of a groupCodeComplete informationSystem callMetaprogrammierungInformationPlanningData storage deviceMathematicsFrequencyMultiplication signQuicksortCASE <Informatik>Social classPower (physics)Factory (trading post)Interactive televisionLine (geometry)Validity (statistics)MultilaterationInheritance (object-oriented programming)BitFeedbackFormal languageSoftware testingCache (computing)Green computingRun time (program lifecycle phase)Process (computing)Compilation albumComputer programmingObject (grammar)WordMeta elementPercolation theoryParameter (computer programming)Dependent and independent variablesXMLUMLComputer animation
06:31
Social classSoftware testingQuicksortCodeLine (geometry)MathematicsClosed setInstance (computer science)System callMultiplication signRevision controlComputer fileSlide ruleCASE <Informatik>Object (grammar)String (computer science)Function (mathematics)RandomizationConfiguration spaceRow (database)MetaprogrammierungSet (mathematics)Symbol tableStatement (computer science)ResultantSubject indexingUsabilityScripting languageType theoryNumberRight angleWrapper (data mining)INTEGRALWritingBitGreatest elementDot productMereologyInheritance (object-oriented programming)TwitterComa BerenicesComputer animation
11:37
Data modelSocial classModule (mathematics)Inheritance (object-oriented programming)Proxy serverHill differential equationModulo (jargon)Social classMathematicsProxy serverInheritance (object-oriented programming)Revision controlSystem callClosed setSoftware testingRow (database)Formal languageHecke operatorInstance (computer science)Electronic mailing listAttribute grammarMereologyUniform resource locatorPoint (geometry)Sign (mathematics)ChainOrder (biology)Object (grammar)Message passingModule (mathematics)Functional (mathematics)Pointer (computer programming)NumberMetaprogrammierungInterface (computing)Kernel (computing)Wave packetPower (physics)DiagramEndliche ModelltheorieBroadcasting (networking)Student's t-testResultantXMLComputer animation
17:55
Module (mathematics)Newton's law of universal gravitationMathematical singularityInstance (computer science)Row (database)Object (grammar)Block (periodic table)DatabaseChainParameter (computer programming)CASE <Informatik>Doubling the cubeBitTablet computerMultiplication signSource codeInheritance (object-oriented programming)AdditionWritingSocial classRevision controlCodeTouchscreenSystem callReal numberArithmetic meanClosed setComplete informationMetaprogrammierungNumber1 (number)SequelError messageSymbol tableRootComputer animation
24:11
Computer virusComputer chessSocial classSocial classObject (grammar)Combinational logicDifferent (Kate Ryan album)Instance (computer science)Data storage deviceCodePerformance appraisalVariable (mathematics)PiUniverse (mathematics)BitMessage passingParameter (computer programming)Closed setWave packetSystem callInheritance (object-oriented programming)CASE <Informatik>1 (number)Line (geometry)Eigenvalues and eigenvectorsDegree (graph theory)Sign (mathematics)String (computer science)Block (periodic table)outputComputer animation
29:33
Object (grammar)Proxy serverInstance (computer science)Object (grammar)Variable (mathematics)Social classResultantMereologyProxy serverSystem callNoise (electronics)Configuration spaceRow (database)Computer animation
30:48
InformationLogarithmInheritance (object-oriented programming)Object (grammar)Windows RegistrySocial classCodeResultantDifferent (Kate Ryan album)System callWindows RegistryLine (geometry)InformationNumberTouchscreenSinc functionKey (cryptography)Proxy serverCASE <Informatik>Object (grammar)Instance (computer science)Computer animation
32:09
Instance (computer science)Inheritance (object-oriented programming)Social classInstance (computer science)Proxy serverObject (grammar)Function (mathematics)ResultantMemory managementNumberSystem call2 (number)Data storage deviceBuilding
33:19
InformationLogarithmInheritance (object-oriented programming)MathematicsProxy serverProxy serverSocial classDifferent (Kate Ryan album)ResultantInformationInstance (computer science)Object (grammar)ExistenceSystem callRun time (program lifecycle phase)Automatic differentiationComputer animation
33:59
Proxy serverObject (grammar)Social classProxy serverSystem callInstance (computer science)CASE <Informatik>Programming languageOpen sourceLevel (video gaming)Factory (trading post)Power (physics)Digital photographyInformationDisk read-and-write headRevision controlProjective planeFlickrMetaprogrammierungMeta elementDifferent (Kate Ryan album)Point (geometry)Formal languageArithmetic meanImplementationComputer animation
36:24
InformationLogarithmInheritance (object-oriented programming)Instance (computer science)Message passingRight angleObject (grammar)Integrated development environmentSocial classParameter (computer programming)Windows RegistryMultiplication signRevision controlSoftware design patternData storage deviceComputer animationLecture/Conference
37:28
Coma BerenicesXML
Transcript: English(auto-generated)
00:19
I'm going to give some people time to, I got the deck up, and you can download it if you
00:22
want to follow along. My name is John DeWise. This is my meta moments, which is a talk about my process of learning metaprogramming, a project I worked on, the concepts I needed to learn to work on that project, and just kind of how to use them, how to start hacking away. So I'm going to jump to the bottom of this slide first.
00:42
I have different code snippets, and we're going to move through them, so if you want to see them in their entirety, be able to go back. If you go to speakerdeck.com, DeWise, my last name slash meta, you can download it on a PDF, or you can just follow along. So if you move on, it's my last name slash meta.
01:00
So I've been a software developer at Braintree in Chicago for two years. That's about how long I've been a dev, so this was a talk about I wanted to learn metaprogramming and how that came about. My GitHub is at DeWise, if you want to check me out there. A required disclaimer, the views expressed in this presentation are my own and not those
01:21
of PayPal or its affiliates like Braintree. This is my first time speaking at a tech conference, so I want your feedback. My hope is I spoke slowly enough, I explained topics clearly without making too many assumptions about what people knew, I used language that was inclusive and welcoming, and most
01:40
importantly, that you got something out of this. I'm open to compliments or criticism, so if you're willing to share, please find me afterwards, I would be grateful. It's not really anonymous feedback, but if you hide your name tag, I can't see who you are, and that's kind of like anonymous feedback, so that'll work. What is metaprogramming? So to start, I'm going to describe some summaries and uses of metaprogramming before
02:02
going into kind of the tools you can use. I know I hate it when I start going to a talk and a few minutes in, realize I'm missing some basic information and too embarrassed to leave, so I just sit there and nod periodically. I've tried to briefly describe any topic I talk about, in case you are pretty new to some stuff, I'm sure I'll miss some spots.
02:22
So feel free to find me afterwards if you have more questions. So what is metaprogramming? I thought about using the Wikipedia definition, but that's boring when you do that. It's long and encompasses a lot more than I'm going to talk about. So I'm going to talk about some ways that I view it, which are not official, but especially when you're learning, I think they're kind of useful.
02:42
The first is writing code that writes code. This is sort of a lazy definition, but the idea is that you can write code in such a way that new behavior can be added at runtime or compile time. So one example, if you've ever used Factory Girl for creating objects and testing, when you have different parameters, those are actually methods that you end up getting called
03:02
that aren't there at time using some of the metaprogramming tools available to you. It's also writing code with incomplete information. I'll talk about this a little more later, but for example, if you've ever used one of these Rails magic methods find by column name, the way that is implemented is with imperfect information, and we'll explain that kind of later.
03:24
Finally, writing code really efficiently. So if we were to take this bit of code, we have a user, we get all the methods on the user, and look for all the methods that begin with validate. And then we're gonna use send, which we'll talk about as a way to call those methods. So these three lines can actually call a ton of lines of code without having to know
03:42
what methods we're even calling. Before we go any further, I wanna talk a little about the perils of metaprogramming. When I told someone on the plane I was doing a metaprogramming talk, they asked, oh, are you just gonna tell people not to do it? Not quite, but there are a few caveats to be aware of.
04:01
Metaprogramming can produce a lot of unexpected behavior. So if you use this, for example. If I didn't see this call and I added a method, I could be calling something that I only wanna call in a very specific situation. It can be hard to test. You can be doing stuff, manipulating the way the code works, manipulating inheritance and ancestry, that it can be sometimes hard to test without doing some really ugly stubs
04:24
and mocks that are not great. It's slow. If you don't know what the method cache is, it stores methods that get called commonly to speed up the process, and every time you add new methods, you invalidate that cache. We all know what it means to invalidate a cache. It's really hard to deal with.
04:42
And finally, metaprogramming is meta. It is clever. It often requires knowledge of the surrounding code and possibly other unseen interactions to work well. And almost always, clever code is the wrong code to write. It can increase the burden of other people jumping into a code base, and often the more clear and readable solution, even if it's gonna be more lines and it's not elegant,
05:05
is probably the better code. That being said, there are also some perks. It's efficient. So going back to that validate method, I can call hundreds of methods with three lines of code. I don't need to worry if someone adds validations. I know they're just gonna get called.
05:22
It's powerful. It can allow you to, again, change the behavior of a class, change the way classes inherit from each other to a certain situation, but not another one. You can manipulate the code base in very useful ways. And I think the most important thing is it's fun, which is why if you're new and someone tells you to not metaprogram, you should metaprogram.
05:42
Don't commit it to production, but play around, have fun, enjoy it. Ruby allows some really powerful stuff that you can do, and I think the curiosity that you use to explore metaprogramming should be fed and should be something that's exciting to play with. So my final caveats. I think of it as kind of like Spider-Man with a lightsaber.
06:03
So if you are familiar with Spider-Man or not, Uncle Ben has these words, with great power comes great responsibility. If any of you are like me, when you were a child, you probably pretended a cardboard tube with some sort of lightsaber, and you learned a few lessons. Lightsabers are fun. If you actually had one, you probably would have cut off all of your limbs by now.
06:25
But if you have one and you haven't cut off your limbs and you know how to use it, that probably means you're a Jedi, which is also pretty cool. So with that, I'm going to start with introducing something I call Putter. It's not putter, despite the picture, but I'm inevitably going to mispronounce my own gem throughout this, so I'm just going to go with it.
06:43
I'm going to show how the gem, what it does, and then all the metaprogramming tools I learned, and then how they got used to build this gem. It's a little bit of shameless self-promotion, but I think it's a lot easier to understand concepts when they're inside of an idea, inside of a set of code, as opposed to just talking about them. So Putter is a puts debugging gem.
07:02
If any of you don't know what puts debugging is, it's the really fancy advanced method where you just put put statements everywhere to figure out where you are in your code and what is going on. Has anyone else written this one before? What's my arg? So, you can find it at github.com, DeWise, Putter, and despite the fact that I have a
07:25
putter in the background, it rhymes with gooder, not gutter. So this is a little script that uses Putter. So we are going to grab it, we have a user class, we're going to make the username read-writable, initialize with a number. The username is going to be user underscore whatever that number is.
07:41
We're going to find two methods, they're just kind of wrappers around the built-in Ruby ones, uppercase and lowercase, and we're going to close them. You'll see me throughout this, I use a lot of semicolons instead of new lines because that way the code can be bigger, so I assure you it works, it's just a way to kind of write the code a little more succinctly. After that, we're going to call Putter dot watch.
08:01
There's two APIs, watch and follow, we're going to use watch here and we'll talk about both later. We'll have a user's array, and then zero to four, so we'll have five users, we'll create them all, we'll put each user into our array, and then we're going to call uppercase on that user. Then we're going to go through and get all the even numbered users, so zero, two and four indexes, and we're going to call lowercase.
08:21
What happens is you get this, and I'll do a zoomed in version on the next slide. We're going to be able to see, these are some snippets, in the top you see that on the user class at line 15 of watch, I called the new method, I passed in zero and the result was a user. The next one is the first instance of user, which is the one I just created.
08:44
On line 17, I call uppercase with no args, and I get user underscore zero and capitalized. Then from the bottom, user instance five, which is actually index four. On line 20, I call lowercase. What you can do with Putter is you can use some integration test or some sort of
09:01
test at the outside of your code. You can watch a class, and you can see every single place where that class is called, any class method is called, or any method is called on an instance of that class. What file it's called in, what args it was called with, and what the result was. I used this when I wanted to understand a code base that I had never seen before, and I needed to follow one path.
09:22
This will tell you a giant output of all the methods that gets called through that. Putter also has some configurations to ignore methods from active record and object. Or if you want to learn how active record works, active record calls ID and class a lot. I had no idea, so I added some ways to ignore that, but it's kind of interesting to see all the different things that Rails does under the hood, you can use this to do.
09:43
Here are the topics we're gonna talk about as far as the tools, and then I'll show how I use them in Putter. I'm not gonna read these right now, but we'll go through each one. Some are more strictly meta than others, but they all can be useful, and if you haven't seen them before, it's kind of important to have these tools at least under your tool belt.
10:01
There are other ways to do metaprogramming. There might be right ways that I'm not discussing. Additionally, I'm probably gonna lie to you, because when it comes to metaprogramming, there's a lot of edge cases, some that I found and some I haven't found yet. Some cases where self changes depending on what method you're in. It's easier just to tell you what's gonna work 90% of the time.
10:23
I encourage you to play with it and find those edge cases for yourself. But just know that metaprogramming is fun, but dark and full of tears. So first up is Send. Send is an extremely powerful tool. It is one method to call them all. Send will invoke a method, it will call a method using a symbol or a string of the
10:41
method name. It also lets you call any method. So if you've ever heard of Ruby that there's no such thing as private methods, it's because you can use Send to call anything. So let's take a look at kind of how that works. So I'm gonna be using the Pry gem here, which is kind of like IRB, just open up a random Ruby session rebel.
11:01
So this is using Pry and Ruby 2.3.1. Most things if you do this will be similar. I removed a couple lines where if you define a method, it returns the symbol of the method name just for ease. But for the most part, you can follow this code along if you wanna type it yourself to see what happens. We'll define a method called send me, it'll let us know it was sent, and we'll close
11:21
it up. We'll create a new instance of our sender, we'll send send me, and we'll see that it was sent. So you can see that we can just call a method using send, and if that method name were a variable that we didn't know, we can call methods we didn't know about. Next is inheritance. I know most people are probably familiar with inheritance.
11:43
A truck is a type of vehicle. Most of our users probably inherit from active record. You may have heard the is a trick, so if something is a something else, it inherits. If it behaves like, it will implement, and depending on what language you're in, those are interfaces or whatever.
12:00
So because of this, like any vehicle, a truck can drive, it has a make and model, but it's a little different because it can tow. A user will have save or update attributes that come from it as a part of active record. That part I think most people have seen before, but what I didn't know about inheritance is when you use include and extend, you're actually dealing with inheritance.
12:21
This is changing inheritance. I used to think this was composition because I was composing modules, I was adding them in. There was no less than sign to inherit from, but that's wrong. It is definitely inheritance. So if I have a television that extends broadcastable or includes watchable, it's actually inheriting from those. That will change the inheritance chain.
12:41
So we can take a look at this one too. We have a parent. We'll have a parent method. It's going to say I'm parenting. Close that. We have a child. We'll include the parent, and we'll define that parent method again, and we'll just call super. So because it's inheritance, I will call whatever my superclass's version of that method is.
13:01
Close it, create a new child, which is much more complicated than just doing this, but we'll call parent method on it, and we'll see I'm parenting. So to explore that a little more, we'll get into ancestors. So how the heck do you know what you're inheriting from? Well, we can look at the ancestors.
13:20
The ancestors method will show the inheritance chain and the order that it happens in. Almost all objects inherent from object, if you've ever seen that. Object also will mix in kernel, which is where puts comes from, or chomp and eval that get included in. And in the end, all objects inherit from basic object, which we'll talk about a little later.
13:41
Finally, the last module that gets included is the first parent. So we have a grandparent. We have a parent. We have a child. We'll include the grandparent first, because they came first, and then the parent, close that up, and then we'll take a look at the ancestors. So we see our child, and even though we include a parent second, it's the first
14:01
one we see, and then grandparent comes next. You'll also see object, because we inherit from that. The pp object mixin is for pretty printing. It's included by pry. If you were using IRB, you would not see that. Then there's also kernel, which is where we get puts, and then basic object. Now we're going to talk about prepend. Prepend. Again, this is not necessarily a metaprogramming thing, but it can be used to do some cool
14:22
metaprogramming stuff. Prepend functions like include, except it puts something before itself. So you know appends, prepend, and JavaScript. It can be useful for things like proxy modules, because you can actually prepend a module whose methods will be called before the class that they are prepended in, and
14:40
we'll see that a little later. So let's make a proxy. We'll have a proxy method. It's going to tell us it was proxied. And then notice we'll call super. Again, I apologize for the semicolons, but we're going to call super on this. We'll create a test class, and we'll prepend proxy. We'll define that same method, letting us know that we got here, because that's how you debug.
15:02
Create a new instance of our test, and we'll call proxy. We'll see first that we get proxied from the method in our proxy, then we get the get there. So as a result, we can create things that interrupt methods and do stuff before they get called. So if we were to look at the ancestors of our test class, we see that proxy is now actually
15:21
sitting in front of the test class itself. So it's a really powerful thing that you can do to be able to stop methods, interrupt them, modify them as they're in flight. Next up is method dispatch. So what is that? Method dispatch is the way that Ruby determines what method to call. There are some really great diagrams with this.
15:41
I didn't want to steal them, so if you Google method dispatch of Ruby, you'll see them. But the idea is, it'll search the ancestry train, going up that list of ancestors, and the first one that implements the method, it will return that method. So we'll take a look at how ancestry works. I was trying to think of an example of something where the location of something
16:01
changes, and I thought a good example would be figuring out who owns my student loans. So if you don't know Sallie Mae, they're now called Navient. So we'll have a Navient loan and a Sallie Mae loan, and Sallie Mae will include the Navient loan. Close that up. We'll have a US government loan, because I think they had it at one point. I don't know. They might still. To find an owner method, they'll let us know that they own the loans.
16:26
We'll create a loan class, which includes the US government loan first, and then Sallie Mae, but notice not explicitly including Navient. We'll close that up. So now we're going to take a look at, when we call this owner method and change it, what happens. We create a new loan, have our instance.
16:42
We call owner, and we see the message that the US government loan had. We'll create a Navient loan to find owner, and if you didn't know this, you can actually reopen up modules and classes, add methods to them at any point. Rails will do stuff. If you've ever used the present method, you can open up the nil class or the true class
17:01
or the false class or number classes, so you can add your own methods to anything. This will say your loans have been sold, and we'll call owner on it now, and now our loans have been sold. Just by adding that method to the Navient loan. So now we'll take a look at the ancestors. So we see first is the loan, then the Sallie Mae loan because we included that first,
17:22
but that also, we include then the Navient loan because it was included by Sallie Mae, which will be included before the US government loan. So the more often that you have things included, that actually changes your ancestry train, can change it a lot because you're including everything that gets included by everything else.
17:40
So what does this have to do with metaprogramming? None of this really has been metaprogramming so far. I promise we're getting there, but you know, knowledge is power. So now we're going to get on to some of the cooler stuff that you can actually do starting to use these tools. Method missing. Method missing, if you haven't seen it before, is defined like this.
18:01
It is called when a method cannot be found. So if you've ever had a method not found error, it typically means that method missing was not defined anywhere in that method dispatch chain. So remember those ancestors? The first method missing that is defined in the first ancestor that it finds it in will be what is used. It has access to the method that was called, the args passed into it, and any blocks that
18:22
may have been passed in. If you haven't seen the ampersand block symbol, any Ruby method can actually implement this. You can pass a block to any method. You don't have to have it. So we'll take a look at how to use this. We have a parent. We'll define method missing on the parent, which we're going to ignore the block for now.
18:40
And when we do that, we'll put the method and the arguments that we get. We have a child, which will include the parent. Then we'll do child new, and we'll call do stuff, and we'll pass and things. Notice we didn't define do stuff anywhere, but what we get is method do stuff, and the args are and things.
19:01
And you'll notice that we get an array, because that star args, if you haven't seen it before, it's splat args, is a way to kind of close up any unknown number of arguments. So if we would have had 10 arguments or three, it combines them all into this one array, which is why we see an array of the two arguments that we passed. So this means any time a method doesn't exist, we can change behavior.
19:21
We can make it exist. And we'll see shortly, we can write code with incomplete information. So a more complicated example. You may have all seen, again, active record defined by magic methods. We're going to make our own version. We're going to call it lazy record, because it's not going to do much. We'll have a method missing. I used m this time just because I needed screen real estate.
19:42
If the method name matches find by underscore, we're going to take the trait. All this does is whatever comes after find by underscore, it's going to grab that thing. So if we did find by name, it would be name. Find by username, it would be username, et cetera. And then we're going to call self.where.
20:02
This I'm going to pretend is the same as an active record call. We can call where with kind of some pseudo SQL, and we're going to pass whatever the first argument passed to the method missing call is. Now we don't have where to find, so we're going to make a fake where. It's going to tell us it's doing a database lookup. And regardless of what we send, it's going to return Mickey Mouse. I'm going to close that up.
20:21
So now we're going to create a user. We're going to use this. We have a user that inherits from lazy record. We're going to find by name Mickey. So we're doing our database lookup. Now I didn't put this out there, but this is basically what we do. If we have this where method that can do a SQL search, it's going to do something like that, and it's going to return Mickey Mouse. Again, to be fair, if we did find by zip code, we would also get Mickey Mouse.
20:42
So this isn't very helpful. But if you were backed up by a database, or if in any case you wanted to respond to methods that were somewhat dynamic or helpful, you can do that. I think active record has since deprecated the find by magic, and there's actually one now find by method that takes arguments. But you still may have seen this before.
21:00
Now we're going to talk about define method. We're starting to see some of the more powerful things we can do. So in addition to responding to methods that don't exist, we can create methods that we want to exist explicitly whenever we want. We use define method. It's a way to define a method via another method, so you don't need to use the def command. You can actually do not even use the def command.
21:21
This can be also used in classes. You can create methods whenever you want, like on method missing. You can create a new method. And ultimately this takes a method name and a block, and a block is the body of that method. So we'll have a define class. We'll define method missing. And we'll say that if that block was passed in, that means someone wants a custom method.
21:43
So we'll define it. And we'll do self.class.send. And the reason we do self.class is because methods can't actually do... They can, but for this purpose, they can't be defined on an instance. So we're going to define it on the class. And we're going to send define method. It's a private method, so we have to use send. The method and the block that's passed in.
22:02
If there is no block, so they didn't pass us a method body, we'll define just a hello method, self.class.send. We'll call define method again, and we're going to pass our own block. So that's what the do is. If you aren't familiar with blocks, or I mean, we've all used them each do, but if you haven't been familiar with how they work, it's a great use of a weekend to kind of dive into
22:22
how they can be passed around, and, you know, there are, like, callbacks in JavaScript was something that kind of helped me. That's not really an accurate definition totally, but I encourage you to explore it more. We're going to close everything up. We'll create a new instance of define, and we're going to call world. We'll see that we didn't pass a block, so we're just defining a hello method, and
22:42
we'll call it again, but now, instead of defining it, we actually have a method that's going to say hello world. If I called it again, the same thing would happen. If we pass a block in, we're going to revolt, and pass an argument. It's going to let us know it doesn't want to say hello whatever, so we'll close that up. Now it's going to tell us we're defining a custom method.
23:02
Now if I call it again, notice I'm not passing a block this time because I don't have a method body, but I'm going to pass an argument, it'll say I don't want to say hello world. So you can actually define new methods with blocks that are passed in by the user. You can define new methods with things that you want. Being able to define new methods when methods don't exist, or for other reasons, gives you a lot of flexibility.
23:22
Basic object. Basic object is the source of all objects. It only has a handful of instance methods, which makes it very useful for metaprogramming. Here are all the instance methods that exist on basic object, all the public ones. Three of them begin with double underscore, they're kind of private internal methods.
23:41
We're going to take a look in a little bit at instance, eval, and exec, but you see that this is mostly a blank slate almost to work with. It's also the source of the method magical method missing, as well as initialize. Those are private instance methods on basic object. Object has about 56 methods, or 63 if you're in pry, and then basic object has, I think
24:04
it was nine. So it kind of tells you how small and paired down this is versus the normal object you interact with. Singleton classes. A singleton class might also be called a metaclass, it might also be called an eigenclass. To my knowledge, there is not universal agreement on this, there might be, but people
24:22
like to argue about what this is called, so I'm going to call it a singleton class. The way to view it is it's a class that itself is an object. So it's not an instance of a class, it is the instance of the class. It's not like user.new. So a class method is actually just an instance method on the singleton class.
24:43
So when you have a class user and you have an all method that gets all the users, you have user.all, that is actually a class method, but it's an instance method on the singleton class. This is one of the harder concepts, at least for me it was a little bit harder to grab, but it's the idea again that that class is its own instance, and we'll take a little
25:02
bit of a look at it. We have a greeter class, it says hello, you can do self.hello, which is I think what mostly we'll see for class methods. You can also define it on the class and do greeter.hi, and if you've ever seen this before, this is actually a way to say I want to open up the singleton class, and I'm going to put methods on it.
25:21
So I'm going to define yo, but notice I don't need self and I don't need the class name. Close that up, call hello, hi, and yo. And if we look at the singleton methods on greeter, we'll see the three ones that we defined. So these are again, all class methods, but they're methods defined on the single, instance
25:43
methods on the singleton class. Another way to look at it would be if we look at all the methods, which returns class methods on greeter, and we subtract away all the methods on object, we'll see this. Or, take greeter, we get the singleton class on greeter, and the instance methods on that, and get rid of all the class methods on object, again, we see this.
26:06
So when I said you can't define a method on an instance, you can't, but you can, because you can actually get a singleton class on an instance. Instances can have their own singleton class, which is kind of a copy of the parent class that they have, but a copy that can be modified, and when you modify it, it only affects
26:24
that instance. So, we have a thing, we'll look at the singleton class of thing. Instead of saying thing, like now we see kind of the Ruby hashtag and then in carrots, if we look at an instance of thing, you've seen this before too.
26:40
Now if we look at the singleton class of that instance, we'll actually see like a, it's not a string, but a, you know, line that looks very similar to a combination of those two. And if we look at the ancestors of that singleton class on the instance, we'll see that, no that wasn't supposed to be that, thing, object, and all through basic object.
27:03
Instance eval. So this is the last thing we're gonna talk about before we get into how putter is put together. There's also instance exact, which we'll talk about the difference shortly. This is a way to eval code in an instance or a class. Kind of, if you've ever heard of Ruby eval, or don't use Ruby eval, it's a way to kind of evaluate code.
27:22
One thing that, I apologize, it's not a method. Eval does not take a method, it actually takes an object. It does not take an object, and exec does. So we'll take a look at whether or not you use which, and why you might pass an object in. So we'll create a spy class. New it up, and we'll store some secrets. The secret apple pie recipe inside the spy.
27:42
If we create a new instance of spy, and we get those secrets, we'll see we have an undefined method secrets, because there's no public getter for that. We could use this really ugly method, which I would not recommend using, but you can do instance variable get to see the instance variable, and we see our secrets, but there's another way to do it. We can new up a new spy, and we can do instance eval,
28:01
and put at secrets, and we see our apple pie recipe, because this code is actually evaluated inside that instance, and inside that instance, at secrets, is a method that you have, a special way to get that instance variable. So this is a little contrived, so let's look at maybe a better example. Which was also contrived, but still useful.
28:21
We have a Neo class. Kiano will be our instance of Neo, so we have this instance. Now we're gonna do a class exec, so instead of eval exec, and we're gonna pass something in. In this case, we're passing just a method, but you could actually pass an object full of data to do various things with. And we're gonna give an argument to the block, which is that method we passed in, or the argument we passed in.
28:41
We're going to define a method, which is going to be train in this case. Kiano's gonna let us know he knows Kung Fu, close it up. We call it kiano.train. Kiano will say, I know Kung Fu. So we will then actually add a method to the class, using the defined method, and add behavior.
29:02
Armed with this ability, being able to pass the variables, user input, we can modify a class whose name we don't know, add a method whose name we don't know, and if we pass around a block, our proc, we can define a method body that we don't know. So it's some pretty powerful stuff if you build stuff in such a way that you can take advantage to it. So I'm gonna show you what I did with Putter
29:22
to use all these tools to kind of create my Puts debugging gem. So first we're gonna look at the method follow. So follow takes an instance. It can also take a class, but more commonly you'll use an instance. And what it does is it creates this Putter follower object, which inherits from basic objects. So this follower has almost no methods on it, and we store the user object, the instance,
29:42
as an instance variable. So we have this follower that is the result of Putter.follow. We saw that. And what we'll do is we'll create a new proxy. We're gonna take our instance, and we are gonna get the singleton class of the instance, which lets you, again, define methods
30:00
just on that instance, and we're going to prepend the proxy. So we end up with something that kind of looks like this. We have a follower object that wraps a user object that has a proxy that sits in front of it. So we're gonna call follower, which is our wrapped instance. I wanna say do something with stuff. The first thing that's gonna happen is we're gonna hit the method missing call
30:22
in the follower object. Again, it's a basic object, so it has nothing on it. Unless you were to call instance eval or exec, you're gonna hit this. And on that method missing, we're gonna determine first if we should add the method to the proxy. Now we could have skipped this step, but Putter has some configuration where you can blacklist or whitelist methods,
30:40
or ignore methods from object or active record to kind of create, downplay some noise. So that's why we do that, but you could skip this part. So we have our add method. Then what we're gonna do is we have this data object, and we're gonna take that proxy, and we're gonna do instance exec. We're gonna pass that data in. That data has information about how to print.
31:00
It has information about the method that we're calling. We're gonna define a method with the method that stored in data. Then what we're gonna do is we're gonna call result equals super. So what we're gonna do in this case, since it's a prepended proxy, it's gonna call super, which is whatever method was on that object, the method we were trying to call to begin with. The only catch is we're gonna store the result. Then we're gonna put log info,
31:22
which you can actually configure with a proc. You can configure it to print anything, and it includes the line number. It figures out where the caller is coming from, and then we'll return the result so as if what you called was return the result anyway. Then if we have already added the method, you might hit the else. Either way, what we end up doing then
31:42
is we call the do something with stuff, and then the user object. And then as far as your code is concerned, it got the same thing back. The only difference was it printed out to the screen information about what just happened. So the other side of ptr is watch. So with watch, we have this watcher class, which actually holds kind of a singleton-esque registry
32:02
where the key is the singleton class of a class, and the value is that data object full of methods and stuff. And we'll have a user class. So we're gonna say ptr watch the user. And then what happens is this. We open up the singleton class of user, and we're gonna prepend an instance follower. We'll describe that in a second.
32:20
And the result of this watcher build proxy. Now, because we opened up that class, if you see the self there, that self is referring to the singleton class of user. So we're now defining things on the singleton class. So we can first build this instance follower. Normally when you create a method that allows you to change initialize,
32:41
or that allows you to new up an object, we use initialize. Because new actually deals with memory allocation. So what we're gonna do is override new kind of. We're still gonna call super on new because I don't wanna mess with the memory allocation. But the object that comes back from that, we're gonna store an array of objects so we know how many are being created. And then we're gonna call ptr.follow on that object.
33:00
So that object that now gets created is actually a followed object that is printing along, and we've set a label to be the instance number of whatever that array size is. So now we're both getting our class methods and instance methods followed. ptr watcher is gonna build a proxy with the singleton class of user, which is what self is. So build proxy looks almost identical
33:21
to what we did before. The only difference is we're gonna call proxy methods on user, which is a method that ptr adds that again checks the methods that are whitelisted and blacklisted. And it checks the existing class methods on the user. It does not, if you were to add methods to user at runtime, it would not get those. There are hooks in Ruby you can do when a method's added
33:40
if you wanna add a PR to a ptr to take care of that. You're welcome to. And then we do the same stuff. We define a method. We get a result of the super class and we put the info and return it. So that looks something like this. We have this object which has a proxy with four of the methods that are proxied, an instance follower and a user class.
34:01
So we user do something, which in this case is a class method. He's gonna call do something on the proxy. In this case, it won't do anything with the instance. And then it'll call do something on the user. User.new is gonna actually return this and let us know that we called that new method. So some are of what this does. We use method missing to call send with instance eval
34:23
inside of define method. We also prepend a proxy that calls super but puts a bunch of information. We're also able to override new, which follows all instances. So what do we do with all this stuff? Metaprogramming in a way is like learning another language,
34:41
another programming language. So if you're out like me, you'll go learn Elixir or Node or Haskell or whatever. And then you sit there thinking, I really wanna make something, but what? So I made this. There's a lot of power and cool stuff that you can do with metaprogramming. So you can contribute to things like Rails or Factory Girl or you can contribute to Putter. It's my first open source project.
35:02
So I will be very nice because I don't really know what I'm doing. So if you're looking for something to get into but you're afraid of people being mean, I'm just as clueless, PRs are welcome. So I made this, what will you make? Here's some resources I used. There's an article by Thoughtbot called Writing a DSL in Ruby.
35:20
It's kind of a slimmed down version of how Factory Girl is implemented, which taught me a lot about how metaprogramming can work. Ruby Under Microscript is a great book, which has been mentioned at a couple other talks. It talks about how Ruby is implemented, kind of at the C level at the VM. The first three chapters, if you are like me, were a little rough because they did talk about the VM and some of the C implementation, which was over my head.
35:42
So if you get to that point, you're like, I don't know if this is for me, you can skip the first three chapters, read about the other stuff, and then when you've kind of got more experience on your feet, go back and read them because they're really great. And Metaprogramming Ruby 2 is just a book about metaprogramming and it's also fantastic. It goes over a lot of this in more detail. It's really useful.
36:01
Here are all the photos I used. Thank you, Flickr. And finally, hopefully you have some tools to start meta-hacking away. I'll ask for questions. I'll probably do a couple and then if anybody has questions, I'll also just kind of stay up here if you don't want to ask in front of a crowd. And otherwise, have a great RubyConf.
36:23
Are there any questions? Yeah. It differs between Instance.exe and Instance.eval. Right here you see Instance.exe takes this data object in. So Instance.exe can take an object, or it takes really just an argument, which you could then use to load up with a bunch of data.
36:41
Instance.eval doesn't. You wouldn't be able to pass anything into Instance.eval. So it's a way to inject something from the outside world, from the outside environment, into that environment where you're evaling. Is the Singleton class related to the design pattern of Singletons? Not really. A Singleton design pattern would be if you have one object and every time you hit new,
37:01
it always returns the same object wherever that's called. Kind of the idea that there's just this one version of that object. That's also a very lazy definition of Singleton design pattern. But it's a way to just kind of guarantee that if I'm using a registry or a store, it's the same everywhere. The Singleton class is not really related to that. It's the idea of just that class as an object. I'm gonna stop now.
37:21
If anybody wants to come up and ask questions, happy to take that. Otherwise, have a great day. Thanks.