fasten 0.12.4 → 0.14.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3529d50d7d2bc28cb85f1c41828504cf602b8b04b066e7a3ab11ec2102c6aee0
4
- data.tar.gz: f68633c37fc30cded6460bf7093ca010d6d214f4d04ed592bc938e078eb8a1b0
3
+ metadata.gz: 790a1d8191184aff7cc7db302aa2675d7222321fadc2762f23037f1ee31c4673
4
+ data.tar.gz: 19d21a87a855fdcb23a07ffd6ccb5787eaf33dfb1ce9eba8edb2e49ffa06ee62
5
5
  SHA512:
6
- metadata.gz: 7d7afa32077b61a6c4d2f14966eee04925bab79b63e4fb297ab64426aabf74def1e7e67ce48c35d58e6d5e883ca112e73d7b37d616c338a4b7d11d618df30919
7
- data.tar.gz: 2b662abc55dc8f6a3248e4b67959c52240b6f27635719295b87dfb794e7d40f8c4573716137ea22a01f7b9076dcdc79c515d9b326692a72bd4696026278f3c18
6
+ metadata.gz: 19539cc0b6f947655a61533020107d8b7e023a3ed69de5aa7b88979f21c2d9ee71a9549c2f1d704880f23f7c666ac57a60ed86724c17c1f34bbe74c9f848b0c5
7
+ data.tar.gz: b3b679c630152b39a489748ace2293992ddef25791698599fe8302257898aec2b47d6ebd4e030e3ac1d75829b18d07581aa7e83995db5a7ef7993c9da8b4055b
data/.gitignore CHANGED
@@ -9,6 +9,8 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # custom ignores
12
14
  /fasten/log
13
15
  /demo/fasten/log
14
16
  *.testfile
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- Metrics/LineLength:
1
+ Layout/LineLength:
2
2
  Max: 160
3
3
  Style/Documentation:
4
4
  Enabled: false
@@ -23,10 +23,14 @@ Style:
23
23
  Metrics/AbcSize:
24
24
  Max: 25
25
25
  Metrics/MethodLength:
26
- Max: 15
26
+ Max: 20
27
27
  Metrics/ParameterLists:
28
28
  Max: 8
29
- Layout/AlignHash:
29
+ Layout/HashAlignment:
30
30
  EnforcedHashRocketStyle: table
31
31
  Style/FormatStringToken:
32
32
  Enabled: false
33
+
34
+ AllCops:
35
+ NewCops: enable
36
+ TargetRubyVersion: 2.6
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.6.4
1
+ ruby-2.6.6
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.6.4
1
+ ruby 2.6.6
data/.travis.yml CHANGED
@@ -3,5 +3,5 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.5.1
7
- before_install: gem install bundler -v 1.16.4
6
+ - 2.7.2
7
+ before_install: gem install bundler -v 2.1.4
data/CODE_OF_CONDUCT.md CHANGED
@@ -68,7 +68,7 @@ members of the project's leadership.
68
68
  ## Attribution
69
69
 
70
70
  This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
72
 
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile CHANGED
@@ -4,3 +4,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in fasten.gemspec
6
6
  gemspec
7
+
8
+ gem 'os'
9
+ gem 'rake', '~> 12.0'
10
+ gem 'rspec', '~> 3.0'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fasten (0.12.4)
4
+ fasten (0.14.4)
5
5
  binding_of_caller
6
6
  hirb
7
7
  os
@@ -10,59 +10,71 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- ast (2.4.0)
14
- binding_of_caller (0.8.0)
13
+ ast (2.4.2)
14
+ binding_of_caller (1.0.0)
15
15
  debug_inspector (>= 0.0.1)
16
- coderay (1.1.2)
17
- curses (1.3.1)
18
- debug_inspector (0.0.3)
19
- diff-lcs (1.3)
16
+ coderay (1.1.3)
17
+ curses (1.4.1)
18
+ debug_inspector (1.1.0)
19
+ diff-lcs (1.4.4)
20
20
  hirb (0.7.3)
21
- jaro_winkler (1.5.3)
22
- method_source (0.9.2)
23
- os (1.0.1)
24
- parallel (1.17.0)
25
- parser (2.6.5.0)
26
- ast (~> 2.4.0)
27
- pry (0.12.2)
28
- coderay (~> 1.1.0)
29
- method_source (~> 0.9.0)
21
+ method_source (1.0.0)
22
+ os (1.1.1)
23
+ parallel (1.20.1)
24
+ parser (3.0.1.1)
25
+ ast (~> 2.4.1)
26
+ pry (0.14.1)
27
+ coderay (~> 1.1)
28
+ method_source (~> 1.0)
30
29
  rainbow (3.0.0)
