A dead simple ruby code hot reloader wrap around zeitwerk and listen.
See README for those gems for usage.
Getting Started
Install via Rubygems
$ gem install hot_reloader
OR ...
Add to your Gemfile
gem 'hot_reloader'
Usage
Following is a example for use hot_reloader with Roda:
Add loader initialize code into config/environment.rb
For simple use case, you just need pass paths to HotReloader.eager_load
or HotReloader.will_listen
.
# config/environment.rb
require 'bundler'
Bundler.require(:default, ENV.fetch('RACK_ENV', "development"))
require_relative 'application'
paths = ["#{__dir__}/../app", "#{__dir__}/../app/models"]
if ENV['RACK_ENV'] == 'development'
HotReloader.will_listen(*paths)
else
HotReloader.eager_load(*paths)
end
For more advanced case(e.g. you need setup zeitwerk loader instance yourself), you can pass this instance to HotReloader methods too.
# config/environment.rb
require 'bundler'
Bundler.require(:default, ENV.fetch('RACK_ENV', "development"))
require_relative 'application'
loader = Zeitwerk::Loader.new
loader.push_dir("#{__dir__}/../app")
loader.push_dir("#{__dir__}/../app/models")
if ENV['RACK_ENV'] == 'development'
HotReloader.will_listen(loader)
else
HotReloader.eager_load(loader)
end
When you change root directories files(app/.rb or app/models/.rb for above case), all monitored files will be reload.
it is possible to trigger reload from any .rb
files, even this file not follow constant
lookup name convention, and this file folder not add to root directories use push_dir
method.
Following is a example.
# app/app.rb
class App < Roda
plugin :hash_routes
Dir["routes/**/*.rb"].each do |route_file|
load route_file
end
end
# routes/blog.rb
class App
hash_routes.on "blog" do |r|
"blog"
end
end
routes/blog.rb
is not follow constant lookup name convention, so, routes/
folder can't be
add to root directories use push_dir method, but you can always trigger with loader.reload
if routes/blog.rb
was changed, then when app/app.rb
reloaded, it will load the
newest code in routes/blog.rb
from the Dir each loop.
For achieve this, you only need pass listened folders to will_listen method as secondary arg.
loader = Zeitwerk::Loader.new
loader.push_dir("#{__dir__}/../app")
listened_folders = ["#{__dir__}/../routes"]
if ENV['RACK_ENV'] == 'development'
HotReloader.will_listen(loader, listened_folders)
else
HotReloader.eager_load(loader)
end
Add other app files
config.ru
which used to start rack based web server with command rackup -o 0.0.0.0 -p 9393
# config.ru
require_relative './config/environment'
if ENV['RACK_ENV'] == 'development'
run ->(env) { App.call(env) }
else
run App.freeze.app
end
Write whatever application needed initialize code into config/application.rb
# config/application.rb
DB = Sequel.connect(ENV.fetch("#{ENV.fetch('RACK_ENV', "development").upcase}_DATABASE_URL"), timeout: 10000)
Sequel::Model.plugin :timestamps
Sequel.extension :symbol_aref
Add roda code into app/app.rb
# app/app.rb
class App < Roda
articles = ['programming ruby', 'programming rust']
route do |r|
r.post "articles" do
articles << r.params["content"]
"Count: #{articles.count}"
end
r.get "articles" do
articles.join(', ')
end
end
end
Directory structure is like this:
├── app/app.rb
├── config/environment.rb
├── config/application.rb
├── config.ru
├── Gemfile
└── Gemfile.lock
After change code in app.rb, all constant get removed from memory, and app.rb evaluated again!
For a more rich WIP sample project, please check my another project marketbet_crawler.
Support
- MRI 2.5+
- JRuby
Dependency
zeitwerk https://github.com/fxn/zeitwerk
listen https://github.com/guard/listen
Contributing
- Bug reports
- Source
- Patches:
- Fork on Github.
- Run
gem install --dev hot_reloader
orbundle install
. - Create your feature branch:
git checkout -b my-new-feature
. - Commit your changes:
git commit -am 'Add some feature'
. - Push to the branch:
git push origin my-new-feature
. - Send a pull request :D.
license
Released under the MIT license, See LICENSE for details.