00:00:17.440
Hello, everyone. I'm Patrick Farley, a developer with ThoughtWorks, and today I'm going to discuss Ruby internals.
00:00:22.720
To clarify what I mean by Ruby internals,
00:00:28.160
I have some JRuby slides and some Rubinius slides, but mostly, I will focus on MRI stuff.
00:00:34.719
Most of you are likely using MRI, which is Matt's Ruby interpreter, especially if you're on a MacBook Pro.
00:00:41.280
Now, I think it's fair to ask why you should care about Ruby internals.
00:00:47.680
Digging into this topic takes some effort, so what value does it really provide?
00:00:54.559
To illustrate this, I'll look at some Ruby code.
00:00:59.920
I'll focus on the weirder side of Ruby, but it's still pretty standard stuff.
00:01:06.080
For instance, I am defining a module here and creating a method, self.bar, within the module Foo.
00:01:11.680
Anyone who has worked with Ruby knows that there’s trouble ahead.
00:01:17.600
If I’m using modules purely for namespace purposes, like creating a function library called Foo, I should be okay.
00:01:23.520
However, if I intend to use modules for code sharing, meaning multiple inheritance,
00:01:29.280
that’s where I’ll run into trouble.
00:01:35.040
Because anyone familiar with Ruby realizes that when I include this module in a new class, let's say Baz,
00:01:42.079
I won’t get bar as a class method.
00:01:48.079
We learn this early on when we start with Ruby: you can’t bring class methods along when you include a module.
00:01:53.360
It’s peculiar to think about why that is.
00:01:59.520
Similarly, in this example, I'm defining a basic module called Laughable.
00:02:04.560
It has a method, laugh, and this should seem familiar to many people.
00:02:11.599
I extend the class Clown with Laughable, which gives me a class method called laugh.
00:02:16.800
Interestingly, if I create a client object and then extend the Clown object with Laughable,
00:02:22.000
I get an instance method on Clown.
00:02:28.080
Again, this might seem straightforward, but it’s a bit unusual.
00:02:37.280
Ruby provides both length and size methods on arrays, yet it appears to overload extends based on context.
00:02:44.480
There are various syntaxes to define a class method.
00:02:50.640
Many know that when you open up a singleton class and define a method, that magically becomes a class method.
00:02:58.400
The question remains: why is that the case?
00:03:05.440
Let’s look at a specific behavior in Rails.
00:03:11.120
When you call id on nil, it returns four.
00:03:18.560
By default, Ruby was returning four.
00:03:25.040
Rails assumes that most likely you didn’t intend to introduce the number four into your codebase this way.
00:03:33.440
You probably don’t want to do like three over id of nil.
00:03:39.440
So, Rails warns you: it throws an error and says, "By the way, you probably don’t want to do that."
00:03:46.239
It's surprising how this works.
00:03:51.440
By a show of hands, how many of you have read the Pragmatic Programmer?
00:03:58.400
The Ruby community is fantastic.
00:04:03.760
I acknowledge that I'm preaching to the choir a bit here.
00:04:11.280
In the Pragmatic Programmer, there’s a section that discusses programming by coincidence, featuring a programmer named Fred.
00:04:18.400
Fred writes code that works, then writes a bit more code that works, and eventually, his code stops functioning.
00:04:23.840
He struggles to figure out why and faces challenges in debugging.
00:04:28.880
The insight from that practice is that Fred doesn't actually know why his code worked in the first place.
00:04:34.720
My viewpoint is that a significant amount of this occurs in the Rails community.
00:04:40.400
Not just in Rails but in the broader Ruby community as well.
00:04:46.240
When diving into more advanced features of the language, many developers are using meta-programming techniques by example but lack the understanding of why those techniques work.
00:04:52.000
This is a craftsmanship issue.
00:04:57.440
It's our responsibility to thoroughly understand the tools we utilize, rather than waiting until we're in a panic like Fred.
00:05:02.720
We've all encountered moments with meta-programming when things don’t work, leading us to believe that meta-programming might be bad.
00:05:09.919
The intent isn't to discourage using meta-programming, but it is an essential tool to have in our tool belt.
00:05:17.919
If you're developing Ruby-based Domain Specific Languages or more intricate applications, understanding why meta-programming works is crucial.
00:05:25.039
Much of what you encounter concerning modules, mixins, metaclasses, and singleton classes is fundamentally about method dispatch.
00:05:34.720
Matt had many options for implementing specific behavior or mixins;
00:05:40.400
however, he made the elegant decision that method dispatch would always function the same way in MRI with no exceptions.
00:05:46.240
This decision elegantly constrains the implementation options for instance-specific behavior, class methods, modules, and mixins.
00:05:52.000
If you grasp how method dispatch operates in Ruby, it becomes much easier to learn how all these other aspects work.
00:05:59.360
Remember, method dispatch always functions the same way.
00:06:04.960
Regarding book recommendations, I recommend the Ruby Hacking Guide by Minero Aoki.
00:06:11.520
Although it's written in Japanese, there are some partial translations available online.
00:06:17.440
It covers much of the material I’ll discuss today.
00:06:23.760
Although somewhat dated, it still contains valuable insights.
00:06:29.360
I encourage you to Google for partial translations, and if you know Japanese, consider contributing to the translation effort.
00:06:35.760
I'd also like to mention another book that has received positive feedback.
00:06:40.800
Now, let's delve into method dispatch.
00:06:47.120
I have a Ninja class featuring a method called b_awesome, which returns the symbol guitar_solo.
00:06:53.840
I know this sounds silly; you may wonder why guitar_solo is a symbol.
00:06:58.960
I've created an instance of Ninja named Bob and I call the b_awesome method.
00:07:05.760
The question is, how does the b_awesome method get connected to the method information found?
00:07:11.760
Interestingly, I made a point to add props to my slides before the sombrero concept appeared.
00:07:18.000
The sombrero concept was a humorous notion, but it was difficult to visualize.
00:07:24.960
Unfortunately, my efforts to create a visual representation of that idea fell short.
00:07:31.840
I tried my best to insert an image but faced issues;
00:07:37.840
needless to say, I gave up on that.
00:07:42.240
Now, the following is a c-struct r object that represents Ruby objects.
00:07:48.000
In Ruby, everything is an object, and I'm referring to standard objects now.
00:07:54.080
Here's what it looks like without any reduction: a couple of data elements.
00:08:00.640
The first is this basic struct, which we'll describe, and the next is this st_table iv table.
00:08:06.160
So, while it seems complex, let’s break it down.
00:08:12.160
Heroes fans, ever notice how certain characters reflect specific traits?
00:08:18.000
There are analogies in the relationship between C and Ruby programmers.
00:08:25.200
They often express confusion when they initially experience C programming concepts.
00:08:32.560
Given the second part, the st_table instance variable table or sc_table acts as a hash implementation.
00:08:39.360
The iv stands for instance variables, so Ruby knows an object’s instance variables, stored in a hash.
00:08:45.120
The other aspect Ruby recognizes is tied to this r_basic.
00:08:51.680
There are only three things everyone knows about an object: its instance variables, its class, and its properties collection.
00:08:58.080
Importantly, behavioral aspects are missing; Ruby functions differently from prototype-based languages.
00:09:03.760
This is a useful perspective when thinking about metaclasses, singleton classes, and so on.
00:09:09.760
Understanding that Ruby’s design encourages storing behavior at a class level is key.
00:09:15.600
When discussing Ninja and b_awesome, this behavior must reside somewhere.
00:09:22.640
Let’s take a look at the struct that represents a class.
00:09:31.360
It's interesting to consider classes as objects in MRI.
00:09:37.120
From a usage perspective, they are, but from an implementation level, it's a different story.
00:09:44.080
Classes—on the surface—stem from a unique struct, as you're probably seeing.
00:09:50.560
The first two elements you'll see are familiar and an instance variable table.
00:09:57.480
But two important distinctions exist: a method table containing behavior and a superclass pointer.
00:10:04.080
Trajectory of method dispatch is crucial.
00:10:09.680
When sending a message to an object, the interpreter dereferences the class pointer.
00:10:16.000
It looks for the method in the method table hash.
00:10:23.200
If not found, it processes the super pointer iteratively.
00:10:28.240
The interpreter keeps referencing until ultimately hitting null and declaring method missing.
00:10:35.680
Visually, this works like this.
00:10:41.600
The class pointer dereferences and searches for the method within that hierarchy.
00:10:48.720
After understanding method dispatch, we can discuss singleton classes and instance-specific behavior.
00:10:56.080
Consider a new Ninja named Joseph.
00:11:03.760
But here’s the twist: Joseph is a pirate hiding as a ninja.
00:11:10.240
He has a specialized method called speak.
00:11:16.640
If you're familiar with Ruby, you might recognize this as defining joseph.speak.
00:11:22.560
This is one way to add instance-specific behavior.
00:11:28.640
In this case, I’ve added the method directly to Joseph.
00:11:34.560
When he speaks, he says 'arr', while Bob, being a ninja, can’t articulate anything.”},{