Packages

Reprise reloads your modules after they've been recompiled. It is thought as a companion to inotify tools. It scans for changes in beam files belonging to youyr project. Doesn't recompile stuff by itself.

Current section

Files

Jump to
reprise lib runner.ex
Raw

lib/runner.ex

defmodule Reprise.Runner do
require Logger
@moduledoc """
Module discovery and reloading.
The main entry point is `go/2` function.
"""
@type time :: :calendar.datetime
@type path :: [String.t]
@type beam :: String.t
@spec load_path(Regex.t) :: path
def load_path(pattern \\ ~r[/_build/]) do
for d <- :code.get_path,
d = Path.expand(d),
Regex.match?(pattern, d),
do: d
end
@spec iterate_beams(path) :: [beam]
def iterate_beams(load_path) do
for d <- load_path, File.dir?(d) do
for f <- File.ls!(d), Path.extname(f)==".beam", do: Path.join(d,f)
end
|> List.flatten
end
@doc """
Returns all beam files belonging to the build of current project.
"""
@spec beams() :: [beam]
def beams(), do: load_path() |> iterate_beams()
@doc """
Returns pairs of beam files and modules which are loaded
and belong to the build of current project.
"""
@spec beam_modules([beam]) :: [{beam, module}]
def beam_modules(beams \\ __MODULE__.beams) do
beamset = beams |> Enum.into(HashSet.new)
for {m,f} <- :code.all_loaded,
is_list(f),
f = Path.expand(f),
Set.member?(beamset, f),
do: {f,m}
end
@doc "Reloads a single module."
@spec reload(module) :: {:module, module} | {:error, atom}
def reload(module) do
:code.purge(module)
:code.load_file(module)
end
@doc """
Attempts to reload all modules which belong to the current mix project
and have changed between given time frames.
If there were any and they reloaded successfully, prints a summary
via `Logger.info/1`.
Modules whose reload errored are silently ignored.
Returns `:ok` if there were any modules successfully reloaded
or `nil` when there were none.
"""
@spec go(time, time) :: :ok | nil
def go(from, to) do
modules = for {f,m} <- beam_modules() do
case File.stat(f, time: :local) do
{:ok, %File.Stat{mtime: mtime}} when mtime >= from and mtime < to ->
case reload(m) do
{:module, m} -> {:reloaded, m}
{:error, _} -> {:load_error, m}
end
{:ok, _} -> {:unmodified, m}
{:error, _} -> {:fstat_error, m}
end
end
reloaded = for {:reloaded, m} <- modules, do: m
unless reloaded == [], do:
Logger.info("Reloaded modules: #{inspect reloaded}")
end
end