wake 0.1.0
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/.gitignore +6 -0
- data/LICENSE +44 -0
- data/Manifest +30 -0
- data/README.rdoc +105 -0
- data/Rakefile +51 -0
- data/bin/wake +88 -0
- data/docs.wk +26 -0
- data/gem.wk +32 -0
- data/lib/wake.rb +109 -0
- data/lib/wake/controller.rb +112 -0
- data/lib/wake/event_handlers/base.rb +48 -0
- data/lib/wake/event_handlers/em.rb +232 -0
- data/lib/wake/event_handlers/portable.rb +60 -0
- data/lib/wake/event_handlers/rev.rb +104 -0
- data/lib/wake/event_handlers/unix.rb +25 -0
- data/lib/wake/script.rb +349 -0
- data/manifest.wk +70 -0
- data/specs.wk +38 -0
- data/test/README +11 -0
- data/test/event_handlers/test_base.rb +24 -0
- data/test/event_handlers/test_em.rb +162 -0
- data/test/event_handlers/test_portable.rb +142 -0
- data/test/event_handlers/test_rev.rb +162 -0
- data/test/test_controller.rb +130 -0
- data/test/test_helper.rb +60 -0
- data/test/test_script.rb +124 -0
- data/test/test_wake.rb +60 -0
- data/wake.gemspec +61 -0
- metadata +139 -0
data/manifest.wk
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ wake -f manifest.wk
|
4
|
+
#
|
5
|
+
# This script will remove a file from from the Manifest when it gets deleted,
|
6
|
+
# and will rebuild the Manifest on Ctrl-\
|
7
|
+
#
|
8
|
+
# Mostly serves as a demo for the :delete event type (and eventually for the
|
9
|
+
# :added event type). In reality this is much better implemented as a git
|
10
|
+
# post-commit script.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'pathname'
|
14
|
+
# --------------------------------------------------
|
15
|
+
# Helpers
|
16
|
+
# --------------------------------------------------
|
17
|
+
module Project
|
18
|
+
extend self
|
19
|
+
def files
|
20
|
+
`git ls-files --full-name`.strip.split($/).sort
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Manifest
|
25
|
+
attr_accessor :path
|
26
|
+
|
27
|
+
def initialize(path)
|
28
|
+
@path = Pathname(path).expand_path
|
29
|
+
create!
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove(path)
|
33
|
+
paths = @path.read.strip.split($/)
|
34
|
+
@path.open('w') {|f| f << (paths - [path]).join("\n") }
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(path)
|
38
|
+
paths = @path.read.strip.split($/)
|
39
|
+
@path.open('w') {|f| f << paths.push(path).sort.join("\n") }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def create!
|
44
|
+
File.open(@path.to_s, 'w') {} unless @path.exist?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
@manifest = Manifest.new('Manifest')
|
50
|
+
|
51
|
+
# --------------------------------------------------
|
52
|
+
# Wake Rules
|
53
|
+
# --------------------------------------------------
|
54
|
+
watch('.*', :deleted ) do |md|
|
55
|
+
@manifest.remove(md[0])
|
56
|
+
puts "removed #{md[0].inspect} from Manifest"
|
57
|
+
end
|
58
|
+
|
59
|
+
# --------------------------------------------------
|
60
|
+
# Signal Handling
|
61
|
+
# --------------------------------------------------
|
62
|
+
# Ctrl-\
|
63
|
+
Signal.trap('QUIT') do
|
64
|
+
puts " --- Updated Manifest ---\n"
|
65
|
+
@manifest.path.open('w') {|m| m << Project.files.join("\n").strip }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ctrl-C
|
69
|
+
Signal.trap('INT') { abort("\n") }
|
70
|
+
|
data/specs.wk
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ wake -f specs.wk
|
4
|
+
|
5
|
+
# --------------------------------------------------
|
6
|
+
# Convenience Methods
|
7
|
+
# --------------------------------------------------
|
8
|
+
def run(cmd)
|
9
|
+
puts(cmd)
|
10
|
+
system(cmd)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_all_tests
|
14
|
+
# see Rakefile for the definition of the test:all task
|
15
|
+
system( "rake -s test:all VERBOSE=true" )
|
16
|
+
end
|
17
|
+
|
18
|
+
# --------------------------------------------------
|
19
|
+
# Wake Rules
|
20
|
+
# --------------------------------------------------
|
21
|
+
watch( '^test.*/test_.*\.rb' ) { |m| run( "ruby -rubygems %s" % m[0] ) }
|
22
|
+
watch( '^lib/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
|
23
|
+
watch( '^lib/wake/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
|
24
|
+
watch( '^lib/wake/event_handlers/(.*)\.rb' ) { |m| run( "ruby -rubygems test/event_handlers/test_%s.rb" % m[1] ) }
|
25
|
+
watch( '^test/test_helper\.rb' ) { run_all_tests }
|
26
|
+
|
27
|
+
# --------------------------------------------------
|
28
|
+
# Signal Handling
|
29
|
+
# --------------------------------------------------
|
30
|
+
# Ctrl-\
|
31
|
+
Signal.trap('QUIT') do
|
32
|
+
puts " --- Running all tests ---\n\n"
|
33
|
+
run_all_tests
|
34
|
+
end
|
35
|
+
|
36
|
+
# Ctrl-C
|
37
|
+
Signal.trap('INT') { abort("\n") }
|
38
|
+
|
data/test/README
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class BaseEventHandlerTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
class Handler
|
6
|
+
include Wake::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,162 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
if false && HAVE_EM
|
4
|
+
|
5
|
+
class Wake::EventHandler::Unix::SingleFileWatcher
|
6
|
+
public :type
|
7
|
+
end
|
8
|
+
|
9
|
+
class UnixEventHandlerTest < Test::Unit::TestCase
|
10
|
+
include Wake
|
11
|
+
|
12
|
+
SingleFileWatcher = EventHandler::Unix::SingleFileWatcher
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@now = Time.now
|
16
|
+
pathname = Pathname.new('foo/bar')
|
17
|
+
pathname.stubs(:atime ).returns(@now)
|
18
|
+
pathname.stubs(:mtime ).returns(@now)
|
19
|
+
pathname.stubs(:ctime ).returns(@now)
|
20
|
+
pathname.stubs(:exist?).returns(true)
|
21
|
+
SingleFileWatcher.any_instance.stubs(:pathname).returns(pathname)
|
22
|
+
|
23
|
+
@loop = Rev::Loop.default
|
24
|
+
@handler = EventHandler::Unix.new
|
25
|
+
@watcher = SingleFileWatcher.new('foo/bar')
|
26
|
+
@loop.stubs(:run)
|
27
|
+
end
|
28
|
+
|
29
|
+
def teardown
|
30
|
+
SingleFileWatcher.handler = nil
|
31
|
+
Rev::Loop.default.watchers.every.detach
|
32
|
+
end
|
33
|
+
|
34
|
+
test "triggers listening state" do
|
35
|
+
@loop.expects(:run)
|
36
|
+
@handler.listen([])
|
37
|
+
end
|
38
|
+
|
39
|
+
## SingleFileWatcher
|
40
|
+
|
41
|
+
test "watcher pathname" do
|
42
|
+
@watcher.pathname.should be_kind_of(Pathname)
|
43
|
+
@watcher.pathname.to_s.should be(@watcher.path)
|
44
|
+
end
|
45
|
+
|
46
|
+
test "stores reference times" do
|
47
|
+
@watcher.pathname.stubs(:atime).returns(:time)
|
48
|
+
@watcher.pathname.stubs(:mtime).returns(:time)
|
49
|
+
@watcher.pathname.stubs(:ctime).returns(:time)
|
50
|
+
|
51
|
+
@watcher.send(:update_reference_times)
|
52
|
+
@watcher.instance_variable_get(:@reference_atime).should be(:time)
|
53
|
+
@watcher.instance_variable_get(:@reference_mtime).should be(:time)
|
54
|
+
@watcher.instance_variable_get(:@reference_ctime).should be(:time)
|
55
|
+
end
|
56
|
+
|
57
|
+
test "stores initial reference times" do
|
58
|
+
SingleFileWatcher.any_instance.expects(:update_reference_times)
|
59
|
+
SingleFileWatcher.new('foo')
|
60
|
+
end
|
61
|
+
|
62
|
+
test "updates reference times on change" do
|
63
|
+
@watcher.expects(:update_reference_times)
|
64
|
+
@watcher.on_change
|
65
|
+
end
|
66
|
+
|
67
|
+
test "detects event type" do
|
68
|
+
trigger_event @watcher, @now, :atime
|
69
|
+
@watcher.type.should be(:accessed)
|
70
|
+
|
71
|
+
trigger_event @watcher, @now, :mtime
|
72
|
+
@watcher.type.should be(:modified)
|
73
|
+
|
74
|
+
trigger_event @watcher, @now, :ctime
|
75
|
+
@watcher.type.should be(:changed)
|
76
|
+
|
77
|
+
trigger_event @watcher, @now, :atime, :mtime
|
78
|
+
@watcher.type.should be(:modified)
|
79
|
+
|
80
|
+
trigger_event @watcher, @now, :mtime, :ctime
|
81
|
+
@watcher.type.should be(:modified)
|
82
|
+
|
83
|
+
trigger_event @watcher, @now, :atime, :ctime
|
84
|
+
@watcher.type.should be(:accessed)
|
85
|
+
|
86
|
+
trigger_event @watcher, @now, :atime, :mtime, :ctime
|
87
|
+
@watcher.type.should be(:modified)
|
88
|
+
|
89
|
+
@watcher.pathname.stubs(:exist?).returns(false)
|
90
|
+
@watcher.type.should be(:deleted)
|
91
|
+
end
|
92
|
+
|
93
|
+
## monitoring file events
|
94
|
+
|
95
|
+
test "listens for events on monitored files" do
|
96
|
+
@handler.listen %w( foo bar )
|
97
|
+
@loop.watchers.size.should be(2)
|
98
|
+
@loop.watchers.every.path.should include('foo', 'bar')
|
99
|
+
@loop.watchers.every.class.uniq.should be([SingleFileWatcher])
|
100
|
+
end
|
101
|
+
|
102
|
+
test "notifies observers on file event" do
|
103
|
+
@watcher.stubs(:path).returns('foo')
|
104
|
+
@handler.expects(:notify).with('foo', anything)
|
105
|
+
@watcher.on_change
|
106
|
+
end
|
107
|
+
|
108
|
+
test "notifies observers of event type" do
|
109
|
+
trigger_event @watcher, @now, :atime
|
110
|
+
@handler.expects(:notify).with('foo/bar', :accessed)
|
111
|
+
@watcher.on_change
|
112
|
+
|
113
|
+
trigger_event @watcher, @now, :mtime
|
114
|
+
@handler.expects(:notify).with('foo/bar', :modified)
|
115
|
+
@watcher.on_change
|
116
|
+
|
117
|
+
trigger_event @watcher, @now, :ctime
|
118
|
+
@handler.expects(:notify).with('foo/bar', :changed)
|
119
|
+
@watcher.on_change
|
120
|
+
|
121
|
+
trigger_event @watcher, @now, :atime, :mtime, :ctime
|
122
|
+
@handler.expects(:notify).with('foo/bar', :modified)
|
123
|
+
@watcher.on_change
|
124
|
+
|
125
|
+
@watcher.pathname.stubs(:exist?).returns(false)
|
126
|
+
@handler.expects(:notify).with('foo/bar', :deleted)
|
127
|
+
@watcher.on_change
|
128
|
+
end
|
129
|
+
|
130
|
+
## on the fly updates of monitored files list
|
131
|
+
|
132
|
+
test "reattaches to new monitored files" do
|
133
|
+
@handler.listen %w( foo bar )
|
134
|
+
@loop.watchers.size.should be(2)
|
135
|
+
@loop.watchers.every.path.should include('foo')
|
136
|
+
@loop.watchers.every.path.should include('bar')
|
137
|
+
|
138
|
+
@handler.refresh %w( baz bax )
|
139
|
+
@loop.watchers.size.should be(2)
|
140
|
+
@loop.watchers.every.path.should include('baz')
|
141
|
+
@loop.watchers.every.path.should include('bax')
|
142
|
+
@loop.watchers.every.path.should exclude('foo')
|
143
|
+
@loop.watchers.every.path.should exclude('bar')
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def trigger_event(watcher, now, *types)
|
149
|
+
watcher.pathname.stubs(:atime).returns(now)
|
150
|
+
watcher.pathname.stubs(:mtime).returns(now)
|
151
|
+
watcher.pathname.stubs(:ctime).returns(now)
|
152
|
+
watcher.instance_variable_set(:@reference_atime, now)
|
153
|
+
watcher.instance_variable_set(:@reference_mtime, now)
|
154
|
+
watcher.instance_variable_set(:@reference_ctime, now)
|
155
|
+
|
156
|
+
types.each do |type|
|
157
|
+
watcher.pathname.stubs(type).returns(now+10)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end # if Wake::HAVE_EM
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class Wake::EventHandler::Portable
|
4
|
+
attr_accessor :monitored_paths
|
5
|
+
end
|
6
|
+
|
7
|
+
class PortableEventHandlerTest < Test::Unit::TestCase
|
8
|
+
include Wake
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@handler = EventHandler::Portable.new
|
12
|
+
@handler.stubs(:loop)
|
13
|
+
|
14
|
+
@foo = Pathname('foo').expand_path
|
15
|
+
@bar = Pathname('bar').expand_path
|
16
|
+
@baz = Pathname('baz').expand_path
|
17
|
+
@bax = Pathname('bax').expand_path
|
18
|
+
|
19
|
+
@now = Time.now
|
20
|
+
[@foo, @bar, @baz, @bax].each do |path|
|
21
|
+
path.stubs(:mtime ).returns(@now - 100)
|
22
|
+
path.stubs(:atime ).returns(@now - 100)
|
23
|
+
path.stubs(:ctime ).returns(@now - 100)
|
24
|
+
path.stubs(:exist?).returns(true)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
test "triggers listening state" do
|
29
|
+
@handler.expects(:loop)
|
30
|
+
@handler.listen([])
|
31
|
+
end
|
32
|
+
|
33
|
+
## monitoring file events
|
34
|
+
|
35
|
+
test "listens for events on monitored files" do
|
36
|
+
@handler.listen [ @foo, @bar ]
|
37
|
+
@handler.monitored_paths.should include(@foo)
|
38
|
+
@handler.monitored_paths.should include(@bar)
|
39
|
+
end
|
40
|
+
|
41
|
+
test "doesn't trigger on start" do
|
42
|
+
end
|
43
|
+
|
44
|
+
## event types
|
45
|
+
|
46
|
+
test "deleted file event" do
|
47
|
+
@foo.stubs(:exist?).returns(false)
|
48
|
+
|
49
|
+
@handler.listen [ @foo, @bar ]
|
50
|
+
@handler.expects(:notify).with(@foo, :deleted)
|
51
|
+
@handler.trigger
|
52
|
+
end
|
53
|
+
|
54
|
+
test "modified file event" do
|
55
|
+
@foo.stubs(:mtime).returns(@now + 100)
|
56
|
+
|
57
|
+
@handler.listen [ @foo, @bar ]
|
58
|
+
@handler.expects(:notify).with(@foo, :modified)
|
59
|
+
@handler.trigger
|
60
|
+
end
|
61
|
+
|
62
|
+
test "accessed file event" do
|
63
|
+
@foo.stubs(:atime).returns(@now + 100)
|
64
|
+
|
65
|
+
@handler.listen [ @foo, @bar ]
|
66
|
+
@handler.expects(:notify).with(@foo, :accessed)
|
67
|
+
@handler.trigger
|
68
|
+
end
|
69
|
+
|
70
|
+
test "changed file event" do
|
71
|
+
@foo.stubs(:ctime).returns(@now + 100)
|
72
|
+
|
73
|
+
@handler.listen [ @foo, @bar ]
|
74
|
+
@handler.expects(:notify).with(@foo, :changed)
|
75
|
+
@handler.trigger
|
76
|
+
end
|
77
|
+
|
78
|
+
## event type priorities
|
79
|
+
|
80
|
+
test "mtime > atime" do
|
81
|
+
@foo.stubs(:mtime).returns(@now + 100)
|
82
|
+
@foo.stubs(:atime).returns(@now + 100)
|
83
|
+
@foo.stubs(:ctime).returns(@now + 100)
|
84
|
+
|
85
|
+
@handler.listen [ @foo, @bar ]
|
86
|
+
@handler.expects(:notify).with(@foo, :modified)
|
87
|
+
@handler.trigger
|
88
|
+
end
|
89
|
+
|
90
|
+
test "mtime > ctime" do
|
91
|
+
@foo.stubs(:mtime).returns(@now + 100)
|
92
|
+
@foo.stubs(:ctime).returns(@now + 100)
|
93
|
+
|
94
|
+
@handler.listen [ @foo, @bar ]
|
95
|
+
@handler.expects(:notify).with(@foo, :modified)
|
96
|
+
@handler.trigger
|
97
|
+
end
|
98
|
+
|
99
|
+
test "atime > ctime" do
|
100
|
+
@foo.stubs(:atime).returns(@now + 100)
|
101
|
+
@foo.stubs(:ctime).returns(@now + 100)
|
102
|
+
|
103
|
+
@handler.listen [ @foo, @bar ]
|
104
|
+
@handler.expects(:notify).with(@foo, :accessed)
|
105
|
+
@handler.trigger
|
106
|
+
end
|
107
|
+
|
108
|
+
test "deleted > mtime" do
|
109
|
+
@foo.stubs(:exist?).returns(false)
|
110
|
+
@foo.stubs(:mtime ).returns(@now + 100)
|
111
|
+
|
112
|
+
@handler.listen [ @foo, @bar ]
|
113
|
+
@handler.expects(:notify).with(@foo, :deleted)
|
114
|
+
@handler.trigger
|
115
|
+
end
|
116
|
+
|
117
|
+
## on the fly updates of monitored files list
|
118
|
+
|
119
|
+
test "reattaches to new monitored files" do
|
120
|
+
@handler.listen [ @foo, @bar ]
|
121
|
+
@handler.monitored_paths.should include(@foo)
|
122
|
+
@handler.monitored_paths.should include(@bar)
|
123
|
+
|
124
|
+
@handler.refresh [ @baz, @bax ]
|
125
|
+
@handler.monitored_paths.should include(@baz)
|
126
|
+
@handler.monitored_paths.should include(@bax)
|
127
|
+
@handler.monitored_paths.should exclude(@foo)
|
128
|
+
@handler.monitored_paths.should exclude(@bar)
|
129
|
+
end
|
130
|
+
|
131
|
+
test "retries on ENOENT errors" do
|
132
|
+
@oops = Pathname('oops').expand_path
|
133
|
+
@oops.stubs(:exist?).returns(true)
|
134
|
+
@oops.stubs(:mtime).raises(Errno::ENOENT).
|
135
|
+
then.returns(Time.now + 100)
|
136
|
+
|
137
|
+
@handler.listen [ @oops ]
|
138
|
+
|
139
|
+
@handler.expects(:notify).with(@oops, :modified)
|
140
|
+
@handler.trigger
|
141
|
+
end
|
142
|
+
end
|