beam_monitor.ex (2211B)
1 require Logger 2 3 defmodule ExSync.BeamMonitor do 4 def start_link(opts \\ []) do 5 GenServer.start_link(__MODULE__, opts) 6 end 7 8 def init(opts) when is_list(opts) do 9 {:ok, watcher_pid} = FileSystem.start_link(dirs: ExSync.Config.beam_dirs()) 10 FileSystem.subscribe(watcher_pid) 11 12 {:ok, %{watcher_pid: watcher_pid, finished_reloading_timer: false}} 13 end 14 15 def handle_info({:file_event, _watcher_pid, {path, events}}, state) do 16 %{finished_reloading_timer: finished_reloading_timer} = state 17 18 if finished_reloading_timer do 19 Process.cancel_timer(finished_reloading_timer) 20 end 21 22 if Path.extname(path) in [".beam"] do 23 {:created in events, :removed in events, :modified in events, File.exists?(path)} 24 |> case do 25 # update 26 {_, _, true, true} -> 27 # At least on linux platform, we're seeing a :modified event followed by a 28 # :modified, closed event. By ensuring the modified event arrives on its own, 29 # we should be ablle to ensure we reload only once in a cross-platorm friendly way. 30 # Note: TODO I don't have a Mac or Windows env to verify this! 31 if :modified in events do 32 Logger.debug "reload module #{Path.basename(path, ".beam")}" 33 ExSync.Utils.reload path 34 end 35 36 # temp file 37 {true, true, _, false} -> 38 nil 39 40 # remove 41 {_, true, _, false} -> 42 Logger.debug("unload module #{Path.basename(path, ".beam")}") 43 ExSync.Utils.unload(path) 44 45 # create 46 _ -> 47 nil 48 end 49 end 50 51 reload_timeout = ExSync.Config.reload_timeout() 52 timer_ref = Process.send_after(self(), :reload_complete, reload_timeout) 53 54 {:noreply, %{state | finished_reloading_timer: timer_ref}} 55 end 56 57 def handle_info({:file_event, watcher_pid, :stop}, %{watcher_pid: watcher_pid} = state) do 58 Logger.debug("ExSync beam monitor stopped.") 59 {:noreply, state} 60 end 61 62 def handle_info(:reload_complete, state) do 63 Logger.debug("ExSync reload complete!") 64 if callback = ExSync.Config.reload_callback() do 65 {mod, fun, args} = callback 66 Task.start(mod, fun, args) 67 end 68 69 {:noreply, state} 70 end 71 end