RailsConf 2017: Perusing the Rails Source Code - A Beginners Guide by Alex Kitchens

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(dramatic music) - Thank you, everyone, for coming to my talk. Gonna start off first with a shout out to my son 'cause my family is watching a livestream, and my 16 month old son, his favorite new word is, "flowers." So, Hudson, look, flowers. (laughs) So, I have this weird habit of starring GitHub repositories and never looking at the source code. I have like 207 something stars. I did this with Rails, and I even went a step further with Rails. I cloned the Rails repository, and never looked at the source code for a long time and then, finally, when I did, this is a little bit of how when I started perusing the source code, how I was comprehending it. I had these little aha moments, and they boost my learning the source code. But I had those over time, and so what I want to do today is I want to share with you some of the aha moments I had so that you can get up and running in the real source code a lot quicker than I did. There are some side effects of perusing rail source code. Going through thousands of line of Ruby code, you're gonna learn probably some new methods that you may not have used before. You're gonna learn a lot about optimizations in Ruby, and you might even be introduced to meta-programming. If you've never done meta-programming, you get to see how Rails uses meta-programming. And then the source repository for Rails is over 10 years old, and it spans over tens of thousands of commits and so it serves as a good playground for leaning some new Git methods and also some options for git methods you already know. Just digging through information, you're gonna learn a lot about git. And then there's one thing that I heard a lot when I was getting into Rails was there's a lot of magic in Rails. And it is really easy to get up and running very quickly in Rails with very little code, but it turns out there's no magic in Rails. There's just a really good public api and it does a lot of the heavy lifting for users so that users can get up and running without hitting these implementation hurdles. So to start off, you need a copy of the Rails source, and you get that that off of GitHub. And then I recommend setting it up for development and the Rails guides has a good setup, a good step by step process on how to get it set up for development. There's a couple of dependencies that you need, and this allows you to run the tests for the Rails source code. When you get the source code, this is a top level view of the repository. So you have these folders for all the different modules. And then you have some documentation, like the license, contributing, read me, as well as the Gem files and the Gem spec. Now the Rails modules are independent, and so they each have their own folder, and they each are their own gem. And the reason that the Rails team does this is because they want them to be interchangeable. So if you're, if you want to use a different ORM then active record in Rails, you can. And that also means you can take active record outside of Rails and use it in another project. And I've done this a couple of times. And then to run through the different modules, I'm just going to go through a typical Rails request. So you start your Rails app with Rails server, and then a request comes in. And that request is gonna be sent to the Routes, and the Routes is gonna parse it. And it's gonna send it to a Controller. The Controller's going to grab usually a Model, and it's also gonna grab a View, and it's gonna go back up to the Routes and then you're gonna get a Response. Now, the Routes and the Controller, those are both a part of action dispatch, or, sorry, ActionPack. And ActionPack is actually made up of two separate modules. There's Action Dispatch and Action Controller. The Routes is Action Dispatch, the Controller's Action Controller. And the Models are usually ActiveRecord or ActiveModel if you don't want to do database interaction or if you want to req your ActiveRecord even more. And then your View is ActionView. And the initialization of Rails, and the command line interface, that's handled by Railties. And then sprinkled throughout the app is ActiveSupport. And ActiveSupport, it's gonna extend the Ruby standard library classes, as well as add some other helpers. And then there's three other modules. I consider these additional modules. So ActionMailer, if you wanted to include mail delivery into your app, so for like user registration and stuff like that you can do that. ActiveJob. That's gonna handle background tasks and jobs. And then ActionCable, that's a newer module. And ActionCable's really cool. It integrates web sites into Rails. And so it allows for a lot of real time application uses. So if you want to add user stories to any of your apps, as well as real time chat, you can. So, also in the top level directory are the guides. And this is guides.rubyonRails.org. And you have there the source code for that. So when you clone the Rails repository, you have a copy of the guides with you. And if you want to make changes to it, you can. Just make changes in here, and submit a pull request. Pretty cool. That's actually how I first started in Rails. I found a typo in the guides and so I submitted a pull request and got added. And I was like, 'Yeah, I'm a committer." (audience laughs) You know. Okay, so we're gonna look at a module, and I'm gonna use ActiveRecord, but the things I'm gonna be showing you, you can do them from module to module. So, the first level of the module, it's gonna have the lib directory. That's where the code is going to lie. And then the tests are gonna be there as well as the readme. I think the readme is a good place to start. If you're familiar with the module, you're probably gonna find at least one or two take aways from it, and if you're not familiar with the module, it's a good overview of the module. And then the Gem spec I also think is good to look at because you get to see the dependencies of the module. So, here you see activerecord depends on activesupport and activemodel. And then another gem called arel, and arel does a lot of the SQL generation within activerecord. If we cd into the lib directory, you're usually gonna see a folder with the module name. You're going to see a Rails folder. And the Rails folder is going to handle a lot of the initialization, or, sorry, no. It's gonna handle a lot of the generators. So, if you're using activerecord within Rails, this is gonna be where Rails generate model. Rails generate migration's gonna live. And then active_record.rb, this is a file with the module name, that's where a lot of the initialization's gonna be configured. So, if you've heard terms like eager loading and auto-loading, that's where that's the majority of that's handled. So if we cd into the active_record, or, yeah, the module directory, this is where you're gonna see the classes for active_record. And, the the documentation on these files, that's what makes up the documentation for the api web site. And so, just like you have the guides with you, you'll also have the api web site by reading through the different files. Good place, when you are looking through here, you're probably gonna see key terms that you may recognize, like attributes and relation and schema. And then you're probably gonna see ones that you don't recognize, like attribute_mutation_tracker and persistence and stuff. And the ones that you do recognize, that's a good place to start because you're gonna be familiar with some of the things within those. And you get to see how they're implemented. So I did this with find_by. Free method. And find_by and where, they're pretty similar. You're gonna be passing in attributes as the parameters, so you want to find the post by the name title. And, they're similar except that find_by is gonna be returning one object, and where is gonna be returning an active record relation collection of objects. And so I wanted to see how similar the code is. So if we look at find_by, you see that find_by actually uses where and then it just takes the first item in it. That's pretty cool. It's sort of a, it's sort of a syntactic sugar for where. Now, it's cool to see that but it's not as simple as saying I want to see what active_record creates doing, so go look in the create file. It doesn't really work that way. You have to do some digging and find out where these methods live. And one of my first aha moments was learning about Ruby's introspection methods. I learned about a lot of these from Aaron Patterson's blog called I'm a Puts Debugger. And he goes over different ways that he debugs with just simple Ruby methods. Ruby has these methods called introspection methods, and they allow you to calm on a class or even a method and learn about that class or method. One that I use very often is method.source_location. And so, you can just call, here we have a class called Post. We're gonna call method. And then the method we want to look at, we put it as either a string or as a symbol. And then call .source_location. And that's gonna return us where that lives within the Rails repository. And then we can go look at that, and we see here that create, creates a new object and then it saves it, and then it returns it. And that's a pretty straight forward version of this. But sometimes it's not so straight forward with source location. So, for instance, save. We're gonna call post, then new, since save is an instance method, and method, save, source_location, and it's gonna return us suppressor. What's interesting is if you look at the api web site, you're gonna see that it lives in persistence. So, why is source location giving us a different version than the api web site? It turns out that save has to do some sort of housekeeping before it actually gets to the core root of the method. So I'm gonna sort of run you through what save does. Here's suppressor, where it said that save lives with source location. And what suppressor does is if you're trying to suppress these kinds of records, saying you don't want them created, it's just gonna return true. We're not doing that, so it's gonna super. And that supers into a method called, in transactions. And what this is gonna do is it's gonna save the state of that record, and if something happens within, where something fails with that, it will just roll back the original state of that when you called save. That's gonna super into a class called dirty. And dirty tracks changes. I see you smiling. (audience laughs) It tracks changes to the objects since you've interacted with the database. And it's not gonna do anything here, so we're just gonna follow that along to validations. And this is where validations are preformed. We don't have any validations here, but if one of them failed, it would return false. Here, it's gonna super. And that's gonna take us to persistence. Now, like I said, we could have looked at the api web site and seen this right here. What save is actually doing for a save. But you have this knowledge gap between what source location is giving you and what the api web site has. And I think you're better off by filling that knowledge gap. Now there is another method called super_method that you can call. So, after method:save, you can super_method and you see here it returns transactions. But, because we have so many supers here, I tend to use, in situations like this I tend to use byebug. And byebug's really cool. It allows you to traverse code as it's executing. And to do this, you need a script to put byebug in. And, the Rails source actually has some templates for, for creating a script. It's just a minimal version of these modules so that you can play around with them and explore and do some assertions. And so we're gonna look at the active_record master script. And what it starts off doing is it first requires bundler/inline. And then it creates an inline gem file. And what this is gonna do is if you don't have a copy of these gems on your computer, it's gonna grab 'em for you. And here you see that we're actually gonna be using the GitHub version. So when you do GitHub like that, it's just gonna grab the master version off of GitHub. And we're also doing ti with arel. The reason we're using arel's master is because usually if you're using Rails master, they're using a unreleased version of arel since they maintain that as well. And so you need that. And then you also have the gem for the database that you're using, and you can change that to whichever one you want to use. And since we're using the cloned version on our computer, you can change that from GitHub to path. And you can make little changes to it and it'll, you can see those changes being, when you're running through it. Then we're gonna do some ActiveRecord configuration. We're gonna establish a connection to the database. We're going to use logger and we're going to define the schema. And here, I'm just creating a table called Post. And I am adding a title attribute. I think it's good to have an attribute in there so you can see how those attributes are passed through. And then we have the, we're just calling the class Post. And then it has a minitest set up here. So if you're asserting stuff and you want to see if something's true or false, you can do that. Usually I just take that out 'cause I'm, I consider these exploration scripts. So I'm not gonna assert anything. I'm just gonna do my coding line. And what's cool is if you want to use something similar to a Rails console, you can also in these scripts, you can require pry. And then you call binding.pry. And that will give you an interactive console. And you can treat it just as a typical Rails console. Now to use byebug, you need to require byebug. And then you call byebug right before the line that you're wanting to explore. And I'm just gonna run through some of the cool methods you can use with byebugs. So here we're at post.create. And if I want to step into create, I just type s and that will take me to the first line in create. Then if I want to skip over something, I want to skip over this if statement, I can type n, and that stands for next. And that will take me to the first line in the else statement. And if I want to see the local variables, so here we have attributes and a block, either of those are passed. And var local will give me the local variables since here we see that title equals hello. Now, running through these scripts when you're playing around with it and you're looking at the variables, I've run into what I call Heisenbug's debugging uncertainty principle, and that's, you know, It can be trouble to know simultaneously the exact value and correct execution of a variable. So what that means is um, I've seen this happen a lot in ActiveRecords query methods. I'm exploring in a query method, like where, and I want to see this variable that keeps getting passed through, and so I look at that. And, the query methods in ActiveRecord, they do what's called lazy querying. So, it's not gonna call the actual query to the database until you really need it. And that's what allows you to chain these query methods, like where dot order, blah, blah, blah. But, when I look at the variable, it's saying, "Hey, I need that." And so it's gonna call that, that SQL query, and a lot of the internals of the query methods depend on that query not being called. So it's actually gonna take you on a different path than it would have had you not looked at that variable. Now as you're going through here, you're going to see, you're going through the classes you know. And as you're going through those, you're gonna be introduced to the classes you don't know. And this gives you a little context to those so you can start digging into those. And the more you do this, the more you're gonna be acquainted to a larger amount of Rails. Now one thing I like to do if there is a line of code that I don't understand, what I like to do is I like to change it or delete it and just get rid of it. And after I do that, I like to run the test for the module, and I like to see what broke. And I was doing this a couple of months ago with a method called define_method_attribute= and what this method does, is it's gonna use meta-programming in ActiveRecord to create, for all the different attributes, it's gonna create the title= or description= methods. And just to run through it, what it does is it takes the name, here we passed a name as title, and it's gonna unpack it into hexadecimal. So it's gonna unpack that into the 479 string, and then it's gonna save that as a saveme. And then it does a bit of metaprogramming and there's this module called generated_attribute_methods that all these methods live on, and it's gonna create a new method on that. And the way is does that is it first creates a method call __temp__ safe_name. So it's gonna have that 479. Then it aliases that to the method name that we're looking for. So it's gonna alias it to title=. Then it undefines the __temp__method, and I thought, "This was weird." It feels like it's going through some extra steps here. Why create a temp method and then alias that and then get rid of it right away? So, I decided to change it to what I thought it should be and just run the test. So, I got rid of the temp method, just named it name=. Then, we don't need the alias method and we don't need the undefine method, so we can get rid of those. And that just leaves us with name=. I ran the test, and sure enough, some failed. And this is an example of one of them. You see it's trying to define a method called a$b. And in Ruby, there's a limitation to that. You can't use dollar signs in method names. So it turns out what Rails is doing here is it's bypassing that Ruby limitation on method names. What it does is that it first creates a method that it knows is gonna be safe. Hence, the safe name. And then it's gonna alias it to what we need it to be, and then it's gonna undefine that temp method 'cause we're not gonna use that. And, I thought, "This is a really cool and sort of sneaky way of getting the functionality that we need out of Rails." And so I was interested in how they came up with this. Did this exist very early on in Rails? Or when did they decide that this should be a part of Rails? And, has this code evolved over time? And so, my second aha moment was learning how to use git effectively to find the information about, or find the story about the code 'cause the commits within the Rails repository, they tell a story. They tell you how the repository or the source code has changed over time. And you can follow the git commit messages and hopefully it's a good message and it's explaining exactly why they did this. And so, I'm gonna take you on sort of a journey. It wasn't a straightforward journey of finding the original commit for this code, but it was some cool, cool git methods that I learned along the way. So, I first started off with just git blame. And that's gonna return the last commit hash for each line of code within the file. And that returns 2016 by Ryota. And I thought that's a little weird. That's pretty new that, to have just be added to Rails. And it turns out what it found was an indentation change. And, so in 2016, the Rails team, they implemented RuboCop style guidelines in the Rails source code. And this was part of them cleaning up the source code to make it match the style guidelines. We don't want to see why it's space changes and so there's an option with git blame that you can pass called -w, and it ignores white space changes. And that gives us a commit by Aaron Patterson in 2013. Well, it turns out that's still not the original commit for this. This code's a little different. This code actually existed before and then Aaron changed it, and then he had to revert it back. And, git blame is only going to give you the latest commit. But we want to see all those. And so I found that git log works a little better with this. Git log has an option that's dash capital s. And you can pass in a search string, and what it's gonna do, it's gonna return you all the commits that have that search string that match that search string in the changes of the commits. And so that's gonna return several commits here. Now, I like to clean this up a little so it's easier for me to read. I like to pass on a couple of options. One is --patch. And --patch or -p for short, it shows you the actually changes that happen in the commits. So, you can see instead of just the commit message, you're gonna see the actual changes. And then --pickaxe-all, that actually comes along with --patch. And if you don't pass in that option, it's only gonna return you the files in those commits that match that search string. So, it's only gonna return us right. But you want to see all the changes that happen with commit 'cause usually those add context to the actual change. And so, we're gonna pass it here, and you can probably see that that first one is the read file. And then --reverse changes it from being newest commits to oldest commits, instead we're gonna do oldest to newest. And I feel like this tells a little better story. You get to see it from where it first existed and follow it along. And that commit wasn't actually the commit that created the functionality. It was actually the one that created this code. But from here what I can do is I can take the comment hash from there, I can check out the repository from there or from the commit before that, and I can follow that along until I find the commit that added the functionality that I'm looking for. I did this and I found a commit from 2011. And along the way, I learned the original purpose of the code. I learned some optimizations that they made to it and also some bugs that they addressed in the code. There is another option or another feature that's similar to what I just did called reblaming. And then two places I know that have reblaming are Vim Fugitive and GitHub. Vim Fugitive is a vim plugin that sort of wraps git in it, provides some really cool git functionality within vim so you don't have to leave vim. You can use it on the command line. And I'm gonna show you what reblaming is on GitHub. So here we have the blame view for that write file. And you'll down here, you'll see a little icon. And it says, "view blame prior to this change." And from what I can gather, what this does is it's gonna check out the repository to what it looked like before this commit was added. And then it's gonna blame it from there. And it's really cool. From there you can just keep clicking that link until you find what you're looking for. Now git has a lot of cool features like this. Git sort of serves as an extension to, or GitHub sort of serves as an extension to git. Where as git provides you with a commit message, GitHub provides you with a whole conversation. For example, when I was upgrading one of my Rails apps from Rails 4 to Rails 5, I kept hitting this deprecation in my controllers that said, redirect to back was deprecated for redirect back to fallback location. I wanted to see why this change was implemented. So, I ran git log -S with some of the deprecation string, and it returned me a commit from Derek Prior. Derek Prior, you may have heard his talk earlier today, he, the reason he made this change, he said that when no refer is available on that, redirect to back can lead to an application error. And so he provides a great commit message. It was a very long commit message. I want to see what was also on GitHub, what extra information I could find. And so I took the commit hash for his commit and I searched by it, and I found his pull request. And in his pull request, he has a conversation with the maintainers and other contributors to Rails. And they're asking him real life scenarios. Where is this actually going to happen? And he provides some real life scenarios. And he also provides a link to a security sit call OWASP about unsafe redirect practices. And this information, for me, I'd never heard, I had heard of OWASP, but I didn't know about unsafe redirect practices and this information was very valuable to me. And a lot of pull requests and issues have this kind of information. So Derek, he works for Thought Bot. He's a Rails consultant there and he looks at all these different Rails apps. That's his day to day job. I, my day to day job, I work on two maybe three Rails apps. And so I'm not introduced to all these different use cases for Rails, these very unique ways of doing things, maybe not always great, but they're unique. And, I sort of think about it in this way. There's this tree, and I'm using another tree analogy today. There's this tree, and at the trunk of it you have the general Rails knowledge. This is the stuff that every typical Rails app does. It's ActiveRecord, creates and stuff. And the Rails source has this handled, or at least testing wise. You're not gonna see a whole lot of bugs here. But as you're dealing with a larger Rails app, as those Rails apps grow or when you're dealing with ones that are doing very particular things, you're gonna go up these branches and you're gonna see little issues that most people don't experience. And I'm not seeing that in my day to day job, and if I'm expecting that, it's gonna take a long time before I see all these different ways of using Rails or even the issues within Rails. But with the issues on GitHub, I can look at an issue, and I can see one with the reproduction script, one of them that we looked at earlier, and this reproduction script, this thing, there's a bug right here. And it's like a little map, and I can take the reproduction script, I can run it on my own machine on my own source of Rails and I can see where that lives. And if, maybe if it's a feature that I'm not used to, I'm getting to see how someone else is using it and I can learn a little bit more about it. I can follow this branch down and add it to my knowledge of Rails. Rails also serves as a forecast to changes and additions being made to it. Eileen was just talking here about how she added system testing, and it took her six months from when the pull request was created to when it was actually merged. And this is how a typical pull request goes. So, a pull request is received. Usually there's a discussion between the maintainers and contributors. It may be, we really like this. I think you should make this little change here. Or I like the idea of it, but I think you need to do it in a complete different way. And so more commits may be added to this pull request. And it may just build up to several commits. And you can follow along those commits. You can see sort of the thinking process behind the person that submitted this pull request and then, if they accept it, those commits may be rebased into one. And if you haven't been introduced to rebasing, what it does is it takes all your commits and it's going to just turn them into one commit. And so we're gonna lose a lot of that contextual information from following along all those commits. And it will finally be merged into master. And when it's merged into master, this is where git starts. So, if you were just looking at git, this is where you first are introduced to this code. But with GitHub you're seeing all this other contextual information and you're learning from it. Now, I have really enjoyed reading through the issues and pull requests on Rails because there's all these little nuggets of knowledge in these pull requests. But the problem with it is when I first started doing this, it's not always easy to follow along with what the issues are and what the pull requests are, especially if you're not familiar with what it's talking about. And so I read through many issues and pull requests with glazed eyes, but over time I would find little nuggets of knowledge, little ahas that I learned something new about Rails. And, as a I did this more and more, I found more and more take aways more frequently. Now, just as we looked at what's familiar to us in the code base, you can also look at what's familiar to you in GitHub by using the labels. And so each module has a label. So you can see ActiveRecord, ActionPatch, or act, yeah, ActionPack. And, you can, if you only want to see issues or pull requests from ActionPack, you can go look at that label and it'll pull up the issues and pull requests related to that. Then, this is more of just a cool thing that I found going through the GitHub. They mark the milestones for the different releases for Rails. So you can see, you go to the milestones, you see like 5.2, 5.1, .0, and you get to see what they're working on a and what they want to get accomplished before they release that version of Rails. So, just like Eileen's, it was six months sitting in GitHub before it finally became something, a and I was watching the repository and I saw when she submitted the pull request and so I knew six months before that they were trying to add system testing to Rails. It's very cool. It's sort of insider knowledge. Now this is my final and probably my biggest aha moment. I decided I wanted to try tackle issues or at least get into the nitty gritty of issues. And so, what I recommend is when you start getting comfortable with the issues, and maybe seeing some in areas that you're comfortable with in Rails, find an issue that has a reproduction script and maybe even an attached PR, I know the maintainers won't like me saying that but, um, look at this issue and don't look at the pull requests yet. Take that script, run it on your machine, see if it fails. If it fails, see if you can find the issue. And if you find the issue, see if you can make the test pass. Change the code and see if you can make that reproduction script pass. Then, once you do that, look at that PR and see what you did versus what the person that submitted that PR did. I bet you you're gonna have taken different ways of making that PR or making that reproduction script pass. Now if you can't solve the issue, which, I'm just gonna tell you, it's, it can be hard. I've looked at lots of issues try to dig in, and I was like, okay, I don't know what's going on here. I'm not even gonna try to fix it. That's fine. The time you have spent in the code and the code that you've been introduced to, you'll understand it a little more and then you can go look at that pull request and I bet you'll have a little more contextual information to understand what's going on in the pull request. And you'll be like, oh, okay. I remember seeing that. Oh, that's actually pretty simple. Now I'm gonna leave you with some resources that really helped me as I started perusing real source code. I found these really handy. First one is the blog by Aaron Patterson. I am a puts debugger. Talked about that earlier. He has a lot of great debugging methods that he uses. Then, Eileen gave a talk in 2015 on contributing to Rails, and she goes into a lot of really in depth things like GitBisect, and it's an hour and 20 minutes and so she has a little more time to go into more of the details on contributing to Rails. I definitely recommend checking that out. And then check out her pull request for System Tests. Her pull request has 37 commits on it. They didn't rebase them when it was merged, and so you can start from when she first submitted that pull request and you can follow along and see her thinking process behind adding this feature. It's really cool. And then check out, if you're looking for a good podcast, check out The Bike Shed. So Derek Prior, he's one of the co-hosts, and Sean Griffin, he maintains ActiveRecord. They both tackle Rails issues and Rails features and every, or a lot of the episodes, either Sean's saying, "Hey," or Derek's saying, "Hey, Sean. I have a questions about Rails." And they go into something with Rails. Or Sean says, "I'm working on this ticket." Or, "I'm working on this feature in Rails," and it's sort of like sitting in at a developer lunch and, like you said, I live in the middle of nowhere, and so I don't get to sit in on these kind of conversations every day. So it's cool. Every Thursday, I get to listen to their podcast. And then another good book is Crafting Rails 4 Applications. It's written by Jose Valin. He was a Rails member. He starts of the book by talking about how to create or render a plug-in within Rails. And, as he's doing that, he goes into how rendering works within Rails. So that's just the first chapter. It's a really good book. And then last, Xavier Noria, he's another one of the core members, he gave a talk last year about everything that Rails does and the boot process. So I definitely recommend checking that out. And then if you need to find the slides, they're on my web site, alexkitchens.net/Railsconf. Thank you for coming to listen to my talk. I'm, I'll be out here taking question. I'm not sure if I've gone over time or how I'm doing, so, if you want to ask me any questions or talk to me, I'll be down in the audience. Thank you. (audience applauds) (electronic whooshing)
Info
Channel: Confreaks
Views: 7,609
Rating: undefined out of 5
Keywords:
Id: Q_MpGRfnY5s
Channel Id: undefined
Length: 38min 45sec (2325 seconds)
Published: Fri May 05 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.