Skip to content

Commit bd68155

Browse files
committed
Add Rotor.run and Rotor.run_async
1 parent 1be7be2 commit bd68155

File tree

6 files changed

+110
-19
lines changed

6 files changed

+110
-19
lines changed

lib/rotor.ex

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ defmodule Rotor do
1212
defdelegate watch(name, paths, rotor_fn), to: Rotor.GroupServer, as: :add
1313

1414

15+
defdelegate run(name), to: Rotor.GroupServer
16+
defdelegate run_async(name), to: Rotor.GroupServer
17+
18+
1519
defdelegate remove_group(name), to: Rotor.GroupServer, as: :remove
1620
defdelegate stop_watching(name), to: Rotor.GroupServer, as: :remove
17-
1821
end

lib/rotor/file_watcher.ex

+16-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ defmodule Rotor.FileWatcher do
22
import Rotor.Utils
33
use GenServer
44

5-
65
def start_link(args \\ []) do
76
GenServer.start_link(__MODULE__, args)
87
end
@@ -18,7 +17,21 @@ defmodule Rotor.FileWatcher do
1817
end
1918

2019

20+
# This is for synchronous poll
21+
def handle_call(:poll, _from, state) do
22+
new_state = poll(state)
23+
{:reply, :ok, new_state}
24+
end
25+
26+
27+
# This is for async poll
2128
def handle_info(:poll, state) do
29+
new_state = poll(state)
30+
{:noreply, new_state}
31+
end
32+
33+
34+
defp poll(state) do
2235
group_info = Rotor.group_info(state.name)
2336
{changed_files, file_index} = case get_in(state, [:file_index]) do
2437
nil ->
@@ -31,7 +44,7 @@ defmodule Rotor.FileWatcher do
3144

3245
state = run_rotor_function(state, changed_files, file_index)
3346
schedule_poll(group_info.options.manual, group_info.options.interval)
34-
{:noreply, state}
47+
state
3548
end
3649

3750

@@ -43,7 +56,7 @@ defmodule Rotor.FileWatcher do
4356
end
4457

4558

46-
defp run_rotor_function(state, [], file_index) do
59+
defp run_rotor_function(state, [], _file_index) do
4760
state
4861
end
4962

lib/rotor/file_watcher_pool.ex

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ defmodule Rotor.FileWatcherPool do
1414

1515
def add(group_name, is_manual) do
1616
watcher_info = %{name: group_name, manual: is_manual}
17+
18+
# Pass ID to make sure that the process is unique.
19+
# This avoids having to handle termination.
1720
child = worker(Rotor.FileWatcher, [watcher_info], id: unique_id(group_name))
1821
Supervisor.start_child(__MODULE__, child)
1922
end

lib/rotor/group_server.ex

+27-10
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,42 @@ defmodule Rotor.GroupServer do
2626
{group_info, updated_groups}
2727
end
2828

29-
start_file_watcher(name, group_info.options)
29+
pid = start_file_watcher(name, group_info.options)
30+
Agent.update __MODULE__, fn(groups)->
31+
put_in groups, [name, :file_watcher_pid], pid
32+
end
3033
:ok
3134
end
3235

3336

37+
def remove(name) do
38+
Agent.update __MODULE__, fn(groups)->
39+
Rotor.FileWatcherPool.remove(name)
40+
Map.delete groups, name
41+
end
42+
end
43+
44+
45+
def run(name) do
46+
group_info = Rotor.group_info name
47+
GenServer.call group_info.file_watcher_pid, :poll
48+
end
49+
50+
51+
def run_async(name) do
52+
group_info = Rotor.group_info name
53+
send group_info.file_watcher_pid, :poll
54+
end
55+
56+
3457
defp start_file_watcher(name, options) do
3558
case Rotor.FileWatcherPool.add(name, options.manual) do
3659
{:error, {:already_started, _pid}} ->
3760
Rotor.FileWatcherPool.remove(name)
3861
start_file_watcher(name, options)
39-
{:ok, pid} -> send(pid, :poll)
40-
end
41-
end
42-
43-
44-
def remove(name) do
45-
Agent.update __MODULE__, fn(groups)->
46-
Rotor.FileWatcherPool.remove(name)
47-
Map.delete groups, name
62+
{:ok, pid} ->
63+
send(pid, :poll)
64+
pid
4865
end
4966
end
5067

