pretty_multitask 0.1.7 → 0.1.12

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: 855e2f7b1a3ddf9492a15002917bad4a6b72fd0bbec804e40adc12d9e9736730
4
- data.tar.gz: fb5d4f8224dd483d1dc6730fd4f9be2145aee5b3f0a560790524fa53a4a0882d
3
+ metadata.gz: f17834bf6e69de1ac60b9c3ef85057af450b3f2670c7436e95ce586bfa85abd5
4
+ data.tar.gz: f7489e37732d411b20f10b541ad8f0a8df6d989a3b6e9889f7bed027e649ffe4
5
5
  SHA512:
6
- metadata.gz: 855889a02269799b938d12af331850b8fa1f3e19a9f2ef912f46c04690a26e1160761ee85195538b82e26c383fb1c7a95cd565bf8bc5510d83db056a5e830b9e
7
- data.tar.gz: 4991776fa6b3ab42143f4e627e3c33a519a9fe099f9cf8c51b51931160214fd8f044352dff9cd62421e781ae43c90ade0a7330fa484f4f3d218137ebe16df098
6
+ metadata.gz: 79848302a5152c65f2262dfb30b045dd090270a8890caba3ef277df3924ebadd1a9b18babe82ac508dd61e1b5232b6ad9eac9e2f474f6f6b5fe0c95f9bb29f43
7
+ data.tar.gz: b3d559a22457161fb6b1d52dec37d3faeb5120eefb37df0e279582779e91b6ecd64132cb16e8f2f55d18c80511255d7fee633fd6ee6df211c7b9267794a39ca0
@@ -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,72 +1,93 @@
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
- pid = fork do
20
- STDERR.reopen err_write
21
- STDOUT.reopen master
22
- begin
23
- result = cmd.call
24
- obj = { result: result, error: nil }
25
- rescue StandardError => e
26
- new_error = e.class.new(e.message)
27
- new_error.set_backtrace e.backtrace
28
- Logger.new(STDERR).error new_error
29
- 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
19
+
20
+ pid = run_on_fork(cmd, err_write, master, w)
21
+ Trapper.trap pid
36
22
 
37
23
  t_out = consume_and_print slave, out, name, false
38
24
  t_err = consume_and_print err_read, out, name, true
39
25
 
40
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)
41
44
  t = Thread.new do
42
- sleep 0.1 until r.ready?
43
- (1..3).each do
44
- chars << r.getc while r.ready?
45
+ sleep 0.1 until reader.ready?
46
+ 3.times do
47
+ store << reader.getc while reader.ready?
45
48
  end
46
49
  end
50
+ t
51
+ end
47
52
 
48
- Process.wait pid
49
- Timeout.timeout(1) { t.join }
50
-
51
- %i[slave err_read].each do |e|
52
- loop { break unless binding.local_variable_get(e).ready? }
53
+ def wait_until_streams_are_ready(streams)
54
+ streams.each do |s|
55
+ loop { break unless s.ready? }
53
56
  end
54
-
55
- begin
56
- Timeout.timeout(0.1) do
57
- %i[t_out t_err].each { |e| binding.local_variable_get(e).join }
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
58
65
  end
59
- rescue Timeout::Error
60
- nil
61
66
  end
62
-
63
- %i[master slave err_read err_write].each { |e| binding.local_variable_get(e).close }
64
-
65
-
66
- result = Marshal.load(chars.join)
67
- raise result[:error] if result[:error]
68
-
69
- result[:result]
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)
74
+ pid = fork do
75
+ STDERR.reopen err_w
76
+ STDOUT.reopen out_w
77
+ begin
78
+ result = cmd.call
79
+ obj = { result: result, error: nil }
80
+ rescue StandardError => e
81
+ new_error = e.class.new(e.message)
82
+ new_error.set_backtrace e.backtrace
83
+ Logger.new(STDERR).error new_error
84
+ obj = { result: nil, error: new_error }
85
+ socket_w.puts Marshal.dump(obj), 0
86
+ end
87
+ socket_w.puts Marshal.dump(obj), 0
88
+ socket_w.close
89
+ end
90
+ pid
70
91
  end
