pretty_multitask 0.1.5 → 0.1.10

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: c87f12464064816aeeca47d2f9e5168a184299a9e71583a9b718ffdb2821a365
4
- data.tar.gz: 4516ed4cfcb98738e840868a9688f44785398e67d48f979ba9195731c59347ec
3
+ metadata.gz: 857353a839ca0c6a9a4ab4cd22de49fb7768b585056f8cae5c71f813ccd87462
4
+ data.tar.gz: d08c7d0d7b6555d67a33b220cfd157c77f87adf6582a6e998c2b0d9f174f4e05
5
5
  SHA512:
6
- metadata.gz: 9e5a3357f92557477761e6175e4ffe97c9470aa3c20247647879f3acb8981cb0b6dc885adf36610a6f012658a80d19c341acbc30e28a638133b2b4b803afd683
7
- data.tar.gz: bc62a7f8b16e85cbfd984b047e4c4ae28f932ced4fbd74507b2bb73794e11e0eef07577038ab4943eca66f41f469caeed896f66d927f4ac73972ff1b0c1b7999
6
+ metadata.gz: c9a7cdf6b4be5189b430464663f8ac3f04c47b1f9368b056ab93ca21c57935e4a5447a32c445cfb5ede85195d254ca9ad9060ca845c03c12478274082ac1ca3a
7
+ data.tar.gz: 9832edc6241532f6ada83da7b532f35adb2650310a2ea80a16861cd19ea98dba33b613f10ee1efb0da7bd56d4b0daa1543f2ca8d342a3ba597cb8a4169dc3418
@@ -0,0 +1,35 @@
1
+ Style/SpecialGlobalVars:
2
+ Enabled: false
3
+
4
+ Style/ConditionalAssignment:
5
+ Enabled: false
6
+
7
+ Style/GuardClause:
8
+ Enabled: false
9
+
10
+ Layout/FirstArrayElementIndentation:
11
+ Enabled: false
12
+
13
+ Metrics/MethodLength:
14
+ Max: 35
15
+
16
+ Metrics/LineLength:
17
+ Max: 120
18
+ Exclude:
19
+ - test/**/*
20
+
21
+ Metrics/CyclomaticComplexity:
22
+ Max: 10
23
+
24
+ Metrics/AbcSize:
25
+ Max: 30
26
+
27
+ Security/MarshalLoad:
28
+ Exclude:
29
+ - lib/pretty_multitask/run_callable.rb
30
+
31
+ Layout/ElseAlignment:
32
+ Enabled: false
33
+ Metrics/BlockLength:
34
+ Exclude:
35
+ - '*.gemspec'
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in pretty_multitask.gemspec
6
8
  gemspec
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
3
5
 
4
6
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "pretty_multitask"
4
+ require 'bundler/setup'
5
+ require 'pretty_multitask'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "pretty_multitask"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1,4 +1,6 @@
1
- require "pretty_multitask/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'pretty_multitask/version'
2
4
  require 'tlopo/executor'
3
5
  require 'logger'
4
6
  require 'pty'
@@ -8,23 +10,27 @@ require 'socket'
8
10
  require 'timeout'
9
11
  require 'yaml'
10
12
  require 'fileutils'
13
+ require 'set'
11
14
 
15
+ # Main module
12
16
  module PrettyMultitask
17
+ require "#{__dir__}/pretty_multitask/trapper"
13
18
  require "#{__dir__}/pretty_multitask/color"
14
19
  require "#{__dir__}/pretty_multitask/runner"
15
20
  require "#{__dir__}/pretty_multitask/run_callable"
16
21
  end
17
22
 
18
23
  def pretty_multitask(hash)
19
- name, tasks = hash.keys.first, hash.values.first
24
+ name = hash.keys.first
25
+ tasks = hash.values.first
20
26
  task name do
21
27
  jobs = []
22
28
  tasks.each do |t|
23
- job = Proc.new do
29
+ job = proc do
24
30
  Rake::Task[t].invoke
25
31
  nil
26
32
  end
27
- jobs.push({ name: t, cmd: job})
33
+ jobs.push({ name: t, cmd: job })
28
34
  end
