eventmachine-tail 0.6.4 → 0.6.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 96b3872cfc6a1c388aa9ee4d8f67ae2b69cc7a9ae5fdbb39449a9a293702fb37
4
+ data.tar.gz: c268790026a0f65cff3d521dcd366db1ab848f685f4ccd9aa69c024bb09bf406
5
+ SHA512:
6
+ metadata.gz: 15956a61dd9222dd09147623d7b8357850d2995ed5a270039b326e623d33916ca1fd2321920a1334ce5a4398a0db49e4134f32dacb1148153cb133cc202281da
7
+ data.tar.gz: 507f39f080224e3784e23bcaf1ba8c680f54d47e277fd134c8becb30089aa33b415eb4e9e0cbe035ec30c8d3a8598ca1505d715a63ec40295fc376c329e5abdc
data/lib/em/filetail.rb CHANGED
@@ -35,10 +35,10 @@ EventMachine.kqueue = true if EventMachine.kqueue?
35
35
  # See also: EventMachine::FileTail#receive_data
36
36
  class EventMachine::FileTail
37
37
  # Maximum size to read at a time from a single file.
38
- CHUNKSIZE = 65536
38
+ CHUNKSIZE = 65536
39
39
 
40
40
  #MAXSLEEP = 2
41
-
41
+
42
42
  FORCE_ENCODING = !! (defined? Encoding)
43
43
 
44
44
  # The path of the file being tailed
@@ -46,7 +46,7 @@ class EventMachine::FileTail
46
46
 
47
47
  # The current file read position
48
48
  attr_reader :position
49
-
49
+
50
50
  # If this tail is closed
51
51
  attr_reader :closed
52
52
 
@@ -57,11 +57,11 @@ class EventMachine::FileTail
57
57
  # Check interval for looking for a file if we are tailing it and it has
58
58
  # gone missing.
59
59
  attr_accessor :missing_file_check_interval
60
-
60
+
61
61
  # Tail a file
62
62
  #
63
63
  # * path is a string file path to tail
64
- # * startpos is an offset to start tailing the file at. If -1, start at end of
64
+ # * startpos is an offset to start tailing the file at. If -1, start at end of
65
65
  # file.
66
66
  #
67
67
  # If you want debug messages, run ruby with '-d' or set $DEBUG
@@ -102,6 +102,8 @@ class EventMachine::FileTail
102
102
 
103
103
  EventMachine::next_tick do
104
104
  open
105
+ next unless @file
106
+
105
107
  if (startpos == -1)
106
108
  @position = @file.sysseek(0, IO::SEEK_END)
107
109
  # TODO(sissel): if we don't have inotify or kqueue, should we
@@ -116,7 +118,7 @@ class EventMachine::FileTail
116
118
  end # EventMachine::next_tick
117
119
  end # def initialize
118
120
 
119
- # This method is called when a tailed file has data read.
121
+ # This method is called when a tailed file has data read.
120
122
  #
121
123
  # * data - string data read from the file.
122
124
  #
@@ -132,7 +134,7 @@ class EventMachine::FileTail
132
134
  # @buffer.extract(data).each do |line|
133
135
  # # do something with 'line'
134
136
  # end
135
- # end
137
+ # end
136
138
  public
137
139
  def receive_data(data)
138
140
  if @handler # FileTail.new called with a block
@@ -171,7 +173,6 @@ class EventMachine::FileTail
171
173
  schedule_next_read
172
174
  elsif status == :moved
173
175
  # read to EOF, then reopen.
174
- @reopen_on_eof = true
175
176
  schedule_next_read
176
177
  elsif status == :unbind
177
178
  # :unbind is called after the :deleted handler
@@ -186,7 +187,7 @@ class EventMachine::FileTail
186
187
  def open
187
188
  return if @closed
188
189
  @file.close if @file && !@file.closed?
189
- return unless File.exists?(@path)
190
+ return unless File.exist?(@path)
190
191
  begin
191
192
  @logger.debug "Opening file #{@path}"
192
193
  @file = File.open(@path, "r")
@@ -214,19 +215,19 @@ class EventMachine::FileTail
214
215
  @file.close if @file
215
216
  end
216
217
  end # def close
217
-
218
+
218
219
  # More rubyesque way of checking if this tail is closed
219
220
  public
220
221
  def closed?
221
222
  @closed
