fsevents 0.0.2 → 0.1.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/History.txt +8 -0
- data/lib/fsevents/event.rb +16 -2
- data/lib/fsevents/stream.rb +23 -3
- data/lib/fsevents/version.rb +2 -2
- data/spec/event_spec.rb +138 -36
- data/spec/stream_spec.rb +114 -6
- metadata +3 -3
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.1.0 2008-06-30
|
2
|
+
|
3
|
+
* 1 minor enhancement:
|
4
|
+
* Added optional cache mode for getting an Event's modified files. The old behavior is the mtime mode, which is default.
|
5
|
+
|
6
|
+
* 1 tiny enhancement:
|
7
|
+
* Now stripping trailing slashes from paths (for both Stream and Event objects)
|
8
|
+
|
1
9
|
== 0.0.2 2008-06-17
|
2
10
|
|
3
11
|
* 1 tiny enhancement:
|
data/lib/fsevents/event.rb
CHANGED
@@ -4,7 +4,7 @@ module FSEvents
|
|
4
4
|
|
5
5
|
def initialize(id, path, stream)
|
6
6
|
@id = id
|
7
|
-
@path = path
|
7
|
+
@path = path.sub(%r%/$%, '')
|
8
8
|
@stream = stream
|
9
9
|
end
|
10
10
|
|
@@ -13,7 +13,21 @@ module FSEvents
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def modified_files
|
16
|
-
|
16
|
+
case stream.mode
|
17
|
+
when :mtime
|
18
|
+
files.select { |f| File.mtime(f) >= stream.last_event }
|
19
|
+
when :cache
|
20
|
+
cache = stream.dirs[path] || {}
|
21
|
+
|
22
|
+
files.select do |f|
|
23
|
+
cached = cache[f]
|
24
|
+
|
25
|
+
cached.nil? or
|
26
|
+
|
27
|
+
File.mtime(f) != cached.mtime or
|
28
|
+
File.size(f) != cached.size
|
29
|
+
end
|
30
|
+
end
|
17
31
|
end
|
18
32
|
end
|
19
33
|
end
|
data/lib/fsevents/stream.rb
CHANGED
@@ -2,11 +2,13 @@ require 'fsevents/event'
|
|
2
2
|
|
3
3
|
module FSEvents
|
4
4
|
class Stream
|
5
|
-
attr_reader :stream, :last_event
|
5
|
+
attr_reader :stream, :mode, :last_event, :dirs
|
6
6
|
attr_reader :allocator, :context, :paths, :since, :latency, :flags, :callback
|
7
7
|
|
8
8
|
class StreamError < StandardError; end
|
9
9
|
|
10
|
+
MODES = [:mtime, :cache]
|
11
|
+
|
10
12
|
def initialize(*paths, &callback)
|
11
13
|
raise ArgumentError, 'A callback block is required' if callback.nil?
|
12
14
|
@callback = callback
|
@@ -14,11 +16,16 @@ module FSEvents
|
|
14
16
|
options = {}
|
15
17
|
options = paths.pop if paths.last.is_a?(Hash)
|
16
18
|
|
19
|
+
@mode = options[:mode] || :mtime
|
20
|
+
raise ArgumentError, "Mode '#{mode}' unknown" unless MODES.include?(@mode)
|
21
|
+
@dirs = {}
|
22
|
+
|
17
23
|
paths = Dir.pwd if paths.empty?
|
24
|
+
paths = [paths].flatten.collect { |path| path.sub(%r%/$%, '') }
|
18
25
|
|
19
26
|
@allocator = options[:allocator] || OSX::KCFAllocatorDefault
|
20
27
|
@context = options[:context] || nil
|
21
|
-
@paths =
|
28
|
+
@paths = paths
|
22
29
|
@since = options[:since] || OSX::KFSEventStreamEventIdSinceNow
|
23
30
|
@latency = options[:latency] || 1.0
|
24
31
|
@flags = options[:flags] || 0
|
@@ -53,7 +60,20 @@ module FSEvents
|
|
53
60
|
end
|
54
61
|
|
55
62
|
def update_last_event
|
56
|
-
|
63
|
+
case mode
|
64
|
+
when :mtime
|
65
|
+
@last_event = Time.now
|
66
|
+
when :cache
|
67
|
+
cache_paths = paths.dup
|
68
|
+
cache_paths.each do |path|
|
69
|
+
dirs[path] = {}
|
70
|
+
Dir["#{path}/*"].each do |file|
|
71
|
+
stat = File::Stat.new(file)
|
72
|
+
dirs[path][file] = stat
|
73
|
+
cache_paths.push(file) if stat.directory?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
57
77
|
end
|
58
78
|
|
59
79
|
def startup
|
data/lib/fsevents/version.rb
CHANGED
data/spec/event_spec.rb
CHANGED
@@ -34,6 +34,10 @@ describe FSEvents::Event do
|
|
34
34
|
FSEvents::Event.new(@id, @path, @stream).path.should == @path
|
35
35
|
end
|
36
36
|
|
37
|
+
it 'should strip a trailing / from the path' do
|
38
|
+
FSEvents::Event.new(@id, "#{@path}/", @stream).path.should == @path
|
39
|
+
end
|
40
|
+
|
37
41
|
it 'should store the stream' do
|
38
42
|
FSEvents::Event.new(@id, @path, @stream).stream.should == @stream
|
39
43
|
end
|
@@ -56,49 +60,147 @@ describe FSEvents::Event do
|
|
56
60
|
describe 'listing modified files' do
|
57
61
|
before :each do
|
58
62
|
@now = Time.now
|
59
|
-
@
|
60
|
-
@files = Array.new(5) do |i|
|
61
|
-
file = stub("file #{i+1}")
|
62
|
-
File.stubs(:mtime).with(file).returns(@now + i - 2)
|
63
|
-
file
|
64
|
-
end
|
63
|
+
@files = Array.new(5) { |i| stub("file #{i+1}") }
|
65
64
|
@event.stubs(:files).returns(@files)
|
66
65
|
end
|
67
|
-
|
68
|
-
it 'should
|
69
|
-
@
|
70
|
-
@event.modified_files
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should get the last event time from the stream' do
|
74
|
-
@stream.expects(:last_event).returns(@now)
|
66
|
+
|
67
|
+
it 'should check the stream mode' do
|
68
|
+
@stream.expects(:mode)
|
75
69
|
@event.modified_files
|
76
70
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
expected_files.each do |file|
|
83
|
-
modified_files.should include(file)
|
71
|
+
|
72
|
+
describe 'when the stream mode is mtime' do
|
73
|
+
before :each do
|
74
|
+
@stream.stubs(:mode).returns(:mtime)
|
84
75
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
76
|
+
|
77
|
+
before :each do
|
78
|
+
@stream.stubs(:last_event).returns(@now)
|
79
|
+
@files.each_with_index do |file, i|
|
80
|
+
File.stubs(:mtime).with(file).returns(@now + i - 2)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should get the file list' do
|
85
|
+
@event.expects(:files).returns(@files)
|
86
|
+
@event.modified_files
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should get the last event time from the stream' do
|
90
|
+
@stream.expects(:last_event).returns(@now)
|
91
|
+
@event.modified_files
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should return files modified after the last event time' do
|
95
|
+
expected_files = @files.values_at(3, 4)
|
96
|
+
modified_files = @event.modified_files
|
97
|
+
|
98
|
+
expected_files.each do |file|
|
99
|
+
modified_files.should include(file)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should return files modified at the last event time' do
|
104
|
+
expected_files = @files.values_at(2)
|
105
|
+
modified_files = @event.modified_files
|
106
|
+
|
107
|
+
expected_files.each do |file|
|
108
|
+
modified_files.should include(file)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should not return files not modified after the last event time' do
|
113
|
+
unexpected_files = @files.values_at(0, 1)
|
114
|
+
modified_files = @event.modified_files
|
115
|
+
|
116
|
+
unexpected_files.each do |file|
|
117
|
+
modified_files.should_not include(file)
|
118
|
+
end
|
93
119
|
end
|
94
120
|
end
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
121
|
+
|
122
|
+
describe 'when the stream mode is cache' do
|
123
|
+
before :each do
|
124
|
+
@stream.stubs(:mode).returns(:cache)
|
125
|
+
end
|
126
|
+
|
127
|
+
before :each do
|
128
|
+
@dir_cache = { @path => {} }
|
129
|
+
@files.each_with_index do |file, i|
|
130
|
+
size = 50 * (i + 1)
|
131
|
+
mtime = @now + i - 2
|
132
|
+
File.stubs(:size).with(file).returns(size)
|
133
|
+
File.stubs(:mtime).with(file).returns(mtime)
|
134
|
+
stat = stub("file #{i+1} stat", :mtime => mtime, :size => size)
|
135
|
+
@dir_cache[@path][file] = stat
|
136
|
+
end
|
137
|
+
@stream.stubs(:dirs).returns(@dir_cache)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should get the file list' do
|
141
|
+
@event.expects(:files).returns(@files)
|
142
|
+
@event.modified_files
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should get the stream dir cache' do
|
146
|
+
@stream.expects(:dirs).returns(@dir_cache)
|
147
|
+
@event.modified_files
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should get the dir cache for the event path' do
|
151
|
+
sub_cache = @dir_cache[@path]
|
152
|
+
@dir_cache.expects(:[]).with(@path).returns(sub_cache)
|
153
|
+
@event.modified_files
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should return files that do not appear in the cache' do
|
157
|
+
expected_files = Array.new(2) { |i| stub("new file #{i+1}") }
|
158
|
+
expected_files.each { |file| @files.push(file) }
|
159
|
+
modified_files = @event.modified_files
|
160
|
+
|
161
|
+
expected_files.each do |file|
|
162
|
+
modified_files.should include(file)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should return files with sizes that differ from the cache' do
|
167
|
+
@dir_cache[@path][@files[3]].stubs(:size).returns(3)
|
168
|
+
@dir_cache[@path][@files[4]].stubs(:size).returns(101)
|
169
|
+
expected_files = @files.values_at(3, 4)
|
170
|
+
modified_files = @event.modified_files
|
171
|
+
|
172
|
+
expected_files.each do |file|
|
173
|
+
modified_files.should include(file)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should return files with mtimes that differ from the cache' do
|
178
|
+
@dir_cache[@path][@files[2]].stubs(:mtime).returns(@now - 234)
|
179
|
+
expected_files = @files.values_at(2)
|
180
|
+
modified_files = @event.modified_files
|
181
|
+
|
182
|
+
expected_files.each do |file|
|
183
|
+
modified_files.should include(file)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should not return files not modified from the cache' do
|
188
|
+
unexpected_files = @files.values_at(0, 1)
|
189
|
+
modified_files = @event.modified_files
|
190
|
+
|
191
|
+
unexpected_files.each do |file|
|
192
|
+
modified_files.should_not include(file)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should handle this path not yet cached' do
|
197
|
+
@dir_cache.delete(@path)
|
198
|
+
expected_files = @files
|
199
|
+
modified_files = @event.modified_files
|
200
|
+
|
201
|
+
expected_files.each do |file|
|
202
|
+
modified_files.should include(file)
|
203
|
+
end
|
102
204
|
end
|
103
205
|
end
|
104
206
|
end
|
data/spec/stream_spec.rb
CHANGED
@@ -50,6 +50,7 @@ describe FSEvents::Stream do
|
|
50
50
|
[:allocator, :context, :since, :latency, :flags].each do |opt|
|
51
51
|
@options[opt] = stub(opt.to_s)
|
52
52
|
end
|
53
|
+
@options[:mode] = :cache
|
53
54
|
@other_path = '/other/path'
|
54
55
|
end
|
55
56
|
|
@@ -87,6 +88,10 @@ describe FSEvents::Stream do
|
|
87
88
|
FSEvents::Stream.new(@options) {}.paths.should == [Dir.pwd]
|
88
89
|
end
|
89
90
|
|
91
|
+
it 'should strip a trailing slash from the path' do
|
92
|
+
FSEvents::Stream.new("#{@path}/", "#{@other_path}/", @options) {}.paths.should == [@path, @other_path]
|
93
|
+
end
|
94
|
+
|
90
95
|
it "should store 'since' (event ID)" do
|
91
96
|
FSEvents::Stream.new(@path, @options) {}.since.should == @options[:since]
|
92
97
|
end
|
@@ -113,6 +118,19 @@ describe FSEvents::Stream do
|
|
113
118
|
@options.delete(:flags)
|
114
119
|
FSEvents::Stream.new(@path, @options) {}.flags.should == 0
|
115
120
|
end
|
121
|
+
|
122
|
+
it 'should store mode' do
|
123
|
+
FSEvents::Stream.new(@path, @options) {}.mode.should == @options[:mode]
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should default mode to mtime' do
|
127
|
+
@options.delete(:mode)
|
128
|
+
FSEvents::Stream.new(@path, @options) {}.mode.should == :mtime
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should not accept any mode other than mtime or cache' do
|
132
|
+
lambda { FSEvents::Stream.new(@path, @options.merge(:mode => :something_else)) {} }.should raise_error(ArgumentError)
|
133
|
+
end
|
116
134
|
end
|
117
135
|
end
|
118
136
|
|
@@ -272,7 +290,7 @@ describe FSEvents::Stream do
|
|
272
290
|
|
273
291
|
it 'should extend the event array' do
|
274
292
|
@args = @args_hash.values_at(*@callback_arg_order)
|
275
|
-
@callback.expects(:call).with
|
293
|
+
@callback.expects(:call).with(kind_of(EventArray))
|
276
294
|
@proc.call(*@args)
|
277
295
|
end
|
278
296
|
|
@@ -405,11 +423,101 @@ describe FSEvents::Stream do
|
|
405
423
|
end
|
406
424
|
|
407
425
|
describe 'updating its last event' do
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
426
|
+
describe 'when mode is mtime' do
|
427
|
+
before :each do
|
428
|
+
@stream.stubs(:mode).returns(:mtime)
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'should store the last event time' do
|
432
|
+
now = Time.now
|
433
|
+
Time.stubs(:now).returns(now)
|
434
|
+
@stream.update_last_event
|
435
|
+
@stream.last_event.should == now
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
describe 'when mode is cache' do
|
440
|
+
before :each do
|
441
|
+
@stream.stubs(:mode).returns(:cache)
|
442
|
+
@files = Array.new(5) { |i| stub("file #{i+1}") }
|
443
|
+
@stats = Array.new(5) { |i| stub("file #{i+1} stat", :directory? => false) }
|
444
|
+
|
445
|
+
@files.zip(@stats).each do |file, stat|
|
446
|
+
File::Stat.stubs(:new).with(file).returns(stat)
|
447
|
+
end
|
448
|
+
|
449
|
+
Dir.stubs(:[]).returns(@files)
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'should get the contents of its path' do
|
453
|
+
Dir.expects(:[]).with("#{@path}/*").returns([])
|
454
|
+
@stream.update_last_event
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'should get stat objects for the path contents' do
|
458
|
+
@files.zip(@stats).each do |file, stat|
|
459
|
+
File::Stat.expects(:new).with(file).returns(stat)
|
460
|
+
end
|
461
|
+
@stream.update_last_event
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'should cache the stat objects' do
|
465
|
+
@stream.update_last_event
|
466
|
+
|
467
|
+
@files.zip(@stats).each do |file, stat|
|
468
|
+
@stream.dirs[@path][file].should == stat
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'should update already-existent cache entries' do
|
473
|
+
file = @files[3]
|
474
|
+
val = @stats[3]
|
475
|
+
@stream.dirs[@path] = { file => 'some other val' }
|
476
|
+
|
477
|
+
@stream.update_last_event
|
478
|
+
@stream.dirs[@path][file].should == val
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'should remove non-needed cache entries' do
|
482
|
+
file = 'some other file'
|
483
|
+
@stream.dirs[@path] = { file => 'some val' }
|
484
|
+
|
485
|
+
@stream.update_last_event
|
486
|
+
@stream.dirs[@path].should_not have_key(file)
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'should handle multiple paths' do
|
490
|
+
other_path = '/other/path'
|
491
|
+
paths = [@path, other_path]
|
492
|
+
@stream.stubs(:paths).returns(paths)
|
493
|
+
|
494
|
+
paths.each do |path|
|
495
|
+
Dir.expects(:[]).with("#{path}/*").returns([])
|
496
|
+
end
|
497
|
+
@stream.update_last_event
|
498
|
+
end
|
499
|
+
|
500
|
+
it 'should see if there are any subdirectories' do
|
501
|
+
@stats.each { |stat| stat.expects(:directory?) }
|
502
|
+
@stream.update_last_event
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'should cache subdirectories' do
|
506
|
+
subdir_path = '/sub/dir/path'
|
507
|
+
@files[3].stubs(:to_s).returns(subdir_path)
|
508
|
+
@stats[3].stubs(:directory?).returns(true)
|
509
|
+
Dir.expects(:[]).with("#{subdir_path}/*").returns([])
|
510
|
+
@stream.update_last_event
|
511
|
+
end
|
512
|
+
|
513
|
+
it 'should not add cached subdirectories to the watched paths' do
|
514
|
+
subdir_path = '/sub/dir/path'
|
515
|
+
@files[3].stubs(:to_s).returns(subdir_path)
|
516
|
+
@stats[3].stubs(:directory?).returns(true)
|
517
|
+
Dir.stubs(:[]).with("#{subdir_path}/*").returns([])
|
518
|
+
@stream.update_last_event
|
519
|
+
@stream.paths.should_not include(@files[3])
|
520
|
+
end
|
413
521
|
end
|
414
522
|
end
|
415
523
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fsevents
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yossef Mendelssohn
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-06-
|
12
|
+
date: 2008-06-30 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
76
|
requirements: []
|
77
77
|
|
78
78
|
rubyforge_project: yomendel
|
79
|
-
rubygems_version: 1.
|
79
|
+
rubygems_version: 1.2.0
|
80
80
|
signing_key:
|
81
81
|
specification_version: 2
|
82
82
|
summary: Simple, usable FSEvents API
|