coque 0.6.0 → 0.7.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 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