222
223
  end
223
-
224
+
224
225
  # Watch our file.
225
226
  private
226
227
  def watch
227
228
  @watch.stop_watching if @watch
228
229
  @symlink_timer.cancel if @symlink_timer
229
- return unless File.exists?(@path)
230
+ return unless File.exist?(@path)
230
231
 
231
232
  @logger.debug "Starting watch on #{@path}"
232
233
  callback = proc { |what| notify(what) }
@@ -354,19 +355,19 @@ class EventMachine::FileTail
354
355
  def read_file_metadata(&block)
355
356
  begin
356
357
  filestat = File.stat(@path)
358
+ symlink_stat = nil
359
+ symlink_target = nil
360
+
361
+ if filestat.symlink?
362
+ symlink_stat = File.lstat(@path) rescue nil
363
+ symlink_target = File.readlink(@path) rescue nil
364
+ end
357
365
  rescue Errno::ENOENT
358
366
  raise
359
367
  rescue => e
360
368
  @logger.debug("File stat on '#{@path}' failed")
361
369
  on_exception e
362
370
  end
363
- symlink_stat = nil
364
- symlink_target = nil
365
-
366
- if File.symlink?(@path)
367
- symlink_stat = File.lstat(@path) rescue nil
368
- symlink_target = File.readlink(@path) rescue nil
369
- end
370
371
 
371
372
  if block_given?
372
373
  yield filestat, symlink_stat, symlink_target
@@ -393,7 +394,7 @@ class EventMachine::FileTail
393
394
  @logger.debug "Symlink target changed. Reopening..."
394
395
  @reopen_on_eof = true
395
396
  schedule_next_read
396
- end
397
+ end
397
398
  elsif (filestat.ino != @filestat.ino or filestat.rdev != @filestat.rdev)
398
399
  @logger.debug "Inode or device changed. Reopening..."
399
400
  @logger.debug filestat
@@ -81,6 +81,15 @@ class EventMachine::FileGlobWatch
81
81
  "module?")
82
82
  end # def file_found
83
83
 
84
+ # This method is called when a file is modified.
85
+ #
86
+ # * path - The string path of the file modified
87
+ #
88
+ # You AREN'T required to implement this in your sublcass or module
89
+ public
90
+ def file_modified(path)
91
+ end
92
+
84
93
  # This method is called when a file is deleted.
85
94
  #
86
95
  # * path - the string path of the file deleted
@@ -104,7 +113,10 @@ class EventMachine::FileGlobWatch
104
113
  fileinfo = FileInfo.new(path) rescue next
105
114
  # Skip files that have the same inode (renamed or hardlinked)
106
115
  known_files.delete(fileinfo.stat.ino)
107
- next if @files.include?(fileinfo.stat.ino)
116
+ if @files.include?(fileinfo.stat.ino)
117
+ next unless modified(fileinfo)
118
+ file_modified(path)
119
+ end
108
120
 
109
121
  track(fileinfo)
110
122
  file_found(path)
@@ -144,6 +156,11 @@ class EventMachine::FileGlobWatch
144
156
  #end
145
157
  end # def watch
146
158
 
159
+ # Tells if a file has been modified since last time
160
+ def modified(fileinfo)
161
+ not @files[fileinfo.stat.ino].stat.mtime == fileinfo.stat.mtime
162
+ end
163
+
147
164
  private
148
165
  class FileWatcher < EventMachine::FileWatch
149
166
  def initialize(globwatch, &block)
data/samples/globwatch.rb CHANGED
@@ -16,6 +16,10 @@ class Watcher < EventMachine::FileGlobWatch
16
16
  def file_found(path)
17
17
  puts "Found: #{path}"
18
18
  end
19
+
20
+ def file_modified(path)
21
+ puts "Modified: #{path}"
22
+ end
19
23
  end # class Watcher
20
24
 
21
25
  EM.run do
@@ -26,11 +26,11 @@ class Reader < EventMachine::FileTail
26
26
  @buffer.extract(data).each do |line|
27
27
  @lineno += 1
28
28
  expected = @data.shift
