dolzenko 0.0.10 → 0.0.11
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/dolzenko.gemspec +0 -0
- data/lib/dolzenko/django_f_object.rb +213 -0
- data/lib/dolzenko/django_q_object.rb +176 -0
- data/lib/dolzenko/error_print.rb +169 -0
- data/lib/dolzenko/gist_readme.rb +20 -0
- data/lib/dolzenko/includable_with_options.rb +106 -0
- data/lib/dolzenko/io_interceptor.rb +64 -0
- data/lib/dolzenko/remote_download.rb +90 -0
- data/lib/dolzenko/shell_out.rb +386 -0
- data/lib/dolzenko/try_block.rb +189 -0
- data/lib/dolzenko-light.rb +11 -0
- data/lib/dolzenko.rb +1 -11
- metadata +13 -3
@@ -0,0 +1,64 @@
|
|
1
|
+
# Safe way to intercept IO stream
|
2
|
+
# where just replacing STDOUT doesn't work:
|
3
|
+
# http://rubyforge.org/tracker/index.php?func=detail&aid=5217&group_id=426&atid=1698
|
4
|
+
#
|
5
|
+
module IoInterceptor
|
6
|
+
def intercept
|
7
|
+
begin
|
8
|
+
@intercept = true
|
9
|
+
@intercepted = ""
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
@intercept = false
|
13
|
+
end
|
14
|
+
@intercepted
|
15
|
+
end
|
16
|
+
|
17
|
+
def supress
|
18
|
+
begin
|
19
|
+
@supress = true
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
@supress = false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(str)
|
27
|
+
if @supress || @intercept
|
28
|
+
@intercepted << str.to_s unless @supress
|
29
|
+
str.size
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if $PROGRAM_NAME == __FILE__
|
37
|
+
require 'spec'
|
38
|
+
|
39
|
+
describe IoInterceptor do
|
40
|
+
before do
|
41
|
+
STDOUT.extend(IoInterceptor)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "intercepts output to stream when the stream is extended with it" do
|
45
|
+
STDOUT.intercept { STDOUT.puts("42") }.should == "42\n"
|
46
|
+
STDOUT.intercept { STDOUT.puts("24") }.should == "24\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "intercepted IO#write still returns the number of bytes written" do
|
50
|
+
STDOUT.intercept { STDOUT.write("42").should == 2 }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "intercepted IO#write argument is converted using to_s" do
|
54
|
+
obj = "42"
|
55
|
+
def obj.to_s
|
56
|
+
"custom to_s"
|
57
|
+
end
|
58
|
+
|
59
|
+
STDOUT.intercept { STDOUT.puts(obj) }.should == "#{ obj.to_s }\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
exit ::Spec::Runner::CommandLine.run
|
64
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module RemoteDownload
|
2
|
+
# Returns IO object to be used with attachment_fu models, if retrieval fails - return nil
|
3
|
+
def get_uploaded_data(url, follow_redirect = 2)
|
4
|
+
uri = URI.parse(url)
|
5
|
+
|
6
|
+
dputs(%{Trying to retrieve image from #{ uri } })
|
7
|
+
|
8
|
+
response = nil
|
9
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
10
|
+
# http.set_debug_output $stderr
|
11
|
+
dputs(%{In Net::HTTP.start})
|
12
|
+
|
13
|
+
response = http.request_get(uri.path + (uri.query ? "?" + uri.query : ""), {
|
14
|
+
"User-Agent" => "curl/7.14.0 (i586-pc-mingw32msvc) libcurl/7.14.0 zlib/1.2.2",
|
15
|
+
"Host" => uri.host,
|
16
|
+
"Accept" => "*/*",
|
17
|
+
"Referer" => SELFPORT,
|
18
|
+
})
|
19
|
+
|
20
|
+
dputs("got response: #{ response }")
|
21
|
+
end
|
22
|
+
|
23
|
+
case response
|
24
|
+
when Net::HTTPSuccess
|
25
|
+
MyStringIO.from_http_response(response, url)
|
26
|
+
|
27
|
+
when Net::HTTPRedirection
|
28
|
+
return nil if follow_redirect <= 0
|
29
|
+
|
30
|
+
defined?(logger) && logger.debug(%{Following redirect from #{uri} to #{response['location']}})
|
31
|
+
|
32
|
+
get_uploaded_data(response['location'], follow_redirect - 1)
|
33
|
+
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
module_function :get_uploaded_data
|
39
|
+
|
40
|
+
# Returns page content, if retrieval failed - just return empty string
|
41
|
+
def download_page(url, follow_redirect = 2)
|
42
|
+
uri = URI.parse(url)
|
43
|
+
|
44
|
+
defined?(logger) && logger.debug(%{Trying to retrieve page #{uri}})
|
45
|
+
|
46
|
+
response = nil
|
47
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
48
|
+
response = http.request_get(uri.path + (uri.query ? "?" + uri.query : ""), {
|
49
|
+
"User-Agent" => "curl/7.14.0 (i586-pc-mingw32msvc) libcurl/7.14.0 zlib/1.2.2",
|
50
|
+
"Host" => uri.host,
|
51
|
+
"Accept" => "text/html",
|
52
|
+
"Referer" => SELFPORT,
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
case response
|
58
|
+
when Net::HTTPSuccess
|
59
|
+
response.body
|
60
|
+
when Net::HTTPRedirection
|
61
|
+
return "" if follow_redirect <= 0
|
62
|
+
|
63
|
+
defined?(logger) && logger.debug(%{Following redirect from #{uri} to #{response['location']}})
|
64
|
+
|
65
|
+
download_page(response['location'], follow_redirect - 1)
|
66
|
+
else
|
67
|
+
""
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module_function :download_page
|
72
|
+
|
73
|
+
class MyStringIO < StringIO
|
74
|
+
def initialize(*args)
|
75
|
+
super(*args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Constructs IO object from HTTPResponse
|
79
|
+
def self.from_http_response(response, request_url)
|
80
|
+
new(response.body).tap do |io|
|
81
|
+
io.size = response.body.size
|
82
|
+
io.content_type = response['content-type']
|
83
|
+
io.filename = File.basename(request_url)
|
84
|
+
io.original_filename = File.basename(request_url)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_accessor :content_type, :filename, :size, :original_filename
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
# ## ShellOut
|
2
|
+
#
|
3
|
+
# Provides a convenient feature-rich way to "shell out" to external commands.
|
4
|
+
# Most useful features come from using `PTY` to execute the command. Not available
|
5
|
+
# on Windows, `Kernel#system` will be used instead.
|
6
|
+
#
|
7
|
+
# ## Features
|
8
|
+
#
|
9
|
+
# ### Interruption
|
10
|
+
#
|
11
|
+
# The external command can be easily interrupted and `Interrupt` exception
|
12
|
+
# will propagate to the calling program.
|
13
|
+
#
|
14
|
+
# For example while something like this can hang your terminal
|
15
|
+
#
|
16
|
+
# loop { system("ls -R /") } # => lists directories indefinitely,
|
17
|
+
# # Ctrl-C only stops ls
|
18
|
+
#
|
19
|
+
# That won't be the case with ShellOut:
|
20
|
+
#
|
21
|
+
# require "shell_out"
|
22
|
+
# include ShellOut
|
23
|
+
# loop { shell_out("ls -R /") } # => when Ctrl-C is pressed ls is terminated
|
24
|
+
# # and Interrupt exception is propagated
|
25
|
+
#
|
26
|
+
# Yes it's possible to examine the `$CHILD_STATUS.exitstatus` variable but that's
|
27
|
+
# not nearly as robust and flexible as `PTY` solution.
|
28
|
+
#
|
29
|
+
# ### TTY-like Output
|
30
|
+
#
|
31
|
+
# External command is running in pseudo TTY provided by `PTY` library on Unix,
|
32
|
+
# which means that commands like `ffmpeg`, `git` can report progress and
|
33
|
+
# **otherwise interact with user as usual**.
|
34
|
+
#
|
35
|
+
# ### Output Capturing
|
36
|
+
#
|
37
|
+
# Output of the command can be captured using `:out` option
|
38
|
+
#
|
39
|
+
# io = StringIO.new
|
40
|
+
# shell_out("echo 42", :out => io) # doesn't print anything
|
41
|
+
# io.string.chomp # => "42"
|
42
|
+
#
|
43
|
+
# If `:out => :return` option is passed - the `shell_out` return the output
|
44
|
+
# of the command instead of exit status.
|
45
|
+
#
|
46
|
+
# ### :raise_exceptions, :verbose, :noop, and :dry_run Options
|
47
|
+
#
|
48
|
+
# * `:raise_exceptions => true` will raise `ShellOutException` for any non-zero
|
49
|
+
# exit status of the command
|
50
|
+
#
|
51
|
+
# Following options have the same semantics as `FileUtils` method options do
|
52
|
+
#
|
53
|
+
# * `:verbose => true` will echo command before execution
|
54
|
+
#
|
55
|
+
# * `:noop => true` will just return zero exit status without executing
|
56
|
+
# the command
|
57
|
+
#
|
58
|
+
# * `:dry_run => true` equivalent to `:verbose => true, :noop => true`
|
59
|
+
#
|
60
|
+
module ShellOut
|
61
|
+
class ShellOutException < Exception
|
62
|
+
end
|
63
|
+
|
64
|
+
CTRL_C_CODE = ?\C-c
|
65
|
+
SUCCESS_EXIT_STATUS = 0
|
66
|
+
|
67
|
+
class << self
|
68
|
+
def before(*args)
|
69
|
+
if args.last.is_a?(Hash)
|
70
|
+
options = args.last
|
71
|
+
|
72
|
+
verbose, dry_run, noop = options.delete(:verbose), options.delete(:dry_run), options.delete(:noop)
|
73
|
+
verbose = noop = true if dry_run
|
74
|
+
|
75
|
+
puts "Executing: #{ args[0..-2].join(" ") }" if verbose
|
76
|
+
|
77
|
+
return false if noop
|
78
|
+
end
|
79
|
+
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def after(exitstatus, out_stream, *args)
|
84
|
+
if args.last.is_a?(Hash) && args.last[:raise_exceptions] == true
|
85
|
+
unless exitstatus == SUCCESS_EXIT_STATUS
|
86
|
+
raise ShellOutException, "`#{ args[0..-2].join(" ") }' command finished with non-zero (#{ exitstatus }) exit status"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
if args.last.is_a?(Hash) && args.last[:out] == :return
|
90
|
+
out_stream.rewind if out_stream.is_a?(StringIO)
|
91
|
+
out_stream.read
|
92
|
+
else
|
93
|
+
exitstatus
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def command(*args)
|
98
|
+
(args.last.is_a?(Hash) ? args[0..-2] : args).join(" ")
|
99
|
+
end
|
100
|
+
|
101
|
+
def getopt(opt, default, *args)
|
102
|
+
if args.last.is_a?(Hash)
|
103
|
+
if opt == :out && args.last[:out] == :return
|
104
|
+
StringIO.new
|
105
|
+
else
|
106
|
+
args.last.fetch(opt, default)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
default
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module_function
|
115
|
+
|
116
|
+
def shell_out_with_pty(*args)
|
117
|
+
old_state = `stty -g`
|
118
|
+
return SUCCESS_EXIT_STATUS unless ShellOut::before(*args)
|
119
|
+
|
120
|
+
# stolen from ruby/ext/pty/script.rb
|
121
|
+
# disable echoing and enable raw (not having to press enter)
|
122
|
+
system "stty -echo raw lnext ^_"
|
123
|
+
|
124
|
+
in_stream = ShellOut.getopt(:in, STDIN, *args)
|
125
|
+
out_stream = ShellOut.getopt(:out, STDOUT, *args)
|
126
|
+
|
127
|
+
PTY.spawn(ShellOut.command(*args)) do |r_pty, w_pty, pid|
|
128
|
+
reader = Thread.current
|
129
|
+
writer = Thread.new do
|
130
|
+
while true
|
131
|
+
break if (ch = in_stream.getc).nil?
|
132
|
+
ch = ch.chr
|
133
|
+
if ch == ShellOut::CTRL_C_CODE
|
134
|
+
reader.raise Interrupt, "Interrupted by user"
|
135
|
+
else
|
136
|
+
w_pty.print ch
|
137
|
+
w_pty.flush
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
writer.abort_on_exception = true
|
142
|
+
|
143
|
+
loop do
|
144
|
+
c = begin
|
145
|
+
r_pty.sysread(512)
|
146
|
+
rescue Errno::EIO, EOFError
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
break if c.nil?
|
150
|
+
|
151
|
+
out_stream.print c
|
152
|
+
out_stream.flush
|
153
|
+
end
|
154
|
+
|
155
|
+
begin
|
156
|
+
# try to invoke waitpid() before the signal handler does it
|
157
|
+
return ShellOut::after(Process::waitpid2(pid)[1].exitstatus, out_stream, *args)
|
158
|
+
rescue Errno::ECHILD
|
159
|
+
# the signal handler managed to call waitpid() first;
|
160
|
+
# PTY::ChildExited will be delivered pretty soon, so just wait for it
|
161
|
+
sleep 1
|
162
|
+
end
|
163
|
+
end
|
164
|
+
rescue PTY::ChildExited => e
|
165
|
+
return ShellOut::after(e.status.exitstatus, out_stream, *args)
|
166
|
+
ensure
|
167
|
+
system "stty #{ old_state }"
|
168
|
+
end
|
169
|
+
|
170
|
+
def shell_out_with_system(*args)
|
171
|
+
return SUCCESS_EXIT_STATUS unless ShellOut::before(*args)
|
172
|
+
|
173
|
+
cleaned_args = if args.last.is_a?(Hash)
|
174
|
+
cleaned_options = args.last.dup.delete_if { |k, | [ :verbose, :raise_exceptions ].include?(k) }
|
175
|
+
require "stringio"
|
176
|
+
if cleaned_options[:out].is_a?(StringIO) ||
|
177
|
+
cleaned_options[:out] == :return
|
178
|
+
r, w = IO.pipe
|
179
|
+
cleaned_options[:out] = w
|
180
|
+
cleaned_options[:err] = [ :child, :out ]
|
181
|
+
end
|
182
|
+
if cleaned_options[:in].is_a?(StringIO)
|
183
|
+
in_r, in_w = IO.pipe
|
184
|
+
in_w.write cleaned_options[:in].read
|
185
|
+
in_w.close
|
186
|
+
cleaned_options[:in] = in_r
|
187
|
+
end
|
188
|
+
cleaned_options.empty? ? args[0..-2] : args[0..-2].dup << cleaned_options
|
189
|
+
else
|
190
|
+
args
|
191
|
+
end
|
192
|
+
|
193
|
+
exitstatus = if Kernel.system(*cleaned_args)
|
194
|
+
SUCCESS_EXIT_STATUS
|
195
|
+
else
|
196
|
+
require "English"
|
197
|
+
$CHILD_STATUS.exitstatus
|
198
|
+
end
|
199
|
+
|
200
|
+
if r
|
201
|
+
w.close
|
202
|
+
unless args.last[:out] == :return
|
203
|
+
args.last[:out] << r.read
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
ShellOut::after(exitstatus, r, *args)
|
208
|
+
end
|
209
|
+
|
210
|
+
begin
|
211
|
+
require "pty"
|
212
|
+
alias shell_out shell_out_with_pty
|
213
|
+
rescue LoadError
|
214
|
+
alias shell_out shell_out_with_system
|
215
|
+
end
|
216
|
+
|
217
|
+
module_function :shell_out
|
218
|
+
end
|
219
|
+
|
220
|
+
def ShellOut(*args)
|
221
|
+
ShellOut.shell_out(*args)
|
222
|
+
end
|
223
|
+
|
224
|
+
if $PROGRAM_NAME == __FILE__
|
225
|
+
require "spec"
|
226
|
+
require "stringio"
|
227
|
+
require "./io_interceptor.rb"
|
228
|
+
|
229
|
+
describe "ShellOut" do
|
230
|
+
before do
|
231
|
+
STDOUT.extend(IoInterceptor)
|
232
|
+
end
|
233
|
+
|
234
|
+
share_examples_for "having base capabilities" do
|
235
|
+
|
236
|
+
it "shells out to successful command and returns 0 exit code" do
|
237
|
+
ShellOut("ruby -e ''").should == 0
|
238
|
+
end
|
239
|
+
|
240
|
+
it "passes arbitrary exit codes" do
|
241
|
+
ShellOut("ruby -e 'exit(42)'").should == 42
|
242
|
+
end
|
243
|
+
|
244
|
+
it "passes STDIN (or options[:in]) stream to the command" do
|
245
|
+
fake_stdin = StringIO.new
|
246
|
+
fake_stdin.write "testing\n"
|
247
|
+
fake_stdin.rewind
|
248
|
+
|
249
|
+
STDOUT.supress do # TODO still mystery why "testing\n" gets written to the output
|
250
|
+
ShellOut("ruby -e 'exit(123) if gets.chomp == \"testing\"'", :in => fake_stdin).should == 123
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe ":raise_exceptions option" do
|
255
|
+
it "raises exception for non-zero exit codes" do
|
256
|
+
lambda do
|
257
|
+
ShellOut("false", :raise_exceptions => true)
|
258
|
+
end.should raise_error(ShellOut::ShellOutException)
|
259
|
+
end
|
260
|
+
|
261
|
+
it "raises exception for non-existing command" do
|
262
|
+
lambda do
|
263
|
+
ShellOut("nonexisting", :raise_exceptions => true)
|
264
|
+
end.should raise_error(ShellOut::ShellOutException)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe ":out option" do
|
269
|
+
it "redirects output of command" do
|
270
|
+
fake_stdout = StringIO.new
|
271
|
+
ShellOut("echo 42", :out => fake_stdout)
|
272
|
+
fake_stdout.string.chomp.should == "42"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe ":out => :return option" do
|
277
|
+
it "return the output of command" do
|
278
|
+
ShellOut("echo 42", :out => :return).chomp.should == "42"
|
279
|
+
end
|
280
|
+
|
281
|
+
it "return STDERR output of command" do
|
282
|
+
ShellOut("ruby -e 'STDERR.puts 42'", :out => :return).chomp.should == "42"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe ":verbose option" do
|
287
|
+
it "echoes the command name" do
|
288
|
+
STDOUT.intercept { ShellOut("true", :verbose => true) }.should include("true")
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def with_vanishing_file(f)
|
293
|
+
yield
|
294
|
+
ensure
|
295
|
+
File.delete(f) rescue nil
|
296
|
+
end
|
297
|
+
|
298
|
+
describe ":noop option" do
|
299
|
+
it "always returns 0 status" do
|
300
|
+
ShellOut("ruby -e 'exit 123'", :noop => true).should == 0
|
301
|
+
end
|
302
|
+
|
303
|
+
it "never executes command" do
|
304
|
+
f = "never_executes_command.test"
|
305
|
+
with_vanishing_file(f) do
|
306
|
+
ShellOut("touch #{ f }", :noop => true)
|
307
|
+
File.exist?(f).should == false
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe ":dry_run option" do
|
313
|
+
it "echoes the command name" do
|
314
|
+
STDOUT.intercept { ShellOut("true", :dry_run => true) }.should include("true")
|
315
|
+
end
|
316
|
+
|
317
|
+
it "always returns 0 status" do
|
318
|
+
STDOUT.supress { ShellOut("ruby -e 'exit 123'", :dry_run => true) }.should == 0
|
319
|
+
end
|
320
|
+
|
321
|
+
it "never executes command" do
|
322
|
+
f = "never_executes_command.test"
|
323
|
+
with_vanishing_file(f) do
|
324
|
+
STDOUT.supress { ShellOut("touch #{ f }", :dry_run => true) }
|
325
|
+
File.exist?(f).should == false
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "#shell_out_with_system" do
|
332
|
+
before do
|
333
|
+
def ShellOut(*args)
|
334
|
+
ShellOut.shell_out_with_system(*args);
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
it_should_behave_like "having base capabilities"
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "#shell_out_with_pty" do
|
342
|
+
begin
|
343
|
+
require "pty"
|
344
|
+
before do
|
345
|
+
def ShellOut(*args)
|
346
|
+
ShellOut.shell_out_with_pty(*args);
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
it_should_behave_like "having base capabilities"
|
351
|
+
|
352
|
+
it "uses pseudo-tty" do
|
353
|
+
ShellOut("ruby -e 'exit(123) if [STDIN, STDOUT, STDERR].all? { |stream| stream.tty? }'").should == 123
|
354
|
+
end
|
355
|
+
|
356
|
+
it "shows the output of the executing command on own STDOUT" do
|
357
|
+
STDOUT.intercept { ShellOut("echo '42'") }.should include("42")
|
358
|
+
end
|
359
|
+
|
360
|
+
it "doesn't pass Ctrl-C to the command and raises Interrupt exception when Ctrl-C is sent" do
|
361
|
+
fake_stdin = StringIO.new
|
362
|
+
fake_stdin.write ShellOut::CTRL_C_CODE
|
363
|
+
fake_stdin.rewind
|
364
|
+
int_trap = "ruby -e 'trap(\"INT\") { puts \"SIGINT received\" }; sleep 999'"
|
365
|
+
lambda do
|
366
|
+
ShellOut(int_trap, :in => fake_stdin)
|
367
|
+
end.should raise_error(Interrupt)
|
368
|
+
|
369
|
+
fake_stdin = StringIO.new
|
370
|
+
fake_stdin.write ShellOut::CTRL_C_CODE
|
371
|
+
fake_stdin.rewind
|
372
|
+
STDOUT.intercept do
|
373
|
+
begin
|
374
|
+
ShellOut(int_trap, :in => fake_stdin)
|
375
|
+
rescue Interrupt
|
376
|
+
end
|
377
|
+
end.should_not include("SIGINT received")
|
378
|
+
end
|
379
|
+
rescue LoadError
|
380
|
+
it "cannot be tested because 'pty' is not available on this system"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
exit ::Spec::Runner::CommandLine.run
|
386
|
+
end
|