khronotab 1.3.2
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +24 -0
- data/README.rdoc +36 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/demo/sample.ct +1 -0
- data/demo/sample.rb +7 -0
- data/khronotab.gemspec +51 -0
- data/lib/khronotab.rb +51 -0
- data/lib/khronotab/cron_job.rb +88 -0
- data/lib/khronotab/cron_unit.rb +47 -0
- data/lib/khronotab/cron_variable.rb +42 -0
- metadata +68 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2010, Kristofer M White
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of the <organization> nor the
|
12
|
+
names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= khronotab
|
2
|
+
|
3
|
+
KhronoTab is a pure ruby interface to the crontab. It uses a hash
|
4
|
+
interface to allow accessing the data of an existing crontab or the
|
5
|
+
creation of a new one far easier.
|
6
|
+
|
7
|
+
== Synopsis
|
8
|
+
This is a sample description of the application.
|
9
|
+
Blah blah blah.
|
10
|
+
|
11
|
+
== Installation
|
12
|
+
gem install khronotab
|
13
|
+
|
14
|
+
== Examples
|
15
|
+
Reading in an existing crontab
|
16
|
+
require 'khronotab'
|
17
|
+
crontab = Khronotab::CronTab.read_from_file('/etc/cron.d/crontab')
|
18
|
+
|
19
|
+
== Author
|
20
|
+
Kristofer M White <me@kmwhite.net>
|
21
|
+
|
22
|
+
== Copyright
|
23
|
+
Copyright (c) 2010 Kristofer M White. Licensed under the BSD License:
|
24
|
+
http://www.opensource.org/licenses/bsd-license.html
|
25
|
+
|
26
|
+
|
27
|
+
== Note on Patches/Pull Requests
|
28
|
+
|
29
|
+
* Fork the project.
|
30
|
+
* Make your feature addition or bug fix.
|
31
|
+
* Add tests for it. This is important so I don't break it in a
|
32
|
+
future version unintentionally.
|
33
|
+
* Commit, do not mess with rakefile, version, or history.
|
34
|
+
(if you want to have your own version, that is fine but
|
35
|
+
bump version in a commit by itself I can ignore when I pull)
|
36
|
+
* Send me a pull request. Bonus points for topic branches.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "khronotab"
|
8
|
+
gem.summary = %Q{A pure ruby cron parser}
|
9
|
+
gem.description = %Q{A pure ruby cron parser with a hash-like interface}
|
10
|
+
gem.email = "me@kmwhite.net"
|
11
|
+
gem.homepage = "http://github.com/kmwhite/khronotab"
|
12
|
+
gem.authors = ["Kristofer M White"]
|
13
|
+
gem.rubyforge_project = %q{khronotab}
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/*_test.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
if File.exist?('VERSION')
|
47
|
+
version = File.read('VERSION')
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "khronotab #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.3.2
|
data/demo/sample.ct
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* * * * * jsmith ls /home
|
data/demo/sample.rb
ADDED
data/khronotab.gemspec
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{khronotab}
|
8
|
+
s.version = "1.3.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kristofer M White"]
|
12
|
+
s.date = %q{2010-07-26}
|
13
|
+
s.description = %q{A pure ruby cron parser with a hash-like interface}
|
14
|
+
s.email = %q{me@kmwhite.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"demo/sample.ct",
|
27
|
+
"demo/sample.rb",
|
28
|
+
"khronotab.gemspec",
|
29
|
+
"lib/khronotab.rb",
|
30
|
+
"lib/khronotab/cron_job.rb",
|
31
|
+
"lib/khronotab/cron_unit.rb",
|
32
|
+
"lib/khronotab/cron_variable.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/kmwhite/khronotab}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubyforge_project = %q{khronotab}
|
38
|
+
s.rubygems_version = %q{1.3.5}
|
39
|
+
s.summary = %q{A pure ruby cron parser}
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
+
else
|
47
|
+
end
|
48
|
+
else
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/khronotab.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
module Khronotab
|
4
|
+
class CronTab
|
5
|
+
|
6
|
+
require 'khronotab/cron_variable'
|
7
|
+
require 'khronotab/cron_job'
|
8
|
+
|
9
|
+
VERSION = '1.3.2'
|
10
|
+
|
11
|
+
attr_accessor :jobs, :variables
|
12
|
+
|
13
|
+
def initialize opt={}
|
14
|
+
self.read_from_file(opt[:file]) if opt[:file]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.read_from_file(*args)
|
18
|
+
self.new.read_from_file(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_from_file(filename)
|
22
|
+
@variables ||= []
|
23
|
+
@jobs ||= []
|
24
|
+
File.open(filename, 'r').readlines.each do |line|
|
25
|
+
if CronVariable.matches?(line)
|
26
|
+
@variables << CronVariable.add_new(line)
|
27
|
+
elsif CronJob.matches?(line)
|
28
|
+
@jobs << CronJob.add_new(line)
|
29
|
+
else
|
30
|
+
STDERR.puts("Warning: Line is not a valid variable or job!")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_to_file(filename)
|
37
|
+
#TODO: Have this validate a file or append to the file if there
|
38
|
+
# is new data. Just overwriting the damn thing isn't very
|
39
|
+
# safe or wise.. -kmwhite 2010.04.19
|
40
|
+
crontab = File.open(filename,'w')
|
41
|
+
@variables.each do |variable|
|
42
|
+
crontab.puts definition.to_line
|
43
|
+
end
|
44
|
+
@jobs.each do |job|
|
45
|
+
crontab.puts job.to_line
|
46
|
+
end
|
47
|
+
crontab.flush
|
48
|
+
crontab.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'date'
|
3
|
+
require 'khronotab/cron_unit'
|
4
|
+
|
5
|
+
|
6
|
+
class CronJob
|
7
|
+
|
8
|
+
attr_accessor :minutes, :hours, :days, :month, :days_of_week, :user, :command, :days_of_month
|
9
|
+
|
10
|
+
JOB_REGEX=/([0-9\*\/\-,]+)\s+([0-9\*\/\-,]+)\s+([0-9\*\/\-,]+)\s+([0-9\*\/\-,]+)\s+([0-9\*\/\-,]+)\s+(\S+)\s+(.+)/
|
11
|
+
|
12
|
+
def self.matches?(ce)
|
13
|
+
return false if ce=~/^[\s]*$/ or ce=~/^\s*#/
|
14
|
+
!!JOB_REGEX.match(ce)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.add_new(cron_entry)
|
18
|
+
return nil unless JOB_REGEX.match(cron_entry)
|
19
|
+
minute, hour, dom, month, dow, user, command =
|
20
|
+
cron_entry.scan(JOB_REGEX).shift
|
21
|
+
self.new(
|
22
|
+
:minutes => minute,
|
23
|
+
:hours => hour,
|
24
|
+
:days_of_month => dom,
|
25
|
+
:month => month,
|
26
|
+
:days_of_week => dow,
|
27
|
+
:user => user,
|
28
|
+
:command => command
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def [] ac
|
33
|
+
return self.send(ac) if self.respond_to?(ac)
|
34
|
+
raise 'Unknown key!'
|
35
|
+
end
|
36
|
+
|
37
|
+
def []= k,v
|
38
|
+
return self.send("#{k}=",v) if self.respond_to?(k)
|
39
|
+
raise 'Unknown key!'
|
40
|
+
end
|
41
|
+
|
42
|
+
def expand_times(date = Date.today, type = :day)
|
43
|
+
case type
|
44
|
+
when :day
|
45
|
+
return [] unless runs_on?(date)
|
46
|
+
time_list = []
|
47
|
+
hours.expanded_form.map do |hour|
|
48
|
+
minutes.expanded_form.map do |minute|
|
49
|
+
time_list << "%02i-%02i-%02i %02i:%02i:%02i" % [date.year, date.month, date.day, hour, minute, 0]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
return time_list
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(data)
|
57
|
+
@minutes = CronUnit.new(data[:minutes], 0, 59)
|
58
|
+
@hours = CronUnit.new(data[:hours], 0, 23)
|
59
|
+
@days_of_month = CronUnit.new(data[:days_of_month], 1, 31)
|
60
|
+
@month = CronUnit.new(data[:month], 1, 12)
|
61
|
+
@days_of_week = CronUnit.new(data[:days_of_week], 0, 7)
|
62
|
+
@user = data[:user]
|
63
|
+
@command = data[:command]
|
64
|
+
end
|
65
|
+
|
66
|
+
def runs_on?(date_to_check)
|
67
|
+
month.contains?(date_to_check.month) &&
|
68
|
+
days_of_month.contains?(date_to_check.day) &&
|
69
|
+
days_of_week.contains?(date_to_check.wday)
|
70
|
+
end
|
71
|
+
|
72
|
+
def runs_today?
|
73
|
+
runs_on?(Time.now)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
puts "<Job minutes:%s hours:%s dom:%s month:%s dow:%s user:%s cmd:%s>" %
|
78
|
+
[ minutes, hours, days_of_month, month, days_of_week, user,
|
79
|
+
command ]
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_line
|
83
|
+
puts "%s %s %s %s %s %s %s" %
|
84
|
+
[ minutes, hours, days_of_month, month, days_of_week, user,
|
85
|
+
command ]
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class CronUnit
|
2
|
+
attr_accessor :cron_form, :minimum, :maximum
|
3
|
+
|
4
|
+
def initialize(cron, min, max)
|
5
|
+
@cron_form = cron
|
6
|
+
@minimum = min
|
7
|
+
@maximum = max
|
8
|
+
end
|
9
|
+
|
10
|
+
def contains?(value)
|
11
|
+
expanded_form.include?(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
def expanded_form
|
15
|
+
values = Array.new
|
16
|
+
@cron_form.split(',').each do |segment|
|
17
|
+
case segment
|
18
|
+
when /-/ #range
|
19
|
+
range = segment.split('-').map! { |x| x.to_i }
|
20
|
+
values.concat((range[0] .. range[1]).to_a)
|
21
|
+
when /\// #interval
|
22
|
+
counter = 0
|
23
|
+
interval = segment.split('/').map! { |x|
|
24
|
+
if x == '*'
|
25
|
+
x = @maximum
|
26
|
+
else
|
27
|
+
x.to_i
|
28
|
+
end
|
29
|
+
}
|
30
|
+
while counter < interval[0]
|
31
|
+
values.push(counter)
|
32
|
+
counter += interval[1]
|
33
|
+
end
|
34
|
+
when /[*]/
|
35
|
+
values.concat((@minimum .. @maximum).to_a)
|
36
|
+
else
|
37
|
+
values << segment.to_i
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return values.sort.uniq
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
@cron_form
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class CronVariable < Hash
|
2
|
+
|
3
|
+
attr_accessor :name, :value
|
4
|
+
|
5
|
+
VAR_REGEX=%r{(^\s*[^#]\w+)=(.+)}i
|
6
|
+
|
7
|
+
def [] ac
|
8
|
+
return self.send(ac) if self.respond_to?(ac)
|
9
|
+
raise 'Unknown key!'
|
10
|
+
end
|
11
|
+
|
12
|
+
def []= k,v
|
13
|
+
return self.send("#{k}=",v) if self.respond_to?(k)
|
14
|
+
raise 'Unknown key!'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.matches?(ce)
|
18
|
+
return false if ce=~/^[\s]*$/ or ce=~/^\s*#/
|
19
|
+
!!VAR_REGEX.match(ce)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.add_new(cron_entry)
|
23
|
+
return nil unless VAR_REGEX.match(cron_entry)
|
24
|
+
name, value = cron_entry.scan(VAR_REGEX).shift
|
25
|
+
self.new( :name => name, :value => value )
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(data)
|
29
|
+
@name = data[:name]
|
30
|
+
@value = data[:value]
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
puts "< CronVariable name: %s, value: '%s' >" %
|
35
|
+
[ self.name, self.value ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_line
|
39
|
+
puts "%s=%s" % [ self.name, self.values ]
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: khronotab
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.3.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kristofer M White
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-07-26 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A pure ruby cron parser with a hash-like interface
|
17
|
+
email: me@kmwhite.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- demo/sample.ct
|
33
|
+
- demo/sample.rb
|
34
|
+
- khronotab.gemspec
|
35
|
+
- lib/khronotab.rb
|
36
|
+
- lib/khronotab/cron_job.rb
|
37
|
+
- lib/khronotab/cron_unit.rb
|
38
|
+
- lib/khronotab/cron_variable.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/kmwhite/khronotab
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project: khronotab
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: A pure ruby cron parser
|
67
|
+
test_files: []
|
68
|
+
|