31
- rake (10.5.0)
32
- rspec (3.8.0)
33
- rspec-core (~> 3.8.0)
34
- rspec-expectations (~> 3.8.0)
35
- rspec-mocks (~> 3.8.0)
36
- rspec-core (3.8.2)
37
- rspec-support (~> 3.8.0)
38
- rspec-expectations (3.8.5)
30
+ rake (12.3.3)
31
+ regexp_parser (2.1.1)
32
+ rexml (3.2.5)
33
+ rspec (3.10.0)
34
+ rspec-core (~> 3.10.0)
35
+ rspec-expectations (~> 3.10.0)
36
+ rspec-mocks (~> 3.10.0)
37
+ rspec-core (3.10.1)
38
+ rspec-support (~> 3.10.0)
39
+ rspec-expectations (3.10.1)
39
40
  diff-lcs (>= 1.2.0, < 2.0)
40
- rspec-support (~> 3.8.0)
41
- rspec-mocks (3.8.2)
41
+ rspec-support (~> 3.10.0)
42
+ rspec-mocks (3.10.2)
42
43
  diff-lcs (>= 1.2.0, < 2.0)
43
- rspec-support (~> 3.8.0)
44
- rspec-support (3.8.3)
45
- rubocop (0.75.0)
46
- jaro_winkler (~> 1.5.1)
44
+ rspec-support (~> 3.10.0)
45
+ rspec-support (3.10.2)
46
+ rubocop (1.16.0)
47
47
  parallel (~> 1.10)
48
- parser (>= 2.6)
48
+ parser (>= 3.0.0.0)
49
49
  rainbow (>= 2.2.2, < 4.0)
50
+ regexp_parser (>= 1.8, < 3.0)
51
+ rexml
52
+ rubocop-ast (>= 1.7.0, < 2.0)
50
53
  ruby-progressbar (~> 1.7)
51
- unicode-display_width (>= 1.4.0, < 1.7)
52
- ruby-progressbar (1.10.1)
53
- unicode-display_width (1.6.0)
54
+ unicode-display_width (>= 1.4.0, < 3.0)
55
+ rubocop-ast (1.7.0)
56
+ parser (>= 3.0.1.1)
57
+ rubocop-rake (0.5.1)
58
+ rubocop
59
+ rubocop-rspec (2.3.0)
60
+ rubocop (~> 1.0)
61
+ rubocop-ast (>= 1.1.0)
62
+ ruby-progressbar (1.11.0)
63
+ unicode-display_width (2.0.0)
54
64
 
55
65
  PLATFORMS
56
66
  ruby
57
67
 
58
68
  DEPENDENCIES
59
- bundler (~> 1.17.1)
60
69
  curses
61
70
  fasten!
71
+ os
62
72
  pry
63
- rake (~> 10.0)
73
+ rake (~> 12.0)
64
74
  rspec (~> 3.0)
65
75
  rubocop
76
+ rubocop-rake
77
+ rubocop-rspec
66
78
 
67
79
  BUNDLED WITH
68
- 1.17.3
80
+ 1.17.2
data/README.md CHANGED
@@ -40,7 +40,7 @@ It has been released so people can try it and make feature requests, comments an
40
40
  - [x] Press ⬅️ or ➡️ keys to dynamically increase/decrease number of jobs
41
41
  - [ ] Use ⬆️ and ⬇️ to select tasks
42
42
  - [ ] Calculate ETA, assuming all tasks take same time
43
- - [ ] Calculate ETA, based on saved tasks statistics
43
+ - [x] Calculate ETA, based on saved tasks statistics
44
44
  - [ ] Live tail -f of selected tasks
45
45
 
46
46
  ### UI/Console
data/fasten.gemspec CHANGED
@@ -1,6 +1,4 @@
1
- lib = File.expand_path('lib', __dir__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'fasten/version'
1
+ require_relative 'lib/fasten/version'
4
2
  require 'os'
5
3
 
6
4
  Gem::Specification.new do |spec|
@@ -13,29 +11,31 @@ Gem::Specification.new do |spec|
13
11
  spec.description = 'Fasten your seatbelts! Run jobs in parallel, intelligently.'
14
12
  spec.homepage = 'https://github.com/a0/a0-fasten-ruby/'
15
13
  spec.license = 'MIT'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
15
+
16
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/a0/a0-fasten-ruby/CHANGELOG.md'
16
21
 
17
22
  # Specify which files should be added to the gem when it is released.
18
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
25
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|demo)/}) }
21
26
  end
22
27
  spec.bindir = 'exe'
23
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
29
  spec.require_paths = ['lib']
25
30
 
26
- spec.add_development_dependency 'bundler', '~> 1.17.1'
27
31
  spec.add_development_dependency 'curses' unless OS.windows?
