winever 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in winever.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Maxime Handfield Lapointe
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,66 @@
1
+ # Winever
2
+
3
+ Winever is a gem that adds features to the [whenever](https://github.com/javan/whenever) gem, making it also compatible
4
+ with Windows, without breaking anything when using Whenever only.
5
+
6
+ Winever creates and removes tasks in the Windows task scheduler using [win32-taskscheduler](https://github.com/djberg96/win32-taskscheduler).
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'winever'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install
21
+
22
+ ## Usage
23
+
24
+ Winever adds the command `winever` which behaves similarly to `whenever`.
25
+
26
+ You can use `winever` without parameters to list a "cron file" using an internal syntax. It will tell you which of the
27
+ jobs of your schedule are supported and which ones aren't. Just like with Whenever, you can use `winever -i` to install/update
28
+ the jobs on your Windows machine and `winever -c` to remove them.
29
+
30
+ Winever creates tasks in Windows' task scheduler. In order for a job to be compatible with Winever, it needs to have an
31
+ additional option: task_name. Note that this task_name will have an identifier (similar to Whenever's comment in the crontab)
32
+ added as suffix to enable Winever to remove old tasks when needed.
33
+
34
+ ```ruby
35
+ every 1.day, at: '00:30 am' do
36
+ rake 'my_backup_task', :task_name => 'MyAppBackup'
37
+ end
38
+ ```
39
+
40
+ If you define new job_types in your schedule. Then you will need to define them differently for Winever. To do so, first add
41
+ `require 'winever'` to the top of your schedule file. This will add the function `winever?` to your schedule, which you can
42
+ use to define some tasks only for Windows or for Linux, and to define a job_type differently for Whenever and for Winever.
43
+ This is the basic line for create a job_type for Winever.
44
+
45
+ ```ruby
46
+ if winever?
47
+ job_type :something, ":task_folder|:task_name|:path|command_you_want_executed_here :output"
48
+ else
49
+ job_type :something, "cd :path && command_you_want_executed_here :output"
50
+ end
51
+ ```
52
+
53
+ The pipes (|) are important, so make sure not to remove any.
54
+ If your task doesn't need to be run in the folder of your application (like the existing job_type "command" of Whenever),
55
+ then remove the :path (leaving the pipes around it intact).
56
+
57
+ As of right now the only type of schedule that is supported are the daily ones (run once per day, at a specific time, every day).
58
+ Pull requests welcomed to add more, cron_entry.rb should be the only file needing edit for that.
59
+
60
+ ## Contributing
61
+
62
+ 1. Fork it
63
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
64
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
65
+ 4. Push to the branch (`git push origin my-new-feature`)
66
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'winever'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: whenever [options]"
10
+ opts.on('-i', '--update [identifier]', 'Default: full path to schedule.rb file') do |identifier|
11
+ options[:update] = true
12
+ options[:identifier] = identifier if identifier
13
+ end
14
+ opts.on('-c', '--clear [identifier]') do |identifier|
15
+ options[:clear] = true
16
+ options[:identifier] = identifier if identifier
17
+ end
18
+ opts.on('-s', '--set [variables]', 'Example: --set \'environment=staging&path=/my/sweet/path\'') do |set|
19
+ options[:set] = set if set
20
+ end
21
+ opts.on('-f', '--load-file [schedule file]', 'Default: config/schedule.rb') do |file|
22
+ options[:file] = file if file
23
+ end
24
+ opts.on('-k', '--cut [lines]', 'Cut lines from the top of the cronfile') do |lines|
25
+ options[:cut] = lines.to_i if lines
26
+ end
27
+ opts.on('-v', '--version') { puts "Winever v#{Winever::VERSION}"; exit(0) }
28
+ end.parse!
29
+
30
+ Winever::CommandLine.execute(options)
@@ -0,0 +1,20 @@
1
+ require 'winever/version'
2
+ require 'whenever'
3
+
4
+ # A very tiny monkey patch of whatever, adding a function useable in the schedule to know if this is going through winever.
5
+ module Whenever
6
+ class JobList
7
+ # We are running from winever
8
+ def winever?
9
+ Winever::WheneverInterface.run_from_winever?
10
+ end
11
+ end
12
+ end
13
+
14
+
15
+ module Winever
16
+ autoload :CommandLine, 'winever/command_line'
17
+ autoload :CronEntry, 'winever/cron_entry'
18
+ autoload :TaskManager, 'winever/task_manager'
19
+ autoload :WheneverInterface, 'winever/whenever_interface'
20
+ end
@@ -0,0 +1,54 @@
1
+ module Winever
2
+ class CommandLine
3
+ def self.execute options={}
4
+ new(options).run
5
+ end
6
+
7
+ def initialize options={}
8
+ @options = options
9
+
10
+ @options[:file] ||= 'config/schedule.rb'
11
+ @options[:cut] ||= 0
12
+ @options[:identifier] ||= default_identifier
13
+
14
+ unless File.exists?(@options[:file])
15
+ warn("[fail] Can't find file: #{@options[:file]}")
16
+ exit(1)
17
+ end
18
+
19
+ if [@options[:update], @options[:clear]].compact.length > 1
20
+ warn("[fail] Can only update or clear. Choose one.")
21
+ exit(1)
22
+ end
23
+
24
+ unless @options[:cut].to_s =~ /[0-9]*/
25
+ warn("[fail] Can't cut negative lines from the crontab #{options[:cut]}")
26
+ exit(1)
27
+ end
28
+ @options[:cut] = @options[:cut].to_i
29
+ end
30
+
31
+
32
+ def run
33
+ if @options[:update]
34
+ Winever::TaskManager.update_tasks(@options)
35
+ elsif @options[:clear]
36
+ Winever::TaskManager.clear_tasks(@options)
37
+ else
38
+ puts Winever::WheneverInterface.cron(@options)
39
+ puts "## [message] Above is your schedule file converted to cron-winever syntax; your crontab file /scheduled tasks were not updated."
40
+ puts "## [message] Run `winever --help' for more options."
41
+ exit(0)
42
+ end
43
+ end
44
+
45
+ #protected
46
+
47
+ def default_identifier
48
+ File.expand_path(@options[:file])
49
+ end
50
+
51
+ end
52
+
53
+
54
+ end
@@ -0,0 +1,58 @@
1
+ module Winever
2
+ class CronEntry
3
+ attr_accessor :cron_time, :task_folder, :task_name, :working_directory, :parameters, :cron_line
4
+
5
+ def self.from_cron_output cron_output, include_invalid=false
6
+ entries = cron_output.split("\n").select(&:present?).map{|o| new(o)}
7
+ entries = entries.select(&:valid?) unless include_invalid
8
+ entries
9
+ end
10
+
11
+ def triggers
12
+ # For now, we don't support anything other than specific time.
13
+ # But it is possible to handle almost all cron schedule options in the task scheduler of Windows.
14
+ # It doesn't help that win32-taskscheduler also seems to only support one trigger per task.
15
+ return [] unless valid_triggers?
16
+
17
+ cron_minute, cron_hour, cron_day, cron_month, cron_dow = @cron_time_parts
18
+ trigger = {
19
+ :start_year => Date.today.year,
20
+ :start_month => Date.today.month,
21
+ :start_day => Date.today.day,
22
+ :start_hour => cron_hour.to_i,
23
+ :start_minute => cron_minute.to_i,
24
+ :trigger_type => Win32::TaskScheduler::TASK_TIME_TRIGGER_DAILY
25
+ }
26
+
27
+ [trigger]
28
+ end
29
+
30
+ def initialize(cron_line)
31
+ @cron_line = cron_line
32
+ @cron_parts = cron_line.split("|", 5)
33
+ @cron_time, @task_folder, @task_name, @working_directory, @parameters = @cron_parts
34
+ @cron_time_parts = @cron_time.split(/ +/)
35
+ end
36
+
37
+ def valid?
38
+ invalid_reason.nil?
39
+ end
40
+
41
+ def valid_triggers?
42
+ return false if @cron_time_parts.length < 5
43
+ cron_minute, cron_hour, cron_day, cron_month, cron_dow = @cron_time_parts
44
+
45
+ return false if [cron_day, cron_month, cron_dow].detect{|v| v != '*'}
46
+ return false if [cron_minute, cron_hour].detect{|v| (v =~ /^\d+$/).nil? }
47
+
48
+ true
49
+ end
50
+
51
+ def invalid_reason
52
+ return "Doesn't match the Winever format" unless @cron_parts.length == 5
53
+ return "Doesn't have a task_name specified" unless @task_name.present?
54
+ return "The schedule is either invalid or not supported" unless valid_triggers?
55
+ nil
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+
2
+ if winever?
3
+ set :job_template, "|:job"
4
+
5
+ # Overwrite to put tasks in a different subfolder of the task scheduler.
6
+ # Right now, anything other than \\ will break clear_tasks, so don't change folder for now.
7
+ set :task_folder, "\\"
8
+
9
+ job_type :command, ":task_folder|:task_name||:task :output"
10
+ job_type :rake, ":task_folder|:task_name|:path|:bundle_command rake :task --silent :environment_variable=:environment :output"
11
+ job_type :script, ":task_folder|:task_name|:path|:bundle_command script/:task :environment_variable=:environment :output"
12
+ job_type :runner, ":task_folder|:task_name|:path|:runner_command -e :environment ':task' :output"
13
+ end
@@ -0,0 +1,158 @@
1
+ module Winever
2
+ class TaskManager
3
+ def self.has_task_scheduler?
4
+ return @has_task_scheduler unless @has_task_scheduler.nil?
5
+ begin
6
+ require 'win32/taskscheduler'
7
+ @has_task_scheduler = true
8
+ rescue LoadError => e
9
+ @has_task_scheduler = false
10
+ end
11
+ @has_task_scheduler
12
+ end
13
+
14
+ def has_task_scheduler?
15
+ self.class.has_task_scheduler?
16
+ end
17
+
18
+ def self.clear_tasks options={}
19
+ new(options).clear_tasks_except
20
+ end
21
+
22
+ def self.update_tasks options={}
23
+ new(options).update_tasks
24
+ end
25
+
26
+ def initialize options={}
27
+ if !has_task_scheduler?
28
+ raise "Cannot use win32/taskscheduler on this system. Are you on windows?"
29
+ end
30
+
31
+ @options = options
32
+ end
33
+
34
+ def password
35
+ return @password.presence if @password
36
+ require 'highline/import'
37
+ prompt = <<-PRMP.gsub(/^ +/, '')
38
+ To setup tasks correctly, the password of the current user account is needed.
39
+ You can leave it empty, but without the password, the task will only be run the user is logged on and will open a black
40
+ console window while running.
41
+ You can manually go edit the scheduled task to add the password manually if you prefer not to give it to Winever, but you will need to go do that every time you edit the tasks through Winever, for each task.
42
+ Enter the password of the current user (or just press enter to skip):
43
+ PRMP
44
+
45
+ pw = ask(prompt){|q| q.echo = false}
46
+ while pw.present? && !validate_password(pw)
47
+ prompt = <<-PRMP.gsub(/^ +/, '')
48
+ Invalid password entered.
49
+ Enter the password of the current user (or just press enter to skip):
50
+ PRMP
51
+ pw = ask(prompt){|q| q.echo = false}
52
+ end
53
+
54
+
55
+ #TODO get the password somehow.
56
+ # require File.expand_path('../../extensions/password', __FILE__)
57
+ # password = Password.ask("Enter password for current user account of the machine to setup tasks: ")
58
+ # validate_password(password)
59
+
60
+ @password = pw
61
+ pw.presence
62
+ end
63
+
64
+ def create_tasks
65
+ cron_entries = Winever::WheneverInterface.valid_cron_entries(@options)
66
+
67
+ created_task_names = []
68
+ cron_entries.each do |cron_entry|
69
+ created_task_names << create_task(cron_entry)
70
+ end
71
+ created_task_names
72
+ end
73
+
74
+ def update_tasks
75
+ task_names = create_tasks
76
+ clear_tasks_except(task_names)
77
+ end
78
+
79
+ def clear_tasks_except keep_tasks=[]
80
+ ts = Win32::TaskScheduler.new
81
+ task_names = ts.tasks.select{|tn| tn.end_with?('.' + identifier)}
82
+ task_names = task_names.reject{|tn| keep_tasks.include?(tn)}
83
+
84
+ task_names.each{|tn| ts.delete(tn)}
85
+ end
86
+
87
+ def create_task cron_entry
88
+ task_name = generate_task_name(cron_entry.task_name)
89
+
90
+ # Replacing the /dev/null by NUL
91
+ parameters = cron_entry.parameters.gsub(/([\s'"])\/dev\/null([\s'"])/, '\1NUL\2')
92
+
93
+ pw = password
94
+ trigger = cron_entry.triggers.first
95
+ work_directory = cron_entry.working_directory
96
+
97
+ ts = Win32::TaskScheduler.new(nil, nil, cron_entry.task_folder, true)
98
+ begin
99
+ ts.password = pw
100
+ ts.new_work_item(task_name, trigger)
101
+ ts.application_name = 'cmd'
102
+ ts.parameters = '/C ' + parameters
103
+ ts.working_directory = work_directory
104
+ ts.activate(task_name)
105
+ rescue
106
+ raise 'Failed at setting the task up. It might have been partially created/updated. This most likely means a bad password was entered.'
107
+ end
108
+
109
+ task_name
110
+ end
111
+
112
+ def generate_task_name task_name
113
+ "#{task_name}.#{identifier}"
114
+ end
115
+
116
+ def identifier
117
+ # Removing the characters blocked by the windows file system. The single quote is just for simplicity.
118
+ iden = @options[:identifier].gsub(/[:\/\\<>:"|?*']/, '_')
119
+ raise 'Identifier must contain at least one letter or number.' unless iden =~ /\w/
120
+ iden
121
+ end
122
+
123
+ def validate_password password
124
+ # Validate a password by trying to create a task with it. If it fails, then the password is wrong.
125
+ # Will delete the created task after.
126
+ ts = Win32::TaskScheduler.new
127
+ base_test_task_name = test_task_name = "Winever_test_task"
128
+ i = 0
129
+ while ts.exists?(test_task_name)
130
+ i += 1
131
+ test_task_name = "#{base_test_task_name}_#{i}"
132
+ end
133
+
134
+ trigger = { :start_year => 2000,
135
+ :start_month => 6,
136
+ :start_day => 12,
137
+ :start_hour => 13,
138
+ :start_minute => 17,
139
+ :trigger_type => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE}
140
+
141
+ ts.new_work_item(test_task_name, trigger)
142
+ valid = false
143
+ begin
144
+ ts.password = password
145
+ ts.application_name = "cmd"
146
+ valid = true
147
+ rescue
148
+ ts.password = nil
149
+ valid = false
150
+ end
151
+ ts.delete(test_task_name)
152
+
153
+ return valid
154
+ end
155
+
156
+ end
157
+
158
+ end
@@ -0,0 +1,3 @@
1
+ module Winever
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,64 @@
1
+ module Winever
2
+ module WheneverInterface
3
+ def self.run_from_winever?
4
+ @run_from_winever || false
5
+ end
6
+
7
+ def self.raw_cron options={}
8
+ # The output of whenever with the custom job_types and job_template.
9
+ options[:file] ||= 'config/schedule.rb'
10
+ options[:cut] ||= 0
11
+ options[:identifier] ||= File.expand_path(options[:file])
12
+
13
+ schedule = if options[:string]
14
+ options[:string]
15
+ elsif options[:file]
16
+ File.read(options[:file])
17
+ end
18
+
19
+ # Prepending out own setup for the schedule to override the existing job_types and job_template.
20
+ options[:string] = File.read(File.dirname(__FILE__)+"/setup_schedule.rb") + "\n" + schedule
21
+
22
+ @run_from_winever = true
23
+ output = Whenever.cron(options)
24
+ @run_from_winever = false
25
+ output
26
+ end
27
+
28
+ def self.valid_cron_entries options={}
29
+ # Array of CronEntry containing only the entry that we support.
30
+ Winever::CronEntry.from_cron_output(raw_cron(options))
31
+ end
32
+
33
+ def self.all_cron_entries options={}
34
+ # Array of CronEntry containing only the entry that we support.
35
+ Winever::CronEntry.from_cron_output(raw_cron(options), true)
36
+ end
37
+
38
+ def self.cron options={}
39
+ entries = all_cron_entries(options)
40
+ valid_entries = entries.select(&:valid?)
41
+ invalid_entries = entries.reject(&:valid?)
42
+
43
+
44
+ output = "# Valid tasks for Winever in internal format:\n"
45
+ if valid_entries.present?
46
+ output << valid_entries.map(&:cron_line).join("\n\n")
47
+ else
48
+ output << "No valid entries"
49
+ end
50
+ output << "\n\n"
51
+
52
+ if invalid_entries.present?
53
+ output << "\n# Invalid entries for Winever in internal format:\n"
54
+ invalid_entries.each do |invalid_entry|
55
+ output << "# #{invalid_entry.invalid_reason}\n"
56
+ output << "#{invalid_entry.cron_line}\n\n"
57
+ end
58
+ end
59
+ output
60
+ end
61
+ end
62
+
63
+
64
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'winever/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "winever"
8
+ spec.version = Winever::VERSION
9
+ spec.authors = ["Maxime Handfield Lapointe"]
10
+ spec.email = ["hunter_spawn@hotmail.com"]
11
+ spec.description = %q{Make it possible to use the Whenever gem's clean ruby syntax for writing and deploying tasks in the windows scheduler, using the same schedule file.}
12
+ spec.summary = %q{Make it possible to use the Whenever gem on Windows.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "whenever", ">= 0.9.1"
22
+ spec.add_dependency "highline", ">= 0.5.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: winever
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Maxime Handfield Lapointe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-06-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: whenever
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: highline
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.5.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.5.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Make it possible to use the Whenever gem's clean ruby syntax for writing
79
+ and deploying tasks in the windows scheduler, using the same schedule file.
80
+ email:
81
+ - hunter_spawn@hotmail.com
82
+ executables:
83
+ - winever
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - bin/winever
93
+ - lib/winever.rb
94
+ - lib/winever/command_line.rb
95
+ - lib/winever/cron_entry.rb
96
+ - lib/winever/setup_schedule.rb
97
+ - lib/winever/task_manager.rb
98
+ - lib/winever/version.rb
99
+ - lib/winever/whenever_interface.rb
100
+ - winever.gemspec
101
+ homepage: ''
102
+ licenses:
103
+ - MIT
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ segments:
115
+ - 0
116
+ hash: -2189449078382166788
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ segments:
124
+ - 0
125
+ hash: -2189449078382166788
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.25
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Make it possible to use the Whenever gem on Windows.
132
+ test_files: []