lib/rotor/utils.ex

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
defmodule Rotor.Utils do
22

3-
43
def update_file_index_timestamps(current_index) do
54
reducer = fn({path, file}, {changed_files, index})->
65
case File.stat(path) do
@@ -25,12 +24,13 @@ defmodule Rotor.Utils do
2524
end
2625

2726

28-
def build_file_index([], file_index) do
29-
file_index
27+
def build_file_index(paths) do
28+
build_file_index(paths, HashDict.new)
3029
end
3130

31+
def build_file_index([], file_index), do: file_index
3232

33-
def build_file_index([path | paths], file_index \\ HashDict.new) do
33+
def build_file_index([path | paths], file_index) do
3434
updated_file_index = Enum.reduce Path.wildcard(path), file_index, fn(file_path, index)->
3535
{:ok, file_info} = File.stat(file_path)
3636
if file_info.type == :directory || HashDict.has_key?(index, file_path) do

test/rotor_test.exs

+56-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ defmodule RotorTest do
2828

2929
test "should watch for changes and run pipeline functions" do
3030
output_path = "test/samples/outputs/app.js"
31+
File.rm output_path
3132
Rotor.watch :javascripts_pipeline_test, ["test/samples/*.js"], fn(_changed_files, all_files)->
32-
IO.inspect "Running callback for javascripts"
3333
read_files(all_files)
3434
|> concat
3535
|> output_to(output_path)
@@ -43,4 +43,59 @@ defmodule RotorTest do
4343
{:ok, contents} = File.read output_path
4444
assert Regex.match?(~r/x=1/, contents) && Regex.match?(~r/y=2/, contents)
4545
end
46+
47+
48+
test "should not watch for changes if group is set to manual" do
49+
group_name = :javascripts_pipeline_test
50+
output_path = "test/samples/outputs/app.js"
51+
File.rm output_path
52+
53+
Rotor.watch(group_name, ["test/samples/*.js"], fn(_changed_files, all_files)->
54+
read_files(all_files)
55+
|> concat
56+
|> output_to(output_path)
57+
end, %{manual: true})
58+
59+
# Touch the file
60+
:ok = :timer.sleep(1000)
61+
:ok = File.touch "test/samples/app1.js"
62+
:ok = :timer.sleep(2000)
63+
64+
# Should be an error
65+
assert File.read(output_path) == {:error, :enoent}
66+
67+
Rotor.run(group_name)
68+
Rotor.stop_watching(group_name)
69+
70+
{:ok, contents} = File.read output_path
71+
assert Regex.match?(~r/x=1/, contents) && Regex.match?(~r/y=2/, contents)
72+
end
73+
74+
75+
test "should not watch for changes if group is set to manual (async)" do
76+
group_name = :javascripts_pipeline_test
77+
output_path = "test/samples/outputs/app.js"
78+
File.rm output_path
79+
80+
Rotor.watch(group_name, ["test/samples/*.js"], fn(_changed_files, all_files)->
81+
read_files(all_files)
82+
|> concat
83+
|> output_to(output_path)
84+
end, %{manual: true})
85+
86+
# Touch the file
87+
:ok = :timer.sleep(1000)
88+
:ok = File.touch "test/samples/app1.js"
89+
:ok = :timer.sleep(2000)
90+
91+
# Should be an error
92+
assert File.read(output_path) == {:error, :enoent}
93+
94+
Rotor.run_async(group_name)
95+
:ok = :timer.sleep(1000)
96+
Rotor.stop_watching(group_name)
97+
98+
{:ok, contents} = File.read output_path
99+
assert Regex.match?(~r/x=1/, contents) && Regex.match?(~r/y=2/, contents)
100+
end
46101
end

0 commit comments

Comments
 (0)