scron 1.0.3 → 1.0.4

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 586a0c5cedf3700fd66abdb7b24403aca01a4b68
4
+ data.tar.gz: 95f660518802bab1ddc320e5192dd111cbcca1c7
5
+ SHA512:
6
+ metadata.gz: ed5492d51a2acd578e9bd791b3d740ea25d96a7b65a5a9e6ebb17cc9211110e0cfa6853cd5cd755c43b063d3c44f1c09c7a5a85350cbea1c3ac8e9397fafd26f
7
+ data.tar.gz: 35e8f7eadabb093c71a5af3440551efdef569f5c79df527dd06723f8102d498daff481209efd7ff515715ccfe471d97eecbbe17ac6ca8515cf388342f12d50f4
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012, Hugh Bien
1
+ Copyright (c) Hugh Bien
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification,
data/README.md CHANGED
@@ -49,6 +49,11 @@ failure and scron will attempt to re-run it again in 2 hours.
49
49
  `$HOME/.scronlog` has the stdout, timestamps, and exit status of last
50
50
  scheduled commands.
51
51
 
52
+ TODO
53
+ ====
54
+
55
+ * rename process to scron
56
+
52
57
  License
53
58
  =======
54
59
 
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'optparse'
3
- require File.expand_path('scron', File.dirname(__FILE__))
3
+ require_relative '../lib/scron'
4
4
 
5
5
  ARGV.options do |o|
6
6
  o.set_summary_indent(' ')
7
7
  o.banner = "Usage: #{File.basename($0)} [OPTION]"
8
8
  o.define_head "Scheduler for laptops/machines which aren't on 24/7"
9
- o.on('-e', '--edit', 'edit jobs') { Scron.edit; exit }
10
- o.on('-r', '--run', 'run jobs') { Scron.run; exit }
9
+ o.on('-e', '--edit', 'edit jobs') { Scron::App.edit; exit }
10
+ o.on('-r', '--run', 'run jobs') { Scron::App.run; exit }
11
11
  o.on('-h', '--help', 'show this help message') { puts o; exit }
12
12
  o.parse!
13
13
  puts o
@@ -0,0 +1,5 @@
1
+ require 'date'
2
+ require_relative 'scron/app'
3
+ require_relative 'scron/schedule'
4
+ require_relative 'scron/history'
5
+ require_relative 'scron/version'
metadata CHANGED
@@ -1,32 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scron
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
5
- prerelease:
4
+ version: 1.0.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Hugh Bien
9
8
  autorequire:
10
- bindir: .
9
+ bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-28 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: minitest
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :development
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
11
+ date: 2020-04-06 00:00:00.000000000 Z
12
+ dependencies: []
30
13
  description: Run commands at scheduled intervals. If an interval is missed, the command
31
14
  will be run as soon as possible.
32
15
  email:
@@ -36,34 +19,31 @@ executables:
36
19
  extensions: []
37
20
  extra_rdoc_files: []
38
21
  files:
39
- - scron_test.rb
40
- - scron.rb
41
22
  - LICENSE.md
42
23
  - README.md
43
- - scron
44
- - ./scron
45
- homepage: https://github.com/hughbien/scron
24
+ - bin/scron
25
+ - lib/scron.rb
26
+ homepage: https://github.com/hughbien/scronrb
46
27
  licenses: []
28
+ metadata: {}
47
29
  post_install_message:
48
30
  rdoc_options: []
49
31
  require_paths:
50
32
  - lib
51
33
  required_ruby_version: !ruby/object:Gem::Requirement
52
- none: false
53
34
  requirements:
54
- - - ! '>='
35
+ - - ">="
55
36
  - !ruby/object:Gem::Version
56
37
  version: '0'
57
38
  required_rubygems_version: !ruby/object:Gem::Requirement
58
- none: false
59
39
  requirements:
60
- - - ! '>='
40
+ - - ">="
61
41
  - !ruby/object:Gem::Version
62
42
  version: 1.3.6
63
43
  requirements: []
64
44
  rubyforge_project:
65
- rubygems_version: 1.8.23
45
+ rubygems_version: 2.5.1
66
46
  signing_key:
67
- specification_version: 3
47
+ specification_version: 4
68
48
  summary: Scheduler for laptops/machines which aren't on 24/7
69
49
  test_files: []
