00:00:11.900
Okay, thank you everyone for coming to my talk. I'm going to start off first with a shout-out to my son. My family's watching the livestream, and my 16-month-old son's favorite new word is "flowers." So, Hudson, look—flowers!
00:00:20.810
I have this weird habit of starring GitHub repositories and never looking at the source code. I have like two hundred and seventy something stars, and I did this with Rails. I even took a step further with Rails: I cloned the Rails repository and never looked at the source code for a long time.
00:00:39.300
Finally, when I did, this is a little bit of how I started perusing the source code and how I comprehended it. I had these little "aha" moments, and they boosted my learning of the source code over time. What I want to do today is share with you some of the "aha" moments I had so that you can get up and running in the Rails source code a lot quicker than I did.
00:01:02.660
There are a few side effects of perusing the Rails source code. Going through thousands of lines of Ruby code, you're likely to learn some new methods that you may not have used before. You'll gain a lot of knowledge about optimizations in Ruby, and you might even be introduced to metaprogramming. If you've never done metaprogramming, you can see how Rails uses it.
00:01:38.340
The source repository for Rails is over ten years old and spans over tens of thousands of commits, making it a good playground for learning some new Git methods and options, as well as good methods you already know. By digging through this information, you'll learn a lot about Git. One thing I heard frequently when getting into Rails is that there is a lot of "magic" in Rails. It's really easy to get up and running quickly with very little code.
00:02:10.649
However, it turns out there's no magic in Rails; there's just a very good public API that does a lot of the heavy lifting for users, enabling them to get up and running without hitting implementation hurdles. To start off, you need a copy of the Rails source, which you can get from GitHub. I recommend setting it up for development, and the Rails guides provide a good step-by-step process on how to set it up.
00:02:45.570
There are a couple of dependencies that you need, which will allow you to run the tests for the Rails source code. When you get the source code, you will see a top-level view of the repository. It contains folders for the various modules, along with documentation like the license, contributing guidelines, the README, and the gem files in the gemspec.
00:03:09.959
The Rails modules are independent, as they each have their own folder and are each their own gem. The reason the Rails team does this is that they want the modules to be interchangeable. So, if you want to use a different ORM than Active Record in Rails, you can. It also means you can take Active Record outside of Rails and use it in another project. I've done this a couple of times.
00:03:51.150
Now, to run through the different modules, I'm going to go through a typical Rails request. You start your Rails app with the command 'rails server,' and when a request comes in, it is sent to the routes. The router will parse it and send it to a controller.
00:04:13.380
The controller usually grabs a model and a view, sending a response back through the routes. The routes and the controller are both part of Action Dispatch, or Action Pack. Action Pack consists of two separate modules: Action Dispatch and Action Controller. The routes refer to Action Dispatch, while the controllers refer to Action Controller.
00:04:51.930
The models are usually Active Record or Active Model, should you choose not to interact with the database or want to wrap your Active Record even more. The view is Action View. These components are initialized by Rails through the command-line interface, which is handled by Railties. Active Support is sprinkled throughout the app and extends the Ruby standard library.
00:05:14.430
Also, there are three other modules I consider additional: Action Mailer, which is used to include mail delivery in your app; Active Job, which handles background tasks and jobs; and Action Cable, a newer module that integrates WebSockets into Rails, allowing for a lot of real-time application use, such as adding user stories and real-time chat to your apps.
00:05:52.920
In the top-level directory, you will find the guides—specifically at guides.rubyonrails.org. When you clone the Rails repository, you also have a copy of the guides with you, so if you want to make changes, you can do so and submit a pull request. That's actually how I first started with Rails—I found a typo in the guidelines, submitted a pull request, and now I'm a committer!
00:06:27.800
So we are going to look at a module, and I'll be using Active Record. The things I will show you can be done from module to module. The first level of a module contains the 'lib' directory, where the code and tests reside, as well as the README. I think the README is a good starting point; if you're familiar with the module, you'll likely find at least one or two takeaways from it.
00:06:55.080
And if you're not familiar with the module, the README provides a solid overview. The gemspec is also good to inspect because you can view the module's dependencies. For example, you will see that Active Record depends on Active Support, Active Model, and another gem called ERB, which handles most of the SQL generation within Active Record.
00:07:34.339
If we look inside the internal 'lib' directory, you'll usually find a folder named after the module, along with a 'rails' folder. The rails folder is responsible for much of the initialization—specifically handling the generators. If you're using Active Record within Rails, this is where you'll find the files for commands such as 'rails generate model' and 'rails generate migration.' The 'active_record.rb' file contains a lot of the initialization configuration.
00:08:12.640
If you've heard of terms like eager loading and auto-loading, that's where most of the handling takes place. If we change directories into the module's directory, we'll see the classes for Active Record. The documentation in these files constitutes the information for the API website.
00:08:51.980
Thus, just as you have the guides, you also have the API website by reading through the various files. As you're exploring here, you may see key terms you recognize, like attributes, relations, and schema. You may also come across terms that you don’t recognize, such as attribute mutation tracker and persistence. The terms you do recognize make for a good starting point because you are already familiar with those concepts, and this allows you to delve into their implementation.
00:09:39.140
For instance, I did this with the 'find_by' query method. The methods 'find_by' and 'where' are quite similar; both accept attributes as parameters. For example, you want to retrieve a post by its title. However, 'find_by' returns a single object, while 'where' returns an Active Record relation collection of objects.
00:10:08.350
By examining 'find_by', you'll find that it actually uses 'where' and just takes the first item it retrieves. That’s interesting—it's essentially syntactic sugar for 'where.' However, exploring Active Record for the 'create' method is not as straightforward. You’ll have to dig to find out where these methods live. One of my first 'aha' moments was learning about Ruby's introspection methods.
00:10:53.80
I discovered many of these through Aaron Patterson's blog, titled 'I'm a Debugger,' where he demonstrates various debugging techniques using simple Ruby methods. Ruby offers introspection methods that enable you to call on a class or method and learn more about it. One method I often use is 'method(:method_name).source_location'. You can call it on a class and get the location of the method within the Rails repository.
00:11:30.000
Then, we can look at the create method. In a straightforward version, it creates a new object, saves it, and returns it. Yet, sometimes the process is not that simple.
00:11:43.600
For instance, calling 'save' is an instance method and its source location will show it resides in persistence. But if you check the API documentation, you'll notice that's not the only aspect of the method. The 'save' function does some housekeeping before reaching the core of its functionality.
00:12:20.470
The 'save' method is actually a bit of an elaborate dance. If you're trying to suppress certain records, it simply returns true. In cases like that, it's going to super into a method call, which saves the state of that record, and if anything fails, it will roll back to the original state when 'save' was called.
00:12:44.320
When 'save' is executed, it goes super into a class called 'Dirty,' which tracks changes to the object since you last interacted with the database. It doesn't perform validation during this stage, but if there were validations that failed, they would return false. The next step would send us over to persistence.
00:13:40.390
While we could have looked at the API documentation to understand what 'save' does, the knowledge gap between what 'source_location' returns and what the API documentation describes could be insightful. Another useful method is 'super_method', which you can call after the 'save' method.
00:14:07.450
I prefer to use 'byebug' when I get stuck in situations like this because it allows me to traverse code while it's executing. You'll need a script to insert 'byebug' into your code, and the Rails source contains templates for creating such a script—essentially a minimal version of these modules for exploration.
00:14:47.600
In the context of our 'Active Record' scenario, the master script begins by requiring bundler in line and creating an inline gem file. If you don’t have a copy of the necessary gems on your computer, this will pull them for you and importantly, it grabs the latest version from GitHub.
00:15:27.310
We also incorporate the gem for the database you're using. You can switch that out to whichever one you prefer. Given that we're using the cloned version on our computer, you can change the required version from GitHub to a local path, allowing you to see your changes as you run your tests.
00:16:04.130
Next, we configure Active Record to establish a connection to the database. We define the schema, creating a table called 'post' that includes a title attribute. Including at least one attribute is helpful to see how data is passed through.
00:16:44.560
After defining the attribute, we proceed to work with our Post class, which has a minitest setup to assert statements. Usually, I skip the assertions because these exploration scripts are about understanding and tinkering, rather than confirming functionality.
00:17:13.390
Interestingly, if you want a similar experience to the Rails console, you could require 'pry' and call 'binding.pry' in these scripts. This gives you an interactive console similar to the standard Rails console interface.
00:17:54.400
To use 'byebug,' simply require it and call 'byebug' in the script before the line you wish to explore. When you're stepping through the process, there are cool methods at your disposal with 'byebug.' For example, while at 'post.create', if you want to step into the 'create' method, type 's.' If you want to skip a method or a statement, use 'n' to go to the first line of the else statement.
00:18:38.440
You can also display local variables with the command 'var local', which will show local variables in scope in the current context. On stepping through the code, I sometimes encounter what I call 'heisenbugs.' These occur when there's uncertainty between the expected and actual running values of a variable.
00:19:23.740
This is particularly common in Active Record's query methods, such as 'where,' where the variable keeps getting passed through but may not have been instantiated until it is finally needed. All of Active Record's query methods are designed for 'lazy query,' meaning the actual database query won't execute until the value is necessary.
00:20:14.290
As you're iterating through the classes, you'll start to familiarize yourself with those you know while getting introduced to new ones. This exposure can help you dig more deeply into the Rails framework, broadening your understanding.
00:20:46.210
If you encounter a line of code you don't understand, it can be helpful to change or delete it and then run the tests for the module to see what breaks. I did this not long ago with a method called 'define_method_attribute_equals.' What this method does is use metaprogramming in Active Record to create methods for all the different attributes.
00:21:29.690
It takes a name, like 'title', unpacks it into hexadecimal, and assigns it a safe name. It then uses a module called 'Generated attribute methods' to create a new method based on that name. This part captivated me; upon experimenting, I realized that Rails implements this method in a clever way.
00:22:06.360
In my tinkering with this code, I discovered how Rails cleverly circumvent Ruby's limitations regarding character use in method names. They generate methods while ensuring those methods can also be aliased to more complex method names, providing thoughtful methods for the framework.
00:22:45.950
The second 'aha' moment came when I learned how to effectively use Git to uncover the story of the code. In the Rails repository, the commit history tells a tale of how the code has evolved over time. Using Git's capabilities, I could follow the commit logs to understand why changes were made.
00:23:39.030
Initially, I started with 'git blame,' which returns the last commit hash for each line of code in a file. When I found an unexpected indentation change from 2016, it led me down a trail of investigating why decorum changes were implemented within the Rails code.
00:25:02.230
Then, I used 'git log -S' along with other options to search through commits and selectively bring up not only the relevant changes to the commit, but also the evolved context of that code. It became apparent that many commit messages are rich with the rationale behind the development stages of the framework.
00:26:05.170
By utilizing the commands 'git log -p' and 'git log -p -A,' I could see changes along with their surrounding context, letting me follow along the evolution of specific functionalities in Rails—a journey marked by its struggle to maintain style guidelines.
00:27:26.420
The visual aspect of GitHub adds another layer, where you're able to check out files as they lived in the past, following their evolution over time. For example, I once explored a deprecation notice regarding a method 'redirect_to_back,' linked to an essential chat with its creator concerning the best practices surrounding safe redirection in applications.
00:28:33.420
What I discovered was a wealth of knowledge, encapsulated not only in the commits, but also the discussions and pull requests surrounding those changes. Engaging with these conversations exposes you to a variety of use cases and unique solutions that can deepen your understanding of Rails.
00:29:17.260
Moreover, as you explore issues on GitHub, you can follow reproduction scripts and experiment on your own. Utilizing the scripts allows you to practically engage with the code and understand where bugs exist, reinforcing your knowledge and making you much more familiar with framework intricacies.
00:30:12.540
Similarly, milestones give you foresight into what changes are being planned for new releases of Rails. Whenever I see someone making provisions for testing—like system testing—it intrigues me to watch the timeline unfold.
00:30:49.560
Pull requests do not merely indicate change; they also reflect collaborative effort. As the community discusses and refines ideas, you will see the branching context of problem-solving manifesting over several commits before it finally merges the solution to master.
00:31:46.50
Over time, I’ve really enjoyed diving into the issues and pull requests of Rails because they spark learning moments with every page I read. At first, it was often challenging to grasp the discussions, but it evolved into a treasure trove of insights into how Rails functions and where enhancements are made.
00:32:34.100
Additionally, by employing various labels on GitHub, you can drill down to specific modules like Active Record or Action Pack. You can filter issues and pull requests that relate directly to your areas of interest, allowing you to focus on what you care about and keep in touch with ongoing discussions.
00:33:24.900
Another resource is the roadmap section in Rails’ GitHub repository, where you can see version milestones and their targeted changes. You can glean insights into the future direction of Rails by observing what discussions take place concerning future features.
00:34:07.230
Finally, my biggest 'aha' moment came when I sought to tackle issues in depth. When you start to become comfortable with the issues, look for ones with attached reproduction scripts and PRs. Investigate those issues before looking at the pull requests—run the scripts on your machine and see if you can rectify the failures.
00:34:58.490
This approach leads to gaining experience as you test your understanding against others'. If you find solutions, compare your methods with those of the contributor. Even if you don’t succeed immediately, exposure to the code will broaden your knowledge.
00:36:02.320
To conclude, I’ll leave you with some valuable resources that helped me delve into Rails' source code. First is Aaron Patterson's talk, 'I’m a Foots Debugger,' which shares many great debugging techniques.
00:36:33.640
Then, Eileen's 2015 talk on contributing to Rails goes into much more depth regarding tools like 'git bisect.' I recommend checking her pull request for systematic tests. This pull request contains a treasure trove of knowledge in how she wrestled with changes to contribute to Rails.
00:37:21.430
Also worth noting is the podcast 'The Bike Shed,' co-hosted by Derrick Pryor and Shawn Griffin, who tackle various Rails features and issues in each episode—like eavesdropping on a developer lunch.
00:37:59.680
Lastly, 'Crafting Rails 4 Applications' by José Valim, dives deep into core concepts and could provide a greater scope for understanding. Sabra Nuria also delivered a talk about the boot process of Rails that’s worthwhile.
00:38:28.780
If you’d like to find slides from my talk, they are available on my website at 'alexkitchen.com/railsconf.' Thank you all for coming to my talk!
00:38:54.500
I'll be out here taking questions, so please feel free to approach me if you're interested in discussing further.