taskloop 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.idea/.gitignore +8 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/taskloop.iml +51 -0
- data/.idea/vcs.xml +6 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +36 -0
- data/README.md +31 -0
- data/Rakefile +4 -0
- data/exe/taskloop +16 -0
- data/lib/taskloop/command/deploy.rb +136 -0
- data/lib/taskloop/command/env.rb +134 -0
- data/lib/taskloop/command/init.rb +42 -0
- data/lib/taskloop/command/launch.rb +67 -0
- data/lib/taskloop/command/list.rb +59 -0
- data/lib/taskloop/command/log.rb +104 -0
- data/lib/taskloop/command/run.rb +106 -0
- data/lib/taskloop/command/shutdown.rb +49 -0
- data/lib/taskloop/command/undeploy.rb +46 -0
- data/lib/taskloop/command.rb +168 -0
- data/lib/taskloop/dsl/dsl.rb +63 -0
- data/lib/taskloop/extension/integer_extension.rb +25 -0
- data/lib/taskloop/extension/string_extension.rb +18 -0
- data/lib/taskloop/rules/after_scope_rule.rb +91 -0
- data/lib/taskloop/rules/before_scope_rule.rb +90 -0
- data/lib/taskloop/rules/between_scope_rule.rb +130 -0
- data/lib/taskloop/rules/date_list_rule.rb +47 -0
- data/lib/taskloop/rules/default_rule.rb +19 -0
- data/lib/taskloop/rules/interval_rule.rb +26 -0
- data/lib/taskloop/rules/loop_rule.rb +25 -0
- data/lib/taskloop/rules/rule.rb +34 -0
- data/lib/taskloop/rules/scope_rule.rb +22 -0
- data/lib/taskloop/rules/specific_rule.rb +64 -0
- data/lib/taskloop/rules/time_list_rule.rb +49 -0
- data/lib/taskloop/task/task.rb +244 -0
- data/lib/taskloop/task/task_data_file.rb +46 -0
- data/lib/taskloop/task/task_error.rb +17 -0
- data/lib/taskloop/task/task_property.rb +294 -0
- data/lib/taskloop/utils/proj_tasklist.rb +145 -0
- data/lib/taskloop/version.rb +5 -0
- data/lib/taskloop.rb +9 -0
- data/sig/taskloop.rbs +4 -0
- data/taskloop.gemspec +37 -0
- metadata +103 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
class Log < Command
|
3
|
+
self.abstract_command = false
|
4
|
+
|
5
|
+
self.summary = "Check log"
|
6
|
+
|
7
|
+
self.description = <<-DESC
|
8
|
+
The 'taskloop log' is used to check log. It supports two options to check different kinds of log.
|
9
|
+
With '--task-name=TASK_NAME' option, you can check a specific task' log.
|
10
|
+
With '--cron' option, you can check the cron log of taskloop.
|
11
|
+
DESC
|
12
|
+
|
13
|
+
def self.options
|
14
|
+
[
|
15
|
+
['--task-name=TASK_NAME', "To show the log of a task specified by option."],
|
16
|
+
['--cron', 'To show the log of taskloop, which is based on cron.'],
|
17
|
+
].concat(super)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(argv)
|
21
|
+
@task_name = argv.option('task-name')
|
22
|
+
@cron = argv.flag?('cron', false)
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate!
|
27
|
+
super
|
28
|
+
if @task_name && @cron
|
29
|
+
help! "The --task-name option and the --cron option cannot be used simultaneously."
|
30
|
+
end
|
31
|
+
|
32
|
+
if @task_name == nil && !@cron
|
33
|
+
help! "Use --task-name option or --cron option for 'taskloop log' command."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
super
|
39
|
+
if @task_name
|
40
|
+
check_log_of_task(@task_name)
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
if @cron
|
45
|
+
check_log_of_cron
|
46
|
+
return
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_log_of_task(name)
|
51
|
+
found = false
|
52
|
+
data_proj_dirs = Dir.entries(taskloop_data_dir)
|
53
|
+
data_proj_dirs.each do |dir|
|
54
|
+
if dir == "." or dir == ".."
|
55
|
+
next
|
56
|
+
end
|
57
|
+
data_proj_dir = File.join(taskloop_data_dir, dir)
|
58
|
+
|
59
|
+
print_proj = false
|
60
|
+
log_files = Dir.entries(data_proj_dir)
|
61
|
+
log_files.each do |file|
|
62
|
+
if "#{name.sha1}_log" == file
|
63
|
+
found = true
|
64
|
+
|
65
|
+
if !print_proj
|
66
|
+
print_proj = true
|
67
|
+
desc_file_path = File.join(data_proj_dir, ".description")
|
68
|
+
puts "=============================".ansi.blue
|
69
|
+
File.open(desc_file_path).each_line do |line|
|
70
|
+
puts "Project of <#{line.strip}>".ansi.blue
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# print
|
74
|
+
task_log_path = File.join(data_proj_dir, "#{name.sha1}_log")
|
75
|
+
puts "Log of <Task.name: #{name}> above: ".ansi.blue
|
76
|
+
File.open(task_log_path).each_line do |line|
|
77
|
+
puts line
|
78
|
+
end
|
79
|
+
|
80
|
+
puts "=============================".ansi.blue
|
81
|
+
puts ""
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
unless found
|
87
|
+
puts "Warning: log of <Task.name: #{name}> not exist. Please check if the name of task is correct.".ansi.yellow
|
88
|
+
puts ""
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_log_of_cron
|
93
|
+
puts "=============================".ansi.blue
|
94
|
+
puts "Log of cron: ".ansi.blue
|
95
|
+
puts ""
|
96
|
+
log_file = File.open(taskloop_cron_log_path, "r")
|
97
|
+
log_file.each_line do |line|
|
98
|
+
puts line
|
99
|
+
end
|
100
|
+
puts "=============================".ansi.blue
|
101
|
+
puts ""
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
class Run < Command
|
3
|
+
require_relative '../task/task'
|
4
|
+
require_relative '../rules/rule'
|
5
|
+
require_relative '../rules/interval_rule'
|
6
|
+
require_relative '../rules/scope_rule'
|
7
|
+
require_relative '../rules/default_rule'
|
8
|
+
require_relative '../rules/specific_rule'
|
9
|
+
require_relative '../rules/after_scope_rule'
|
10
|
+
require_relative '../rules/before_scope_rule'
|
11
|
+
require_relative '../rules/between_scope_rule'
|
12
|
+
require_relative '../rules/loop_rule'
|
13
|
+
require_relative '../rules/date_list_rule'
|
14
|
+
require_relative '../rules/time_list_rule'
|
15
|
+
require_relative '../extension/string_extension'
|
16
|
+
require_relative '../extension/integer_extension'
|
17
|
+
require_relative '../utils/proj_tasklist'
|
18
|
+
require_relative '../dsl/dsl'
|
19
|
+
require 'open3'
|
20
|
+
|
21
|
+
include TaskLoop::DSL
|
22
|
+
include TaskLoop::ProjTaskList
|
23
|
+
|
24
|
+
self.abstract_command = false
|
25
|
+
|
26
|
+
self.summary = "Execute all the registered tasks that meet their requirements."
|
27
|
+
|
28
|
+
self.description = <<-DESC
|
29
|
+
The `taskloop run` command will execute all the registered tasks that meet their requiremets.
|
30
|
+
Taskloop will read all the registered Taskfiles from `~/.taskloop/tasklist.json`, then execute
|
31
|
+
each Tasfile to figure out if there is any task need to be executed. If needed, they will be
|
32
|
+
executed.
|
33
|
+
DESC
|
34
|
+
|
35
|
+
#################################
|
36
|
+
# Utils Methods
|
37
|
+
#################################
|
38
|
+
|
39
|
+
def run
|
40
|
+
super
|
41
|
+
create_data_proj_dir_if_needed
|
42
|
+
create_data_proj_description_if_needed
|
43
|
+
|
44
|
+
construct_proj_tasklist_map
|
45
|
+
setup_task_property
|
46
|
+
|
47
|
+
create_data_proj_task_log_if_needed
|
48
|
+
create_data_proj_task_time_if_needed
|
49
|
+
create_data_proj_task_loop_if_needed
|
50
|
+
|
51
|
+
execute_tasks_if_needed
|
52
|
+
clean_cache_file_if_needed
|
53
|
+
end
|
54
|
+
|
55
|
+
private def execute_tasks_if_needed
|
56
|
+
unless @proj_tasklist_map != nil
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
puts "Trigger Time: <#{Time.now}>"
|
61
|
+
@proj_tasklist_map.each do |proj, list|
|
62
|
+
list.each do |task|
|
63
|
+
unless task.check_rule_conflict?
|
64
|
+
puts "Warning: There is a rule conflict in #{task.desc}, taskloop will skip its execution.".ansi.yellow
|
65
|
+
next
|
66
|
+
end
|
67
|
+
unless task.check_all_rules?
|
68
|
+
puts "Checking: #{task.desc} does not meet the execution rules, taskloop will skip its execution.".ansi.blue
|
69
|
+
next
|
70
|
+
end
|
71
|
+
puts "Checking: #{task.desc} does meet the execution rules, taskloop start to execute.".ansi.blue
|
72
|
+
execute_task(proj, task)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private def execute_task(proj, task)
|
78
|
+
path = task.path
|
79
|
+
unless path[0] == '/'
|
80
|
+
path = File.join(proj, path)
|
81
|
+
end
|
82
|
+
|
83
|
+
unless File.exists?(path)
|
84
|
+
errmsg = "No such file or directory - #{path}"
|
85
|
+
task.write_to_logfile(errmsg)
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
89
|
+
# record execute timestamp into task's timefile
|
90
|
+
timestamp = Time.now.to_i
|
91
|
+
task.write_to_timefile(timestamp)
|
92
|
+
count = task.loop_count + 1
|
93
|
+
task.write_to_loopfile(count)
|
94
|
+
|
95
|
+
cmd = path
|
96
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
97
|
+
# record execute information into task's logfile
|
98
|
+
out = stdout.read
|
99
|
+
err = stderr.read
|
100
|
+
content = out + "\n" + err
|
101
|
+
content = "<Trigger Time: #{Time.now}>\n" + content
|
102
|
+
task.write_to_logfile(content)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
class Shutdown < Command
|
3
|
+
self.abstract_command = false
|
4
|
+
|
5
|
+
self.summary = "Shutdown taskloop. "
|
6
|
+
|
7
|
+
self.description = <<-DESC
|
8
|
+
The `taskloop shutdown` command will shutdown taskloop. Therefore, every Taskfile registered in taskloop will stop.
|
9
|
+
It is a global switch that controls taskloop. If you execute `taskloop launch` command again after executing 'taskloop shutdown',
|
10
|
+
every Taskfile you deployed before will resume automatically.
|
11
|
+
DESC
|
12
|
+
def run
|
13
|
+
super
|
14
|
+
unregister_taskloop_from_crontab_if_needed
|
15
|
+
end
|
16
|
+
|
17
|
+
def unregister_taskloop_from_crontab_if_needed
|
18
|
+
system("crontab -l > #{taskloop_cron_tab_path}")
|
19
|
+
registered = false
|
20
|
+
pattern = /\A\* \* \* \* \* sh ~\/\.tasklooprc/
|
21
|
+
remain = []
|
22
|
+
File.open(taskloop_cron_tab_path, "r").each_line do |line|
|
23
|
+
if line.match?(pattern)
|
24
|
+
registered = true
|
25
|
+
else
|
26
|
+
remain << line
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless registered
|
31
|
+
puts "Warning: taskloop has already shutdown. Please do not shutdown again.".ansi.yellow
|
32
|
+
puts " If your want to launch taskloop, please execute the `taskloop launch` command.".ansi.yellow
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
File.open(taskloop_cron_tab_path, "w") do |file|
|
37
|
+
file.write(remain.join)
|
38
|
+
end
|
39
|
+
|
40
|
+
system("crontab #{taskloop_cron_tab_path}")
|
41
|
+
|
42
|
+
puts LOGO.ansi.blue
|
43
|
+
puts " taskloop has shutdown successfully. ".ansi.blue
|
44
|
+
puts ""
|
45
|
+
puts " byeeeeeeeeeeeeeeeee !".ansi.blue
|
46
|
+
puts ""
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module TaskLoop
|
4
|
+
class Undeploy < Command
|
5
|
+
self.abstract_command = false
|
6
|
+
|
7
|
+
self.summary = "Undeploy a Taskfile from taskloop."
|
8
|
+
|
9
|
+
self.description = <<-DESC
|
10
|
+
The "taskloop undeploy" command will undeploy a Taskfile from taskloop. It should be executed in the directory where
|
11
|
+
Taskfile exists.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def run
|
15
|
+
super
|
16
|
+
unless File.exists?(:Taskfile.to_s)
|
17
|
+
puts "Error: 'taskloop undeploy' command should be executed in the directory where Taskfile exists.".ansi.red
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
remove_proj_path_from_projlist
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_proj_path_from_projlist
|
25
|
+
current_dir = Dir.pwd
|
26
|
+
proj_list_dirs = taskloop_proj_list_dirs
|
27
|
+
unless proj_list_dirs.include?(current_dir)
|
28
|
+
puts "Warning: current Taskfile is not deployed before. Do not need to undeploy it again.".ansi.yellow
|
29
|
+
return
|
30
|
+
end
|
31
|
+
proj_list_dirs.delete(current_dir)
|
32
|
+
File.open(taskloop_proj_list_path, "w") do |file|
|
33
|
+
file.write(JSON.pretty_generate(proj_list_dirs))
|
34
|
+
end
|
35
|
+
|
36
|
+
data_proj_dir = File.join(taskloop_data_dir, current_dir.sha1_8bit)
|
37
|
+
unless File.directory?(data_proj_dir)
|
38
|
+
puts "Warning: #{data_proj_dir} not exist. ".ansi.yellow
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
FileUtils.rm_rf(data_proj_dir)
|
43
|
+
puts "Taskfile in <#{current_dir}> has been undeployed successfully.".ansi.blue
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
require 'claide'
|
3
|
+
|
4
|
+
class Command < CLAide::Command
|
5
|
+
require 'digest'
|
6
|
+
require 'taskloop/extension/string_extension'
|
7
|
+
require 'taskloop/command/init'
|
8
|
+
require 'taskloop/command/list'
|
9
|
+
require 'taskloop/command/log'
|
10
|
+
require 'taskloop/command/deploy'
|
11
|
+
require 'taskloop/command/run'
|
12
|
+
require 'taskloop/command/launch'
|
13
|
+
require 'taskloop/command/shutdown'
|
14
|
+
require 'taskloop/command/env'
|
15
|
+
require 'taskloop/command/undeploy'
|
16
|
+
|
17
|
+
self.abstract_command = true
|
18
|
+
|
19
|
+
self.description = <<-DESC
|
20
|
+
Taskloop is a scheduled task manager based on cron. It offers a more clear syntax, more convenient log management.
|
21
|
+
It also solves environment variable import issues, and provides a more user-friendly experience.'
|
22
|
+
DESC
|
23
|
+
|
24
|
+
self.command = 'taskloop'
|
25
|
+
|
26
|
+
def initialize(argv)
|
27
|
+
# @verbose = argv.flag?('verbose', true)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
create_taskloop_file_structure_if_needed
|
33
|
+
end
|
34
|
+
|
35
|
+
#################################
|
36
|
+
# Path and Directory and Files
|
37
|
+
#################################
|
38
|
+
def create_taskloop_file_structure_if_needed
|
39
|
+
# create ~/.taskloop/ dir
|
40
|
+
create_dir_if_needed(taskloop_dir)
|
41
|
+
# create ~/.taskloop/projlist file
|
42
|
+
unless File.exists?(taskloop_proj_list_path)
|
43
|
+
projlist = File.new(taskloop_proj_list_path, "w+")
|
44
|
+
projlist.puts "[]"
|
45
|
+
projlist.close
|
46
|
+
end
|
47
|
+
|
48
|
+
# create ~/.taskloop/crontab file
|
49
|
+
create_file_if_needed(taskloop_cron_tab_path)
|
50
|
+
# create ~/.taskloop/cronlog file
|
51
|
+
create_file_if_needed(taskloop_cron_log_path)
|
52
|
+
|
53
|
+
# create ~/.taskloop/data/ dir
|
54
|
+
create_dir_if_needed(taskloop_data_dir)
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_file_if_needed(path)
|
58
|
+
unless File.file?(path)
|
59
|
+
File.new(path, "w")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def create_dir_if_needed(dir)
|
63
|
+
unless File.directory?(dir)
|
64
|
+
FileUtils.mkdir(dir)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def tasklooprc_path
|
69
|
+
File.join(Dir.home, ".tasklooprc")
|
70
|
+
end
|
71
|
+
|
72
|
+
def taskloop_dir
|
73
|
+
File.join(Dir.home, ".taskloop")
|
74
|
+
end
|
75
|
+
|
76
|
+
def taskloop_cron_log_path
|
77
|
+
File.join(taskloop_dir, "cronlog")
|
78
|
+
end
|
79
|
+
|
80
|
+
def taskloop_cron_tab_path
|
81
|
+
File.join(taskloop_dir, "crontab")
|
82
|
+
end
|
83
|
+
|
84
|
+
def taskloop_environments_path
|
85
|
+
File.join(taskloop_dir, "environments")
|
86
|
+
end
|
87
|
+
|
88
|
+
def taskloop_proj_list_path
|
89
|
+
File.join(taskloop_dir, "projlist")
|
90
|
+
end
|
91
|
+
|
92
|
+
def taskloop_data_dir
|
93
|
+
File.join(taskloop_dir, "data")
|
94
|
+
end
|
95
|
+
|
96
|
+
def taskloop_proj_list_dirs
|
97
|
+
json_string = File.read(taskloop_proj_list_path)
|
98
|
+
parsed_json = JSON.parse(json_string)
|
99
|
+
return parsed_json
|
100
|
+
end
|
101
|
+
|
102
|
+
def taskloop_data_proj_dirs
|
103
|
+
dirs = Dir.entries(taskloop_data_dir)
|
104
|
+
result = []
|
105
|
+
dirs.each do |dir|
|
106
|
+
if dir != '.' && dir != '..'
|
107
|
+
result.push(dir)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
return result
|
111
|
+
end
|
112
|
+
|
113
|
+
def taskloop_taskfile_paths
|
114
|
+
paths = taskloop_data_proj_dirs
|
115
|
+
result = []
|
116
|
+
paths.each do |path|
|
117
|
+
result.push(File.join(path, ".Taskfile.deploy"))
|
118
|
+
end
|
119
|
+
return result
|
120
|
+
end
|
121
|
+
|
122
|
+
LOGO = <<-DESC
|
123
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
124
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
125
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
126
|
+
@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
127
|
+
@@@@@@@@@@@ @@@@@@ @@@@@ @@@ @@ @@ @@@@@ @@@@ @@@ @@@@@@@@@
|
128
|
+
@@@@@@@@@@@ @@@@ @@ @@ @@@ @@ @ @@@ @@@@ @@ @@ @@ @@ @@ @@@@@@@@
|
129
|
+
@@@@@@@@@@@ @@@@@@@@@ @@@ @@@@@@ @@@@ @@@@ @@ @@ @@ @@ @@ @@@@@@@@
|
130
|
+
@@@@@@@@@@@ @@@@ @@@@@@ @@@ @@@@ @@@@ @@ @@ @@ @@ @@ @@@@@@@@
|
131
|
+
@@@@@@@@@@@ @@@ @@@ @@ @@@ @@ @ @@@ @@@@ @@ @@ @@ @@ @@ @@@@@@@@
|
132
|
+
@@@@@@@@@@@ @@@@ @@@ @@@ @@ @@ @@@ @@@@ @@@ @@@@@@@@@
|
133
|
+
@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@
|
134
|
+
@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
135
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
136
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
137
|
+
DESC
|
138
|
+
|
139
|
+
DOLPHIN = <<-DESC
|
140
|
+
|
141
|
+
(@&/////%@@@@@@@@@@@@@#
|
142
|
+
@@&@&////////////////(@@#
|
143
|
+
/@(///////////////////////%@/
|
144
|
+
*@////////////////////////////#@,
|
145
|
+
@&///////////////////////////////@%
|
146
|
+
@&//////////////(@////@&#/////////(@
|
147
|
+
@////////////@@ @////@ ,.@////@@
|
148
|
+
/@//////////&@ ,@@////@@@&. *///@@
|
149
|
+
,@/////////(@@@ @///&@ /@@///@@
|
150
|
+
@%////////@@ @@@& @@/@#
|
151
|
+
@#///////@ ,
|
152
|
+
@@//////@& @(//@
|
153
|
+
@@/////%@ @////@
|
154
|
+
@@/////#@@@////@
|
155
|
+
&@@@&#((&@@
|
156
|
+
/&# @///@@
|
157
|
+
,///,*. @////@
|
158
|
+
&# %/,@ @@///@
|
159
|
+
(, .*/&*%/*%///& (@@@ ,*/////*.
|
160
|
+
%/#@, ////& @ % .& /@%/(/ /#/#@( #@#/&
|
161
|
+
(/& . .////, &//
|
162
|
+
&(//@ &//////& @///@
|
163
|
+
(@%//////#&@@@@@@@@@@%///////@@ @@///////%@@@@@@@@@@&#//////#@#
|
164
|
+
%@@@@&@@@@@&&@@@@/ /@@@@&&@@@@@&@@@@&
|
165
|
+
DESC
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
module DSL
|
3
|
+
|
4
|
+
#################################
|
5
|
+
# Loop Syntax
|
6
|
+
#################################
|
7
|
+
def interval(interval)
|
8
|
+
IntervalRule.new(:unknown, interval)
|
9
|
+
end
|
10
|
+
|
11
|
+
#################################
|
12
|
+
# Specific Syntax
|
13
|
+
#################################
|
14
|
+
def at(value)
|
15
|
+
SpecificRule.new(:unknown, value)
|
16
|
+
end
|
17
|
+
|
18
|
+
#################################
|
19
|
+
# Scope Syntax
|
20
|
+
#################################
|
21
|
+
def before(right)
|
22
|
+
BeforeScopeRule.new(:unknown, :before, right)
|
23
|
+
end
|
24
|
+
|
25
|
+
def between(left, right)
|
26
|
+
BetweenScopeRule.new(:unknown, :between, left, right)
|
27
|
+
end
|
28
|
+
|
29
|
+
def after(left)
|
30
|
+
AfterScopeRule.new(:unknown, :after, left)
|
31
|
+
end
|
32
|
+
|
33
|
+
#################################
|
34
|
+
# Loop Syntax
|
35
|
+
#################################
|
36
|
+
def loop(count)
|
37
|
+
LoopRule.new(:unknown, count)
|
38
|
+
end
|
39
|
+
|
40
|
+
#################################
|
41
|
+
# Time List Syntax
|
42
|
+
#################################
|
43
|
+
def time(*args)
|
44
|
+
TimeListRule.new(:unknown, args)
|
45
|
+
end
|
46
|
+
|
47
|
+
#################################
|
48
|
+
# Date List Syntax
|
49
|
+
#################################
|
50
|
+
def date(*args)
|
51
|
+
DateListRule.new(:unknown, args)
|
52
|
+
end
|
53
|
+
|
54
|
+
#################################
|
55
|
+
# Env
|
56
|
+
#################################
|
57
|
+
def env(name, value)
|
58
|
+
ENV[name] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module TaskLoop
|
2
|
+
class AfterScopeRule < ScopeRule
|
3
|
+
|
4
|
+
attr_accessor :left
|
5
|
+
|
6
|
+
def initialize(unit, scope, left)
|
7
|
+
super unit, scope
|
8
|
+
@left = left
|
9
|
+
end
|
10
|
+
|
11
|
+
def invalidate!
|
12
|
+
super
|
13
|
+
if @unit == :day
|
14
|
+
unless Task::WEEK.has_key?(@left) || Task::DAY.has_key?(@left)
|
15
|
+
raise ArgumentError, "#{left} must be a Symbol defined in Task::WEEK or Task::DAY"
|
16
|
+
end
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
if @unit == :month
|
21
|
+
unless Task::MONTH.has_key?(@left)
|
22
|
+
raise ArgumentError, "#{left} must be a Symbol defined in Task::MONTH"
|
23
|
+
end
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
unless @left.is_a?(Integer)
|
28
|
+
raise TypeError, "'left' need to be Symbol or Integer"
|
29
|
+
end
|
30
|
+
|
31
|
+
if @unit == :minute && (@left < 0 || @left > 59)
|
32
|
+
raise ArgumentError, "'right' for 'minute' must >= 0 and <= 59"
|
33
|
+
end
|
34
|
+
|
35
|
+
if @unit == :hour && (@left < 0 || @left > 23)
|
36
|
+
raise ArgumentError, "'right' for 'hour' must >= 0 and <= 23"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def left_value
|
42
|
+
if (Task::DAY.has_key?(@left))
|
43
|
+
return Task::DAY[@left]
|
44
|
+
end
|
45
|
+
if (Task::WEEK.has_key?(@left))
|
46
|
+
return Task::WEEK[@left]
|
47
|
+
end
|
48
|
+
if (Task::MONTH.has_key?(@left))
|
49
|
+
return Task::MONTH[@left]
|
50
|
+
end
|
51
|
+
|
52
|
+
unless @left != nil && @left.is_a?(Integer)
|
53
|
+
return -1
|
54
|
+
end
|
55
|
+
|
56
|
+
return @left
|
57
|
+
end
|
58
|
+
|
59
|
+
def is_week_value?
|
60
|
+
if @unit == :day && Task::WEEK.has_key?(@left)
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_conform_rule?(last_exec_time)
|
67
|
+
current = Time.now
|
68
|
+
value = left_value
|
69
|
+
result = false
|
70
|
+
case @unit
|
71
|
+
when :year then
|
72
|
+
result = current.year > value
|
73
|
+
when :month then
|
74
|
+
result = current.month > value
|
75
|
+
when :day then
|
76
|
+
if is_week_value?
|
77
|
+
result = current.wday > (value % TaskLoop::WEEK_BASE)
|
78
|
+
else
|
79
|
+
result = current.day > value
|
80
|
+
end
|
81
|
+
when :hour then
|
82
|
+
result = current.hour > value
|
83
|
+
end
|
84
|
+
return result
|
85
|
+
end
|
86
|
+
|
87
|
+
def desc
|
88
|
+
super + " #{left}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|