schedulero 0.1.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/lib/schedulero.rb +112 -63
  4. data/lib/utils.rb +30 -0
  5. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68ae0fbd4f3eb868a50f64ca330706a1acf29a71d42aa2eb89d6232abbf36c60
4
- data.tar.gz: 223117c9bec29af59fda35bb8d64b9286216192d153da5a598d4283b945e22a2
3
+ metadata.gz: 2cfd26c98933e9a712a4352b8bae35e58e352389584db91bea451744d2b1ecb2
4
+ data.tar.gz: 8f3781c8e20b9b054711de0f22531e047807565d21749ad94c69597f7b7df0e3
5
5
  SHA512:
6
- metadata.gz: 91dd146dd80f9d06f77395cb23f67925b671cec1f3df7eb1519bfaf6bcf45ac0865fb3013d289ee749b3485a5a1e724d5f693c970b6f03a62989b6e9af2d8ce6
7
- data.tar.gz: 30762af755ba57d8d3c8b2362f743b9c283ebccba80514cfd77da1ecad69aad1511003039d61bb7c62081c4e94606e6130bceff75c556073dcd77cc461cfad62
6
+ metadata.gz: dcf894b27957183ccdeb7d2ca2a37902cfbe7a5093e5ecd752adc599e14e46ad13da55f66f0ffcef194d754ed01e43cb9d214f778528f48376830bf840b8584b
7
+ data.tar.gz: 1e0d99eb6a9cafc56b40a7b88ceb479900f9e1aee3ac29cc487bbc2bf4062dc376d625c4a5a4cd6c9917886748f1945dd70659e4978597ba773d45fee8b8c94f
data/.version CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.3
data/lib/schedulero.rb CHANGED
@@ -3,110 +3,159 @@ require 'json'
3
3
  require 'logger'
4
4
  require 'colorize'
5
5
  require 'as-duration'
6
+ require 'pp'
7
+
8
+ require_relative './utils'
6
9
 
7
10
  class Schedulero
11
+ include Schedulero::Utils
12
+
13
+ attr_reader :tasks, :logger
14
+
8
15
  def initialize state_file: nil, log_file: true
16
+ init_log log_file
17
+ init_state state_file
9
18
 
19
+ @tasks = {}
20
+ @running = {}
21
+ @count = 0
22
+ end
23
+
24
+ def init_state state_file
25
+ # state file
26
+ state_file ||= "./tmp/schedulero.json"
27
+ puts 'State file: %s' % state_file
28
+
29
+ @state_file = Pathname.new state_file
30
+ @state_file.write '{}' unless @state_file.exist?
31
+ end
32
+
33
+ def init_log log_file
10
34
  # log file
11
35
  log_file = case log_file
12
36
  when String
13
37
  log_file
14
- when true
15
- './log/schedulero.log'
16
38
  when false
17
39
  nil
40
+ else
41
+ "./log/schedulero.log'"
18
42
  end
19
43
 
20
- @logger = Logger.new log_file
21
- @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
44
+ puts 'Log file : %s' % log_file
22
45
 
23
- # state file
24
- unless state_file
25
- state_file = 'schedulero.json'
26
- state_file = Dir.exists?('./tmp') ? "./tmp/#{state_file}" : state_file
27
- end
28
-
29
- @state_file = Pathname.new state_file
30
-
31
- if @state_file.exist?
32
- @state = JSON.load @state_file.read
33
- else
34
- @state = {}
46
+ @logger = Logger.new log_file
47
+ @logger.formatter = proc do |severity, datetime, progname, msg|
48
+ severity = severity == 'INFO' ? '' : "(#{severity}) "
49
+ "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}]: #{severity}#{msg}\n"
35
50
  end
36
51
  end
37
52
 
38
- def run &block
39
- @logger.info 'running...'
40
-
41
- instance_exec &block
53
+ # add task
54
+ def every name, seconds, proc=nil, &block
55
+ proc ||= block
56
+ @tasks[name] = { interval: seconds , func: proc, name: name }
57
+ end
42
58
 
43
- @state_file.write @state.to_json
59
+ # run task at specific hours
60
+ def at name, hours, proc=nil, &block
61
+ proc ||= block
62
+ @tasks[name] = { at: hours , func: proc, name: name }
44
63
  end
45
64
 
46
- def every name, seconds, &block
47
- @state[name] ||= 0
65
+ def run_forever interval: 3
66
+ Thread.new do
67
+ loop do
68
+ puts 'looping ...'
69
+ run
70
+ sleep interval
71
+ end
72
+ end
73
+ end
48
74
 
49
- now = Time.now.to_i
50
- diff = (@state[name] + seconds.to_i) - now
75
+ # run all tasks once, safe
76
+ def run
77
+ state = JSON.load @state_file.read
51
78
 
52
- if diff < 0
53
- puts 'running "%s"' % name.green
79
+ state['_pid'] ||= Process.pid
80
+ state['_last_run'] ||= Time.now.to_i
81
+ diff = Time.now.to_i - state['_last_run']
54
82
 