28
32
  spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'rake', '~> 10.0'
30
- spec.add_development_dependency 'rspec', '~> 3.0'
31
33
  spec.add_development_dependency 'rubocop'
34
+ spec.add_development_dependency 'rubocop-rake'
35
+ spec.add_development_dependency 'rubocop-rspec'
32
36
 
33
37
  spec.add_runtime_dependency 'binding_of_caller'
34
38
  spec.add_runtime_dependency 'hirb'
35
39
  spec.add_runtime_dependency 'os'
36
40
  spec.add_runtime_dependency 'parallel'
37
-
38
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
39
-
40
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
41
41
  end
@@ -35,13 +35,13 @@ module Fasten
35
35
 
36
36
  require 'fasten/ui/curses'
37
37
 
38
- @default_ui_mode = STDIN.tty? && STDOUT.tty? ? :curses : :console
38
+ @default_ui_mode = $stdin.tty? && $stdout.tty? ? :curses : :console
39
39
  rescue StandardError, LoadError
40
40
  @default_ui_mode = :console
41
41
  end
42
42
 
43
43
  def default_developer
44
- STDIN.tty? && STDOUT.tty?
44
+ $stdin.tty? && $stdout.tty?
45
45
  end
46
46
 
47
47
  def default_priority
data/lib/fasten/runner.rb CHANGED
@@ -37,7 +37,6 @@ module Fasten
37
37
  end
38
38
 
39
39
  initialize_stats
40
- initialize_logger
41
40
  end
42
41
 
43
42
  def task(name, **opts, &block)
@@ -51,9 +50,12 @@ module Fasten
51
50
  end
52
51
 
53
52
  def perform
53
+ initialize_logger
54
+ StdThreadProxy.install if use_threads
54
55
  self.state = :RUNNING
55
56
  log_ini self, running_counters
56
57
  load_stats
58
+ touch_task_logs
57
59
 
58
60
  run_ui do
59
61
  perform_loop
@@ -66,9 +68,20 @@ module Fasten
66
68
 
67
69
  stats_summary if summary
68
70
  ensure
71
+ StdThreadProxy.uninstall if use_threads
72
+ close_logger
69
73
  save_stats
70
74
  end
71
75
 
76
+ def touch_task_logs
77
+ FileUtils.mkdir_p "#{fasten_dir}/log/task/"
78
+ tasks.each do |task|
79
+ path = "#{fasten_dir}/log/task/#{task.name}.log"
80
+ puts "Fasten: creating log #{path}"
81
+ FileUtils.touch path
82
+ end
83
+ end
84
+
72
85
  def map(list, &block)
73
86
  list.each do |item|
74
87
  task item.to_s, request: item, &block
@@ -218,7 +231,7 @@ module Fasten
218
231
  end
219
232
 
220
233
  def find_or_create_worker(worker_class:)
221
- worker = workers.find { |item| item.class == worker_class && item.running_task.nil? }
234
+ worker = workers.find { |item| item.instance_of?(worker_class) && item.running_task.nil? }
222
235
 
223
236
  unless worker
224
237
  @worker_id = (@worker_id || 0) + 1
@@ -1,24 +1,28 @@
1
1
  module Fasten
2
2
  class StdThreadProxy
3
- def initialize(original)
4
- @original = original
3
+ attr_reader :fasten_original
4
+
5
+ def initialize(fasten_original)
6
+ @fasten_original = fasten_original
5
7
  end
6
8
 
7
- def respond_to?(name, include_private = false)
8
- target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @original
9
- target.send :respond_to?, name, include_private
9
+ def respond_to?(*args)
10
+ target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @fasten_original
11
+ target.send :respond_to?, *args
10
12
  end
11
13
 
12
14
  private
13
15
 
14
- def respond_to_missing?(name, include_private = false)
15
- target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @original
16
- target.send :respond_to_missing?, name, include_private
16
+ def respond_to_missing?(*args)
17
+ target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @fasten_original
18
+ target.send :respond_to_missing?, *args
17
19
  end
18
20
 
19
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper
20
- target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @original
21
+ def method_missing(method, *args, &block)
22
+ target = Thread.current[:FASTEN_STD_THREAD_PROXY] || @fasten_original
21
23
  target.send method, *args, &block
24
+ rescue StandardError => e
25
+ raise e
22
26
  end
23
27
 
24
28
  class << self
@@ -28,8 +32,9 @@ module Fasten
28
32
  oldverbose = $VERBOSE
29
33
  $VERBOSE = nil
30
34
 
