fasten 0.5.2 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d8a5e3862d3e3572c5fd0316458efd9676999cf764b15109870fc2886f3e4e4
4
- data.tar.gz: 9e8b633cac41ad9d10b08d615f63c1010abe7f375579620e52ce90456f0b53cb
3
+ metadata.gz: e475b26b22ab143005ca207f29ee7036571b71934a7fa2bba0ea2be74ba1aaab
4
+ data.tar.gz: 39429fea342cf0c43e1b84cef14e897a1d12f56a5f11e4c555c28176bfa1cdda
5
5
  SHA512:
6
- metadata.gz: 743f18548d13b69747d11057c4bd8923448642cdaafaa8f91d68fa5156d613db15094d2194b2d16a1fc2a88493cc52741b76d1e652b3db9cf4a2d9ec3991d332
7
- data.tar.gz: 5a1f5d9d57b26b31ff90867a999ea285cef8e09e4972e648baffe7887d732680f64d0aea09d19ef728616b9d430ae89142e448d49c9e7a979e8e22bc1fb50c76
6
+ metadata.gz: 7bd2923a0ae1d4cf5f31b36a3999be1bfec058a99acdd381712e91454e2ed9baf7193bb87de835359b300a9317cdd47f2c92e850c54a36e7dcf26dd88776d720
7
+ data.tar.gz: b4307bc7ee694ad3740b0c0b361d4c5a4888f5e405d32b6aa91ceee8c0023ee1a6421771a6915a2f3916d0610cdf5136f329f47076e33bb066e7e3eb4fb572e3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fasten (0.5.2)
4
+ fasten (0.5.4)
5
5
  binding_of_caller
6
6
  curses
7
7
  hirb
data/lib/fasten/dag.rb CHANGED
@@ -45,7 +45,7 @@ module Fasten
45
45
  end
46
46
 
47
47
  def next_task
48
- task_waiting_list.sort_by!(&:run_score).pop
48
+ task_waiting_list.shift
49
49
  end
50
50
 
51
51
  def task_waiting_list
@@ -67,16 +67,30 @@ module Fasten
67
67
  @task_waiting_list ||= []
68
68
  @task_pending_list -= move_list
69
69
  @task_waiting_list += move_list
70
+ @task_waiting_list.sort_by!.with_index do |x, index|
71
+ x.state = :WAIT
72
+ [-x.run_score, index]
73
+ end
70
74
  end
71
75
 
72
76
  def reset_tasks
73
77
  @task_pending_list.clear
74
78
  @task_done_list.clear
79
+ @task_error_list.clear
80
+
75
81
  @task_list.each do |task|
76
82
  task.dependants = []
77
83
  task.depends = []
78
84
  task.run_score = 0
79
- task.done ? @task_done_list << task : @task_pending_list << task
85
+
86
+ if task.state == :DONE
87
+ @task_done_list << task
88
+ elsif task.state == :FAIL
89
+ @task_error_list << task
90
+ else
91
+ task.state = :IDLE
92
+ @task_pending_list << task
93
+ end
80
94
  end
81
95
  end
82
96
 
@@ -1,21 +1,28 @@
1
1
  module Fasten
2
- class Executor < Task
2
+ class Executor
3
3
  include Fasten::Logger
4
+ include Fasten::State
4
5
  include Fasten::DAG
5
6
  include Fasten::UI
6
- include Fasten::LoadSave
7
+ include Fasten::Yaml
7
8
  include Fasten::Stats
8
9
 
