Class: Schedulero

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/utils.rb,
lib/schedulero.rb

Defined Under Namespace

Modules: Utils

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#humanize_seconds, #quick_overview

Constructor Details

#initialize(state_file: nil, log_file: true, silent: false) ⇒ Schedulero

Returns a new instance of Schedulero.



15
16
17
18
19
20
21
22
23
24
# File 'lib/schedulero.rb', line 15

def initialize state_file: nil, log_file: true, silent: false
  @silent  = silent
  @tasks   = {}
  @running = {}
  @count   = 0

  init_log   log_file
  init_state state_file

end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



13
14
15
# File 'lib/schedulero.rb', line 13

def logger
  @logger
end

#tasksObject (readonly)

Returns the value of attribute tasks.



13
14
15
# File 'lib/schedulero.rb', line 13

def tasks
  @tasks
end

Instance Method Details

#at(name, hours, proc = nil, &block) ⇒ Object

run task at specific hours



67
68
69
70
# File 'lib/schedulero.rb', line 67

def at name, hours, proc=nil, &block
  proc ||= block
  @tasks[name] = { at: hours , func: proc, name: name }
end

#every(name, seconds, proc = nil, &block) ⇒ Object

add task



61
62
63
64
# File 'lib/schedulero.rb', line 61

def every name, seconds, proc=nil, &block
  proc ||= block
  @tasks[name] = { interval: seconds , func: proc, name: name }
end

#init_log(log_file) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/schedulero.rb', line 35

def init_log log_file
  # log file
  @log_file = case log_file
    when String
      log_file
    when false
      nil
    else
      "./log/schedulero.log"
  end

  show 'Log file  : %s' % @log_file

  @logger = Logger.new @log_file
  @logger.formatter = proc do |severity, datetime, progname, msg|
    severity = severity == 'INFO' ? '' : "(#{severity}) "
    "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}]: #{severity}#{msg}\n"
  end
end

#init_state(state_file) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/schedulero.rb', line 26

def init_state state_file
  # state file
  state_file ||= "./tmp/schedulero.json"
  show 'State file: %s' % state_file

  @state_file = Pathname.new state_file
  @state_file.write '{}' unless @state_file.exist?
end

#log_errror(name) ⇒ Object

show and log error



156
157
158
159
160
161
162
163
164
165
166
# File 'lib/schedulero.rb', line 156

def log_errror name
  msg  = if $!
    '%s: %s (%s)' % [name, $!.message, $!.class]
  else
    name
  end

  show msg.red

  @logger.error(msg)
end

#runObject

run all tasks once, safe



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
# File 'lib/schedulero.rb', line 83

def run
  state = JSON.load @state_file.read

  state['_pid']      ||= Process.pid
  state['_last_run'] ||= Time.now.to_i
  diff = Time.now.to_i - state['_last_run']

  # if another process is controlling state, exit
  if state['_pid'] != Process.pid && diff < 10
    show "Another process [#{state['_pid']}] is controlling state before #{diff} sec, skipping. I am (#{Process.pid})".red
    return
  end

  for name, block in @tasks
    state[name] ||= 0
    now           = Time.now.to_i

    if block[:at]
      # run at specific times
      hour_now = Time.now.hour
      hours    = block[:at].class == Array ? block[:at] : [block[:at]]

      if hours.include?(hour_now) && (Time.now.to_i - state[name] > 3700)
        state[name] = now
        safe_run block
      end
    else
      # run in intervals
      seconds = block[:interval]
      diff    = (state[name].to_i + seconds.to_i) - now

      if diff < 0
        state[name] = now
        safe_run block
      else
        show 'skipping "%s" for %s' % [name, humanize_seconds(diff)]
      end
    end
  end

  state['_last_run'] = Time.now.to_i
  state['_pid']      = Process.pid

  @state_file.write state.to_json
end

#run_forever(interval: 3) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/schedulero.rb', line 72

def run_forever interval: 3
  Thread.new do
    loop do
      show 'looping ...'
      run
      sleep interval
    end
  end
end

#safe_run(block) ⇒ Object

run in rescue mode, kill if still running



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/schedulero.rb', line 130

def safe_run block
  name = block[:name]

  show 'Running "%s"' % name.green
  @logger.info 'Run: %s' % name

  if block[:running]
    log_errror "Task [#{block[:name]}] is still running, killing..."
    Thread.kill(block[:running])
  end

  thread = Thread.start(block) do |b|
    block[:running] = thread

    begin
      @count += 1
      b[:func].call @count
    rescue
      log_errror b[:name]
    end

    b[:running] = false
  end
end

#show(text) ⇒ Object



55
56
57
58
# File 'lib/schedulero.rb', line 55

def show text
  return if @silent
  puts 'Schedulero: %s' % text
end