data/scron.rb DELETED
@@ -1,140 +0,0 @@
1
- require 'date'
2
-
3
- class Scron
4
- VERSION = '1.0.3'
5
- SCHEDULE_FILE = "#{ENV['HOME']}/.scron"
6
- HISTORY_FILE = "#{ENV['HOME']}/.scrondb"
7
- LOG_FILE = "#{ENV['HOME']}/.scronlog"
8
- EDITOR = ENV['EDITOR'] || 'vi'
9
- attr_reader :history, :schedules
10
-
11
- def initialize(text, history_text)
12
- @history = History.new(history_text)
13
- @schedules = text.split("\n").
14
- reject {|l| l =~ /^\s+$/}.
15
- map {|l| Schedule.new(l, @history)}
16
- end
17
-
18
- def self.run
19
- scron = Scron.new(read(SCHEDULE_FILE), read(HISTORY_FILE))
20
- overdue = scron.schedules.select {|s| s.overdue?}
21
- nowstr = now.strftime(History::FORMAT)
22
-
23
- logger = File.open(LOG_FILE, "a")
24
- logger.puts("=> #{nowstr} running")
25
- return if overdue.size == 0
26
-
27
- overdue.each do |schedule|
28
- logger.puts("=> #{nowstr} #{schedule.command} (start)")
29
- output = safe_cmd(schedule.command)
30
- logger.puts("=> #{nowstr} #{schedule.command} (exit=#{$?.to_i})")
31
- logger.puts(output) unless output == ''
32
- if $?.to_i == 0
33
- scron.history.touch(schedule.command)
34
- File.open(HISTORY_FILE, "w") {|f| f.puts scron.history.to_s}
35
- end
36
- end
37
- ensure
38
- logger.close
39
- end
40
-
41
- def self.now
42
- @now ||= begin
43
- now = DateTime.now
44
- now += now.offset
45
- now = now.new_offset('+00:00')
46
- end
47
- end
48
-
49
- def self.edit
50
- `#{EDITOR} #{SCHEDULE_FILE} < \`tty\` > \`tty\``
51
- end
52
-
53
- private
54
- def self.safe_cmd(command)
55
- `#{command}`
56
- rescue StandardError => error
57
- error.to_s
58
- end
59
-
60
- def self.read(filename)
61
- File.exist?(filename) ? File.read(filename) : ''
62
- end
63
- end
64
-
65
- class Schedule
66
- attr_reader :interval, :command
67
- WEEKDAYS = {'Mo' => 1, 'Tu' => 2, 'We' => 3, 'Th' => 4, 'Fr' => 5,
68
- 'Sa' => 6, 'Su' => 7}
69
-
70
- def initialize(line, history)
71
- interval, command = line.split(/\s+/, 2)
72
- @interval = interval.split(',').map {|i| parse_days(i)}.min
73
- @command = command.strip
74
- @overdue = history[command].nil? ||
75
- (Scron.now - history[command]).to_f > @interval
76
- end
77
-
78
- def overdue?
79
- !!@overdue
80
- end
81
-
82
- private
83
- def parse_days(interval)
84
- now = Scron.now
85
- if WEEKDAYS[interval]
86
- (now.cwday - WEEKDAYS[interval]) % 7 + 1
87
- elsif interval =~ /^\d+(st|nd|rd|th)$/
88
- day = interval.to_i
89
- delta = now.day >= day ?
90
- now.day - day :
91
- now - last_month(day)
92
- delta.to_i + 1
93
- elsif interval =~ /^(\d+)\/(\d+)$/
94
- year, month, day = Scron.now.year, $1.to_i, $2.to_i
95
- year -= 1 if Scron.now.month < month ||
96
- (Scron.now.month == month && Scron.now.day < day)
97
- (Scron.now - DateTime.new(year, month, day)).to_i + 1
98
- elsif interval =~ /^\d+d$/
99
- interval.to_i
100
- else
101
- raise ArgumentError.new("Unable to parse: #{interval}")
102
- end
103
- end
104
-
105
- def last_month(day)
106
- last = Scron.now << 1
107
- [day, 30, 29, 28].each do |d|
108
- date = DateTime.new(last.year, last.month, d) rescue nil
109
- return date if date
110
- end
111
- end
112
- end
113
-
114
- class History
115
- FORMAT = '%Y-%m-%d.%H:%M'
116
-
117
- def initialize(text)
118
- @history = {}
119
- text.split("\n").reject {|l| l =~ /^\s+$/}.each do |line|
120
- timestamp, command = line.split(/\s+/, 2)
121
- @history[command.strip] = DateTime.parse(timestamp, FORMAT)
122
- end
123
- end
124
-
125
- def [](command)
126
- @history[command]
127
- end
128
-
129
- def touch(command)
130
- @history[command] = Scron.now
131
- end
132
-
133
- def to_s
134
- lines = []
135
- @history.each do |command, timestamp|
136
- lines << "#{timestamp.strftime(FORMAT)} #{command}"
137
- end
138
- lines.join("\n")
139
- end
140
- end
@@ -1,126 +0,0 @@
1
- require 'rubygems'
2
- require "#{File.dirname(__FILE__)}/scron"
3
- require 'minitest/autorun'
4
-
5
- class ScronTest < MiniTest::Unit::TestCase
6
- def setup
7
- Scron.instance_variable_set(:@now, DateTime.new(2010, 3, 15))
8
- end
9
- end
10
-
11
- class AppTest < ScronTest
12
- def test_files
13
- assert_equal("#{ENV['HOME']}/.scron", Scron::SCHEDULE_FILE)
14
- assert_equal("#{ENV['HOME']}/.scrondb", Scron::HISTORY_FILE)
15
- assert_equal("#{ENV['HOME']}/.scronlog", Scron::LOG_FILE)
16
- end
17
-
18
- def test_editor
19
- assert_includes([ENV['EDITOR'] || 'vi'], Scron::EDITOR)
20
- end
21
-
22
- def test_empty
23
- assert_equal('', Scron.send(:read, './non-existent-file'))
24
- refute_equal('', Scron.send(:read, 'README.md'))
25
- end
26
-
27
- def test_no_schedules
28
- scron = Scron.new('', '')
29
- assert_equal([], scron.schedules)
30
- end
31
-
32
- def test_initialize_schedules
33
- scron = Scron.new(
34
- "30d cmd arg1 arg2\n" +
35
- "7d /path/to/script.rb\n" +
36
- "1d /path/to/script2.rb",
37
- "2100-01-01.01:00 cmd arg1 arg2\n" +
38
- "2000-01-01.01:00 /path/to/script.rb")
39
- assert_equal(3, scron.schedules.size)
40
- refute(scron.schedules[0].overdue?)
41
- assert(scron.schedules[1].overdue?)
42
- assert(scron.schedules[2].overdue?)
43
- end
44
- end
45
-
46
- class ScheduleTest < ScronTest
47
- def test_parse_day_interval
48
- sched = Schedule.new('1d c', History.new(''))
49
- assert_equal(1, sched.send(:parse_days, '1d'))
50
- assert_equal(30, sched.send(:parse_days, '30d'))
51
- end
52
-
53
- def test_parse_day_of_week
54
- sched = Schedule.new('1d c', History.new(''))
55
- assert_equal(1, sched.send(:parse_days, 'Mo'))
56
- assert_equal(7, sched.send(:parse_days, 'Tu'))
57
- assert_equal(6, sched.send(:parse_days, 'We'))
58
- assert_equal(5, sched.send(:parse_days, 'Th'))
59
- assert_equal(4, sched.send(:parse_days, 'Fr'))
60
- assert_equal(3, sched.send(:parse_days, 'Sa'))
61
- assert_equal(2, sched.send(:parse_days, 'Su'))
62
- end
63
-
64
- def test_parse_day_of_month
65
- sched = Schedule.new('1d c', History.new(''))
66
- assert_equal(15, sched.send(:parse_days, '1st'))
67
- assert_equal(1, sched.send(:parse_days, '15th'))
68
- assert_equal(21, sched.send(:parse_days, '23rd'))
69
- assert_equal(16, sched.send(:parse_days, '31st'))
70
- end
71
-
72
- def test_parse_day_of_year
73
- sched = Schedule.new('1d c', History.new(''))
74
- assert_equal(74, sched.send(:parse_days, '1/1'))
75
- assert_equal(1, sched.send(:parse_days, '3/15'))
76
- assert_equal(81, sched.send(:parse_days, '12/25'))
77
- end
78
-
79
- def test_initialize_command
80
- sched = Schedule.new('30d cmd arg1 arg2', History.new(''))
81
- assert_equal('cmd arg1 arg2', sched.command)
82
- assert_equal(30, sched.interval)
83
- assert(sched.overdue?)
84
- end
85
-
86
- def test_bad_date
87
- sched = Schedule.new('1d c', History.new(''))
88
- assert_raises(ArgumentError) { sched.send(:parse_days, '2/31') }
89
- assert_raises(ArgumentError) { sched.send(:parse_days, '1') }
90
- end
91
-
92
- def test_multiple_intervals
93
- assert_equal(1, Schedule.new('1d,2d,3d cmd', History.new('')).interval)
94
- assert_equal(2, Schedule.new('Fr,Sa,Su cmd', History.new('')).interval)
95
- assert_equal(15, Schedule.new('1st,23rd cmd', History.new('')).interval)
96
- end
97
-
98
- def test_overdue_history
99
- sched = Schedule.new('30d cmd', History.new('2000-01-01.01:00 cmd'))
100
- assert(sched.overdue?)
101
- end
102
-
103
- def test_recent_history
104
- sched = Schedule.new('30d cmd', History.new('2100-01-01.01:00 cmd'))
105
- refute(sched.overdue?)
106
- end
107
- end
108
-
109
- class HistoryTest < ScronTest
110
- def test_initialize
111
- history = History.new('2100-01-01.01:00 cmd arg1 arg2')
112
- assert_equal(DateTime.new(2100, 1, 1, 1, 0), history['cmd arg1 arg2'])
113
- end
114
-
115
- def test_update_command
116
- history = History.new('')
117
- history.touch('cmd')
118
- assert_kind_of(DateTime, history['cmd'])
119
- end
120
-
121
- def test_output
122
- history = History.new('')
123
- history.touch('cmd')
124
- assert_match(/^20\d{2}-\d{2}-\d{2}.\d{2}:\d{2} cmd$/, history.to_s)
125
- end
126
- end