9
- def initialize(name: nil, developer: STDIN.tty? && STDOUT.tty?, workers: Parallel.physical_processor_count, worker_class: Fasten::Worker, fasten_dir: '.fasten')
10
- setup_stats(name)
11
- super name: name || "#{self.class} #{$PID}", workers: workers, pid: $PID, state: :IDLE, worker_class: worker_class, fasten_dir: fasten_dir, developer: developer
10
+ attr_accessor :name, :workers, :worker_class, :pid, :fasten_dir, :developer, :stats, :worker_list, :block
11
+
12
+ def initialize(name: nil, developer: STDIN.tty? && STDOUT.tty?, workers: Parallel.physical_processor_count, worker_class: Worker, fasten_dir: '.fasten')
13
+ self.stats = name && true
14
+ self.name = name || "#{self.class} #{$PID}"
15
+ self.state = :IDLE
16
+ self.workers = workers
17
+ self.worker_class = worker_class
18
+ self.fasten_dir = fasten_dir
19
+ self.developer = developer
20
+
12
21
  initialize_dag
22
+ initialize_stats
23
+ initialize_logger
13
24
 
14
25
  self.worker_list = []
15
- log_path = "#{fasten_dir}/log/executor/#{self.name}.log"
16
- FileUtils.mkdir_p File.dirname(log_path)
17
- self.log_file = File.new(log_path, 'a')
18
- Fasten.logger.reopen log_file
19
26
  end
20
27
 
21
28
  def perform
@@ -62,7 +69,7 @@ module Fasten
62
69
  def check_state
63
70
  if state == :PAUSING && no_running_tasks?
64
71
  self.state = :PAUSED
65
- self.ui.message = nil
72
+ ui.message = nil
66
73
  ui.force_clear
67
74
  elsif state == :QUITTING && no_running_tasks?
68
75
  self.state = :QUIT
@@ -101,17 +108,22 @@ module Fasten
101
108
  end
102
109
  end
103
110
 
104
- def raise_error_in_failure
105
- return unless tasks_failed?
106
-
111
+ def show_error_tasks
107
112
  task_error_list.each do |task|
108
113
  log_info "task: #{task} error:#{task.error}\n#{task.error&.backtrace&.join("\n")}"
109
114
  end
115
+ end
116
+
117
+ def raise_error_in_failure
118
+ return unless tasks_failed?
119
+
120
+ show_error_tasks
121
+
122
+ message = "Stopping because the following tasks failed: #{task_error_list.map(&:to_s).join(', ')}"
110
123
 
111
124
  if developer
112
125
  ui.cleanup
113
- puts "Stopping because the following tasks failed:\n"
114
- task_error_list.map(&:to_s).each { |x| puts " #{x}" }
126
+ puts message
115
127
 
116
128
  puts 'Entering development console'
117
129
 
@@ -119,7 +131,7 @@ module Fasten
119
131
  else
120
132
  remove_all_workers
121
133
 
122
- raise "Stopping because the following tasks failed: #{task_error_list.map(&:to_s).join(', ')}"
134
+ raise message
123
135
  end
124
136
  end
125
137
 
data/lib/fasten/logger.rb CHANGED
@@ -4,6 +4,8 @@ module Fasten
4
4
  end
5
5
 
6
6
  module Logger
7
+ attr_accessor :log_file
8
+
7
9
  %w[debug info error].each do |method|
8
10
  define_method "log_#{method}" do |msg|
9
11
  return unless Fasten.logger.respond_to?(method)
@@ -14,16 +16,23 @@ module Fasten
14
16
  end
15
17
  end
16
18
 
19
+ def initialize_logger
20
+ log_path = "#{fasten_dir}/log/executor/#{name}.log"
21
+ FileUtils.mkdir_p File.dirname(log_path)
22
+ self.log_file = File.new(log_path, 'a')
23
+ Fasten.logger.reopen log_file
24
+ end
25
+
17
26
  def log_ini(object, message = nil)
18
27
  object.ini ||= Time.new
19
- log_info "Init #{object.class} #{object} #{message}"
28
+ log_info "Ini #{object.state} #{object.class} #{object} #{message}"
20
29
  end
21
30
 
22
31
  def log_fin(object, message = nil)
23
32
  object.fin ||= Time.new
24
33
  object.dif = object.fin - object.ini
25
34
 
26
- log_info "Done #{object.class} #{object} #{message} in #{object.dif}"
35
+ log_info "Fin #{object.state} #{object.class} #{object} #{message} in #{object.dif}"
27
36
  end
