mynyml-watchr 0.3.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +46 -24
- data/Rakefile +10 -55
- data/TODO.txt +30 -26
- data/bin/watchr +9 -6
- data/docs.watchr +26 -0
- data/lib/watchr.rb +62 -123
- data/lib/watchr/controller.rb +79 -0
- data/lib/watchr/event_handlers/base.rb +48 -0
- data/lib/watchr/event_handlers/portable.rb +55 -0
- data/lib/watchr/event_handlers/unix.rb +62 -0
- data/lib/watchr/script.rb +192 -0
- data/lib/watchr/version.rb +4 -4
- data/specs.watchr +21 -12
- data/test/event_handlers/test_base.rb +24 -0
- data/test/event_handlers/test_portable.rb +58 -0
- data/test/event_handlers/test_unix.rb +56 -0
- data/test/test_controller.rb +104 -0
- data/test/test_helper.rb +20 -37
- data/test/test_script.rb +88 -0
- data/test/test_watchr.rb +32 -155
- data/watchr.gemspec +61 -70
- metadata +99 -29
- data/rdoc.watchr +0 -15
- data/yard.watchr +0 -18
data/specs.watchr
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
#
|
3
3
|
# $ watchr specs.watchr
|
4
4
|
|
5
|
+
# --------------------------------------------------
|
6
|
+
# Convenience Methods
|
7
|
+
# --------------------------------------------------
|
5
8
|
def all_test_files
|
6
9
|
Dir['test/**/test_*.rb'] - ['test/test_helper.rb']
|
7
10
|
end
|
@@ -12,24 +15,30 @@ def run(cmd)
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def run_all_tests
|
15
|
-
cmd = "ruby -rubygems -
|
18
|
+
cmd = "ruby -rubygems -Ilib -e'%w( #{all_test_files.join(' ')} ).each {|file| require file }'"
|
16
19
|
run(cmd)
|
17
20
|
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
# --------------------------------------------------
|
23
|
+
# Watchr Rules
|
24
|
+
# --------------------------------------------------
|
25
|
+
watch( '^test.*/test_.*\.rb' ) { |m| run( "ruby -rubygems %s" % m[0] ) }
|
26
|
+
watch( '^lib/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
|
27
|
+
watch( '^lib/watchr/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
|
28
|
+
watch( '^lib/watchr/event_handlers/(.*)\.rb' ) { |m| run( "ruby -rubygems test/event_handlers/test_%s.rb" % m[1] ) }
|
29
|
+
watch( '^test/test_helper\.rb' ) { run_all_tests }
|
30
|
+
|
31
|
+
# --------------------------------------------------
|
32
|
+
# Signal Handling
|
33
|
+
# --------------------------------------------------
|
34
|
+
# Ctrl-\
|
35
|
+
Signal.trap('QUIT') do
|
36
|
+
puts " --- Running all tests ---\n\n"
|
26
37
|
run_all_tests
|
27
38
|
end
|
28
39
|
|
29
|
-
# Ctrl
|
30
|
-
Signal.trap('
|
31
|
-
|
32
|
-
|
40
|
+
# Ctrl-C
|
41
|
+
Signal.trap('INT') { abort("\n") }
|
33
42
|
|
34
43
|
|
35
44
|
# vim:ft=ruby
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class BaseEventHandlerTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
class Handler
|
6
|
+
include Watchr::EventHandler::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@handler = Handler.new
|
11
|
+
end
|
12
|
+
|
13
|
+
test "api" do
|
14
|
+
@handler.should respond_to(:notify)
|
15
|
+
@handler.should respond_to(:listen)
|
16
|
+
@handler.should respond_to(:refresh)
|
17
|
+
@handler.class.ancestors.should include(Observable)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "notifies observers" do
|
21
|
+
@handler.expects(:notify_observers).with('foo/bar', nil)
|
22
|
+
@handler.notify('foo/bar', nil)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class UnixEventHandlerTest < Test::Unit::TestCase
|
4
|
+
include Watchr
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@handler = EventHandler::Portable.new
|
8
|
+
@handler.stubs(:loop)
|
9
|
+
|
10
|
+
@foo = Pathname('foo').expand_path
|
11
|
+
@bar = Pathname('bar').expand_path
|
12
|
+
@baz = Pathname('baz').expand_path
|
13
|
+
@bax = Pathname('bax').expand_path
|
14
|
+
|
15
|
+
@foo.stubs(:mtime).returns(Time.now - 100)
|
16
|
+
@bar.stubs(:mtime).returns(Time.now - 100)
|
17
|
+
@baz.stubs(:mtime).returns(Time.now - 100)
|
18
|
+
@bax.stubs(:mtime).returns(Time.now - 100)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "triggers listening state" do
|
22
|
+
@handler.expects(:loop)
|
23
|
+
@handler.listen([])
|
24
|
+
end
|
25
|
+
|
26
|
+
## monitoring file events
|
27
|
+
|
28
|
+
test "listens for events on monitored files" do
|
29
|
+
@handler.listen [ @foo, @bar ]
|
30
|
+
@handler.monitored_paths.should include(@foo)
|
31
|
+
@handler.monitored_paths.should include(@bar)
|
32
|
+
end
|
33
|
+
|
34
|
+
test "notifies observers on file event" do
|
35
|
+
@foo.stubs(:mtime).returns(Time.now + 100) # fake event
|
36
|
+
|
37
|
+
@handler.listen [ @foo, @bar ]
|
38
|
+
@handler.expects(:notify).with(@foo, :changed)
|
39
|
+
@handler.trigger
|
40
|
+
end
|
41
|
+
|
42
|
+
test "doesn't trigger on start" do
|
43
|
+
end
|
44
|
+
|
45
|
+
## on the fly updates of monitored files list
|
46
|
+
|
47
|
+
test "reattaches to new monitored files" do
|
48
|
+
@handler.listen [ @foo, @bar ]
|
49
|
+
@handler.monitored_paths.should include(@foo)
|
50
|
+
@handler.monitored_paths.should include(@bar)
|
51
|
+
|
52
|
+
@handler.refresh [ @baz, @bax ]
|
53
|
+
@handler.monitored_paths.should include(@baz)
|
54
|
+
@handler.monitored_paths.should include(@bax)
|
55
|
+
@handler.monitored_paths.should exclude(@foo)
|
56
|
+
@handler.monitored_paths.should exclude(@bar)
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class UnixEventHandlerTest < Test::Unit::TestCase
|
4
|
+
include Watchr
|
5
|
+
|
6
|
+
SingleFileWatcher = EventHandler::Unix::SingleFileWatcher
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@loop = Rev::Loop.default
|
10
|
+
@handler = EventHandler::Unix.new
|
11
|
+
@loop.stubs(:run)
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
SingleFileWatcher.handler = nil
|
16
|
+
Rev::Loop.default.watchers.every.detach
|
17
|
+
end
|
18
|
+
|
19
|
+
test "triggers listening state" do
|
20
|
+
@loop.expects(:run)
|
21
|
+
@handler.listen([])
|
22
|
+
end
|
23
|
+
|
24
|
+
## monitoring file events
|
25
|
+
|
26
|
+
test "listens for events on monitored files" do
|
27
|
+
@handler.listen %w( foo bar )
|
28
|
+
@loop.watchers.size.should be(2)
|
29
|
+
@loop.watchers.every.path.should include('foo', 'bar')
|
30
|
+
@loop.watchers.every.class.uniq.should be([SingleFileWatcher])
|
31
|
+
end
|
32
|
+
|
33
|
+
test "notifies observers on file event" do
|
34
|
+
watcher = SingleFileWatcher.new('foo/bar')
|
35
|
+
watcher.stubs(:path).returns('foo/bar')
|
36
|
+
|
37
|
+
@handler.expects(:notify).with('foo/bar', :changed)
|
38
|
+
watcher.on_change
|
39
|
+
end
|
40
|
+
|
41
|
+
## on the fly updates of monitored files list
|
42
|
+
|
43
|
+
test "reattaches to new monitored files" do
|
44
|
+
@handler.listen %w( foo bar )
|
45
|
+
@loop.watchers.size.should be(2)
|
46
|
+
@loop.watchers.every.path.should include('foo')
|
47
|
+
@loop.watchers.every.path.should include('bar')
|
48
|
+
|
49
|
+
@handler.refresh %w( baz bax )
|
50
|
+
@loop.watchers.size.should be(2)
|
51
|
+
@loop.watchers.every.path.should include('baz')
|
52
|
+
@loop.watchers.every.path.should include('bax')
|
53
|
+
@loop.watchers.every.path.should exclude('foo')
|
54
|
+
@loop.watchers.every.path.should exclude('bar')
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
require 'observer'
|
3
|
+
|
4
|
+
class MockHandler
|
5
|
+
include Observable
|
6
|
+
def listen(paths) end
|
7
|
+
def refresh(paths) end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestController < Test::Unit::TestCase
|
11
|
+
include Watchr
|
12
|
+
|
13
|
+
def to_p(str)
|
14
|
+
Pathname(str).expand_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@script = Script.new
|
19
|
+
@handler = MockHandler.new
|
20
|
+
@controller = Controller.new(@script, @handler)
|
21
|
+
end
|
22
|
+
|
23
|
+
test "triggers listening state on run" do
|
24
|
+
@controller.stubs(:monitored_paths).returns %w( foo bar )
|
25
|
+
@handler.expects(:listen).with %w( foo bar )
|
26
|
+
@controller.run
|
27
|
+
end
|
28
|
+
|
29
|
+
test "adds itself as handler observer" do
|
30
|
+
@handler.count_observers.should be(1)
|
31
|
+
@handler.delete_observer(@controller)
|
32
|
+
@handler.count_observers.should be(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
## monitored paths list
|
36
|
+
|
37
|
+
test "fetches monitored paths" do
|
38
|
+
Dir.expects(:[]).at_least_once.with('**/*').returns(%w(
|
39
|
+
a
|
40
|
+
b/x.z
|
41
|
+
b/c
|
42
|
+
b/c/y.z
|
43
|
+
))
|
44
|
+
script = Script.new
|
45
|
+
script.watch('.\.z') { :x }
|
46
|
+
|
47
|
+
contrl = Controller.new(script, MockHandler.new)
|
48
|
+
contrl.monitored_paths.should include(to_p('b/x.z'))
|
49
|
+
contrl.monitored_paths.should include(to_p('b/c/y.z'))
|
50
|
+
end
|
51
|
+
|
52
|
+
test "doesn't fetch unmonitored paths" do
|
53
|
+
Dir.expects(:[]).at_least_once.with('**/*').returns(%w(
|
54
|
+
a
|
55
|
+
b/x.z
|
56
|
+
b/c
|
57
|
+
b/c/y.z
|
58
|
+
))
|
59
|
+
script = Script.new
|
60
|
+
script.watch('.\.z') { :x }
|
61
|
+
|
62
|
+
contrl = Controller.new(script, MockHandler.new)
|
63
|
+
contrl.monitored_paths.should exclude(to_p('a'))
|
64
|
+
contrl.monitored_paths.should exclude(to_p('b/c'))
|
65
|
+
contrl.monitored_paths.should exclude(to_p('p/q.z'))
|
66
|
+
end
|
67
|
+
|
68
|
+
test "monitored paths include script" do
|
69
|
+
Dir.expects(:[]).at_least_once.with('**/*').returns(%w( a ))
|
70
|
+
Script.any_instance.stubs(:parse!)
|
71
|
+
|
72
|
+
path = to_p('some/file')
|
73
|
+
script = Script.new(path)
|
74
|
+
contrl = Controller.new(script, MockHandler.new)
|
75
|
+
contrl.monitored_paths.should include(path)
|
76
|
+
end
|
77
|
+
|
78
|
+
## on update
|
79
|
+
|
80
|
+
test "calls action for path" do
|
81
|
+
path = to_p('abc')
|
82
|
+
@script.expects(:action_for).with(path).returns(lambda {})
|
83
|
+
|
84
|
+
@controller.update('abc')
|
85
|
+
end
|
86
|
+
|
87
|
+
test "parses script on script file update" do
|
88
|
+
path = to_p('abc')
|
89
|
+
@script.stubs(:path).returns(path)
|
90
|
+
@script.expects(:parse!)
|
91
|
+
|
92
|
+
@controller.update('abc')
|
93
|
+
end
|
94
|
+
|
95
|
+
test "refreshes handler on script file update" do
|
96
|
+
path = to_p('abc')
|
97
|
+
@script.stubs(:path).returns(path)
|
98
|
+
@controller.stubs(:monitored_paths).returns %w( foo bar )
|
99
|
+
|
100
|
+
@handler.expects(:refresh).with %w( foo bar )
|
101
|
+
@controller.update('abc')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
data/test/test_helper.rb
CHANGED
@@ -6,13 +6,13 @@ require 'every'
|
|
6
6
|
require 'pending'
|
7
7
|
begin
|
8
8
|
require 'ruby-debug'
|
9
|
-
require 'phocus'
|
10
9
|
require 'redgreen'
|
10
|
+
require 'phocus'
|
11
11
|
rescue LoadError, RuntimeError
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
$:.unshift(
|
14
|
+
root = Pathname(__FILE__).dirname.parent.expand_path
|
15
|
+
$:.unshift(root.join('lib').to_s).uniq!
|
16
16
|
|
17
17
|
require 'watchr'
|
18
18
|
|
@@ -23,45 +23,28 @@ class Test::Unit::TestCase
|
|
23
23
|
define_method(name, &block)
|
24
24
|
end
|
25
25
|
alias :should :test
|
26
|
-
end
|
27
|
-
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
self.relative_path_from(ROOT).to_s
|
32
|
-
end
|
33
|
-
def pattern
|
34
|
-
Regexp.escape(self.rel)
|
35
|
-
end
|
36
|
-
def touch(time = Time.now)
|
37
|
-
`touch -mt #{time.strftime('%Y%m%d%H%M.%S')} #{self.expand_path.to_s}`
|
38
|
-
self
|
39
|
-
end
|
40
|
-
def mtime=(t)
|
41
|
-
self.touch(t).mtime
|
27
|
+
# noop
|
28
|
+
def xtest(*args) end
|
42
29
|
end
|
43
30
|
end
|
44
31
|
|
45
|
-
|
46
|
-
|
32
|
+
# taken from minitest/unit.rb
|
33
|
+
# (with modifications)
|
34
|
+
def capture_io
|
35
|
+
require 'stringio'
|
47
36
|
|
48
|
-
|
49
|
-
|
37
|
+
orig_stdout, orig_stderr = $stdout, $stderr
|
38
|
+
captured_stdout, captured_stderr = StringIO.new, StringIO.new
|
39
|
+
$stdout, $stderr = captured_stdout, captured_stderr
|
50
40
|
|
51
|
-
|
52
|
-
name ||= 'a.rb'
|
53
|
-
file = DIR.join(name)
|
54
|
-
self.files ||= []
|
55
|
-
self.files << file
|
56
|
-
file.open('w+') {|f| f << (content || "fixture\n") }
|
57
|
-
file
|
58
|
-
end
|
41
|
+
yield
|
59
42
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
43
|
+
return Struct.new(:stdout, :stderr).new(
|
44
|
+
captured_stdout.string,
|
45
|
+
captured_stderr.string
|
46
|
+
)
|
47
|
+
ensure
|
48
|
+
$stdout = orig_stdout
|
49
|
+
$stderr = orig_stderr
|
67
50
|
end
|
data/test/test_script.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class TestScript < Test::Unit::TestCase
|
4
|
+
include Watchr
|
5
|
+
|
6
|
+
## external api
|
7
|
+
|
8
|
+
test "watch" do
|
9
|
+
Script.new.watch('pattern')
|
10
|
+
Script.new.watch('pattern') { nil }
|
11
|
+
end
|
12
|
+
|
13
|
+
test "default action" do
|
14
|
+
Script.new.default_action { nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
## functionality
|
18
|
+
|
19
|
+
test "rule object" do
|
20
|
+
rule = Script.new.watch('pattern') { nil }
|
21
|
+
rule.pattern.should be('pattern')
|
22
|
+
rule.action.call.should be(nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "finds action for path" do
|
26
|
+
script = Script.new
|
27
|
+
script.watch('abc') { :x }
|
28
|
+
script.watch('def') { :y }
|
29
|
+
script.action_for('abc').call.should be(:x)
|
30
|
+
end
|
31
|
+
|
32
|
+
test "collects patterns" do
|
33
|
+
script = Script.new
|
34
|
+
script.watch('abc')
|
35
|
+
script.watch('def')
|
36
|
+
script.patterns.should include('abc')
|
37
|
+
script.patterns.should include('def')
|
38
|
+
end
|
39
|
+
|
40
|
+
test "parses script file" do
|
41
|
+
file = StringIO.new(<<-STR)
|
42
|
+
watch( 'abc' ) { :x }
|
43
|
+
STR
|
44
|
+
script = Script.new(file)
|
45
|
+
script.action_for('abc').call.should be(:x)
|
46
|
+
end
|
47
|
+
|
48
|
+
test "actions receive a MatchData object" do
|
49
|
+
script = Script.new
|
50
|
+
script.watch('de(.)') {|m| [m[0], m[1]] }
|
51
|
+
script.action_for('def').call.should be(%w( def f ))
|
52
|
+
end
|
53
|
+
|
54
|
+
test "rule's default action" do
|
55
|
+
script = Script.new
|
56
|
+
|
57
|
+
script.watch('abc')
|
58
|
+
script.action_for('abc').call.should be(nil)
|
59
|
+
script.default_action { :x }
|
60
|
+
|
61
|
+
script.watch('def')
|
62
|
+
script.action_for('def').call.should be(:x)
|
63
|
+
end
|
64
|
+
|
65
|
+
test "file path" do
|
66
|
+
Script.any_instance.stubs(:parse!)
|
67
|
+
path = Pathname('some/file').expand_path
|
68
|
+
script = Script.new(path)
|
69
|
+
script.path.should be(path)
|
70
|
+
end
|
71
|
+
|
72
|
+
test "later rules take precedence" do
|
73
|
+
script = Script.new
|
74
|
+
|
75
|
+
script.watch('a/(.*)\.x') { :x }
|
76
|
+
script.watch('a/b/(.*)\.x') { :y }
|
77
|
+
|
78
|
+
script.action_for('a/b/c.x').call.should be(:y)
|
79
|
+
end
|
80
|
+
|
81
|
+
test "rule patterns match against paths relative to pwd" do
|
82
|
+
script = Script.new
|
83
|
+
|
84
|
+
script.watch('^abc') { :x }
|
85
|
+
path = Pathname(Dir.pwd) + 'abc'
|
86
|
+
script.action_for(path).call.should be(:x)
|
87
|
+
end
|
88
|
+
end
|