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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +34 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/bin/nestor +3 -0
- data/doc/.gitignore +3 -0
- data/doc/state-diagram.graffle +3870 -0
- data/doc/state-diagram.png +0 -0
- data/lib/nestor/cli.rb +52 -0
- data/lib/nestor/machine.rb +161 -0
- data/lib/nestor/strategies/test/unit.rb +116 -0
- data/lib/nestor/strategies.rb +18 -0
- data/lib/nestor/watchers/rails.rb +56 -0
- data/lib/nestor/watchers/rails_script.rb +83 -0
- data/lib/nestor/watchers.rb +1 -0
- data/lib/nestor.rb +11 -0
- data/spec/machine_spec.rb +56 -0
- data/spec/spec_helper.rb +9 -0
- data/vendor/watchr-0.5.7/.gitignore +5 -0
- data/vendor/watchr-0.5.7/History.txt +32 -0
- data/vendor/watchr-0.5.7/LICENSE +19 -0
- data/vendor/watchr-0.5.7/Manifest +27 -0
- data/vendor/watchr-0.5.7/README.rdoc +108 -0
- data/vendor/watchr-0.5.7/Rakefile +49 -0
- data/vendor/watchr-0.5.7/TODO.txt +40 -0
- data/vendor/watchr-0.5.7/bin/watchr +77 -0
- data/vendor/watchr-0.5.7/docs.watchr +26 -0
- data/vendor/watchr-0.5.7/gem.watchr +32 -0
- data/vendor/watchr-0.5.7/lib/watchr/controller.rb +81 -0
- data/vendor/watchr-0.5.7/lib/watchr/event_handlers/base.rb +48 -0
- data/vendor/watchr-0.5.7/lib/watchr/event_handlers/portable.rb +55 -0
- data/vendor/watchr-0.5.7/lib/watchr/event_handlers/unix.rb +97 -0
- data/vendor/watchr-0.5.7/lib/watchr/script.rb +203 -0
- data/vendor/watchr-0.5.7/lib/watchr.rb +113 -0
- data/vendor/watchr-0.5.7/manifest.watchr +70 -0
- data/vendor/watchr-0.5.7/specs.watchr +38 -0
- data/vendor/watchr-0.5.7/test/README +11 -0
- data/vendor/watchr-0.5.7/test/event_handlers/test_base.rb +24 -0
- data/vendor/watchr-0.5.7/test/event_handlers/test_portable.rb +58 -0
- data/vendor/watchr-0.5.7/test/event_handlers/test_unix.rb +162 -0
- data/vendor/watchr-0.5.7/test/test_controller.rb +103 -0
- data/vendor/watchr-0.5.7/test/test_helper.rb +52 -0
- data/vendor/watchr-0.5.7/test/test_script.rb +123 -0
- data/vendor/watchr-0.5.7/test/test_watchr.rb +60 -0
- data/vendor/watchr-0.5.7/watchr.gemspec +60 -0
- 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,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
|