Packages
quantum
1.0.4
3.5.3
3.5.2
retired
3.5.1
retired
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
retired
2.3.0
retired
2.2.7
2.2.6
2.2.5
retired
2.2.4
retired
2.2.3
retired
2.2.2
retired
2.2.1
retired
2.2.0
retired
2.1.3
retired
2.1.2
retired
2.1.1
retired
2.1.0
retired
2.1.0-beta.1
retired
2.0.4
retired
2.0.3
retired
2.0.2
retired
2.0.1
retired
2.0.0
retired
2.0.0-beta.2
retired
2.0.0-beta.1
retired
1.9.3
retired
1.9.2
retired
1.9.1
retired
1.9.0
retired
1.8.1
retired
1.8.0
retired
1.7.1
retired
1.7.0
retired
1.6.1
retired
1.6.0
retired
1.5.0
retired
1.4.0
retired
1.3.2
retired
1.3.1
retired
1.3.0
retired
1.2.4
retired
1.2.3
retired
1.2.2
retired
1.2.1
retired
1.2.0
retired
1.1.0
retired
1.0.4
retired
1.0.3
retired
1.0.2
retired
1.0.1
retired
1.0.0
retired
Cron-like job scheduler for Elixir.
Retired package: Security issue - v1 is no longer supported
Current section
Files
Jump to
Current section
Files
lib/quantum.ex
defmodule Quantum do
use GenServer
import Process, only: [send_after: 3]
@days ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
@months ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__] ++ options)
end
def init(_) do
send_after(self, :tick, 1000)
{_, {_, m, _}} = :calendar.now_to_universal_time(:os.timestamp)
{:ok, %{jobs: Application.get_env(:quantum, :cron, []), d: nil, h: nil, m: m, w: nil}}
end
def handle_info(:tick, state) do
send_after(self, :tick, 1000)
{d, {h, m, _}} = :calendar.now_to_universal_time(:os.timestamp)
if state.d != d do
state = %{state | w: rem(:calendar.day_of_the_week(d), 7)}
end
if state.m != m do
state = %{state | d: d, h: h, m: m}
Enum.each(state.jobs, fn({e, fun}) -> Task.start(__MODULE__, :execute, [e, fun, state]) end)
end
{:noreply, state}
end
def handle_info(_, state), do: {:noreply, state}
def execute(e, fun, state) when e |> is_atom do
execute(e |> Atom.to_string |> String.downcase |> translate, fun, state)
end
def execute("* * * * *", fun, _), do: fun.()
def execute("@hourly", fun, %{m: 0}), do: fun.()
def execute("0 * * * *", fun, %{m: 0}), do: fun.()
def execute("@daily", fun, %{m: 0, h: 0}), do: fun.()
def execute("0 0 * * *", fun, %{m: 0, h: 0}), do: fun.()
def execute("@weekly", fun, %{m: 0, h: 0, w: 0}), do: fun.()
def execute("0 0 * * 0", fun, %{m: 0, h: 0, w: 0}), do: fun.()
def execute("@monthly", fun, %{m: 0, h: 0, d: {_, _, 1}}), do: fun.()
def execute("0 0 1 * *", fun, %{m: 0, h: 0, d: {_, _, 1}}), do: fun.()
def execute("@yearly", fun, %{m: 0, h: 0, d: {_, 1, 1}}), do: fun.()
def execute("0 0 1 1 *", fun, %{m: 0, h: 0, d: {_, 1, 1}}), do: fun.()
def execute("@hourly", _, _), do: false
def execute("@daily", _, _), do: false
def execute("@weekly", _, _), do: false
def execute("@yearly", _, _), do: false
def execute("@monthly", _, _), do: false
def execute(e, fun, state) do
[m, h, d, n, w] = e |> String.split(" ")
{_, cur_mon, cur_day} = state.d
cond do
!match(m, state.m, 0, 59) -> false
!match(h, state.h, 0, 59) -> false
!match(d, cur_day, 1, 31) -> false
!match(n, cur_mon, 1, 12) -> false
!match(w, state.w, 0, 6) -> false
true -> fun.()
end
end
defp translate(e) do
{e,_} = List.foldl(@days, {e,0}, fn(x, acc) -> translate(acc, x) end)
{e,_} = List.foldl(@months, {e,1}, fn(x, acc) -> translate(acc, x) end)
e
end
defp translate({e, i}, term), do: {String.replace(e, term, "#{i}"), i+1}
defp match("*", _, _, _), do: true
defp match([], _, _, _), do: false
defp match([e|t], v, min, max), do: Enum.any?(parse(e, min, max), &(&1 == v)) or match(t, v, min, max)
defp match(e, v, min, max), do: match(e |> String.split(","), v, min, max)
def parse([], _, _), do: []
def parse(e, min, max) when e |> is_list do
[h|t] = e
(parse(h, min, max) ++ parse(t, min, max)) |> :lists.usort
end
def parse("*/" <> _ = e, min, max) do
[_,i] = e |> String.split("/")
{x,_} = i |> Integer.parse
Enum.reject(min..max, &(rem(&1, x) != 0))
end
def parse(e, min, max) do
[r|i] = e |> String.split("/")
[x|y] = r |> String.split("-")
{v,_} = x |> Integer.parse
parse(v, y, i, min, max) |> Enum.reject(&((&1 < min) or (&1 > max)))
end
def parse(v, [], [], _, _), do: [v]
def parse(v, [], [i], _, _) do
{x,_} = i |> Integer.parse
[rem(v,x)]
end
def parse(v, [y], [], min, max) do
{t,_} = y |> Integer.parse
if v < t do
Enum.to_list(v..t)
else
Enum.to_list(min..t) ++ Enum.to_list(v..max)
end
end
def parse(v, y, i, min, max) do
{x, _} = i |> Integer.parse
parse(v, y, [], min, max) |> Enum.reject(&(rem(&1, x) != 0))
end
end