Module: SilentStream::Extracted

Defined in:
lib/silent_stream.rb

Overview

Extracted from:
https://github.com/rails/rails/blob/4-2-stable/activesupport/lib/active_support/core_ext/kernel/reporting.rb

Constant Summary collapse

SILENT_STREAM_NULL_DEVICE =
if defined?(IO::NULL)
  IO::NULL
else
  # :nocov:
  Gem.win_platform? ? "NUL:" : "/dev/null"
  # :nocov:
end

Instance Method Summary collapse

Instance Method Details

#capture(stream) { ... } ⇒ String

Note:

This method is not thread-safe.

Captures the given stream and returns it:

stream = capture(:stdout) { puts ‘notice’ }
stream # => “notice\n”

stream = capture(:stderr) { warn ‘error’ }
stream # => “error\n”

even for subprocesses:

stream = capture(:stdout) { system(‘echo notice’) }
stream # => “notice\n”

stream = capture(:stderr) { system(‘echo error 1>&2’) }
stream # => “error\n”

Parameters:

  • stream (Symbol, String)

    :stdout or :stderr (or equivalents), selecting which stream to capture.

Yields:

  • Work that writes to the selected stream.

Returns:

  • (String)

    Captured contents of the stream written during the block.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/silent_stream.rb', line 128

def capture(stream)
  stream = stream.to_s
  io_const = (stream == "stdout") ? STDOUT : STDERR # rubocop:disable Style/GlobalStdStream
  captured_stream = Tempfile.new(stream)
  # Save original global var ($stdout/$stderr) and a dup of IO constant for restoration
  origin_gvar = (stream == "stdout") ? $stdout : $stderr
  origin_io_dup = io_const.dup
  begin
    io_const.reopen(captured_stream)
  rescue StandardError => e
    io_const.puts "[SilentStream] Unable to capture. #{e.class}: #{e.message}"
  end
  io_const.sync = true
  if stream == "stdout"
    $stdout = io_const
  else
    $stderr = io_const
  end

  yield

  begin
    io_const.flush
  rescue StandardError
    # ignore
  end
  captured_stream.rewind
  captured_stream.read
ensure
  begin
    io_const.reopen(origin_io_dup) if defined?(io_const) && io_const
    origin_io_dup.close if defined?(origin_io_dup) && origin_io_dup
  rescue StandardError
    # ignore
  end
  # Unexpected, and not reasonably testable.
  # :nocov:
  raise "Expected the global variable to exist" unless defined?(origin_gvar)
  # :nocov:

  if stream == "stdout"
    $stdout = origin_gvar
  else
    $stderr = origin_gvar
  end
  if defined?(captured_stream) && captured_stream
    begin
      captured_stream.close
    rescue
      nil
    end
    begin
      captured_stream.unlink
    rescue
      nil
    end
  end
end

#quietly { ... } ⇒ Object

Note:

This method is not thread-safe.

Silences both STDOUT and STDERR, even for subprocesses.

quietly { system ‘bundle install’ }

rubocop:disable Style/GlobalStdStream

Yields:

  • Work to perform while both streams are silenced.

Returns:

  • (Object)

    The block’s return value.



198
199
200
201
202
# File 'lib/silent_stream.rb', line 198

def quietly(&block)
  silence_stream(STDOUT) do
    silence_stream(STDERR, &block)
  end
end

#silence_stderr { ... } ⇒ Object

This method is not thread-safe.

Yields:

  • Work to perform while STDERR is silenced.

Returns:

  • (Object)

    The block’s return value.



78
79
80
# File 'lib/silent_stream.rb', line 78

def silence_stderr(&block)
  silence_stream(STDERR, &block)
end

#silence_stream(stream) { ... } ⇒ Object

Note:

This method is not thread-safe.

Silences any stream for the duration of the block.

silence_stream(STDOUT) do
puts ‘This will never be seen’
end

puts ‘But this will’

Parameters:

  • stream (IO)

    The stream to silence (e.g., STDOUT or STDERR).

Yields:

  • Work to perform while the stream is silenced.

Returns:

  • (Object)

    The block’s return value.



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/silent_stream.rb', line 94

def silence_stream(stream)
  old_stream = stream.dup
  begin
    stream.reopen(SILENT_STREAM_NULL_DEVICE, "a+")
  rescue StandardError => e
    stream.puts "[SilentStream] Unable to silence. #{e.class}: #{e.message}"
  end
  stream.sync = true
  yield
ensure
  stream.reopen(old_stream)
  old_stream.close
end