feather_watch 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +18 -0
- data/Feather Watch.png +0 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +67 -0
- data/Rakefile +7 -0
- data/feather_watch.gemspec +29 -0
- data/lib/feather_watch.rb +17 -0
- data/lib/feather_watch/core/darwin_watcher.rb +35 -0
- data/lib/feather_watch/core/linux_watcher.rb +52 -0
- data/lib/feather_watch/core/windows_watcher.rb +46 -0
- data/lib/feather_watch/os.rb +17 -0
- data/lib/feather_watch/version.rb +3 -0
- data/lib/feather_watch/watcher.rb +50 -0
- data/spec/lib/feather_watch/core/watcher_spec.rb +159 -0
- data/spec/lib/feather_watch/feather_watch_spec.rb +81 -0
- data/spec/spec_helper.rb +110 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c365a117cc400f3b83860aabf245dbdd24a87d0e
|
4
|
+
data.tar.gz: e37efc790a575ed9c9beb2f2699d6e4eff745fbd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 54894b9dc54e207f0afc675e75ceddcfb036bfa502d55bbef66fdfcc25b647e5ab85e86807c9a6783d04f8aeaea2868cf61ee4994043ebcbef77372b767f144d
|
7
|
+
data.tar.gz: 5ccbdba157e5c9cab6cfd23e4033a837259975a81e1cefa0cf6e6900f1543e637f822f90db76cbe0bf8b15bb8b6309521350953484406650548bcec5b0484ef0
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
gemfile:
|
2
|
+
- Gemfile
|
3
|
+
language: ruby
|
4
|
+
rvm:
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1.0
|
7
|
+
- 2.1.1
|
8
|
+
- 2.1.2
|
9
|
+
- 2.1.3
|
10
|
+
matrix:
|
11
|
+
allow_failures:
|
12
|
+
- rvm: 1.8.7
|
13
|
+
- rvm: 1.9.2
|
14
|
+
- rvm: 1.9.3
|
15
|
+
- rvm: jruby-head
|
16
|
+
- rvm: ruby-head
|
17
|
+
- rvm: rbx
|
18
|
+
fast_finish: true
|
data/Feather Watch.png
ADDED
Binary file
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Stephan Nordnes Eriksen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
![Feather Watch](/Feather Watch.png?raw=true)
|
2
|
+
|
3
|
+
Light weight, cross platform, file system watcher.
|
4
|
+
|
5
|
+
|
6
|
+
### Should work windows, but not yet tested becase I do not have access to a windows machine now. If you do, I would love for you to run the specs and see if it works :)
|
7
|
+
|
8
|
+
[![Build Status](https://travis-ci.org/stephan-nordnes-eriksen/feather_watch.svg?branch=master)](https://travis-ci.org/stephan-nordnes-eriksen/feather_watch)
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'feather_watch'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install feather_watch
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
require "feather_watch"
|
27
|
+
callback = lambda{|e| puts "Event #{e[:status]} on file #{e[:file]}"}
|
28
|
+
paths_to_watch = "/" #can be a string or array of string, eg: ["/home/data", "/home/pictures"]
|
29
|
+
watcher = FeatherWatch.new(paths_to_watch, callback)
|
30
|
+
watcher.start #non-blocking
|
31
|
+
|
32
|
+
#To stop:
|
33
|
+
watcher.stop
|
34
|
+
|
35
|
+
### Received statuses
|
36
|
+
|
37
|
+
- :added
|
38
|
+
- :modified
|
39
|
+
- :removed
|
40
|
+
|
41
|
+
|
42
|
+
# Important Notes!
|
43
|
+
Events on OSx, also known as darwin, is currenty an approximation as the underlying libraries does not currently support actual events. Thus on OSx, you get `:removed` if the file recieved does not exist by checking File.file?(the_file). If it does exist you get `:modified`. So you will **not** get any `:added` events on OSx.
|
44
|
+
|
45
|
+
Feather Watch will recieve very many file events, even for temp files. Care should be taken when handling the events. Make sure to only process what you need. For instance, you should check against temp-files, and skip those events. Example:
|
46
|
+
|
47
|
+
#black list approach
|
48
|
+
un_accepted_file_types = ["tmp", "cache", "db"]
|
49
|
+
callback = lambda{|e| use_event(e) unless un_accepted_file_types.include?(e[:file].split(".")[-1])}
|
50
|
+
|
51
|
+
#white list approach
|
52
|
+
accepted_file_types = ["png", "jpg", "jpeg", "gif"]
|
53
|
+
callback = lambda{|e| use_event(e) if accepted_file_types.include?(e[:file].split(".")[-1])}
|
54
|
+
|
55
|
+
You need to take care to filter out what you do not need for each target platform.
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
1. Fork it ( https://github.com/[my-github-username]/feather_watch/fork )
|
64
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
65
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
66
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
67
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'feather_watch/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "feather_watch"
|
8
|
+
spec.version = FeatherWatch::VERSION
|
9
|
+
spec.authors = ["Stephan Nordnes Eriksen"]
|
10
|
+
spec.email = ["stephanruler@gmail.com"]
|
11
|
+
spec.summary = %q{Barebones, simple, and fast file system watcher}
|
12
|
+
spec.description = %q{A barebones file system watcher which uses native file system events for Linux, OSx, and Windows.}
|
13
|
+
spec.homepage = "https://github.com/stephan-nordnes-eriksen/feather_watch"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'rb-fsevent', '~> 0.9'
|
22
|
+
spec.add_dependency 'rb-inotify', '~> 0.9'
|
23
|
+
spec.add_dependency 'wdm', '~> 0.1'
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "simplecov"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "feather_watch/version"
|
2
|
+
require "feather_watch/os"
|
3
|
+
require "feather_watch/watcher"
|
4
|
+
require "feather_watch/core/darwin_watcher"
|
5
|
+
require "feather_watch/core/linux_watcher"
|
6
|
+
require "feather_watch/core/windows_watcher"
|
7
|
+
|
8
|
+
|
9
|
+
require 'rb-fsevent' if FeatherWatch::OS.mac?
|
10
|
+
require 'rb-inotify' if FeatherWatch::OS.linux?
|
11
|
+
require 'wdm' if FeatherWatch::OS.windows?
|
12
|
+
|
13
|
+
module FeatherWatch
|
14
|
+
def self.new(*args)
|
15
|
+
FeatherWatch::Watcher.new(*args)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module FeatherWatch::Core
|
2
|
+
class DarwinWatcher
|
3
|
+
def initialize(directories, callback, verbose= false)
|
4
|
+
@verbose = verbose
|
5
|
+
puts "Initializing mac watcher" if @verbose
|
6
|
+
@fs_event = FSEvent.new
|
7
|
+
options = { :no_defer => true,
|
8
|
+
:file_events => true }
|
9
|
+
|
10
|
+
@fs_event.watch directories, options do |changed_files|
|
11
|
+
changed_files.each do |f|
|
12
|
+
if File.file?(f)
|
13
|
+
puts "Change on file: #{f}" if @verbose
|
14
|
+
callback.call({status: :modified, file: f})
|
15
|
+
else
|
16
|
+
puts "Removed file: #{f}" if @verbose
|
17
|
+
callback.call({status: :removed, file: f})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
puts "Starting mac watcher" if @verbose
|
25
|
+
Thread.new do
|
26
|
+
@fs_event.run
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
puts "Stopping mac watcher" if @verbose
|
32
|
+
@fs_event.stop
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module FeatherWatch::Core
|
2
|
+
class LinuxWatcher
|
3
|
+
def initialize(directories, callback, verbose= false)
|
4
|
+
@verbose = verbose
|
5
|
+
puts "Initializing linux watcher" if @verbose
|
6
|
+
@notifiers = []
|
7
|
+
directories.each do |dir|
|
8
|
+
notifier = INotify::Notifier.new
|
9
|
+
@notifiers << notifier
|
10
|
+
#Avaliable events: :access, :attrib, :close_write, :close_nowrite, :create, :delete, :delete_self, :ignored, :modify, :move_self, :moved_from, :moved_to, :open
|
11
|
+
notifier.watch(dir, :create, :attrib, :delete, :close_write, :delete_self, :modify, :move_self, :moved_from, :moved_to) do |event|
|
12
|
+
#TODO: This information is probably in the event, but I'm on a mac now, so I can't test it properly
|
13
|
+
|
14
|
+
if !([:attrib, :close_write, :modify] & event.flags ).empty?
|
15
|
+
puts "Change on file: #{event.absolute_name}" if @verbose
|
16
|
+
callback.call({status: :modified, file: event.absolute_name})
|
17
|
+
elsif !([:moved_to] & event.flags ).empty?
|
18
|
+
puts "File added: #{event.absolute_name}" if @verbose
|
19
|
+
callback.call({status: :added, file: event.absolute_name})
|
20
|
+
elsif !([:moved_from] & event.flags ).empty?
|
21
|
+
puts "File removed: #{event.absolute_name}" if @verbose
|
22
|
+
callback.call({status: :removed, file: event.absolute_name})
|
23
|
+
elsif !([:create] & event.flags ).empty?
|
24
|
+
puts "File added: #{event.absolute_name}" if @verbose
|
25
|
+
callback.call({status: :added, file: event.absolute_name})
|
26
|
+
elsif !([:delete, :delete_self] & event.flags ).empty?
|
27
|
+
puts "File removed: #{event.absolute_name}" if @verbose
|
28
|
+
callback.call({status: :removed, file: event.absolute_name})
|
29
|
+
else
|
30
|
+
puts "Unhandled status flags: #{event.flags} for file #{event.absolute_name}" if @verbose
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def start
|
37
|
+
puts "Starting linux watcher" if @verbose
|
38
|
+
@notifiers.each do |notifier|
|
39
|
+
Thread.new do
|
40
|
+
notifier.run
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop
|
46
|
+
puts "Stopping linux watcher" if @verbose
|
47
|
+
@notifiers.each do |notifier|
|
48
|
+
notifier.stop
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module FeatherWatch::Core
|
2
|
+
class WindowsWatcher
|
3
|
+
def initialize(directories, callback, verbose= false)
|
4
|
+
@verbose = verbose
|
5
|
+
puts "Initializing windows watcher" if @verbose
|
6
|
+
@monitors = []
|
7
|
+
directories.each do |dir|
|
8
|
+
monitor = WDM::Monitor.new
|
9
|
+
@monitors << monitor
|
10
|
+
monitor.watch_recursively(dir, :files) do |change|
|
11
|
+
#TODO: Have not tested this. It should work
|
12
|
+
|
13
|
+
case change.type
|
14
|
+
when :added, :renamed_new_file
|
15
|
+
puts "File added: #{change.path}" if @verbose
|
16
|
+
callback.call({status: :added, file: change.path})
|
17
|
+
when :removed, :renamed_old_file
|
18
|
+
puts "Removed file: #{change.path}" if @verbose
|
19
|
+
callback.call({status: :removed, file: change.path})
|
20
|
+
when :modified, :attrib
|
21
|
+
puts "File modified: #{change.path}" if @verbose
|
22
|
+
callback.call({status: :modified, file: change.path})
|
23
|
+
else
|
24
|
+
puts "Unhandled status type: #{change.type} for file #{change.path}" if @verbose
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def start
|
31
|
+
puts "Starting windows watcher" if @verbose
|
32
|
+
@monitors.each do |monitor|
|
33
|
+
Thread.new do
|
34
|
+
monitor.run!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
puts "Stopping windows watcher" if @verbose
|
41
|
+
@monitors.each do |monitor|
|
42
|
+
monitor.stop
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module FeatherWatch::OS
|
2
|
+
def self.windows?
|
3
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.mac?
|
7
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.unix?
|
11
|
+
!self.windows?
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.linux?
|
15
|
+
self.unix? and not self.mac?
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FeatherWatch
|
2
|
+
class Watcher
|
3
|
+
|
4
|
+
# Public: Initialize the file system watcher.
|
5
|
+
#
|
6
|
+
# directories - An array of directories to watch.
|
7
|
+
# callback - A callback that will receive events. Recieved objects on form: {status: [:modified, :removed].sample, file: "path/to/file"}. ":modified" means everything except deleted.
|
8
|
+
# verbose=false - Enable verbose mode. Warning: Can produce very much output
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# FeatherWatch::Watcher.new([File.join(Dir.home, 'Desktop')], lambda{|e| puts "got event with status: #{e[:status]} on file #{e[:file]}"})
|
13
|
+
#
|
14
|
+
# Returns a Watcher object.
|
15
|
+
def initialize(directories, callback, verbose= false)
|
16
|
+
@verbose = verbose
|
17
|
+
dir = directories
|
18
|
+
dir = [directories] if directories.is_a?(String)
|
19
|
+
raise "Unknown datatype for directories. Was: #{dir}" unless dir.is_a?(Array)
|
20
|
+
|
21
|
+
@listener = FeatherWatch::Core::DarwinWatcher.new(dir, callback, verbose) if FeatherWatch::OS.mac?
|
22
|
+
@listener = FeatherWatch::Core::LinuxWatcher.new(dir, callback, verbose) if FeatherWatch::OS.linux?
|
23
|
+
@listener = FeatherWatch::Core::WindowsWatcher.new(dir, callback, verbose) if FeatherWatch::OS.windows?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Starts the watcher.
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
# watcher.start
|
31
|
+
# # => nil
|
32
|
+
#
|
33
|
+
# Returns nil
|
34
|
+
def start
|
35
|
+
@listener.start if @listener
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Stops the watcher.
|
39
|
+
#
|
40
|
+
# Examples
|
41
|
+
#
|
42
|
+
# watcher.start
|
43
|
+
# # => nil
|
44
|
+
#
|
45
|
+
# Returns nil
|
46
|
+
def stop
|
47
|
+
@listener.stop if @listener
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
|
5
|
+
describe FeatherWatch::Watcher do
|
6
|
+
describe ".initialize" do
|
7
|
+
it "does not crash" do
|
8
|
+
expect{FeatherWatch::Watcher.new("/",lambda { |e| })}.to_not raise_error
|
9
|
+
expect{FeatherWatch::Watcher.new("/",lambda { |e| }, false)}.to_not raise_error
|
10
|
+
end
|
11
|
+
it "calls DarwinWatcher.new on darwin" do
|
12
|
+
allow(Kernel).to receive(:require).with('rb-fsevent')
|
13
|
+
allow(FeatherWatch::OS).to receive(:mac?) {true}
|
14
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
15
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
16
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
17
|
+
|
18
|
+
path = "/"
|
19
|
+
callback = lambda { |e| }
|
20
|
+
verbose = false
|
21
|
+
|
22
|
+
expect(FeatherWatch::Core::DarwinWatcher).to receive(:new).with([path],callback,verbose)
|
23
|
+
|
24
|
+
FeatherWatch::Watcher.new(path,callback,verbose)
|
25
|
+
end
|
26
|
+
it "calls LinuxWatcher.new on linux" do
|
27
|
+
allow(Kernel).to receive(:require).with('rb-inotify')
|
28
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
29
|
+
allow(FeatherWatch::OS).to receive(:linux?) {true}
|
30
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
31
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
32
|
+
|
33
|
+
path = "/"
|
34
|
+
callback = lambda { |e| }
|
35
|
+
verbose = false
|
36
|
+
|
37
|
+
expect(FeatherWatch::Core::LinuxWatcher).to receive(:new).with([path],callback,verbose)
|
38
|
+
|
39
|
+
FeatherWatch::Watcher.new(path,callback,verbose)
|
40
|
+
end
|
41
|
+
it "calls WindowsWatcher.new on windows" do
|
42
|
+
allow(Kernel).to receive(:require).with('wdm')
|
43
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
44
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
45
|
+
allow(FeatherWatch::OS).to receive(:windows?) {true}
|
46
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
47
|
+
|
48
|
+
path = "/"
|
49
|
+
callback = lambda { |e| }
|
50
|
+
verbose = false
|
51
|
+
|
52
|
+
expect(FeatherWatch::Core::WindowsWatcher).to receive(:new).with([path],callback,verbose)
|
53
|
+
|
54
|
+
FeatherWatch::Watcher.new(path,callback,verbose)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe ".start" do
|
59
|
+
it "calls start on a DarwinWatcher on darwin" do
|
60
|
+
allow(Kernel).to receive(:require).with('rb-fsevent')
|
61
|
+
allow(FeatherWatch::OS).to receive(:mac?) {true}
|
62
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
63
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
64
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
65
|
+
|
66
|
+
path = "/"
|
67
|
+
callback = lambda { |e| }
|
68
|
+
verbose = false
|
69
|
+
|
70
|
+
darwin_spy = spy "DarwinWatcher"
|
71
|
+
expect(FeatherWatch::Core::DarwinWatcher).to receive(:new).with([path],callback,verbose) {darwin_spy}
|
72
|
+
expect(darwin_spy).to receive(:start)
|
73
|
+
FeatherWatch::Watcher.new(path,callback,verbose).start
|
74
|
+
end
|
75
|
+
it "calls start on a LinuxWatcher on linux" do
|
76
|
+
allow(Kernel).to receive(:require).with('rb-inotify')
|
77
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
78
|
+
allow(FeatherWatch::OS).to receive(:linux?) {true}
|
79
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
80
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
81
|
+
|
82
|
+
path = "/"
|
83
|
+
callback = lambda { |e| }
|
84
|
+
verbose = false
|
85
|
+
|
86
|
+
linux_spy = spy "LinuxWatcher"
|
87
|
+
expect(FeatherWatch::Core::LinuxWatcher).to receive(:new).with([path],callback,verbose) {linux_spy}
|
88
|
+
expect(linux_spy).to receive(:start)
|
89
|
+
FeatherWatch::Watcher.new(path,callback,verbose).start
|
90
|
+
end
|
91
|
+
it "calls start on a WindowsWatcher on windows" do
|
92
|
+
allow(Kernel).to receive(:require).with('wdm')
|
93
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
94
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
95
|
+
allow(FeatherWatch::OS).to receive(:windows?) {true}
|
96
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
97
|
+
|
98
|
+
path = "/"
|
99
|
+
callback = lambda { |e| }
|
100
|
+
verbose = false
|
101
|
+
|
102
|
+
windows_spy = spy "WindowsWatcher"
|
103
|
+
expect(FeatherWatch::Core::WindowsWatcher).to receive(:new).with([path],callback,verbose) {windows_spy}
|
104
|
+
expect(windows_spy).to receive(:start)
|
105
|
+
FeatherWatch::Watcher.new(path,callback,verbose).start
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe ".stop" do
|
110
|
+
it "calls stop on a DarwinWatcher on darwin" do
|
111
|
+
allow(Kernel).to receive(:require).with('rb-fsevent')
|
112
|
+
allow(FeatherWatch::OS).to receive(:mac?) {true}
|
113
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
114
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
115
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
116
|
+
|
117
|
+
path = "/"
|
118
|
+
callback = lambda { |e| }
|
119
|
+
verbose = false
|
120
|
+
|
121
|
+
darwin_spy = spy "DarwinWatcher"
|
122
|
+
expect(FeatherWatch::Core::DarwinWatcher).to receive(:new).with([path],callback,verbose) {darwin_spy}
|
123
|
+
expect(darwin_spy).to receive(:stop)
|
124
|
+
FeatherWatch::Watcher.new(path,callback,verbose).stop
|
125
|
+
end
|
126
|
+
it "calls stop on a LinuxWatcher on linux" do
|
127
|
+
allow(Kernel).to receive(:require).with('rb-inotify')
|
128
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
129
|
+
allow(FeatherWatch::OS).to receive(:linux?) {true}
|
130
|
+
allow(FeatherWatch::OS).to receive(:windows?) {false}
|
131
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
132
|
+
|
133
|
+
path = "/"
|
134
|
+
callback = lambda { |e| }
|
135
|
+
verbose = false
|
136
|
+
|
137
|
+
linux_spy = spy "LinuxWatcher"
|
138
|
+
expect(FeatherWatch::Core::LinuxWatcher).to receive(:new).with([path],callback,verbose) {linux_spy}
|
139
|
+
expect(linux_spy).to receive(:stop)
|
140
|
+
FeatherWatch::Watcher.new(path,callback,verbose).stop
|
141
|
+
end
|
142
|
+
it "calls stop on a WindowsWatcher on windows" do
|
143
|
+
allow(Kernel).to receive(:require).with('wdm')
|
144
|
+
allow(FeatherWatch::OS).to receive(:mac?) {false}
|
145
|
+
allow(FeatherWatch::OS).to receive(:linux?) {false}
|
146
|
+
allow(FeatherWatch::OS).to receive(:windows?) {true}
|
147
|
+
allow(FeatherWatch::OS).to receive(:unix?) {false}
|
148
|
+
|
149
|
+
path = "/"
|
150
|
+
callback = lambda { |e| }
|
151
|
+
verbose = false
|
152
|
+
|
153
|
+
windows_spy = spy "WindowsWatcher"
|
154
|
+
expect(FeatherWatch::Core::WindowsWatcher).to receive(:new).with([path],callback,verbose) {windows_spy}
|
155
|
+
expect(windows_spy).to receive(:stop)
|
156
|
+
FeatherWatch::Watcher.new(path,callback,verbose).stop
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
describe FeatherWatch do
|
2
|
+
before(:each) do
|
3
|
+
FileUtils.mkdir(File.join(Dir.pwd, "test_folder"))
|
4
|
+
end
|
5
|
+
after(:each) do
|
6
|
+
FileUtils.rm_rf(File.join(Dir.pwd, "test_folder"))
|
7
|
+
end
|
8
|
+
|
9
|
+
it "passen self.new on to FeatherWatch::Watcher" do
|
10
|
+
args = ["/",lambda { |e| }, false]
|
11
|
+
expect(FeatherWatch::Watcher).to receive(:new).with(*args)
|
12
|
+
FeatherWatch.new(*args)
|
13
|
+
end
|
14
|
+
#These tests are platform dependent and should be run once on windows, darwin, and linux
|
15
|
+
#This test is also prone to sporadic failures.
|
16
|
+
it "returns correct file path and status when file is added" do
|
17
|
+
path = File.join(Dir.pwd, "test_folder")
|
18
|
+
callback_spy = spy("Callback Spy")
|
19
|
+
verbose = false
|
20
|
+
|
21
|
+
file_path = File.join(Dir.pwd, "test_folder", "new_file")
|
22
|
+
|
23
|
+
if FeatherWatch::OS.mac?
|
24
|
+
expect(callback_spy).to receive(:call).with({status: :modified ,file: file_path})
|
25
|
+
else
|
26
|
+
expect(callback_spy).to receive(:call).with({status: :added ,file: file_path})
|
27
|
+
end
|
28
|
+
|
29
|
+
watcher = FeatherWatch::Watcher.new(path,callback_spy,verbose)
|
30
|
+
watcher.start
|
31
|
+
sleep 0.1 #need to wait for watcher to properly start
|
32
|
+
FileUtils.touch(file_path)
|
33
|
+
sleep 0.1 #need to wait for file event to propegate.
|
34
|
+
watcher.stop
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns correct file path and status when file is removed" do
|
38
|
+
path = File.join(Dir.pwd, "test_folder")
|
39
|
+
callback_spy = spy("Callback Spy")
|
40
|
+
verbose = false
|
41
|
+
|
42
|
+
file_path = File.join(Dir.pwd, "test_folder", "new_file")
|
43
|
+
FileUtils.touch(file_path)
|
44
|
+
sleep 0.1
|
45
|
+
expect(callback_spy).to receive(:call).with({status: :removed ,file: file_path})
|
46
|
+
|
47
|
+
watcher = FeatherWatch::Watcher.new(path,callback_spy,verbose)
|
48
|
+
watcher.start
|
49
|
+
sleep 0.1 #need to wait for watcher to properly start
|
50
|
+
FileUtils.rm(file_path)
|
51
|
+
sleep 0.1 #need to wait for file event to propegate.
|
52
|
+
watcher.stop
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns correct file path and status when file is moved" do
|
56
|
+
path = File.join(Dir.pwd, "test_folder")
|
57
|
+
callback_spy = spy("Callback Spy")
|
58
|
+
verbose = false
|
59
|
+
|
60
|
+
file_path = File.join(Dir.pwd, "test_folder", "new_file")
|
61
|
+
file_path_new = File.join(Dir.pwd, "test_folder", "new_file_new")
|
62
|
+
|
63
|
+
FileUtils.touch(file_path)
|
64
|
+
sleep 0.1
|
65
|
+
|
66
|
+
expect(callback_spy).to receive(:call).with({status: :removed ,file: file_path})
|
67
|
+
if FeatherWatch::OS.mac?
|
68
|
+
expect(callback_spy).to receive(:call).with({status: :modified ,file: file_path_new})
|
69
|
+
else
|
70
|
+
expect(callback_spy).to receive(:call).with({status: :added ,file: file_path_new})
|
71
|
+
end
|
72
|
+
|
73
|
+
watcher = FeatherWatch::Watcher.new(path,callback_spy,verbose)
|
74
|
+
watcher.start
|
75
|
+
sleep 0.1 #need to wait for watcher to properly start
|
76
|
+
FileUtils.mv(file_path, file_path_new)
|
77
|
+
sleep 0.1 #need to wait for file event to propegate.
|
78
|
+
watcher.stop
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
#START: HACK TO GET TRAVIS TO FAIL CORRECTLY WHEN SPECS FAIL:
|
2
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION >= "1.9"
|
3
|
+
module Kernel
|
4
|
+
alias :__at_exit :at_exit
|
5
|
+
def at_exit(&block)
|
6
|
+
__at_exit do
|
7
|
+
exit_status = $!.status if $!.is_a?(SystemExit)
|
8
|
+
block.call
|
9
|
+
exit exit_status if exit_status
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
#END
|
15
|
+
|
16
|
+
require 'simplecov'
|
17
|
+
SimpleCov.start
|
18
|
+
|
19
|
+
require "feather_watch"
|
20
|
+
require "fileutils"
|
21
|
+
|
22
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
23
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
24
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
25
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
26
|
+
#
|
27
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
28
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
29
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
30
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
31
|
+
# a separate helper file that requires the additional dependencies and performs
|
32
|
+
# the additional setup, and require it from the spec files that actually need it.
|
33
|
+
#
|
34
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
35
|
+
# users commonly want.
|
36
|
+
#
|
37
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
38
|
+
RSpec.configure do |config|
|
39
|
+
# rspec-expectations config goes here. You can use an alternate
|
40
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
41
|
+
# assertions if you prefer.
|
42
|
+
config.expect_with :rspec do |expectations|
|
43
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
44
|
+
# and `failure_message` of custom matchers include text for helper methods
|
45
|
+
# defined using `chain`, e.g.:
|
46
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
47
|
+
# # => "be bigger than 2 and smaller than 4"
|
48
|
+
# ...rather than:
|
49
|
+
# # => "be bigger than 2"
|
50
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
51
|
+
end
|
52
|
+
|
53
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
54
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
55
|
+
config.mock_with :rspec do |mocks|
|
56
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
57
|
+
# a real object. This is generally recommended, and will default to
|
58
|
+
# `true` in RSpec 4.
|
59
|
+
mocks.verify_partial_doubles = true
|
60
|
+
end
|
61
|
+
|
62
|
+
# The settings below are suggested to provide a good initial experience
|
63
|
+
# with RSpec, but feel free to customize to your heart's content.
|
64
|
+
=begin
|
65
|
+
# These two settings work together to allow you to limit a spec run
|
66
|
+
# to individual examples or groups you care about by tagging them with
|
67
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
68
|
+
# get run.
|
69
|
+
config.filter_run :focus
|
70
|
+
config.run_all_when_everything_filtered = true
|
71
|
+
|
72
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
73
|
+
# For more details, see:
|
74
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
75
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
76
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
77
|
+
config.disable_monkey_patching!
|
78
|
+
|
79
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
80
|
+
# be too noisy due to issues in dependencies.
|
81
|
+
config.warnings = true
|
82
|
+
|
83
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
84
|
+
# file, and it's useful to allow more verbose output when running an
|
85
|
+
# individual spec file.
|
86
|
+
if config.files_to_run.one?
|
87
|
+
# Use the documentation formatter for detailed output,
|
88
|
+
# unless a formatter has already been configured
|
89
|
+
# (e.g. via a command-line flag).
|
90
|
+
config.default_formatter = 'doc'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Print the 10 slowest examples and example groups at the
|
94
|
+
# end of the spec run, to help surface which specs are running
|
95
|
+
# particularly slow.
|
96
|
+
config.profile_examples = 10
|
97
|
+
|
98
|
+
# Run specs in random order to surface order dependencies. If you find an
|
99
|
+
# order dependency and want to debug it, you can fix the order by providing
|
100
|
+
# the seed, which is printed after each run.
|
101
|
+
# --seed 1234
|
102
|
+
config.order = :random
|
103
|
+
|
104
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
105
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
106
|
+
# test failures related to randomization by passing the same `--seed` value
|
107
|
+
# as the one that triggered the failure.
|
108
|
+
Kernel.srand config.seed
|
109
|
+
=end
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: feather_watch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephan Nordnes Eriksen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rb-fsevent
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rb-inotify
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: wdm
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A barebones file system watcher which uses native file system events
|
112
|
+
for Linux, OSx, and Windows.
|
113
|
+
email:
|
114
|
+
- stephanruler@gmail.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rspec"
|
121
|
+
- ".travis.yml"
|
122
|
+
- Feather Watch.png
|
123
|
+
- Gemfile
|
124
|
+
- LICENSE.txt
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- feather_watch.gemspec
|
128
|
+
- lib/feather_watch.rb
|
129
|
+
- lib/feather_watch/core/darwin_watcher.rb
|
130
|
+
- lib/feather_watch/core/linux_watcher.rb
|
131
|
+
- lib/feather_watch/core/windows_watcher.rb
|
132
|
+
- lib/feather_watch/os.rb
|
133
|
+
- lib/feather_watch/version.rb
|
134
|
+
- lib/feather_watch/watcher.rb
|
135
|
+
- spec/lib/feather_watch/core/watcher_spec.rb
|
136
|
+
- spec/lib/feather_watch/feather_watch_spec.rb
|
137
|
+
- spec/spec_helper.rb
|
138
|
+
homepage: https://github.com/stephan-nordnes-eriksen/feather_watch
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata: {}
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubyforge_project:
|
158
|
+
rubygems_version: 2.2.2
|
159
|
+
signing_key:
|
160
|
+
specification_version: 4
|
161
|
+
summary: Barebones, simple, and fast file system watcher
|
162
|
+
test_files:
|
163
|
+
- spec/lib/feather_watch/core/watcher_spec.rb
|
164
|
+
- spec/lib/feather_watch/feather_watch_spec.rb
|
165
|
+
- spec/spec_helper.rb
|
166
|
+
has_rdoc:
|