Alki::Reload
Installation
Add this line to your application’s Gemfile:
gem 'alki-reload'
And then execute:
$ bundle
Or install it yourself as:
$ gem install alki-reload
Usage
To use Alki::Reload in an Alki project, it must be mounted in the assembly. By default, Alki::Reload
will not actively watch files or actively hook into services. Setting enable
to true will enable both.
Alki do
try_mount :reloader, 'alki/reload', enable: true
# ...
end
Naming Pitfalls
Alki::Reload attempts to be conservative in what it reloads. By default, it will only reload files which are in your project, and are either in the ruby load path or registered with Alki::Loader.
Additionally, Alki::Reload will only reload files where it can find a corresponding constant. This is done via a straightforward "classify" method which does not take abbreviations into account.
Examples of Classes that Alki::Reload will not reload
class Foo
end
Expects Foo
to be in the module MyProject
.
module MyProject
class HTTPClient
end
end
Doesn’t know that "HTTP" is an acronym, so it expects class to be HttpClient
.
Conditional usage
Because enabling reload can have a performance impact, typically it’s only enabled when in some sort of development mode.
Alki do
set(:development?) { ENV['APP_ENV'] != 'production' }
try_mount :reloader, 'alki/reload' do
set(:enable) { development? }
end
# ...
end
In addition, using try_mount
instead of the normal mount
will only mount the reloader if the gem can be found,
which can be controlled using Bundler.
Main Loops
One of the core issues in code reloading is when to actually do the reload. If your Assembly is called from some other code (such as a Rails application), then reloading can just be done between calls into the assembly.
But, if the entire application is running inside the assembly, then a way to reload the assembly while it’s still running is needed.
Most applications spend most of their time inside a "main loop". If it’s a server, it might be the loop listening for incoming data, if it’s a console application it might be the loop waiting for user input.
Because the main loop is always running, there is never an opportunity to reload it. Alki::Reload provides a feature to help work around this.
First off, because the service the main loop is in can’t be reloaded, it should be made as small and simple as possible, offloading all other functionality into secondary services that it takes as dependencies.
Second, it should be tagged with a main_loop
tag.
By tagging your main loop service,
Alki::Reload will actively hook into the service
and wrap it’s dependencies with wrapper objects
that will pick up the new version of those dependencies
whenever the project is reloaded.
Alki::Loader must be enabled for this feature to be active.
Alki do
mount :reloader, 'alki/reload', enable: true
set :prompt, "> "
service :handler do
-> line { puts line }
end
tag :main_loop
service :main do
require 'readline_loop'
ReadlineLoop.new prompt, handler
end
# ...
end
require 'readline'
class ReadlineLoop
def initialize(prompt, handler)
@prompt = prompt
@handler = handler
end
def run
while line = Readline.readline(@prompt,true)
@handler.call line
end
end
end
In this example, our main loop is main.run
. Because the main
service is tagged even while it
is running the prompt and handler can be changed and reloaded.
Watched Directories
By default, lib
, config
and any files or directories configured in
Alki::Loader are watched.
Additional directories can be added by overriding the dirs
element. Additional directories must also
be in $LOAD_PATH
.
Alki do
mount :reloader, 'alki/reload' do
set(:enable) { true }
set(:dirs) { original.dirs + ['app'] }
end
# ...
end
Manual Reloading
In addition to watching for filesystem changes, a project can be reloaded manually by calling
the reload
func in the reloader. This works even when the reloader is not enabled.
Alki do
mount :reloader, 'alki/reload'
# ...
end
instance.reloader.reload # reload instance
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/alki-project/alki-reload. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.