31
- Object.const_set :STDOUT, StdThreadProxy.new(STDOUT)
32
- Object.const_set :STDERR, StdThreadProxy.new(STDERR)
35
+ Object.const_set :STDOUT, StdThreadProxy.new(STDOUT) # rubocop:disable Style/GlobalStdStream
36
+ Object.const_set :STDERR, StdThreadProxy.new(STDERR) # rubocop:disable Style/GlobalStdStream
37
+
33
38
  $stdout = StdThreadProxy.new $stdout
34
39
  $stderr = StdThreadProxy.new $stderr
35
40
 
@@ -49,7 +54,18 @@ module Fasten
49
54
  def uninstall
50
55
  return unless @installed
51
56
 
57
+ oldverbose = $VERBOSE
58
+ $VERBOSE = nil
59
+
60
+ Object.const_set :STDOUT, STDOUT.fasten_original if STDOUT.is_a? StdThreadProxy # rubocop:disable Style/GlobalStdStream
61
+ Object.const_set :STDERR, STDERR.fasten_original if STDERR.is_a? StdThreadProxy # rubocop:disable Style/GlobalStdStream
62
+
63
+ $stdout = $stdout.fasten_original if $stdout.is_a? StdThreadProxy
64
+ $stderr = $stderr.fasten_original if $stderr.is_a? StdThreadProxy
65
+
52
66
  @installed = nil
67
+ ensure
68
+ $VERBOSE = oldverbose
53
69
  end
54
70
  end
55
71
  end
@@ -75,12 +75,8 @@ module Fasten
75
75
  end
76
76
 
77
77
  def redirect_std(path)
78
- logger.reopen($stdout)
79
-
80
- @saved_stdout_instance = $stdout.clone
81
- @saved_stderr_instance = $stderr.clone
82
- @saved_stdout_constant = STDOUT.clone
83
- @saved_stderr_constant = STDERR.clone
78
+ @saved_stdout_constant ||= $stdout.clone
79
+ @saved_stderr_constant ||= $stderr.clone
84
80
 
85
81
  FileUtils.mkdir_p File.dirname(path)
86
82
  @redirect_log = File.new path, 'a'
@@ -88,22 +84,15 @@ module Fasten
88
84
 
89
85
  $stdout.reopen @redirect_log
90
86
  $stderr.reopen @redirect_log
91
- STDOUT.reopen @redirect_log
92
- STDERR.reopen @redirect_log
87
+
88
+ logger.reopen(@redirect_log)
93
89
  end
94
90
 
95
91
  def restore_std
96
- oldverbose = $VERBOSE
97
- $VERBOSE = nil
98
-
99
- $stdout = @saved_stdout_instance
100
- $stderr = @saved_stderr_instance
101
- Object.const_set :STDOUT, @saved_stdout_constant
102
- Object.const_set :STDERR, @saved_stderr_constant
103
-
104
92
  @redirect_log.close
105
- ensure
106
- $VERBOSE = oldverbose
93
+
94
+ $stdout.reopen @saved_stdout_constant
95
+ $stderr.reopen @saved_stderr_constant
107
96
  end
108
97
  end
109
98
  end
@@ -26,14 +26,18 @@ module Fasten
26
26
  if log_file
27
27
  self.log_file = log_file
28
28
  else
29
- log_path ||= "#{fasten_dir}/log/#{kind}/#{name}.log"
30
- FileUtils.mkdir_p File.dirname(log_path)
31
- self.log_file = File.new(log_path, 'a')
32
- self.log_file.sync = true
29
+ self.log_file ||= "#{fasten_dir}/log/#{kind}/#{name}.log"
30
+ FileUtils.mkdir_p File.dirname(self.log_file)
33
31
  end
32
+
33
+ close_logger
34
34
  self.logger = ::Logger.new self.log_file, level: Fasten.logger.level, progname: Fasten.logger.progname
35
35
  end
36
36
 
37
+ def close_logger
38
+ logger.close if logger.is_a? ::Logger
39
+ end
40
+
37
41
  def log_ini(object, message = nil)
38
42
  object.ini ||= Time.new
39
43
  log_info "Ini #{object.state} #{object.class} #{object} #{message}"
@@ -51,7 +55,7 @@ end
51
55
 
52
56
  Fasten.logger ||=
53
57
  begin
54
- Logger.new STDOUT, level: Logger::DEBUG, progname: $PROGRAM_NAME
58
+ Logger.new $stdout, level: Logger::DEBUG, progname: $PROGRAM_NAME
55
59
  rescue StandardError
56
60
  nil
57
61
  end
@@ -1,7 +1,9 @@
1
+ require 'digest'
2
+
1
3
  module Fasten
2
4
  module Support
3
5
  module State
4
- attr_accessor :error, :ini, :fin, :dif, :last
6
+ attr_accessor :error, :ini, :fin, :dif, :runner
5
7
  attr_writer :state
