schedulero 0.1.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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