taskloop 0.1.0

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.
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