6
8
 
7
9
  def state
@@ -27,6 +29,54 @@ module Fasten
27
29
  def quitting?
28
30
  state == :QUITTING
29
31
  end
32
+
33
+ def last_stat
34
+ return @last_stat if defined? @last_stat
35
+
36
+ return {} unless @runner
37
+
38
+ @last_stat = runner.stats_last(self)
39
+ end
40
+
41
+ def last_avg
42
+ @last_avg ||= last_stat['avg']
43
+ end
44
+
45
+ def last_err
46
+ @last_err ||= last_stat['err']
47
+ end
48
+
49
+ def deps
50
+ return @deps if defined? @deps
51
+
52
+ str = deps_str
53
+
54
+ @deps = str && Digest::SHA1.hexdigest(str)
55
+ end
56
+
57
+ def deps_str
58
+ if is_a? Fasten::Task
59
+ deps_str_task
60
+ elsif is_a? Fasten::Runner
61
+ deps_str_runner
62
+ end
63
+ end
64
+
65
+ def deps_str_task
66
+ if after.is_a? Array
67
+ after.sort_by do |task|
68
+ task.is_a?(Fasten::Task) ? task.name : task
69
+ end&.join(', ')
70
+ else
71
+ after
72
+ end
73
+ end
74
+
75
+ def deps_str_runner
76
+ tasks.sort_by(&:name).map do |task|
77
+ [task.name, task.deps_str].compact.join(': ')
78
+ end.join("\n")
79
+ end
30
80
  end
31
81
  end
32
82
  end
@@ -5,8 +5,8 @@ require 'fileutils'
5
5
  module Fasten
6
6
  module Support
7
7
  module Stats
8
- attr_writer :stats_data, :stats_entries
9
- attr_reader :stats_path
8
+ # attr_writer :stats_data, :stats_entries
9
+ # attr_reader :stats_path
10
10
 
11
11
  def initialize_stats
12
12
  return unless stats
@@ -20,14 +20,17 @@ module Fasten
20
20
  def load_stats
21
21
  return unless @stats_path && File.exist?(@stats_path)
22
22
 
23
- self.stats_data = []
24
- CSV.foreach(@stats_path, headers: true) do |row|
23
+ @stats_data = []
24
+ CSV.foreach(@stats_path, headers: true, converters: [:all]) do |row|
25
25
  stats_data << row.to_h
26
26
  end
27
27
 
28
28
  @tasks.each do |task|
29
- stats_last(task)
29
+ task.runner = self
30
+ task.last_stat
30
31
  end
32
+ self.runner = self
33
+ last_stat
31
34
 
32
35
  @tasks.waiting = nil
33
36
  rescue StandardError
@@ -37,7 +40,7 @@ module Fasten
37
40
  def save_stats
38
41
  return unless @stats_path && stats_data
39
42
 
40
- keys = %w[state kind name run cnt avg std err ini fin]
43
+ keys = %w[state kind name run cnt avg std err ini fin deps]
41
44
 
42
45
  CSV.open(@stats_path, 'wb') do |csv|
43
46
  csv << keys
@@ -55,6 +58,7 @@ module Fasten
55
58
  'ini' => target.ini.to_f,
56
59
  'fin' => target.fin.to_f,
57
60
  'run' => target.fin - target.ini,
61
+ 'deps' => target.deps,
58
62
  'worker' => target.respond_to?(:worker) ? target.worker.name : nil }
59
63
  end
60
64
 
@@ -73,7 +77,7 @@ module Fasten
73
77
  stats_data << entry
74
78
  stats_entries << entry
75
79
 
76
- history = stats_history(entry)
80
+ history = stats_run_history(entry)
77
81
 
78
82
  update_stats(history, entry)
79
83
  end
@@ -120,14 +124,12 @@ module Fasten
120
124
  task: hformat(sub), runner: hformat(tot, sub), saved: hformat(sub - tot, sub), jobs: jobs.to_s)
121
125
  end
122
126
 
123
- def stats_history(entry)
124
- stats_data.select { |e| e['state'] == entry['state'] && e['kind'] == entry['kind'] && e['name'] == entry['name'] }.map { |x| x['run'].to_f }
127
+ def stats_run_history(entry)
128
+ stats_data.select { |item| %w[state kind name deps].all? { |key| item[key] == entry[key] } }.map { |item| item['run'] }
125
129
  end
126
130
 
127
- def stats_last(item)
128
- return item.last if item.last
129
-
130
- item.last = stats_data.select { |e| e['kind'] == item.kind && e['name'] == item.name }.last || {}
131
+ def stats_last(target)
132
+ stats_data.select { |item| %w[kind name deps].all? { |key| item[key] == target.send(key) } }.last || {}
131
133
  end