55
- @state[name] = now
83
+ # if another process is controlling state, exit
84
+ if state['_pid'] != Process.pid && diff < 10
85
+ puts "Another process [#{state['_pid']}] is controlling state before #{diff} sec, skipping. I am (#{Process.pid})".red
86
+ return
87
+ end
56
88
 
57
- begin
58
- @logger.info 'Run: %s' % name
59
- yield
60
- rescue
61
- log_errror name
89
+ for name, block in @tasks
90
+ state[name] ||= 0
91
+ now = Time.now.to_i
92
+
93
+ if block[:at]
94
+ # run at specific times
95
+ hour_now = Time.now.hour
96
+ hours = block[:at].class == Array ? block[:at] : [block[:at]]
97
+
98
+ if hours.include?(hour_now) && (Time.now.to_i - state[name] > 3700)
99
+ state[name] = now
100
+ safe_run block
101
+ end
102
+ else
103
+ # run in intervals
104
+ seconds = block[:interval]
105
+ diff = (state[name].to_i + seconds.to_i) - now
106
+
107
+ if diff < 0
108
+ state[name] = now
109
+ safe_run block
110
+ else
111
+ puts 'skipping "%s" for %s' % [name, humanize_seconds(diff)]
112
+ end
62
113
  end
63
- else
64
- puts 'skipping "%s" for %s' % [name, humanize_seconds(diff)]
65
114
  end
115
+
116
+ state['_last_run'] = Time.now.to_i
117
+ state['_pid'] = Process.pid
118
+
119
+ @state_file.write state.to_json
66
120
  end
67
121
 
68
- def at name, hours, &block
69
- @state[name] ||= 0
122
+ # run in rescue mode, kill if still running
123
+ def safe_run block
124
+ name = block[:name]
70
125
 
71
- hour_now = Time.now.hour
72
- hours = [hours] unless hours.class == Array
126
+ puts 'Running "%s"' % name.green
127
+ @logger.info 'Run: %s' % name
73
128
 
74
- if hours.include?(hour_now) && (Time.now.to_i - @state[name] > 3700)
75
- puts 'running "%s"' % name.green
129
+ if block[:running]
130
+ log_errror "Task [#{block[:name]}] is still running, killing..."
131
+ Thread.kill(block[:running])
132
+ end
76
133
 
77
- @state[name] = Time.now.to_i
134
+ thread = Thread.start(block) do |b|
135
+ block[:running] = thread
78
136
 
79
137
  begin
80
- @logger.info 'Run: %s' % name
81
- yield
138
+ @count += 1
139
+ b[:func].call @count
82
140
  rescue
83
- log_errror name
141
+ log_errror b[:name]
84
142
  end
85
- else
86
- puts 'skipping "%s" at %d, running in %s' % [name, hour_now, hours]
143
+
144
+ b[:running] = false
87
145
  end
88
146
  end
89
147
 
148
+ # show and log error
90
149
  def log_errror name
91
- msg = '%s: %s (%s)' % [name, $!.message, $!.class]
92
- puts msg.red
150
+ msg = if $!
151
+ '%s: %s (%s)' % [name, $!.message, $!.class]
152
+ else
153
+ name
154
+ end
93
155
 
94
- Dir.mkdir('./log') unless Dir.exist?('./log')
156
+ puts msg.red
95
157
 
96
158
  @logger.error(msg)
97
159
  end
98
-
99
- def humanize_seconds secs
100
- return '-' unless secs
101
-
102
- secs = secs.to_i
103
-
104
- [[60, :sec], [60, :min], [24, :h], [356, :days], [1000, :years]].map{ |count, name|
105
- if secs > 0
106
- secs, n = secs.divmod(count)
107
- "#{n.to_i} #{name}"
108
- end
109
- }.compact.reverse.slice(0,2).join(' ')
110
- end
111
160
  end
112
161
 
data/lib/utils.rb ADDED
@@ -0,0 +1,30 @@
1
+ class Schedulero
2
+ module Utils
3
+ def humanize_seconds secs
4
+ return '-' unless secs
5
+
6
+ secs = secs.to_i
7
+
8
+ [[60, :sec], [60, :min], [24, :h], [356, :days], [1000, :years]].map{ |count, name|
9
+ if secs > 0
10
+ secs, n = secs.divmod(count)
11
+ "#{n.to_i} #{name}"
12
+ end
13
+ }.compact.reverse.slice(0,2).join(' ')
14
+ end
15
+
16
+ def quick_overview lines: 100
17
+ data = ['Tasks:']
18
+
19
+ for name, task in tasks
20
+ data.push '- %s, every %s' % [name, humanize_seconds(task[:interval])]
21
+ end
22
+
23
+ data.push ['','###', '']
24
+
25
+ data.push `tail -#{lines} ./log/schedulero.log`.split($/).reverse
26
+
27
+ data.join($/)
28
+ end
29
+ end
30
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schedulero
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dino Reic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-08 00:00:00.000000000 Z
11
+ date: 2018-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: as-duration
@@ -46,6 +46,7 @@ extra_rdoc_files: []
46
46
  files:
47
47
  - "./.version"
48
48
  - "./lib/schedulero.rb"
49
+ - "./lib/utils.rb"
49
50
  homepage: https://github.com/dux/schedulero
50
51
  licenses:
51
52
  - MIT