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.
@@ -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:
@@ -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
- files.select { |f| File.mtime(f) >= stream.last_event }
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
@@ -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 = [paths].flatten
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
- @last_event = Time.now
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
@@ -1,8 +1,8 @@
1
1
  module Fsevents #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 0
5
- TINY = 2
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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
- @stream.stubs(:last_event).returns(@now)
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 get the file list' do
69
- @event.expects(:files).returns(@files)
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
- it 'should return files modified after the last event time' do
79
- expected_files = @files.values_at(3, 4)
80
- modified_files = @event.modified_files
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
- end
86
-
87
- it 'should return files modified at the last event time' do
88
- expected_files = @files.values_at(2)
89
- modified_files = @event.modified_files
90
-
91
- expected_files.each do |file|
92
- modified_files.should include(file)
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
- it 'should not return files not modified after the last event time' do
97
- unexpected_files = @files.values_at(0, 1)
98
- modified_files = @event.modified_files
99
-
100
- unexpected_files.each do |file|
101
- modified_files.should_not include(file)
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
@@ -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 { |events| events.is_a?(EventArray) }
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
- it 'should store the last event time' do
409
- now = Time.now
410
- Time.stubs(:now).returns(now)
411
- @stream.update_last_event
412
- @stream.last_event.should == now
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.2
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-17 00:00:00 -05:00
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.1.1
79
+ rubygems_version: 1.2.0
80
80
  signing_key:
81
81
  specification_version: 2
82
82
  summary: Simple, usable FSEvents API