29
35
  PrettyMultitask::Runner.new(jobs).run
30
36
  end
@@ -1,14 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrettyMultitask
4
+ # Class to provide some ANSI colors
2
5
  class Color
3
6
  def self.green(str)
4
7
  "\e[32;1m#{str}\e[0m"
5
8
  end
9
+
6
10
  def self.yellow(str)
7
11
  "\e[33;1m#{str}\e[0m"
8
12
  end
13
+
9
14
  def self.red(str)
10
15
  "\e[31;1m#{str}\e[0m"
11
16
  end
12
17
  end
13
18
  end
14
-
@@ -1,24 +1,79 @@
1
- module PrettyMultitask
1
+ # frozen_string_literal: true
2
2
 
3
+ module PrettyMultitask
4
+ # This class will run a callable, and wrap it's output adding nice format
3
5
  class RunCallable
4
6
  def initialize(opts = {})
5
7
  @opts = opts
6
8
  end
7
-
9
+
8
10
  def run(opts = @opts)
9
11
  cmd = opts[:cmd]
10
12
  name = opts[:name]
11
- out = opts[:out] || STDOUT
12
- err = opts[:err] || STDERR
13
+ out = STDOUT
13
14
  @padding = opts[:padding]
14
15
  master, slave = PTY.open
15
16
  err_read, err_write = IO.pipe
16
-
17
+
17
18
  r, w = UNIXSocket.pair(:STREAM, 0)
18
-
19
+
20
+ pid = run_on_fork(cmd, err_write, master, w)
21
+ Trapper.trap pid
22
+
23
+ t_out = consume_and_print slave, out, name, false
24
+ t_err = consume_and_print err_read, out, name, true
25
+
26
+ chars = []
27
+ t = consume_and_store r, chars
28
+
29
+ Process.wait pid
30
+
31
+ wait_until_streams_are_ready [slave, err_read]
32
+
33
+ join_threads [t_out, t_err, t]
34
+
35
+ close_streams [master, slave, err_read, err_write]
36
+
37
+ result = Marshal.load(chars.join)
38
+ raise result[:error] if result[:error]
39
+
40
+ result[:result]
41
+ end
42
+
43
+ def consume_and_store(reader, store)
44
+ t = Thread.new do
45
+ sleep 0.1 until reader.ready?
46
+ 3.times do
47
+ store << reader.getc while reader.ready?
48
+ end
49
+ end
50
+ t
51
+ end
52
+
53
+ def wait_until_streams_are_ready(streams)
54
+ streams.each do |s|
55
+ loop { break unless s.ready? }
56
+ end
57
+ end
58
+
59
+ def join_threads(threads)
60
+ threads.each do |t|
61
+ begin
62
+ Timeout.timeout(0.1) { t.join }
63
+ rescue Timeout::Error
64
+ nil
65
+ end
66
+ end
67
+ end
68
+
69
+ def close_streams(streams)
70
+ streams.each(&:close)
71
+ end
72
+
73
+ def run_on_fork(cmd, err_w, out_w, socket_w)
19
74
  pid = fork do
20
- STDERR.reopen err_write
21
- STDOUT.reopen master
75
+ STDERR.reopen err_w
76
+ STDOUT.reopen out_w
22
77
  begin
23
78
  result = cmd.call
24
79
  obj = { result: result, error: nil }
@@ -27,45 +82,12 @@ module PrettyMultitask
27
82
  new_error.set_backtrace e.backtrace
28
83
  Logger.new(STDERR).error new_error
29
84
  obj = { result: nil, error: new_error }
30
- File.open('/tmp/error','w+'){|f| f.puts obj.to_yaml}
31
- w.puts Marshal.dump(obj), 0
32
- end
33
- w.puts Marshal.dump(obj), 0
34
- w.close
35
- end
36
-
37
- t_out = consume_and_print slave, out, name, false
38
- t_err = consume_and_print err_read, out, name, true
39
-
40
- %i[slave err_read].each do |e|
41
- loop { break unless binding.local_variable_get(e).ready? }
42
- end
43
-
44
- begin
45
- Timeout.timeout(0.1) do
46
- %i[t_out t_err].each { |e| binding.local_variable_get(e).join }
47
- end
48
- rescue Timeout::Error
49
- nil
50
- end
51
-
52
- %i[master slave err_read err_write].each { |e| binding.local_variable_get(e).close }
53
-
54
- chars = []
55
- t = Thread.new do
56
- sleep 0.1 until r.ready?
57
- (1..3).each do
58
- chars << r.getc while r.ready?
85
+ socket_w.puts Marshal.dump(obj), 0
59
86
  end
