schedule 0.0.1

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/README.md ADDED
@@ -0,0 +1 @@
1
+ A Ruby replacement for Cron
data/Rakefile ADDED
@@ -0,0 +1,115 @@
1
+ require "rubygems"
2
+ require "rake/gempackagetask"
3
+ require "rake/rdoctask"
4
+ require "rake/testtask"
5
+
6
+ task :default => :test
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = "schedule"
10
+ s.version = "0.0.1"
11
+ s.summary = "A Ruby replacement for Cron"
12
+ s.author = "Mark Dodwell"
13
+ s.email = "hi@mkdynamic.co.uk"
14
+ s.homepage = "http://github.com/mkdynamic/schedule"
15
+
16
+ # Rdoc
17
+ # s.has_rdoc = true
18
+ # s.extra_rdoc_files = %w(README.md)
19
+ # s.rdoc_options = %w(--main README.md)
20
+
21
+ # Files
22
+ s.files = %w(Rakefile README.md) + Dir.glob("{bin,lib,test}/**/*")
23
+ s.executables = FileList["bin/**"].map { |f| File.basename(f) }
24
+ s.require_paths = ["bin", "lib"]
25
+
26
+ # Dependencies
27
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3")
28
+ s.add_development_dependency("shoulda")
29
+ s.add_development_dependency("timecop")
30
+ end
31
+
32
+ #
33
+ # Specification
34
+ #
35
+
36
+ desc "Build the gemspec file #{spec.name}.gemspec"
37
+ task :gemspec do
38
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
39
+ File.open(file, "w") { |f| f.write(spec.to_ruby) }
40
+ end
41
+
42
+ #
43
+ # Build
44
+ #
45
+
46
+ Rake::GemPackageTask.new(spec) do |pkg|
47
+ pkg.gem_spec = spec
48
+ end
49
+
50
+ task :package => :gemspec # build gemspec when packaging
51
+
52
+ #
53
+ # Tests
54
+ #
55
+
56
+ Rake::TestTask.new do |t|
57
+ t.libs = [File.expand_path("lib"), "test"]
58
+ t.test_files = FileList["test/**/*_test.rb"]
59
+ t.verbose = true
60
+ end
61
+
62
+ #
63
+ # Documentation
64
+ #
65
+
66
+ # Rake::RDocTask.new do |rd|
67
+ # rd.main = "README.md"
68
+ # rd.rdoc_files.include("README.md", "lib/**/*.rb")
69
+ # rd.rdoc_dir = "rdoc"
70
+ # end
71
+
72
+ # desc "Clear out RDoc and generated packages"
73
+ # task :clean => [:clobber_rdoc, :clobber_package] do
74
+ # rm "#{spec.name}.gemspec"
75
+ # end
76
+
77
+ #
78
+ # Release
79
+ #
80
+
81
+ desc "Tag the repository in git with gem version number"
82
+ task :tag do
83
+ #`git fetch --tags`
84
+ if `git tag`.split("\n").include?("v#{spec.version}")
85
+ raise "Version #{spec.version} has already been released."
86
+ end
87
+
88
+ changed_files = `git diff --cached --name-only`.split("\n") + `git diff --name-only`.split("\n")
89
+ if changed_files == ["Rakefile"]
90
+ # MAGIC! automatically update Schedule::VERSION constant
91
+ file = "lib/#{spec.name}.rb"
92
+ s = File.read(file)
93
+ File.open(file, "w") do |f|
94
+ f.write(s.gsub(/^(\s*)VERSION(\s*)=(\s*)(["'])[\d\.]+(['"])(\s*)$/, %{\\1VERSION\\2=\\3\\4#{spec.version.to_s}\\5\\6}))
95
+ end
96
+ if File.read(file).match(/^\s*VERSION\s*=\s*"([\d\.]+)"\s*$/)[1] != spec.version.to_s
97
+ "VERSION constant in #{file} differs from gemspec version."
98
+ end
99
+
100
+ Rake::Task["package"].invoke
101
+
102
+ `git add #{File.expand_path("../#{spec.name}.gemspec", __FILE__)} Rakefile lib/schedule.rb`
103
+ `git commit -m "prepare version #{spec.version}"`
104
+ `git tag v#{spec.version}`
105
+ #`git push --tags`
106
+ #`git push`
107
+ else
108
+ raise "Repository contains uncommitted changes; either commit or stash."
109
+ end
110
+ end
111
+
112
+ # desc "Tag and publish the gem to rubygems.org"
113
+ # task :publish => :tag do
114
+ # `gem push pkg/#{spec.name}-#{spec.version}.gem`
115
+ # end
data/bin/cr ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require "schedule/task"
7
+ require "schedule/runner"
8
+ require "schedule/notifier"
9
+ require "schedule/logger"
10
+
11
+ def invalid_arg!(arg, options, command)
12
+ raise "Invalid argument: #{options.inspect} / #{command.inspect} / #{arg.inspect}"
13
+ end
14
+
15
+ args = ARGV.dup
16
+ command = nil
17
+ options = {}
18
+ until args.empty? do
19
+ case (arg = args.shift.strip)
20
+ when /^[^\-]/
21
+ invalid_arg!(arg, options, command) unless command.nil?
22
+ command = arg
23
+ when "-n", "--name" then options[:name] = args.shift
24
+ when "-l", "--log" then options[:log] = args.shift
25
+ when "-e", "--email" then options[:email] = args.shift
26
+ when "--" # noop
27
+ else invalid_arg!(arg, options, command)
28
+ end
29
+ end
30
+
31
+ raise "Did not pass any command to run" unless command
32
+
33
+ task = Schedule::Task.new(options[:name], command)
34
+
35
+ logger = if options[:log]
36
+ Schedule::Logger.new(options[:log], options[:name])
37
+ else
38
+ nil
39
+ end
40
+
41
+ notifier = if options[:email]
42
+ Schedule::Notifier::Email.new({
43
+ :to => options[:email]
44
+ })
45
+ else
46
+ nil
47
+ end
48
+
49
+ exit Schedule::Runner.new(task, logger, notifier).run
data/lib/schedule.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "schedule/task"
2
+ require "schedule/notifier"
3
+ require "schedule/logger"
4
+ require "schedule/runner"
5
+
6
+ module Schedule
7
+ VERSION = "0.0.1"
8
+ end
@@ -0,0 +1,56 @@
1
+ require "thread"
2
+ require "stringio"
3
+
4
+ module Schedule
5
+ class Logger
6
+ LINE_SEPERATOR = /\r\n|\n|\r/ # CRLF, LF or CR
7
+
8
+ attr_reader :buffer
9
+
10
+ def initialize(device, prefix)
11
+ if device == STDOUT
12
+ @device = device
13
+ elsif device.is_a?(String)
14
+ @device = File.open(device, File::WRONLY | File::APPEND | File::CREAT)
15
+ @device.sync = true
16
+ else
17
+ raise ArgumentError, "Log device must be a file path or STDOUT"
18
+ end
19
+
20
+ @formatter = Proc.new { |line, timestamp| "#{timestamp.strftime("%Y-%m-%d %H:%M:%S")} [#{@prefix}] #{line}" }
21
+ @buffer = StringIO.new
22
+ @prefix = prefix
23
+ @semaphore = Mutex.new
24
+ end
25
+
26
+ def log(msg)
27
+ timestamp = Time.now
28
+
29
+ # split into lines, preserving whitespace
30
+ lines = msg.split(LINE_SEPERATOR)
31
+ msg.scan(LINE_SEPERATOR).size.times { lines << "\n" } if lines.empty?
32
+
33
+ unless lines.empty?
34
+ @semaphore.synchronize do
35
+ @device.flock(File::LOCK_EX) if device_is_file?
36
+ begin
37
+ @device.write(lines.map { |line| @formatter.call(line, timestamp) }.join("\n") + "\n")#
38
+ @buffer.write(lines.join("\n") + "\n")
39
+ ensure
40
+ @device.flock(File::LOCK_UN) if device_is_file?
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def close
47
+ @device.close
48
+ end
49
+
50
+ private
51
+
52
+ def device_is_file?
53
+ @device_is_file ||= @device.is_a?(File)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ require "schedule/notifier/base"
2
+ require "schedule/notifier/console"
3
+ require "schedule/notifier/email"
@@ -0,0 +1,21 @@
1
+ module Schedule
2
+ module Notifier
3
+ class Base
4
+ def initialize(opts = {})
5
+ @options = opts
6
+ end
7
+
8
+ def notify(task, message)
9
+ @task = task
10
+ @message = message
11
+ notify!
12
+ end
13
+
14
+ protected
15
+
16
+ def notify!
17
+ raise NotImplementedError, "You must subclass this method!"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ require "schedule/notifier/base"
2
+
3
+ module Schedule
4
+ module Notifier
5
+ class Console < Base
6
+ private
7
+
8
+ def notify!
9
+ mail = []
10
+ mail << "From: #{@options[:from]} <#{@options[:from]}>"
11
+ mail << "To: #{@options[:to]} <#{@options[:to]}>"
12
+ mail << "Subject: #{subject}"
13
+ mail << "\n#{@message}"
14
+ mail = mail.join("\n")
15
+
16
+ puts mail
17
+ end
18
+
19
+ def subject
20
+ "[Schedule] #{@task.name}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ require "schedule/notifier/base"
2
+ require "net/smtp"
3
+ require "tempfile"
4
+
5
+ module Schedule
6
+ module Notifier
7
+ class Email < Base
8
+ private
9
+
10
+ def notify!
11
+ mail = []
12
+ mail << "From: #{@options[:from]} <#{@options[:from]}>" if @options[:from]
13
+ mail << "To: #{@options[:to]} <#{@options[:to]}>"
14
+ mail << "Subject: #{subject}"
15
+ mail << "\n#{@message}"
16
+ mail = mail.join("\n")
17
+
18
+ begin
19
+ Net::SMTP.start("localhost") do |smtp|
20
+ smtp.send_message(mail, @options[:from], @options[:to])
21
+ end
22
+ rescue
23
+ mail_file = Tempfile.new("mail")
24
+ mail_file.write(mail)
25
+ mail_file.close
26
+ begin
27
+ result = system("sendmail #{@options[:to]} < #{File.expand_path(mail_file.path)}")
28
+ raise unless result
29
+ rescue => e
30
+ raise "Could not deliver email using either local SMTP or sendmail!"
31
+ ensure
32
+ mail_file.unlink rescue nil
33
+ end
34
+ end
35
+ end
36
+
37
+ def subject
38
+ "[Schedule] #{@task.name}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,101 @@
1
+ require "schedule/logger"
2
+
3
+ module Schedule
4
+ class Runner
5
+ def initialize(task, logger = nil, notifier = nil)
6
+ @task = task
7
+ @logger = logger || Logger.new(STDOUT, @task.name)
8
+ @notifier = notifier
9
+ end
10
+
11
+ def run
12
+ @logger.log "=== Started (#{@task.command}) ==="
13
+
14
+ status = nil
15
+
16
+ duration = with_timing do
17
+ status = execute(@task.command) do |output|
18
+ buffer = ""
19
+ begin
20
+ while (buffer << output.readpartial(1024))
21
+ if lines = buffer.slice!(/^.*[\n\r]/m)
22
+ @logger.log(lines)
23
+ end
24
+ end
25
+ rescue EOFError
26
+ @logger.log(buffer) unless buffer == ""
27
+ ensure
28
+ buffer = nil
29
+ end
30
+ end
31
+ end
32
+
33
+ exit_status = if status && status.exitstatus == 0
34
+ 0
35
+ elsif status && status.exitstatus
36
+ status.exitstatus
37
+ else
38
+ 1
39
+ end
40
+
41
+ if exit_status == 0
42
+ @logger.log "=== Completed successfully (took #{duration} seconds) ==="
43
+ else
44
+ @logger.log "=== Failed (took #{duration} seconds) ==="
45
+ end
46
+
47
+ if @notifier
48
+ @notifier.notify(@task, @logger.buffer.string)
49
+ end
50
+
51
+ return exit_status
52
+ end
53
+
54
+ private
55
+
56
+ # executes system command as a child process (fork + exec)
57
+ # returns stdout and stderr as a single stream
58
+ # will execute code inside block
59
+ def execute(cmd)
60
+ status = nil
61
+
62
+ begin
63
+ pipe = IO::pipe
64
+
65
+ pid = fork do
66
+ pipe[0].close
67
+ STDOUT.reopen(pipe[1])
68
+ pipe[1].close
69
+
70
+ STDERR.reopen(STDOUT)
71
+
72
+ begin
73
+ exec cmd
74
+ rescue => e
75
+ # only some ruby level errors (e.g. exec('nonexistentfile')), caught here by this rescue
76
+ # once the process is exec'd it'll take us over so any errors at that
77
+ # level will be piped to STDERR/STDOUT and dealt with in out execute block in #run
78
+ STDERR.puts "Error running command: #{e.to_s} (#{e.class.name})"#\n#{e.backtrace.map { |l| " from #{l}" }.join("\n")}"
79
+ exit(1)
80
+ end
81
+ end
82
+
83
+ pipe[1].close
84
+
85
+ yield pipe[0]
86
+ ensure
87
+ _, status = Process.waitpid2(pid)# rescue nil
88
+ pipe.each { |io| io.close unless io.closed? }# rescue nil
89
+ end
90
+
91
+ status
92
+ end
93
+
94
+ def with_timing
95
+ started = Time.now
96
+ yield
97
+ finished = Time.now
98
+ finished - started
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,10 @@
1
+ module Schedule
2
+ class Task
3
+ attr_reader :name, :command
4
+
5
+ def initialize(name, command)
6
+ @name = name
7
+ @command = command
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ 11.times do |i|
4
+ $stdout.printf("\rProgress: %s", "#{i * 10}%")
5
+ $stdout.flush
6
+ sleep 0.25
7
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ `touch #{ARGV.first}`
4
+ puts "Running command..."
5
+ raise "Error!"
6
+ puts "done."
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ `touch #{ARGV.first}`
4
+ puts "Running command..."
5
+ puts "done."
@@ -0,0 +1,44 @@
1
+ require "test_helper"
2
+ require "tempfile"
3
+
4
+ class BinaryTest < Test::Unit::TestCase
5
+ context "command runner" do
6
+ context "successful command" do
7
+ setup do
8
+ @file = Tempfile.new("log")
9
+ @path = @file.path
10
+ @file.close
11
+ @file.unlink
12
+
13
+ command_to_run_path = File.expand_path("../../commands/success", __FILE__)
14
+ @command_to_run = "#{command_to_run_path} #{@path}"
15
+ @command_runner_path = File.expand_path("../../../bin/cr", __FILE__)
16
+ end
17
+
18
+ context "no options" do
19
+ setup do
20
+ @command = %{#{@command_runner_path} "#{@command_to_run}"}
21
+ end
22
+
23
+ should "execute command" do
24
+ silence_stream(STDOUT) do
25
+ system(@command)
26
+ assert File.exists?(@path)
27
+ end
28
+ end
29
+
30
+ should "return status code 0" do
31
+ silence_stream(STDOUT) do
32
+ assert_equal true, system(@command)
33
+ end
34
+ end
35
+
36
+ should "log to stdout"
37
+ end
38
+
39
+ teardown do
40
+ File.unlink(@path) rescue nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "test/unit"
3
+ require "shoulda"
4
+
5
+ class Test::Unit::TestCase
6
+ # from ActiveSupport 3
7
+ def silence_stream(stream)
8
+ old_stream = stream.dup
9
+ stream.reopen(RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ ? "NUL:" : "/dev/null")
10
+ stream.sync = true
11
+ yield
12
+ ensure
13
+ stream.reopen(old_stream)
14
+ end
15
+ end
@@ -0,0 +1,109 @@
1
+ require "test_helper"
2
+ require "schedule/logger"
3
+ require "tempfile"
4
+ require "timecop"
5
+
6
+ class LoggerTest < Test::Unit::TestCase
7
+ context "creating a new logger" do
8
+ context "with the path of an existing file" do
9
+ setup do
10
+ @file = Tempfile.new("log")
11
+ @path = @file.path
12
+
13
+ @file.write "Line 1\nLine2"
14
+ @file.close
15
+
16
+ @logger = Schedule::Logger.new(@path, "")
17
+ end
18
+
19
+ should "not overwrite the file" do
20
+ assert_equal "Line 1\nLine2", File.read(@path)
21
+ end
22
+
23
+ teardown do
24
+ @file.unlink
25
+ end
26
+ end
27
+
28
+ context "with the path of a non-existing file" do
29
+ setup do
30
+ file = Tempfile.new("log")
31
+ @path = file.path
32
+ file.close
33
+ file.unlink
34
+
35
+ @logger = Schedule::Logger.new(@path, "")
36
+ end
37
+
38
+ should "create the file" do
39
+ assert File.exists?(@path)
40
+ end
41
+ end
42
+
43
+ context "with stdout" do
44
+ should "not explode" do
45
+ assert_nothing_raised { Schedule::Logger.new(STDOUT, "") }
46
+ end
47
+ end
48
+ end
49
+
50
+ context "logging a message" do
51
+ setup do
52
+ file = Tempfile.new("log")
53
+ @path = file.path
54
+ file.close
55
+ file.unlink
56
+
57
+ @logger = Schedule::Logger.new(@path, "Awesome")
58
+ @now = Time.now
59
+ end
60
+
61
+ context "single line" do
62
+ setup do
63
+ Timecop.freeze(@now) do
64
+ @logger.log("Testing 123")
65
+ end
66
+ end
67
+
68
+ should "write to file immediately" do
69
+ assert_match "Testing 123", File.read(@path)
70
+ end
71
+
72
+ should "prefix message with time and label" do
73
+ assert_match /^.*#{@now.strftime("%Y-%m-%d %H:%M:%S")}.*Awesome.*Testing 123.*$/, File.read(@path)
74
+ end
75
+
76
+ context "logging a subsequent message" do
77
+ setup do
78
+ @logger.log("Entry number 2")
79
+ end
80
+
81
+ should "be written to a seperate line" do
82
+ lines = File.read(@path).split(/\n/)
83
+ assert_match "Testing 123", lines[0]
84
+ assert_match "Entry number 2", lines[1]
85
+ end
86
+ end
87
+ end
88
+
89
+ context "multiple lines" do
90
+ setup do
91
+ Timecop.freeze(@now) do
92
+ @logger.log("Multiline 1 \n\n Multiline 2\rMultiline 3 ")
93
+ end
94
+ end
95
+
96
+ should "write as multiple, prefixed lines, preserving per-line whitespace and blank lines" do
97
+ expecting = []
98
+ expecting << "#{@now.strftime("%Y-%m-%d %H:%M:%S")} [Awesome] Multiline 1 "
99
+ expecting << "#{@now.strftime("%Y-%m-%d %H:%M:%S")} [Awesome] "
100
+ expecting << "#{@now.strftime("%Y-%m-%d %H:%M:%S")} [Awesome] Multiline 2"
101
+ expecting << "#{@now.strftime("%Y-%m-%d %H:%M:%S")} [Awesome] Multiline 3 "
102
+ expecting << ""
103
+ expecting = expecting.join("\n")
104
+
105
+ assert_match expecting, File.read(@path)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,89 @@
1
+ require "test_helper"
2
+ require "schedule/task"
3
+ require "schedule/runner"
4
+ require "tempfile"
5
+
6
+ class RunnerTest < Test::Unit::TestCase
7
+ context "a successful task" do
8
+ setup do
9
+ @file = Tempfile.new("log")
10
+ @path = @file.path
11
+ @file.close
12
+
13
+ @task = Schedule::Task.new("Test", "echo `pwd` > #{@path}")
14
+ @runner = Schedule::Runner.new(@task)
15
+ end
16
+
17
+ should "return 0" do
18
+ silence_stream(STDOUT) do
19
+ assert_equal 0, @runner.run
20
+ end
21
+ end
22
+
23
+ should "execute command" do
24
+ silence_stream(STDOUT) do
25
+ @runner.run
26
+ assert_equal `pwd`, File.read(@path)
27
+ end
28
+ end
29
+
30
+ teardown do
31
+ @file.unlink
32
+ end
33
+ end
34
+
35
+ context "a non-existent command" do
36
+ setup do
37
+ @task = Schedule::Task.new("Test", "fsjfslsfdanl")
38
+ @runner = Schedule::Runner.new(@task)
39
+ end
40
+
41
+ should "return 1" do
42
+ silence_stream(STDOUT) do
43
+ assert_equal 1, @runner.run
44
+ end
45
+ end
46
+ end
47
+
48
+ context "a command which uses carriage returns to seperate lines" do
49
+ setup do
50
+ @task = Schedule::Task.new("Flushing", File.expand_path("../../commands/flushing", __FILE__))
51
+ @runner = Schedule::Runner.new(@task)
52
+ end
53
+
54
+ should "return 0" do
55
+ silence_stream(STDOUT) do
56
+ assert_equal 0, @runner.run
57
+ end
58
+ end
59
+ end
60
+
61
+ context "a command that raises an exception" do
62
+ setup do
63
+ @file = Tempfile.new("log")
64
+ @path = @file.path
65
+ @file.close
66
+ @file.unlink
67
+
68
+ @task = Schedule::Task.new("Test", File.expand_path("../../commands/raises_exception #{@path}", __FILE__))
69
+ @runner = Schedule::Runner.new(@task)
70
+ end
71
+
72
+ should "execute command" do#
73
+ silence_stream(STDOUT) do
74
+ @runner.run
75
+ assert File.exists?(@path)
76
+ end
77
+ end
78
+
79
+ should "return 1" do
80
+ silence_stream(STDOUT) do
81
+ assert_equal 1, @runner.run
82
+ end
83
+ end
84
+
85
+ teardown do
86
+ File.unlink(@path) rescue nil
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+ require "schedule/task"
3
+
4
+ class TaskTest < Test::Unit::TestCase
5
+ context "a new task" do
6
+ setup do
7
+ @task = Schedule::Task.new("Testing", "pwd")
8
+ end
9
+
10
+ should "return #name of Testing" do
11
+ assert_equal "Testing", @task.name
12
+ end
13
+
14
+ should "return #command of pwd" do
15
+ assert_equal "pwd", @task.command
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schedule
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Mark Dodwell
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-09 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: timecop
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :development
45
+ version_requirements: *id002
46
+ description:
47
+ email: hi@mkdynamic.co.uk
48
+ executables:
49
+ - cr
50
+ extensions: []
51
+
52
+ extra_rdoc_files: []
53
+
54
+ files:
55
+ - Rakefile
56
+ - README.md
57
+ - bin/cr
58
+ - lib/schedule/logger.rb
59
+ - lib/schedule/notifier/base.rb
60
+ - lib/schedule/notifier/console.rb
61
+ - lib/schedule/notifier/email.rb
62
+ - lib/schedule/notifier.rb
63
+ - lib/schedule/runner.rb
64
+ - lib/schedule/task.rb
65
+ - lib/schedule.rb
66
+ - test/commands/flushing
67
+ - test/commands/raises_exception
68
+ - test/commands/success
69
+ - test/integration/binary_test.rb
70
+ - test/test_helper.rb
71
+ - test/unit/logger_test.rb
72
+ - test/unit/runner_test.rb
73
+ - test/unit/task_test.rb
74
+ has_rdoc: true
75
+ homepage: http://github.com/mkdynamic/schedule
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options: []
80
+
81
+ require_paths:
82
+ - bin
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 1
99
+ - 3
100
+ version: "1.3"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.7
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: A Ruby replacement for Cron
108
+ test_files: []
109
+