Contribute to Rails 6!

Once upon a time, there was a Rails developer who wanted to become a Rails contributor. Deep down he even wanted to become a Rails core team member! So one day he decided to take a first step: he downloaded the source code of Ruby on Rails!

So I downloaded the source code of Rails:

$ git clone git@github.com:rails/rails.git
$ cd rails
$ ls
actioncable
actionmailbox
actionmailer
actionpack
actiontext
actionview
activejob
activemodel
activerecord
activestorage
activesupport
Brewfile
ci
CODE_OF_CONDUCT.md
CONTRIBUTING.md
Gemfile
Gemfile.lock
guides
MIT-LICENSE
package.json
rails.gemspec
RAILS_VERSION
railties
Rakefile
README.md
RELEASING_RAILS.md
tasks
tmp
tools
version.rb
yarn.lock

Damn.. so much stuff in here.. I know most of Rails components, but there are some novelties in here as well. Thankfully Rails has several guides to help me fill the gaps.

  • ActionCable is a WebSocket wrapper that helps Rails applications do server/clients real-time communication. If you want your web page to receive notifications from your server without AJAX calls, this is what you need.
  • ActionMailbox allows your application to receive e-mail and process them in controllers.
  • ActionMailer is for sending e-mails from your application.
  • ActionPack bundles two major components: ActionDispatch and ActionController.
  • ActionDispatch is in charge of routing an incoming request to the right controller/action for processing.
  • ActionController has the responsibility of processing the request and providing a response (MVC).
  • ActionText provides a rich textbox to your forms (a textbox where you can put styled text, images, etc.)
  • ActionView is a set of tools (layouts, partials, form builders, ..) for compiling the response of the controller (MVC).
  • ActiveJob is an interface for scheduling asynchronous jobs.
  • ActiveModel provides helpers to help you structure your model classes (defining attributes, setting validations and callbacks, ..).
  • ActiveRecord is an ORM, meaning it is the layer that lets your controllers talk to your database(s) (MVC).
  • ActiveStorage takes care of file upload for you.
  • ActiveSupport makes Ruby programming even nicer by adding to it a lot of pretty neat features.
  • Railties is the core of the Rails framework. It provides several hooks to extend Rails and/or modify the initialization process. This allows the above-mentioned components and any gem included in your Rails application to extend the Rails framework.
  • The other files are irrelevant for me at this point, I just need to get a sense of how Rails source code is organized.

Okay, so I have an overview of the Rails repository structure. Now what?

Well, my goal is to contribute to Rails. The simplest -yet always useful- contribution one can make to an open source project is a contribution to the documentation. Let’s start with that.

After forking the Rails repository, I need to clone it and configure it to have an easy way to update it from upstream:

$ cd
$ mv rails rails_upstream
$ git clone git@github.com:<username>/rails.git rails_fork
$ cd rails_fork
$ git remote add upstream git@github.com:rails/rails.git
$ git fetch upstream

Now when I need to update my fork, I’ll do the following:

$ git checkout master
$ git pull upstream master
$ git push

And when I’m asked to rebase my branch on master, I’ll do the following:

$ git checkout master
$ git pull upstream master
$ git push
$ git checkout <my-branch>
$ git rebase master <my-branch>
$ git push -f

Contribution to documentation

ActiveStorage and its documentation are pretty new, so I guess there are more chances to find something easy to contribute there.

Reading the source code of ActiveStorage (activestorage/lib/active_storage/service.rb), I noticed a call to ActiveSupport::Notifications.instrument. I didn't know what that is so I looked it up:

The instrumentation API provided by Active Support allows developers to provide hooks which other developers may hook into. There are several of these within the Rails framework. With this API, developers can choose to be notified when certain events occur inside their application or another piece of Ruby code.

Okay cool. There are several hooks added by ActiveStorage, so maybe they’re not all mentioned in Active Support Instrumentation guide.

I guessed right, three hooks are missing:

  • service_download_chunk.active_storage
  • service_update_metadata.active_storage
  • preview.active_storage

I think I found my first contribution to Rails :D

$ git checkout -b add-activestorage-instrumentation-hooks-to-guide
$ # apply wanted modifications to guides/source/active_support_instrumentation.md
$ git add guides/source/active_support_instrumentation.md
$ git commit
$ git push -u origin add-activestorage-instrumentation-hooks-to-guide

Important: since this commit only changes documentation, it is important to add [ci skip] to the commit message so that the CI doesn't get overloaded unnecessarily.

Now that my branch is pushed, I have to go to https://github.com/rails/rails and open a new pull request.

Pull Request: https://github.com/rails/rails/pull/36010

