watchr 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -77,11 +77,16 @@ Install
77
77
 
78
78
  gem install watchr
79
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.
80
+ If you're on Linux/BSD and have the [rev][4] gem installed, Watchr will detect
81
+ it and use it automatically. This will make Watchr evented.
82
82
 
83
83
  gem install rev
84
84
 
85
+ You can get the same evented behaviour on OS X by installing
86
+ [ruby-fsevent][10].
87
+
88
+ gem install ruby-fsevent
89
+
85
90
  See Also
86
91
  --------
87
92
 
@@ -110,4 +115,5 @@ Links
110
115
  [7]: http://github.com/mynyml/phocus
111
116
  [8]: http://github.com/viking/autowatchr
112
117
  [9]: http://github.com/francois/nestor
118
+ [10]: http://github.com/sandro/ruby-fsevent
113
119
 
data/Rakefile CHANGED
@@ -1,3 +1,12 @@
1
+ def gem_opt
2
+ defined?(Gem) ? "-rubygems" : ""
3
+ end
4
+
5
+ def ruby
6
+ require 'rbconfig'
7
+ File.join([Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']]) << Config::CONFIG['EXEEXT']
8
+ end
9
+
1
10
  # --------------------------------------------------
2
11
  # Tests
3
12
  # --------------------------------------------------
@@ -8,7 +17,7 @@ namespace(:test) do
8
17
  desc "Run all tests"
9
18
  task(:all) do
10
19
  tests = Dir['test/**/test_*.rb'] - ['test/test_helper.rb']
11
- exit system("ruby -rubygems -I.:lib -e'%w( #{tests.join(' ')} ).each {|file| require file }'")
20
+ exit system(%Q{#{ruby} #{gem_opt} -I.:lib -e"%w( #{tests.join(' ')} ).each {|file| require file }"})
12
21
  end
13
22
 
14
23
  desc "Run all tests on multiple ruby versions (requires rvm)"
data/TODO.md CHANGED
@@ -9,6 +9,8 @@ Features
9
9
  * requires new handler(s)
10
10
  * will allow recognizing `:added` events
11
11
 
12
+ * allow setting latency
13
+
12
14
  Bugs
13
15
  ----
14
16
 
data/bin/watchr CHANGED
@@ -4,7 +4,7 @@ require 'pathname'
4
4
  require 'optparse'
5
5
  require 'tempfile'
6
6
 
7
- require 'watchr'
7
+ require File.dirname(__FILE__) + '/../lib/watchr'
8
8
 
9
9
  module Watchr
10
10
  # Namespaced to avoid defining global methods
@@ -12,8 +12,16 @@ require 'rbconfig'
12
12
  # # on command line, from project's root dir
13
13
  # $ watchr path/to/script
14
14
  #
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
16
  module Watchr
16
- VERSION = '0.6'
17
+ VERSION = '0.7'
18
+
19
+ begin
20
+ require 'fsevent'
21
+ HAVE_FSE = true
22
+ rescue LoadError, RuntimeError
23
+ HAVE_FSE = false
24
+ end
17
25
 
18
26
  begin
19
27
  require 'rev'
@@ -27,8 +35,9 @@ module Watchr
27
35
 
28
36
  module EventHandler
29
37
  autoload :Base, 'watchr/event_handlers/base'
30
- autoload :Unix, 'watchr/event_handlers/unix' if ::Watchr::HAVE_REV
31
38
  autoload :Portable, 'watchr/event_handlers/portable'
39
+ autoload :Unix, 'watchr/event_handlers/unix' if ::Watchr::HAVE_REV
40
+ autoload :Darwin, 'watchr/event_handlers/darwin' if ::Watchr::HAVE_FSE
32
41
  end
33
42
 
34
43
  class << self
@@ -100,15 +109,22 @@ module Watchr
100
109
  def handler
101
110
  @handler ||=
102
111
  case ENV['HANDLER'] || Config::CONFIG['host_os']
103
- when /mswin|windows|cygwin/i
104
- Watchr::EventHandler::Portable
105
- when /sunos|solaris|darwin|mach|osx|bsd|linux/i, 'unix'
106
- if ::Watchr::HAVE_REV
112
+ when /darwin|mach|osx|fsevents?/i
113
+ if Watchr::HAVE_FSE
114
+ Watchr::EventHandler::Darwin
115
+ else
116
+ Watchr.debug "fsevent not found. `gem install ruby-fsevent` to get evented handler"
117
+ Watchr::EventHandler::Portable
118
+ end
119
+ when /sunos|solaris|bsd|linux|unix/i
120
+ if Watchr::HAVE_REV
107
121
  Watchr::EventHandler::Unix
108
122
  else
109
123
  Watchr.debug "rev not found. `gem install rev` to get evented handler"
110
124
  Watchr::EventHandler::Portable
111
125
  end
126
+ when /mswin|windows|cygwin/i
127
+ Watchr::EventHandler::Portable
112
128
  else
113
129
  Watchr::EventHandler::Portable
114
130
  end
@@ -58,6 +58,7 @@ module Watchr
58
58
  def update(path, event_type = nil)
59
59
  path = Pathname(path).expand_path
60
60
 
61
+ Watchr.debug("received #{event_type.inspect} event for #{path.relative_path_from(Pathname(Dir.pwd))}")
61
62
  if path == @script.path
62
63
  @script.parse!
63
64
  @handler.refresh(monitored_paths)
@@ -0,0 +1,160 @@
1
+ module Watchr
2
+ module EventHandler
3
+
4
+ class ::FSEvents
5
+ # Same as Watch.debug, but prefixed with [fsevents] instead.
6
+ #
7
+ # @example
8
+ #
9
+ # FSEvents.debug('missfired')
10
+ #
11
+ # @param [String] message
12
+ # debug message to print
13
+ #
14
+ # @return [nil]
15
+ #
16
+ def self.debug(msg)
17
+ puts "[fsevents] #{msg}" if Watchr.options.debug
18
+ end
19
+ end
20
+
21
+ # FSEvents based event handler for Darwin/OSX
22
+ #
23
+ # Uses ruby-fsevents (http://github.com/sandro/ruby-fsevent)
24
+ #
25
+ class Darwin < FSEvent
26
+ include Base
27
+
28
+ def initialize
29
+ super
30
+ self.latency = 0.2
31
+ end
32
+
33
+ # Enter listening loop. Will block control flow until application is
34
+ # explicitly stopped/killed.
35
+ #
36
+ # @return [undefined]
37
+ #
38
+ def listen(monitored_paths)
39
+ register_paths(monitored_paths)
40
+ start
41
+ end
42
+
43
+ # Rebuild file bindings. Will detach all current bindings, and reattach
44
+ # the `monitored_paths`
45
+ #
46
+ # @param [Array<Pathname>] monitored_paths
47
+ # list of paths the application is currently monitoring.
48
+ #
49
+ # @return [undefined]
50
+ #
51
+ def refresh(monitored_paths)
52
+ register_paths(monitored_paths)
53
+ restart
54
+ end
55
+
56
+ private
57
+
58
+ # Callback. Called on file change event. Delegates to
59
+ # {Controller#update}, passing in path and event type
60
+ #
61
+ # @return [undefined]
62
+ #
63
+ def on_change(dirs)
64
+ dirs.each do |dir|
65
+ path, type = detect_change(dir)
66
+ notify(path, type) unless path.nil?
67
+ end
68
+ end
69
+
70
+ # Detected latest updated file within given directory
71
+ #
72
+ # @param [Pathname, String] dir
73
+ # directory reporting event
74
+ #
75
+ # @return [Array(Pathname, Symbol)] path and type
76
+ # path to updated file and event type
77
+ #
78
+ def detect_change(dir)
79
+ paths = monitored_paths_for(dir)
80
+ type = nil
81
+ path = paths.find {|path| type = event_type(path) }
82
+
83
+ FSEvents.debug("event detection error") if type.nil?
84
+
85
+ update_reference_times
86
+ [path, type]
87
+ end
88
+
89
+ # Detect type of event for path, if any
90
+ #
91
+ # Path times (atime, mtime, ctime) are compared to stored references.
92
+ # If any is more recent, the event is reported as a symbol.
93
+ #
94
+ # @param [Pathname] path
95
+ #
96
+ # @return [Symbol, nil] event type
97
+ # Event type if detected, nil otherwise.
98
+ # Symbol is on of :deleted, :modified, :accessed, :changed
99
+ #
100
+ def event_type(path)
101
+ return :deleted if !path.exist?
102
+ return :modified if path.mtime > @reference_times[path][:mtime]
103
+ return :accessed if path.atime > @reference_times[path][:atime]
104
+ return :changed if path.ctime > @reference_times[path][:ctime]
105
+ nil
106
+ end
107
+
108
+ # Monitored paths within given dir
109
+ #
110
+ # @param [Pathname, String] dir
111
+ #
112
+ # @return [Array<Pathname>] monitored_paths
113
+ #
114
+ def monitored_paths_for(dir)
115
+ dir = Pathname(dir).expand_path
116
+ @paths.select {|path| path.dirname.expand_path == dir }
117
+ end
118
+
119
+ # Register watches for paths
120
+ #
121
+ # @param [Array<Pathname>] paths
122
+ #
123
+ # @return [undefined]
124
+ #
125
+ def register_paths(paths)
126
+ @paths = paths
127
+ watch_directories(dirs_for(@paths))
128
+ update_reference_times
129
+ end
130
+
131
+ # Directories for paths
132
+ #
133
+ # A unique list of directories containing given paths
134
+ #
135
+ # @param [Array<Pathname>] paths
136
+ #
137
+ # @return [Array<Pathname>] dirs
138
+ #
139
+ def dirs_for(paths)
140
+ paths.map {|path| path.dirname.to_s }.uniq
141
+ end
142
+
143
+ # Update reference times for registered paths
144
+ #
145
+ # @return [undefined]
146
+ #
147
+ def update_reference_times
148
+ @reference_times = {}
149
+ now = Time.now
150
+ @paths.each do |path|
151
+ @reference_times[path] = {}
152
+ @reference_times[path][:atime] = now
153
+ @reference_times[path][:mtime] = now
154
+ @reference_times[path][:ctime] = now
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+
@@ -0,0 +1,111 @@
1
+ require 'test/test_helper'
2
+
3
+ if Watchr::HAVE_FSE
4
+
5
+ class Watchr::EventHandler::Darwin
6
+ attr_accessor :paths
7
+
8
+ def start() end #noop
9
+ def restart() end #noop
10
+
11
+ public :on_change, :registered_directories
12
+ end
13
+
14
+ class DarwinEventHandlerTest < MiniTest::Unit::TestCase
15
+ include Watchr
16
+
17
+ def tempfile(name)
18
+ file = Tempfile.new(name, @root.to_s)
19
+ Pathname(file.path)
20
+ ensure
21
+ file.close
22
+ end
23
+
24
+ def setup
25
+ @root = Pathname(Dir.mktmpdir("WATCHR_SPECS-"))
26
+
27
+ @now = Time.now
28
+ @handler = EventHandler::Darwin.new
29
+
30
+ @foo = tempfile('foo').expand_path
31
+ @bar = tempfile('bar').expand_path
32
+ end
33
+
34
+ def teardown
35
+ FileUtils.remove_entry_secure(@root.to_s)
36
+ end
37
+
38
+ test "listening triggers listening state" do
39
+ @handler.expects(:start)
40
+ @handler.listen([])
41
+ end
42
+
43
+ test "listens for events on monitored files" do
44
+ @handler.listen [ @foo, @bar ]
45
+ assert_includes @handler.paths, @foo
46
+ assert_includes @handler.paths, @bar
47
+ end
48
+
49
+ test "reattaches to new monitored files" do
50
+ @baz = tempfile('baz').expand_path
51
+ @bax = tempfile('bax').expand_path
52
+
53
+ @handler.listen [ @foo, @bar ]
54
+ assert_includes @handler.paths, @foo
55
+ assert_includes @handler.paths, @bar
56
+
57
+ @handler.refresh [ @baz, @bax ]
58
+ assert_includes @handler.paths, @baz
59
+ assert_includes @handler.paths, @bax
60
+ refute_includes @handler.paths, @foo
61
+ refute_includes @handler.paths, @bar
62
+ end
63
+
64
+ ## event types
65
+
66
+ test "deleted file event" do
67
+ @foo.stubs(:exist?).returns(false)
68
+
69
+ @handler.listen [ @foo, @bar ]
70
+ @handler.expects(:notify).with(@foo, :deleted)
71
+ @handler.on_change [@root]
72
+ end
73
+
74
+ test "modified file event" do
75
+ @foo.stubs(:mtime).returns(@now + 100)
76
+ @handler.expects(:notify).with(@foo, :modified)
77
+
78
+ @handler.listen [ @foo, @bar ]
79
+ @handler.on_change [@root]
80
+ end
81
+
82
+ test "accessed file event" do
83
+ @foo.stubs(:atime).returns(@now + 100)
84
+ @handler.expects(:notify).with(@foo, :accessed)
85
+
86
+ @handler.listen [ @foo, @bar ]
87
+ @handler.on_change [@root]
88
+ end
89
+
90
+ test "changed file event" do
91
+ @foo.stubs(:ctime).returns(@now + 100)
92
+ @handler.expects(:notify).with(@foo, :changed)
93
+
94
+ @handler.listen [ @foo, @bar ]
95
+ @handler.on_change [@root]
96
+ end
97
+
98
+ ## internal
99
+
100
+ test "registers directories" do
101
+ @handler.listen [ @foo, @bar ]
102
+
103
+ assert_equal @foo.dirname, @bar.dirname # make sure all tempfiles are in same dir
104
+ assert_equal 1, @handler.registered_directories.size
105
+ assert_includes @handler.registered_directories, @foo.dirname.to_s
106
+ assert_includes @handler.registered_directories, @bar.dirname.to_s
107
+ end
108
+ end
109
+
110
+ end # if Watchr::HAVE_FSE
111
+
@@ -1,6 +1,7 @@
1
1
  require 'pathname'
2
+ require 'tmpdir'
2
3
  require 'tempfile'
3
- require 'set'
4
+ require 'fileutils'
4
5
 
5
6
  require 'minitest/autorun'
6
7
  require 'mocha'
@@ -30,3 +31,7 @@ unless Watchr::HAVE_REV
30
31
  puts "Skipping Unix handler tests. Install Rev (gem install rev) to properly test full suite"
31
32
  end
32
33
 
34
+ unless Watchr::HAVE_FSE
35
+ puts "Skipping Darwin handler tests. Install FSEvent (gem install ruby-fsevent) to properly test full suite (osx only)"
36
+ end
37
+
@@ -35,12 +35,24 @@ class TestWatchr < MiniTest::Unit::TestCase
35
35
  assert_equal Watchr::EventHandler::Unix, Watchr.handler
36
36
 
37
37
  Watchr.handler = nil
38
- ENV['HANDLER'] = 'darwin'
38
+ ENV['HANDLER'] = 'unix'
39
39
  assert_equal Watchr::EventHandler::Unix, Watchr.handler
40
40
 
41
+ end
42
+
43
+ if Watchr::HAVE_FSE
44
+
41
45
  Watchr.handler = nil
42
- ENV['HANDLER'] = 'unix'
43
- assert_equal Watchr::EventHandler::Unix, Watchr.handler
46
+ ENV['HANDLER'] = 'darwin'
47
+ assert_equal Watchr::EventHandler::Darwin, Watchr.handler
48
+
49
+ Watchr.handler = nil
50
+ ENV['HANDLER'] = 'osx'
51
+ assert_equal Watchr::EventHandler::Darwin, Watchr.handler
52
+
53
+ Watchr.handler = nil
54
+ ENV['HANDLER'] = 'fsevent'
55
+ assert_equal Watchr::EventHandler::Darwin, Watchr.handler
44
56
 
45
57
  end
46
58
 
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.bindir = "bin"
13
13
  s.executables = "watchr"
14
14
  s.version = Watchr::VERSION
15
- s.files = File.read("Manifest").strip.split("\n")
15
+ s.files = `git ls-files`.strip.split("\n")
16
16
 
17
17
  s.add_development_dependency 'minitest'
18
18
  s.add_development_dependency 'mocha'
metadata CHANGED
@@ -1,7 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: watchr
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.6"
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 7
8
+ version: "0.7"
5
9
  platform: ruby
6
10
  authors:
7
11
  - mynyml
@@ -9,39 +13,45 @@ autorequire:
9
13
  bindir: bin
10
14
  cert_chain: []
11
15
 
12
- date: 2010-02-10 00:00:00 -05:00
16
+ date: 2010-08-23 00:00:00 -07:00
13
17
  default_executable:
14
18
  dependencies:
15
19
  - !ruby/object:Gem::Dependency
16
20
  name: minitest
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
20
23
  requirements:
21
24
  - - ">="
22
25
  - !ruby/object:Gem::Version
26
+ segments:
27
+ - 0
23
28
  version: "0"
24
- version:
29
+ type: :development
30
+ version_requirements: *id001
25
31
  - !ruby/object:Gem::Dependency
26
32
  name: mocha
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
33
+ prerelease: false
34
+ requirement: &id002 !ruby/object:Gem::Requirement
30
35
  requirements:
31
36
  - - ">="
32
37
  - !ruby/object:Gem::Version
38
+ segments:
39
+ - 0
33
40
  version: "0"
34
- version:
41
+ type: :development
42
+ version_requirements: *id002
35
43
  - !ruby/object:Gem::Dependency
36
44
  name: every
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
45
+ prerelease: false
46
+ requirement: &id003 !ruby/object:Gem::Requirement
40
47
  requirements:
41
48
  - - ">="
42
49
  - !ruby/object:Gem::Version
50
+ segments:
51
+ - 0
43
52
  version: "0"
44
- version:
53
+ type: :development
54
+ version_requirements: *id003
45
55
  description: Modern continious testing (flexible alternative to autotest).
46
56
  email: mynyml@gmail.com
47
57
  executables:
@@ -54,7 +64,6 @@ files:
54
64
  - .gitignore
55
65
  - History.txt
56
66
  - LICENSE
57
- - Manifest
58
67
  - README.md
59
68
  - Rakefile
60
69
  - TODO.md
@@ -65,12 +74,14 @@ files:
65
74
  - lib/watchr.rb
66
75
  - lib/watchr/controller.rb
67
76
  - lib/watchr/event_handlers/base.rb
77
+ - lib/watchr/event_handlers/darwin.rb
68
78
  - lib/watchr/event_handlers/portable.rb
69
79
  - lib/watchr/event_handlers/unix.rb
70
80
  - lib/watchr/script.rb
71
81
  - specs.watchr
72
82
  - test/README
73
83
  - test/event_handlers/test_base.rb
84
+ - test/event_handlers/test_darwin.rb
74
85
  - test/event_handlers/test_portable.rb
75
86
  - test/event_handlers/test_unix.rb
76
87
  - test/test_controller.rb
@@ -91,18 +102,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
102
  requirements:
92
103
  - - ">="
93
104
  - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
94
107
  version: "0"
95
- version:
96
108
  required_rubygems_version: !ruby/object:Gem::Requirement
97
109
  requirements:
98
110
  - - ">="
99
111
  - !ruby/object:Gem::Version
112
+ segments:
113
+ - 0
100
114
  version: "0"
101
- version:
102
115
  requirements: []
103
116
 
104
117
  rubyforge_project: watchr
105
- rubygems_version: 1.3.5
118
+ rubygems_version: 1.3.6
106
119
  signing_key:
107
120
  specification_version: 3
108
121
  summary: Modern continious testing (flexible alternative to autotest)
data/Manifest DELETED
@@ -1,27 +0,0 @@
1
- .gitignore
2
- History.txt
3
- LICENSE
4
- Manifest
5
- README.md
6
- Rakefile
7
- TODO.md
8
- bin/watchr
9
- contributions.txt
10
- docs.watchr
11
- gem.watchr
12
- lib/watchr.rb
13
- lib/watchr/controller.rb
14
- lib/watchr/event_handlers/base.rb
15
- lib/watchr/event_handlers/portable.rb
16
- lib/watchr/event_handlers/unix.rb
17
- lib/watchr/script.rb
18
- specs.watchr
19
- test/README
20
- test/event_handlers/test_base.rb
21
- test/event_handlers/test_portable.rb
22
- test/event_handlers/test_unix.rb
23
- test/test_controller.rb
24
- test/test_helper.rb
25
- test/test_script.rb
26
- test/test_watchr.rb
27
- watchr.gemspec