childprocess 0.2.8 → 0.2.9
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.
- data/.travis.yml +8 -2
- data/README.md +1 -1
- data/lib/childprocess.rb +21 -19
- data/lib/childprocess/errors.rb +3 -3
- data/lib/childprocess/jruby.rb +49 -2
- data/lib/childprocess/tools/generator.rb +10 -3
- data/lib/childprocess/unix/lib.rb +10 -1
- data/lib/childprocess/unix/platform/x86_64-linux.rb +2 -1
- data/lib/childprocess/unix/posix_spawn_process.rb +53 -29
- data/lib/childprocess/version.rb +1 -1
- data/lib/childprocess/windows/lib.rb +10 -2
- data/lib/childprocess/windows/process_builder.rb +4 -4
- data/spec/io_spec.rb +10 -7
- data/spec/jruby_spec.rb +2 -2
- data/spec/spec_helper.rb +28 -2
- data/spec/unix_spec.rb +2 -1
- metadata +4 -4
data/.travis.yml
CHANGED
@@ -3,7 +3,13 @@ rvm:
|
|
3
3
|
- 1.9.2
|
4
4
|
- 1.9.3
|
5
5
|
- jruby
|
6
|
-
|
6
|
+
- rbx
|
7
7
|
env:
|
8
8
|
- CHILDPROCESS_POSIX_SPAWN=true
|
9
|
-
- CHILDPROCESS_POSIX_SPAWN=false
|
9
|
+
- CHILDPROCESS_POSIX_SPAWN=false
|
10
|
+
matrix:
|
11
|
+
exclude:
|
12
|
+
- rvm: rbx
|
13
|
+
env: CHILDPROCESS_POSIX_SPAWN=true
|
14
|
+
- rvm: jruby
|
15
|
+
env: CHILDPROCESS_POSIX_SPAWN=true
|
data/README.md
CHANGED
data/lib/childprocess.rb
CHANGED
@@ -10,17 +10,17 @@ module ChildProcess
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def new(*args)
|
13
|
-
case
|
14
|
-
when :jruby
|
15
|
-
JRuby::Process.new(args)
|
16
|
-
when :windows
|
17
|
-
Windows::Process.new(args)
|
13
|
+
case os
|
18
14
|
when :macosx, :linux, :unix, :cygwin
|
19
15
|
if posix_spawn?
|
20
16
|
Unix::PosixSpawnProcess.new(args)
|
17
|
+
elsif jruby?
|
18
|
+
JRuby::Process.new(args)
|
21
19
|
else
|
22
20
|
Unix::ForkExecProcess.new(args)
|
23
21
|
end
|
22
|
+
when :windows
|
23
|
+
Windows::Process.new(args)
|
24
24
|
else
|
25
25
|
raise Error, "unsupported platform #{platform.inspect}"
|
26
26
|
end
|
@@ -32,10 +32,6 @@ module ChildProcess
|
|
32
32
|
:jruby
|
33
33
|
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
|
34
34
|
:ironruby
|
35
|
-
elsif RUBY_PLATFORM =~ /mswin|msys|mingw32/
|
36
|
-
:windows
|
37
|
-
elsif RUBY_PLATFORM =~ /cygwin/
|
38
|
-
:cygwin
|
39
35
|
else
|
40
36
|
os
|
41
37
|
end
|
@@ -46,7 +42,7 @@ module ChildProcess
|
|
46
42
|
end
|
47
43
|
|
48
44
|
def unix?
|
49
|
-
!
|
45
|
+
!windows?
|
50
46
|
end
|
51
47
|
|
52
48
|
def linux?
|
@@ -57,14 +53,12 @@ module ChildProcess
|
|
57
53
|
platform == :jruby
|
58
54
|
end
|
59
55
|
|
60
|
-
def jruby_on_unix?
|
61
|
-
jruby? and [:macosx, :linux, :unix].include? os
|
62
|
-
end
|
63
|
-
|
64
56
|
def windows?
|
65
|
-
|
57
|
+
os == :windows
|
66
58
|
end
|
67
59
|
|
60
|
+
@posix_spawn = false
|
61
|
+
|
68
62
|
def posix_spawn?
|
69
63
|
enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
|
70
64
|
return false unless enabled
|
@@ -85,6 +79,10 @@ module ChildProcess
|
|
85
79
|
false
|
86
80
|
end
|
87
81
|
|
82
|
+
#
|
83
|
+
# Set this to true to enable experimental use of posix_spawn.
|
84
|
+
#
|
85
|
+
|
88
86
|
def posix_spawn=(bool)
|
89
87
|
@posix_spawn = bool
|
90
88
|
end
|
@@ -95,12 +93,14 @@ module ChildProcess
|
|
95
93
|
host_os = RbConfig::CONFIG['host_os'].downcase
|
96
94
|
|
97
95
|
case host_os
|
98
|
-
when /mswin|msys|mingw32|cygwin/
|
99
|
-
:windows
|
100
|
-
when /darwin|mac os/
|
101
|
-
:macosx
|
102
96
|
when /linux/
|
103
97
|
:linux
|
98
|
+
when /darwin|mac os/
|
99
|
+
:macosx
|
100
|
+
when /mswin|msys|mingw32/
|
101
|
+
:windows
|
102
|
+
when /cygwin/
|
103
|
+
:cygwin
|
104
104
|
when /solaris|bsd/
|
105
105
|
:unix
|
106
106
|
else
|
@@ -161,3 +161,5 @@ module ChildProcess
|
|
161
161
|
|
162
162
|
end # class << self
|
163
163
|
end # ChildProcess
|
164
|
+
|
165
|
+
require 'jruby' if ChildProcess.jruby?
|
data/lib/childprocess/errors.rb
CHANGED
@@ -2,15 +2,15 @@ module ChildProcess
|
|
2
2
|
class Error < StandardError; end
|
3
3
|
class TimeoutError < StandardError; end
|
4
4
|
class SubclassResponsibility < StandardError; end
|
5
|
-
class
|
5
|
+
class InvalidEnvironmentVariable < StandardError; end
|
6
6
|
class LaunchError < StandardError; end
|
7
7
|
|
8
8
|
class MissingPlatformError < StandardError
|
9
9
|
def initialize
|
10
10
|
platform = ChildProcess.platform_name
|
11
11
|
|
12
|
-
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to
|
13
|
-
"
|
12
|
+
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
|
13
|
+
"If you believe this is an error, please file a bug at http://github.com/jarib/childprocess/issues"
|
14
14
|
|
15
15
|
super(message)
|
16
16
|
end
|
data/lib/childprocess/jruby.rb
CHANGED
@@ -1,9 +1,56 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'jruby'
|
3
|
+
|
4
|
+
class Java::SunNioCh::FileChannelImpl
|
5
|
+
field_reader :fd
|
6
|
+
end
|
7
|
+
|
8
|
+
class Java::JavaIo::FileDescriptor
|
9
|
+
if ChildProcess.os == :windows
|
10
|
+
field_reader :handle
|
11
|
+
else
|
12
|
+
field_reader :fd
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
1
16
|
module ChildProcess
|
2
17
|
module JRuby
|
18
|
+
def self.posix_fileno_for(obj)
|
19
|
+
channel = ::JRuby.reference(obj).channel
|
20
|
+
begin
|
21
|
+
channel.getFDVal
|
22
|
+
rescue NoMethodError
|
23
|
+
fileno = channel.fd
|
24
|
+
if fileno.kind_of?(Java::JavaIo::FileDescriptor)
|
25
|
+
fileno = fileno.fd
|
26
|
+
end
|
27
|
+
|
28
|
+
fileno == -1 ? obj.fileno : fileno
|
29
|
+
end
|
30
|
+
rescue
|
31
|
+
# fall back
|
32
|
+
obj.fileno
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.windows_handle_for(obj)
|
36
|
+
channel = ::JRuby.reference(obj).channel
|
37
|
+
fileno = obj.fileno
|
38
|
+
|
39
|
+
begin
|
40
|
+
fileno = channel.getFDVal
|
41
|
+
rescue NoMethodError
|
42
|
+
fileno = channel.fd if channel.respond_to?(:fd)
|
43
|
+
end
|
44
|
+
|
45
|
+
if fileno.kind_of? Java::JavaIo::FileDescriptor
|
46
|
+
fileno.handle
|
47
|
+
else
|
48
|
+
Windows::Lib.handle_for fileno
|
49
|
+
end
|
50
|
+
end
|
3
51
|
end
|
4
52
|
end
|
5
53
|
|
6
|
-
require "java"
|
7
54
|
require "childprocess/jruby/pump"
|
8
55
|
require "childprocess/jruby/io"
|
9
|
-
require "childprocess/jruby/process"
|
56
|
+
require "childprocess/jruby/process"
|
@@ -46,6 +46,7 @@ module ChildProcess
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def fetch_size(type_name, opts = {})
|
49
|
+
print "sizeof(#{type_name}): "
|
49
50
|
src = <<-EOF
|
50
51
|
int main() {
|
51
52
|
printf("%d", (unsigned int)sizeof(#{type_name}));
|
@@ -59,10 +60,14 @@ int main() {
|
|
59
60
|
raise "sizeof(#{type_name}) == #{output.to_i} (output=#{output})"
|
60
61
|
end
|
61
62
|
|
62
|
-
|
63
|
+
size = output.to_i
|
64
|
+
@sizeof[type_name] = size
|
65
|
+
|
66
|
+
puts size
|
63
67
|
end
|
64
68
|
|
65
69
|
def fetch_constant(name, opts)
|
70
|
+
print "#{name}: "
|
66
71
|
src = <<-EOF
|
67
72
|
int main() {
|
68
73
|
printf("%d", (unsigned int)#{name});
|
@@ -71,12 +76,14 @@ int main() {
|
|
71
76
|
EOF
|
72
77
|
|
73
78
|
output = execute(src, opts)
|
74
|
-
|
79
|
+
value = Integer(output)
|
80
|
+
@constants[name] = value
|
81
|
+
|
82
|
+
puts value
|
75
83
|
end
|
76
84
|
|
77
85
|
|
78
86
|
def execute(src, opts)
|
79
|
-
|
80
87
|
program = Array(opts[:define]).map do |key, value|
|
81
88
|
<<-SRC
|
82
89
|
#ifndef #{key}
|
@@ -1,10 +1,19 @@
|
|
1
|
+
|
1
2
|
module ChildProcess
|
2
3
|
module Unix
|
3
4
|
module Lib
|
4
5
|
extend FFI::Library
|
5
|
-
|
6
6
|
ffi_lib FFI::Library::LIBC
|
7
7
|
|
8
|
+
if ChildProcess.os == :macosx
|
9
|
+
attach_function :_NSGetEnviron, [], :pointer
|
10
|
+
def self.environ
|
11
|
+
_NSGetEnviron().read_pointer
|
12
|
+
end
|
13
|
+
elsif respond_to? :attach_variable
|
14
|
+
attach_variable :environ, :pointer
|
15
|
+
end
|
16
|
+
|
8
17
|
attach_function :strerror, [:int], :string
|
9
18
|
|
10
19
|
# int posix_spawnp(
|
@@ -13,22 +13,22 @@ module ChildProcess
|
|
13
13
|
|
14
14
|
if @io
|
15
15
|
if @io.stdout
|
16
|
-
actions.add_dup @io.stdout
|
16
|
+
actions.add_dup fileno_for(@io.stdout), fileno_for($stdout)
|
17
17
|
else
|
18
|
-
actions.add_open $stdout
|
18
|
+
actions.add_open fileno_for($stdout), "/dev/null", File::WRONLY, 0644
|
19
19
|
end
|
20
20
|
|
21
21
|
if @io.stderr
|
22
|
-
actions.add_dup @io.stderr
|
22
|
+
actions.add_dup fileno_for(@io.stderr), fileno_for($stderr)
|
23
23
|
else
|
24
|
-
actions.add_open $stderr
|
24
|
+
actions.add_open fileno_for($stderr), "/dev/null", File::WRONLY, 0644
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
if duplex?
|
29
29
|
reader, writer = ::IO.pipe
|
30
|
-
actions.add_dup reader
|
31
|
-
actions.add_close writer
|
30
|
+
actions.add_dup fileno_for(reader), fileno_for($stdin)
|
31
|
+
actions.add_close fileno_for(writer)
|
32
32
|
end
|
33
33
|
|
34
34
|
if defined? Platform::POSIX_SPAWN_USEVFORK
|
@@ -37,13 +37,17 @@ module ChildProcess
|
|
37
37
|
|
38
38
|
attrs.flags = flags
|
39
39
|
|
40
|
+
# wrap in helper classes in order to avoid GC'ed pointers
|
41
|
+
argv = Argv.new(@args)
|
42
|
+
envp = Envp.new(ENV.to_hash.merge(@environment))
|
43
|
+
|
40
44
|
ret = Lib.posix_spawnp(
|
41
45
|
pid_ptr,
|
42
46
|
@args.first, # TODO: not sure this matches exec() behaviour
|
43
47
|
actions,
|
44
48
|
attrs,
|
45
49
|
argv,
|
46
|
-
|
50
|
+
envp
|
47
51
|
)
|
48
52
|
|
49
53
|
if duplex?
|
@@ -62,38 +66,58 @@ module ChildProcess
|
|
62
66
|
::Process.detach(@pid) if detach?
|
63
67
|
end
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
if ChildProcess.jruby?
|
70
|
+
def fileno_for(obj)
|
71
|
+
ChildProcess::JRuby.posix_fileno_for(obj)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
def fileno_for(obj)
|
75
|
+
obj.fileno
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Argv
|
80
|
+
def initialize(args)
|
81
|
+
@ptrs = args.map do |e|
|
82
|
+
if e.include?("\0")
|
83
|
+
raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}"
|
84
|
+
end
|
85
|
+
|
86
|
+
FFI::MemoryPointer.from_string(e.to_s)
|
69
87
|
end
|
70
|
-
|
88
|
+
|
89
|
+
@ptrs << nil
|
71
90
|
end
|
72
|
-
arg_ptrs << nil
|
73
91
|
|
74
|
-
|
75
|
-
|
92
|
+
def to_ptr
|
93
|
+
argv = FFI::MemoryPointer.new(:pointer, @ptrs.size)
|
94
|
+
argv.put_array_of_pointer(0, @ptrs)
|
76
95
|
|
77
|
-
|
78
|
-
|
96
|
+
argv
|
97
|
+
end
|
98
|
+
end # Argv
|
79
99
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
100
|
+
class Envp
|
101
|
+
def initialize(env)
|
102
|
+
@ptrs = env.map do |key, val|
|
103
|
+
if key =~ /=|\0/ || val.include?("\0")
|
104
|
+
raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
|
105
|
+
end
|
106
|
+
|
107
|
+
FFI::MemoryPointer.from_string("#{key}=#{val}")
|
84
108
|
end
|
85
109
|
|
86
|
-
|
110
|
+
@ptrs << nil
|
87
111
|
end
|
88
112
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
env.put_array_of_pointer(0, env_ptrs)
|
113
|
+
def to_ptr
|
114
|
+
env = FFI::MemoryPointer.new(:pointer, @ptrs.size)
|
115
|
+
env.put_array_of_pointer(0, @ptrs)
|
93
116
|
|
94
|
-
|
95
|
-
|
117
|
+
env
|
118
|
+
end
|
119
|
+
end # Envp
|
96
120
|
|
97
121
|
end
|
98
122
|
end
|
99
|
-
end
|
123
|
+
end
|
data/lib/childprocess/version.rb
CHANGED
@@ -252,12 +252,20 @@ module ChildProcess
|
|
252
252
|
)
|
253
253
|
|
254
254
|
str = buf.read_string(size).strip
|
255
|
-
|
255
|
+
if errnum == 0
|
256
|
+
"Unknown error (Windows says #{str.inspect}, but it did not.)"
|
257
|
+
else
|
258
|
+
"#{str} (#{errnum})"
|
259
|
+
end
|
256
260
|
end
|
257
261
|
|
258
262
|
def handle_for(fd_or_io)
|
259
263
|
if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
|
260
|
-
|
264
|
+
if ChildProcess.jruby?
|
265
|
+
handle = ChildProcess::JRuby.windows_handle_for(fd_or_io)
|
266
|
+
else
|
267
|
+
handle = get_osfhandle(fd_or_io.fileno)
|
268
|
+
end
|
261
269
|
elsif fd_or_io.kind_of?(Fixnum)
|
262
270
|
handle = get_osfhandle(fd_or_io)
|
263
271
|
elsif fd_or_io.respond_to?(:to_io)
|
@@ -48,12 +48,12 @@ module ChildProcess
|
|
48
48
|
strings = ENV.map { |k,v| "#{k}=#{v}\0" }
|
49
49
|
|
50
50
|
# extras
|
51
|
-
@environment.each do |key,
|
52
|
-
if key.include?("
|
53
|
-
raise
|
51
|
+
@environment.each do |key, val|
|
52
|
+
if key =~ /=|\0/ || val.include?("\0")
|
53
|
+
raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
|
54
54
|
end
|
55
55
|
|
56
|
-
strings << "#{key}=#{
|
56
|
+
strings << "#{key}=#{val}\0"
|
57
57
|
end
|
58
58
|
|
59
59
|
strings << "\0" # terminate the env block
|
data/spec/io_spec.rb
CHANGED
@@ -85,10 +85,10 @@ describe ChildProcess do
|
|
85
85
|
# http://travis-ci.org/#!/jarib/childprocess/jobs/487331
|
86
86
|
#
|
87
87
|
|
88
|
-
it "works with pipes", :
|
88
|
+
it "works with pipes", :process_builder => false do
|
89
89
|
process = ruby(<<-CODE)
|
90
|
-
STDOUT.
|
91
|
-
STDERR.
|
90
|
+
STDOUT.print "stdout"
|
91
|
+
STDERR.print "stderr"
|
92
92
|
CODE
|
93
93
|
|
94
94
|
stdout, stdout_w = IO.pipe
|
@@ -109,12 +109,15 @@ describe ChildProcess do
|
|
109
109
|
stdout_w.close
|
110
110
|
stderr_w.close
|
111
111
|
|
112
|
-
stdout.read
|
113
|
-
stderr.read
|
112
|
+
out = stdout.read
|
113
|
+
err = stderr.read
|
114
|
+
|
115
|
+
[out, err].should == %w[stdout stderr]
|
114
116
|
end
|
115
117
|
|
116
118
|
it "can set close-on-exec when IO is inherited" do
|
117
|
-
|
119
|
+
port = random_free_port
|
120
|
+
server = TCPServer.new("127.0.0.1", port)
|
118
121
|
ChildProcess.close_on_exec server
|
119
122
|
|
120
123
|
process = sleeping_ruby
|
@@ -123,6 +126,6 @@ describe ChildProcess do
|
|
123
126
|
process.start
|
124
127
|
server.close
|
125
128
|
|
126
|
-
|
129
|
+
wait_until { can_bind? "127.0.0.1", port }
|
127
130
|
end
|
128
131
|
end
|
data/spec/jruby_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
require "pid_behavior"
|
3
3
|
|
4
|
-
if ChildProcess.jruby?
|
4
|
+
if ChildProcess.jruby? && !ChildProcess.windows?
|
5
5
|
describe ChildProcess::JRuby::IO do
|
6
6
|
let(:io) { ChildProcess::JRuby::IO.new }
|
7
7
|
|
@@ -11,7 +11,7 @@ if ChildProcess.jruby?
|
|
11
11
|
end
|
12
12
|
|
13
13
|
describe ChildProcess::JRuby::Process do
|
14
|
-
if ChildProcess.
|
14
|
+
if ChildProcess.unix?
|
15
15
|
it_behaves_like "a platform that provides the child's pid"
|
16
16
|
else
|
17
17
|
it "raises an error when trying to access the child's pid" do
|
data/spec/spec_helper.rb
CHANGED
@@ -115,6 +115,32 @@ module ChildProcessSpecHelper
|
|
115
115
|
10
|
116
116
|
end
|
117
117
|
|
118
|
+
def random_free_port
|
119
|
+
server = TCPServer.new('127.0.0.1', 0)
|
120
|
+
port = server.addr[1]
|
121
|
+
server.close
|
122
|
+
|
123
|
+
port
|
124
|
+
end
|
125
|
+
|
126
|
+
def wait_until(timeout = 10, &blk)
|
127
|
+
end_time = Time.now + timeout
|
128
|
+
|
129
|
+
until Time.now >= end_time
|
130
|
+
return if yield
|
131
|
+
sleep 0.05
|
132
|
+
end
|
133
|
+
|
134
|
+
raise "timed out"
|
135
|
+
end
|
136
|
+
|
137
|
+
def can_bind?(host, port)
|
138
|
+
TCPServer.new(host, port).close
|
139
|
+
true
|
140
|
+
rescue
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
118
144
|
end # ChildProcessSpecHelper
|
119
145
|
|
120
146
|
Thread.abort_on_exception = true
|
@@ -125,8 +151,8 @@ RSpec.configure do |c|
|
|
125
151
|
@process && @process.alive? && @process.stop
|
126
152
|
}
|
127
153
|
|
128
|
-
if
|
129
|
-
c.filter_run_excluding :
|
154
|
+
if ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
155
|
+
c.filter_run_excluding :process_builder => false
|
130
156
|
end
|
131
157
|
|
132
158
|
if ChildProcess.linux? && ChildProcess.posix_spawn?
|
data/spec/unix_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
require "pid_behavior"
|
3
3
|
|
4
|
-
if ChildProcess.unix? && !ChildProcess.posix_spawn?
|
4
|
+
if ChildProcess.unix? && !ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
5
|
+
|
5
6
|
describe ChildProcess::Unix::Process do
|
6
7
|
it_behaves_like "a platform that provides the child's pid"
|
7
8
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: childprocess
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 9
|
10
|
+
version: 0.2.9
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jari Bakken
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
18
|
+
date: 2012-01-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|