coque 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/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +135 -0
- data/Rakefile +10 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/coque.gemspec +39 -0
- data/drake.rb +113 -0
- data/io.rb +215 -0
- data/lib/coque/cmd.rb +35 -0
- data/lib/coque/context.rb +35 -0
- data/lib/coque/errors.rb +4 -0
- data/lib/coque/pipeline.rb +62 -0
- data/lib/coque/rb.rb +54 -0
- data/lib/coque/redirectable.rb +96 -0
- data/lib/coque/result.rb +24 -0
- data/lib/coque/sh.rb +45 -0
- data/lib/coque/version.rb +3 -0
- data/lib/coque.rb +27 -0
- data/script.py +129 -0
- metadata +153 -0
data/io.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
def banner(msg)
|
4
|
+
puts "******* #{msg} *******"
|
5
|
+
end
|
6
|
+
|
7
|
+
def read_from_spawned_pipe
|
8
|
+
banner("read_from_spawned_pipe")
|
9
|
+
pipe_me_in, pipe_peer_out = IO.pipe
|
10
|
+
pipe_peer_in, pipe_me_out = IO.pipe
|
11
|
+
|
12
|
+
pid = spawn('ls',
|
13
|
+
out: pipe_peer_out,
|
14
|
+
pipe_peer_out => pipe_peer_out,
|
15
|
+
in: pipe_peer_in,
|
16
|
+
pipe_peer_in => pipe_peer_in)
|
17
|
+
puts "Spawned #{pid}"
|
18
|
+
|
19
|
+
pipe_peer_out.close
|
20
|
+
puts pipe_me_in.read
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_write_to_spawned
|
24
|
+
banner("read_write_to_spawned")
|
25
|
+
pipe_me_in, pipe_peer_out = IO.pipe
|
26
|
+
pipe_peer_in, pipe_me_out = IO.pipe
|
27
|
+
|
28
|
+
['a', 'b', 'c', 'ab'].each { |l| pipe_me_out.puts(l) }
|
29
|
+
|
30
|
+
pid = spawn('grep a',
|
31
|
+
out: pipe_peer_out,
|
32
|
+
pipe_peer_out => pipe_peer_out,
|
33
|
+
in: pipe_peer_in,
|
34
|
+
pipe_peer_in => pipe_peer_in)
|
35
|
+
puts "Spawned #{pid}"
|
36
|
+
pipe_me_out.close
|
37
|
+
|
38
|
+
pipe_peer_out.close
|
39
|
+
puts pipe_me_in.read
|
40
|
+
end
|
41
|
+
|
42
|
+
def chain_two_native_processes
|
43
|
+
banner("chain_two_native_processes")
|
44
|
+
a_in_read, a_in_write = IO.pipe
|
45
|
+
a_out_read, a_out_write = IO.pipe
|
46
|
+
|
47
|
+
p1 = spawn('echo "a\nb\nc\nab\n"',
|
48
|
+
out: a_out_write,
|
49
|
+
a_out_write => a_out_write,
|
50
|
+
in: a_in_read,
|
51
|
+
a_in_read => a_in_read)
|
52
|
+
|
53
|
+
b_out_read, b_out_write = IO.pipe
|
54
|
+
|
55
|
+
p2 = spawn('grep a',
|
56
|
+
out: b_out_write,
|
57
|
+
b_out_write => b_out_write,
|
58
|
+
in: a_out_read,
|
59
|
+
a_out_read => a_out_read)
|
60
|
+
|
61
|
+
puts "Spawned a: #{p1}, b: #{p2}"
|
62
|
+
# Q: Why do we have to close these? do the spawned processes not close them?
|
63
|
+
b_out_write.close
|
64
|
+
a_out_write.close
|
65
|
+
puts b_out_read.read
|
66
|
+
end
|
67
|
+
|
68
|
+
def reading_large_output
|
69
|
+
banner("reading_large_output")
|
70
|
+
a_in_read, a_in_write = IO.pipe
|
71
|
+
a_out_read, a_out_write = IO.pipe
|
72
|
+
|
73
|
+
p1 = spawn('cat /usr/share/dict/words',
|
74
|
+
out: a_out_write,
|
75
|
+
a_out_write => a_out_write)
|
76
|
+
|
77
|
+
a_out_write.close
|
78
|
+
puts a_out_read.read
|
79
|
+
end
|
80
|
+
|
81
|
+
def pipe_to_forked_ruby
|
82
|
+
banner("pipe_to_forked_ruby")
|
83
|
+
a_in_read, a_in_write = IO.pipe
|
84
|
+
b_out_read, b_out_write = IO.pipe
|
85
|
+
|
86
|
+
['a', 'b', 'c', 'ab'].each { |l| a_in_write.puts(l) }
|
87
|
+
a_in_write.flush
|
88
|
+
a_in_write.close
|
89
|
+
|
90
|
+
child = fork do
|
91
|
+
# change our stdin to be the read end of the pipe
|
92
|
+
STDOUT.puts "forked process to stdout (#{STDOUT.fileno})"
|
93
|
+
b_out_write.puts "******* forked process to pipe (#{b_out_write.fileno}) **********"
|
94
|
+
|
95
|
+
a_in_read.each_line { |l| b_out_write.puts "child - #{l}" }
|
96
|
+
end
|
97
|
+
puts "forked #{child}"
|
98
|
+
|
99
|
+
a_in_write.close
|
100
|
+
b_out_write.close
|
101
|
+
|
102
|
+
b_out_read.each_line { |l| puts "read from parent - #{l}" }
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def native_process_to_ruby_block
|
107
|
+
banner("native_process_to_ruby_block")
|
108
|
+
# a_in_read, a_in_write = IO.pipe
|
109
|
+
a_out_read, a_out_write = IO.pipe
|
110
|
+
b_out_read, b_out_write = IO.pipe
|
111
|
+
puts "parent file descriptors"
|
112
|
+
puts "a read: #{a_out_read.fileno}"
|
113
|
+
puts "a write: #{a_out_write.fileno}"
|
114
|
+
puts "b read: #{b_out_read.fileno}"
|
115
|
+
puts "b write: #{b_out_write.fileno}"
|
116
|
+
|
117
|
+
# Receives copy of a_out_write; closes when done
|
118
|
+
p1 = spawn('echo "a\nb\nc\nab"', out: a_out_write)
|
119
|
+
|
120
|
+
# Receives copy of
|
121
|
+
# a_out_read - needs to read; closed automatically?
|
122
|
+
# a_out_write - doesn't need, close immediately
|
123
|
+
# b_out_read - doesn't need, close immediately
|
124
|
+
# b_out_write - needs to write; close when done
|
125
|
+
child = fork do
|
126
|
+
puts "child file descriptors:"
|
127
|
+
puts "a read: #{a_out_read.fileno}"
|
128
|
+
puts "a write: #{a_out_write.fileno}"
|
129
|
+
puts "b read: #{b_out_read.fileno}"
|
130
|
+
puts "b write: #{b_out_write.fileno}"
|
131
|
+
a_out_write.close
|
132
|
+
b_out_read.close
|
133
|
+
|
134
|
+
puts "******* IN FORK **********"
|
135
|
+
|
136
|
+
# while l = a_out_read.gets
|
137
|
+
# puts "working #{l}"
|
138
|
+
# b_out_write.puts "child - #{l}"
|
139
|
+
# end
|
140
|
+
|
141
|
+
a_out_read.each_line { |l| puts "working #{l}"; b_out_write.puts "child - #{l}" }
|
142
|
+
puts "child done - close writer"
|
143
|
+
a_out_read.close
|
144
|
+
b_out_write.close
|
145
|
+
puts "Fork work done"
|
146
|
+
end
|
147
|
+
|
148
|
+
puts "done forked"
|
149
|
+
|
150
|
+
a_out_write.close
|
151
|
+
b_out_write.close
|
152
|
+
a_out_read.close
|
153
|
+
|
154
|
+
puts "** Display child output:"
|
155
|
+
b_out_read.each_line { |l| puts "read from parent - #{l}" }
|
156
|
+
b_out_read.close
|
157
|
+
puts "done reading"
|
158
|
+
end
|
159
|
+
|
160
|
+
# read_from_spawned_pipe
|
161
|
+
# read_write_to_spawned
|
162
|
+
# pipe_to_forked_ruby
|
163
|
+
# reading_large_output
|
164
|
+
# chain_two_native_processes
|
165
|
+
# native_process_to_ruby_block
|
166
|
+
|
167
|
+
def run_fork(stdin, stdout, &block)
|
168
|
+
fork do
|
169
|
+
STDOUT.reopen(stdout)
|
170
|
+
stdin.each_line(&block)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def three_step
|
175
|
+
writers = []
|
176
|
+
banner("three step")
|
177
|
+
a_in_read, a_in_write = IO.pipe
|
178
|
+
a_out_read, a_out_write = IO.pipe
|
179
|
+
|
180
|
+
a_unused = [a_out_write]
|
181
|
+
|
182
|
+
p1 = spawn('echo "a\nb\nc\nab\n"',
|
183
|
+
out: a_out_write,
|
184
|
+
a_out_write => a_out_write,
|
185
|
+
in: a_in_read,
|
186
|
+
a_in_read => a_in_read)
|
187
|
+
|
188
|
+
b_out_read, b_out_write = IO.pipe
|
189
|
+
b_unused = [b_out_write]
|
190
|
+
|
191
|
+
a_unused.each(&:close)
|
192
|
+
run_fork(a_out_read, b_out_write) { |l| puts "~~ - #{l}" }
|
193
|
+
|
194
|
+
c_out_read, c_out_write = IO.pipe
|
195
|
+
c_unused = [c_out_write]
|
196
|
+
|
197
|
+
p2 = spawn('grep a',
|
198
|
+
out: c_out_write,
|
199
|
+
c_out_write => c_out_write,
|
200
|
+
in: b_out_read,
|
201
|
+
b_out_read => b_out_read)
|
202
|
+
b_unused.each(&:close)
|
203
|
+
|
204
|
+
# Q: Why do we have to close these? do the spawned processes not close them?
|
205
|
+
c_unused.each(&:close)
|
206
|
+
puts c_out_read.read
|
207
|
+
end
|
208
|
+
|
209
|
+
# three_step
|
210
|
+
|
211
|
+
def stdin_redirect
|
212
|
+
stdin = File.open("/usr/share/dict/words", "r")
|
213
|
+
spawn("head", in: stdin, stdin => stdin)
|
214
|
+
end
|
215
|
+
stdin_redirect
|
data/lib/coque/cmd.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Coque
|
2
|
+
class Cmd
|
3
|
+
include Redirectable
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def |(other)
|
7
|
+
verify_redirectable(other)
|
8
|
+
case other
|
9
|
+
when Cmd
|
10
|
+
Pipeline.new([self, other])
|
11
|
+
when Pipeline
|
12
|
+
Pipeline.new([self] + other.commands)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def clone
|
17
|
+
raise "Not Implemented - Override"
|
18
|
+
end
|
19
|
+
|
20
|
+
def ensure_default_fds
|
21
|
+
if self.stdin.nil?
|
22
|
+
inr, inw = IO.pipe
|
23
|
+
inw.close
|
24
|
+
self.stdin = inr
|
25
|
+
end
|
26
|
+
|
27
|
+
if self.stdout.nil?
|
28
|
+
outr, outw = IO.pipe
|
29
|
+
self.stdout = outw
|
30
|
+
# only used for Result if this is the last command in a pipe
|
31
|
+
@stdout_read = outr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Coque
|
2
|
+
class Context
|
3
|
+
attr_reader :dir, :env
|
4
|
+
def initialize(dir = Dir.pwd, env = {}, disinherits_env = false)
|
5
|
+
@dir = dir
|
6
|
+
@env = env
|
7
|
+
@disinherits_env = disinherits_env
|
8
|
+
end
|
9
|
+
|
10
|
+
def disinherits_env?
|
11
|
+
@disinherits_env
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](*args)
|
15
|
+
Sh.new(self, args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def rb(&block)
|
19
|
+
Rb.new(self, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def chdir(new_dir)
|
23
|
+
Context.new(new_dir, env, disinherits_env?)
|
24
|
+
end
|
25
|
+
|
26
|
+
def setenv(opts)
|
27
|
+
opts = opts.map { |k,v| [k.to_s, v.to_s] }.to_h
|
28
|
+
Context.new(dir, self.env.merge(opts), disinherits_env?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def disinherit_env
|
32
|
+
Context.new(dir, {}, true)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/coque/errors.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Coque
|
2
|
+
class Pipeline
|
3
|
+
include Redirectable
|
4
|
+
|
5
|
+
attr_reader :commands
|
6
|
+
def initialize(commands = [])
|
7
|
+
@commands = commands
|
8
|
+
end
|
9
|
+
|
10
|
+
def clone
|
11
|
+
self.class.new(commands)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"<Pipeline #{commands.join(" | ")} >"
|
16
|
+
end
|
17
|
+
|
18
|
+
def |(other)
|
19
|
+
verify_redirectable(other)
|
20
|
+
case other
|
21
|
+
when Pipeline
|
22
|
+
Pipeline.new(commands + other.commands)
|
23
|
+
when Cmd
|
24
|
+
Pipeline.new(commands + [other])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def stitch
|
29
|
+
# Set head in
|
30
|
+
if commands.first.stdin.nil?
|
31
|
+
start_r, start_w = IO.pipe
|
32
|
+
start_w.close
|
33
|
+
commands.first.stdin = start_r
|
34
|
+
end
|
35
|
+
|
36
|
+
# Connect intermediate in/outs
|
37
|
+
commands.each_cons(2) do |left, right|
|
38
|
+
read, write = IO.pipe
|
39
|
+
left.stdout = write
|
40
|
+
right.stdin = read
|
41
|
+
end
|
42
|
+
|
43
|
+
# Set tail out
|
44
|
+
if self.stdout
|
45
|
+
commands.last.stdout = stdout
|
46
|
+
stdout
|
47
|
+
elsif commands.last.stdout
|
48
|
+
commands.last.stdout
|
49
|
+
else
|
50
|
+
next_r, next_w = IO.pipe
|
51
|
+
commands.last.stdout = next_w
|
52
|
+
next_r
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def run
|
57
|
+
stdout = stitch
|
58
|
+
results = commands.map(&:run)
|
59
|
+
Result.new(results.last.pid, stdout)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/coque/rb.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Coque
|
2
|
+
class Rb < Cmd
|
3
|
+
NOOP = Proc.new { }
|
4
|
+
attr_reader :block, :pre_block, :post_block
|
5
|
+
def initialize(context = Context.new, &block)
|
6
|
+
if block_given?
|
7
|
+
@block = block
|
8
|
+
else
|
9
|
+
@block = NOOP
|
10
|
+
end
|
11
|
+
@pre_block = nil
|
12
|
+
@post_block = nil
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
def clone
|
17
|
+
self.class.new(context, &block).pre(&pre_block).post(&post_block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def pre(&block)
|
21
|
+
if block_given?
|
22
|
+
@pre_block = block
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def post(&block)
|
28
|
+
if block_given?
|
29
|
+
@post_block = block
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
ensure_default_fds
|
36
|
+
|
37
|
+
pid = fork do
|
38
|
+
STDOUT.reopen(stdout)
|
39
|
+
Dir.chdir(context.dir)
|
40
|
+
if context.disinherits_env?
|
41
|
+
ENV.clear
|
42
|
+
end
|
43
|
+
context.env.each do |k,v|
|
44
|
+
ENV[k] = v
|
45
|
+
end
|
46
|
+
@pre_block.call if @pre_block
|
47
|
+
stdin.each_line(&@block)
|
48
|
+
@post_block.call if @post_block
|
49
|
+
end
|
50
|
+
stdout.close
|
51
|
+
Result.new(pid, stdout_read)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Coque
|
4
|
+
module Redirectable
|
5
|
+
attr_reader :stdin, :stdout, :stderr
|
6
|
+
|
7
|
+
def >(io)
|
8
|
+
clone.tap do |c|
|
9
|
+
c.stdout = io
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def <(io)
|
14
|
+
clone.tap do |c|
|
15
|
+
c.stdin = io
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def >=(io)
|
20
|
+
clone.tap do |c|
|
21
|
+
c.stderr = io
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def getio(io, mode = "r")
|
26
|
+
case io
|
27
|
+
when String
|
28
|
+
File.open(io, mode)
|
29
|
+
when Pathname
|
30
|
+
File.open(io, mode)
|
31
|
+
when IO
|
32
|
+
io
|
33
|
+
when Tempfile
|
34
|
+
io
|
35
|
+
else
|
36
|
+
raise ArgumentError.new("Can't redirect stream to #{io}, must be String, Pathname, or IO")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def stdin_redirected?
|
41
|
+
defined? @stdin
|
42
|
+
end
|
43
|
+
|
44
|
+
def stdout_redirected?
|
45
|
+
defined? @stdout
|
46
|
+
end
|
47
|
+
|
48
|
+
def stderr_redirected?
|
49
|
+
defined? @stderr
|
50
|
+
end
|
51
|
+
|
52
|
+
def stderr=(s)
|
53
|
+
if stderr_redirected?
|
54
|
+
raise RedirectionError.new("Can't set stderr of #{self} to #{s}, is already set to #{stderr}")
|
55
|
+
else
|
56
|
+
@stderr = getio(s, "w")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def stdout=(s)
|
61
|
+
if stdout_redirected?
|
62
|
+
raise RedirectionError.new("Can't set stdout of #{self} to #{s}, is already set to #{stdout}")
|
63
|
+
else
|
64
|
+
@stdout = getio(s, "w")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def stdin=(s)
|
69
|
+
if stdin_redirected?
|
70
|
+
raise RedirectionError.new("Can't set stdin of #{self} to #{s}, is already set to #{stdin}")
|
71
|
+
else
|
72
|
+
@stdin = getio(s, "r")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def verify_redirectable(other)
|
77
|
+
if self.stdout_redirected?
|
78
|
+
raise RedirectionError.new("Can't pipe #{self} into #{other} -- #{self}'s STDIN is already redirected")
|
79
|
+
end
|
80
|
+
|
81
|
+
if other.stdin_redirected?
|
82
|
+
raise RedirectionError.new("Can't pipe #{self} into #{other} -- #{other}'s STDIN is already redirected")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def stdout_read
|
89
|
+
if defined? @stdout_read
|
90
|
+
@stdout_read
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/coque/result.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class Coque::Result
|
2
|
+
attr_reader :pid, :exit_code
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(pid, out)
|
6
|
+
@pid = pid
|
7
|
+
@out = out
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
@out.each_line do |line|
|
12
|
+
block.call(line.chomp)
|
13
|
+
end
|
14
|
+
unless defined? @exit_code
|
15
|
+
wait
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def wait
|
20
|
+
_, status = Process.waitpid2(pid)
|
21
|
+
@exit_code = status.exitstatus
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
data/lib/coque/sh.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Coque
|
2
|
+
class Sh < Cmd
|
3
|
+
attr_reader :args, :context
|
4
|
+
def initialize(context, args)
|
5
|
+
@context = context
|
6
|
+
@args = args
|
7
|
+
end
|
8
|
+
|
9
|
+
def clone
|
10
|
+
self.class.new(context, args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"<Coque::Sh #{args.inspect}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](*new_args)
|
22
|
+
self.class.new(self.context, self.args + new_args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
ensure_default_fds
|
27
|
+
opts = {in: stdin, stdin.fileno => stdin.fileno,
|
28
|
+
out: stdout, stdout.fileno => stdout.fileno,
|
29
|
+
chdir: context.dir, unsetenv_others: context.disinherits_env?}
|
30
|
+
|
31
|
+
# Redirect err to out: (e.g. for 2>&1)
|
32
|
+
# {err: [:child, :out]}
|
33
|
+
err_opts = if stderr
|
34
|
+
{err: stderr, stderr.fileno => stderr.fileno}
|
35
|
+
else
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
39
|
+
pid = spawn(context.env, args.join(" "), opts.merge(err_opts))
|
40
|
+
|
41
|
+
stdout.close
|
42
|
+
Result.new(pid, stdout_read)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/coque.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "coque/redirectable"
|
2
|
+
require "coque/cmd"
|
3
|
+
require "coque/sh"
|
4
|
+
require "coque/rb"
|
5
|
+
require "coque/context"
|
6
|
+
require "coque/errors"
|
7
|
+
require "coque/pipeline"
|
8
|
+
require "coque/result"
|
9
|
+
require "coque/version"
|
10
|
+
|
11
|
+
module Coque
|
12
|
+
def self.context(dir: Dir.pwd, env: {}, disinherits_env: false)
|
13
|
+
Context.new(dir, env, disinherits_env)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.[](*args)
|
17
|
+
Context.new[*args]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.rb(&block)
|
21
|
+
Rb.new(Context.new, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.pipeline(*commands)
|
25
|
+
commands.reduce(:|)
|
26
|
+
end
|
27
|
+
end
|