A simple way to add code auto-reloading to not Rails app
Add this line to your application's Gemfile:
gem 'ruby-code-autoreloader', resolving: 'ruby-code-autoreloader', tag: '0.1.0' # or ref: 'some md5'And then execute:
$ bundle installTo set configuration use RubyCodeAutoreloader.configure with a hash of params.
List of possible params:
| Name | Default value | Description |
|---|---|---|
| default_file_watcher | ActiveSupport::FileUpdateChecker | File watcher class that will be used for checking files updates |
| autoreload_enabled | ENV['RACK_ENV'] == 'development' | The code reloading will works only if this param is true. Then all libs specified in autoloadable_paths will be loaded with load directive, e.g. load 'lib_path/file.rb'. If this param is false, then all libs specified in autoloadable_paths will be loaded as usual with require. Loading process uses the ActiveSupport::Dependencies::Loadable.require_or_load method |
| autoloadable_paths | [] | This is an Array of paths to files or folders from which will be loaded all ruby files '*.rb'. All files and Classes should be in the compliance with the Naming convention described below. |
| reload_only_on_change | TRUE | If this is true, then Reloader will check files in autoloadable_paths with initialized file_watcher and will reload the files if any of them was updated. If this is false, then files will be reloaded on each RubyCodeAutoreloader.reload method call, e.g. always |
| logger | Logger.new(STDOUT) | Logger object |
Also you can add specific environment variable for changing reloader mode, for
example ENV['AUTORELOAD_ENABLED']='true', and use it in configure.
Example of initializer with configuration:
# config/initializers/ruby_code_autoreloader.rb
require 'ruby_code_autoreloader'
RubyCodeAutoreloader.configure(autoreload_enabled: (ENV['RACK_ENV'] == 'development' &&
ENV['AUTORELOAD_ENABLED'] == 'true'),
reload_only_on_change: false,
autoloadable_paths: %w(app/endpoint_flux/middlewares/decorator
app/endpoint_flux/middlewares/validator
app/endpoint_flux/validations/concern/error.rb
app/models
app/endpoint_flux/decorators
app/endpoint_flux/endpoints
app/workers).freeze)RubyCodeAutoreloader will search for all ruby "*.rb" files inside directories that described in autoloadable_paths.
The pattern for searching files is Dir.glob("#{path}/**/*.rb"), e.g. will be loaded all files from subdirectories too.
Or you can define the direct file path, for example app/endpoint_flux/validations/concern/error.rb.
Each file will be loaded by load or require directive, depends on autoreload_enabled mode. After each file loading,
RubyCodeAutoreloader will accumulate loaded Classes/Modules inside module variable @autoloaded_classes, that will be
used to remove constant before each Reloading.
And here we have a special Naming convention:
the Classes/Modules that will be used for Reloading (e.g. will be added to autoloaded_classes), should have
the same last module names as the files name too. Otherwise it will not be included for Reloading and
will be loaded only once at the start.
That's because it's hard to track what modules/classes was loaded from the file as they might have their own hidden dependencies/libs inside,
and we can't track in what path they were defined.
For example we have this file app/endpoint_flux/endpoints/users/create.rb in specified dir app/endpoint_flux/endpoints,
then Class/Module defined inside should has the name Create as the last module name,
e.g. like this ::SomeModule::Create or Users::Create or EndpointFlux::Users::Create or just Create
Firstly you need to configure it with initializer, as described in Configuration.
Then you need to call RubyCodeAutoreloader.start method after all initializers.
Depends on the autoreload_enabled config param, RubyCodeAutoreloader will loads the ruby libs specified in
autoloadable_paths by load or require directive.
Example of environment.rb with initializing the RubyCodeAutoreloader by start method:
# config/environment.rb
ENV['RACK_ENV'] ||= 'development'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
if ENV['RACK_ENV'] == 'development' || ENV['RACK_ENV'] == 'test'
require 'dotenv'
Dotenv.load(".env.#{ENV['RACK_ENV']}", '.env')
end
require 'bundler/setup'
Bundler.require(:default, ENV['RACK_ENV'])
$LOAD_PATH.unshift File.expand_path('..', __dir__)
$LOAD_PATH.unshift File.expand_path('../app', __dir__)
Dir.glob('config/initializers/*.rb').each { |file| require file }
require "config/environments/#{ENV['RACK_ENV']}.rb"
RubyCodeAutoreloader.start ### Run it only after all initializersThen you can just put RubyCodeAutoreloader.reload to the place where you want to check for updates before starting
processing the query. In the example below this call_endpoint_flux in Sneakers worker will be called on each query
to the service, and we call RubyCodeAutoreloader.reload before another methods. If autoreload_enabled is set
to false, then reloading will be skipped.
Example of Sneakers worker with the RubyCodeAutoreloader.reload call inside:
# app/workers/base_worker.rb
module BunnyPublisher
module EndpointFlux
class SneakersWorker
def call_endpoint_flux(message, props)
action = self.class.endpoint_action || props[:headers]['action']
RubyCodeAutoreloader.reload # calling it before processing the query
endpoint = endpoint_for("#{self.class.endpoint_namespace}/#{action}")
_, response = endpoint.perform(request_object(message))
response.body
end
end
end
endThe gem is available as open source under the terms of the MIT License.