filewatcher 0.5.4 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/filewatcher.rb +79 -138
- data/lib/filewatcher/cycles.rb +56 -0
- data/lib/filewatcher/snapshot.rb +65 -0
- data/lib/filewatcher/snapshots.rb +56 -0
- data/lib/filewatcher/spec_helper.rb +66 -0
- data/lib/filewatcher/spec_helper/watch_run.rb +74 -0
- data/lib/filewatcher/version.rb +5 -0
- data/spec/filewatcher/snapshot_spec.rb +67 -0
- data/spec/filewatcher/version_spec.rb +11 -0
- data/spec/filewatcher_spec.rb +289 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/spec_helper/ruby_watch_run.rb +82 -0
- metadata +92 -42
- data/LICENSE +0 -20
- data/README.md +0 -269
- data/Rakefile +0 -19
- data/bin/filewatcher +0 -168
- data/test/fixtures/file1.txt +0 -1
- data/test/fixtures/file2.txt +0 -1
- data/test/fixtures/file3.rb +0 -1
- data/test/fixtures/file4.rb +0 -1
- data/test/fixtures/subdir/file5.rb +0 -1
- data/test/fixtures/subdir/file6.rb +0 -1
- data/test/test_filewatcher.rb +0 -181
data/Rakefile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rake'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
task :default => :test
|
6
|
-
|
7
|
-
desc "Run tests"
|
8
|
-
task :test do
|
9
|
-
sh "bacon -Ilib -Itest --automatic --quiet"
|
10
|
-
delete_list =
|
11
|
-
%w(test/fixtures/file3.txt
|
12
|
-
test/fixtures/file4.txt
|
13
|
-
test/fixtures/file5.txt
|
14
|
-
test/fixtures/file6.txt
|
15
|
-
test/fixtures/file7.txt)
|
16
|
-
delete_list.each do |file|
|
17
|
-
FileUtils.rm(file)
|
18
|
-
end
|
19
|
-
end
|
data/bin/filewatcher
DELETED
@@ -1,168 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'filewatcher'
|
4
|
-
require 'trollop'
|
5
|
-
require 'pathname'
|
6
|
-
require 'thread'
|
7
|
-
|
8
|
-
options = Trollop::options do
|
9
|
-
version "filewatcher, version #{FileWatcher.VERSION} by Thomas Flemming 2016"
|
10
|
-
banner <<-EOS
|
11
|
-
Filewatcher scans the filesystem and executes shell commands when files changes.
|
12
|
-
|
13
|
-
Usage:
|
14
|
-
filewatcher [--restart] '<filenames or patterns>' '<shell command>'
|
15
|
-
Where
|
16
|
-
filename: filename(s) to scan.
|
17
|
-
shell command: shell command to execute when file changes on disk.
|
18
|
-
|
19
|
-
Examples:
|
20
|
-
filewatcher "myfile" "echo 'myfile has changed'"
|
21
|
-
filewatcher '*.rb' 'ruby $FILENAME'
|
22
|
-
filewatcher '**/*.rb' 'ruby $FILENAME' # Watch subdirectories
|
23
|
-
|
24
|
-
Other available environment variables are BASENAME, ABSOLUTE_FILENAME,
|
25
|
-
RELATIVE_FILENAME, EVENT and DIRNAME.
|
26
|
-
|
27
|
-
Options:
|
28
|
-
EOS
|
29
|
-
|
30
|
-
opt :dontwait, "Do not wait for filesystem updates before running", :short => 'd', :type => :boolean, :default => false
|
31
|
-
opt :daemon, "Run in the background as system daemon.", :short => 'D', :type => :boolean, :default => false
|
32
|
-
opt :restart, "Restart process when filesystem is updated", :short => 'r', :type => :boolean, :default => false
|
33
|
-
opt :list, "Print name of files being watched"
|
34
|
-
opt :exec, "Execute file as a script when file is updated.", :short => 'e', :type => :boolean, :default => false
|
35
|
-
opt :include, "Include files", :type => :string, :default => "*"
|
36
|
-
opt :exclude, "Exclude file(s) matching", :type => :string, :default => ""
|
37
|
-
opt :interval, "Interval to scan filesystem.", :short => 'i', :type => :float, :default => 0.5
|
38
|
-
opt :spinner, "Show an ascii spinner", :short => 's', :type => :boolean, :default => false
|
39
|
-
end
|
40
|
-
|
41
|
-
Trollop::die Trollop::educate if(ARGV.size == 0)
|
42
|
-
|
43
|
-
files = []
|
44
|
-
ARGV[0...-1].each do |a|
|
45
|
-
files << a
|
46
|
-
end
|
47
|
-
|
48
|
-
if(ARGV.length == 1)
|
49
|
-
files << ARGV[0]
|
50
|
-
end
|
51
|
-
|
52
|
-
def split_files_void_escaped_whitespace(files)
|
53
|
-
splitted_filenames = []
|
54
|
-
files.each do |name|
|
55
|
-
name = name.gsub(/\\\s/,'_ESCAPED_WHITESPACE_')
|
56
|
-
splitted_filenames << name.split(/\s/)
|
57
|
-
end
|
58
|
-
files = splitted_filenames.flatten.uniq
|
59
|
-
splitted_filenames = []
|
60
|
-
files.each do |name|
|
61
|
-
splitted_filenames << name.gsub('_ESCAPED_WHITESPACE_','\ ')
|
62
|
-
end
|
63
|
-
files = splitted_filenames
|
64
|
-
end
|
65
|
-
|
66
|
-
files = split_files_void_escaped_whitespace(files)
|
67
|
-
child_pid = nil
|
68
|
-
|
69
|
-
def restart(child_pid, env, cmd)
|
70
|
-
Process.kill(9, child_pid)
|
71
|
-
Process.wait(child_pid)
|
72
|
-
rescue Errno::ESRCH
|
73
|
-
# already killed
|
74
|
-
ensure
|
75
|
-
return Process.spawn(env, cmd)
|
76
|
-
end
|
77
|
-
|
78
|
-
if(options[:exclude] != "")
|
79
|
-
options[:exclude] = split_files_void_escaped_whitespace(options[:exclude].split(" "))
|
80
|
-
end
|
81
|
-
|
82
|
-
begin
|
83
|
-
fw = FileWatcher.new(files, options)
|
84
|
-
|
85
|
-
if(options[:list])
|
86
|
-
puts 'Watching:'
|
87
|
-
fw.last_found_filenames.each do |filename|
|
88
|
-
puts " #{filename}"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
Process.daemon(true, true) if options[:daemon]
|
93
|
-
|
94
|
-
fw.watch(options[:interval]) do |filename, event|
|
95
|
-
cmd = nil
|
96
|
-
if(options[:exec] and File.exist?(filename))
|
97
|
-
extension = filename[/(\.[^\.]*)$/,0]
|
98
|
-
runners = {
|
99
|
-
".py" => "python",
|
100
|
-
".js" => "node",
|
101
|
-
".rb" => "ruby",
|
102
|
-
".pl" => "perl",
|
103
|
-
".awk" => "awk",
|
104
|
-
".php" => "php",
|
105
|
-
".phtml" => "php",
|
106
|
-
".php4" => "php",
|
107
|
-
".php3" => "php",
|
108
|
-
".php5" => "php",
|
109
|
-
".phps" => "php"
|
110
|
-
}
|
111
|
-
runner = runners[extension]
|
112
|
-
if(runner)
|
113
|
-
cmd = "env #{runner.to_s} #{filename}"
|
114
|
-
end
|
115
|
-
elsif(ARGV.length > 1)
|
116
|
-
cmd = ARGV[-1]
|
117
|
-
end
|
118
|
-
|
119
|
-
if(cmd)
|
120
|
-
path = Pathname.new(filename)
|
121
|
-
env = {
|
122
|
-
'FILENAME' => filename,
|
123
|
-
'BASENAME' => path.basename.to_s,
|
124
|
-
'FILEDIR' => File.join(Pathname.new('.').realpath.to_s, path.parent.to_s), # Deprecated
|
125
|
-
'FSEVENT' => event.to_s, # Deprecated,
|
126
|
-
'EVENT' => event.to_s,
|
127
|
-
'DIRNAME' => File.join(Pathname.new('.').realpath.to_s, path.parent.to_s),
|
128
|
-
'ABSOLUTE_FILENAME' => File.join(Pathname.new('.').realpath.to_s, path.to_s),
|
129
|
-
'RELATIVE_FILENAME' => File.join(Pathname.new('.').to_s, path.to_s)
|
130
|
-
}
|
131
|
-
|
132
|
-
if(event != :delete)
|
133
|
-
ENV['FILEPATH'] = path.realpath.to_s
|
134
|
-
end
|
135
|
-
|
136
|
-
if(options[:restart])
|
137
|
-
if child_pid.nil?
|
138
|
-
child_pid = Process.spawn(env, cmd)
|
139
|
-
else
|
140
|
-
child_pid = restart(child_pid, env, cmd)
|
141
|
-
end
|
142
|
-
else
|
143
|
-
begin
|
144
|
-
pid = Process.spawn(env, cmd)
|
145
|
-
Process.wait()
|
146
|
-
rescue SystemExit, Interrupt
|
147
|
-
exit(0)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
else
|
152
|
-
case(event)
|
153
|
-
when :changed
|
154
|
-
print "file updated"
|
155
|
-
when :delete
|
156
|
-
print "file deleted"
|
157
|
-
when :new
|
158
|
-
print "new file"
|
159
|
-
else
|
160
|
-
print event.to_s
|
161
|
-
end
|
162
|
-
puts ": " + filename
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
rescue SystemExit, Interrupt
|
167
|
-
fw.finalize
|
168
|
-
end
|
data/test/fixtures/file1.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
content1
|
data/test/fixtures/file2.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
content2
|
data/test/fixtures/file3.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
|
data/test/fixtures/file4.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
puts "Hello world from Ruby!!!"
|
@@ -1 +0,0 @@
|
|
1
|
-
|
@@ -1 +0,0 @@
|
|
1
|
-
puts "This is file6.rb executing"
|
data/test/test_filewatcher.rb
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bacon'
|
3
|
-
require 'fileutils'
|
4
|
-
require File.expand_path("../lib/filewatcher.rb",File.dirname(__FILE__))
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
describe FileWatcher do
|
9
|
-
|
10
|
-
fixtures =
|
11
|
-
%w(test/fixtures/file4.rb
|
12
|
-
test/fixtures/subdir/file6.rb
|
13
|
-
test/fixtures/subdir/file5.rb
|
14
|
-
test/fixtures/file2.txt
|
15
|
-
test/fixtures/file1.txt
|
16
|
-
test/fixtures/file3.rb)
|
17
|
-
|
18
|
-
explicit_relative_fixtures = fixtures.map { |it| "./#{it}" }
|
19
|
-
|
20
|
-
subfolder = 'test/fixtures/new_sub_folder'
|
21
|
-
|
22
|
-
after do
|
23
|
-
FileUtils.rm_rf subfolder
|
24
|
-
end
|
25
|
-
|
26
|
-
def includes_all(elements)
|
27
|
-
lambda { |it| elements.all? { |element| it.include? element }}
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should exclude selected file patterns" do
|
31
|
-
filewatcher = FileWatcher.new(File.expand_path('test/fixtures/**/*'), :exclude => [File.expand_path("test/fixtures/**/*.txt")])
|
32
|
-
filtered_fixtures =
|
33
|
-
%w(test/fixtures/file4.rb
|
34
|
-
test/fixtures/subdir/file6.rb
|
35
|
-
test/fixtures/subdir/file5.rb
|
36
|
-
test/fixtures/file3.rb)
|
37
|
-
filewatcher.filenames.should.satisfy &includes_all(filtered_fixtures.map { |it| File.expand_path(it) })
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should handle absolute paths with globs" do
|
41
|
-
filewatcher = FileWatcher.new(File.expand_path('test/fixtures/**/*'))
|
42
|
-
|
43
|
-
filewatcher.filenames.should.satisfy &includes_all(fixtures.map { |it| File.expand_path(it) })
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should handle globs" do
|
47
|
-
filewatcher = FileWatcher.new('test/fixtures/**/*')
|
48
|
-
|
49
|
-
filewatcher.filenames.should.satisfy &includes_all(fixtures)
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
it "should handle explicit relative paths with globs" do
|
54
|
-
filewatcher = FileWatcher.new('./test/fixtures/**/*')
|
55
|
-
|
56
|
-
filewatcher.filenames.should.satisfy &includes_all(explicit_relative_fixtures)
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should handle explicit relative paths" do
|
60
|
-
filewatcher = FileWatcher.new('./test/fixtures')
|
61
|
-
|
62
|
-
filewatcher.filenames.should.satisfy &includes_all(explicit_relative_fixtures)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "should handle tilde expansion" do
|
66
|
-
filename = File.expand_path('~/file_watcher_1.txt')
|
67
|
-
open(filename, 'w') { |f| f.puts 'content1' }
|
68
|
-
|
69
|
-
filewatcher = FileWatcher.new('~/file_watcher_1.txt')
|
70
|
-
|
71
|
-
begin
|
72
|
-
filewatcher.filenames.should == [filename]
|
73
|
-
ensure
|
74
|
-
FileUtils.rm(filename)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
it "should detect file deletions" do
|
79
|
-
filename = "test/fixtures/file1.txt"
|
80
|
-
open(filename,"w") { |f| f.puts "content1" }
|
81
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
82
|
-
filewatcher.filesystem_updated?.should.be.false
|
83
|
-
FileUtils.rm(filename)
|
84
|
-
filewatcher.filesystem_updated?.should.be.true
|
85
|
-
end
|
86
|
-
|
87
|
-
it "should detect file additions" do
|
88
|
-
filename = "test/fixtures/file1.txt"
|
89
|
-
FileUtils.rm(filename) if File.exists?(filename)
|
90
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
91
|
-
filewatcher.filesystem_updated?.should.be.false
|
92
|
-
open(filename,"w") { |f| f.puts "content1" }
|
93
|
-
filewatcher.filesystem_updated?.should.be.true
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should detect file updates" do
|
97
|
-
filename = "test/fixtures/file1.txt"
|
98
|
-
open(filename,"w") { |f| f.puts "content1" }
|
99
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
100
|
-
filewatcher.filesystem_updated?.should.be.false
|
101
|
-
sleep 1
|
102
|
-
open(filename,"w") { |f| f.puts "content2" }
|
103
|
-
filewatcher.filesystem_updated?.should.be.true
|
104
|
-
end
|
105
|
-
|
106
|
-
it "should detect new files in subfolders" do
|
107
|
-
FileUtils::mkdir_p subfolder
|
108
|
-
|
109
|
-
filewatcher = FileWatcher.new(["./test/fixtures"])
|
110
|
-
filewatcher.filesystem_updated?.should.be.false
|
111
|
-
|
112
|
-
open(subfolder + "/file.txt","w") { |f| f.puts "xyz" }
|
113
|
-
filewatcher.filesystem_updated?.should.be.true
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should detect new subfolders" do
|
117
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
118
|
-
filewatcher.filesystem_updated?.should.be.false
|
119
|
-
|
120
|
-
FileUtils::mkdir_p subfolder
|
121
|
-
filewatcher.filesystem_updated?.should.be.true
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should be stoppable" do
|
125
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
126
|
-
thread = Thread.new(filewatcher){filewatcher.watch(0.1)}
|
127
|
-
sleep 0.2 # thread needs a chance to start
|
128
|
-
filewatcher.stop
|
129
|
-
thread.join.should.equal thread # Proves thread successfully joined
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should be pauseable/resumable" do
|
133
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
134
|
-
filewatcher.filesystem_updated?.should.be.false
|
135
|
-
processed = []
|
136
|
-
thread = Thread.new(filewatcher,processed) do
|
137
|
-
filewatcher.watch(0.1){|f,e| processed << f }
|
138
|
-
end
|
139
|
-
sleep 0.2 # thread needs a chance to start
|
140
|
-
filewatcher.pause
|
141
|
-
(1..4).each do |n|
|
142
|
-
open("test/fixtures/file#{n}.txt","w") { |f| f.puts "content#{n}" }
|
143
|
-
end
|
144
|
-
sleep 0.2 # Give filewatcher time to respond
|
145
|
-
processed.should.equal [] #update block should not have been called
|
146
|
-
filewatcher.resume
|
147
|
-
sleep 0.2 # Give filewatcher time to respond
|
148
|
-
processed.should.equal [] #update block still should not have been called
|
149
|
-
added_files = []
|
150
|
-
(5..7).each do |n|
|
151
|
-
added_files << "test/fixtures/file#{n}.txt"
|
152
|
-
open(added_files.last,"w") { |f| f.puts "content#{n}" }
|
153
|
-
end
|
154
|
-
sleep 0.2 # Give filewatcher time to respond
|
155
|
-
filewatcher.stop
|
156
|
-
processed.should.satisfy &includes_all(added_files)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should process all remaining changes at finalize" do
|
160
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
161
|
-
filewatcher.filesystem_updated?.should.be.false
|
162
|
-
processed = []
|
163
|
-
thread = Thread.new(filewatcher,processed) do
|
164
|
-
filewatcher.watch(0.1){|f,e| processed << f }
|
165
|
-
end
|
166
|
-
sleep 0.2 # thread needs a chance to start
|
167
|
-
filewatcher.stop
|
168
|
-
thread.join
|
169
|
-
added_files = []
|
170
|
-
(1..4).each do |n|
|
171
|
-
added_files << "test/fixtures/file#{n}.txt"
|
172
|
-
open(added_files.last,"w") { |f| f.puts "content#{n}" }
|
173
|
-
end
|
174
|
-
filewatcher.finalize
|
175
|
-
puts "What is wrong with finalize:"
|
176
|
-
puts "Expect: #{added_files.inspect}"
|
177
|
-
puts "Actual: #{processed.inspect}"
|
178
|
-
# processed.should.satisfy &includes_all(added_files)
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|