71
92
 
72
93
  def consume_and_print(reader, writer, name, error = false)
@@ -77,10 +98,12 @@ module PrettyMultitask
77
98
  colored = Color.green format(fmt, name) unless error
78
99
  reader.each_line do |line|
79
100
  writer.write "#{colored} #{line}"
80
- File.open(@opts[:out_file],'a+') do |f|
101
+ next unless @opts[:out_file]
102
+
103
+ File.open(@opts[:out_file], 'a+') do |f|
81
104
  f.puts "#{colored} #{line}" if error
82
105
  f.puts "#{colored} #{line}" unless error
83
- end if @opts[:out_file]
106
+ end
84
107
  end
85
108
  rescue Errno::EIO
86
109
  nil
@@ -1,47 +1,55 @@
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
+ width = 80 unless width > 0
39
+ left = '=' * ((width - label.length) / 2)
31
40
  right = j[:name].length.even? ? left : left + '='
32
41
  puts "\n"
33
- puts Color.yellow left + label + right
42
+ puts Color.yellow left + label + right
34
43
  puts File.read j[:out_file]
35
- puts Color.yellow "="*width
44
+ puts Color.yellow '=' * width
36
45
  File.delete j[:out_file]
37
46
  end
38
-
39
- unless errors.empty?
40
- errors.each {|e| LOGGER.error e}
41
- raise 'Found errors'
42
- end
43
47
  end
44
-
45
- end
46
48
 
49
+ def longest_jobname
50
+ longest = 0
51
+ @jobs.each { |job| longest = job[:name].length if job[:name].length > longest }
52
+ longest
53
+ end
54
+ end
47
55
  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.7"
4
+ VERSION = '0.1.12'
3
5
  end
@@ -1,38 +1,40 @@
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'
35
+ spec.add_dependency 'io-console', '~> 0.4'
34
36
 
35
- spec.add_development_dependency "bundler", "~> 1.15"
36
- spec.add_development_dependency "rake", "~> 10.0"
37
- spec.add_development_dependency "minitest", "~> 5.0"
37
+ spec.add_development_dependency 'bundler', '~> 2.1'
38
+ spec.add_development_dependency 'minitest', '~> 5.0'
39
+ spec.add_development_dependency 'rake', '~> 12.3.3'
38
40
  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.7
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Lopo Da Silva
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-09 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tlopo-executor
@@ -25,33 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: io-console
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.15'
34
- type: :development
33
+ version: '0.4'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.15'
40
+ version: '0.4'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '2.1'
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: '2.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 12.3.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 12.3.3
69
83
  description: Multitask with pretty output for Rake
70
84
  email:
71
85
  - tiagolopo@yahoo.com.br
@@ -74,6 +88,7 @@ extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
90
  - ".gitignore"
91
+ - ".rubocop.yml"
77
92
  - ".travis.yml"
78
93
  - CODE_OF_CONDUCT.md
79
94
  - Gemfile
@@ -86,13 +101,14 @@ files:
86
101
  - lib/pretty_multitask/color.rb
87
102
  - lib/pretty_multitask/run_callable.rb
88
103
  - lib/pretty_multitask/runner.rb
104
+ - lib/pretty_multitask/trapper.rb
89
105
  - lib/pretty_multitask/version.rb
90
106
  - pretty_multitask.gemspec
91
107
  homepage: https://github.com/tlopo-ruby/tlopo-executor
92
108
  licenses:
93
109
  - MIT
94
110
  metadata: {}
95
- post_install_message:
111
+ post_install_message:
96
112
  rdoc_options: []
97
113
  require_paths:
98
114
  - lib
@@ -107,8 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
123
  - !ruby/object:Gem::Version
108
124
  version: '0'
109
125
  requirements: []
110
- rubygems_version: 3.0.3
111
- signing_key:
126
+ rubygems_version: 3.1.2
127
+ signing_key:
112
128
  specification_version: 4
113
129
  summary: Multitask with pretty output for Rake
114
130
  test_files: []