HeavyControl

Gem Version Build Status Code Climate Test Coverage Issue Count git.legal

HeavyControl adds tools which allows you to modify Rails autoloading logic.

Installation

Add this line to your application's Gemfile:

gem 'heavy_control'

And then execute:

$ bundle

Rails 4.2 and 5.0 are supported by this gem.

Usage

Create initializer and place HeavyControl config inside:

# config/initializers/heavy_control.rb
HeavyControl.config do
  # example config:
  ignore_subfolder 'operations'
  always_load 'SomeClass'
end

HeavyControl resolves several problems related to Rails' autoloading:

  • Debugging
  • Ignore subfolders while constant resolving
  • 'Toplevel constant referenced' situations

Debugging

It may be useful to debug autoloading behaviour. To enable this feature just add debug to HeavyControl config:

# config/initializers/heavy_control.rb
HeavyControl.config do
  debug
end

And you will get logs on DEBUG level via Rails.logger. Like this:

HeavyControl: Load missing constant 'Organization' from Object
HeavyControl: Search for file with suffix 'organization'
HeavyControl: and found '/vagrant/app/models/organization.rb'
HeavyControl: Require of load '/vagrant/app/models/organization' with const_path 'Organization'

You may also turn off previously enabled debugging using debug false.

Ignore subfolders

Rails automatically adds all folders under /app into autoloading paths. When you use constant (class, module) first time autoloading will search for file with implementation. File path calculation is based on naming convention. So, for YourContext::YourClass it will be [RAILS_ROOT]/app/[ANY_FOLDER]/your_context/your_class.rb.

But sometimes (when we use trailblazer, as example) we want to split our files into subfolders without affecting their namespaces. In other words we want YourContext::YourClass to be found in app/whatever/your_context/your_class.rb OR app/whatever/your_context/subfolder/your_class.rb. To achieve this you should add ignore_subfolder option to HeavyControl's config:

# config/initializers/heavy_control.rb
HeavyControl.config do
  ignore_subfolder 'subfolder'
end

For example lets write substitution to trailblazer-loader. README of this gem describes three naming and directory layout styles: Compound-Singular, Explicit-Singular and Explicit-Plural.

Compound-Singular

It's very non-rails style. It forces us to keep from one to several class definitions in a single file. If you are using Rails I suggest you to avoid this. Currently, heavy_control doesn't support any code-placement styles where we put several classes inside one file.

Explicit-Singular and Explicit-Plural

This directory layout styles will work with classic rails autoloading correctly, except 'operation' and 'operations' folders. Given directories doesn't affect class namespaces. So, we will easy express this rule via ignore_subfolder option.

# config/initializers/heavy_control.rb
HeavyControl.config do
  ignore_subfolder 'operation'  # singular
  ignore_subfolder 'operations' # plural
end

'Toplevel constant referenced' situations

Sometimes we can get errors related to warnings like this:

warning: toplevel constant YourClass referenced by YourContext::YourClass

Classic solution is using of require_dependency method. HeavyControl brings another approach: always_load option. Solution for given warning will be:

# config/initializers/heavy_control.rb
HeavyControl.config do
  always_load 'YourContext::YourClass'
end

In other words, for always_load you should use constant name which displays right after 'referenced by' words in a warning text.

You may write several names separated by comma.

always_load differs from require_dependency. It explicitly resolves constant names on initalization via constantize (before other constants are loaded). Also it happens each reload in development.

It seems to be a bit more accurate way than require_dependency because constant resolving involves Rails autoloading mechanism without direct using of more "low-level" require_dependency. Also it guarantees that ignore_subfolder feature will work as expected.

Development

This gem uses appraisal for testing against both 4.2 and 5.0 Rails versions. The command is bundle exec appraisal rake.

Also code inside your PR should pass rubocop checks: bundle exec rake rubocop.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ffloyd/heavy_control.

License

The gem is available as open source under the terms of the MIT License.