Pothibo

Namespace stuff in your app/ folder

One thing that have been bothering me for a good year now is how everything in the app folder pollutes the global namespace. A convention is to use CamelCase class to avoid constant collisions. That’s why you have UsersHelper, UsersFormBuilder, UsersPresenter, etc. In many cases, it can be enough. However, when your needs start to get bigger, this solution gets messy (I like to avoid the term doesn’t scale).

I want to namespace all my form builders under the Forms constant. Ruby requires system has been somewhat hi-jacked by rails so it’s not always clear how it works. Let’s start with Ruby.

Ruby require/load

Require and load are similar method that lets you load your file into your environment. The difference between the two unfortunately falls outside the scope of this blog post. What’s important here is that the path to your file is irrelevant to what’s in it. Here’s an example of a ruby file that you could require.

# this file is located in ~/some/other/folder/example.rb
puts "I'm required!"
module MyAwesomeModule
  class CoolObject
  end
end

Open up IRB and see what happens:

irb> require ./some/other/folder/example.rb
I'm required!
=> true
irb> MyAwesomeModule::CoolObject
=> MyAwesomeModule::CoolObject

Autoload

A cool feature with ruby is to use Kernel#autoload(symbol, file_path) instead. This let’s you load the module only when you access it the first time, which can speed up your program if you have a lot of modules being required. Some things you need to know about autoload:

  1. The file is only required when the constant is accessed;
  2. You can wrap it in another module;
  3. The constant doesn’t have to have any relation with your file.

The last two are meant to be use when you have a module that needs to load dependencies. When you access a given module, all the module necessary are loaded into memory.

Let me show you a few example that implements these 3 assertions. You can test those in IRB if you wish. I will use the same example.rb file I used above.

The file is only required when the constant is accessed

irb> autoload :MyAwesomeModule, './some/other/folder/example'
=> nil
irb> MyAwesomeModule
I'm required!
=> MyAwesomeModule

You can wrap your file in another module

irb> module Another
  *>   autoload :MyAwesomeModule, './some/other/folder/example'
  *> end
=> nil
irb> MyAwesomeModule
NameError: uninitialized constant MyAwesomeModule
irb> Another::MyAwesomeModule
I'm required!
NameError: uninitialized constant Another::MyAwesomeModule
irb> MyAwesomeModule::CoolObject
=> MyAwesomeModule::CoolObject

The constant doesn’t have to have any relation with your file

irb> autoload :Wat, './some/other/folder/example'
=> nil
irb> MyAwesomeModule
NameError: uninitialized constant MyAwesomeModule
irb> Wat
I'm required!
NameError: uninitialized constant Wat
irb> MyAwesomeModule::CoolObject
=> MyAwesomeModule::CoolObject

Obviously, the tests here are useless by nature. It’s to give you an understanding of how autoload behaves.

Now, Rails autoload

Rails autoload is somewhat a different beast. To be precise, it uses inflector to give meanings to path. Something like forms/builder/user.rb becomes Forms::Builder::User under rails. That’s almost what I want. If you give it a try and add forms/builder/user.rb and try to use it in your view, it won’t be the expected result; you’ll get a module undefined error for the module Forms. That’s because rails uses a bit of both concepts and app/ is loaded using ruby’s require with some added magics.

Rails uses an object name Rails::Paths to load the app/ folder. There’s a lot of stuff going on here but what you need to remember is that Rails get a list of all the direct children and require from here. All subsequent children follow Rails rules about inflection which makes our initial Forms::Builder::User be Builder::User. Not what I want. It would be possible to have forms/forms/builder/user.rb but seriously…

app/forms as expected

This answer is two folds: First we need to remove app/forms from existing autoloader and two: it needs to behave the same way as the other folder, except the extra module.

Also, it’s best if we use something that rails uses and not some ugly hack.

Enter ActionDispatch::Reloader

This baby is why your rails code gets refreshed in development at every request. It’s also how rails load all users’ file into memory. By default, Rails has 3 reloader:

  • Routes Reloader;
  • I18N Reloader;
  • ‘Everything in app/’ Reloader.

The last reloader uses ActiveSupport::Dependencies.autoload_paths. The strategy is to remove app/forms from the autoload_paths if it’s there and give it its own reloader. Seems complicated? Not really.

The best way would be to create its own initializer. Let’s name it config/initializer/forms.rb

# config/initializers/forms.rb
Rails.application.config.to_prepare do
  path = Rails.root + "app/forms"
  ActiveSupport::Dependencies.autoload_paths -= [path.to_s]
  reloader = ActiveSupport::FileUpdateChecker.new [], path.to_s => [:rb] do
    ActiveSupport::DescendantsTracker.clear
    ActiveSupport::Dependencies.clear
      Dir[path + "**/*.rb"].each do |file|
        ActiveSupport.require_or_load file
    end
  end
  Rails.application.reloaders << reloader
  ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
  reloader.execute
end

ActiveSupport::FileUpdateChecker

This is a class that monitors files and folder. The empty array can be a list of file you want to explicitly check for changes. The second parameter is a hash to monitor folders. They key is an absolute path and the value is an array of file extension to check.

ActiveSupport::*.clear

ActiveSupport::FileUpdateChecker clears ActiveSupport::DescendantsTracker & ActiveSupport::Dependencies. This is the same that happens with stuff in you app/ folder. You want to purge your rails environment from any constants that are generated in your code and reload them so you have the expected behaviour in development (Reloading classes on every request).

ActiveSupport.require_or_load

At the beginning of this post, I talked a lot about require, autoload, and Rails autoloading scheme. You notice that I use neither here. The reason is that I don’t have any constant to pass. I want to use Rails’ folder/constant convention where a folder is a module. This is exactly what it does.

ActionDispatch::Reloader

Like we saw above, the reloader will take care of executing the block before each request using the callback to_prepare.

You can see that the reloader is executed at the end of the initializer to load all the forms when the server boots up.

Finally

Now you can namespace not only your forms, but your validator, presenter, decorators using the same technique and reuse some class name that would be otherwise unavailable to you because of the global namespace.

I hope you enjoyed this post. If you have, you can follow me on Twitter.

Get more ideas like this to your inbox

You will never receive spam, ever.