Create a gist now

Instantly share code, notes, and snippets.

@shime /
Last active Nov 16, 2017

What would you like to do?
bake your own code reloader for rails

Let's build our own code reloader for Rails, shall we?

Require dependency problems

Run this inside rails/railties:

$ grep -rn "eager_load_paths" .

You should get the results from Rails::Engine::Configuration and Rails::Engine. As you know, each Rails application is actually a Rails::Engine and Rails::Engine::Configuration is that thing wrapped inside Rails.application.config block.

This is the method responsible for eager loading in Rails::Engine:

def eager_load!
  config.eager_load_paths.each do |load_path|
    matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
    Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
      require_dependency file.sub(matcher, '\1')

As you can see, it uses require_dependency and you can read more about its magic in this great answer. Long story short, Ruby instance doesn't allow you to "unrequire" a file, it does its requiring only one time. That's where require_dependency comes in.

Try using it and it will still not work. That's because require_dependency is depending on the context it's called from. Let me explain.

If you have a controller like this

require_dependency "monkey"

class MonkeyController < ApplicationController
  def index
    render :text =>

and the matching lib file looks like this

class Monkey
  def greet
    "ZOMG, I'm a monkey!"

then Monkey will be reloaded only when the controller is reloaded. Controller is reloaded only when you change it, so this is probably not what you want.

Reloading on every request

You want your monkey to be reloaded on each request and things get a little bit complicated because of that.

Let's see where this happens... Take a look at Rails::Application::Finisher. This is the thing responsible for finishing the Rails initialization process.

  # Set clearing dependencies after the finisher hook to ensure paths
  # added in the hook are taken into account.
  initializer :set_clear_dependencies_hook, group: :all do
    callback = lambda do

    if config.reload_classes_only_on_change
      reloader =*watchable_args, &callback)
      self.reloaders << reloader

      # Prepend this callback to have autoloaded constants cleared before
      # any other possible reloading, in case they need to autoload fresh
      # constants.
      ActionDispatch::Reloader.to_prepare(prepend: true) do
        # In addition to changes detected by the file watcher, if routes
        # or i18n have been updated we also need to clear constants,
        # that's why we run #execute rather than #execute_if_updated, this
        # callback has to clear autoloaded constants after any update.

It looks like ActionDispatch::Reloader and ActiveSupport::FileUpdateChecker are doing all that's necessary for this. Reloader is just a Rack middleware (hit rake middleware to convince yourself) which calls FileUpdateChecker on every request.

Now that we know all of this, we can build our own reloader that's thousand times simpler than this and you should probably burn it after:

class StupidReloader
  def initialize(app)
    @app = app

  def call(env)
    do_crazy_stuff! env


    def do_crazy_stuff!

      require_dependency "#{File.join(Rails.root, "lib", "monkey")}"

Replace the default Rails' reloader:

# somewhere inside config/environments/development.rb
config.middleware.swap ActionDispatch::Reloader,
  StupidReloader # we like to live dangerously 

Monkey should now get reloaded on every request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment