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.
@@ -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
+
@@ -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
+
@@ -0,0 +1,11 @@
1
+
2
+ To use local wake executable for dev work:
3
+
4
+ $ ruby -rubygems -Ilib ./bin/wake -d specs.wk
5
+
6
+ To force a specific handler:
7
+
8
+ $ HANDLER=protable wake -d specs.wk
9
+ $ HANDLER=unix wake -d specs.wk
10
+
11
+ (see Wake.handler)
@@ -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