29
- @testobj.assert_equal(expected, line,
29
+ @testobj.assert_equal(expected, line,
30
30
  "Expected '#{expected}' on line #{@lineno}, but got '#{line}'")
31
31
  end # @buffer.extract
32
32
  end # def receive_data
33
-
33
+
34
34
  # This effectively tests EOF handling by requiring it to work in order
35
35
  # for the tests to pass.
36
36
  def eof
@@ -63,6 +63,23 @@ class TestFileTail < Test::Unit::TestCase
63
63
  end # EM.run
64
64
  end # def test_filetail
65
65
 
66
+ def test_filetail_close
67
+ tmp = Tempfile.new("testfiletail")
68
+ data = DATA.clone
69
+ data.each { |i| tmp.puts i }
70
+ tmp.flush
71
+
72
+ EM.run do
73
+ abort_after_timeout(2)
74
+
75
+ ft = EM::file_tail(tmp.path, Reader, -1, self)
76
+ ft.close
77
+ timer = EM::PeriodicTimer.new(0.2) do
78
+ timer.cancel and finish if ft.closed?
79
+ end
80
+ end # EM.run
81
+ end # def test_filetail_close
82
+
66
83
  def test_filetail_with_seek
67
84
  tmp = Tempfile.new("testfiletail")
68
85
  data = DATA.clone
@@ -86,7 +103,7 @@ class TestFileTail < Test::Unit::TestCase
86
103
  EM::file_tail(tmp.path) do |filetail, line|
87
104
  lineno += 1
88
105
  expected = data.shift
89
- assert_equal(expected, line,
106
+ assert_equal(expected, line,
90
107
  "Expected '#{expected}' on line #{@lineno}, but got '#{line}'")
91
108
  finish if data.length == 0
92
109
  end
@@ -120,7 +137,7 @@ class TestFileTail < Test::Unit::TestCase
120
137
  lineno += 1
121
138
  expected = data.shift
122
139
  #puts "Got #{lineno}: #{line}"
123
- assert_equal(expected, line,
140
+ assert_equal(expected, line,
124
141
  "Expected '#{expected}' on line #{lineno}, but got '#{line}'")
125
142
  finish if data.length == 0
126
143
 
@@ -176,7 +193,7 @@ class TestFileTail < Test::Unit::TestCase
176
193
  lineno += 1
177
194
  expected = data.shift
178
195
  puts "Got #{lineno}: #{line}" if $debug
179
- assert_equal(expected, line,
196
+ assert_equal(expected, line,
180
197
  "Expected '#{expected}' on line #{lineno}, but got '#{line}'")
181
198
  finish if data.length == 0
182
199
 
@@ -208,7 +225,7 @@ class TestFileTail < Test::Unit::TestCase
208
225
  File.delete(f.path)
209
226
  end
210
227
  end # def test_filetail_tracks_renames
211
-
228
+
212
229
  def test_encoding
213
230
  return if RUBY_VERSION < '1.9.0'
214
231
  tmp = Tempfile.new("testfiletail")
@@ -217,7 +234,7 @@ class TestFileTail < Test::Unit::TestCase
217
234
  abort_after_timeout(1)
218
235
 
219
236
  EM::file_tail(tmp.path) do |filetail, line|
220
- assert_equal(Encoding.default_external, line.encoding,
237
+ assert_equal(Encoding.default_external, line.encoding,
221
238
  "Expected the read data to have the encoding specified in Encoding.default_external (#{Encoding.default_external}, but was #{line.encoding})")
222
239
  finish
223
240
  end
data/test/test_glob.rb CHANGED
@@ -32,6 +32,26 @@ class Watcher < EventMachine::FileGlobWatch
32
32
  end
33
33
  end # class Reader
34
34
 
35
+ class ModificationWatcher < EventMachine::FileGlobWatch
36
+ def initialize(path, interval, data, testobj)
37
+ super(path, interval)
38
+ @data = data
39
+ @testobj = testobj
40
+ end # def initialize
41
+
42
+ def file_found(path)
43
+ end
44
+
45
+ def file_deleted(patH)
46
+ end
47
+
48
+ def file_modified(path)
49
+ @testobj.assert(@data.include?(path), "Expected #{path} in \n#{@data.join("\n")}")
50
+ @data.delete(path)
51
+ @testobj.finish if @data.length == 0
52
+ end
53
+ end # class ModificationWatcher
54
+
35
55
  class TestGlobWatcher < Test::Unit::TestCase
36
56
  include EventMachineTailTestHelpers
37
57
  SLEEPMAX = 1
@@ -142,5 +162,29 @@ class TestGlobWatcher < Test::Unit::TestCase
142
162
  end
143
163
  end
144
164
  end # def test_glob_ignores_file_renames
165
+
166
+ def test_glob_finds_modified_files_at_runtime
167
+ EM.run do
168
+ abort_after_timeout(SLEEPMAX * @data.length + 10)
169
+
170
+ datacopy = @data.clone
171
+
172
+ # To test if file edit is detected, file must exist first
173
+ datacopy.each do |filename|
174
+ File.new(filename, "w").close
175
+ end
176
+
177
+ EM::watch_glob("#{@dir}/*", ModificationWatcher, @watchinterval, @data.clone, self)
178
+
179
+ sleep(2) # Modification time resolution is 1 second, so we need to allow mtimes to change
180
+ timer = EM::PeriodicTimer.new(0.2) do
181
+ File.open(datacopy.shift, "w") do |f|
182
+ f.puts("LOLCAT!")
183
+ end
184
+ sleep(rand * SLEEPMAX)
185
+ timer.cancel if datacopy.length == 0
186
+ end
187
+ end # EM.run
188
+ end # def test_glob_finds_modified_files_at_runtime
145
189
  end # class TestGlobWatcher
146
190
 
metadata CHANGED
@@ -1,77 +1,84 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventmachine-tail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
5
- prerelease:
4
+ version: 0.6.6
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jordan Sissel
9
- autorequire:
10
8
  bindir: bin
11
9
  cert_chain: []
12
- date: 2012-10-23 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
13
11
  dependencies:
14
12
  - !ruby/object:Gem::Dependency
15
13
  name: eventmachine
16
14
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
15
  requirements:
19
- - - ! '>='
16
+ - - ">="
20
17
  - !ruby/object:Gem::Version
21
18
  version: '0'
22
19
  type: :runtime
23
20
  prerelease: false
24
21
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
22
  requirements:
27
- - - ! '>='
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: test-unit
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
28
38
  - !ruby/object:Gem::Version
29
39
  version: '0'
30
40
  description: Add file 'tail' implemented with EventMachine. Also includes a 'glob
31
41
  watch' class for watching a directory pattern for new matches, like /var/log/*.log
32
42
  email: jls@semicomplete.com
33
43
  executables:
34
- - rtail
35
44
  - emtail
45
+ - rtail
36
46
  extensions: []
37
47
  extra_rdoc_files: []
38
48
  files:
39
- - lib/eventmachine-tail.rb
40
- - lib/em/globwatcher.rb
49
+ - bin/emtail
50
+ - bin/rtail
41
51
  - lib/em/filetail.rb
42
- - samples/tail.rb
52
+ - lib/em/globwatcher.rb
53
+ - lib/eventmachine-tail.rb
54
+ - samples/glob-tail.rb
43
55
  - samples/globwatch.rb
44
56
  - samples/tail-with-block.rb
45
- - samples/glob-tail.rb
57
+ - samples/tail.rb
58
+ - test/alltests.rb
46
59
  - test/test_filetail.rb
47
60
  - test/test_glob.rb
48
61
  - test/testcase_helpers.rb
49
- - test/alltests.rb
50
- - bin/emtail
51
- - bin/rtail
52
62
  homepage: http://code.google.com/p/semicomplete/wiki/EventMachineTail
53
- licenses: []
54
- post_install_message:
63
+ licenses:
64
+ - BSD-3-Clause
65
+ metadata: {}
55
66
  rdoc_options: []
56
67
  require_paths:
57
68
  - lib
58
69
  - lib
59
70
  required_ruby_version: !ruby/object:Gem::Requirement
60
- none: false
61
71
  requirements:
62
- - - ! '>='
72
+ - - ">="
63
73
  - !ruby/object:Gem::Version
64
74
  version: '0'
65
75
  required_rubygems_version: !ruby/object:Gem::Requirement
66
- none: false
67
76
  requirements:
68
- - - ! '>='
77
+ - - ">="
69
78
  - !ruby/object:Gem::Version
70
79
  version: '0'
71
80
  requirements: []
72
- rubyforge_project:
73
- rubygems_version: 1.8.24
74
- signing_key:
75
- specification_version: 3
81
+ rubygems_version: 3.6.7
82
+ specification_version: 4
76
83
  summary: eventmachine tail - a file tail implementation with glob support
77
84
  test_files: []