eventmachine-tail 0.1.20100506012705 → 0.2.20100516235116

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rtail CHANGED
@@ -2,31 +2,75 @@
2
2
  require "rubygems"
3
3
  require "eventmachine"
4
4
  require "eventmachine-tail"
5
+ require "optparse"
5
6
 
6
7
  class Reader < EventMachine::FileTail
7
- def initialize(path, startpos=-1)
8
+ def initialize(path, startpos=-1, with_filenames=true)
8
9
  super(path, startpos)
9
10
  @buffer = BufferedTokenizer.new
11
+ @with_filenames = with_filenames
10
12
  end
11
13
 
12
14
  def receive_data(data)
13
15
  @buffer.extract(data).each do |line|
14
- puts "#{path}: #{line}"
15
- end
16
- end
17
- end
16
+ if @with_filenames # global flag, see the '-n' option
17
+ puts "#{path}: #{line}"
18
+ else
19
+ puts line
20
+ end # if @with_filenames
21
+ end # buffer extract
22
+ end # def receive_data
23
+ end # class Reader
24
+
25
+ def pattern_to_regexp(pattern)
26
+ pattern.gsub!(".", "\\.") # fix literal .
27
+ pattern.gsub!("*", ".+") # * becomes .+
28
+ pattern.gsub!("?", ".") # ? becomes .
29
+ return Regexp.new(pattern)
30
+ end # def pattern_to_regexp
18
31
 
19
32
  def main(args)
33
+ with_filenames = true
34
+ globcheck_interval = 5
35
+ exclude_patterns = []
36
+
37
+ opts = OptionParser.new do |opts|
38
+ opts.banner = "Usage: #{$0} [options] <path_or_glob> [path_or_glob2] [...]"
39
+
40
+ opts.on("-n", "--no-filename",
41
+ "Supress prefixing of output with file names") do |x|
42
+ with_filenames = false
43
+ end # -n
44
+
45
+ opts.on("-i SECONDS", "--check-interval SECONDS",
46
+ "How frequently, in seconds, to check the glob patterns" \
47
+ "for new files") do |x|
48
+ globcheck_interval = x.to_f
49
+ end # -i SECONDS
50
+
51
+ opts.on("-x EXCLUDE", "--exclude EXCLUDE",
52
+ "A pattern to ignore. Wildcard/globs accepted." \
53
+ " Can be specified multiple times") do |pattern|
54
+ exclude_patterns << pattern_to_regexp(pattern)
55
+ end
56
+ end # OptionParser
57
+
58
+ opts.parse!(args)
59
+
20
60
  if args.length == 0
21
- puts "Usage: #{$0} <path_or_glob> [path_or_glob2] [...]"
61
+ puts opts.banner
22
62
  return 1
23
63
  end
24
64
 
25
65
  EventMachine.run do
26
66
  args.each do |path|
27
- EventMachine::FileGlobWatchTail.new(path, Reader)
28
- end
29
- end
67
+ EventMachine::FileGlobWatchTail.new(path, Reader,
68
+ interval = globcheck_interval,
69
+ exclude = exclude_patterns,
70
+ start_pos = -1,
71
+ with_filenames = with_filenames)
72
+ end # args.each
73
+ end # EventMachine.run
30
74
  end # def main
31
75
 
32
76
  exit(main(ARGV))
data/lib/em/filetail.rb CHANGED
@@ -43,6 +43,7 @@ class EventMachine::FileTail
43
43
  @logger.level = ($DEBUG and Logger::DEBUG or Logger::WARN)
44
44
  @logger.debug("Tailing #{path} starting at position #{startpos}")
45
45
 
46
+ @file = nil
46
47
  @fstat = File.stat(@path)
47
48
 
48
49
  if @fstat.directory?
@@ -62,7 +62,6 @@ class EventMachine::FileGlobWatch
62
62
 
63
63
  private
64
64
  def add(path)
65
- @logger.info "Watching #{path}"
66
65
  @files.add(path)
67
66
 
68
67
  # If EventMachine::watch_file fails, that's ok, I guess.
@@ -97,15 +96,26 @@ class EventMachine::FileGlobWatch
97
96
  end # class EventMachine::FileGlobWatch
98
97
 
99
98
  class EventMachine::FileGlobWatchTail < EventMachine::FileGlobWatch