87
+ socket_w.puts Marshal.dump(obj), 0
88
+ socket_w.close
60
89
  end
61
-
62
- Process.wait pid
63
- Timeout.timeout(1) { t.join }
64
-
65
- result = Marshal.load(chars.join)
66
- raise result[:error] if result[:error]
67
-
68
- result[:result]
90
+ pid
69
91
  end
70
92
 
71
93
  def consume_and_print(reader, writer, name, error = false)
@@ -76,10 +98,12 @@ module PrettyMultitask
76
98
  colored = Color.green format(fmt, name) unless error
77
99
  reader.each_line do |line|
78
100
  writer.write "#{colored} #{line}"
79
- File.open(@opts[:out_file],'a+') do |f|
101
+ next unless @opts[:out_file]
102
+
103
+ File.open(@opts[:out_file], 'a+') do |f|
80
104
  f.puts "#{colored} #{line}" if error
81
105
  f.puts "#{colored} #{line}" unless error
82
- end if @opts[:out_file]
106
+ end
83
107
  end
84
108
  rescue Errno::EIO
85
109
  nil
@@ -1,47 +1,54 @@
1
- module PrettyMultitask
1
+ # frozen_string_literal: true
2
2
 
3
- LOGGER ||= Logger.new STDOUT
3
+ module PrettyMultitask
4
+ # This class will run multiple callables in parallel using RunCallable which add a nice format
4
5
  class Runner
6
+ LOGGER ||= Logger.new STDOUT
5
7
  def initialize(jobs)
6
8
  @jobs = jobs
7
9
  @jobs.each do |j|
8
- [:name, :cmd ].each {|o| raise "#{o} must be specified for job #{j}" unless j[o] }
10
+ %i[name cmd].each { |o| raise "#{o} must be specified for job #{j}" unless j[o] }
9
11
  j[:out_file] = "/tmp/#{j[:name]}-#{Time.now.strftime('%s.%N')}"
10
12
  FileUtils.touch j[:out_file]
11
13
  end
12
14
  end
13
-
15
+
14
16
  def run
15
17
  exec = Tlopo::Executor.new 10
16
-
17
- longest= 0
18
- @jobs.each {|job| longest = job[:name].length if job[:name].length > longest }
19
- @jobs.each {|job| job[:padding] = longest }
20
-
18
+
19
+ @jobs.each { |job| job[:padding] = longest_jobname }
21
20
  @jobs.each do |j|
22
21
  task = proc { j[:exit_status] = RunCallable.new(j).run }
23
22
  exec.schedule task
24
23
  end
25
24
  errors = exec.run.errors
26
-
25
+
26
+ print_out
27
+
28
+ unless errors.empty?
29
+ errors.each { |e| LOGGER.error e }
30
+ raise 'Found errors'
31
+ end
32
+ end
33
+
34
+ def print_out
27
35
  @jobs.each do |j|
28
36
  label = "[ #{j[:name]} ]"
29
37
  width = IO.console.winsize[-1]
30
- left = '='*((width - label.length)/2)
38
+ left = '=' * ((width - label.length) / 2)
31
39
  right = j[:name].length.even? ? left : left + '='
32
40
  puts "\n"
33
- puts Color.yellow left + label + right
41
+ puts Color.yellow left + label + right
34
42
  puts File.read j[:out_file]
35
- puts Color.yellow "="*width
43
+ puts Color.yellow '=' * width
36
44
  File.delete j[:out_file]
37
45
  end
38
-
39
- unless errors.empty?
40
- errors.each {|e| LOGGER.error e}
41
- raise 'Found errors'
42
- end
43
46
  end
44
-
45
- end
46
47
 
