RailsConf 2021

Implicit to Explicit: Decoding Ruby's Magical Syntax

Implicit to Explicit: Decoding Ruby's Magical Syntax

by Justin Gordon

In the talk titled "Implicit to Explicit: Decoding Ruby's Magical Syntax," Justin Gordon explores the unique characteristics of Ruby, particularly its implicitness, and how it can create a sense of "magical syntax" compared to languages like JavaScript. Gordon aims to demystify Ruby's syntax for Rails developers, emphasizing the importance of understanding the underlying Ruby code behind Rails' Domain Specific Language (DSL).

Key Points Discussed:

  • Understanding the Rails DSL: Gordon highlights how many Rails developers struggle to explain the Rails DSL in terms of basic Ruby code. By breaking down this connection, developers can improve their coding skills beyond mere code copying.
  • Comparison to Java: Using a simple Java code snippet, he illustrates Java's explicit nature, pointing out the verbosity and complexity of Java web applications compared to the more succinct nature of Ruby code used in Rails.
  • Implicit vs. Explicit: Throughout the talk, Gordon emphasizes the differences between implicit messages in Ruby (where parentheses are optional) and the explicit structure required in JavaScript. He demonstrates how Ruby code often relies on an implicit self, making it less clear at first.
  • Examples of Code Translation: Gordon provides several examples of translating lines of code from implicit to explicit forms. For instance, he explains how the has_many relationship in ActiveRecord is simply a method call that can be made explicit by adding "self."
  • Pry for Debugging: The speaker introduces Pry as a powerful debugging tool that can be used to understand Ruby code execution. He demonstrates how to use Pry to inspect the value of self, check method calls, and view documentation directly from the context of a Rails app.
  • Method Missing in Rails: Gordon discusses how Ruby's method missing functionality contributes to the DSL's magic, particularly in Rails' configuration files, allowing for dynamic method creation based on the context in which they are called.

Conclusion and Takeaways:

By the end of the presentation, Gordon aims for attendees to view Ruby from a new perspective—recognizing the value in understanding its syntax rather than perceiving it as magical. He encourages developers to experiment with the Ruby code they encounter and utilize tools like Pry to deepen their understanding. Gordon concludes with a reminder that clearer comprehension of Ruby syntax can enhance programming skills and reduce reliance on copy-pasting code.