100
- def initialize(path, handler=nil, interval=60, *args)
99
+ def initialize(path, handler=nil, interval=60, exclude=[], *args)
101
100
  super(path, interval)
102
101
  @handler = handler
103
102
  @args = args
103
+ @exclude = exclude
104
104
  end
105
105
 
106
106
  def file_found(path)
107
107
  begin
108
- EventMachine::file_tail(path, @handler)
108
+ @logger.info "#{self.class}: Trying #{path}"
109
+ @exclude.each do |exclude|
110
+ @logger.info "#{self.class}: Testing #{exclude} =~ #{path} == #{exclude.match(path) != nil}"
111
+ if exclude.match(path) != nil
112
+ file_excluded(path)
113
+ return
114
+ end
115
+ end
116
+ @logger.info "#{self.class}: Watching #{path}"
117
+
118
+ EventMachine::file_tail(path, @handler, *@args)
109
119
  rescue Errno::EACCES => e
110
120
  file_error(path, e)
111
121
  rescue Errno::EISDIR => e
@@ -113,13 +123,17 @@ class EventMachine::FileGlobWatchTail < EventMachine::FileGlobWatch
113
123
  end
114
124
  end
115
125
 
126
+ def file_excluded(path)
127
+ @logger.info "#{self.class}: Skipping path #{path} due to exclude rule"
128
+ end
129
+
116
130
  def file_removed(path)
117
131
  # Nothing to do
118
132
  end
119
133
 
120
134
  def file_error(path, e)
121
135
  $stderr.puts "#{e.class} while trying to tail #{path}"
122
- # Ignore by default
136
+ # otherwise, drop the error by default
123
137
  end
124
138
  end # class EventMachine::FileGlobWatchHandler
125
139
 
@@ -133,4 +147,14 @@ module EventMachine
133
147
  yield c if block_given?
134
148
  return c
135
149
  end
136
- end
150
+
151
+ def self.watch_glob(path, handler=nil, *args)
152
+ # This code mostly styled on what EventMachine does in many of it's other
153
+ # methods.
154
+ args = [path, *args]
155
+ klass = klass_from_handler(EventMachine::FileGlobWatch, handler, *args);
156
+ c = klass.new(*args)
157
+ yield c if block_given?
158
+ return c
159
+ end # def EventMachine::watch_glob
160
+ end # module EventMachine
@@ -6,6 +6,7 @@ require 'eventmachine-tail'
6
6
  require 'tempfile'
7
7
  require 'test/unit'
8
8
  require 'timeout'
9
+ require 'testcase_helpers.rb'
9
10
 
10
11
 
11
12
  # Generate some data
@@ -27,20 +28,13 @@ class Reader < EventMachine::FileTail
27
28
  expected = @data.shift
28
29
  @testobj.assert_equal(expected, line,
29
30
  "Expected '#{expected}' on line #{@lineno}, but got '#{line}'")
30
- if @data.length == 0
31
- EM.stop_event_loop
32
- end
31
+ @testobj.finish if @data.length == 0
33
32
  end # @buffer.extract
34
33
  end # def receive_data
35
34
  end # class Reader
36
35
 
37
36
  class TestFileTail < Test::Unit::TestCase
38
- def abort_after_timeout(seconds)
39
- EM::Timer.new(seconds) do
40
- EM.stop_event_loop
41
- flunk("Timeout (#{seconds} seconds) while running tests. Failing.")
42
- end
43
- end
37
+ include EventMachineTailTestHelpers
44
38
 
45
39
  # This test should run slow. We are trying to ensure that
46
40
  # our file_tail correctly reads data slowly fed into the file