48
+ def longest_jobname
49
+ longest = 0
50
+ @jobs.each { |job| longest = job[:name].length if job[:name].length > longest }
51
+ longest
52
+ end
53
+ end
47
54
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrettyMultitask
4
+ module Trapper
5
+ module_function
6
+
7
+ PIDS ||= Set.new
8
+ def trap(pid)
9
+ PIDS << pid
10
+ %w[SIGINT SIGTERM SIGHUP].each do |sig|
11
+ Signal.trap(sig) do
12
+ begin
13
+ PIDS.each { |pid| Process.kill sig, pid }
14
+ rescue Errno::ESRCH
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrettyMultitask
2
- VERSION = "0.1.5"
4
+ VERSION = '0.1.10'
3
5
  end
@@ -1,38 +1,39 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "pretty_multitask/version"
5
+ require 'pretty_multitask/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "pretty_multitask"
8
+ spec.name = 'pretty_multitask'
8
9
  spec.version = PrettyMultitask::VERSION
9
- spec.authors = ["Tiago Lopo Da Silva"]
10
- spec.email = ["tiagolopo@yahoo.com.br"]
10
+ spec.authors = ['Tiago Lopo Da Silva']
11
+ spec.email = ['tiagolopo@yahoo.com.br']
11
12
 
12
- spec.summary = %q{Multitask with pretty output for Rake}
13
- spec.description = %q{Multitask with pretty output for Rake}
14
- spec.homepage = "https://github.com/tlopo-ruby/tlopo-executor"
15
- spec.license = "MIT"
13
+ spec.summary = 'Multitask with pretty output for Rake'
14
+ spec.description = 'Multitask with pretty output for Rake'
15
+ spec.homepage = 'https://github.com/tlopo-ruby/tlopo-executor'
16
+ spec.license = 'MIT'
16
17
 
17
18
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
19
  # to allow pushing to a single host or delete this section to allow pushing to any host.
19
20
  if spec.respond_to?(:metadata)
20
- #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
22
  else
22
- raise "RubyGems 2.0 or newer is required to protect against " \
23
- "public gem pushes."
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
24
25
  end
25
26
 
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
28
  f.match(%r{^(test|spec|features)/})
28
29
  end
29
- spec.bindir = "exe"
30
+ spec.bindir = 'exe'
30
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
- spec.require_paths = ["lib"]
32
+ spec.require_paths = ['lib']
32
33
 
33
- spec.add_dependency "tlopo-executor", "~> 0.1.0"
34
+ spec.add_dependency 'tlopo-executor', '~> 0.1.0'
34
35
 
35
- spec.add_development_dependency "bundler", "~> 1.15"
36
- spec.add_development_dependency "rake", "~> 10.0"
37
- spec.add_development_dependency "minitest", "~> 5.0"
36
+ spec.add_development_dependency 'bundler', '~> 1.15'
37
+ spec.add_development_dependency 'minitest', '~> 5.0'
38
+ spec.add_development_dependency 'rake', '~> 12.3.3'
38
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pretty_multitask
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Lopo Da Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-09 00:00:00.000000000 Z
11
+ date: 2020-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tlopo-executor
@@ -39,33 +39,33 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.15'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '5.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '5.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: minitest
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '5.0'
61
+ version: 12.3.3
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '5.0'
68
+ version: 12.3.3
69
69
  description: Multitask with pretty output for Rake
70
70
  email:
71
71
  - tiagolopo@yahoo.com.br
@@ -74,6 +74,7 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - ".gitignore"
77
+ - ".rubocop.yml"
77
78
  - ".travis.yml"
78
79
  - CODE_OF_CONDUCT.md
79
80
  - Gemfile
@@ -86,6 +87,7 @@ files:
86
87
  - lib/pretty_multitask/color.rb
87
88
  - lib/pretty_multitask/run_callable.rb
88
89
  - lib/pretty_multitask/runner.rb
90
+ - lib/pretty_multitask/trapper.rb
89
91
  - lib/pretty_multitask/version.rb
90
92
  - pretty_multitask.gemspec
91
93
  homepage: https://github.com/tlopo-ruby/tlopo-executor