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,244 @@
1
+ module TaskLoop
2
+ class Task
3
+ require 'digest'
4
+ require 'taskloop/extension/string_extension'
5
+ require 'taskloop/extension/integer_extension'
6
+ require_relative './task_error'
7
+ require_relative './task_property'
8
+ require_relative './task_data_file'
9
+
10
+ include TaskLoop::TaskProperty
11
+ include TaskLoop::TaskDataFile
12
+
13
+ @@tasklist = []
14
+ def self.tasklist
15
+ @@tasklist
16
+ end
17
+
18
+ def self.tasklist=(value)
19
+ @@tasklist = value
20
+ end
21
+
22
+ def initialize()
23
+ yield self
24
+ check = true
25
+ unless @name
26
+ puts "Error: task name is required.".ansi.red
27
+ puts ""
28
+ check = false
29
+ end
30
+ unless @path
31
+ puts "Error: task path is required.".ansi.red
32
+ puts ""
33
+ check = false
34
+ end
35
+ unless check
36
+ raise TaskArgumentError, "Lack of <required> arguments."
37
+ end
38
+ @@tasklist.push(self)
39
+ end
40
+
41
+ #################################
42
+ # Check and Lint
43
+ #################################
44
+ def deploy_lint?
45
+ result = true
46
+ # check task name uniqueness
47
+ @@tasklist.each do |task|
48
+ if task != self && task.name == @name
49
+ puts "Error: #{task.desc} => there are multiple tasks with the same name in the Taskfile. Please check the task name again.".ansi.red
50
+ result = false
51
+ end
52
+ # only check with the task before self
53
+ if task == self
54
+ break
55
+ end
56
+ end
57
+ # check if task path exists
58
+ unless @path && File.exists?(@path)
59
+ puts "Error: #{desc} => the file in <#{@path}> is not exist. Please check the task path again.".ansi.red
60
+ result = false
61
+ end
62
+ # check task rules conflict
63
+ unless check_rule_conflict?
64
+ puts "Error: #{desc} rule conflicts have been detected above.".ansi.red
65
+ result = false
66
+ end
67
+ unless result
68
+ puts "=============================".ansi.red
69
+ end
70
+ return result
71
+ end
72
+
73
+ def check_rule_conflict?
74
+ result = true
75
+ # check year/month/day with date, they cannot be set at the same time
76
+ if hasYMD? && hasDate?
77
+ puts "Error: #{desc} => <year/month/day> and <date> cannot be set at the same time.".ansi.red
78
+ result = false
79
+ end
80
+
81
+ # check hour/minute with time, they cannot be set at the same time
82
+ if hasHM? && hasTime?
83
+ puts "Error: #{desc} => <hour/minute> and <time> cannot be set at the same time.".ansi.red
84
+ result = false
85
+ end
86
+
87
+ # check rule type
88
+
89
+ if hasYMD? && hasHM?
90
+ # check minute/hour/day/month/year. Get the last IntervalRule, than check if there is SpecificRule or ScopeRule before
91
+ rules = [minute, hour, day, month, year]
92
+ rIdx = rules.rindex { |rule| rule.is_a?(IntervalRule) }
93
+ if rIdx != nil
94
+ rules.each_with_index do |rule, index|
95
+ if index < rIdx && (rule.is_a?(ScopeRule) || rule.is_a?(SpecificRule))
96
+ puts "Error: #{desc} => a ScopeRule or a SpecificRule is assigned to a smaller unit while a IntervalRule is assigned to a larger unit.".ansi.red
97
+ result = false
98
+ break
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+
105
+ end
106
+
107
+ if hasYMD? && hasTime?
108
+ # check year/month/day with time, YMD can not have IntervalRule
109
+ rules = [day, month, year]
110
+ hasInterval = rules.any? { |rule| rule.is_a?(IntervalRule) }
111
+ if hasInterval
112
+ puts "Error: #{desc} => a IntervalRule is assigned to <year/month/day> while <time> is assigned. It is a conflict!".ansi.red
113
+ result = false
114
+ end
115
+ end
116
+
117
+ if hasDate? && hasHM?
118
+ # it's ok
119
+ end
120
+
121
+ if hasDate? && hasTime?
122
+ # it's ok
123
+ end
124
+
125
+ return result
126
+ end
127
+
128
+ def check_all_rules?
129
+ last_exec_time = last_time
130
+
131
+ result = true
132
+ result &&= check_interval_rule?(last_exec_time)
133
+ result &&= check_scope_rule?(last_exec_time)
134
+ result &&= check_specific_fule?(last_exec_time)
135
+ result &&= check_loop_rule?(last_exec_time)
136
+ return result
137
+ end
138
+
139
+ private def check_interval_rule?(last_exec_time)
140
+ result = true
141
+ min = 0
142
+ if year.is_a?(IntervalRule)
143
+ min += year.interval * YEAR_MIN
144
+ end
145
+ if month.is_a?(IntervalRule)
146
+ min += month.interval * MONTH_MIN
147
+ end
148
+ if day.is_a?(IntervalRule)
149
+ min += day.interval * DAY_MIN
150
+ end
151
+ if hour.is_a?(IntervalRule)
152
+ min += hour.interval * HOUR_MIN
153
+ end
154
+ if minute.is_a?(IntervalRule)
155
+ min += minute.interval
156
+ end
157
+ correction = 20
158
+ result &&= (Time.now.to_i - last_exec_time.to_i + correction) / 60.0 >= min
159
+ return result
160
+ end
161
+
162
+ private def check_scope_rule?(last_exec_time)
163
+ result = true
164
+ if year.is_a?(ScopeRule)
165
+ result &&= year.is_conform_rule?(last_exec_time)
166
+ end
167
+ if month.is_a?(ScopeRule)
168
+ result &&= month.is_conform_rule?(last_exec_time)
169
+ end
170
+ if day.is_a?(ScopeRule)
171
+ result &&= day.is_conform_rule?(last_exec_time)
172
+ end
173
+ if hour.is_a?(ScopeRule)
174
+ result &&= hour.is_conform_rule?(last_exec_time)
175
+ end
176
+ if minute.is_a?(ScopeRule)
177
+ result &&= minute.is_conform_rule?(last_exec_time)
178
+ end
179
+ return result
180
+ end
181
+
182
+ private def check_specific_fule?(last_exec_time)
183
+ result = true
184
+ if year.is_a?(SpecificRule)
185
+ result &&= year.is_conform_rule?(last_exec_time)
186
+ end
187
+ if month.is_a?(SpecificRule)
188
+ result &&= month.is_conform_rule?(last_exec_time)
189
+ end
190
+ if day.is_a?(SpecificRule)
191
+ result &&= day.is_conform_rule?(last_exec_time)
192
+ end
193
+ if hour.is_a?(SpecificRule)
194
+ result &&= hour.is_conform_rule?(last_exec_time)
195
+ end
196
+ if minute.is_a?(SpecificRule)
197
+ result &&= minute.is_conform_rule?(last_exec_time)
198
+ end
199
+ if time.is_a?(TimeListRule)
200
+ result &&= time.is_conform_rule?(last_exec_time)
201
+ end
202
+ if date.is_a?(DateListRule)
203
+ result &&= time.is_conform_rule?(last_exec_time)
204
+ end
205
+ return result
206
+ end
207
+
208
+ private def check_loop_rule?(last_exec_time)
209
+ count = loop_count
210
+
211
+ result = true
212
+ if loop.is_a?(LoopRule)
213
+ result = count < loop.count
214
+ end
215
+ return result
216
+ end
217
+
218
+
219
+ #################################
220
+ # Sha1 and description
221
+ #################################
222
+ def sha1
223
+ sha1_digest = Digest::SHA1.new
224
+ sha1_digest.update(@name)
225
+ return sha1_digest.hexdigest
226
+ end
227
+
228
+ def desc
229
+ "<Task.name: #{@name}, sha1: #{sha1}>"
230
+ end
231
+
232
+ #################################
233
+ # Time Seconds
234
+ #################################
235
+
236
+ YEAR_MIN = 365 * 24 * 60
237
+
238
+ MONTH_MIN = 30 * 24 * 60
239
+
240
+ DAY_MIN = 24 * 60
241
+
242
+ HOUR_MIN = 60
243
+ end
244
+ end
@@ -0,0 +1,46 @@
1
+ module TaskLoop
2
+ module TaskDataFile
3
+ # ~/.taskloop/cache/<project-sha>.
4
+ attr_accessor :data_proj_dir
5
+
6
+ def logfile_path
7
+ return File.join(@data_proj_dir, logfile_name)
8
+ end
9
+
10
+ def timefile_path
11
+ return File.join(@data_proj_dir, timefile_name)
12
+ end
13
+
14
+ def loopfile_path
15
+ return File.join(@data_proj_dir, loopfile_name)
16
+ end
17
+
18
+ def logfile_name
19
+ return sha1 + "_log"
20
+ end
21
+ def timefile_name
22
+ return sha1 + "_time"
23
+ end
24
+ def loopfile_name
25
+ return sha1 + "_loop"
26
+ end
27
+
28
+ def write_to_logfile(content)
29
+ file = File.open(logfile_path, "w")
30
+ file.puts content
31
+ file.close
32
+ end
33
+
34
+ def write_to_timefile(content)
35
+ file = File.open(timefile_path, "w")
36
+ file.puts content
37
+ file.close
38
+ end
39
+
40
+ def write_to_loopfile(content)
41
+ file = File.open(loopfile_path, "w")
42
+ file.puts content
43
+ file.close
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ module TaskLoop
2
+ class TaskNameError < StandardError
3
+ end
4
+
5
+ class TaskPathError < StandardError
6
+
7
+ end
8
+
9
+ class TaskArgumentError < StandardError
10
+
11
+ end
12
+
13
+ class TaskfileDeployError < StandardError
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,294 @@
1
+ module TaskLoop
2
+ module TaskProperty
3
+
4
+ MONTH = {
5
+ :Jan => 1,
6
+ :Feb => 2,
7
+ :Mar => 3,
8
+ :Apr => 4,
9
+ :Jun => 6,
10
+ :Jul => 7,
11
+ :Aug => 8,
12
+ :Sep => 9,
13
+ :Oct => 10,
14
+ :Nov => 11,
15
+ :Dec => 12,
16
+ :mon1 => 1,
17
+ :mon2 => 2,
18
+ :mon3 => 3,
19
+ :mon4 => 4,
20
+ :mon5 => 5,
21
+ :mon6 => 6,
22
+ :mon7 => 7,
23
+ :mon8 => 8,
24
+ :mon9 => 9,
25
+ :mon10 => 10,
26
+ :mon11 => 11,
27
+ :mon12 => 12,
28
+ }
29
+
30
+ WEEK_BASE = 10000
31
+ WEEK = {
32
+ :Sun => 10000,
33
+ :Mon => 10001,
34
+ :Tue => 10002,
35
+ :Wed => 10003,
36
+ :Thu => 10004,
37
+ :Fri => 10005,
38
+ :Sat => 10006,
39
+ }
40
+
41
+ DAY = {
42
+ :day1 => 1,
43
+ :day2 => 2,
44
+ :day3 => 3,
45
+ :day4 => 4,
46
+ :day5 => 5,
47
+ :day6 => 6,
48
+ :day7 => 7,
49
+ :day8 => 8,
50
+ :day9 => 9,
51
+ :day10 => 10,
52
+ :day11 => 11,
53
+ :day12 => 12,
54
+ :day13 => 13,
55
+ :day14 => 14,
56
+ :day15 => 15,
57
+ :day16 => 16,
58
+ :day17 => 17,
59
+ :day18 => 18,
60
+ :day19 => 19,
61
+ :day20 => 20,
62
+ :day21 => 21,
63
+ :day22 => 22,
64
+ :day23 => 23,
65
+ :day24 => 24,
66
+ :day25 => 25,
67
+ :day26 => 26,
68
+ :day27 => 27,
69
+ :day28 => 28,
70
+ :day29 => 29,
71
+ :day30 => 30,
72
+ :day31 => 31,
73
+ }
74
+
75
+ # the name of task
76
+ attr_accessor :name
77
+ # the path of task
78
+ attr_accessor :path
79
+
80
+ #################################
81
+ # Setters & Getters
82
+ #################################
83
+ # specific syntax
84
+ # - of
85
+ # - example: in 2024
86
+ # interval syntax
87
+ # - interval
88
+ # - example: interval 1.year
89
+ # scope syntax
90
+ # - before
91
+ # - example: before 2025
92
+ # - between
93
+ # - example: between 2025, 2026
94
+ # - after
95
+ # - examle: after 2023
96
+ def year=(rule)
97
+ unless rule.is_a?(Rule)
98
+ raise TypeError, "the rule of year must be a class or subclass of Rule"
99
+ end
100
+
101
+ @year = rule
102
+ @year.unit = :year
103
+ end
104
+
105
+ def year
106
+ @year ||= DefaultRule.new(:year)
107
+ end
108
+
109
+ # specific syntax
110
+ # - of
111
+ # - example: of :Jan, :Feb, :Mar, :Apr, :Jun, :Jul, :Aug, :Sep, :Oct, :Nov, :Dec;
112
+ # - example: of :month1, :month2, :month3, ....
113
+ # interval syntax
114
+ # - every
115
+ # - example: interval 1.month
116
+ # scope syntax
117
+ # - before
118
+ # - example: before 2025
119
+ # - between
120
+ # - example: between 2025, 2026
121
+ # - after
122
+ # - examle: after 2023
123
+ def month=(rule)
124
+ unless rule.is_a?(Rule)
125
+ raise TypeError, "the rule of month must be a class or subclass of Rule"
126
+ end
127
+ @month = rule
128
+ @month.unit = :month
129
+ end
130
+
131
+ def month
132
+ @month||= DefaultRule.new(:month)
133
+ end
134
+
135
+ # specific syntax
136
+ # - on
137
+ # - example: on :Mon, :Tue, :Wed, :Thu, :Fri, :Sat, :Sun;
138
+ # - example: on :day1, :day2, :day3;
139
+ # interval syntax
140
+ # - interval
141
+ # - example: interval 10.day
142
+ # scope syntax
143
+ # - before
144
+ # - example: before :day10
145
+ # - between
146
+ # - example: between :day10, :day19
147
+ # - example: between :Mon, :Fri
148
+ # - after
149
+ # - example: after :day10
150
+ def day=(rule)
151
+ unless rule.is_a?(Rule)
152
+ raise TypeError, "the rule of day must be a class or subclass of Rule"
153
+ end
154
+ @day = rule
155
+ @day.unit = :day
156
+ end
157
+
158
+ def day
159
+ @day ||= DefaultRule.new(:day)
160
+ end
161
+
162
+ # specific syntax
163
+ # - at
164
+ # - example: at 10; at 23
165
+ # interval syntax
166
+ # - interval
167
+ # - example: interval 10.hour
168
+ # scope syntax
169
+ # - before
170
+ # - example: before 9
171
+ # - between
172
+ # - example: between 10, 12
173
+ # - after
174
+ # - example: after 11
175
+ def hour=(rule)
176
+ unless rule.is_a?(Rule)
177
+ raise TypeError, "the rule of hour must be a class or subclass of Rule"
178
+ end
179
+ @hour = rule
180
+ @hour.unit = :hour
181
+ end
182
+
183
+ def hour
184
+ @hour ||= DefaultRule.new(:hour)
185
+ end
186
+
187
+ # specific syntax
188
+ # - at
189
+ # - example: at 59; at 45
190
+ # interval syntax
191
+ # - interval
192
+ # - example: interval 5.minute
193
+ def minute=(rule)
194
+ unless rule.is_a?(Rule)
195
+ raise TypeError, "the rule of minute must be a class or subclass of Rule"
196
+ end
197
+
198
+ if rule.is_a?(ScopeRule)
199
+ raise TypeError, "the rule of minute cannot be a class or subclass of ScopeRule"
200
+ end
201
+ @minute = rule
202
+ @minute.unit = :minute
203
+ end
204
+
205
+ def minute
206
+ @minute ||= DefaultRule.new(:minute)
207
+ end
208
+
209
+ # loop syntax
210
+ # - loop
211
+ # - example: loop 5.times
212
+ def loop=(rule)
213
+ unless rule.is_a?(Rule)
214
+ raise TypeError, "the rule of loop must be a class or subclass of Rule"
215
+ end
216
+
217
+ @loop = rule
218
+ @loop.unit = :loop
219
+ end
220
+ def loop
221
+ @loop ||= DefaultRule.new(:loop)
222
+ end
223
+
224
+ # time list syntax
225
+ # - time
226
+ # - example: time "10:10:30", "9:10:20", "20:10:20"
227
+ def time=(rule)
228
+ unless rule.is_a?(TimeListRule)
229
+ raise TypeError, "the rule of time must be a TimeList Rule"
230
+ end
231
+
232
+ @time = rule
233
+ @time.unit = :time
234
+ end
235
+ def time
236
+ @time ||= DefaultRule.new(:time)
237
+ end
238
+
239
+ # date list syntax
240
+ # - date
241
+ # - example: date "2023-10-10", "2013-10-11", "2023-11-10"
242
+ def date=(rule)
243
+ unless rule.is_a?(DateListRule)
244
+ raise TypeError, "the rule of time must be a DateList Rule"
245
+ end
246
+
247
+ @date = rule
248
+ @date.unit = :date
249
+ end
250
+ def date
251
+ @date ||= DefaultRule.new(:date)
252
+ end
253
+
254
+ #################################
255
+ # Help Methods
256
+ #################################
257
+ def hasDate?
258
+ !date.is_a?(DefaultRule)
259
+ end
260
+
261
+ def hasTime?
262
+ !time.is_a?(DefaultRule)
263
+ end
264
+
265
+ def hasYMD?
266
+ [year, month, day].any? { |rule| !rule.is_a?(DefaultRule) }
267
+ end
268
+
269
+ def hasHM?
270
+ [hour, minute].any? { |rule| !rule.is_a?(DefaultRule) }
271
+ end
272
+
273
+ def has_interval_rule?
274
+ rules = [year, month, day, hour, minute]
275
+ return rules.any?{ |rule| rule.is_a?(IntervalRule) }
276
+ end
277
+
278
+ def loop_count
279
+ count = 0
280
+ File.open(loopfile_path, 'r') do |file|
281
+ count = file.gets.to_i
282
+ end
283
+ return count
284
+ end
285
+
286
+ def last_time
287
+ timestamp = 0
288
+ File.open(timefile_path, 'r') do |file|
289
+ timestamp = file.gets.to_i
290
+ end
291
+ return Time.at(timestamp)
292
+ end
293
+ end
294
+ end