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