132
134
 
133
135
  def update_stats(history, entry)
@@ -54,7 +54,6 @@ module Fasten
54
54
  def restore_std
55
55
  @redirect_log&.close
56
56
  StdThreadProxy.thread_io = nil
57
- logger.reopen(log_file)
58
57
  end
59
58
  end
60
59
  end
@@ -6,7 +6,7 @@ module Fasten
6
6
  def ui
7
7
  require 'fasten/ui/curses'
8
8
 
9
- @ui ||= if ui_mode.to_s == 'curses' && STDIN.tty? && STDOUT.tty?
9
+ @ui ||= if ui_mode.to_s == 'curses' && $stdin.tty? && $stdout.tty?
10
10
  Fasten::UI::Curses.new(runner: self)
11
11
  else
12
12
  Fasten::UI::Console.new(runner: self)
@@ -5,9 +5,10 @@ module Fasten
5
5
  module Yaml
6
6
  def load_yaml(path)
7
7
  items = YAML.safe_load(File.read(path)).each do |name, params|
8
- if params.is_a? String
8
+ case params
9
+ when String
9
10
  params = { after: params }
10
- elsif params.is_a? Hash
11
+ when Hash
11
12
  transform_params(params)
12
13
  else
13
14
  params = {}
@@ -38,15 +39,16 @@ module Fasten
38
39
  protected
39
40
 
40
41
  def transform_params(params)
41
- params.keys.each do |k|
42
- val = params[k]
42
+ keys = params.keys
43
+
44
+ keys.each do |key|
45
+ val = params.delete key
43
46
 
44
47
  if val.is_a?(String) && (match = %r{^/(.+)/$}.match(val))
45
48
  val = Regexp.new(match[1])
46
49
  end
47
50
 
48
- params[k.to_sym] = val
49
- params.delete(k)
51
+ params[key.to_sym] = val
50
52
  end
51
53
  end
52
54
  end
data/lib/fasten/task.rb CHANGED
@@ -4,8 +4,7 @@ module Fasten
4
4
  class Task
5
5
  include Fasten::Support::State
6
6
 
7
- attr_accessor :name, :after, :shell, :ruby, :worker_class
8
- attr_accessor :dependants, :depends, :request, :response, :worker, :run_score, :weight, :block
7
+ attr_accessor :name, :after, :shell, :ruby, :worker_class, :dependants, :depends, :request, :response, :worker, :run_score, :weight, :block
9
8
 
10
9
  def initialize(name:, shell: nil, ruby: nil, block: nil, request: nil, after: nil, weight: 1, worker_class: nil)
11
10
  self.name = name
@@ -16,6 +15,20 @@ module Fasten
16
15
  self.after = after
17
16
  self.weight = weight
18
17
  self.worker_class = worker_class
18
+
19
+ # ObjectSpace.define_finalizer(self) do
20
+ # puts "I am dying! pid: #{Process.pid} thread: #{Thread.current} TASK #{@name}"
21
+ # end
22
+
23
+ block&.object_id
24
+ # block && begin
25
+
26
+ # # puts "block_id: #{block.object_id} for task #{@name}"
27
+ # end
28
+
29
+ # block && ObjectSpace.define_finalizer(block) do
30
+ # puts "I am dying! pid: #{Process.pid} thread: #{Thread.current} TASK #{@name} BLOCK"
31
+ # end
19
32
  end
20
33
 
21
34
  def marshal_dump
@@ -24,7 +37,11 @@ module Fasten
24
37
 
25
38
  def marshal_load(data)
26
39
  @name, @state, @ini, @fin, @dif, @request, @response, @shell, @ruby, block_id, @error = data
27
- @block = ObjectSpace._id2ref block_id if block_id
40
+ @block = begin
41
+ ObjectSpace._id2ref block_id.to_i if block_id
42
+ rescue StandardError
43
+ # pass
44
+ end
28
45
 
29
46
  raise "Sorry, unable to get block for task #{self}, please try using threads" if block_id && !@block.is_a?(Proc)
30
47
  end
@@ -2,7 +2,7 @@ module Fasten
2
2
  class TaskManager < Array # rubocop:disable Metrics/ClassLength
3
3
  attr_reader :done, :failed, :pending, :running, :targets, :runner
4
4
 
5
- def initialize(targets: [], runner:)
5
+ def initialize(runner:, targets: [])
6
6
  super()
7
7
 
8
8
  @map = {}
@@ -120,6 +120,10 @@ module Fasten
120
120
  mark_needed(task)
121
121
  end
122
122
 
