mynyml-watchr 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.rdoc +73 -0
- data/Rakefile +70 -0
- data/TODO.txt +27 -0
- data/bin/watchr +36 -0
- data/lib/watchr/version.rb +11 -0
- data/lib/watchr.rb +137 -0
- data/rdoc.watchr +15 -0
- data/specs.watchr +35 -0
- data/test/test_helper.rb +67 -0
- data/test/test_watchr.rb +182 -0
- data/watchr.gemspec +71 -0
- data/yard.watchr +18 -0
- metadata +71 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright © 2009 Martin Aumont (mynyml)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
==== Summary
|
2
|
+
|
3
|
+
Agile development tool that monitors a directory recursively, and triggers a
|
4
|
+
user defined action whenever an observed file is modified. Its most typical use
|
5
|
+
is continious testing, and as such it is a more flexible alternative to
|
6
|
+
autotest.
|
7
|
+
|
8
|
+
|
9
|
+
==== Features
|
10
|
+
|
11
|
+
* Ridiculously simple to use
|
12
|
+
* web framework agnostic <i>(rails, merb, sinatra, camping, invisible, ...)</i>
|
13
|
+
* test framework agnostic <i>(test/unit, minitest, rspec, test/spec, expectations, ...)</i>
|
14
|
+
* ruby interpreter agnostic <i>(ruby1.8, ruby1.9, MRI, JRuby, ...)</i>
|
15
|
+
* package framework agnostic <i>(rubygems, rip, ...)</i>
|
16
|
+
* Low level / highly flexible
|
17
|
+
|
18
|
+
|
19
|
+
==== Usage
|
20
|
+
|
21
|
+
On the command line,
|
22
|
+
|
23
|
+
$ watchr path/to/script.file
|
24
|
+
|
25
|
+
will monitor all files from within the current directory and below it
|
26
|
+
recursively, and react to events on those files in accordance with the script.
|
27
|
+
|
28
|
+
|
29
|
+
==== Scripts
|
30
|
+
|
31
|
+
The script contains a set of simple rules that map observed files to an action.
|
32
|
+
Its DSL is a single method: watch(pattern, &action)
|
33
|
+
|
34
|
+
watch( 'a regexp pattern matching paths to observe' ) {|match_data_object| command_to_run }
|
35
|
+
|
36
|
+
So for example,
|
37
|
+
|
38
|
+
watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
|
39
|
+
|
40
|
+
will match test files and run them whenever they are modified.
|
41
|
+
|
42
|
+
A continious testing script for a basic project could be
|
43
|
+
|
44
|
+
watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
|
45
|
+
watch( 'lib/(.*)\.rb' ) {|md| system("ruby test/test_#{md[1]}.rb") }
|
46
|
+
|
47
|
+
which, in addition to running any saved test file as above, will also run a
|
48
|
+
lib file's associated test. This mimics the equivalent autotest behaviour.
|
49
|
+
|
50
|
+
It's easy to see why watchr is so flexible, since the whole command is custom.
|
51
|
+
And remember the scripts are pure ruby, so feel free to add methods,
|
52
|
+
Signal#trap calls, etc.
|
53
|
+
|
54
|
+
The wiki[http://wiki.github.com/mynyml/watchr] has more details and examples.
|
55
|
+
|
56
|
+
|
57
|
+
==== Install
|
58
|
+
|
59
|
+
gem install mynyml-watchr --source http://gems.github.com/
|
60
|
+
|
61
|
+
|
62
|
+
==== See Also
|
63
|
+
|
64
|
+
redgreen[http://github.com/mynyml/redgreen]:: Standalone redgreen eye candy for test results, ala autotest.
|
65
|
+
phocus[http://github.com/mynyml/phocus]:: Run focused tests when running the whole file/suite is unnecessary.
|
66
|
+
|
67
|
+
|
68
|
+
==== Links
|
69
|
+
|
70
|
+
source:: http://github.com/mynyml/watchr
|
71
|
+
rdocs:: http://docs.github.com/mynyml/watchr
|
72
|
+
wiki:: http://wiki.github.com/mynyml/watchr
|
73
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# --------------------------------------------------
|
2
|
+
# based on thin's Rakefile (http://github.com/macournoyer/thin)
|
3
|
+
# --------------------------------------------------
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'pathname'
|
7
|
+
require 'yaml'
|
8
|
+
require 'lib/watchr/version'
|
9
|
+
|
10
|
+
RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
|
11
|
+
WIN = (RUBY_PLATFORM =~ /mswin|cygwin/)
|
12
|
+
SUDO = (WIN ? "" : "sudo")
|
13
|
+
|
14
|
+
def gem
|
15
|
+
RUBY_1_9 ? 'gem19' : 'gem'
|
16
|
+
end
|
17
|
+
|
18
|
+
def all_except(res)
|
19
|
+
Dir['**/*'].reject do |path|
|
20
|
+
Array(res).any? {|re| path.match(re) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
spec = Gem::Specification.new do |s|
|
25
|
+
s.name = 'watchr'
|
26
|
+
s.version = Watchr.version
|
27
|
+
s.summary = "Continious anything"
|
28
|
+
s.description = "Continious anything; project files observer/trigger."
|
29
|
+
s.author = "Martin Aumont"
|
30
|
+
s.email = 'mynyml@gmail.com'
|
31
|
+
s.homepage = ''
|
32
|
+
s.has_rdoc = true
|
33
|
+
s.require_path = "lib"
|
34
|
+
s.bindir = "bin"
|
35
|
+
s.executables = "watchr"
|
36
|
+
s.files = all_except %w( ^doc ^pkg ^test/fixtures )
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Generate rdoc documentation."
|
40
|
+
Rake::RDocTask.new(:rdoc => 'rdoc', :clobber_rdoc => 'rdoc:clean', :rerdoc => 'rdoc:force') { |rdoc|
|
41
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
42
|
+
rdoc.title = "Watchr"
|
43
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
44
|
+
rdoc.options << '--charset' << 'utf-8'
|
45
|
+
rdoc.main = 'README.rdoc'
|
46
|
+
rdoc.rdoc_files.include('README.rdoc')
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
48
|
+
}
|
49
|
+
|
50
|
+
Rake::GemPackageTask.new(spec) do |p|
|
51
|
+
p.gem_spec = spec
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Remove package products"
|
55
|
+
task :clean => :clobber_package
|
56
|
+
|
57
|
+
desc "Update the gemspec for GitHub's gem server"
|
58
|
+
task :gemspec do
|
59
|
+
Pathname("#{spec.name}.gemspec").open('w') {|f| f << YAML.dump(spec) }
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Install gem"
|
63
|
+
task :install => [:clobber, :package] do
|
64
|
+
sh "#{SUDO} #{gem} install pkg/#{spec.full_name}.gem"
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Uninstall gem"
|
68
|
+
task :uninstall => :clean do
|
69
|
+
sh "#{SUDO} #{gem} uninstall -v #{spec.version} -x #{spec.name}"
|
70
|
+
end
|
data/TODO.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
o write a few prepackaged scripts
|
3
|
+
o post on gists
|
4
|
+
o post links on wiki
|
5
|
+
o post main links in readme
|
6
|
+
|
7
|
+
o split watchr.rb classes into own files?
|
8
|
+
o run script within own context?
|
9
|
+
|
10
|
+
o use filesystem events for file monitoring (MA)
|
11
|
+
o linux: inotify
|
12
|
+
o osx: fsevent
|
13
|
+
o win: Directory Management? NTFS Change Journal? ReadDirectoryChangesW?
|
14
|
+
o *BSD: kqueue? pnotify?
|
15
|
+
o solaris: FEM?
|
16
|
+
o fallback to current method when no other method is available
|
17
|
+
o api will also need refactoring for clearer separation of functionality
|
18
|
+
|
19
|
+
o document source
|
20
|
+
o rdoc or yard?
|
21
|
+
|
22
|
+
o respond to different file events
|
23
|
+
o modified
|
24
|
+
o created
|
25
|
+
o deleted
|
26
|
+
o etc.
|
27
|
+
o watch(pattern, EVENT, &action)
|
data/bin/watchr
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'optparse'
|
5
|
+
root = Pathname(__FILE__).dirname.parent
|
6
|
+
require root.join('lib/watchr')
|
7
|
+
require root.join('lib/watchr/version')
|
8
|
+
|
9
|
+
def usage
|
10
|
+
"Usage: watchr [opts] path/to/script"
|
11
|
+
end
|
12
|
+
|
13
|
+
def version
|
14
|
+
"watchr version: %s" % Watchr.version
|
15
|
+
end
|
16
|
+
|
17
|
+
opts = OptionParser.new do |opts|
|
18
|
+
opts.banner = usage
|
19
|
+
|
20
|
+
opts.on('-d', '--debug', "Print extra debug info while program runs") {
|
21
|
+
Watchr.options.debug = true
|
22
|
+
}
|
23
|
+
|
24
|
+
opts.on_tail('-h', '--help', "Print inline help") { puts opts; exit }
|
25
|
+
opts.on_tail('-v', '--version', "Print version" ) { puts version; exit }
|
26
|
+
|
27
|
+
opts.parse! ARGV
|
28
|
+
end
|
29
|
+
|
30
|
+
abort(usage) unless ARGV.first
|
31
|
+
|
32
|
+
file = Pathname(ARGV.first)
|
33
|
+
abort(%|no script found; file "#{file.to_s}" doesn't exist.|) unless file.exist?
|
34
|
+
|
35
|
+
Watchr::Runner.new(file).run
|
36
|
+
|
data/lib/watchr.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
module Watchr
|
2
|
+
class << self
|
3
|
+
attr_accessor :options
|
4
|
+
|
5
|
+
def options
|
6
|
+
@options ||= Struct.new(:debug).new
|
7
|
+
# set default options
|
8
|
+
@options.debug ||= false
|
9
|
+
@options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Script
|
14
|
+
attr_accessor :map
|
15
|
+
attr_accessor :file
|
16
|
+
attr_accessor :reference_time
|
17
|
+
|
18
|
+
def initialize(file = nil)
|
19
|
+
self.map = []
|
20
|
+
self.file = file.is_a?(Pathname) ? file : Pathname.new(file) unless file.nil?
|
21
|
+
self.parse!
|
22
|
+
end
|
23
|
+
|
24
|
+
def watch(pattern, &action)
|
25
|
+
a = block_given? ? action : @default_action
|
26
|
+
self.map << [pattern, a]
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_action(&action)
|
30
|
+
@default_action = action
|
31
|
+
end
|
32
|
+
|
33
|
+
def changed?
|
34
|
+
return false unless self.bound?
|
35
|
+
self.file.mtime > self.reference_time
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse!
|
39
|
+
puts "[debug] loading script file #{self.file.to_s.inspect}" if Watchr.options.debug
|
40
|
+
|
41
|
+
return false unless self.bound?
|
42
|
+
self.map.clear
|
43
|
+
self.instance_eval(self.file.read)
|
44
|
+
self.reference_time = self.file.mtime
|
45
|
+
end
|
46
|
+
|
47
|
+
def bound?
|
48
|
+
self.file && self.file.respond_to?(:exist?) && self.file.exist?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Runner
|
53
|
+
attr_accessor :script
|
54
|
+
attr_accessor :map
|
55
|
+
attr_accessor :init_time
|
56
|
+
attr_accessor :reference_file
|
57
|
+
|
58
|
+
# Caches reference_file.mtime to allow picking up an update to the
|
59
|
+
# reference file itself
|
60
|
+
attr_accessor :reference_time
|
61
|
+
|
62
|
+
def initialize(script)
|
63
|
+
self.init_time = Time.now.to_f
|
64
|
+
self.script = script.is_a?(Script) ? script : Script.new(script)
|
65
|
+
end
|
66
|
+
|
67
|
+
def paths
|
68
|
+
self.map.keys
|
69
|
+
end
|
70
|
+
|
71
|
+
def last_updated_file
|
72
|
+
path = self.paths.max {|a,b| File.mtime(a) <=> File.mtime(b) }
|
73
|
+
Pathname(path)
|
74
|
+
end
|
75
|
+
|
76
|
+
# TODO extract updating the reference out of this method
|
77
|
+
def changed?
|
78
|
+
return true if self.paths.empty?
|
79
|
+
return false if self.last_updated_file.mtime.to_f < self.init_time.to_f
|
80
|
+
|
81
|
+
if self.reference_file.nil? || (self.reference_time.to_f < self.last_updated_file.mtime.to_f)
|
82
|
+
self.reference_file = self.last_updated_file
|
83
|
+
self.reference_time = self.last_updated_file.mtime
|
84
|
+
true
|
85
|
+
else
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def run
|
91
|
+
# enter monitoring state
|
92
|
+
loop do
|
93
|
+
self.trigger
|
94
|
+
Kernel.sleep(1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def trigger
|
99
|
+
self.script.parse! && self.map! if self.script.changed?
|
100
|
+
self.call_action! if self.changed?
|
101
|
+
end
|
102
|
+
|
103
|
+
def map
|
104
|
+
@map || self.map!
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
def call_action!
|
110
|
+
puts "[debug] monitoring paths: #{self.paths.inspect}" if Watchr.options.debug
|
111
|
+
raise "no reference file" if self.reference_file.nil?
|
112
|
+
|
113
|
+
ref = self.reference_file.to_s
|
114
|
+
pattern, action = self.map[ref]
|
115
|
+
md = ref.match(pattern)
|
116
|
+
action.call(md)
|
117
|
+
end
|
118
|
+
|
119
|
+
def map!
|
120
|
+
@map = {}
|
121
|
+
patterns = self.script.map.map {|mapping| mapping[0] }
|
122
|
+
patterns.each do |pattern|
|
123
|
+
local_files.each do |path|
|
124
|
+
if path.match(pattern)
|
125
|
+
action = self.script.map.assoc(pattern)[1]
|
126
|
+
@map[path] = [pattern, action]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
@map
|
131
|
+
end
|
132
|
+
|
133
|
+
def local_files
|
134
|
+
Dir['**/*']
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/rdoc.watchr
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ watchr docs.watchr
|
4
|
+
|
5
|
+
run_rdoc = lambda { system('rake --silent rdoc') }
|
6
|
+
|
7
|
+
watch( '(lib|bin)/.*\.rb', &run_rdoc )
|
8
|
+
watch( 'README.rdoc', &run_rdoc )
|
9
|
+
watch( 'TODO.txt', &run_rdoc )
|
10
|
+
watch( 'LICENSE', &run_rdoc )
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
# vim:ft=ruby
|
data/specs.watchr
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ watchr specs.watchr
|
4
|
+
|
5
|
+
def all_test_files
|
6
|
+
Dir['test/**/test_*.rb'] - ['test/test_helper.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(cmd)
|
10
|
+
puts(cmd)
|
11
|
+
system(cmd)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_all_tests
|
15
|
+
cmd = "ruby -rubygems -I.:lib:test -e'%w( #{all_test_files.join(' ')} ).each {|file| require file }'"
|
16
|
+
run(cmd)
|
17
|
+
end
|
18
|
+
|
19
|
+
watch( 'test/test_.*\.rb' ) {|md| run("ruby -rubygems #{md[0]}") }
|
20
|
+
watch( 'lib/(.*)\.rb' ) {|md| run("ruby -rubygems test/test_#{md[1]}.rb") }
|
21
|
+
watch( 'test/test_helper\.rb' ) { run_all_tests }
|
22
|
+
|
23
|
+
# Ctrl-C
|
24
|
+
Signal.trap('INT') do
|
25
|
+
puts " RERUNING ALL TESTS (Ctrl-\\ to quit)\n\n"
|
26
|
+
run_all_tests
|
27
|
+
end
|
28
|
+
|
29
|
+
# Ctrl-\
|
30
|
+
Signal.trap('QUIT') { abort("\n") }
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# vim:ft=ruby
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'matchy'
|
4
|
+
require 'mocha'
|
5
|
+
require 'every'
|
6
|
+
require 'pending'
|
7
|
+
begin
|
8
|
+
require 'ruby-debug'
|
9
|
+
require 'phocus'
|
10
|
+
require 'redgreen'
|
11
|
+
rescue LoadError, RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
ROOT = Pathname(__FILE__).dirname.parent
|
15
|
+
$:.unshift(ROOT.join('lib'))
|
16
|
+
|
17
|
+
require 'watchr'
|
18
|
+
|
19
|
+
class Test::Unit::TestCase
|
20
|
+
class << self
|
21
|
+
def test(name, &block)
|
22
|
+
name = :"test_#{name.gsub(/\s/,'_')}"
|
23
|
+
define_method(name, &block)
|
24
|
+
end
|
25
|
+
alias :should :test
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Pathname
|
30
|
+
def rel
|
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
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Fixture
|
46
|
+
DIR = Pathname(__FILE__).dirname.join('fixtures')
|
47
|
+
|
48
|
+
class << self
|
49
|
+
attr_accessor :files
|
50
|
+
|
51
|
+
def create(name=nil, content=nil)
|
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
|
59
|
+
|
60
|
+
def delete_all
|
61
|
+
DIR.entries.each do |fixture|
|
62
|
+
next if %w( .. . ).include?(fixture.to_s)
|
63
|
+
DIR.join(fixture.to_s).expand_path.delete
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/test/test_watchr.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class TestWatchr < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Watchr.options = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
## options
|
10
|
+
|
11
|
+
test "debug" do
|
12
|
+
Watchr.options.debug.should be(false)
|
13
|
+
Watchr.options.debug = true
|
14
|
+
Watchr.options.debug.should be(true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class TestScript < Test::Unit::TestCase
|
19
|
+
include Watchr
|
20
|
+
|
21
|
+
## api
|
22
|
+
|
23
|
+
test "watch" do
|
24
|
+
script = Script.new
|
25
|
+
script.watch('pattern') { nil }
|
26
|
+
|
27
|
+
script.map.first[0].should be('pattern')
|
28
|
+
script.map.first[1].call.should be(nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "default action" do
|
32
|
+
script = Script.new
|
33
|
+
script.default_action { nil }
|
34
|
+
script.watch('pattern')
|
35
|
+
|
36
|
+
script.map.first[0].should be('pattern')
|
37
|
+
script.map.first[1].call.should be(nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
test "automatically picks up changes to script file" do
|
41
|
+
file = Fixture.create('script.watchr', "watch('abc')")
|
42
|
+
script = Script.new(file)
|
43
|
+
script.changed?.should be(false)
|
44
|
+
|
45
|
+
script.stubs(:reference_time).returns(Time.now - 10) #mock sleep
|
46
|
+
|
47
|
+
Fixture.create('script.watchr', "watch('def')")
|
48
|
+
script.changed?.should be(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
test "reparses script file" do
|
52
|
+
file = Fixture.create('script.watchr', "watch('abc')")
|
53
|
+
script = Script.new(file)
|
54
|
+
script.map.first.should include('abc')
|
55
|
+
script.map.first.should exclude('def')
|
56
|
+
|
57
|
+
script.stubs(:reference_time).returns(Time.now - 10) #mock sleep
|
58
|
+
Fixture.create('script.watchr', "watch('def')")
|
59
|
+
script.parse!
|
60
|
+
script.map.first.should include('def')
|
61
|
+
script.map.first.should exclude('abc')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class TestRunner < Test::Unit::TestCase
|
66
|
+
include Watchr
|
67
|
+
|
68
|
+
def teardown
|
69
|
+
Fixture.delete_all
|
70
|
+
Watchr.options = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
test "maps observed files to their pattern and the actions they trigger" do
|
74
|
+
file_a = Fixture.create('a.rb')
|
75
|
+
file_b = Fixture.create('b.rb')
|
76
|
+
script = Script.new
|
77
|
+
script.watch(file_a.pattern) { 'ohaie' }
|
78
|
+
script.watch(file_b.pattern) { 'kthnx' }
|
79
|
+
|
80
|
+
runner = Runner.new(script)
|
81
|
+
runner.map[file_a.rel][0].should be(file_a.pattern)
|
82
|
+
runner.map[file_b.rel][0].should be(file_b.pattern)
|
83
|
+
runner.map[file_a.rel][1].call.should be('ohaie')
|
84
|
+
runner.map[file_b.rel][1].call.should be('kthnx')
|
85
|
+
end
|
86
|
+
|
87
|
+
test "latest mtime" do
|
88
|
+
file_a = Fixture.create('a.rb')
|
89
|
+
file_b = Fixture.create('b.rb')
|
90
|
+
script = Script.new
|
91
|
+
script.watch(file_a.pattern) { 'ohaie' }
|
92
|
+
script.watch(file_b.pattern) { 'kthnx' }
|
93
|
+
|
94
|
+
runner = Runner.new(script)
|
95
|
+
file_a.touch
|
96
|
+
|
97
|
+
runner.last_updated_file.rel.should be(file_a.rel)
|
98
|
+
end
|
99
|
+
|
100
|
+
test "monitors file changes" do
|
101
|
+
file_a = Fixture.create('a.rb')
|
102
|
+
script = Script.new
|
103
|
+
script.watch(file_a.pattern) { nil }
|
104
|
+
|
105
|
+
runner = Runner.new(script)
|
106
|
+
runner.changed?.should be(false)
|
107
|
+
|
108
|
+
# fake Kernel.sleep(2)
|
109
|
+
file_a.mtime = Time.now - 2
|
110
|
+
runner.init_time = Time.now - 2
|
111
|
+
|
112
|
+
file_a.touch
|
113
|
+
runner.changed?.should be(true)
|
114
|
+
end
|
115
|
+
|
116
|
+
test "calls action corresponding to file changed" do
|
117
|
+
script = Script.new
|
118
|
+
script.watch(Fixture.create.pattern) { throw(:ohaie) }
|
119
|
+
|
120
|
+
runner = Runner.new(script)
|
121
|
+
runner.init_time = Time.now - 2
|
122
|
+
runner.changed?
|
123
|
+
assert_throws(:ohaie) do
|
124
|
+
runner.instance_eval { call_action! }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
test "passes match data to action" do
|
129
|
+
file_a = Fixture.create('a.rb')
|
130
|
+
script = Script.new
|
131
|
+
pattern = Fixture::DIR.join('(.*)\.(.*)$').rel
|
132
|
+
script.watch((pattern)) {|md| [md[1], md[2]].join('|') }
|
133
|
+
|
134
|
+
runner = Runner.new(script)
|
135
|
+
runner.init_time = Time.now - 2
|
136
|
+
file_a.touch
|
137
|
+
runner.changed?
|
138
|
+
runner.instance_eval { call_action! }.should be('a|rb')
|
139
|
+
end
|
140
|
+
|
141
|
+
test "doesn't run at startup" do
|
142
|
+
file = Fixture.create('a.rb')
|
143
|
+
script = Script.new
|
144
|
+
script.watch(file.pattern) { nil }
|
145
|
+
|
146
|
+
runner = Runner.new(script)
|
147
|
+
runner.changed?.should be(false)
|
148
|
+
end
|
149
|
+
|
150
|
+
test "a path only triggers its last matching pattern's action" do
|
151
|
+
file_a = Fixture.create('fix_a.rb')
|
152
|
+
file_b = Fixture.create('fix_b.rb')
|
153
|
+
script = Script.new
|
154
|
+
script.watch('fix_a\.rb') { throw(:ohaie) }
|
155
|
+
script.watch('fix_.*\.rb') { throw(:kkthx) }
|
156
|
+
|
157
|
+
runner = Runner.new(script)
|
158
|
+
runner.init_time = Time.now - 2
|
159
|
+
file_a.touch
|
160
|
+
runner.changed?
|
161
|
+
assert_throws(:kkthx) do
|
162
|
+
runner.instance_eval { call_action! }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
test "updates map when script changes" do
|
167
|
+
file_a = Fixture.create('aaa')
|
168
|
+
file_b = Fixture.create('bbb')
|
169
|
+
script = Fixture.create('script.watchr', "watch('aaa')")
|
170
|
+
|
171
|
+
# fake Kernel.sleep(2)
|
172
|
+
script.mtime = Time.now - 2
|
173
|
+
|
174
|
+
runner = Runner.new(script)
|
175
|
+
assert runner.paths.first.match('aaa')
|
176
|
+
|
177
|
+
Fixture.create('script.watchr', "watch('bbb')")
|
178
|
+
|
179
|
+
runner.trigger
|
180
|
+
assert runner.paths.first.match('bbb')
|
181
|
+
end
|
182
|
+
end
|
data/watchr.gemspec
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: watchr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Aumont
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-26 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Continious anything; project files observer/trigger.
|
17
|
+
email: mynyml@gmail.com
|
18
|
+
executables:
|
19
|
+
- watchr
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Rakefile
|
26
|
+
- test
|
27
|
+
- test/test_watchr.rb
|
28
|
+
- test/test_helper.rb
|
29
|
+
- TODO.txt
|
30
|
+
- bin
|
31
|
+
- bin/watchr
|
32
|
+
- lib
|
33
|
+
- lib/watchr
|
34
|
+
- lib/watchr/version.rb
|
35
|
+
- lib/watchr.rb
|
36
|
+
- README.rdoc
|
37
|
+
- LICENSE
|
38
|
+
- yard.watchr
|
39
|
+
- rdoc.watchr
|
40
|
+
- specs.watchr
|
41
|
+
- watchr.gemspec
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: ""
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Continious anything
|
70
|
+
test_files: []
|
71
|
+
|
data/yard.watchr
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ watchr docs-yard.watchr
|
4
|
+
|
5
|
+
def run_yard
|
6
|
+
print "Updating yardocs... "
|
7
|
+
system('yardoc -o doc/yard --readme README.rdoc --files LICENSE')
|
8
|
+
print "done.\n"
|
9
|
+
end
|
10
|
+
|
11
|
+
watch( '^(lib|bin)/.*\.rb' ) { run_yard }
|
12
|
+
watch( '^README.rdoc' ) { run_yard }
|
13
|
+
watch( '^LICENSE' ) { run_yard }
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
# vim:ft=ruby
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mynyml-watchr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Aumont
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-25 21:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Continious anything; project files observer/trigger.
|
17
|
+
email: mynyml@gmail.com
|
18
|
+
executables:
|
19
|
+
- watchr
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Rakefile
|
26
|
+
- test
|
27
|
+
- test/test_watchr.rb
|
28
|
+
- test/test_helper.rb
|
29
|
+
- TODO.txt
|
30
|
+
- bin
|
31
|
+
- bin/watchr
|
32
|
+
- lib
|
33
|
+
- lib/watchr
|
34
|
+
- lib/watchr/version.rb
|
35
|
+
- lib/watchr.rb
|
36
|
+
- README.rdoc
|
37
|
+
- LICENSE
|
38
|
+
- yard.watchr
|
39
|
+
- rdoc.watchr
|
40
|
+
- specs.watchr
|
41
|
+
- watchr.gemspec
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: ""
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Continious anything
|
70
|
+
test_files: []
|
71
|
+
|