watchr 0.5.9 → 0.6
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/Manifest +2 -2
- data/README.md +113 -0
- data/Rakefile +1 -29
- data/TODO.md +29 -0
- data/bin/watchr +57 -11
- data/docs.watchr +20 -20
- data/gem.watchr +10 -20
- data/lib/watchr.rb +36 -32
- data/lib/watchr/controller.rb +29 -26
- data/lib/watchr/event_handlers/base.rb +24 -15
- data/lib/watchr/event_handlers/portable.rb +21 -7
- data/lib/watchr/event_handlers/unix.rb +39 -15
- data/lib/watchr/script.rb +89 -81
- data/specs.watchr +23 -24
- data/watchr.gemspec +1 -4
- metadata +8 -9
- data/README.rdoc +0 -104
- data/TODO.txt +0 -40
data/Manifest
CHANGED
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
Summary
|
2
|
+
-------
|
3
|
+
|
4
|
+
Agile development tool that monitors a directory tree, and triggers a user
|
5
|
+
defined action whenever an observed file is modified. Its most typical use is
|
6
|
+
continuous testing, and as such it is a more flexible alternative to autotest.
|
7
|
+
|
8
|
+
Features
|
9
|
+
--------
|
10
|
+
|
11
|
+
watchr is:
|
12
|
+
|
13
|
+
* Simple to use
|
14
|
+
* Highly flexible
|
15
|
+
* Evented ( Listens for filesystem events with native c libs )
|
16
|
+
* Portable ( Linux, \*BSD, OSX, Solaris, Windows )
|
17
|
+
* Fast ( Immediately reacts to file changes )
|
18
|
+
|
19
|
+
Most importantly it allows running tests in an environment that is **agnostic** to:
|
20
|
+
|
21
|
+
* Web frameworks ( rails, merb, sinatra, camping, invisible, ... )
|
22
|
+
* Test frameworks ( test/unit, minitest, rspec, test/spec, expectations, ... )
|
23
|
+
* Ruby interpreters ( ruby1.8, ruby1.9, MRI, JRuby, Rubinius, ... )
|
24
|
+
* Package frameworks ( rubygems, rip, ... )
|
25
|
+
|
26
|
+
Usage
|
27
|
+
-----
|
28
|
+
|
29
|
+
On the command line,
|
30
|
+
|
31
|
+
$ watchr path/to/script.file
|
32
|
+
|
33
|
+
will monitor files in the current directory tree, and react to events on those
|
34
|
+
files in accordance with the script.
|
35
|
+
|
36
|
+
Scripts
|
37
|
+
-------
|
38
|
+
|
39
|
+
The script contains a set of simple rules that map observed files to an action.
|
40
|
+
Its DSL is a single method: `watch(pattern, &action)`
|
41
|
+
|
42
|
+
watch( 'a regexp pattern matching paths to observe' ) {|match_data_object| command_to_run }
|
43
|
+
|
44
|
+
So for example,
|
45
|
+
|
46
|
+
watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
|
47
|
+
|
48
|
+
will match any test file and run it whenever it is saved.
|
49
|
+
|
50
|
+
A continuous testing script for a basic project could be
|
51
|
+
|
52
|
+
watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
|
53
|
+
watch( 'lib/(.*)\.rb' ) {|md| system("ruby test/test_#{md[1]}.rb") }
|
54
|
+
|
55
|
+
which, in addition to running any saved test file as above, will also run a
|
56
|
+
lib file's associated test. This mimics the equivalent autotest behaviour.
|
57
|
+
|
58
|
+
It's easy to see why watchr is so flexible, since the whole command is custom.
|
59
|
+
The above actions could just as easily call "jruby", "ruby --rubygems", "ruby
|
60
|
+
-Ilib", "specrb", "rbx", ... or any combination of these. For the sake of
|
61
|
+
comparison, autotest runs with:
|
62
|
+
|
63
|
+
$ /usr/bin/ruby1.8 -I.:lib:test -rubygems -e "%w[test/unit test/test_helper.rb test/test_watchr.rb].each { |f| require f }"
|
64
|
+
|
65
|
+
locking the environment into ruby1.8, rubygems and test/unit for all tests.
|
66
|
+
|
67
|
+
And remember the scripts are pure ruby, so feel free to add methods,
|
68
|
+
`Signal#trap` calls, etc. Updates to script files are picked up on the fly (no
|
69
|
+
need to restart watchr) so experimenting is painless.
|
70
|
+
|
71
|
+
The [wiki][5] has more details and examples. You might also want to take a
|
72
|
+
look at watchr's own scripts, [specs.watchr][1], [docs.watchr][2] and
|
73
|
+
[gem.watchr][3], to get you started.
|
74
|
+
|
75
|
+
Install
|
76
|
+
-------
|
77
|
+
|
78
|
+
gem install watchr
|
79
|
+
|
80
|
+
If you're on \*nix and have the [rev][4] gem installed, Watchr will detect it
|
81
|
+
and use it automatically. This will make Watchr evented.
|
82
|
+
|
83
|
+
gem install rev
|
84
|
+
|
85
|
+
See Also
|
86
|
+
--------
|
87
|
+
|
88
|
+
* [redgreen][6]: Standalone redgreen eye candy for test results, ala autotest.
|
89
|
+
* [phocus][7]: Run focused tests when running the whole file/suite is unnecessary.
|
90
|
+
* [autowatchr][8]: Provides some autotest-like behavior for watchr
|
91
|
+
* [nestor][9]: Continuous testing server for Rails
|
92
|
+
|
93
|
+
Links
|
94
|
+
-----
|
95
|
+
|
96
|
+
* code: <http://github.com/mynyml/watchr>
|
97
|
+
* docs: <http://yardoc.org/docs/mynyml-watchr/file:README.rdoc>
|
98
|
+
* wiki: <http://wiki.github.com/mynyml/watchr>
|
99
|
+
* bugs: <http://github.com/mynyml/watchr/issues>
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
[1]: http://github.com/mynyml/watchr/blob/master/specs.watchr
|
105
|
+
[2]: http://github.com/mynyml/watchr/blob/master/docs.watchr
|
106
|
+
[3]: http://github.com/mynyml/watchr/blob/master/gem.watchr
|
107
|
+
[4]: http://github.com/tarcieri/rev/
|
108
|
+
[5]: http://wiki.github.com/mynyml/watchr
|
109
|
+
[6]: http://github.com/mynyml/redgreen
|
110
|
+
[7]: http://github.com/mynyml/phocus
|
111
|
+
[8]: http://github.com/viking/autowatchr
|
112
|
+
[9]: http://github.com/francois/nestor
|
113
|
+
|
data/Rakefile
CHANGED
@@ -8,9 +8,7 @@ namespace(:test) do
|
|
8
8
|
desc "Run all tests"
|
9
9
|
task(:all) do
|
10
10
|
tests = Dir['test/**/test_*.rb'] - ['test/test_helper.rb']
|
11
|
-
|
12
|
-
puts cmd if ENV['VERBOSE']
|
13
|
-
system cmd
|
11
|
+
exit system("ruby -rubygems -I.:lib -e'%w( #{tests.join(' ')} ).each {|file| require file }'")
|
14
12
|
end
|
15
13
|
|
16
14
|
desc "Run all tests on multiple ruby versions (requires rvm)"
|
@@ -25,29 +23,3 @@ namespace(:test) do
|
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
28
|
-
|
29
|
-
# --------------------------------------------------
|
30
|
-
# Docs
|
31
|
-
# --------------------------------------------------
|
32
|
-
require 'rake/rdoctask'
|
33
|
-
desc "Generate rdoc documentation."
|
34
|
-
Rake::RDocTask.new(:rdoc => 'rdoc', :clobber_rdoc => 'rdoc:clean', :rerdoc => 'rdoc:force') do |rdoc|
|
35
|
-
rdoc.rdoc_dir = 'doc/rdoc'
|
36
|
-
rdoc.title = "Watchr"
|
37
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
38
|
-
rdoc.options << '--charset' << 'utf-8'
|
39
|
-
rdoc.main = 'README.rdoc'
|
40
|
-
rdoc.rdoc_files.include('README.rdoc')
|
41
|
-
rdoc.rdoc_files.include('TODO.txt')
|
42
|
-
rdoc.rdoc_files.include('LICENSE')
|
43
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
-
end
|
45
|
-
|
46
|
-
desc "Generate YARD Documentation"
|
47
|
-
task(:yardoc) do
|
48
|
-
require 'yard'
|
49
|
-
files = %w( lib/**/*.rb )
|
50
|
-
options = %w( -o doc/yard --readme README.rdoc --files LICENSE )
|
51
|
-
YARD::CLI::Yardoc.run *(options + files)
|
52
|
-
end
|
53
|
-
|
data/TODO.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Features
|
2
|
+
--------
|
3
|
+
|
4
|
+
* watchr -e ( `$ watchr -e "watch('foo.gemspec') { system('gem build foo.gemspec') }"` )
|
5
|
+
* watchr --auto
|
6
|
+
* watchr --fetch
|
7
|
+
|
8
|
+
* enable ability to watch dirs
|
9
|
+
* requires new handler(s)
|
10
|
+
* will allow recognizing `:added` events
|
11
|
+
|
12
|
+
Bugs
|
13
|
+
----
|
14
|
+
|
15
|
+
* sometimes an action is fired without a file being saved
|
16
|
+
* buffer flushing issue?
|
17
|
+
* libev issue?
|
18
|
+
* probably fixed with event type handling update, which ignores atime
|
19
|
+
updates by defaults
|
20
|
+
|
21
|
+
* when a file is saved twice quickly, subsequent events are ignored.
|
22
|
+
* seems like rev/libev drops the file watch
|
23
|
+
|
24
|
+
Other
|
25
|
+
-----
|
26
|
+
|
27
|
+
* add tests for executable
|
28
|
+
* memory profiling / benchmarks
|
29
|
+
|
data/bin/watchr
CHANGED
@@ -2,14 +2,21 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'optparse'
|
5
|
+
require 'tempfile'
|
5
6
|
|
6
7
|
require 'watchr'
|
7
8
|
|
8
9
|
module Watchr
|
9
10
|
# Namespaced to avoid defining global methods
|
10
|
-
|
11
|
+
#
|
12
|
+
# @private
|
13
|
+
module Bin
|
11
14
|
extend self
|
12
15
|
|
16
|
+
DEFAULT_SCRIPT_PATH = Pathname.new('specs.watchr')
|
17
|
+
|
18
|
+
attr_accessor :path
|
19
|
+
|
13
20
|
def usage
|
14
21
|
"Usage: watchr [opts] path/to/script"
|
15
22
|
end
|
@@ -18,20 +25,41 @@ module Watchr
|
|
18
25
|
"watchr version: %s" % Watchr::VERSION
|
19
26
|
end
|
20
27
|
|
28
|
+
# Absolute path to script file
|
29
|
+
#
|
30
|
+
# Unless set manually, the script's path is either first arg or
|
31
|
+
# `DEFAULT_SCRIPT_PATH`. If neither exists, the script immediatly aborts
|
32
|
+
# with an appropriate error message.
|
33
|
+
#
|
34
|
+
# @return [Pathname]
|
35
|
+
# absolute path to script
|
36
|
+
#
|
37
|
+
def path!
|
38
|
+
return @path unless @path.nil?
|
39
|
+
rel = relative_path or abort( usage )
|
40
|
+
find_in_load_path(rel) or abort("no script found: file #{rel.to_s.inspect} is not in path.")
|
41
|
+
end
|
42
|
+
|
21
43
|
# Find a partial path name in load path
|
22
44
|
#
|
23
|
-
#
|
24
|
-
#
|
45
|
+
# @param [Pathname] path
|
46
|
+
# partial pathname
|
25
47
|
#
|
26
|
-
#
|
27
|
-
# <Pathname>::
|
48
|
+
# @return [Pathname]
|
28
49
|
# absolute path of first occurence of partial path in load path, or nil if not found
|
29
|
-
|
30
|
-
# Adds '.' for ruby1.9.2
|
50
|
+
#
|
31
51
|
def find_in_load_path(path)
|
52
|
+
# Adds '.' for ruby1.9.2
|
32
53
|
dir = (['.'] + $LOAD_PATH).uniq.detect {|p| Pathname(p).join(path).exist? }
|
33
54
|
dir ? path.expand_path(dir) : nil
|
34
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def relative_path
|
60
|
+
return Pathname.new(ARGV.first) if ARGV.first
|
61
|
+
return DEFAULT_SCRIPT_PATH if DEFAULT_SCRIPT_PATH.exist?
|
62
|
+
end
|
35
63
|
end
|
36
64
|
end
|
37
65
|
|
@@ -45,6 +73,27 @@ opts = OptionParser.new do |opts|
|
|
45
73
|
rescue LoadError, RuntimeError
|
46
74
|
end
|
47
75
|
}
|
76
|
+
opts.on('-l', '--list', "Display list of files monitored by script and exit") {
|
77
|
+
script = Watchr::Script.new(Watchr::Bin.path!)
|
78
|
+
controller = Watchr::Controller.new(script, Watchr.handler.new)
|
79
|
+
script.parse!
|
80
|
+
puts controller.monitored_paths
|
81
|
+
exit
|
82
|
+
}
|
83
|
+
|
84
|
+
def assert_syntax(code)
|
85
|
+
catch(:ok) { Object.new.instance_eval("BEGIN { throw :ok }; #{code}", %|-e "#{code}"|, 0) }
|
86
|
+
rescue SyntaxError => e
|
87
|
+
puts e.message.split("\n")[1]
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on('-e', '--eval INLINE_SCRIPT', %|Evaluate script inline ($ watchr -e "watch('foo') { puts 'bar' }")|) {|code|
|
92
|
+
assert_syntax(code)
|
93
|
+
|
94
|
+
Tempfile.open('foo') {|f| f << code; @__path = f.path }
|
95
|
+
Watchr::Bin.path = Pathname(@__path)
|
96
|
+
}
|
48
97
|
|
49
98
|
opts.on_tail('-h', '--help', "Print inline help") { puts opts; exit }
|
50
99
|
opts.on_tail('-v', '--version', "Print version" ) { puts Watchr::Bin.version; exit }
|
@@ -52,8 +101,5 @@ opts = OptionParser.new do |opts|
|
|
52
101
|
opts.parse! ARGV
|
53
102
|
end
|
54
103
|
|
55
|
-
|
56
|
-
absolute_path = Watchr::Bin.find_in_load_path(relative_path) or abort("no script found; file #{relative_path.to_s.inspect} is not in path.")
|
57
|
-
|
58
|
-
Watchr::Controller.new(Watchr::Script.new(absolute_path), Watchr.handler.new).run
|
104
|
+
Watchr::Controller.new(Watchr::Script.new(Watchr::Bin.path!), Watchr.handler.new).run
|
59
105
|
|
data/docs.watchr
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# Run me with:
|
2
|
-
#
|
3
2
|
# $ watchr docs.watchr
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
require 'yard'
|
5
|
+
# --------------------------------------------------
|
6
|
+
# Rules
|
7
|
+
# --------------------------------------------------
|
8
|
+
watch( 'lib/.*\.rb' ) { yard }
|
9
|
+
watch( 'README.md' ) { yard }
|
10
|
+
watch( 'TODO.md' ) { yard }
|
11
|
+
watch( 'LICENSE' ) { yard }
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
# --------------------------------------------------
|
14
|
+
# Signal Handling
|
15
|
+
# --------------------------------------------------
|
16
|
+
Signal.trap('QUIT') { yard } # Ctrl-\
|
17
|
+
Signal.trap('INT' ) { abort("\n") } # Ctrl-C
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
# --------------------------------------------------
|
20
|
+
# Helpers
|
21
|
+
# --------------------------------------------------
|
22
|
+
def yard
|
23
|
+
print "Updating yardocs... "; STDOUT.flush
|
24
|
+
YARD::CLI::Yardoc.run *%w( -o doc/yard --readme README.md --markup markdown - LICENSE TODO.md )
|
25
|
+
print "done\n"
|
18
26
|
end
|
19
|
-
|
20
|
-
watch( 'lib/.*\.rb' ) { document }
|
21
|
-
watch( 'README.rdoc' ) { document }
|
22
|
-
watch( 'TODO.txt' ) { document }
|
23
|
-
watch( 'LICENSE' ) { document }
|
24
|
-
|
25
|
-
|
26
|
-
# vim:ft=ruby
|
data/gem.watchr
CHANGED
@@ -1,32 +1,22 @@
|
|
1
1
|
# Run me with:
|
2
|
-
#
|
3
2
|
# $ watchr gem.watchr
|
4
3
|
|
4
|
+
def gemspec() Dir['*.gemspec'].first end
|
5
5
|
# --------------------------------------------------
|
6
|
-
#
|
6
|
+
# Rules
|
7
7
|
# --------------------------------------------------
|
8
|
-
|
9
|
-
system "gem build %s" % gemspec
|
10
|
-
FileUtils.mv Dir['watchr-*.gem'], 'pkg/'
|
11
|
-
puts
|
12
|
-
end
|
8
|
+
watch( gemspec ) { build }
|
13
9
|
|
14
10
|
# --------------------------------------------------
|
15
|
-
#
|
11
|
+
# Signal Handling
|
16
12
|
# --------------------------------------------------
|
17
|
-
|
13
|
+
Signal.trap('QUIT') { build } # Ctrl-\
|
14
|
+
Signal.trap('INT' ) { abort("\n") } # Ctrl-C
|
18
15
|
|
19
16
|
# --------------------------------------------------
|
20
|
-
#
|
17
|
+
# Helpers
|
21
18
|
# --------------------------------------------------
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
build 'watchr.gemspec'
|
19
|
+
def build
|
20
|
+
puts; system "gem build #{gemspec}"
|
21
|
+
FileUtils.mv( Dir['*.gem'], 'pkg/' )
|
26
22
|
end
|
27
|
-
|
28
|
-
# Ctrl-C
|
29
|
-
Signal.trap('INT') { abort("\n") }
|
30
|
-
|
31
|
-
|
32
|
-
# vim:ft=ruby
|
data/lib/watchr.rb
CHANGED
@@ -5,15 +5,15 @@ require 'rbconfig'
|
|
5
5
|
# user defined action whenever an observed file is modified. Its most typical
|
6
6
|
# use is continuous testing.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# See README for more details
|
9
9
|
#
|
10
|
-
#
|
11
|
-
# $ watchr path/to/script
|
10
|
+
# @example
|
12
11
|
#
|
13
|
-
#
|
12
|
+
# # on command line, from project's root dir
|
13
|
+
# $ watchr path/to/script
|
14
14
|
#
|
15
15
|
module Watchr
|
16
|
-
VERSION = '0.
|
16
|
+
VERSION = '0.6'
|
17
17
|
|
18
18
|
begin
|
19
19
|
require 'rev'
|
@@ -35,7 +35,7 @@ module Watchr
|
|
35
35
|
attr_accessor :options
|
36
36
|
attr_accessor :handler
|
37
37
|
|
38
|
-
#
|
38
|
+
# @deprecated
|
39
39
|
def version #:nodoc:
|
40
40
|
Watchr::VERSION
|
41
41
|
end
|
@@ -43,55 +43,59 @@ module Watchr
|
|
43
43
|
# Options proxy.
|
44
44
|
#
|
45
45
|
# Currently supported options:
|
46
|
-
# * debug<Boolean> Debugging state. More verbose.
|
47
46
|
#
|
48
|
-
#
|
47
|
+
# * debug[Boolean] Debugging state. More verbose.
|
48
|
+
#
|
49
|
+
# @example
|
49
50
|
#
|
50
|
-
#
|
51
|
-
#
|
51
|
+
# Watchr.options.debug #=> false
|
52
|
+
# Watchr.options.debug = true
|
52
53
|
#
|
53
|
-
#
|
54
|
-
#
|
54
|
+
# @return [Struct]
|
55
|
+
# options proxy.
|
55
56
|
#
|
56
|
-
#--
|
57
|
-
# On first use, initialize the options struct and default option values.
|
58
57
|
def options
|
59
58
|
@options ||= Struct.new(:debug).new
|
60
59
|
@options.debug ||= false
|
61
60
|
@options
|
62
61
|
end
|
63
62
|
|
64
|
-
# Outputs formatted debug statement to stdout, only if
|
63
|
+
# Outputs formatted debug statement to stdout, only if `::options.debug` is true
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
#
|
67
|
+
# Watchr.options.debug = true
|
68
|
+
# Watchr.debug('im in ur codes, notifayinin u')
|
65
69
|
#
|
66
|
-
#
|
70
|
+
# #outputs: "[watchr debug] im in ur codes, notifayinin u"
|
67
71
|
#
|
68
|
-
#
|
69
|
-
#
|
72
|
+
# @param [String] message
|
73
|
+
# debug message to print
|
70
74
|
#
|
71
|
-
#
|
75
|
+
# @return [nil]
|
72
76
|
#
|
73
|
-
def debug(
|
74
|
-
puts "[watchr debug] #{
|
77
|
+
def debug(msg)
|
78
|
+
puts "[watchr debug] #{msg}" if options.debug
|
75
79
|
end
|
76
80
|
|
77
81
|
# Detect current OS and return appropriate handler.
|
78
82
|
#
|
79
|
-
#
|
83
|
+
# @example
|
80
84
|
#
|
81
|
-
#
|
82
|
-
#
|
85
|
+
# Config::CONFIG['host_os'] #=> 'linux-gnu'
|
86
|
+
# Watchr.handler #=> Watchr::EventHandler::Unix
|
83
87
|
#
|
84
|
-
#
|
85
|
-
#
|
88
|
+
# Config::CONFIG['host_os'] #=> 'cygwin'
|
89
|
+
# Watchr.handler #=> Watchr::EventHandler::Portable
|
86
90
|
#
|
87
|
-
#
|
88
|
-
#
|
91
|
+
# ENV['HANDLER'] #=> 'unix'
|
92
|
+
# Watchr.handler #=> Watchr::EventHandler::Unix
|
89
93
|
#
|
90
|
-
#
|
91
|
-
#
|
94
|
+
# ENV['HANDLER'] #=> 'portable'
|
95
|
+
# Watchr.handler #=> Watchr::EventHandler::Portable
|
92
96
|
#
|
93
|
-
#
|
94
|
-
#
|
97
|
+
# @return [Class]
|
98
|
+
# handler class for current architecture
|
95
99
|
#
|
96
100
|
def handler
|
97
101
|
@handler ||=
|