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 install
To 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 initializers
Then 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
end
The gem is available as open source under the terms of the MIT License.