pretty_multitask 0.1.7 → 0.1.8

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: a6d5c09df7c6d302bcb593e960369610d59fe42a0a30325138578e72d1cf8f9c
4
+ data.tar.gz: 57162253ef143f8865477141490d0f1839920bdd70d36a95154c3938d79b06d5
5
5
  SHA512:
6
- metadata.gz: 855889a02269799b938d12af331850b8fa1f3e19a9f2ef912f46c04690a26e1160761ee85195538b82e26c383fb1c7a95cd565bf8bc5510d83db056a5e830b9e
7
- data.tar.gz: 4991776fa6b3ab42143f4e627e3c33a519a9fe099f9cf8c51b51931160214fd8f044352dff9cd62421e781ae43c90ade0a7330fa484f4f3d218137ebe16df098
6
+ metadata.gz: 5c6c07f9165c2fa8add57d0647e64d936a915f54c4f09596f5a1160dcb46f1d537677124384d8f71021fb3f64cc0c85e255ffa8c63be7b25463ae83d3c60c4f9
7
+ data.tar.gz: 85526699eaa75ab56932d9de650cc2eb46991a1449ad3e3872bb0f04fc7ef2b725b5ae85d45980cbe51adb773e45d7416352771bd82cc005f24808f8177e82c2
data/.rubocop.yml ADDED
@@ -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
data/bin/console CHANGED
@@ -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,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,92 @@
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)
36
21
 
37
22
  t_out = consume_and_print slave, out, name, false
38
23
  t_err = consume_and_print err_read, out, name, true
39
24
 
40
25
  chars = []
26
+ t = consume_and_store r, chars
27
+
28
+ Process.wait pid
29
+
30
+ wait_until_streams_are_ready [slave, err_read]
31
+
32
+ join_threads [t_out, t_err, t]
33
+
34
+ close_streams [master, slave, err_read, err_write]
35
+
36
+ result = Marshal.load(chars.join)
37
+ raise result[:error] if result[:error]
38
+
39
+ result[:result]
40
+ end
41
+
42
+ def consume_and_store(reader, store)
41
43
  t = Thread.new do
42
- sleep 0.1 until r.ready?
43
- (1..3).each do
44
- chars << r.getc while r.ready?
44
+ sleep 0.1 until reader.ready?
45
+ 3.times do
46
+ store << reader.getc while reader.ready?
45
47
  end
46
48
  end
49
+ t
50
+ end
47
51
 
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? }
52
+ def wait_until_streams_are_ready(streams)
53
+ streams.each do |s|
54
+ loop { break unless s.ready? }
53
55
  end
54
-
55
- begin
56
- Timeout.timeout(0.1) do
57
- %i[t_out t_err].each { |e| binding.local_variable_get(e).join }
56
+ end
57
+
58
+ def join_threads(threads)
59
+ threads.each do |t|
60
+ begin
61
+ Timeout.timeout(0.1) { t.join }
62
+ rescue Timeout::Error
63
+ nil
58
64
  end
59
- rescue Timeout::Error
60
- nil
61
65
  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]
66
+ end
67
+
68
+ def close_streams(streams)
69
+ streams.each(&:close)
70
+ end
71
+
72
+ def run_on_fork(cmd, err_w, out_w, socket_w)
73
+ pid = fork do
74
+ STDERR.reopen err_w
75
+ STDOUT.reopen out_w
76
+ begin
77
+ result = cmd.call
78
+ obj = { result: result, error: nil }
79
+ rescue StandardError => e
80
+ new_error = e.class.new(e.message)
81
+ new_error.set_backtrace e.backtrace
82
+ Logger.new(STDERR).error new_error
83
+ obj = { result: nil, error: new_error }
84
+ socket_w.puts Marshal.dump(obj), 0
85
+ end
86
+ socket_w.puts Marshal.dump(obj), 0
87
+ socket_w.close
88
+ end
89
+ pid
70
90
  end
71
91
 
72
92
  def consume_and_print(reader, writer, name, error = false)
@@ -77,10 +97,12 @@ module PrettyMultitask
77
97
  colored = Color.green format(fmt, name) unless error
78
98
  reader.each_line do |line|
79
99
  writer.write "#{colored} #{line}"
80
- File.open(@opts[:out_file],'a+') do |f|
100
+ next unless @opts[:out_file]
101
+
102
+ File.open(@opts[:out_file], 'a+') do |f|
81
103
  f.puts "#{colored} #{line}" if error
82
104
  f.puts "#{colored} #{line}" unless error
83
- end if @opts[:out_file]
105
+ end
84
106
  end
85
107
  rescue Errno::EIO
86
108
  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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrettyMultitask
2
- VERSION = "0.1.7"
4
+ VERSION = '0.1.8'
3
5
  end
@@ -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'
@@ -9,6 +11,7 @@ require 'timeout'
9
11
  require 'yaml'
10
12
  require 'fileutils'
11
13
 
14
+ # Main module
12
15
  module PrettyMultitask
13
16
  require "#{__dir__}/pretty_multitask/color"
14
17
  require "#{__dir__}/pretty_multitask/runner"
@@ -16,15 +19,16 @@ module PrettyMultitask
16
19
  end
17
20
 
18
21
  def pretty_multitask(hash)
19
- name, tasks = hash.keys.first, hash.values.first
22
+ name = hash.keys.first
23
+ tasks = hash.values.first
20
24
  task name do
21
25
  jobs = []
22
26
  tasks.each do |t|
23
- job = Proc.new do
27
+ job = proc do
24
28
  Rake::Task[t].invoke
25
29
  nil
26
30
  end
27
- jobs.push({ name: t, cmd: job})
31
+ jobs.push({ name: t, cmd: job })
28
32
  end
29
33
  PrettyMultitask::Runner.new(jobs).run
30
34
  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,7 +1,7 @@
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.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Lopo Da Silva
@@ -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