nestor 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.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +34 -0
  5. data/Rakefile +76 -0
  6. data/VERSION +1 -0
  7. data/bin/nestor +3 -0
  8. data/doc/.gitignore +3 -0
  9. data/doc/state-diagram.graffle +3870 -0
  10. data/doc/state-diagram.png +0 -0
  11. data/lib/nestor/cli.rb +52 -0
  12. data/lib/nestor/machine.rb +161 -0
  13. data/lib/nestor/strategies/test/unit.rb +116 -0
  14. data/lib/nestor/strategies.rb +18 -0
  15. data/lib/nestor/watchers/rails.rb +56 -0
  16. data/lib/nestor/watchers/rails_script.rb +83 -0
  17. data/lib/nestor/watchers.rb +1 -0
  18. data/lib/nestor.rb +11 -0
  19. data/spec/machine_spec.rb +56 -0
  20. data/spec/spec_helper.rb +9 -0
  21. data/vendor/watchr-0.5.7/.gitignore +5 -0
  22. data/vendor/watchr-0.5.7/History.txt +32 -0
  23. data/vendor/watchr-0.5.7/LICENSE +19 -0
  24. data/vendor/watchr-0.5.7/Manifest +27 -0
  25. data/vendor/watchr-0.5.7/README.rdoc +108 -0
  26. data/vendor/watchr-0.5.7/Rakefile +49 -0
  27. data/vendor/watchr-0.5.7/TODO.txt +40 -0
  28. data/vendor/watchr-0.5.7/bin/watchr +77 -0
  29. data/vendor/watchr-0.5.7/docs.watchr +26 -0
  30. data/vendor/watchr-0.5.7/gem.watchr +32 -0
  31. data/vendor/watchr-0.5.7/lib/watchr/controller.rb +81 -0
  32. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/base.rb +48 -0
  33. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/portable.rb +55 -0
  34. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/unix.rb +97 -0
  35. data/vendor/watchr-0.5.7/lib/watchr/script.rb +203 -0
  36. data/vendor/watchr-0.5.7/lib/watchr.rb +113 -0
  37. data/vendor/watchr-0.5.7/manifest.watchr +70 -0
  38. data/vendor/watchr-0.5.7/specs.watchr +38 -0
  39. data/vendor/watchr-0.5.7/test/README +11 -0
  40. data/vendor/watchr-0.5.7/test/event_handlers/test_base.rb +24 -0
  41. data/vendor/watchr-0.5.7/test/event_handlers/test_portable.rb +58 -0
  42. data/vendor/watchr-0.5.7/test/event_handlers/test_unix.rb +162 -0
  43. data/vendor/watchr-0.5.7/test/test_controller.rb +103 -0
  44. data/vendor/watchr-0.5.7/test/test_helper.rb +52 -0
  45. data/vendor/watchr-0.5.7/test/test_script.rb +123 -0
  46. data/vendor/watchr-0.5.7/test/test_watchr.rb +60 -0
  47. data/vendor/watchr-0.5.7/watchr.gemspec +60 -0
  48. metadata +152 -0
