Class: File::Tail::Group

Inherits:
Object
  • Object
show all
Defined in:
lib/file/tail/group.rb

Overview

This class can be used to coordinate tailing of many files, which have been added to the group.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Group

Creates a new File::Tail::Group instance.

The following options can be given as arguments:

:files

an array of files (or filenames to open) that are placed into the group.



13
14
15
16
17
18
# File 'lib/file/tail/group.rb', line 13

def initialize(opts = {})
  @tailers = ThreadGroup.new
  if files = opts[:files]
    Array(files).each { |file| add file }
  end
end

Class Method Details

.[](*files) ⇒ Object

Creates a group for files (IO instances or filename strings).



21
22
23
# File 'lib/file/tail/group.rb', line 21

def self.[](*files)
  new(:files => files)
end

Instance Method Details

#add(file_or_filename) ⇒ Object Also known as: <<

Add a file (IO instance) or filename (responding to to_str) to this group.



27
28
29
30
31
32
33
# File 'lib/file/tail/group.rb', line 27

def add(file_or_filename)
  if file_or_filename.respond_to?(:to_io)
    add_file file_or_filename.to_io
  elsif file_or_filename.respond_to?(:to_str)
    add_filename file_or_filename
  end
end

#add_file(file) ⇒ Object

Add the IO instance file to this group.



38
39
40
41
# File 'lib/file/tail/group.rb', line 38

def add_file(file)
  setup_file_tailer file
  self
end

#add_filename(filename, n = 0) ⇒ Object

Add a file created by opening filename to this group after stepping n lines backwards from the end of it.



45
46
47
48
49
50
# File 'lib/file/tail/group.rb', line 45

def add_filename(filename, n = 0)
  file = Logfile.open(filename.to_str, :backward => n)
  file.backward n
  setup_file_tailer file
  self
end

#each_file(&block) ⇒ Object

Iterate over all files contained in this group yielding to block for each of them.



54
55
56
# File 'lib/file/tail/group.rb', line 54

def each_file(&block)
  each_tailer { |t| t.file }.map(&block)
end

#each_tailer(&block) ⇒ Object

Iterate over all tailers in this group yielding to block for each of them.



60
61
62
# File 'lib/file/tail/group.rb', line 60

def each_tailer(&block)
  @tailers.list.map(&block)
end

#setup_file_tailer(file) ⇒ Object (private)



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/file/tail/group.rb', line 89

def setup_file_tailer(file)
  file.extend File::Tail
  setup = ConditionVariable.new
  mutex = Mutex.new
  ft = nil
  mutex.synchronize do
    ft = Tailer.new do
      t = Thread.current
      t[:queue] = Queue.new
      t[:file]  = file
      mutex.synchronize do
        setup.signal
      end
      file.tail { |line| t[:queue] << line }
    end
    setup.wait mutex
  end
  @tailers.add ft
  nil
end

#stopObject

Stop all tailers in this group at once.



65
66
67
68
69
# File 'lib/file/tail/group.rb', line 65

def stop
  each_tailer { |t| t.stop }
  each_tailer { |t| t.join }
  self
end

#tailObject

Tail all the lines of all the files in the Tail::Group instance, that is yield to each of them.

Every line is extended with the LineExtension module, that adds some methods to the line string. To get the path of the file this line was received from call line.file.path.



77
78
79
80
81
82
83
84
85
# File 'lib/file/tail/group.rb', line 77

def tail
  wait_for_activity do |tailer|
    tailer.pending_lines.each do |line|
      line.extend LineExtension
      line.instance_variable_set :@tailer, tailer
      yield line
    end
  end
end

#wait_for_activity(&block) ⇒ Object (private)

Wait until new input is receŃ–ved on any of the tailers in the group. If so call block with all of these trailers as an argument.



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/file/tail/group.rb', line 112

def wait_for_activity(&block)
  loop do
    pending = each_tailer.select(&:pending_lines?)
    if pending.empty?
      interval = each_file.map { |t| t.interval }.compact.min || 0.1
      sleep interval
    else
      pending.each(&block)
    end
  end
end