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 ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'khronotab'
4
+ c = Khronotab::CronTab.new
5
+ c.read_from_file('sample.ct')
6
+ puts c.jobs.first.inspect
7
+ puts c.jobs.first.runs_today?
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
+