 Here has Rails source actually installed and tests running. Okay, everyone who doesn't, who isn't sitting next to someone who does, you should sit next to somebody who does. So we're not going to use Rails a lot, but if you want to see how the tests look when they run, you should sit next to somebody who does have it installed. Or you can run it later when you do get that installed. Once I have USBs if the Wi-Fi is not working, but it seems that everyone else doesn't need them and is not having trouble. You can use bundle install local so you don't need to hammer the Wi-Fi to install a bunch of gems. Cuz I have a cash version of gems in the repo for everybody. And if you're using Windows, be sure to check out the Windows branch. Because SQLite and Okagiri have specific gems for Windows. Today we're going to talk about how to contribute to Rails. We're not going to talk specifically about getting, how to get your environment set up to run Rails source. But rather, we're going to focus on guidelines and advanced technical knowledge you need to know to have the confidence to contribute to Rails. I'm Eileen Ushitel, and I'm a programmer at Basecamp. Or as you might know it better, the place where Rails was created. You can find me pretty much anywhere online at the handle Eileen codes. I'm also on the Rails Committers team, which means I can push directly to master but I'm not on course so I'm not allowed to have access to the Twitter account. I'm from a small city called Kingston, New York, which is about two hours north of New York City. It was the first capital of New York in 1777. This is our dog, Arya. Yes, she is named after Arya from Game of Thrones. And you can follow her on Twitter at AryaDog. There are a few requirements for this workshop. Although I would love to help everyone get their environment set up, we don't have time for that. So we're going to focus more on advanced Git and co-traversing tools that you don't use every day. This isn't a beginner course in Rails and I'll be assuming you know how to debug environment issues and install multiple versions of Ruby. You should also know how to install the required databases and be comfortable with Git and GitHub and know how to create branches, rebase, push, and clone. The goal of this workshop is to get you comfortable enough to contribute to Rails. I started contributing to Rails in March of last year after speaking at Mountain West where I gave a talk and active record. Aaron Patterson was there and it turned out I had discovered some bugs in Rails. We worked together to fix that bug and it made my introduction to Rails a lot easier and less intimidating. I want to help you feel as comfortable and confident as I do contributing to Rails. So we're going to focus on debugging issues, traversing source code, and learning advanced Git so that when you're asked to defend a change or change your commit history, you're confident enough to do that. I'll be posting the slides from everything we learned here today. So don't worry if you miss something, you can always go back and do it later. I've had many people tell me that contributing to Rails is inaccessible because the issues tracker is difficult to parse. There are so many open pull requests. It's hard to tell what's worth working on and what's really an issue. It takes a lot of experience with the Rails team and the code base to understand what's a desired feature and what qualifies as a bug. To be honest, even I have trouble finding things to work on. But it's not just Rails that's hard to contribute to. Open source in general is pretty intimidating. You can put in a lot of time and effort and sometimes the payoff is unappreciative users. But most of the time, developers are grateful for your time and your energy. They're so happy when you fix a problem for them. It's amazing to be able to help an entire community by fixing documentation or squashing a bug. You're literally saving other people hours of time. Although this talk is going to focus on specifically contributing to Rails, I assure you many of the techniques you learn can be applied to any open source project. The format of today's workshop will be a little bit less hands on than other workshops you may have done in the past. Especially because we have a lot of attendees here. I've formatted it so that it works as a talk and a workshop where you'll be following me and doing hands on stuff as I go through the slides. And we'll do some live coding. In between topics, I'll pause for questions so you can try out what we talk about. If you haven't done so already, clone the repo I provided. This contains script for debugging Rails problems and set up for practicing Git later. You should do bundle install local to get to install the gem cache instead of using the Wi-Fi to bundle. If you're using Ruby on Windows, use the VM, and you're not using the Rails VM, you need to check out the Windows branch and do one on install local after that. The gem cache requires Ruby 222. If you don't have Ruby 222, try changing the Ruby version file to a Ruby version you do have, if you're having a problem. I'm going to touch on setting up your environment really quickly, but we're not going to talk much about this. If you want to contribute to Rails, you likely already have the necessary libraries and databases installed, or you at least know how to get them running. You should probably already have a favorite Ruby manager like RBM for Che Ruby. Rails Master requires Ruby 222. Rails also provides a VM, but the majority of core members and contributors run Rails locally on their machines. If you use Windows, it might be better that you use the VM, because none of us have any idea how that works. Additionally, you'll need the necessary databases, MySQL, Postgres, and SQLite. There are tests and adapters for Oracle and a few other databases, but those aren't run as part of the main test suite. So you don't need to worry about them unless you're specifically working with that database. And of course, you'll need Git, because GitHub doesn't exactly work without it. In fact, I'm not really sure how you've survived up to this point working with Rails without having it installed. So this should already be set up. A GitHub account is required so that you can open issues and send pull requests. If you've ever checked out the Rails source and tried to run the entire test suite, you know that it takes forever. And you're probably like, no, I don't have time for this. Running the entire test suite takes a really long time. I actually am not sure I've ever done it. But you don't need to worry about that, because that's why they made continuous integration. And Travis CI is going to run for every single pull request you push, and every time they merge something into master. So you don't have to worry about something getting missed. Well, unless it's not tested, then it can get missed. Generally, I only run tests for the part of Rails that I'm touching. If you're changing action pack, run action pack test only. If you're changing active model, I recommend running both active model and active record test because they're tightly coupled. Just seeding into library, you want to test and run rake test. Travis CI is running, so when you push a pull request, you can always depend on it to run your pull request with all supported versions of Ruby. For active record, if you run rake test, you'll be running the test for MySQL, MySQL 2 SQLite, and Postgres adapters. To run tests for a specific adapter, you can use the rake test with the adapter name command. Generally, databases behave the same. Across, we have the same unless you're touching the adapters, so running just SQLite and MySQL should generally be fine. To run a single test file, you can use ruby-ilib and the path to the test file. These tests will default to using the SQLite adapter. To run a single test in a file, you can append dash n and the test name to the previous command. Again, this will default to using the SQLite adapter. If you want to run a single test or single test case using a specific adapter, you can use the ARCON method to change the database adapter. In this example, the test will run using MySQL 2 instead of SQLite. You can also run a single test with all adapters with bundle exact test and the test name. I want you to try running some of the Rails tests if you have Rails installed. If you don't have Rails installed, sit next to someone who does and look over their shoulder. CD into the Active Record directory and run the above commands. I'll be posting these slides later on, so if you don't have a chance to run these, it's okay. So the first one will run all of the tests in Active Record using the SQLite adapter. The second command will run just the reflection test.rb file in Active Record using SQLite. And the third one will run the same reflection test file, but it's running a specific test in that file with the MySQL adapter. I'll give everyone five minutes to try that out and let me know if you have any questions. Yes. Apparently, it takes 17 minutes to run the entire Rails test suite. That's good to know, because that's fast. I mean, probably compared to Ruby, right? Yeah, Rails High takes like 10 minutes by itself. No, you shouldn't have any errors. You might need to bundle your Rails if you haven't ever tried running the test before, or if you recently pulled changes from Rails. A lot of times, the gems change, and you'll need to bundle right after a pull. Oh, load. Okay, yes. So if you see notifications while you're running, and it's not an E or an F, that's a warning, and sometimes it's just that there's a... A lot of times you get a warning if you have a shadow variable, if the variable name is the same as another variable somewhere else. Rails will warn you about that. We try to remove that from the running test, but sometimes they get missed. Oh, those are tests that are for specific adapters. So if you are running the SQLite, those might be... I don't know off the top of the head which one's got skipped. Those might be Postgres tests that are getting skipped. You can always go back and look at the slides later. I'll post them right after, especially because we got good Wi-Fi. Every open-source project has their own way of doing things. To learn more about this, you should read the contributing guideline on that project. Generally, opening an issue is the same across repos, but there are a few things that I want to go over that may be specific to Rails. When opening an issue, it helps to explain the problem as clearly as possible. There's a lot of code in Rails, and it's hard for us to know exactly what's happening. Cryptic titles and missing reproduction scripts won't help get your problem solved, and instead your issue will get closed. Rails contributors understand your frustration, but we can't help you if you don't understand or can't reproduce your issue. Never ever open a security report on the issues tracker. Doing this could result in someone without your best interest in mind getting a hold of an exploit. Security issues should be kept between you and the Rails security team to maintain the safety of everyone who uses Rails. To report security issues, always email security at rubionrals.org or a core team member directly. Rails doesn't accept issues on GitHub that aren't a bug in Rails itself. Any issue pertaining to how do I do this or why isn't this working is usually related to a gem you're using or code that you wrote and won't be discussed on the issues tracker. This is to keep the number of open issues down so that you can get the help and attention you need. The community is huge, and there are a lot more developers on Stack Overflow that can help you debug your environment problems than there are on the Rails GitHub issues tracker. If you already have an open pull request, there's no reason to open an issue. Some projects may prefer an issue to be paired with a pull request, but on the Rails repo it's not necessary. This will just create excess noise for Rails core and committers. Pull requests don't need corresponding issues because searching issues on GitHub reveals both pull requests and issues. It's easier to have one place to discuss a bug or feature than two, but of course, if there's already an open pull request, an open issue, feel free to send a pull request to fix that bug. A big difference between Rails and other projects is we don't accept feature requests on the issues tracker. Don't get me wrong, we love new features. It's just that we have so much on our plates already that if you want to see something new in Rails, you either have to convince us to write it or be willing to write it yourself. If you already have a pull request, it's better you just send it than ask for permission or if we want the feature. If you don't have a pull request and you're in doubt, it will be accepted and you don't want to work on it until you have approval, then you can bring it up on the Rails core mailing list for debate. Including a test script can help immensely when opening a Rails issue. It's pretty much required for any active record or action controller bug. It makes it easier to isolate your bug and to be sure that it's not another gem causing the problem. And now we're going to take a look at how to create a runable active record script. You can find examples for other executable bug reports in the bug reports templates directory on GitHub in the Rails repo. To follow along with your own copy, CD into your RailsConf scripts directory and open the script called scriptexample.rb with your favorite text editor. I'll give everyone a minute to do that. Yes. Yeah, that was on the first slide. I also have USBs that have this repo on them. They're just like small scripts. Does anyone else need a... Ooh. Does anyone else need a USB? Okay. Pass them around if anyone else needs them. Because I only have 10. This example shows the bare minimum for a self-contained active record script. First, we add the required gems. You'll see some of the scripts include a gem file at the top, but because I run all of my scripts against my local checkout of Rails, it's not necessary here. It's also not necessary for you because I provided the gem file and bundled the gems for you ahead of time. You can include the active record gem with the version of Rails you're testing if you have a specific problem with a specific version. When you open your issue, you'll see some of the Rails you've seen the problem with. We then establish a connection with an in-memory SQLite database. Using the in-memory database has a lot of benefits. For one, it's a lot faster, and two, you don't need to worry about authentication or the intricacies of other databases, like MySQL. The data is created and destroyed in I.O. while the script runs. No data or tables are left behind to interfere with future runs. Next, we set the logger to standard out so that we can easily see the SQL generated by each command in the script when we run it. Then we define the schema of the database. There's no need to create or run migrations. The SQLite memory database will apply the structure from the schema. We can easily... Here, we can easily add columns or tables for our script. Anything that gets generated in your schema file in your Rails application can be put here, like foreign keys and indexes. Then we establish the models required to reproduce the problem. I recommend using a simple example as possible. You don't want other developers misunderstanding your problem because they can't parse the relationships between your models. The more mundane your example, the easier it is to understand. Of course, not all problems are simple. I merely mean to pare down your example as much as possible with as few models and relationships as necessary. If it only takes two models to reproduce your problem, don't include all it's hand from your application. Lastly, we write a test. As long as your script outputs an example of what's happening versus what's expected, that's good enough for anyone trying to figure out what's going on with your issue. This helps us also eliminate the possibility of environment variables and outside gem dependencies. If you can't reproduce an active record problem in an active record script, we can't either. And your issue is likely not with Rails itself. You can run the script from inside your RailsConf scripts directory with bundle exec Ruby and the path to the file. Depending on your environment setup, bundle exec may be required here to use the cached versions of your gems from the directory, not the version of Rails installed with your version of Ruby. When run, you should see a validation failure in the script. Try out running the script and let me know if you have any questions. I'll give everyone a couple minutes to try that out. Everyone see that validation failure? Now we'll discuss guidelines for opening a pull request on the Rails project. Always open your pull request pointing to master unless your change affects a specific release. Don't point your branches to release branches because your pull request will get automatically closed when a new version of Rails is released. That's just a GitHub thing. Not, we don't do that. Rails does not accept cosmetic changes to the code base. This means we won't merge in changes that change all the double quotes to single quotes, remove white space, or otherwise are refactorings that don't improve the readability of the code. The reason we have this rule is to keep Git blame clean. We don't want to have to chase down a code change among cosmetic sweeping edits. To help us in future contributors understand what and why you're changing something. Imagine that GitHub disappeared tomorrow, but your commit messages live on forever. Sometimes I'm on a plane and I don't have Wi-Fi and I'm working on a Rails bug anyway. I don't want to have to wait until I land to go to GitHub to understand why you changed something. Don't assume that everyone knows X does this and should be avoided. Explain why X should be avoided in detail in your commit message. A good commit message includes a summary of the changes, 50 characters or less, and a more in-depth detail description below. Feel free to include bullets, code samples, anything that can help someone understand why you made a change. Often we'll ask programmers to squash their commits. This makes it a lot easier to backport changes with CherryPick and keeps the Git log clear and concise. Generally, if your change is small with a test, that should be just one commit. If you spent months on a feature and have clean history, you're likely to get your commits merged in without squashing into a single commit as long as your history is clean. Later on, we'll go over how to squash commits. Rails gets a lot of pull requests and sometimes we fall behind reviewing them or aren't sure they should be merged in as is. Sometimes we just don't have time to review them. Unless you have a good reason, don't constantly ping members of the team asking for a review. That said, smaller, concise changes are easier to parse and maybe faster to get merged in. Please don't plus one issues you want fixed or pull requests you want merged in. Only comment on a PR issue if you have added insight. For example, you've run the patch and confirmed that it fixes the original issue. Be sure to read all of the other comments first. Often they point to why a proposed fix won't work and can't be merged in. It's not uncommon for original reporters to become too busy to remain involved. Be proactive, not reactive when commenting on Rails issues. Yes. There's a way to say that you want things without just plus oneing. It sends every single person who watches a repository an email. Sometimes you'll just see issues that have lots of plus ones, but if you read the beginning of it, you'll see why it's not either it's not as simple as just fixing it. What I mean by be proactive is if no one's fixing it, then ask for help how to fix it rather than plus oneing it because it's usually not a case of we don't care. It's a case of either how to fix it or we'll see in earlier comments maybe it can't be fixed for some reason. Our documentation always needs work. Sometimes documentation is missing or code is changed and it's wrong. Occasionally it's just grammatically incorrect even if the content isn't. We love documentation pull requests and I personally feel like it's one of the best contributions you can make. It's a great way to gain understanding of this and the contribution process. Making sure documentation is correct prevents tons of old and new developers from having a bad day. If you see incorrect documentation you should send a pull request as soon as possible. When sending a documentation pull request make sure you use CI skip in the first line of the command message. This makes sure that Travis CI doesn't run on your documentation pull request. Rails is one of the largest and most contributed to code bases on Travis CI. So that their CI server isn't running when necessary and so that we can keep the queue open so that actual code changes can run because it actually takes a really long time to run pull requests with all the supported versions of Ruby. This won't work all the time because issues aren't always simple but the simpler keep your code the faster it is for us to merge it in. If you're working on a yakshave it's better to send a bunch of smaller pull requests if possible because it's easier to review a small change over large hundreds of lines of code. If you work on a pull request and you want to prove that you've made the code faster you should use benchmark IPs. Benchmark IPs measure the number of iterations per second the specified code can run. This means that the higher numbers with lower standard deviation means the code is faster. This really takes a lot of guess work about whether or not your code speeds sped up Rails. You should include these benchmarks in your commit message if you're sending a performance-related pull request. Now we're going to look at how to write one of these benchmark IPs script. You can follow along by opening benchmark IPs example.rbscript in your RailsConf scripts directory. I got this specific example from a pull request that Eric Obermeichel sent to Rails. So this is an example of a benchmark IP script. It defines two methods that demonstrate two ways of sampling an array. As far as benchmark IPs go this is a very simple script. Sometimes you can't have the old slow method and the new fast method in the same script because they require too many changes to the internals of Rails so you'll have to write two scripts and compare them yourself. For this case we're simply comparing two ways of sampling an array outside of Rails so we don't need two scripts. First we require benchmark IPs then we define the two methods we need to compare for speed. The first slow method shuffles the array and takes the first item. The second fast method uses the sample method on the array. Then we invoke benchmark IPs in a block and use the report method to run each command and record the time spent. The compare method will report the number of times slower the slow method is in the fast method. Times slower. First the script calculates the number of records it can run it can iterate in 100 milliseconds. And then calculates the differences with standard deviation telling us how far the measurements are from the average. A high standard deviation means that the code is varying too much. Remember that your system memory and available CPU and other factors will affect the results so you need to focus on the standard deviation and the comparison. Here we can see on that on my machine array.shuffled at first is 18 times slower than array.sample. I want you to practice running the script with Ruby go into your rails.com scripts directory and run the benchmark IPs example. Your times will not be exactly the same as mine but you should still see that there's a big difference between the slow and the fast methods. And I'll give everyone a minute to try that out. This particular one was to change places in rails that used the slow version of array. I think it was array.sample.first to replace that with array.shuffled and using that as proof that it made it faster. Sorry. I've seen it run when it's in the bottom of the commit message. I know that it definitely doesn't run when it's in the first line but I've seen people put it in the body and it will still run. Maybe there's something in our settings that is causing that but it also makes it clear from the very beginning that it's a documentation pull request rather than having to read through it. Not that that's a reason. I don't know. When it was anywhere. Anyway. Doesn't matter. I always forget to put it in there anyway. I just have the power to stop Travis from running. Writing tests is not always fun especially when you aren't sure what models and tables there are on the rails test suite. If you find a regression or bug, you must add a test. Fix a regression or bug, you must add a test. If you're adding a new feature, you must add a test. The only time you don't need to add a test is the existing code and not altering the behavior of rails but in that case no test should fail. Your bug fix or feature request will not get merged without tests and because tests help us protect against regressions in other people's applications. When writing tests, try to use existing models first. Don't add new models or tables unless it's absolutely necessary. This is somewhat of an aside but I wanted to briefly talk about deprecating methods in rails. If a method has a no doc signature next to it you don't need to be deprecated if you want to remove it. This means that the method is private and rails applications should not be using it themselves. No doc means that the methods are not documented and will not be included in the rails docs. This example shows how to no doc an entire class. You can also no doc globals, adder accessors and methods as well. If a method is not no doc and you intend to remove it it needs to be deprecated and cannot be removed from rails until the next cycle. To write a deprecation warning you can use the active record deprecation class and the warn method. There are many examples of this throughout the rails codebase. Now we're going to learn some tools that will help you in finding your way through the rails codebase between metaprogram methods and mysterious errors. Sometimes it's hard to find the code that's causing a bug. I know that you're skeptical that I can make this easier. Finding your way through any new codebase can be irritating and frustrating but there are a lot of methods and tools that can help us find our way through any Ruby codebase. The first we're going to talk about ships with rails source itself and is called source location. Source locations method is defined in action view. This code will basically output the file name and line number of the method you're calling it on. Let's take a look at how to use this. Follow along with the slides on your own computer. I want you to CD into rails.com scripts directory and open the source location file with your favorite text editor. I'll give everyone a minute to do that. Find the line that says source.comments.deleteAll. We're going to use source location to find where the deleteAll method is defined in rails. Wrap your call to deleteAll in method and append source location. Add puts to the front so that it outputs the source location when we run the script. I'll give everyone a minute to get that set up. Can you see that? Now run the script with bundle exec Ruby and the script name as we did before. Remember you need bundle execs so it runs against your cache copy of rails directory. I'll go to the next slide that will show you what it looks like. You should see the path to the file and the line number among a bunch of other SQL. There's a bunch of other stuff there, but it should be in collection proxy. The line number may vary depending on if it changes since I wrote these slides. Now we know where deleteAll is defined on a collection proxy in rails. If we look at that file, we can see that deleteAll method is calling deleteAll on the association. We're going to need to define the definition of deleteAll on association to figure out what the code is actually doing because this definition doesn't really help us a lot. In this case, you could continue using source location with inside rails to find the definition or you could use something like c tags instead. I'm not going to have everyone kill the Wi-Fi by installing c tags right now, so just don't do this yet, and you can do it later. I promise you can ask me questions on Twitter if you get confused. C tags indexes your code base, methods, classes, and variables, and other identifiers in a tags file. This allows you to put your cursor on a method name and find where it is defined in any code base, particularly rails for this example. You can install c tags with homebrew on mac with apt-get, or yum on mac, or apt-get and yum on Linux. There's a version for windows, but it's not as simple as running one command. Does anyone actually have windows in here? I'll stop giving you windows examples. Yeah. Well, I wanted to make sure if there were any windows uses they didn't feel unloved. This is the bare minimum of what you need to do with your code base is to run c tags in your rails directory, but a lot of people don't like their tags file to sit in the main directory of their applications, so they put them in their .get directory instead, and you can do that with .get slash tags instead. From this point on, I'm only showing examples for Vim because that's what I use, and I'm familiar with. You can see the instructions at these repos on GitHub, and you can get these URLs later. Actually, if you just search Google, that'll come up as the first thing. Hopefully those are the right ones. If you use Vim, you'll want to add the following to your VRAM RC. This sets your c tags, excludes get directories, and skips indexing for JavaScript and SQL. To tag your rails directory, you can type your leader and RT, instead of running what we showed earlier, and you can use this in any of your applications or code bases. Now we are ready to search for definitions. To find a method definition with Vim, you type control write bracket when your cursor is on that method. If that method has more than one definition, you can see a list of possible matches by typing colon ts, and just use a different definition, type the number of the index file, and hit enter. I've recorded a screencast specifically how this works. We're going to open the file we were looking at earlier, collection proxy, and look at line 442 where delete all is defined on the collection proxy. We're going to put our cursor on the delete all, and we're going to use control write bracket, and it tells us that there is one of five more tags. With ts, we're going to look at the list of tags, and looking at that, we can see the one that we currently are looking at, and the next one is probably not it, but we're going to look at the collection association. We're going to type 3 and enter, and here we've found the definition of delete all. This doesn't really tell us a lot, because there's another method in there. We're going to go look at delete another file records, and see how that's defined. Because there's only one definition of it, it's going to take us right to the definition rather than having us choose a file first. With c tags, you can also look up how classes are defined. We can see that has many association here that inherits from collection association. We can use control write bracket to find collection association definition, and easily see that that inherits from association. You can see with this how we can easily move through a code base, finding different definitions of methods, classes, whatever we need, rather than using ac or grep to find definitions, because that takes actually a lot longer than moving through the code base with c tags. Let's say we know that this association primary key is in our stack, but we don't know who's actually calling it first. We could raise to find out who's calling it, but that's just going to stop the execution of our code. Instead of raising, we can use caller, which is a Ruby method defined by Ruby's kernel module. This will output the stack trace at the point the method is executed and won't stop our code. When run, the script returns a path of the file, the line number, and the name of the method that was called for each method in the stack. Your script continued to run without stopping because we didn't raise. It just simply outputs the current trace at the point of the code that you're in. Starting at the bottom, you can see that the trace started with our script with lead all, and moved traveled through and said that's not from my script that I wrote that we haven't run yet. Traveling through up to construct join attributes, and we can see that the association primary key is first called from the through association file on line 48 in construct join attributes. And this is the line that actually calls association primary key in Rails. Okay, let's practice using puts caller in one of our active record scripts. Open the puts caller file in the Rails com scripts directory with your favorite text editor. We can see that in this file there's a callback defined that is run after create and it just outputs I am a callback. This is very sophisticated. We're going to use puts caller here to find where this method is actually called from inside Rails because Rails is what's firing those callbacks. We want to know the stack trace at the point in which this code is hit. So add puts caller to the top of the call me method and save the file. And then run the script like we did before with bundle exec Ruby. And you should see a pretty big stack trace like this. And if you go to the very top you can see that that callback is called from callbacks.rb on line 428 in the method make lambda. And if we go there we can see the real reason why some developers hate callbacks in Rails. I call this method lambda vomit and every time someone's like oh I love callbacks I send them to this code. Ask them to tell me what this is doing. So now we know how to find who's calling method but what if we need to search for dynamically created method instead like ship attributes. Source location and puts caller isn't going to work there. The reason we can't use source location and c tags is because ship attributes is not actually defined as ship attributes. And c tags is kind of not very smart about it doesn't know, it has no idea that that's actually the definition of that method so we can't find it. And we can't use puts caller because we're not calling a method that we will tell us. We can't put it puts caller inside that method because we don't know what that method is or we don't actually have that method. To solve this problem we can use trace point. We can use trace point 2.0. Unfortunately it's not available in 1.9 or lower but with Rails master you should be using Ruby 2.2.2 anyway so that won't be a problem. Trace point provides the functionality of kernel set trace function in easy to use object oriented API. With trace point you can specify the types of events you want to listen for and can even filter the results. First we call trace point and pass call. We open a block and put the arguments returned that will be our stack trace and then we enable trace point with tpEnable. This will begin capturing our stack trace. We then call the code we want to trace and finally we disable trace point indicating for the trace to stop. Only the stack between tpEnable and tpDisable will be output. I say only but it's gigantic. Trace point the output is going to be really long and seems kind of difficult to read but for our purposes we only care about the top line where ship attributes is defined. We can see that that's in nested attributes on line 347. If we go there we can see why this method in the Rails framework simply can't be found with c tags because it's dynamically created based on your association in that you give to Rails. And we can see that it's actually defined as association name underscore attributes and association can be literally anything. Okay, now that we understand trace point let's use it in one of our scripts. Open the script called tracepoint.rb with your favorite text editor so we have a user class and that user class has one avatar and an avatar class that belongs to the user. Because of this we can use avatar attributes to create avatars through that user. Let's practice using tracepoint to find out where avatar attributes is defined in Rails. First invoke tracepoint and look for methods that call Ruby by using call. I'll pause while everyone adds this to their script. Again this is going to just return methods that are that are called by Ruby itself and then the arguments that will be output that's our stack trace that's going to be returned. Then put TP enable before the call to avatar attributes and TP disable after. Again I'll wait for everyone to get this all added in and if you are ahead you can run it if you remember you can use bundle exec Ruby to run the script and we'll see that on the next slide if you're not ahead. There might be I don't know I only used this like once I thought it was really cool though I wanted to show everyone how useful because we only actually only really care about the first line so it doesn't really matter how much it outputs right I mean you could actually you could just like you probably could tell it to just return the first one. You probably just put arguments first I think I would assume it would give you the first one. Someone try it out let me know. Now run the script with bundle exec and you should get a really long stack trace like I said before and if you find the top of the stack trace your attributes is defined in the same place we saw earlier nested attributes on line 347 and after this I promise we're done with stack traces. Yeah and this is where you should see it defined again line number might I think the line number is correct sometimes it changes a little bit people like to touch the callback code all right so all of these guidelines and code traversing would be useless if you didn't know the powerful get tools that would help you send pull requests with perfect commit history before we start if you don't want to lose all of the changes you made to your active record scripts add your changes and commit them you won't be able to push because you don't have permissions to my repo but you won't lose changes either if you don't want to say the changes you can do a get stash or a hard reset and I'll give everyone a minute to commit their changes so they don't lose them okay is everybody good ready we're ready to learn some fun get okay we're not going to talk about get commands that everyone knows like pull, clone, and push instead we're going to focus on commands that are more complex but will help you find where bugs are introduced right concise get history and even help undo your mistakes we're going to talk about get bisect interactive rebase reset and reflog if you're using github correctly you created a fork of rails in your own github this is required because you don't have push access to the rails repo you'll need to update your master often to be up to date with the main rails repo to do this you'll need to add a remote upstream to your local branch technically upstream can be called anything but I personally prefer to use origin for my fork and upstream to pull from the main repo first we'll need to add the remote to our branch with get remote add upstream and the URL to the original project I actually made a mistake in the blog post about rails so if you did that you'll need to remove it and redo it if you followed my instructions exactly so to remove or remotes you can use get remote remove upstream again you only need to do the remove part if you copied my blog post exactly and you can do this now if you want or you can skip it I was going to skip this because I didn't think we had wifi but we appear to have pretty good wifi so this simply just allows you to be able to easily pull in changes from rails by giving get to URLs and one of them is your URL and one of them is rails URL and you can get changes into your URL from rails but you can't push back up to rails and you can get those changes from rails master from github by doing get pull dash dash rebase upstream master this command will do a get fetch and then replay your changes on top of upstream master on top of your origin master from upstream master be sure to push this update from the rebase to your origin remote because if you reset one of your local branches to your remote master without doing this you won't have the updates you just pulled from the rails repo we're not going to try we're not going to try this out right now because I don't know if you have your actual rails repo set up to have origin as your your fork so I don't want you to mess anything up all right so git bisect is one of my favorite git commands bisect can seem really overwhelming because the documentation is confusing but it can be really helpful in identifying whenever regression was introduced into the code git bisect takes two points a bad point and a good point bad and good are really subjective terms here bad just means you but usually I in my case I believe bad means the bug exists or where code was added and behavior changed good just means the behavior bug that you're looking for does not exist there in that revision bisect takes those two points and starting in the middle picks a new revision for you to test if it's good or bad I recommend using an active record script like I talked about earlier when using git bisect because bisect needs to be run in the root directory and any test you might be relying on could have changed in the commits that you're bisecting to start you need a new to start you need to find a revision that's not broken you can give git bisect a branch a commit ref or a tag once you know where the bug exists and where it does not you can start Rails tags every release so it's pretty simple to check out a tag and see if the bug exists there or not when you have the ref that calls the bug you can start a bisect with git bisect start you then need to give it a bad point where the bug exists and a good point where the regression does not exist to name master as the bad starting point simply type git bisect bad to name a tag is good type git bisect good on the tag name to demonstrate I've recorded a screencast of me doing it git bisect in this demonstration I'm using a script that was provided in a bug report to Rails so the bug report included this included this bug report template already it has the required gems it does the connection to the SQLite database that we talked about earlier it defines a schema that just has a post table it has a post class that defines a custom arrow scope and then we finally have a test that is just asserting that the custom arrow scope has the the SQL that we expect so we're going to run this test against version 418 on Rails and you can see that we have an error that we cannot visit the arrow whatever I missed it then we're going to check out version 417 and we're going to run the script again and we can see the script passes so the bug exists on 418 and it does not exist on 417 so we're going to start the bisect git bisect start and then we're going to name 418 is bad and we're going to name 417 as good and now we can start and now bisect is picked a revision for us to test from the middle and we have about five steps before we know where the bug is so we run the script and because this passes this revision is good so now bisect is picked a new revision for us to test based on that and because we have the arrow select manager bug we can say that this is a bad and then we're going to run it again and it picks a new revision and this is good because the test passes and then a new revision and it can get a little bit tedious there's actually a way to automate this but we're not going to go over that today and you keep running it until it gives you the first bad commit and we can see that it was on September 4th and it's all Aaron Patterson's fault and you can use git show to see exactly what changed in that and when you're done if you need to go back and get out of the bisect you can type git bisect reset and it will return you back to where you started and you're no longer in a revision because bisect can be kind of confusing we're going to do this one together and we're going to live code all of the git sections together so I want you to follow along with me in your terminal as we bisect the branch in the RailsConf script repo so you guys can all see that right because you couldn't see it before so if you use the if you got the scripts from the repo from the GitHub repo you're going to need to do a git fetch and then check out the practicing git branch I can't spell it so you check out the practicing git branch and so what we're going to do is we're going to use this where in so there's documentation in this repo we're going to use bisect to find where the bisect documentation was introduced into the readme of this repo so first we're going to cat the readme and then we're going to look at the bisect section cat readme and we can see at the bottom there that there is a section about bisecting in the documentation so we're going to go back to master with git checkout master and we're going to cat the readme again and we can see that there is no bisecting documentation there so now that we know that we have a good and we have a bad point the good point is master the bad point is practicing git because the thing we're looking for exists there again, merely subjective terms they don't really actually mean anything you just need to keep them separate in your head so that's why I define it as behavior that exists versus behavior that doesn't exist in this case behavior is documentation existing so we're going to go back to the practicing git branch and we're going to start our we're going to start our bisect so git bisect start oops, I have two G's there we're going to name practicing git as bad by just typing git bisect bad and then we're going to name master as good with git bisect and the branch name so git bisect good and the branch name so git bisect good master and then we're in a now we're in a bisect and we're ready to start saying good or bad to each revision and find where this was introduced so now that we've started we can cat the readme again and we see that there's more documentation there was on master but I still don't see anything about bisect so this is a good revision alright now bisect has found a new revision for us to test and cat the readme again and we see code about bisect we see documentation about bisect so this is a bad revision because it has the behavior that we're looking for so git bisect bad and this is obviously a very simple example compared to running active record scripts but it gives you the idea of how you can separate your subjective knowledge of good and bad and how you can use bisect to find where something happened and you know that you're almost done because it says zero revisions left after this so cat the readme again and we still have bisect code so this one is also bad so we had a good and two bads and when you're done with that you should see this is the first bad commit and if you found the commit that says add section about bisecting this is the commit you want to find when you're practicing bisecting and you have successfully done your first bisect when you're done you're going to need to get out of here because this is not where you want to be editing code in the middle of a bisect so you do git bisect reset and it's going to return you to the practice and git branch before you started the bisect a useful git command is git commit amend amend allows you to edit your last commit message or commit if you have stage changes this will wrap up those changes into your last commit and you can edit the commit message I find this useful for when I know my change was part of the last commit like a typo and a method name or documentation but there are times I've accidentally done a git commit amend because I forgot what the last commit was and then you need to undo it and how are you going to do that that's really hard to undo an amended commit you can use git reset soft head at one this moves your branch to the previous state which is before the amended commit this will undo the amended commit but not the entire commit a git status will show you that the files are uncommitted but still staged if you needed to undo something like an entire commit you can use git reset soft till the end where n is any number of commits you want to rewind to before head this will remove the requested number of commits from from head a git status will show that the files that you changed are staged but no longer committed if you want to unstage those commits you can use git reset head so let's practice doing some git amend and resets together we're going to write some commits and then undo them I want you to follow along in your own terminal with me alright so on your practicing git branch open the read me with your favorite text editor and just add a section to the bottom and save it and it can be anything because we're going to get rid of this so don't worry about leaving weird stuff in your read me and when you're done doing that we can add the commits with git add and then we're going to use a we're going to amend the commit instead of writing a new commit so git commit dash dash amend and then we realize oh no I did not mean to add a new section to my commit about fixing spacing that's not okay those are not the same thing and I don't want to edit this commit to include two changes completely unrelated to each other how are we going to there's no way to get out of this situation we're stuck we're not well you could do that but then you end up with this weird file in your vim so quit and we can easily undo the commit that we did with git reset soft head at 1 and again this is going to rewind our history but it's not going to undo the entire commit so if you do a git status you'll see that these changes are staged and if you do then to unstage them you can use git reset head in all caps and in git diff we'll show you that we only undid the little part we just added not the entire commit it should only be the stuff that you just changed and so now now we realize that we don't actually want all the changes we made and we could stash but we're concerned that we made other changes and we haven't pushed them and we really just want our branch to be exactly the same as the remote so we're going to do a hard reset careful with this because you can lose well we're going to go over how to get it back if you do lose stuff but if you're going to lose anything that's not pushed to the remote but sometimes you have like a staging branch you want to reset it to master you don't need what was on it before you want to reset it to master so in that case you can use git reset hard origin for the remote or that could be upstream if you wanted to reset it to upstream master or something and we're going to reset it to our practicing git so that we know that this I probably need to spell correctly so that we know that it's back to where it started and we don't have any changes that we don't want and it gets status will show us that it got rid of the changes we had and we don't need to worry about that any questions so far about that stuff sometimes the Rails team will ask you to squash your commits if you have perfect commit history and are sending a pull request that you spent a lot of time on you can get more commits for that pull request a pull request with lots of oops that was wrong commits isn't going to get breached until those commits are gone we want to make sure that the git history is clean in this case your best friend is interactive rebase after making sure your master is up to date do git rebase-imaster on your branch I recommend doing this after you've already rebased and resolved your conflicts because we don't want to be resolving conflicts while we're editing our branch's history it can get really confusing if you're trying to change your commit messages and fix conflicts at the same time the best part about interactive rebase is you look like a genius you will always have perfect commit history and you look like you knew what you were doing the whole time no one ever has to know how many mistakes you made when you start an interactive rebase it looks like this pick it just means use the commit as is this is the default when you start an interactive rebase reword stops so that you can update the commit message but you can't change the commit itself edit stops the rebase so that you can amend the commit you also need to be explicit about continuing the rebase with git-rebase-dash-continue squash matches two commits into one and includes both commit messages and fixup is like squash but instead of including both commit messages it combines the changes from the commits and trashes the second commit message exec allows you to run commit messages as commands this sounds absolutely terrifying and I've never actually used it so if anyone knows how useful it is I'd love to know now we're going to practice doing an interactive rebase together I want you to follow along in your terminal like you've been doing before and to start an interactive rebase again we do git-rebase-imaster and do this on your practicing git branch because theoretically you can do this on master but then you're going to have to force push and it's okay to force push your own branch but it's not okay to force push public repos so git-rebase-imaster and we've got six commits and let's change the bottom one let's edit the commit message so change the word pick to edit on the sixth commit save and quit and so then interactive rebase stops for us to amend the commit the reason that it stops is in an edited commit you can change the commit message the author, the date the actual things that are committed in that commit it's really useful for just changing you could just change the entire commit like it's entire existence in this with edit so git-commit-amend like we did before when we were doing the resets and just add some text and save it with right-quit and because it's an amended commit and not a reword so a reword wouldn't stop like this it would just actually stop with the commit message and you'd edit it because you're not editing the commit itself you're just editing the message with reword so edit stops and you need to be explicit about continuing the rebase because the interactive rebase doesn't know you want to keep going just yet because you could like I said earlier you could change the author here or you could change the date or you could change everything so you're going to have to do a git-rebase- continue and if you do a git log you'll see that you should have the edit and commit in your history now and let's practice doing a squash and we're going to see what actually doing a full squash with interactive rebases like so start a new rebase with git-rebase-imaster and we're going to change all of the picks to squash except for the first one we don't want to change the first one because there's nothing to squash it into and the rebase will error so if you don't know how to use vim you need to use colon you can either change them annually or you can use colon percent s slash pick, slash squash slash gc this is going to replace pick with squash but pause and ask us first if we want to change it enter and it's a no for the first one and then yes, yes, yes, yes, yes so we're going to change all of them except for the first one to be squash and the last one is also a no because it's an edit, it's a comment but you don't need to if you change it it's not going to change how git works and then save and quit and squash stops so that if you don't actually want all of these commit messages in there you could edit it here sometimes I will squash and I will completely change the commit messages because they don't make sense as to separate commit messages and I will just write a whole new commit message for our purposes we'll just save and quit and if you do a git log again you can see all of the commit messages in one commit there that's pretty cool when you're done if you've already opened a pull request on Rails you're going to need to force push you can't do that here because you don't have permission to push to my repo but I want you to know that it's okay to force push your branches on Rails if you had a commit bit and were allowed to push to Rails you're not allowed to force push but if you have your own branch on your own repo it's okay to force push a lot of times we see developers will close their pull requests and open a new one because they think they can't force push don't worry about it we won't get in trouble with two pull requests instead of just one so don't worry about force pushing and you can always do that on your own stuff if you're unsure how to do something ask for help we're very willing to help anyone even if you think the question is stupid we help people squash all the time and if you get stuck we can always do the squash for you it's not a big deal another favorite git command of mine is git reflog they should change its name to git rescue me from the stupid thing I just did it's your safety net it literally tracks everything you're doing inside of a repository it records when you switch branches perform to rebase, stash to change reset your branch, everything did you do a bad rebase and push after? you can undo that did you hard reset your branch? you can undo that too you can literally go back in time with replog it's one of my favorite tools and everyone should know how to use it in your git repository you'll see git reflog if you type git reflog you'll see a list of actions performed like commit, rebase, aborting a rebase on the left is the rep of the change you made and the head number you can choose any of the number of refs and use a hard reset on your branch to go back in time so we're going to do this one together again alright so one of the cases where I've used a hard reset or not hard reset, git reflog is one time I squashed commits that I was going to send to Rails and realized that they actually should have been two commits, two pull requests they were not related enough that they should be in one pull request so I decided to unsquash them but I had already pushed to my branch so I couldn't just do a reset to the remote and go back in time so I had to use reflog to undo the squash so you can type git reflog and we can see the first one is where we finish the rebase rebase-i-finish the next five are the squashes that we did rebase-i-squash and then the one last set number six is where we started the rebase where we did the squash so that's rebase-i-start and then the one before that is where we did the rebase interactive rebase to edit the commit this is where we want to go back in time we don't want to lose the edit we did but we do want to undo the squash so we're going to quit reflog with Q oops you're going to want to grab the ref that's next to head at 7 it will be different than mine you also cannot reset to head at 7 itself because the one thing that's interesting is reflog actually tracks how similar your branch or the part you are in is so although we have head at 7 if we looked further down we'd actually be able to see all the other points in the history of the branch that was exactly the same as head at 7 because they'll have the same number and I think I've just totally confused you all here I'll show you so the number next to head at 7 is 5CFADE8 if we keep going down we can see that head at 8 was the same as that same number and there aren't any others right now that I could find so that's why you could use the number instead of using head at 7 so you can either do get reset hard, head at 7 or you can use the reference number, the revision number and then if you do that and you get log you'll see that you have all of your commits back but you didn't get rid of the edit you did and then you're gone back just 7 points in your history nope, I'm reset back to where where we were at 7 if your numbers may vary they shouldn't though your 7 should be the same as mine if you were doing everything that I was doing but if you accidentally took one that was in the middle of a rebase you could end up in a detached head state probably and it's okay because if you mess up reflog it's also recorded you messing up so you can just keep going backwards until you figure out where you didn't mess up and there is some garbage collection and you can't go back like 20 days later and get back something that you did probably but maybe I think you can control when it does the garbage collection when it trashes the reflog it's pretty cool one of the biggest questions I get is how to find issues to work on the issues tracker is difficult to parse even with all of my work on rails I've actually only ever fixed one or two issues I didn't find or create myself so what should you work on one of the best ways to help rails find regressions is to test the release candidates generally the release hasn't been used in production and there are sure to be regressions having a small test app that you can easily upgrade to check if everything is working okay is a really good way to test the releases and find bugs that you can fix as I've said many times before fixing incorrect documentation is one of the most important contributions you can make this can save others so much time if documentation is correct we have lots of sections of the guides that are work in progress and there are always grammatical errors and spelling mistakes that need attention it's a really really good way to gain experience in sending pull requests to rails and getting stuff merged in a lot of people are like documentation is too easy documentation is not easy documentation is the best part of rails and it needs the most help and a lot of times people have been working on documentation and they find a bug because the documentation doesn't match and that's an easy way to find if something is not right you have to ask or make a case for which one is right a lot of times the code is right because the documentation gets forgotten but sometimes when you're fixing the documentation you'll find that there's actually something that got totally missed in there you should focus on your strengths if you are not interested in SQL or aren't good at it focus on something that you're more confident in if HTTP requests are your thing work on action dispatch if frontend is more your thing work on action view or template generators there's something for everyone in rails there's new gems inside rails that don't get much attention as much attention as active record if you're a loss for what to work on these two are a really good place to start reviewing open pull requests can be beneficial to you and the community you'll become more familiar with the code base and contribution process as well as helping rails maintain a high standard of code you may also catch something that didn't get merged in and then you'll get a pull request remember rails does not take cosmetic changes like changing all the double quotes to single quotes or moving white space but we do take refactoring changes as long as they make the code cleaner Aaron Patterson and I spent three months refactoring association scope just so we could understand what it was doing it resulted in a code that was much easier to read and understand mostly breaking up complex methods can be beneficial to current and future contributors and although it is a pain watching the issues tracker is a good way to find an issue eventually it is full of lots of stuff that's really hard to work on but and it may take a while for you to find one that you can work on but it can be really rewarding when you find that bug and fix it for someone else it is possible you put a lot of time in and someone will come along and resolve it before you and that's happened to me and I'm not going to pretend that doesn't feel awful but it's an unfortunate reality of open source at the very least you'll have learned something from working through an issue and you'll gain a greater understanding of the Rails code base and the next time you have to fix a bug it'll be faster maybe we covered a lot today getting set up, guidelines for opening issues and pull requests, we went over advanced git commands and tools for traversing unfamiliar source code I hope that this talk has left you with the confidence to contribute to Rails or other open source projects and thank you for coming to my workshop on contributing to Rails again you can find me anywhere online at Eileen codes I'll be posting these slides on speaker deck later and of course you can download the repo at any time would it be helpful for me to push the answers to the active record scripts to the repo? I'll push those too I'm also going to follow up with some blog posts later next week so that if you missed anything and the slides aren't clear enough you can follow that again thank you everybody