wroc_love.rb 2023

Ruby Rendezvous: Method Call, Proc, and Beyond

wroc_love.rb 2023

00:00:03.060 Hello, um, first of all, I would like to thank the organizers for having me here today. I would also like to thank all of you for coming to listen to my talk, especially considering it is pretty early and yesterday's party lasted late into the night. I really appreciate your presence here.
00:00:39.300 I want to be honest with you: I'm very stressed right now. Even after so many years of work, I'm still not very comfortable speaking English. When I get stressed, I tend to speed up my presentation, so who knows? Maybe after 50 minutes, we won't be in this talk anymore, and you'll have a break for a short nap.
00:01:05.760 Now, with that said, we can start. I had a problem with what to call this presentation. I have a vision of what I want to say, but it consists of small pieces in Ruby that are not directly connected. This made it very hard for me to find the right title. I thought about it myself for a few weeks, then asked my husband for help, and ultimately, I decided to ask AI for suggestions.
00:01:31.640 ChatGPT suggested "Mastering Ruby Methodology and Harnessing the Power of Procs: Mapping Techniques and the Procs Method in Ruby." That set the bar pretty high—this talk sounded very serious, like a university presentation, which might be a bit boring. So I asked for another title: "Unlocking the Potential of Ruby: Diving Deep into Method Invocation, Procs Utilization, and the Intricacies of Map and To Proc." I liked the idea of ‘unlocking the potential,’ but the rest still sounded very boring and theoretical. So, after some tweaks, I got it: "Ruby Rendezvous: Method Calls, Procs, and Beyond." I think that's a pretty nice title—it's interesting without being overly technical.
00:02:02.640 Based on that title, you should have a general idea of what the presentation is about. This will not be about RaceEventStore; it will focus more on Ruby itself. But did you know this is not my first speech on this stage? The first time I spoke at a Ruby conference was in 2020, the pandemic year. The funny thing is that people congratulated me on my first speech at this conference, even though we didn't have the conference that year.
00:02:34.980 As already mentioned, I joined the Ruby community around 2011 or 2012, which is over ten years ago. Every time I come here to Wrocław, I feel a bit nostalgic—well, maybe even more than just a bit. So be aware that this talk will have a little bit of nostalgia as well. I started with Ruby 1.9.3 and Rails 3.0, and I even remember the old logo of RubyConf.
00:04:12.299 I wasn't present for all editions, but maybe some of you were. Is there anyone here who has been around since then? Okay, nice! I also remember the lighting talk from 2012. It's not from this conference, but it's certainly worth a look. By the way, all the links and sources will be at the end of the presentation, so don’t worry. I will share that with you along with my slides.
00:04:58.500 Let's get back to the topic. I liked this lighting talk so much that I started looking for fun, interesting, and little-known Ruby features, and here we are! I would like to share some of them with you today, and I hope you'll discover something you didn't know about Ruby during this talk. But first, let's take a look at what's changed between the old lighting talk and the current version of Ruby.
00:05:35.220 So, first, `a = a` in Ruby 3.2 still works without throwing any exceptions. If we declare `method_missing` in IRB, we will get a 'stack level too deep' error, and the console will close. The `better_words` method no longer works in the Ruby console like it did before; that felt like real magic when I first saw it. However, we can do something like this: we can combine multiple strings with spaces between them without any additional method in Ruby.
00:06:45.620 We can also mix different ways of declaring strings and do it across multiple lines. This feature isn't only available in the latest version of Ruby; it also exists in older versions, as well as in Python, C, and C++. If you can think of another language where this method of combining strings works, please let me know.
00:07:56.219 Now, how is this possible? To answer this question, we need to look at the `pars.y` file in the Ruby source code for the implementation details.
00:08:06.060 This file uses notation similar to Backus-Naur Form (BNF), which allows us to provide information on how the language should look through a metaprogramming approach. It is utilized by YACC, a compiler-compiler program for parsing generation. The output of YACC will be in C, meaning Ruby either treats it as a string one or a recursive definition of concatenation involving string one and the concatenated string itself.
00:09:20.699 So after that, I began to think about what else could be interesting in Ruby. For sure, calling a method in Ruby is an interesting topic. We have several ways to call a method, but I will not show you all of them.
00:09:48.600 Greg VTech wrote a great article about this, where he detailed twelve ways of calling a method. I know two more, so I will only focus on those two. I will stick to the same convention as we can see in Greg's article.
00:10:21.960 We have a user class, and we can create an object to call a method as we often do. So far, so good. The first interesting method-calling strategy I found was in Nick Schroeder’s Ruby Archaeology talk—hope I pronounced his name correctly. It was a great talk, and if you haven't seen it yet, I highly recommend it!
00:10:51.240 By the way, I like the title ‘Ruby Detective’ that Nick used to describe what he does. I often think of myself as a Ruby detective, especially when I'm debugging some problem in code. The method-calling strategy he presented was used in the Merb framework—remember that framework? It was an interesting time when the Merb core team merged with the Rails core team. I included a link at the end for those who haven't heard the story.
00:12:08.820 The way to call a method involves using double colons, which looks like class inheritance but actually works. In this case, we have thirteen different ways to call a method in Ruby. According to Greg's article, the double colon can be described as a constant resolution operator or, better yet, a scope resolution operator. Usually, we use it to manage the scope of classes and modules.
00:12:56.760 While it's not very common to find places where we call a method using double colons, it's crucial to note that calls of double colons are not exactly the same as using a dot. We can interchangeably use them to call a method, but not when managing scope. Let's imagine that 'user' is within the scope of the workplace: we can use the double colon here. However, we cannot do it by merely changing the scope as it would throw an error saying 'user is not a method of workplace.'
00:14:40.100 This method of calling was novel for me when I first encountered it in Nick's presentation. While it's not entirely unknown—it's noted in Robocop's documentation, for instance—this is something I've rarely applied in practical scenarios. The documentation states, 'Use double colons only for referencing constants, including classes and modules. Do not use double colons for simple method calls.'
00:15:11.260 Given this information, we might prefer using a dot over double colons when we call a method. While I’m not here to judge whether this is a good or bad decision, it’s a fact that we generally prefer dot notation over double colons in our method calls.
00:16:22.720 The fourth method of calling a method we will explore today involves using a proc. A proc object is an encapsulation of a block of code that can be stored in a local variable, passed to a method or another proc, and invoked. Procs are essential in Ruby and form the core of its functional programming features.
00:17:23.560 So how can we use a proc to call a method? We can invoke it in several ways: through `call`, `yield`, or square brackets. All these methods of calling are detailed in Greg's article, but I will illustrate one more way of doing this.
00:17:41.820 Before continuing, let’s examine the implementation of `to_proc` for a symbol. I will check that in the Ruby news, which is Ruby written in Ruby. Although the last update of Rubinos was in 2020, it's still easier to read in Ruby than in C, at least for me.
00:19:12.720 Also, do you remember the IronRuby project—the Ruby in C# from 2011 that was designed for the .NET framework? That was quite a long time ago!
00:19:56.900 Moving on, we have a proc method. First, we assign our symbol (for example, 'hello') to a variable called `symbol`. We do this because if we use `self` in the block below, and the block is passed to instance_eval, `self` becomes the object on which instance_eval was called, rather than our symbol. To manage the context correctly, we assign it to an instance variable.
00:20:30.660 Then we create a proc, combining all the arguments into the `args` variable. We also have a variable `b` representing the block code. Importantly, we check that we have at least one argument as the `call` method must have an argument to avoid the 'no receiver given' error.
00:20:53.340 The most crucial step is taking the first argument (which is our object) to call the method on and sending the method (which is in `symbol`) to that object. We then process the rest of the arguments along with the block. Let's see how this works for our user example.
00:21:45.320 We have our proc method calling `hello`. We assign `hello` to `symbol` and create a block that checks if there’s at least one argument. We need this user object and then we can call the `hello` method on this user object without any additional arguments.
00:22:34.680 Now that we know how the code works, let’s check another way of calling a proc. I didn’t see it in Greg’s article, so I asked him about it. Did you know that you can call a proc using triple equal signs?
00:23:20.400 Greg replied that he didn't even know that approach, and he also asked when it was added to Ruby. The first version of Ruby where I found this was 1.9.1. The triple equal sign is a reliable older way of calling a proc.
00:23:54.540 Also, you can leverage the triple equal sign in a case statement. I prepared a method for this example that includes a proc to check if a phrase includes the term 'message.' When we use our message-type proc in the case statement, if the condition is satisfied, it returns 'it's a message type.' If not, then 'other type.'
00:24:36.580 For instance, when we call the `check_type` method with a string that does not contain 'message,' we will receive 'other type.' However, if we call it with a string that contains the phrase, we get 'it's a message type.'
00:25:15.780 Now, what could be even more interesting in Ruby? Calling a method with arguments, of course! The next consideration is how to call a method with an argument using a proc. Let's return to our user class.
00:25:55.850 We will create a method `say_my_age`. When I did this, a pretty bad joke popped into my head about Beyoncé's song 'Say My Age, Say My Name.' We create another instance of the user and can call our method `say_my_age` with age '15.' This may not reflect my actual age, but I can assure you that I often seem like a teenager. I tried hard today to look more serious. You can tell me if that worked!
00:26:53.460 So we can obviously call this method normally. But how do we use the proc for that? We can do this in several ways: by putting a second argument in the call or in the yield. Let's check one more time the definition of `to_proc` with an argument.
00:28:05.740 We have `to_proc`, assigning `say_my_age` to our symbol variable. After creating our proc, we'll check if `args` is empty. In our case, it's not because we have the user object and the number 15. Then we send the `say_my_age` method to the user object with 15 as an argument.
00:28:58.679 This is done without a block, keeping it simple. Now, can we achieve something similar with a triple equal sign? If we attempt something like that, it will throw an error. However, changing the space to a dot will work.
00:29:27.720 This works because we won't use operator syntactic sugar anymore and will call it as a normal method. Let's look at other possibilities for calling a proc with an argument using the triple equal sign.
00:30:21.940 We can utilize currying, which is not the kind of spice but a method, and then use square brackets to call an argument, or we can call normally as before. I like the complexity of this line, so I want to focus on the curry method for a moment because it will be helpful later in the talk.
00:31:05.120 The curry method allows us to tell the proc how many arguments we need to run the logic. In our case, we expect two arguments. If we run the first proc with fewer arguments than declared, we will receive a curried proc in return, indicating it's partially executed. Only when the required arguments are accounted for is the method actually called.
00:31:59.220 We can achieve this in a rather fancy way using double brackets—one pair of brackets isn't sufficient as it will result in an exception. Additionally, I’d like to discuss using procs with maps and the ampersand shortcut.
00:32:40.100 People often think that this is a shortcut for a standard line, but to be honest, it serves as a shortcut for calling a proc as a block argument. Thus, we have our proc and will call it within our map.
00:33:28.600 I started contemplating whether it's possible to call this map with an ampersand while including an additional argument. Unfortunately, doing this will not work. If we try that, we will receive an exception. However, there are alternative ways to accomplish something similar.
00:34:10.379 In this case, if we want to increase the number in our enumerable object by 2 instead of just 1 as before, we can do it differently. As I previously mentioned, we can't do it as conventionally, but we can do it using a method that generates a method object.
00:35:12.300 In this instance, we want to create a method that takes a symbol 'plus' as an argument. This method is associated with a specific object, not a class. So when we call it for our procs, we can pass it to our enumerable. The first argument is already provided through the object, while the second comes from the enumerable.
00:36:07.040 Next, let's explore other methods to call the proc. We can convert our enumerable to an object, inject an argument into it, and in our case, that argument is 2, followed by the plus method with an ampersand, just like we did earlier.
00:37:01.860 Another way to invoke it is by converting our 'plus' method to a proc, specifying the number of arguments, which in this case is two. With that, we can pass just the first argument; the rest will be treated as a proc to be executed within the map.
00:37:57.300 Since we are discussing maps and ampersands, did you know you can call a hash with a map? A hash implements a `to_proc` method, which we can give to the map, and it will return our enumerable. This works because when we invoke the `to_proc` method on a hash, we convert it to a proc in a specific way.
00:38:22.800 Let’s return to implementation details one last time to examine the `to_proc` method for a hash. We have `self`, which is a hash, and create a method object with brackets, turning it into a proc.
00:39:01.680 So, a hash is the last basic object where the `to_proc` method is implemented. We see this for both symbol and method objects as well. I don’t count procs themselves, as it’s obvious we can invoke the `to_proc` method.
00:39:40.560 As we near the end, I have one more tip. When dealing with maps in Ruby and how to use the iterator variable, we can do this the usual way by putting an argument in pipes, but we can also use the underscore notation.
00:40:04.160 This will work not only for a single argument but also for multiple arguments using `_1`, `_2`, `_3`, and so on.
00:40:34.520 So that’s basically everything I wanted to say. Let's summarize it using a simple plus example.
00:40:59.620 We can use a regular method call to send a message directly to the object, including arguments, or we can call the method using the triple equal sign or the standard call method.
00:41:26.600 That's all! Thank you for listening. You can find me online at Woman on Rails. The slides are available at womanonrails.com/presentation. Here are my social media links, including my blog and X (formerly Twitter). Thank you very much!
00:42:15.300 I think it's too early for questions.
00:42:21.300 Thank you for this presentation! It was exciting to learn something new about Ruby. Have you had a chance to use any of these techniques in your ongoing projects?
00:42:50.300 Yes, for sure! I often use the triple equal sign for the proc and call case. When you use Rails, you often work with some of these small proc parts. Some of the examples I showed are more like fun topics to think and discuss about, but not necessarily production-ready solutions.
00:43:09.300 I do use the ampersand with method names, but not with additional arguments. Some of them can be applied in projects, but not too often. Thank you!
00:43:32.300 Any other questions?
00:43:44.500 Do you use function concatenation in your work? There are some functional programming things where you can call a proc within a proc and pass it along in a block.
00:44:04.300 I’ve tried that, but it can often be hard to read. It’s more of a fun Ruby detective kind of challenge.
00:44:20.300 Yes, I agree; it's mostly for fun. When I do that, I don’t do it in production because it often isn't as readable as handling it with join or plus.
00:44:31.300 In terms of readability, have you compared speed differences between the methods you presented?
00:44:49.300 No, I haven’t done that. However, I’ve been curious if there are any other benefits aside from the enjoyable aspect.
00:45:02.100 Thank you!
00:45:11.700 Anyone else? Okay, I think that’s it. Thank you for the amazing opportunity!