taskloop 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.gitignore +8 -0
  3. data/.idea/misc.xml +4 -0
  4. data/.idea/modules.xml +8 -0
  5. data/.idea/taskloop.iml +51 -0
  6. data/.idea/vcs.xml +6 -0
  7. data/Gemfile +9 -0
  8. data/Gemfile.lock +36 -0
  9. data/README.md +31 -0
  10. data/Rakefile +4 -0
  11. data/exe/taskloop +16 -0
  12. data/lib/taskloop/command/deploy.rb +136 -0
  13. data/lib/taskloop/command/env.rb +134 -0
  14. data/lib/taskloop/command/init.rb +42 -0
  15. data/lib/taskloop/command/launch.rb +67 -0
  16. data/lib/taskloop/command/list.rb +59 -0
  17. data/lib/taskloop/command/log.rb +104 -0
  18. data/lib/taskloop/command/run.rb +106 -0
  19. data/lib/taskloop/command/shutdown.rb +49 -0
  20. data/lib/taskloop/command/undeploy.rb +46 -0
  21. data/lib/taskloop/command.rb +168 -0
  22. data/lib/taskloop/dsl/dsl.rb +63 -0
  23. data/lib/taskloop/extension/integer_extension.rb +25 -0
  24. data/lib/taskloop/extension/string_extension.rb +18 -0
  25. data/lib/taskloop/rules/after_scope_rule.rb +91 -0
  26. data/lib/taskloop/rules/before_scope_rule.rb +90 -0
  27. data/lib/taskloop/rules/between_scope_rule.rb +130 -0
  28. data/lib/taskloop/rules/date_list_rule.rb +47 -0
  29. data/lib/taskloop/rules/default_rule.rb +19 -0
  30. data/lib/taskloop/rules/interval_rule.rb +26 -0
  31. data/lib/taskloop/rules/loop_rule.rb +25 -0
  32. data/lib/taskloop/rules/rule.rb +34 -0
  33. data/lib/taskloop/rules/scope_rule.rb +22 -0
  34. data/lib/taskloop/rules/specific_rule.rb +64 -0
  35. data/lib/taskloop/rules/time_list_rule.rb +49 -0
  36. data/lib/taskloop/task/task.rb +244 -0
  37. data/lib/taskloop/task/task_data_file.rb +46 -0
  38. data/lib/taskloop/task/task_error.rb +17 -0
  39. data/lib/taskloop/task/task_property.rb +294 -0
  40. data/lib/taskloop/utils/proj_tasklist.rb +145 -0
  41. data/lib/taskloop/version.rb +5 -0
  42. data/lib/taskloop.rb +9 -0
  43. data/sig/taskloop.rbs +4 -0
  44. data/taskloop.gemspec +37 -0
  45. 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,25 @@
1
+ class Integer
2
+ def minute
3
+ self
4
+ end
5
+
6
+ def hour
7
+ self
8
+ end
9
+
10
+ def day
11
+ self
12
+ end
13
+
14
+ def month
15
+ self
16
+ end
17
+
18
+ def year
19
+ self
20
+ end
21
+
22
+ def times
23
+ self
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ class String
2
+ require 'digest'
3
+
4
+ def sha1
5
+ sha1_digest = Digest::SHA1.new
6
+ sha1_digest.update(self)
7
+ return sha1_digest.hexdigest
8
+ end
9
+
10
+ def sha1_32bit
11
+ sha1[0..31]
12
+ end
13
+
14
+ def sha1_8bit
15
+ sha1[0..8]
16
+ end
17
+
18
+ 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