Packages

ActiveEx is a sync replacement that uses native file-system OS async listeners to automatic compile and to reload after saving all *.ex and *.erl files of a project. It acts as a FS subscriber under supervision. NOTE: On Linux you need to install inotify-tools.

Current section

Files

Jump to
active_ex lib active_events.ex
Raw

lib/active_events.ex

defmodule ActiveEx.Events do
@moduledoc """
Documentation for ActiveEx.Events
"""
@doc """
Subcribe on :fs events.
ReCompiling all *.ex and *.erl files
"""
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
## Callbacks
@impl true
def init(stack) do
IO.puts("fs subscribe active_ex")
{:ok, _pid} = :fs.start_link(:active_ex)
:fs.subscribe(:active_ex)
{:ok, stack}
end
@impl true
def handle_call(event, _from, state) do
inspect(event, label: "active_ex: call event:")
{:reply, :ok, state}
end
@impl true
def handle_cast(event, state) do
inspect(event, label: "active_ex: cast event:")
# spawn (fn -> ActiveEx.start self, {} end)
{:noreply, state}
end
def handle_request(event, state) do
inspect(event, label: "active_ex: request event:")
{:noreply, state}
end
@impl true
def handle_info({_Pid, {:fs, :file_event}, {path, flags}}, state) do
# IO.inspect(state, label: "state before file_event #{path}")
stateAfter = reload(path, flags, state)
{:noreply, stateAfter}
end
def handle_info({_port, {:exit_status, _status}}, state) do
acc = Keyword.get(state, :acc, [])
messages = :erlang.iolist_to_binary(:lists.reverse(acc))
IO.puts(messages)
mod = Keyword.fetch!(state, :mod)
files = Path.wildcard(File.cwd!()<>"/deps/#{mod}/**/*.erl")
Enum.each(files, fn f ->
mod0 = String.to_atom(Path.basename(f, ".erl"))
IEx.Helpers.l(mod0)
end)
stateAfter = []
{:noreply, stateAfter}
end
def handle_info({_port, {:data, {type, line}}}, state) when type == :eol or type == :noeol do
# IO.inspect(state, label: "state before line")
acc = Keyword.get(state, :acc, [])
stateAfter = Keyword.put(state, :acc, [line|acc])
{:noreply, stateAfter}
end
def handle_info({_port, {:data, data}}, state) do
# IO.inspect(state, label: "state before :data")
acc = Keyword.get(state, :acc, [])
stateAfter = Keyword.put(state, :acc, [data|acc])
{:noreply, stateAfter}
end
def handle_info({_port, :eof}, state), do: {:noreply, state}
def handle_info(:run, state) do
# IO.inspect(state, label: "state before run")
mod = Keyword.fetch!(state, :mod)
case mod do
[] -> :skip
_ -> run(String.to_charlist("mix deps.compile #{mod} --force"))
end
{:noreply, state}
end
def handle_info(event, state) do
IO.inspect(event, label: "active_ex: unknown info")
{:noreply, state}
end
@impl true
def terminate(reason, state) do
IO.inspect(reason, label: "active_ex: terminating")
{reason, state}
end
def reload(path, _flags, state) do
dirs = Path.split(path)
except = [".elixir_ls", "build", "_build", "ebin", "test", ".git"]
is_filtered = Enum.any?(except, fn p ->
Enum.member?(dirs, p)
end)
stateAfter = case is_filtered do
true -> #IO.inspect(path, label: "active filtered")
state
false -> path_before = Keyword.get(state, :path, [])
ext = Path.extname(path)
stateAfter1 = case {path, ext} do
{^path_before, _} -> state
{_, ext} when ext == ".erl" or ext == ".hrl" ->
state0 = Keyword.put(state, :path, path)
mod = get_module_name(dirs)
stateAfter0 = case mod do
[] -> IO.puts("active_ex: Recompiling erlang modules (received: #{path})")
try do
compile_load(path, false)
rescue
_ -> inspect(__STACKTRACE__)
end
state0
_ -> IO.puts("active_ex: Recompiling deps/#{mod} erlang project (received: #{path})")
state1 = Keyword.put(state0, :mod, mod)
try do
handle_info(:run, state1)
rescue
_ -> inspect(__STACKTRACE__)
end
state1
end
stateAfter0
# IO.inspect(flags, label: "path flags")
{_, ".ex"} -> _state0 = Keyword.put(state, :path, path)
IO.puts("active_ex: Recompiling elixir module (received: #{path})")
try do
compile_load(path, true)
rescue
_ -> inspect(__STACKTRACE__)
end
[]
{_, ".exs"} -> IO.inspect(path, label: "active_ex: Changed elixir script (do nothing)")
state
{_, _} -> state
#IO.inspect(path, label: "active_ex trash files")
end
# IO.inspect(stateAfter1, label: "(RELOAD) stateAfter1")
stateAfter1
end
stateAfter
end
def compile_load(path, recompile) do
case IEx.Helpers.c(to_string(path), :in_memory) do
[] -> IO.puts("active_ex: Didn't compile #{path}")
:ignore
modules -> Enum.each(modules, fn(mod) -> case recompile do
true -> IEx.Helpers.r(mod)
false -> IEx.Helpers.l(mod)
end
end)
end
end
def get_module_name(dirs) do
name = Enum.reduce(dirs, false, fn x, acc -> case acc do
true -> x
false -> case x == "deps" do
true -> true
false -> false
end
_ -> acc
end
end)
case is_boolean(name) do
true -> []
false -> String.to_atom(name)
end
end
def run(args) do
:erlang.open_port({:spawn_executable, :os.find_executable('sh')},
[:stream, :in, :out, :eof, :use_stdio, :stderr_to_stdout, :binary, :exit_status,
{:args, ["-c", args]}, {:cd, :erlang.element(2, :file.get_cwd())}, {:env, []}])
end
end