eksek 0.1.0
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 +7 -0
- data/.gemspec +18 -0
- data/README.md +84 -0
- data/lib/eksek.rb +18 -0
- data/lib/eksek_error.rb +6 -0
- data/lib/eksek_result.rb +33 -0
- data/lib/eksekuter.rb +80 -0
- data/license.txt +15 -0
- metadata +96 -0
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
|
+
[](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
|
data/lib/eksek_error.rb
ADDED
data/lib/eksek_result.rb
ADDED
@@ -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: []
|