coque 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 133e5322dad9101c4294013b52cabba3d1089565
4
- data.tar.gz: e6bba14d02cad53d6d2129d69a9b318c4450d4e5
3
+ metadata.gz: 7f24fa9416a64dc742a217e43ef53cbb6d6fc3b0
4
+ data.tar.gz: 0bc4970fa99aeac4190fd30c4752a40a890ba96e
5
5
  SHA512:
6
- metadata.gz: de7106b725db062ef1b61976d5107c38cceaa61cd2ffbec64b58819753504a1e77c64628c02ae520f49c600b6b1ff4a9f469ab78ca7f79fa2ecdb2727cdab3be
7
- data.tar.gz: 1c965fd41cac24672ff20a63e5afe726f68727e342f257483e328520e47a3b0e3629b2765170e93a39856967ad7b64e4575ff94b2c19a093146d573768ce5f85
6
+ metadata.gz: e0029d5784e6afc64595e38461fbb44c7003d0782fe9bc78864c57289dda090a1e943dac0d134e26870f041274aad223563916bb0825cc3a8dfcee20e1c38fa6
7
+ data.tar.gz: 2c6f2e2d665b75341081ac6d1cf7a924a41dcab2088da3d3c2d95096c8bfcda37a44bc3be4388fe588cd341a87f062a13147de506e8e1ff67900faea18d9a7fe
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- coque (0.5.0)
4
+ coque (0.6.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -33,7 +33,7 @@ PLATFORMS
33
33
  ruby
34
34
 
35
35
  DEPENDENCIES
36
- bundler (~> 1.16)
36
+ bundler (~> 2.0)
37
37
  coque!
38
38
  minitest (~> 5.11)
39
39
  minitest-reporters
@@ -42,4 +42,4 @@ DEPENDENCIES
42
42
  simplecov
43
43
 
44
44
  BUNDLED WITH
45
- 1.16.2
45
+ 2.0.1
data/README.md CHANGED
@@ -99,6 +99,43 @@ Coque also includes a `Coque.source` helper for feeding Ruby enumerables into sh
99
99
  # => ["500"]
100
100
  ```
101
101
 
102
+ #### Logging
103
+
104
+ You can set a logger for Coque, which will be used to output messages when commands are executed:
105
+
106
+ ```rb
107
+ Coque.logger = Logger.new(STDOUT)
108
+ (Coque["echo", "hi"] | Coque["wc", "-c"]).run!
109
+ ```
110
+
111
+ #### Named (Non-Operator) Method Alternatives
112
+
113
+ The main piping and redirection methods also include named alternatives:
114
+
115
+ * `|` is aliased to `pipe`
116
+ * `>` is aliased to `out`
117
+ * `>=` is aliased to `err`
118
+ * `<` is aliased to `in`
119
+
120
+ So these 2 invocations are equivalent:
121
+
122
+ ```rb
123
+ (Coque["echo", "hi"] | Coque["wc", "-c"] > STDERR).run!
124
+ # is the same as...
125
+ Coque["echo", "hi"].pipe(Coque["wc", "-c"]).out(STDERR).run!
126
+ ```
127
+
128
+ Will log:
129
+
130
+ ```
131
+ I, [2019-02-20T20:31:00.325777 #16749] INFO -- : Executing Coque Command: <Pipeline <Coque::Sh echo hi> | <Coque::Sh wc -c> >
132
+ I, [2019-02-20T20:31:00.325971 #16749] INFO -- : Executing Coque Command: <Coque::Sh echo hi>
133
+ I, [2019-02-20T20:31:00.327719 #16749] INFO -- : Coque Command: <Coque::Sh echo hi> finished in 0.001683 seconds.
134
+ I, [2019-02-20T20:31:00.327771 #16749] INFO -- : Executing Coque Command: <Coque::Sh wc -c>
135
+ I, [2019-02-20T20:31:00.329586 #16749] INFO -- : Coque Command: <Coque::Sh wc -c> finished in 0.001739 seconds.
136
+ I, [2019-02-20T20:31:00.329725 #16749] INFO -- : Coque Command: <Pipeline <Coque::Sh echo hi> | <Coque::Sh wc -c> > finished in 0.003796 seconds.
137
+ ```
138
+
102
139
  ### Streaming Performance
103
140
 
104
141
  Should be little overhead compared with the equivalent pipeline from a standard shell.
data/coque.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
 
33
- spec.add_development_dependency "bundler", "~> 1.16"
33
+ spec.add_development_dependency "bundler", "~> 2.0"
34
34
  spec.add_development_dependency "rake", "~> 12.3"
35
35
  spec.add_development_dependency "minitest", "~> 5.11"
36
36
  spec.add_development_dependency "minitest-reporters"
data/lib/coque/cmd.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  module Coque
2
2
  class Cmd
3
3
  include Redirectable
4
+ include Runnable
4
5
  attr_reader :context
5
6
 
6
- def |(other)
7
+ def pipe(other)
7
8
  case other
8
9
  when Cmd
9
10
  Pipeline.new([self.clone, other.clone])
@@ -12,6 +13,10 @@ module Coque
12
13
  end
13
14
  end
14
15
 
16
+ def |(other)
17
+ pipe(other)
18
+ end
19
+
15
20
  def clone
16
21
  raise "Not Implemented - Override"
17
22
  end
@@ -1,6 +1,7 @@
1
1
  module Coque
2
2
  class Pipeline
3
3
  include Redirectable
4
+ include Runnable
4
5
 
5
6
  attr_reader :commands
6
7
  def initialize(commands = [])
@@ -15,7 +16,7 @@ module Coque
15
16
  "<Pipeline #{commands.join(" | ")} >"
16
17
  end
17
18
 
18
- def |(other)
19
+ def pipe(other)
19
20
  case other
20
21
  when Pipeline
21
22
  Pipeline.new(commands + other.commands)
@@ -24,6 +25,10 @@ module Coque
24
25
  end
25
26
  end
26
27
 
28
+ def |(other)
29
+ pipe(other)
30
+ end
31
+
27
32
  def stitch
28
33
  # Set head in
29
34
  if commands.first.stdin.nil?
@@ -52,7 +57,7 @@ module Coque
52
57
  end
53
58
  end
54
59
 
55
- def run
60
+ def get_result
56
61
  stdout = stitch
57
62
  results = commands.map(&:run)
58
63
  Result.new(results.last.pid, stdout)
data/lib/coque/rb.rb CHANGED
@@ -31,7 +31,7 @@ module Coque
31
31
  self
32
32
  end
33
33
 
34
- def run
34
+ def get_result
35
35
  stdin, stdoutr, stdoutw = get_default_fds
36
36
 
37
37
  pid = fork do
@@ -4,24 +4,36 @@ module Coque
4
4
  module Redirectable
5
5
  attr_reader :stdin, :stdout, :stderr
6
6
 
7
- def >(io)
7
+ def out(io)
8
8
  clone.tap do |c|
9
9
  c.stdout = io
10
10
  end
11
11
  end
12
12
 
13
- def <(io)
13
+ def >(io)
14
+ out(io)
15
+ end
16
+
17
+ def in(io)
14
18
  clone.tap do |c|
15
19
  c.stdin = io
16
20
  end
17
21
  end
18
22
 
19
- def >=(io)
23
+ def <(io)
24
+ self.in(io)
25
+ end
26
+
27
+ def err(io)
20
28
  clone.tap do |c|
21
29
  c.stderr = io
22
30
  end
23
31
  end
24
32
 
33
+ def >=(io)
34
+ err(io)
35
+ end
36
+
25
37
  def getio(io, mode = "r")
26
38
  case io
27
39
  when STDERR
@@ -53,20 +65,6 @@ module Coque
53
65
  @stdin = getio(s, "r")
54
66
  end
55
67
 
56
- def to_a
57
- run.to_a
58
- end
59
-
60
- def success?
61
- run.success?
62
- end
63
-
64
- def run!
65
- if !success?
66
- raise "Coque Command Failed: #{self}"
67
- end
68
- end
69
-
70
68
  private
71
69
 
72
70
  def stdout_read
@@ -0,0 +1,38 @@
1
+ module Coque
2
+ module Runnable
3
+ def to_a
4
+ run.to_a
5
+ end
6
+
7
+ def success?
8
+ run.success?
9
+ end
10
+
11
+ def run!
12
+ if !success?
13
+ raise "Coque Command Failed: #{self}"
14
+ end
15
+ end
16
+
17
+ def run
18
+ log_start
19
+ start_time = Time.now
20
+ result = get_result
21
+ end_time = Time.now
22
+ log_end(end_time - start_time)
23
+ result
24
+ end
25
+
26
+ def log_end(seconds)
27
+ if Coque.logger
28
+ Coque.logger.info("Coque Command: #{self.to_s} finished in #{seconds} seconds.")
29
+ end
30
+ end
31
+
32
+ def log_start
33
+ if Coque.logger
34
+ Coque.logger.info("Executing Coque Command: #{self.to_s}")
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/coque/sh.rb CHANGED
@@ -25,7 +25,7 @@ module Coque
25
25
  self.class.new(self.context, self.args + new_args)
26
26
  end
27
27
 
28
- def run
28
+ def get_result
29
29
  stdin, stdoutr, stdoutw = get_default_fds
30
30
  opts = {in: stdin, stdin.fileno => stdin.fileno,
31
31
  out: stdoutw, stdoutw.fileno => stdoutw.fileno,
data/lib/coque/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Coque
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/coque.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "coque/redirectable"
2
+ require "coque/runnable"
2
3
  require "coque/cmd"
3
4
  require "coque/sh"
4
5
  require "coque/rb"
@@ -9,6 +10,15 @@ require "coque/result"
9
10
  require "coque/version"
10
11
 
11
12
  module Coque
13
+ @@logger = nil
14
+ def self.logger=(logger)
15
+ @@logger = logger
16
+ end
17
+
18
+ def self.logger
19
+ @@logger
20
+ end
21
+
12
22
  def self.context(dir: Dir.pwd, env: {}, disinherits_env: false)
13
23
  Context.new(dir, env, disinherits_env)
14
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coque
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Horace Williams
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-29 00:00:00.000000000 Z
11
+ date: 2019-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -112,8 +112,6 @@ files:
112
112
  - bin/console
113
113
  - bin/setup
114
114
  - coque.gemspec
115
- - drake.rb
116
- - io.rb
117
115
  - lib/coque.rb
118
116
  - lib/coque/cmd.rb
119
117
  - lib/coque/context.rb
@@ -122,6 +120,7 @@ files:
122
120
  - lib/coque/rb.rb
123
121
  - lib/coque/redirectable.rb
124
122
  - lib/coque/result.rb
123
+ - lib/coque/runnable.rb
125
124
  - lib/coque/sh.rb
126
125
  - lib/coque/version.rb
127
126
  - script.py
@@ -146,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
145
  version: '0'
147
146
  requirements: []
148
147
  rubyforge_project:
149
- rubygems_version: 2.6.14.1
148
+ rubygems_version: 2.5.1
150
149
  signing_key:
151
150
  specification_version: 4
152
151
  summary: Shell command utilities with easy integration with Ruby code.
data/drake.rb DELETED
@@ -1,113 +0,0 @@
1
- require "open3"
2
- require "pry"
3
-
4
- class Pipeline
5
- attr_reader :commands
6
- def initialize(commands = [])
7
- @commands = commands
8
- end
9
-
10
- def to_s
11
- "Pipeline of #{commands.join("|")}"
12
- end
13
-
14
- def |(other)
15
- case other
16
- when Pipeline
17
- Pipeline.new(commands + other.commands)
18
- when Cmd
19
- Pipeline.new(commands + [other])
20
- when Crb
21
- Pipeline.new(commands + [other])
22
- end
23
- end
24
-
25
- def run
26
- commands = self.commands
27
- pids = []
28
-
29
- in_read, in_write = IO.pipe
30
- out_read = nil
31
- out_write = nil
32
-
33
- while commands.any? do
34
- in_write.close
35
- out_read, out_write = IO.pipe
36
-
37
- cmd = commands.shift
38
- puts "Spawn command: #{cmd}"
39
- pids << cmd.run(in_read, out_write)
40
-
41
- in_read = out_read
42
- in_write = out_write
43
- end
44
- out_write.close
45
- [pids, out_read]
46
- end
47
- end
48
-
49
- class Cmd
50
- attr_reader :args
51
- def initialize(args)
52
- @args = args
53
- end
54
-
55
- def to_s
56
- "Cmd to run: `#{args.inspect}`"
57
- end
58
-
59
- def self.[](*args)
60
- puts args.inspect
61
- Cmd.new(args)
62
- end
63
-
64
- def command
65
- args.join(" ")
66
- end
67
-
68
- def run(stdin, stdout)
69
- spawn(args.join(" "), in: stdin, stdin => stdin, out: stdout, stdout => stdout)
70
- end
71
-
72
- def |(other)
73
- case other
74
- when Cmd
75
- Pipeline.new([self, other])
76
- when Crb
77
- Pipeline.new([self, other])
78
- when Pipeline
79
- Pipeline.new([self] + other.commands)
80
- end
81
- end
82
- end
83
-
84
- class Crb
85
- def initialize(&block)
86
- @block = block
87
- end
88
-
89
- def run(stdin, stdout)
90
- fork do
91
- STDOUT.reopen(stdout)
92
- stdin.each_line(&@block)
93
- end
94
- end
95
-
96
- def |(other)
97
- case other
98
- when Cmd
99
- Pipeline.new([self, other])
100
- when Crb
101
- Pipeline.new([self, other])
102
- when Pipeline
103
- Pipeline.new([self] + other.commands)
104
- end
105
- end
106
- end
107
-
108
-
109
- c = Cmd['cat', '/usr/share/dict/words'] | Cmd['head'] | Crb.new { |line| puts "crb - #{line}" }
110
- puts "pipeline: #{c}"
111
- pids, out = c.run
112
- puts "Spawned pids: #{pids}"
113
- out.each_line { |l| puts l }
data/io.rb DELETED
@@ -1,215 +0,0 @@
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