Class: Forever::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/forever/base.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &block) ⇒ Base

Returns a new instance of Base.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/forever/base.rb', line 8

def initialize(options={}, &block)
  @options = options
  forking = options.delete(:fork)

  # Run others methods
  options.each { |k,v| send(k, v) if respond_to?(k) }

  instance_eval(&block)

  # Setup directories
  Dir.chdir(dir)
  Dir.mkdir(tmp) unless File.exist?(tmp)
  Dir.mkdir(File.dirname(log)) if log && !File.exist?(File.dirname(log))

  write_config!

  case ARGV[0]
    when 'config'
      print config.to_yaml
      exit
    when 'start', 'restart', 'up', nil
      stop
    when 'run'
      detach = false
      stop
    when 'stop'
      stop
      exit
    when 'kill'
      stop!
      exit
    when 'update'
      print "[\e[90m%s\e[0m] Config written in \e[1m%s\e[0m\n" % [name, FOREVER_PATH]
      exit
    when 'remove'
      stop
      remove
      exit
    else
      print <<-RUBY.gsub(/ {10}/,'') % name
        Usage: \e[1m./%s\e[0m [start|stop|kill|restart|config|update]

        Commands:

          start      stop (if present) the daemon and perform a start
          stop       stop the daemon if a during when it is idle
          restart    same as start
          kill       force stop by sending a KILL signal to the process
          config     show the current daemons config
          update     update the daemon config
          remove     removes the daemon config

      RUBY
      exit
  end

  clean_tmp!

  # Enable REE - http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
  GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)

  maybe_fork(detach) do
    Process.setsid if detach != false

    $0 = "Forever: #{$0}" unless ENV['DONT_TOUCH_PS']
    print "[\e[90m%s\e[0m] Process %s with pid \e[1m%d\e[0m with \e[1m%s\e[0m and Forever v.%s\n" %
      [name, detach != false ? :daemonized : :running, Process.pid, forking ? :fork : :thread, Forever::VERSION]

    %w(INT TERM KILL).each { |signal| trap(signal)  { stop! } }
    trap(:HUP) do
      IO.open(1, 'w'){ |s| s.puts config }
    end

    File.open(pid, "w") { |f| f.write(Process.pid.to_s) } if pid

    stream      = log ? File.new(log, @options[:append_log] ? 'a' : 'w') : File.open('/dev/null', 'w')
    stream.sync = true

    STDOUT.reopen(stream)
    STDERR.reopen(STDOUT)

    @started_at = Time.now

    # Invoke our before :all filters
    filters[:before][:all].each { |block| safe_call(block) }

    # Store pids of childs
    pids = []

    # Start deamons
    until stopping?
      current_queue = 1

      jobs.each do |job|
        next unless job.time?(Time.now)
        if queue && current_queue > queue
          puts "\n\nThe queue limit of #{queue} has been exceeded.\n\n"
          on_limit_exceeded ? on_limit_exceeded.call : sleep(60)
          break
        end
        if forking
          begin
            GC.start
            pids << fork { job_call(job) }
          rescue Errno::EAGAIN
            puts "\n\nWait all processes since os cannot create a new one\n\n"
            Process.waitall
          end
        else
          Thread.new { job_call(job) }
        end
        current_queue += 1
      end

      # Detach zombies, our ps will be happier
      pids.delete_if { |p| Process.detach(p).stop? }

      sleep 0.5
    end


    # Invoke our after :all filters
    filters[:after][:all].each { |block| safe_call(block) }

    # If we are here it means we are exiting so we can remove the pid and pending stop.txt
    clean_tmp!
  end

  self
end

Instance Attribute Details

#started_atObject (readonly)

Returns the value of attribute started_at.



6
7
8
# File 'lib/forever/base.rb', line 6

def started_at
  @started_at
end

Instance Method Details

#after(filter, &block) ⇒ Object

After :all or :each jobs hook



315
316
317
318
# File 'lib/forever/base.rb', line 315

def after(filter, &block)
  raise "Filter #{filter.inspect} not supported, available options are: :each, :all" unless [:each, :all].include?(filter)
  filters[:after][filter] << block
end

#before(filter, &block) ⇒ Object

Before :all or :each jobs hook



307
308
309
310
# File 'lib/forever/base.rb', line 307

def before(filter, &block)
  raise "Filter #{filter.inspect} not supported, available options are: :each, :all" unless [:each, :all].include?(filter)
  filters[:before][filter] << block
end

#configObject

Return config of current worker in a hash



323
324
325
# File 'lib/forever/base.rb', line 323

def config
  { :dir => dir, :file => file, :log => log, :pid => pid }
end

#dir(value = nil) ⇒ Object Also known as: workspace

Base working Directory



182
183
184
# File 'lib/forever/base.rb', line 182

def dir(value=nil)
  value ? @_dir = value : @_dir
end

#every(period, options = {}, &block) ⇒ Object

Define a new job task

Example:

every 1.second, :at => '12:00' do
  my_long_task
end


147
148
149
# File 'lib/forever/base.rb', line 147

def every(period, options={}, &block)
  jobs << Forever::Job.new(period, options.merge!(:dir => dir), &block)
end

#file(value = nil) ⇒ Object

Caller file



161
162
163
# File 'lib/forever/base.rb', line 161

def file(value=nil)
  value ? @_file = value : @_file
end

#jobsObject

Our job list



154
155
156
# File 'lib/forever/base.rb', line 154

def jobs
  @_jobs ||= []
end

#log(value = nil) ⇒ Object

File were we redirect STOUT and STDERR, can be false.

Default: dir + ‘log/.log’



199
200
201
202
# File 'lib/forever/base.rb', line 199

def log(value=nil)
  @_log ||= File.join(dir, "log/#{name}.log") if exists?(dir, file)
  value.nil? ? @_log : @_log = value
end

#nameObject

Daemon name



168
169
170
# File 'lib/forever/base.rb', line 168

def name
  File.basename(file, '.*')
end

#on_error(&block) ⇒ Object

Callback raised when an error occour



258
259
260
# File 'lib/forever/base.rb', line 258

def on_error(&block)
  block_given? ? @_on_error = block : @_on_error
end

#on_exit(&block) ⇒ Object

Callback raised when at exit



272
273
274
# File 'lib/forever/base.rb', line 272

def on_exit(&block)
  after(:all, &block)
end

#on_limit_exceeded(&block) ⇒ Object

Callback raised when queue limit was exceeded



265
266
267
# File 'lib/forever/base.rb', line 265

def on_limit_exceeded(&block)
  block_given? ? @_on_limit_exceeded = block : @_on_limit_exceeded
end

#on_ready(&block) ⇒ Object

Callback to fire when the daemon start (blocking, not in thread)



279
280
281
# File 'lib/forever/base.rb', line 279

def on_ready(&block)
  before(:all, &block)
end

#pid(value = nil) ⇒ Object

File were we store pid

Default: dir + ‘tmp/.pid’



209
210
211
212
# File 'lib/forever/base.rb', line 209

def pid(value=nil)
  @_pid ||= File.join(tmp, "#{name}.pid") if exists?(dir, file)
  value.nil? ? @_pid : @_pid = value
end

#queue(value = nil) ⇒ Object

Queue size



175
176
177
# File 'lib/forever/base.rb', line 175

def queue(value=nil)
  value ? @_queue = value : @_queue
end

#removeObject

Remove the daemon from the config file



248
249
250
251
252
253
# File 'lib/forever/base.rb', line 248

def remove
  print "[\e[90m%s\e[0m] Removed the daemon from the config " % name
  config_was = File.exist?(FOREVER_PATH) ? YAML.load_file(FOREVER_PATH) : []
  config_was.delete_if { |conf| conf[:file] == file }
  File.open(FOREVER_PATH, "w") { |f| f.write config_was.to_yaml }
end

#running?(silent = false) ⇒ Boolean

Returns true if the pid exist and the process is running

Returns:

  • (Boolean)


286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/forever/base.rb', line 286

def running?(silent=false)
  if exists?(pid)
    current = File.read(pid).to_i
    print "[\e[90m%s\e[0m] Found pid \e[1m%d\e[0m...\n" % [name, current] unless silent
  else
    print "[\e[90m%s\e[0m] Pid \e[1mnot found\e[0m, process seems doesn't exist!\n" % name unless silent
    return false
  end

  is_running = begin
    Process.kill(0, current)
  rescue Errno::ESRCH
    false
  end

  is_running
end

#stopObject

Perform a soft stop



233
234
235
236
237
238
239
240
241
242
243
# File 'lib/forever/base.rb', line 233

def stop
  if running?
    print "[\e[90m%s\e[0m] Waiting the daemon\'s death " % name
    FileUtils.touch(stop_txt)
    while running?(true)
      print '.'; $stdout.flush
      sleep 1
    end
    print " \e[1mDONE\e[0m\n"
  end
end

#stop!Object

Search if there is a running process and stop it



217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/forever/base.rb', line 217

def stop!
  FileUtils.rm_f(stop_txt)
  if running?
    pid_was = File.read(pid).to_i
    print "[\e[90m%s\e[0m] Killing process \e[1m%d\e[0m...\n" % [name, pid_was]
    filters[:after][:all].each { |block| safe_call(block) }
    clean_tmp!
    Process.kill(:KILL, pid_was)
  else
    print "[\e[90m%s\e[0m] Process with \e[1mnot found\e[0m" % name
  end
end

#tmpObject

Temp directory, used to store pids and jobs status



190
191
192
# File 'lib/forever/base.rb', line 190

def tmp
  File.join(dir, 'tmp')
end

#to_sObject Also known as: inspect

Convert forever object in a readable string showing current config



330
331
332
# File 'lib/forever/base.rb', line 330

def to_s
  "#<Forever dir:#{dir}, file:#{file}, log:#{log}, pid:#{pid} jobs:#{jobs.size}>"
end