Talks

Things you can't do in Ruby

This video was recorded on http://wrocloverb.com. You should follow us at https://twitter.com/wrocloverb. See you next year!

Ever since I learnt the Ruby language, I thought I can use it to programme virtually anything. Well, I was wrong and I'd like to share this sad story with all of you.

wroc_love.rb 2013

00:00:18.199 Hi everyone, my name is Piotr Niełacny, and I wish to share my story about why I chose Ruby about three years ago. I went shopping to buy a Python book, but I couldn't find any. As I was leaving, I found a book about Ruby. I sat down to start reading it, and after the first chapter, I thought to myself, 'Oh my God, I can't do anything with this language.' After three years, I have learned a couple of things I can do with Ruby, and I would like to share that short list today.
00:01:12.640 First, let's talk about inheritance. You can do this in Ruby, but there are some syntax implications. You can't include a class into a class directly; you need to specify the module name after including it, or else you will get a type error, which is not fun. Another thing is object unfreeze. In the standard library, you have only the freeze method on each object, but you can't unfreeze an object, which can be quite annoying. Additionally, you cannot easily change the class of an object. Even though you know that the internal representation of the object is the same, the methods are different. Why is that? Lastly, for a bonus point, similar to CoffeeScript, you can't use instance variables as arguments in methods without some additional work. This means writing much more code.
00:02:24.680 Normally, I should end this presentation and say goodbye. However, what if I told you that you can do all these things in Ruby? I want to explain step by step how to create a solution without writing a C extension. You might think I will write a C extension, but that’s not true. First, you need to clone the MRI repository and find the Ruby headers. After this, I want to clarify that I won't be writing a C extension. So, what can we find in this file?
00:03:19.599 For example, we’ll start with constants. The first four lines you will find are the object IDs of the objects. For instance, the object ID of 'nil' is always four. When you execute 'nil.object_id', you will always get four; this is a magic number. We can simply move this to a Ruby module; it’s just a matter of copy-pasting. Next, we can find in this file aliases and also move this from the C file to the Ruby module. But how can we create aliases in pure Ruby? We can use the dynamic linker, which is part of the Ruby standard library. Note that the dynamic linker doesn’t have much documentation.
00:04:40.360 When you want to create an alias, you need to require the dynamic linker and then import the extending module. By executing 'type alias' this way, you can create aliases for types. Another thing you can find in the Ruby header file is structures. You can also use the struct method from the dynamic linker. In Ruby, every object has a basic structure and additional components attached to it. When we want to create more complex objects, we can use this basic structure and add additional elements to it.
00:05:55.000 After importing everything, we will have all constants, aliases, and structures in our Ruby module. To refer to an object in Ruby, we use the object ID introduced in Ruby 1.9. It’s the method 'object_id'; in Ruby 1.8, it was just 'id'. If we multiply the object ID by two, we obtain the integer representation of the object's pointer in memory. When we refer to this object, we can create an object of a class or a structure. For example, an 'air' object is composed of its basic structure and some additional elements.
00:07:01.680 So, how can we accomplish multi-inheritance? We can trick Ruby into thinking that an object is a new module because a module is a singleton, and the class of the object is pointing to the module. We can also unfreeze an object; it’s quite the opposite of freezing. By changing the pointer of a class, Ruby thinks that this is an object of another class. I can demonstrate that today.
00:08:56.000 Here, I have a plain installation of Ruby from RVM. When I require my Ruby file, I can create a class of my array and include it as a module. My array is just an array, but due to the same internal representation, we can change the class of this object. I will demonstrate this. Right now, the class is 'my ARR', and I will change it to the array class. I can define a method, open the class again, and include the array, creating an object. I can then change the class of this object to an array and execute it. I can also freeze the object, but if I try to modify it, it won't work. However, I have an 'unfreeze' method now, which allows me to modify the object again.
00:10:18.720 If you write in CoffeeScript, you may be familiar with lots of syntax, just like we have in Ruby. You will see how I encounter an error that tells me I can't use formal arguments. This error appears because when parsing the recognizer, you cannot pass an instance variable into method declaration in that way. To debug this, I find that Ruby does not recognize this instance variable correctly, so I need to adjust my implementation.
00:11:00.680 When I fix the method name to avoid using an instance variable, I also point out the issue with passing arguments at all. After this change, everything seems to work well. I need to change the name of the required argument because Ruby does not allow certain names. The implementation ensures the empty methods created are filled with instance variables, and each should work without errors.
00:14:30.800 Now, let’s create a class in Ruby using our new syntax. Everything seems to be functioning properly; we can create accessors to this instance variable. It should return the corresponding name. Fortunately, nothing is impossible when it comes to Ruby. If you want to include a module into a class, you can download my gem called 'include.' It works great on Ruby 1.3, but I haven't tested it on 2.0 yet, so you may want to give that a try.
00:16:43.080 It's noteworthy to mention how to unfreeze some objects. While discussing security, it’s possible to create an untainted object. If you find the proper constants, you can do this, but be cautious as it may not be thread-safe. There are some ways to un-extend objects, such as using a Ruby library available on GitHub. This task can be challenging to implement purely in Ruby.
00:19:22.360 Before I conclude, I want to address a question regarding class changes. If you set the class of your instance to something like 'Fixnum', certain unforeseen behaviors might occur. You can extend a class from array and make certain checks to see how things interface with each other. In case of any errors such as a syntax issue, you can troubleshoot until you find a working solution.