mynyml-watchr 0.3.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/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
|
+
|