これはなんだ
Rubyでhot reloadするのを簡潔に書いてみるサンプルです。
hot reloadとは
Rubyをはじめ殆どのプログラミング言語では、通常はプログラムを実行すると(もしくは最初にソース/バイナリが読み込まれたとき以降)、実行中はソースコードの反映は行われません。
それを実行中にソースコードの反映を行うのがhot reloadです。 主に開発環境で使用され、起動に時間のかかるプログラムの開発等に恩恵があります。
実装してみる
ソースは
1. hot reloadがない状態
# a.rb class A def self.say_hello puts 'Hello, World' end end
# main.rb loop do A.say_hello sleep 3 end
main.rbを実行すると3秒おきに標準出力に 'Hello, World'と出力される簡単なプログラムです。
2. 変更を反映させるようにする
# main.rb loop do load File.expand_path(File.dirname(__FILE__) + '/a.rb') A.say_hello sleep 3 end
ループ毎にソースコードをloadするようにしてみました。 これでa.rbのソースコードを実行中に
# a.rb class A def self.say_hello puts 'こんにちは、世界!' end end
のように書き換えると途中から出力が変わります。 簡単ですね。
ちなみにloadの代わりにrequireを使うと期待通りには動きません。
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/require.html
Ruby ライブラリ feature をロードします。拡張子補完を行い、同じファイルの複数回ロードはしません。
3. 問題点の解消
さて、前節の内容で上手く行ったように見えますが、実は問題があります。 一度定義したメソッドなどが消えないのです。
前節で行った内容の範囲内ではなぜうまく行くかと言うと、 Rubyはオープンクラスで、既にClass, Moduleが定義されていても さらにそのClassにModuleを開いて定義しなおすことができます。 既にあるメソッドと同名のメソッドを書くと上書きが行われます。
メソッドの追加、更新の場合はそれでもうごくのですが、削除はその仕組みではうまくいきません。
なのでこうします
# main.rb loop do if Module.const_defined?('A') Object.send(:remove_const, 'A') end load File.expand_path(File.dirname(__FILE__) + '/a.rb') A.say_hello sleep 3 end
remove_constを使うと既に定義されている定数、Module, Classを削除できます(厳密にはそうではない、ですがここでは詳しい話は省きます)
これでざっくりとhot reloadする簡単なコードができました。