123
+ setup_pending
124
+ end
125
+
126
+ def setup_pending
123
127
  @pending.reject { |task| task.state == :NEED }.each do |task|
124
128
  @pending.delete task
125
129
  delete task
@@ -159,8 +163,8 @@ module Fasten
159
163
  when :dependants_avg
160
164
  @waiting.sort_by!.with_index do |task, index|
161
165
  task.state = :WAIT
162
- last_avg = task.last && task.last['avg'] || 0
163
- [-task.run_score, -last_avg.to_f, index]
166
+ last_avg = task.last_avg || 0
167
+ [-task.run_score, -last_avg, index]
164
168
  end
165
169
  else
166
170
  raise "Unknown priority #{@runner.priority}"
@@ -49,7 +49,7 @@ module Fasten
49
49
  return unless old.count != orig.count
50
50
 
51
51
  (orig - old).each do |task|
52
- puts "Time: #{hformat Time.new - runner.ini} #{message} #{hformat task.dif} Task #{task}"
52
+ puts "Time: #{hformat Time.new - runner.ini} #{message} #{hformat task.dif} #{task.worker} Task #{task}"
53
53
  old << task
54
54
  end
55
55
  end
@@ -16,6 +16,8 @@ module Fasten
16
16
 
17
17
  SPINNER_STR = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
18
18
  SPINNER_LEN = SPINNER_STR.length
19
+ MOON_STR = '🌑🌒🌓🌔🌕'
20
+ MOON_LEN = MOON_STR.length
19
21
  PROGRESSBAR_STR = ' ▏▎▍▌▋▊▉'
20
22
  PROGRESSBAR_LEN = PROGRESSBAR_STR.length
21
23
 
@@ -37,7 +39,7 @@ module Fasten
37
39
 
38
40
  def draw_title
39
41
  ui_text_aligned(0, :left, 'Fasten your seatbelts!')
40
- ui_text_aligned(0, :center, name.to_s)
42
+ ui_text_aligned(0, :center, "#{name} #{$PID}")
41
43
  ui_text_aligned(0, :right, Time.new.to_s)
42
44
  end
43
45
 
@@ -125,11 +127,12 @@ module Fasten
125
127
  end
126
128
 
127
129
  def ui_jobs_summary
128
- running_count = tasks.running.count
129
- waiting_count = tasks.waiting.count
130
- workers_count = workers.count
130
+ running = tasks.running.count
131
+ waiting = tasks.waiting.count
132
+ working = workers.count
133
+ idle = working - running
131
134
 
132
- "Procs running: #{running_count} idle: #{workers_count - running_count} waiting: #{waiting_count} #{runner.use_threads ? 'threads' : 'processes'}: #{jobs}"
135
+ "Procs running: #{running} idle: #{idle} waiting: #{waiting} #{runner.use_threads ? 'threads' : 'processes'}: #{jobs}"
133
136
  end
134
137
 
135
138
  def ui_jobs
@@ -194,6 +197,22 @@ module Fasten
194
197
  end
195
198
  end
196
199
 
200
+ def ui_task_clock(task, cur, avg)
201
+ return unless task.ini
202
+
203
+ dif = cur - task.ini
204
+ avg = avg.to_f
205
+ if task.ini && avg.positive?
206
+ percent = dif / avg
207
+ index = (percent * MOON_LEN).to_i
208
+ index = MOON_LEN - 1 if index > MOON_LEN - 1
209
+
210
+ format ' %.2f s %s ', dif, MOON_STR[index]
211
+ else
212
+ format ' %.2f s ', dif
213
+ end
214
+ end
215
+
197
216
  def ui_task_color(task)
198
217
  rev = task == selected ? A_REVERSE : 0
199
218
 
@@ -219,6 +238,9 @@ module Fasten
219
238
 
220
239
  str ||= icon ? "#{icon} #{task}" : task.to_s
221
240
 
241
+ delta = x + str.length - n_cols
242
+ str = str[0...-delta] if delta.positive?
243
+
222
244
  attrset attrs if attrs
223
245
  addstr str
224
246
  attroff attrs if attrs
@@ -231,14 +253,38 @@ module Fasten
231
253
  worker.spinner = (worker.spinner + 1) % SPINNER_LEN if worker.running?
232
254
  end
233
255
 
256
+ cur = Time.new
257
+
234
258
  count_done = tasks.done.count
235
259
  count_total = tasks.count
236
260
  tl = count_total.to_s.length
