eksek 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 38c99ca643c947821f164f389981a9958424ede39ba1aeea5ba956265291d0ff
4
+ data.tar.gz: bc09955bf615924204cad88a4a7b840826633184b6830207cb1193e71f132346
5
+ SHA512:
6
+ metadata.gz: a91f53242d38998a7b2ab739050379b07108697c488f9dc69120e8538c6ee1299a66cc07ea203bca54aa76341d8c817f107af365cbaa669f6c365704ca780757
7
+ data.tar.gz: 2918aa0f4ecea9affba5a6d531a7fa7cfd89ae2186ae30f033efda40e9480a91157afe4d20879591e7e8013a92a19adea79284dcc8cbc966737d6b6624080f7f
data/.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'eksek'
3
+ s.version = '0.1.0'
4
+ s.authors = ['Johnny Lee-Othon', 'Thomas Bretzke']
5
+ s.homepage = 'https://github.com/taccon/eksek'
6
+ s.summary = 'A better backticks'
7
+ s.description = <<~END
8
+ Execute shell commands and easily get stdout, stderr, exit code, and more
9
+ END
10
+ s.license = 'ISC'
11
+
12
+ s.files = Dir['.gemspec', 'lib/**/*.rb', 'license.txt', 'readme.md']
13
+
14
+ s.required_ruby_version = '~> 2.3'
15
+ s.add_development_dependency 'rspec','~> 3'
16
+ s.add_development_dependency 'rubocop', '~> 0'
17
+ s.add_development_dependency 'bundler', '~> 1.0'
18
+ end
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # eksek
2
+
3
+ [![Travis](https://img.shields.io/travis/jleeothon/eksek.svg)](https://travis-ci.org/jleeothon/eksek)
4
+
5
+ `eksek` is a library to run shell commands synchronously and obtain any of the standard output, standard error, and exit code with flexibility.
6
+
7
+ ## Features
8
+
9
+ ### Basic usage
10
+
11
+ Use the `eksek` to execute a command:
12
+
13
+ ```ruby
14
+ eksek 'echo Hello'
15
+ ```
16
+
17
+ This returns an `EksekResult` object providing the following methods:
18
+
19
+ - `exit` returns the exit code.
20
+ - `stdout` returns the standard output as a string.
21
+ - `stderr` returns the standard error as a string.
22
+ - `success?` returns `true` or `false` depending of the exit code (`0` for `true`).
23
+ - `success!` throws an exception if the command exited with a non-0 code.
24
+
25
+ The `success!` method can be chained with any other of the above ones and it is wrapped in the convenience method `eksek!` to have a "fail or return" like so:
26
+
27
+ ```ruby
28
+ puts eksek!('echo Hello').stdout # Hello
29
+
30
+ # The above is essentially the same as:
31
+
32
+ puts eksek('echo Hello').success!.stdout
33
+ ```
34
+
35
+ ### Writing to stdin
36
+
37
+ To write into the standard input a block can be used:
38
+
39
+ ```ruby
40
+ r = eksek('read A; echo $A') { |stdin| stdin.write "Hello" }
41
+ r.success!
42
+ ```
43
+
44
+ If the block returns a `String` or `IO`, it will be written into the stdio.
45
+
46
+ ```ruby
47
+ r = eksek('read A; echo $A') { "Hello" }
48
+ r.success!
49
+
50
+ r = eksek('read A; echo $A') { File.open('myfile.txt') }
51
+ r.success!
52
+ ```
53
+
54
+ ### Passing other options
55
+
56
+ `eksek` has the same signature as `Process#spawn`. This means that:
57
+
58
+ - the first parameter can optionally be a hash, passed as the process environment;
59
+ - the command can be passed as a single string or as variable-length arguments;
60
+ - other options can be passed as hash arguments.
61
+
62
+ Additionally, the environment will have its keys stringified, so that symbols can be used too. For example:
63
+
64
+ ```ruby
65
+ r = eksek { A: 'Hello' }, 'echo $A'
66
+ puts r.stdout # Hello
67
+
68
+ r = eksek 'echo', 'Hello'
69
+ puts r.stdout # Hello
70
+
71
+ r = eksek 'echo $PWD', chdir: '/tmp'
72
+ puts r.stdout # /tmp
73
+ ```
74
+
75
+ ### Further information
76
+
77
+ In case you prefer an object oriented method, you can also use the `Eksekuter` class that is used by the `eksek` method directly. The following examples are basically the same:
78
+
79
+ ```ruby
80
+ # With eksek
81
+ eksek 'echo Hello' { 'This goes to stdin.' }
82
+ # With Eksekuter
83
+ Eksekuter.new('echo Hello').run { 'This goes to stdin.' }
84
+ ```
data/lib/eksek.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'eksekuter'
4
+
5
+ # Executes shell commands and can be specified to return the standard output,
6
+ # standard error, etc.
7
+ # Accepts the same options as Process.spawn e.g. :chdir, :env.
8
+ module Kernel
9
+ private
10
+
11
+ def eksek *args, **opts, &block
12
+ Eksekuter.new(*args, **opts).run(&block)
13
+ end
14
+
15
+ def eksek! *args, **opts, &block
16
+ eksek(*args, **opts, &block).success!
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Represents an error in the executio of a system call when success! was
4
+ # required.
5
+ class EksekError < StandardError
6
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'eksek_error'
4
+
5
+ # Describes a result object to be used for evaluating
6
+ # return values from a command
7
+ class EksekResult
8
+ # rubocop:disable Metrics/ParameterLists
9
+ def initialize env, cmd, exit_code, success, stdout, stderr
10
+ @env = env
11
+ @cmd = cmd
12
+ @exit_code = exit_code
13
+ @success = success
14
+ @stdout = stdout
15
+ @stderr = stderr
16
+ end
17
+ # rubocop:enable Metrics/ParameterLists
18
+
19
+ attr_reader :exit_code, :stdout, :stderr
20
+
21
+ def success?
22
+ @success
23
+ end
24
+
25
+ def success!
26
+ raise EksekError, "Command failed: #{@cmd.inspect}" unless success?
27
+ self
28
+ end
29
+
30
+ def to_s
31
+ stdout
32
+ end
33
+ end
data/lib/eksekuter.rb ADDED
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ require_relative 'eksek_result'
6
+
7
+ # Class that command execution is delegated to by Eksek.
8
+ class Eksekuter
9
+ def initialize *args, **opts
10
+ @env = args[0].is_a?(Hash) ? args.shift : {}
11
+ @env = @env.each_with_object({}) { |(k, v), o| o[k.to_s] = v }
12
+ @cmd = args.size == 1 ? args.first : args
13
+ @opts = opts
14
+ end
15
+
16
+ def run &block
17
+ spawn_process
18
+ write_and_close_stdin(&block)
19
+ wait
20
+ read_and_close_stdout_stderr
21
+ assemble_result
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader(
27
+ :cmd,
28
+ :env,
29
+ :err_str,
30
+ :opts,
31
+ :out_str,
32
+ :process_status,
33
+ :stdin,
34
+ :wait_thr,
35
+ )
36
+
37
+ def stdout
38
+ @stdout ||= StringIO.new('', 'r')
39
+ end
40
+
41
+ def stderr
42
+ @stderr ||= StringIO.new('', 'r')
43
+ end
44
+
45
+ def spawn_process
46
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(env, *cmd, opts)
47
+ nil
48
+ end
49
+
50
+ def write_and_close_stdin
51
+ return if stdin.closed? || !block_given?
52
+ block_result = yield stdin
53
+ block_result = StringIO.new(block_result) if block_result.is_a? String
54
+ IO.copy_stream(block_result, stdin) if block_result.respond_to? :read
55
+ stdin.close
56
+ nil
57
+ end
58
+
59
+ def wait
60
+ @process_status = wait_thr.value
61
+ end
62
+
63
+ def read_and_close_stdout_stderr
64
+ streams = [stdout, stderr]
65
+ @out_str, @err_str = streams.map(&:read).map(&:chomp)
66
+ streams.each(&:close)
67
+ nil
68
+ end
69
+
70
+ def assemble_result
71
+ EksekResult.new(
72
+ env,
73
+ cmd,
74
+ process_status.exitstatus,
75
+ process_status.success?,
76
+ out_str,
77
+ err_str,
78
+ )
79
+ end
80
+ end
data/license.txt ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2018, Johnny Enrique Lee-Othon
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eksek
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Johnny Lee-Othon
8
+ - Thomas Bretzke
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-04-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rubocop
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.0'
56
+ description: 'Execute shell commands and easily get stdout, stderr, exit code, and
57
+ more
58
+
59
+ '
60
+ email:
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gemspec"
66
+ - README.md
67
+ - lib/eksek.rb
68
+ - lib/eksek_error.rb
69
+ - lib/eksek_result.rb
70
+ - lib/eksekuter.rb
71
+ - license.txt
72
+ homepage: https://github.com/taccon/eksek
73
+ licenses:
74
+ - ISC
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '2.3'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.7.6
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: A better backticks
96
+ test_files: []