data/test/test_glob.rb ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
5
+ require 'eventmachine-tail'
6
+ require 'tempfile'
7
+ require 'test/unit'
8
+ require 'timeout'
9
+ require 'tmpdir'
10
+
11
+ require 'testcase_helpers'
12
+
13
+ class Watcher < EventMachine::FileGlobWatch
14
+ def initialize(path, interval, data, testobj)
15
+ super(path, interval)
16
+ @data = data
17
+ @testobj = testobj
18
+ end # def initialize
19
+
20
+ def file_found(path)
21
+ # Use .include? here because files aren't going to be found in any
22
+ # particular order.
23
+ @testobj.assert(@data.include?(path), "Expected #{path} in \n#{@data.join("\n")}")
24
+ @data.delete(path)
25
+ @testobj.finish if @data.length == 0
26
+ end
27
+
28
+ def file_removed(path)
29
+ @testobj.assert(@data.include?(path), "Expected #{path} in \n#{@data.join("\n")}")
30
+ @data.delete(path)
31
+ @testobj.finish if @data.length == 0
32
+ end
33
+ end # class Reader
34
+
35
+ class TestGlobWatcher < Test::Unit::TestCase
36
+ include EventMachineTailTestHelpers
37
+ SLEEPMAX = 2
38
+
39
+ def setup
40
+ @watchinterval = 0.2
41
+ @dir = Dir.mktmpdir
42
+ @data = []
43
+ @data << "#{@dir}/#{rand}"
44
+ @data << "#{@dir}/#{rand}"
45
+ @data << "#{@dir}/#{rand}"
46
+ @data << "#{@dir}/#{rand}"
47
+ @data << "#{@dir}/#{rand}"
48
+ @data << "#{@dir}/#{rand}"
49
+ @data << "#{@dir}/#{rand}"
50
+ @data << "#{@dir}/#{rand}.gz"
51
+ @data << "#{@dir}/#{rand}.gz"
52
+ @data << "#{@dir}/#{rand}.tar.gz"
53
+ end # def setup
54
+
55
+ def teardown
56
+ @data.each do |file|
57
+ #puts "Deleting #{file}"
58
+ File.delete(file) rescue nil
59
+ end
60
+ Dir.delete(@dir)
61
+ end # def teardown
62
+
63
+ def finish
64
+ EM.stop_event_loop
65
+ end
66
+
67
+ def test_glob_finds_existing_files
68
+ EM.run do
69
+ abort_after_timeout(SLEEPMAX * @data.length + 10)
70
+
71
+ @data.each do |path|
72
+ File.new(path, "w").close
73
+ end
74
+ EM::watch_glob("#{@dir}/*", Watcher, @watchinterval, @data.clone, self)
75
+ end # EM.run
76
+ end # def test_glob_finds_existing_files
77
+
78
+ # This test should run slow. We are trying to ensure that
79
+ # our file_tail correctly reads data slowly fed into the file
80
+ # as 'tail -f' would.
81
+ def test_glob_finds_newly_created_files_at_runtime
82
+ EM.run do
83
+ abort_after_timeout(SLEEPMAX * @data.length + 10)
84
+
85
+ EM::watch_glob("#{@dir}/*", Watcher, @watchinterval, @data.clone, self)
86
+ datacopy = @data.clone
87
+ timer = EM::PeriodicTimer.new(0.2) do
88
+ #puts "Creating: #{datacopy.first}"
89
+ File.new(datacopy.shift, "w")
90
+ sleep(rand * SLEEPMAX)
91
+ timer.cancel if datacopy.length == 0
92
+ end
93
+ end # EM.run
94
+ end # def test_glob_finds_newly_created_files_at_runtime
95
+ end # class TestGlobWatcher
96
+
@@ -0,0 +1,13 @@
1
+
2
+ module EventMachineTailTestHelpers
3
+ def abort_after_timeout(seconds)
4
+ EM::Timer.new(seconds) do
5
+ EM.stop_event_loop
6
+ flunk("Timeout (#{seconds} seconds) while running tests. Failing.")
7
+ end
8
+ end
9
+
10
+ def finish
11
+ EventMachine.stop_event_loop
12
+ end
13
+ end # module EventMachineTailTestHelpers
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 20100506012705
9
- version: 0.1.20100506012705
7
+ - 2
8
+ - 20100516235116
9
+ version: 0.2.20100516235116
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jordan Sissel
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-06 00:00:00 -07:00
17
+ date: 2010-05-16 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -45,6 +45,8 @@ files:
45
45
  - samples/glob-tail.rb
46
46
  - samples/globwatch.rb
47
47
  - test/test_filetail.rb
48
+ - test/test_glob.rb
49
+ - test/testcase_helpers.rb
48
50
  - bin/rtail
49
51
  has_rdoc: true
50
52
  homepage: http://code.google.com/p/semicomplete/wiki/EventMachineTail
@@ -76,6 +78,6 @@ rubyforge_project:
76
78
  rubygems_version: 1.3.6
77
79
  signing_key:
78
80
  specification_version: 3
79
- summary: eventmachine tail - a file tail implementation
81
+ summary: eventmachine tail - a file tail implementation with glob support
80
82
  test_files: []
81
83