237
- col_ini = ui_text_aligned(2, :left, format("Tasks %#{tl}d/%d", count_done, count_total)) + 1
238
- col_fin = n_cols - 5
239
- ui_text_aligned(2, :right, "#{(count_done * 100 / count_total).to_i}%") if count_total.positive?
261
+ percentstr = count_total.positive? && " #{(count_done * 100 / count_total).to_i}%"
262
+ elapsed_str = format ' %.2f s', (dif = cur - runner.ini) if runner.ini
263
+
264
+ @stat_str ||= begin
265
+ @runner_last_avg = runner.last_avg
266
+ if runner.last_avg && runner.last_err
267
+ format '≈ %.2f s ± %.2f', runner.last_avg, runner.last_err
268
+ elsif runner.last_avg
269
+ format '≈ %.2f s', runner.last_avg
270
+ end
271
+ end
272
+
273
+ end_str = [elapsed_str, @stat_str].compact.join(' ')
274
+
275
+ if @runner_last_avg
276
+ a = dif
277
+ b = @runner_last_avg
278
+ else
279
+ a = count_done
280
+ b = count_total
281
+ end
282
+
283
+ col_ini = ui_text_aligned(2, :left, format("Tasks %#{tl}d/%d%s", count_done, count_total, percentstr)) + 1
284
+ col_fin = n_cols - 1 - end_str.length
285
+ ui_text_aligned(2, :right, end_str)
240
286
 
241
- ui_progressbar(2, col_ini, col_fin, count_done, count_total)
287
+ ui_progressbar(2, col_ini, col_fin, a, b)
242
288
 
243
289
  max = 2
244
290
  list = tasks.sort_by.with_index { |x, index| [x.run_score, index] }
@@ -260,13 +306,16 @@ module Fasten
260
306
  end
261
307
  else
262
308
  x = max + 1
263
- last = runner.stats_last(task)
309
+ last_avg = task.last_avg
310
+ last_err = task.last_err
264
311
  if task.dif
265
312
  str = format ' %.2f s', task.dif
266
- elsif last['avg'] && last['err']
267
- str = format '≈ %.2f s ± %.2f %s', last['avg'], last['err'], task.worker&.name
268
- elsif last['avg']
269
- str = format '≈ %.2f s %s', last['avg'], task.worker&.name
313
+ elsif last_avg && last_err
314
+ str = format '%s ≈ %.2f s ± %.2f %s', ui_task_clock(task, cur, last_avg), last_avg, last_err, task.worker&.name
315
+ elsif last_avg
316
+ str = format '%s ≈ %.2f s %s', ui_task_clock(task, cur, last_avg), last_avg, task.worker&.name
317
+ else
318
+ str = ui_task_clock(task, cur, 0)
270
319
  end
271
320
  ui_task_string(task, 3 + index, x, str: str) if str
272
321
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fasten
4
- VERSION = '0.12.4'
4
+ VERSION = '0.14.4'
5
5
  end
data/lib/fasten/worker.rb CHANGED
@@ -89,6 +89,7 @@ module Fasten
89
89
  perform_task task
90
90
  ensure
91
91
  restore_std
92
+
92
93
  logger.reopen(log_file)
93
94
  log_fin task, 'run_task'
94
95
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fasten
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.4
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldrin Martoq
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-06 00:00:00.000000000 Z
11
+ date: 2021-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 1.17.1
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 1.17.1
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: curses
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -53,35 +39,35 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: rake
42
+ name: rubocop
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - "~>"
45
+ - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: '10.0'
47
+ version: '0'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - "~>"
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '10.0'
54
+ version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: rspec
56
+ name: rubocop-rake
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - "~>"
59
+ - - ">="
74
60
  - !ruby/object:Gem::Version
75
- version: '3.0'
61
+ version: '0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - "~>"
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '3.0'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
- name: rubocop
70
+ name: rubocop-rspec
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - ">="
@@ -199,7 +185,9 @@ licenses:
199
185
  - MIT
200
186
  metadata:
201
187
  allowed_push_host: https://rubygems.org
202
- post_install_message:
188
+ homepage_uri: https://github.com/a0/a0-fasten-ruby/
189
+ source_code_uri: https://github.com/a0/a0-fasten-ruby/CHANGELOG.md
190
+ post_install_message:
203
191
  rdoc_options: []
204
192
  require_paths:
205
193
  - lib
@@ -207,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
207
195
  requirements:
208
196
  - - ">="
209
197
  - !ruby/object:Gem::Version
210
- version: '0'
198
+ version: 2.6.0
211
199
  required_rubygems_version: !ruby/object:Gem::Requirement
212
200
  requirements:
213
201
  - - ">="
@@ -215,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
203
  version: '0'
216
204
  requirements: []
217
205
  rubygems_version: 3.0.3
218
- signing_key:
206
+ signing_key:
219
207
  specification_version: 4
220
208
  summary: Fasten your seatbelts! Run jobs in parallel, intelligently.
221
209
  test_files: []