28
37
 
29
38
  def redirect_std(path)
@@ -0,0 +1,30 @@
1
+ module Fasten
2
+ module State
3
+ attr_accessor :error, :ini, :fin, :dif, :last
4
+ attr_writer :state
5
+
6
+ def state
7
+ @state || :IDLE
8
+ end
9
+
10
+ def running?
11
+ state == :RUNNING
12
+ end
13
+
14
+ def idle?
15
+ state == :IDLE
16
+ end
17
+
18
+ def pausing?
19
+ state == :PAUSING
20
+ end
21
+
22
+ def paused?
23
+ state == :PAUSED
24
+ end
25
+
26
+ def quitting?
27
+ state == :QUITTING
28
+ end
29
+ end
30
+ end
data/lib/fasten/stats.rb CHANGED
@@ -1,30 +1,78 @@
1
1
  module Fasten
2
2
  module Stats
3
+ attr_writer :stats_data, :stats_entries
4
+ attr_reader :stats_path
5
+
6
+ def initialize_stats
7
+ return unless stats
8
+
9
+ @stats_path = "#{ENV['HOME']}/.fasten/stats/#{name}.csv" if ENV['HOME']
10
+ FileUtils.mkdir_p File.dirname(@stats_path)
11
+ rescue StandardError
12
+ @stats_path = nil
13
+ end
14
+
15
+ def load_stats
16
+ return unless @stats_path && File.exist?(@stats_path)
17
+
18
+ self.stats_data = []
19
+ CSV.foreach(@stats_path, headers: true) do |row|
20
+ stats_data << row.to_h
21
+ end
22
+
23
+ @task_waiting_list = nil
24
+ rescue StandardError
25
+ nil
26
+ ensure
27
+ self.stats ||= {}
28
+ end
29
+
30
+ def save_stats
31
+ return unless @stats_path && stats_data
32
+
33
+ keys = %w[state kind name run cnt avg std err]
34
+
35
+ CSV.open(@stats_path, 'wb') do |csv|
36
+ csv << keys
37
+
38
+ stats_data.each do |data|
39
+ csv << keys.map { |i| data[i] }
40
+ end
41
+ end
42
+ end
43
+
3
44
  def stats_create_entry(state, target)
4
- {
5
- 'state' => state.to_s,
6
- 'kind' => target.is_a?(Fasten::Executor) ? 'executor' : 'task',
7
- 'name' => target.name,
8
- 'ini' => target.ini.to_f,
9
- 'fin' => target.fin.to_f,
10
- 'run' => target.fin - target.ini
11
- }
45
+ { 'state' => state.to_s,
46
+ 'kind' => stats_kind_for(target),
47
+ 'name' => target.name,
48
+ 'ini' => target.ini.to_f,
49
+ 'fin' => target.fin.to_f,
50
+ 'run' => target.fin - target.ini,
51
+ 'worker' => target.respond_to?(:worker) ? target.worker.name : nil }
52
+ end
53
+
54
+ def stats_data
55
+ @stats_data ||= []
56
+ end
57
+
58
+ def stats_entries
59
+ @stats_entries ||= []
60
+ end
61
+
62
+ def stats_kind_for(object)
63
+ object.is_a?(Fasten::Executor) ? 'executor' : 'task'
12
64
  end
13
65
 
14
66
  def stats_add_entry(state, target)
15
67
  return unless target.ini && target.fin
16
68
 
17
69
  entry = stats_create_entry(state, target)
18
- self.stats_data ||= []
19
- self.stats_entries ||= []
20
70
  stats_data << entry
21
71
  stats_entries << entry
22
72
 
23
73
  history = stats_history(entry)
24
74
 
25
- update_cnt(history, entry)
26
- update_avg(history, entry)
27
- update_std(history, entry)
75
+ update_stats(history, entry)
28
76
  end
29
77
 
30
78
  FLOAT_FORMATTER = ->(f) { format('%7.3f', f) }
@@ -61,8 +109,8 @@ module Fasten
61
109
  sub, tot = stats_table_run