There is now one very difficult thing to do: wait. Wait for a member of the core team to review the pull request and comment on it or merge it. Wait without reloading the Github page every 5 seconds. Just move on to something else and wait for an e-mail notification.

It got merged.

Contribute to a Rails component

I started with ActiveStorage, I’m going to stick with it for now. I don’t have a feature I want to add to it so from time to time I check the issues on Github in search of inspiration. The bug I found that’s related to ActiveStorage and pretty easy to fix is the following: https://github.com/rails/rails/issues/35953

Great! I now have my next thing contribution and this time, it’s code. Code goes with testing, so I have to prepare my development environment to run the test suite of Rails. Scary, but nothing unachievable :)

https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#setting-up-a-development-environment

There are mainly two roads for this: one can either install all the dependencies or run everything in a virtual machine. I’m personally going to do both for the sake of learning, but I’ll only mention the easiest way in this blog, the virtual machine way.

First I followed this guide: https://github.com/rails/rails-dev-box

After installing VirtualBox and Vagrant on my Debian 9.8, I ran the following commands

$ # in the host:
$ cd
$ git clone https://github.com/rails/rails-dev-box.git
$ cd rails-dev-box
$ cp -r ~/rails_fork ./rails
$ vagrant up
$ vagrant ssh
$ # once in the virtual machine:
$ cd /vagrant/rails
$ bundle

Notice I copied my rails_fork directory inside the rails-dev-box directory so that I can ssh into the virtual machine and access the source code I'm working on (to run the tests).

Okay so now I have my development environment setup ready to rock n roll, I need to run the tests before anything else. I want to make sure the tests run successfully before changing the source code of ActiveStorage (so that if it fails I know because of my code changes, not some setup mistake).

$ # in the virtual machine:
$ cd /vagrant/rails
$ bundle exec rake

And again: wait. Just.. wait.. for.. it.. to.. run.

Please be green

Done. Yeah, it’s green!! Let’s get down to business.

Note: as advised in the documentation, I’ll edit the code in the host and run the tests in the virtual machine. This way I don’t install any software on the virtual machine and avoid accidental changes of package versions and what not.

$ git checkout -b active-storage-bmp-variants-support

I can now add the following changes:

  • edit activestorage/lib/active_storage/engine.rb to fix the issue (here I'm whitelisting a mime type for ActiveStorage to know it can create BMP variants)
  • edit activestorage/test/models/variant_test.rb to test that ActiveStorage actually succeeds at creating a BMP variant
  • add activestorage/test/fixtures/files/colors.bmp that will be used by the above test
  • edit activestorage/CHANGELOG.md to inform Rails developers of the changes my commit introduces.

It hasn’t been that hard to write this patch. The only part that I struggeled with a bit was the testing part because I’m used to RSpec, not minitest. I read the existing tests to find out how to write my own, then I googled how to test a single file. In my case, I ran:

$ cd /vagrant/rails/activestorage
$ bundle exec ruby -Itest ./test/models/variant_test.rb`

Green. I like that. I can now commit and push to Github:

$ cd /vagrant/rails
$ git add ./activestorage
$ git commit
$ git push -u origin active-storage-bmp-variants-support

Pull request: https://github.com/rails/rails/pull/36051

Note: sometimes you’ll have multiple commits in your pull request. You might be asked to squash them to help keep the history as clean as possible. If asked so, git rebase -i and git commit --amend are your friends.

So I opened a pull request and.. waited. Yes, there’s a lot of waiting in open source contributions. Keep in mind that the core contributors have their paid job to take care of, and their personal life, and a bunch of issues to read, and a bunch of pull requests to review, and their own pull requests to work on, and mailing lists to keep up to date with, etc. Yes it does take time. Yes we love them for the awesome work they do. Yes when we start contributing, it’s frustrating to wait and we almost get banned from Github for refreshing too often our pull request page and that’s okay. We’re contributing to Rails and that’s really an awesome feeling because we get to help other developers, we learn more about Rails and we appreciate even more the work all these contributors have done before us.

Closing note

First of all, a big thank you to @eileencodes ❤ for her talk at RailsConf 2015 Breaking Down the Barrier: Demystifying Contributing to Rails: https://www.youtube.com/watch?v=7zoD6NZy6vY

Guys, if you’ve been playing with Rails for two or three years, you probably can contribute. My advice is this: read issues on Github, test them to confirm that they are reproducible or ask for more details when need be, read pull requests, when you find something new to you don’t just read tutorials and documentation but go read the source code as well. It doesn’t matter if you don’t understand everything that’s going on, at least you’re gathering knowledge and slowly building an intuitive understanding of how Rails works. Before you know it, you’ll be so familiar with it that contributing to Ruby on Rails will be a piece of cake.

Peace and love my friends.