00:00:06.259 Aloha from beautiful Maui! My name is Justin Gordon, and I'm here today to give you my talk for Rails Conference 2021: "Implicit to Explicit: Decoding Ruby's Magical Syntax." I've wanted to give this talk for a really long time. One of the reasons I want to present it is that I interview a lot of Rails developers as I'm hiring right now.
00:00:12.120 One of the problems that I see is that many Rails developers cannot explain how the Rails DSL works in terms of basic Ruby code. Once you can understand how the Rails DSL is really just plain Ruby code, it will open up your understanding of what you're doing.
00:00:25.019 You're not going to just be copying and pasting code without any understanding. You will be able to look at the code and think, 'This is exactly what the Ruby interpreter is probably thinking. I know where to go get the documentation. I know how to debug it.' I definitely had a lot of these same issues when I started Ruby a long time ago.
00:00:38.700 That's why I'm so excited to give this talk today. So, without further ado, let's get started. How did I get into Ruby on Rails? For many years before Ruby on Rails, I was writing Java code. Java is known as a general-purpose language.
00:00:50.039 So what is a general-purpose language? It's a language you can use to do pretty much anything. This is opposed to the Domain Specific Language (DSL) that we will talk about today.
00:01:01.920 Here's a little bit of Java code. It's very basic and just a lot of code to say 'Hello, world,' essentially. Every little character in there is necessary. All the prints, braces, etc. That's what makes Java explicit.
00:01:12.480 Now, this explicitness makes Java a terrible DSL because if you remember what it's like to write a web application in Java, there's just so many files and complications. It's quite cumbersome, which is why Ruby on Rails took off.
00:01:25.019 With Rails, we have a Domain Specific Language. This means that when we write some Ruby code, it speaks directly to the problem we're trying to solve: building a web application.
00:01:38.100 For instance, if you look at this Rails code: 'class Person < ApplicationRecord; validates :name, presence: true.' There are hardly any extra characters in this code. This code succinctly defines the Person class and specifies validation on the name.
00:01:49.200 This simplicity is what we mean by a DSL. However, this code also contains a lot of implicit elements, making it a great DSL but hard to understand. Those are the aspects I will cover in this talk.
00:02:02.700 These topics include what exactly 'self' is, as well as variable declarations. In Ruby, you don't have to explicitly declare variables, and there are nuances like parentheses that can be left out.
00:02:11.400 A long time ago, I learned Ruby on Rails from the Rails Tutorial, and I imagined it would be straightforward given all my experience with Java. But the reality was that when I read Ruby code, I felt very much lost.
00:02:27.840 I was looking at the syntax wondering, 'What exactly is going on here?' It felt somewhat like the compiled code I was used to, and I struggled to distinguish between Ruby and Ruby on Rails.
00:02:43.800 I confess that often I resorted to just copying and pasting code without fully understanding it. How many of you felt like that when going through the Rails tutorial? I know I did.
00:02:58.560 Even though I wrote a lot of code, I didn't truly grasp that all that DSL code was just plain Ruby code. I could have added print statements or used 'binding.pry' to see exactly what was going on.
00:03:12.120 So, let's fix this. Can we learn to read the code like the Ruby interpreter does? I want you to look at this code, and after this talk, be able to understand it better.
00:03:22.200 It is important to comprehend that you shouldn't simply copy and paste code; you must understand it. After this talk, you will know how to read that code and realize it's not some magic Rails DSL.
00:03:35.280 It's just plain Ruby code. These are messages we're sending to objects, involving arguments and variable declarations. Many of these elements are straightforward.
00:03:49.080 I want to examine some fundamental building blocks: assigning values to variables and sending messages to objects. Now, let's compare JavaScript and Ruby.
00:04:03.840 JavaScript is very explicit, while Ruby is often implicit. Many of you, especially newer Ruby on Rails programmers, are probably familiar with JavaScript.
00:04:14.640 In JavaScript, parentheses always mean function invocation. Without parentheses, there is no function invocation. That's absolutely critical.
00:04:27.660 How many Ruby on Rails programmers have done some JavaScript and forgotten to include parentheses? I certainly have. If we look at a line like 'window.close', that's just a value; it could even be a function in Ruby.
00:04:41.880 In Ruby, parentheses are optional. For example, when we say 'has many :micro_posts dependent: :destroy,' it looks unclear at first—but as soon as we add parentheses, it becomes clear.
00:04:56.580 We can see that 'has many' takes an argument of ':micro_posts' and ':dependent => :destroy' indicates a hash.
00:05:06.420 Another critical thing in Ruby is that zero-argument method calls are indistinguishable from values. For example, 'user.first' is implicitly understood.
00:05:20.400 Although nobody writes the parentheses explicitly, you could say 'user.first()' to be clear that you're invoking the method. This means you're sending messages to that object.
00:05:33.840 Let’s flip between implicit and explicit. For example, an implicit 'has many' on a user model versus an explicit 'self.has_many' shows us exactly what's going on.
00:05:42.300 An early Ruby on Rails programmer might view this syntax as magical, struggling to understand what 'has many' entails. The truth is, it's simply a method called on 'self,' where 'self' is the user class.
00:05:58.740 Now, let’s take a break from the slides and look at how we can edit 'usertest.rb' and see if the tests still pass. This is a great way to validate what I've just explained.
00:06:08.880 If I make a minor edit, such as adding space, we can then check our tests. Ensuring the tests pass confirms that we're editing the right file.
00:06:22.680 Let's observe whether the changes affect our code. If it's affected, it's great. They still pass! This demonstrates how you can explore Rails code more confidently.
00:06:34.040 Next, let’s delve into custom operators. These can make the Ruby code seem quite magical, but with the right knowledge, we can demystify them.
00:06:46.080 For instance, we might encounter code featuring custom operators. Knowing how to interpret this code enables us to analyze its functions effectively.
00:06:58.440 Many beginning Rails programmers struggle to explain these operators in simple terms of Ruby code functionalities. It's important to understand it contextually.
00:07:12.780 Now, moving forward, let's assess a JavaScript code example. Remember that JavaScript doesn’t have implicit receivers, except for the global window object.
00:07:26.640 Consequently, you can write 'confirm("hi")' and it’s equivalent to 'window.confirm("hi")'. Yet, missing out on using 'this' with method calls results in errors.
00:07:39.240 In contrast, Ruby's implicit self means messages are sent with built-in context. This identification enables understanding of what the implicit 'self' is referring to.
00:07:51.780 For instance, you may have 'class User: has_many' followed by method calls. However, this can also be written out explicitly as 'self.has_many' for clarity.
00:08:03.060 This level of clarity reveals where methods like 'has_many' originate and shows their arguments clearly. Going from implicit to explicit enhances comprehension of the code.
00:08:18.600 In this diagram, I'm illustrating the implicit message receiver, followed by the message name and arguments. Recognizing these elements sheds light on the DSL built on Ruby.
00:08:30.540 Essentially, 'self' serves as an implicit message receiver, almost never explicitly stated. This understanding contributes significantly to grasping Ruby code nuances.
00:08:46.920 Now, you may wonder why 'pry' is included in this discussion. One important aspect of 'pry' is that it allows us to see what 'self' is at any given point in our code.
00:09:01.920 In a quick demo, I will put several 'binding.pry' calls within our Ruby code to allow the code execution to pause at a defined point.
00:09:16.560 After starting the Rails server and including a few 'binding.pry' commands, we can see the code execution halt at the break points established.
00:09:29.520 When we resume execution, we will see what 'self' refers to within various parts of our Rails application, making it a very useful debugging tool.
00:09:43.680 In one instance, we can inspect the context of 'self' before and after executing particular code lines. This insight provides greater control over our debugging process.
00:11:09.300 Let’s look at explicit variable declarations. In JavaScript, declarations must be explicit using 'var', 'let', or 'const', unlike Ruby’s implicit declaration.
00:11:29.880 Implicit declaration means syntax for creating a variable often takes precedence over method calls in Ruby. For instance, 'result = ...' is treated as a variable declaration.
00:11:45.120 When defining methods, like a setter method 'email=', you must be explicit with 'self.email' instead of just 'email' to avoid variable conflicts.
00:12:05.940 Otherwise, Ruby could mistakenly interpret it as creating a new local variable. When using 'self.email', it denotes that we are explicitly calling the method.
00:12:18.540 Let's go into another Rails controller example. Many developers find this code mystifying due to the layers of abstraction at play.
00:12:31.620 This code handles returning XML or JSON based on browser requests. Internally, it involves method calls and blocks that interact with format types.
00:12:56.520 Understanding the explicit nature of the code demystifies the operations being performed, therefore leading to a clearer comprehension.
00:13:09.180 For example, the 'respond_to' method sends back the appropriate format and manages blocks adequately, as the code acts on provided methods.
00:13:24.840 Lastly, we can acknowledge differences in return syntax as well: JavaScript requires explicit returns while Ruby allows implicit returns.
00:13:39.960 In JavaScript, you frequently have to import everything explicitly—no surprises. In Ruby, this isn't the case; it utilizes constant lookups, offering more freedom.
00:13:54.720 Now, enough of the theoretical discussions. Let's explore 'pry' further. There are various techniques I will showcase that allow better comprehension of Ruby on Rails code.
00:14:09.480 Using this, you can quickly iterate through code, make interactive changes, and experiment with outcomes—all useful for fine-tuning your Ruby skills.
00:14:24.300 Let's start a quick demo of 'pry' to gain more insights. I'll configure the Rails server using a special script to prevent multithreading issues.
00:14:38.580 As I delve into relationships and controller methods, you'll notice how intuitively manageable the code becomes with the right tools.
00:14:52.860 While exploring further, I will demonstrate how to quickly attain necessary information, documentation, and shortcuts within 'pry'.
00:15:07.560 In one instance, I can search for documentation on methods by leveraging 'pry', significantly reducing time compared to a web search.
00:15:22.680 Let's continue our demonstration, leveraging 'binding.pry' in various contexts to retrieve insights effectively.
00:15:35.460 As I step into 'self', we can explore further, dispelling any confusions and thoroughly understanding each step in the code.
00:15:51.960 Exploring the relationships and how they correlate in our Rails application offers clarity that illustrates the underlying principles.
00:16:07.260 Now let's get back to configuration settings, where understanding implicit or explicit self plays a significant role.
00:16:24.600 During my explorations, I’ll be investigating how these configurations are built, specifically regarding missing methods.
00:16:39.780 This approach allows an array of values to be set through 'method_missing', thus making the DSL relevant and usable.
00:16:56.160 For example, if we had a configuration statement that didn’t exist, we could utilize 'method_missing' to manage those missing methods.
00:17:09.840 Let’s dive into 'config.environment.rb' and witness firsthand the effects of binding on implicit variables and configurations.
00:17:30.480 It's vital to understand the top-level object in Ruby and how it works with configurations to extract maximum functionality.
00:17:45.000 I’ll perform a live demonstration, showcasing what 'self' is when attempting to access or set configuration variables.
00:17:57.720 From this understanding, we can set and retrieve configurations accurately by employing 'method_missing' effectively.
00:18:11.640 You will see how calling non-existent methods prompts the system to execute code that can intelligently handle these calls.
00:18:25.680 By observing how we can dynamically create and access configuration values through method calls, you’ll appreciate Ruby's flexibility.
00:18:38.640 After reviewing this process, you should now feel more confident about working with Ruby on Rails and recognize its underlying simplicity.
00:18:52.440 This thorough examination will improve your ability to write cleaner and clearer Ruby code.
00:19:06.300 Ultimately, as you engage more deeply with Ruby and its constructs, it will feel less magical and far more approachable.
00:19:18.720 So, to sum up: the key takeaway is that when Ruby code appears magical, try to make it explicit in your mind.
00:19:34.080 Utilize 'binding.pry' to explore what’s occurring under the hood and become more confident in your Ruby programming.
00:19:49.440 Thank you for attending this talk, and I hope you found valuable insights on Ruby's magical yet straightforward syntax!
00:20:09.060 I want to give a shout-out to the open-source sponsors that support my journey and project. They have contributed significantly to the community.
00:20:25.320 Lastly, if you're interested in joining me or want to explore more on this topic, feel free to reach out! Happy Ruby programming! Aloha.