62
110
 
63
111
  Hirb::Console.render_output(stats_entries,
64
- fields: %w[state kind name run cnt avg std], unicode: true, class: 'Hirb::Helpers::AutoTable',
65
- filters: { 'run' => FLOAT_FORMATTER, 'avg' => FLOAT_FORMATTER, 'std' => FLOAT_FORMATTER },
112
+ fields: %w[state kind name run cnt avg std err worker], unicode: true, class: 'Hirb::Helpers::AutoTable',
113
+ filters: { 'run' => FLOAT_FORMATTER, 'avg' => FLOAT_FORMATTER, 'std' => FLOAT_FORMATTER, 'err' => FLOAT_FORMATTER },
66
114
  description: false)
67
115
 
68
116
  puts format('∑tasks: %<task>s ∑executed: %<executed>s saved: %<saved>s workers: %<workers>s',
@@ -73,16 +121,17 @@ module Fasten
73
121
  stats_data.select { |e| e['state'] == entry['state'] && e['kind'] == entry['kind'] && e['name'] == entry['name'] }
74
122
  end
75
123
 
76
- def update_cnt(history, entry)
77
- entry['cnt'] = history.size
78
- end
124
+ def stats_last(item)
125
+ return item.last if item.last
79
126
 
80
- def update_avg(history, entry)
81
- entry['avg'] = history.inject(0.0) { |s, x| s + x['run'].to_f } / history.size
127
+ item.last = stats_data.select { |e| e['kind'] == stats_kind_for(item) && e['name'] == item.name }.last || {}
82
128
  end
83
129
 
84
- def update_std(history, entry)
85
- entry['std'] = Math.sqrt(history.inject(0.0) { |v, x| v + (x['run'].to_f - entry['avg'])**2 })
130
+ def update_stats(history, entry)
131
+ entry['cnt'] = count = history.size
132
+ entry['avg'] = avg = history.inject(0.0) { |s, x| s + x['run'].to_f } / count
133
+ entry['std'] = std = Math.sqrt(history.inject(0.0) { |v, x| v + (x['run'].to_f - avg)**2 })
134
+ entry['err'] = std / Math.sqrt(count) if count.positive?
86
135
  end
87
136
  end
88
137
  end
data/lib/fasten/task.rb CHANGED
@@ -1,6 +1,26 @@
1
1
  module Fasten
2
- class Task < OpenStruct
2
+ class Task
3
3
  include Fasten::Logger
4
+ include Fasten::State
5
+
6
+ attr_accessor :name, :after, :shell, :ruby
7
+ attr_accessor :dependants, :depends, :request, :response, :worker, :run_score
8
+
9
+ def initialize(name: nil, shell: nil, ruby: nil, request: nil, after: nil)
10
+ self.name = name
11
+ self.after = after
12
+ self.shell = shell
13
+ self.ruby = ruby
14
+ self.request = request
15
+ end
16
+
17
+ def marshal_dump
18
+ [@name, @state, @ini, @fin, @dif, @request, @response, @shell, @ruby, @error]
19
+ end
20
+
21
+ def marshal_load(data)
22
+ @name, @state, @ini, @fin, @dif, @request, @response, @shell, @ruby, @error = data
23
+ end
4
24
 
5
25
  def to_s
6
26
  name
@@ -4,6 +4,7 @@ module Fasten
4
4
  module UI
5
5
  class Console
6
6
  extend Forwardable
7
+
7
8
  def_delegators :executor, :worker_list, :task_list, :task_done_list, :task_error_list, :task_running_list, :task_waiting_list, :worker_list
8
9
  def_delegators :executor, :name, :workers, :workers=, :state, :state=, :hformat
9
10
 
@@ -7,6 +7,7 @@ module Fasten
7
7
  class Curses
8
8
  include ::Curses
9
9
  extend Forwardable
10
+
10
11
  def_delegators :executor, :worker_list, :task_list, :task_done_list, :task_error_list, :task_running_list, :task_waiting_list, :worker_list
11
12
  def_delegators :executor, :name, :workers, :workers=, :state, :state=
12
13
 
@@ -139,13 +140,13 @@ module Fasten
139
140
  end
140
141
 
141
142
  def ui_state
142
- if state == :RUNNING
143
+ if executor.running?
143
144
  attrs = color_pair(2)
144
- elsif state == :PAUSING
145
+ elsif executor.pausing?
145
146
  attrs = color_pair(1) | A_BLINK | A_STANDOUT
146
- elsif state == :PAUSED
147
+ elsif executor.paused?
147
148
  attrs = color_pair(1) | A_STANDOUT
148
- elsif state == :QUITTING
149
+ elsif executor.quitting?
149
150
  attrs = color_pair(3) | A_BLINK | A_STANDOUT
150
151
  end
151
152
 
@@ -161,7 +162,7 @@ module Fasten
161
162
  col_ini.upto col_fin do |col|
162
163
  setpos row, col
163
164
  count -= slice
164
- if count.positive?
165
+ if count >= 0
165
166
  addstr PROGRESSBAR_STR[-1]
166
167
  elsif count > -slice
167
168
  addstr PROGRESSBAR_STR[(count * PROGRESSBAR_LEN / slice) % PROGRESSBAR_LEN]
@@ -179,8 +180,10 @@ module Fasten
179
180
  '✘'
180
181
  when :DONE
181
182
  '✔'
182
- else
183
+ when :WAIT
183
184
  '…'
185
+ else
186
+ ' '
184
187
  end
185
188
  end
186
189
 
@@ -193,7 +196,11 @@ module Fasten
193
196
  when :DONE
194
197
  color_pair(2) | A_TOP
195
198
  else
196
- color_pair(4) | A_TOP
199
+ if task_waiting_list.include? task
200
+ A_TOP
201
+ else
202
+ color_pair(4) | A_DIM
203
+ end
197
204
  end
198
205
  end
199
206
 
@@ -227,7 +234,7 @@ module Fasten
227
234
  ui_progressbar(2, col_ini, col_fin, count_done, count_total)
228
235
 
229
236
  max = 2
230
- list = task_list.sort_by(&:run_score)
237
+ list = task_list.sort_by.with_index { |x, index| [x.run_score, index] }
231
238
  list.each_with_index do |task, index|
232
239
  next if 3 + index >= n_rows
233
240
 
@@ -238,16 +245,23 @@ module Fasten
238
245
  list.each_with_index do |task, index|
239
246
  next if 3 + index >= n_rows
240
247
 
241
- if task.dif
242
- setpos 3 + index, max + 2
243
- ui_task_string(task, 3 + index, max + 2, str: format('%.2f s', task.dif))
244
- elsif task.depends && !task.depends.empty?
245
- setpos 3 + index, max
246
- x = max + 2
247
- addstr ':'
248
+ if task.depends && !task.depends.empty?
249
+ x = max
250
+ x = ui_task_string(task, 3 + index, x, str: ':') + 1
248
251
  task.depends.each do |dependant_task|
249
252
  x = ui_task_string(dependant_task, 3 + index, x) + 1
250
253
  end
254
+ else
255
+ x = max + 1
256
+ last = executor.stats_last(task)
257
+ if task.dif
258
+ str = format ' %.2f s', task.dif
259
+ elsif last['avg'] && last['err']
260
+ str = format '≈ %.2f s ± %.2f %s', last['avg'], last['err'], task.worker&.name
261
+ elsif last['avg']
262
+ str = format '≈ %.2f s %s', last['avg'], task.worker&.name
263
+ end
264
+ ui_task_string(task, 3 + index, x, str: str) if str
251
265
  end
252
266
  end
253
267
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fasten
4
- VERSION = '0.5.2'
4
+ VERSION = '0.5.4'
5
5
  end
data/lib/fasten/worker.rb CHANGED
@@ -8,11 +8,16 @@ module Fasten
8
8
  end
9
9
  end
10
10
 
11
- class Worker < Task
11
+ class Worker
12
12
  include Fasten::Logger
13
+ include Fasten::State
14
+
15
+ attr_accessor :executor, :name, :spinner, :child_read, :child_write, :parent_read, :parent_write, :pid, :block, :running_task
13
16
 
14
17
  def initialize(executor:, name: nil)
15
- super executor: executor, name: name, spinner: 0
18
+ self.executor = executor
19
+ self.name = name
20
+ self.spinner = 0
16
21
  end
17
22
 
18
23
  def perform(task)
@@ -48,40 +53,32 @@ module Fasten
48
53
  end
49
54
 
50
55
  def send_request(task)
51
- Marshal.dump(Task.new(task.to_h.merge(depends: nil, dependants: nil)), parent_write)
52
- self.running_task = task
53
- task.worker = self
54
56
  task.state = :RUNNING
57
+ task.worker = self
58
+ self.running_task = task
59
+ self.state = :RUNNING
60
+ Marshal.dump(task, parent_write)
55
61
  end
56
62
 
57
63
  def receive_response
58
64
  updated_task = Marshal.load(parent_read) # rubocop:disable Security/MarshalLoad because pipe is a secure channel
59
65
 
60
- %i[ini fin response error].each { |key| running_task[key] = updated_task[key] }
66
+ %i[state ini fin dif response error].each { |key| running_task.send "#{key}=", updated_task.send(key) }
61
67
 
62
68
  task = running_task
63
- self.running_task = nil
64
- task.state = task.error ? :FAIL : :DONE
69
+ self.running_task = self.state = nil
65
70
 
66
71
  task
67
72
  end
68
73
 
69
74
  def kill
70
75
  log_info 'Removing worker'
71
- Process.kill(:KILL, pid)
76
+ Process.kill :KILL, pid
72
77
  close_parent_pipes
73
78
  rescue StandardError => error
74
79
  log_warn "Ignoring error killing worker #{self}, error: #{error}"
75
80
  end
76
81
 
77
- def idle?
78
- running_task.nil?
79
- end
80
-
81
- def running?
82
- !idle?
83
- end
84
-
85
82
  protected
86
83
 
87
84
  def create_pipes
@@ -127,7 +124,9 @@ module Fasten
127
124
  log_ini task, 'perform'
128
125
 
129
126
  perform(task)
127
+ task.state = :DONE
130
128
  rescue StandardError => error
129
+ task.state = :FAIL
131
130
  task.error = WorkerError.new(error)
132
131
  ensure
133
132
  log_fin task, 'perform'
@@ -0,0 +1,48 @@
1
+ module Fasten
2
+ module Yaml
3
+ def transform_params(params)
4
+ params.keys.each do |k|
5
+ val = params[k]
6
+
7
+ if val.is_a?(String) && (match = %r{^/(.+)/$}.match(val))
8
+ val = Regexp.new(match[1])
9
+ end
10
+
11
+ params[k.to_sym] = val
12
+ params.delete(k)
13
+ end
14
+ end
15
+
16
+ def load_yaml(path)
17
+ items = YAML.safe_load(File.read(path)).each do |name, params|
18
+ if params.is_a? String
19
+ params = { after: params }
20
+ elsif params.is_a? Hash
21
+ transform_params(params)
22
+ else
23
+ params = {}
24
+ end
25
+
26
+ add Fasten::Task.new({ name: name }.merge(params))
27
+ end
28
+
29
+ log_info "Loaded #{items.count} tasks from #{path}"
30
+ end
31
+
32
+ def save_yaml(path)
33
+ keys = %i[after shell]
34
+
35
+ items = task_list.map do |task|
36
+ data = task.to_h.select do |key, _val|
37
+ keys.include? key
38
+ end
39
+
40
+ [task.name, data]
41
+ end.to_h
42
+
43
+ File.write path, items.to_yaml
44
+
45
+ log_info "Loaded #{items.count} tasks into #{path}"
46
+ end
47
+ end
48
+ end
data/lib/fasten.rb CHANGED
@@ -11,12 +11,13 @@ require 'csv'
11
11
  require 'hirb'
12
12
  require 'parallel'
13
13
 
14
+ require 'fasten/state'
14
15
  require 'fasten/logger'
15
16
  require 'fasten/stats'
16
17
  require 'fasten/task'
17
18
  require 'fasten/ui'
18
19
  require 'fasten/dag'
19
- require 'fasten/load_save'
20
+ require 'fasten/yaml'
20
21
  require 'fasten/executor'
21
22
  require 'fasten/worker'
22
23
  require 'fasten/version'
@@ -25,9 +26,9 @@ module Fasten
25
26
  class << self
26
27
  include Fasten::Logger
27
28
 
28
- def load(path, **options)
29
+ def from_yaml(path, **options)
29
30
  executor = Fasten::Executor.new(**options)
30
- executor.load(path)
31
+ executor.load_yaml(path)
31
32
 
32
33
  executor
33
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fasten
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldrin Martoq
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-26 00:00:00.000000000 Z
11
+ date: 2018-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -161,8 +161,8 @@ files:
161
161
  - lib/fasten.rb
162
162
  - lib/fasten/dag.rb
163
163
  - lib/fasten/executor.rb
164
- - lib/fasten/load_save.rb
165
164
  - lib/fasten/logger.rb
165
+ - lib/fasten/state.rb
166
166
  - lib/fasten/stats.rb
167
167
  - lib/fasten/task.rb
168
168
  - lib/fasten/ui.rb
@@ -170,6 +170,7 @@ files:
170
170
  - lib/fasten/ui/curses.rb
171
171
  - lib/fasten/version.rb
172
172
  - lib/fasten/worker.rb
173
+ - lib/fasten/yaml.rb
173
174
  homepage: https://github.com/a0/fasten/
174
175
  licenses:
175
176
  - MIT
@@ -1,73 +0,0 @@
1
- module Fasten
2
- module LoadSave
3
- attr_reader :stats_path
4
-
5
- def load(path)
6
- items = YAML.safe_load(File.read(path)).each do |name, params|
7
- if params.is_a? String
8
- params = { after: params }
9
- else
10
- params&.each do |key, val|
11
- next unless val.is_a?(String) && (match = %r{^/(.+)/$}.match(val))
12
-
13
- params[key] = Regexp.new(match[1])
14
- end
15
- end
16
-
17
- add Fasten::Task.new({ name: name }.merge(params || {}))
18
- end
19
-
20
- log_info "Loaded #{items.count} tasks from #{path}"
21
- end
22
-
23
- def save(path)
24
- keys = %i[after shell]
25
-
26
- items = task_list.map do |task|
27
- data = task.to_h.select do |key, _val|
28
- keys.include? key
29
- end
30
-
31
- [task.name, data]
32
- end.to_h
33
-
34
- File.write path, items.to_yaml
35
-
36
- log_info "Loaded #{items.count} tasks into #{path}"
37
- end
38
-
39
- def load_stats
40
- return unless @stats_path && File.exist?(@stats_path)
41
-
42
- self.stats_data = []
43
- CSV.foreach(@stats_path, headers: true) do |row|
44
- stats_data << row.to_h
45
- end
46
-
47
- @task_waiting_list = nil
48
- rescue StandardError
49
- nil
50
- ensure
51
- self.stats ||= {}
52
- end
53
-
54
- def save_stats
55
- return unless @stats_path && stats_data
56
-
57
- CSV.open(@stats_path, 'wb') do |csv|
58
- csv << stats_data.first.keys
59
-
60
- stats_data.each do |data|
61
- csv << data.values
62
- end
63
- end
64
- end
65
-
66
- def setup_stats(name)
67
- @stats_path = "#{ENV['HOME']}/.fasten/stats/#{name}.csv" if ENV['HOME'] && name
68
- FileUtils.mkdir_p File.dirname(@stats_path)
69
- rescue StandardError
70
- @stats_path = nil
71
- end
72
- end
73
- end