@@ -0,0 +1,203 @@
1
+ module Watchr
2
+
3
+ # A script object wraps a script file, and is used by a controller.
4
+ #
5
+ # ===== Examples
6
+ #
7
+ # path = Pathname.new('specs.watchr')
8
+ # script = Watchr::Script.new(path)
9
+ #
10
+ class Script
11
+ DEFAULT_EVENT_TYPE = :modified
12
+
13
+ # Convenience type. Provides clearer and simpler access to rule properties.
14
+ #
15
+ # ===== Examples
16
+ #
17
+ # rule = script.watch('lib/.*\.rb') { 'ohaie' }
18
+ # rule.pattern #=> 'lib/.*\.rb'
19
+ # rule.action.call #=> 'ohaie'
20
+ #
21
+ Rule = Struct.new(:pattern, :event_type, :action)
22
+
23
+ # TODO eval context
24
+ class API #:nodoc:
25
+ end
26
+
27
+ # Creates a script object for <tt>path</tt>.
28
+ #
29
+ # Does not parse the script. The controller knows when to parse the script.
30
+ #
31
+ # ===== Parameters
32
+ # path<Pathname>:: the path to the script
33
+ #
34
+ def initialize(path)
35
+ @path = path
36
+ @rules = []
37
+ @default_action = lambda {}
38
+ end
39
+
40
+ # Main script API method. Builds a new rule, binding a pattern to an action.
41
+ #
42
+ # Whenever a file is saved that matches a rule's <tt>pattern</tt>, its
43
+ # corresponding <tt>action</tt> is triggered.
44
+ #
45
+ # Patterns can be either a Regexp or a string. Because they always
46
+ # represent paths however, it's simpler to use strings. But remember to use
47
+ # single quotes (not double quotes), otherwise escape sequences will be
48
+ # parsed (for example "foo/bar\.rb" #=> "foo/bar.rb", notice "\." becomes
49
+ # "."), and won't be interpreted as the regexp you expect.
50
+ #
51
+ # Also note that patterns will be matched against relative paths (relative
52
+ # from current working directory).
53
+ #
54
+ # Actions, the blocks passed to <tt>watch</tt>, receive a MatchData object
55
+ # as argument. It will be populated with the whole matched string (md[0])
56
+ # as well as individual backreferences (md[1..n]). See MatchData#[]
57
+ # documentation for more details.
58
+ #
59
+ # ===== Examples
60
+ #
61
+ # # in script file
62
+ # watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
63
+ # watch( 'lib/(.*)\.rb' ) {|md| system("ruby test/test_#{md[1]}.rb") }
64
+ #
65
+ # With these two rules, watchr will run any test file whenever it is itself
66
+ # changed (first rule), and will also run a corresponding test file
67
+ # whenever a lib file is changed (second rule).
68
+ #
69
+ # ===== Parameters
70
+ # pattern<~#match>:: pattern to match targetted paths
71
+ # event_type<Symbol>::
72
+ # Rule will only match events of this type. Accepted types are :accessed,
73
+ # :modified, :changed, :delete and nil (any), where the first three
74
+ # correspond to atime, mtime and ctime respectively. Defaults to
75
+ # :modified.
76
+ # action<Block>:: action to trigger
77
+ #
78
+ # ===== Returns
79
+ # rule<Rule>:: rule created by the method
80
+ #
81
+ def watch(pattern, event_type = DEFAULT_EVENT_TYPE, &action)
82
+ @rules << Rule.new(pattern, event_type, action || @default_action)
83
+ @rules.last
84
+ end
85
+
86
+ # Convenience method. Define a default action to be triggered when a rule
87
+ # has none specified.
88
+ #
89
+ # ===== Examples
90
+ #
91
+ # # in script file
92
+ #
93
+ # default_action { system('rake --silent rdoc') }
94
+ #
95
+ # watch( 'lib/.*\.rb' )
96
+ # watch( 'README.rdoc' )
97
+ # watch( 'TODO.txt' )
98
+ # watch( 'LICENSE' )
99
+ #
100
+ # # equivalent to:
101
+ #
102
+ # watch( 'lib/.*\.rb' ) { system('rake --silent rdoc') }
103
+ # watch( 'README.rdoc' ) { system('rake --silent rdoc') }
104
+ # watch( 'TODO.txt' ) { system('rake --silent rdoc') }
105
+ # watch( 'LICENSE' ) { system('rake --silent rdoc') }
106
+ #
107
+ def default_action(&action)
108
+ @default_action = action
109
+ end
110
+
111
+ # Eval content of script file.
112
+ #--
113
+ # TODO fix script file not found error
114
+ def parse!
115
+ Watchr.debug('loading script file %s' % @path.to_s.inspect)
116
+
117
+ reset
118
+ instance_eval(@path.read)
119
+
120
+ rescue Errno::ENOENT
121
+ # TODO figure out why this is happening. still can't reproduce
122
+ Watchr.debug('script file "not found". wth')
123
+ sleep(0.3) #enough?
124
+ instance_eval(@path.read)
125
+ end
126
+
127
+ # Find an action corresponding to a path and event type. The returned
128
+ # action is actually a wrapper around the rule's action, with the
129
+ # match_data prepopulated.
130
+ #
131
+ # ===== Params
132
+ # path<Pathnane,String>:: Find action that correspond to this path.
133
+ # event_type<Symbol>:: Find action only if rule's event if of this type.
134
+ #
135
+ # ===== Examples
136
+ #
137
+ # script.watch( 'test/test_.*\.rb' ) {|md| "ruby #{md[0]}" }
138
+ # script.action_for('test/test_watchr.rb').call #=> "ruby test/test_watchr.rb"
139
+ #
140
+ def action_for(path, event_type = DEFAULT_EVENT_TYPE)
141
+ path = rel_path(path).to_s
142
+ rule = rules_for(path).detect {|rule| rule.event_type.nil? || rule.event_type == event_type }
143
+ if rule
144
+ data = path.match(rule.pattern)
145
+ lambda { rule.action.call(data) }
146
+ else
147
+ lambda {}
148
+ end
149
+ end
150
+
151
+ # Collection of all patterns defined in script.
152
+ #
153
+ # ===== Returns
154
+ # patterns<String, Regexp>:: all patterns
155
+ #
156
+ def patterns
157
+ #@rules.every.pattern
158
+ @rules.map {|r| r.pattern }
159
+ end
160
+
161
+ # Path to the script file
162
+ #
163
+ # ===== Returns
164
+ # path<Pathname>:: path to script file
165
+ #
166
+ def path
167
+ Pathname(@path.respond_to?(:to_path) ? @path.to_path : @path.to_s).expand_path
168
+ end
169
+
170
+ private
171
+
172
+ # Rules corresponding to a given path, in reversed order of precedence
173
+ # (latest one is most inportant).
174
+ #
175
+ # ===== Parameters
176
+ # path<Pathname, String>:: path to look up rule for
177
+ #
178
+ # ===== Returns
179
+ # rules<Array(Rule)>:: rules corresponding to <tt>path</tt>
180
+ #
181
+ def rules_for(path)
182
+ @rules.reverse.select {|rule| path.match(rule.pattern) }
183
+ end
184
+
185
+ # Make a path relative to current working directory.
186
+ #
187
+ # ===== Parameters
188
+ # path<Pathname, String>:: absolute or relative path
189
+ #
190
+ # ===== Returns
191
+ # path<Pathname>:: relative path, from current working directory.
192
+ #
193
+ def rel_path(path)
194
+ Pathname(path).expand_path.relative_path_from(Pathname(Dir.pwd))
195
+ end
196
+
197
+ # Reset script state
198
+ def reset
199
+ @default_action = lambda {}
200
+ @rules.clear
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,113 @@
1
+ require 'pathname'
2
+ require 'rbconfig'
3
+
4
+ # Agile development tool that monitors a directory recursively, and triggers a
5
+ # user defined action whenever an observed file is modified. Its most typical
6
+ # use is continuous testing.
7
+ #
8
+ # Usage:
9
+ #
10
+ # # on command line, from project's root dir
11
+ # $ watchr path/to/script
12
+ #
13
+ # See README for more details
14
+ #
15
+ module Watchr
16
+ VERSION = '0.5.7'
17
+
18
+ begin
19
+ require 'rev'
20
+ HAVE_REV = true
21
+ rescue LoadError, RuntimeError
22
+ HAVE_REV = false
23
+ end
24
+
25
+ autoload :Script, 'watchr/script'
26
+ autoload :Controller, 'watchr/controller'
27
+
28
+ module EventHandler
29
+ autoload :Base, 'watchr/event_handlers/base'
30
+ autoload :Unix, 'watchr/event_handlers/unix' if ::Watchr::HAVE_REV
31
+ autoload :Portable, 'watchr/event_handlers/portable'
32
+ end
33
+
34
+ class << self
35
+ attr_accessor :options
36
+ attr_accessor :handler
37
+
38
+ # backwards compatibility
39
+ def version #:nodoc:
40
+ Watchr::VERSION
41
+ end
42
+
43
+ # Options proxy.
44
+ #
45
+ # Currently supported options:
46
+ # * debug<Boolean> Debugging state. More verbose.
47
+ #
48
+ # ===== Examples
49
+ #
50
+ # Watchr.options.debug #=> false
51
+ # Watchr.options.debug = true
52
+ #
53
+ # ===== Returns
54
+ # options<Struct>:: options proxy.
55
+ #
56
+ #--
57
+ # On first use, initialize the options struct and default option values.
58
+ def options
59
+ @options ||= Struct.new(:debug).new
60
+ @options.debug ||= false
61
+ @options
62
+ end
63
+
64
+ # Outputs formatted debug statement to stdout, only if ::options.debug is true
65
+ #
66
+ # ===== Examples
67
+ #
68
+ # Watchr.options.debug = true
69
+ # Watchr.debug('im in ur codes, notifayinin u')
70
+ #
71
+ # outputs: "[watchr debug] im in ur codes, notifayinin u"
72
+ #
73
+ def debug(str)
74
+ puts "[watchr debug] #{str}" if options.debug
75
+ end
76
+
77
+ # Detect current OS and return appropriate handler.
78
+ #
79
+ # ===== Examples
80
+ #
81
+ # Config::CONFIG['host_os'] #=> 'linux-gnu'
82
+ # Watchr.handler #=> Watchr::EventHandler::Unix
83
+ #
84
+ # Config::CONFIG['host_os'] #=> 'cygwin'
85
+ # Watchr.handler #=> Watchr::EventHandler::Portable
86
+ #
87
+ # ENV['HANDLER'] #=> 'unix'
88
+ # Watchr.handler #=> Watchr::EventHandler::Unix
89
+ #
90
+ # ENV['HANDLER'] #=> 'portable'
91
+ # Watchr.handler #=> Watchr::EventHandler::Portable
92
+ #
93
+ # ===== Returns
94
+ # handler<Class>:: handler class for current architecture
95
+ #
96
+ def handler
97
+ @handler ||=
98
+ case ENV['HANDLER'] || Config::CONFIG['host_os']
99
+ when /mswin|windows|cygwin/i
100
+ Watchr::EventHandler::Portable
101
+ when /sunos|solaris|darwin|mach|osx|bsd|linux/i, 'unix'
102
+ if ::Watchr::HAVE_REV
103
+ Watchr::EventHandler::Unix
104
+ else
105
+ Watchr.debug "rev not found. `gem install rev` to get evented handler"
106
+ Watchr::EventHandler::Portable
107
+ end
108
+ else
109
+ Watchr::EventHandler::Portable
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,70 @@
1
+ # Run me with:
2
+ #
3
+ # $ watchr manifest.watchr
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
+ # Watchr 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
+ # $ watchr specs.watchr
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
+ # Watchr 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/watchr/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
24
+ watch( '^lib/watchr/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 watchr executable for dev work:
3
+
4
+ $ ruby -rubygems -Ilib ./bin/watchr -d specs.watchr
5
+
6
+ To force a specific handler:
7
+
8
+ $ HANDLER=protable watchr -d specs.watchr
9
+ $ HANDLER=unix watchr -d specs.watchr
10
+
11
+ (see Watchr.handler)
@@ -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 PortableEventHandlerTest < 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, :modified)
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,162 @@
1
+ require 'test/test_helper'
2
+
3
+ if Watchr::HAVE_REV
4
+
5
+ class Watchr::EventHandler::Unix::SingleFileWatcher
6
+ public :type
7
+ end
8
+
9
+ class UnixEventHandlerTest < Test::Unit::TestCase
10
+ include Watchr
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 Watchr::HAVE_REV