angry_io 0.1.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +111 -0
- data/lib/angry_io/enable_for_ci_true.rb +16 -0
- data/lib/angry_io/minitest.rb +42 -0
- data/lib/angry_io/rspec.rb +29 -0
- data/lib/angry_io/version.rb +5 -0
- data/lib/angry_io.rb +57 -0
- metadata +56 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8333b37ab325a405861724fec5d256e85255551879736909482389b3a203c14c
|
|
4
|
+
data.tar.gz: 2b3f00b29dc6573e4ddf3ef33b7c91856ee49d56d31d2a0b0b293a59ca02adec
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ef3525263035d341ada9c302472c468676a2196df6a1b57ed9b67d09e7c2d1bde7148dd18ca6fc6ed888e78bff6949ea97f4d645914d08e3a21094db77c306b4
|
|
7
|
+
data.tar.gz: 3ca41798772a5e17b5406b4d70446edb08eedffb21f5c62c1115c22e849e1b397cbe3ea5dec41bf44e3b84da4fcc0fcfc5e565396294226d8fe922ab9bc72e54
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benjamin Quorning
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# AngryIo
|
|
2
|
+
|
|
3
|
+
Friends don't let friends write tests or specs that outputs to stdout/stderr. When you look at your test output, you should only see green dots, right?
|
|
4
|
+
|
|
5
|
+
AngryIo replaces `$stdout` and `$stderr` during your test suite with an IO that _raises on write_, so accidental output fails loudly instead of quietly cluttering your output. It ships self-registering adapters for both RSpec and Minitest, gated by a configurable `enabled` callable.
|
|
6
|
+
|
|
7
|
+
The name and the default opt-out metadata are a nod to [minitest](https://github.com/minitest/minitest)'s `i_suck_and_my_tests_are_order_dependent!`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
group :test do
|
|
15
|
+
gem "angry_io", require: "angry_io/enable_for_ci_true"
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install angry_io
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
The simplest setup is to point the Gemfile `require:` at `angry_io/enable_for_ci_true`. That file turns AngryIo on only when the `CI` environment variable is `"true"` — a convention set by GitHub Actions, GitLab CI, CircleCI, Travis, and others — and wires up whichever test framework is loaded (RSpec or Minitest); the other is a no-op.
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# Gemfile
|
|
31
|
+
gem "angry_io", require: "angry_io/enable_for_ci_true"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
That's it. Under `CI=true`, every test that writes to `$stdout` or `$stderr` raises:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
IOError: not opened for writing
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Opting out
|
|
41
|
+
|
|
42
|
+
While onboarding this gem, some tests will likely already be writing to stdout. Opt them out per-test until they are fixed:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# RSpec
|
|
46
|
+
it "prints a deprecation", :i_absolutely_need_to_write_to_stdout do
|
|
47
|
+
puts "deprecated!"
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# Minitest — opt out a whole test class
|
|
53
|
+
class PrintTest < Minitest::Test
|
|
54
|
+
i_absolutely_need_to_write_to_stdout!
|
|
55
|
+
|
|
56
|
+
def test_prints
|
|
57
|
+
puts "ok"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Manual setup
|
|
63
|
+
|
|
64
|
+
If you'd rather control the gate yourself, require an adapter directly. `enabled` defaults to always-on, so it's most useful as a kill switch for environments where you need real output:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# spec_helper.rb (RSpec) or test_helper.rb (Minitest)
|
|
68
|
+
require "angry_io/rspec" # or "angry_io/minitest"
|
|
69
|
+
|
|
70
|
+
# On by default; turn it off in environments where you need real output.
|
|
71
|
+
AngryIo.configure do |config|
|
|
72
|
+
config.enabled = -> { ENV["ANGRY_IO"] != "off" }
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Requiring `angry_io/rspec` or `angry_io/minitest` self-registers the hook if that framework is loaded; requiring plain `angry_io` gives you just the `AngryIo::Stream` class with no side effects.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
`AngryIo.configure` yields a config struct with two fields:
|
|
81
|
+
|
|
82
|
+
| Field | Default | Description |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `enabled` | `-> { true }` | A callable returning whether AngryIo is active. Invoked once per test, so it can read env vars or feature flags live. |
|
|
85
|
+
| `opt_out_metadata` | `:i_absolutely_need_to_write_to_stdout` | The RSpec metadata symbol that opts an example out. |
|
|
86
|
+
|
|
87
|
+
## How it works
|
|
88
|
+
|
|
89
|
+
`AngryIo::Stream` is a `StringIO` backed by a frozen empty string, which makes `StringIO` refuse writes with an `IOError`. Around each test, the adapter swaps `$stdout` and `$stderr` to fresh `AngryIo::Stream` instances and restores the originals (closing the buffers) in an `ensure`.
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the test suite (Minitest + RSpec, both dogfooding the gem's own adapters) and StandardRB.
|
|
94
|
+
|
|
95
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
96
|
+
|
|
97
|
+
### Releasing
|
|
98
|
+
|
|
99
|
+
Releases are automated. Bump the version in `lib/angry_io/version.rb`, commit, and merge to `main` — the `Publish to RubyGems.org` workflow builds the gem, creates and pushes the `vX.Y.Z` tag, and pushes the `.gem` file to [rubygems.org](https://rubygems.org) via trusted publishing (OIDC, no stored API key). You can also trigger a release manually from the Actions tab via `workflow_dispatch`.
|
|
100
|
+
|
|
101
|
+
## Contributing
|
|
102
|
+
|
|
103
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/bquorning/angry_io.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
108
|
+
|
|
109
|
+
## Code of Conduct
|
|
110
|
+
|
|
111
|
+
Everyone interacting in the AngryIo project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/bquorning/angry_io/blob/main/CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Require this file from your Gemfile (+require: "angry_io/enable_for_ci_true"+)
|
|
4
|
+
# to turn AngryIo on only when the +CI+ environment variable is +"true"+ — a
|
|
5
|
+
# convention set by GitHub Actions, GitLab CI, CircleCI, Travis, and others.
|
|
6
|
+
# Whichever test framework is loaded (RSpec or Minitest) wires itself up; the
|
|
7
|
+
# other is a no-op.
|
|
8
|
+
|
|
9
|
+
require "angry_io"
|
|
10
|
+
|
|
11
|
+
AngryIo.configure do |config|
|
|
12
|
+
config.enabled = -> { ENV["CI"] == "true" }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
require "angry_io/rspec"
|
|
16
|
+
require "angry_io/minitest"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "angry_io"
|
|
4
|
+
|
|
5
|
+
# Prepends an override of +Minitest::Test#run+ that swaps $stdout/$stderr to
|
|
6
|
+
# AngryIo::Stream instances around each test. A test class opts out by calling
|
|
7
|
+
# +i_absolutely_need_to_write_to_stdout!+ in its body — a nod to minitest's own
|
|
8
|
+
# +i_suck_and_my_tests_are_order_dependent!+. Force-loads Minitest so
|
|
9
|
+
# registration works regardless of Bundler.require ordering; no-op if Minitest
|
|
10
|
+
# isn't installed.
|
|
11
|
+
module AngryIo
|
|
12
|
+
module Minitest
|
|
13
|
+
module Adapter
|
|
14
|
+
def run
|
|
15
|
+
AngryIo.around_streams(opted_out: self.class.i_absolutely_need_to_write_to_stdout?) { super }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
def i_absolutely_need_to_write_to_stdout!
|
|
21
|
+
@i_absolutely_need_to_write_to_stdout = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def i_absolutely_need_to_write_to_stdout?
|
|
25
|
+
@i_absolutely_need_to_write_to_stdout ||= false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.setup!
|
|
30
|
+
test = ::Minitest::Test
|
|
31
|
+
test.extend(ClassMethods)
|
|
32
|
+
test.prepend(Adapter)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
require "minitest"
|
|
39
|
+
AngryIo::Minitest.setup!
|
|
40
|
+
rescue LoadError
|
|
41
|
+
# Minitest isn't installed; nothing to wire up.
|
|
42
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "angry_io"
|
|
4
|
+
|
|
5
|
+
# Registers an +around+ hook that swaps $stdout/$stderr to AngryIo::Stream
|
|
6
|
+
# instances during each example, unless the example opts out via the configured
|
|
7
|
+
# metadata (default +:i_absolutely_need_to_write_to_stdout+). Force-loads
|
|
8
|
+
# rspec-core so registration works regardless of Bundler.require ordering;
|
|
9
|
+
# no-op if RSpec isn't installed.
|
|
10
|
+
module AngryIo
|
|
11
|
+
module RSpec
|
|
12
|
+
def self.setup!
|
|
13
|
+
::RSpec.configure do |config|
|
|
14
|
+
config.around do |example|
|
|
15
|
+
opt_out = example.metadata.key?(AngryIo.config.opt_out_metadata) &&
|
|
16
|
+
example.metadata[AngryIo.config.opt_out_metadata]
|
|
17
|
+
AngryIo.around_streams(opted_out: opt_out) { example.run }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
require "rspec/core"
|
|
26
|
+
AngryIo::RSpec.setup!
|
|
27
|
+
rescue LoadError
|
|
28
|
+
# RSpec isn't installed; nothing to wire up.
|
|
29
|
+
end
|
data/lib/angry_io.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
require_relative "angry_io/version"
|
|
5
|
+
|
|
6
|
+
module AngryIo
|
|
7
|
+
# An IO that raises when you write to it, so tests can't silently pollute
|
|
8
|
+
# stdout/stderr. Backed by a frozen empty string, which makes StringIO
|
|
9
|
+
# refuse writes with an IOError ("not opened for writing").
|
|
10
|
+
class Stream < StringIO
|
|
11
|
+
def initialize
|
|
12
|
+
super(-"")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Config = Struct.new(:enabled, :opt_out_metadata) do
|
|
17
|
+
def initialize
|
|
18
|
+
self.enabled = -> { true }
|
|
19
|
+
self.opt_out_metadata = :i_absolutely_need_to_write_to_stdout
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@config = Config.new
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
attr_reader :config
|
|
27
|
+
|
|
28
|
+
def configure
|
|
29
|
+
yield @config
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Swap $stdout and $stderr to AngryIo::Stream instances around the given
|
|
33
|
+
# block, restoring them (and closing the buffers) in an ensure. No-ops when
|
|
34
|
+
# +opted_out+ is true or when +config.enabled+ returns false, so the block
|
|
35
|
+
# runs untouched.
|
|
36
|
+
def around_streams(opted_out: false)
|
|
37
|
+
return yield if opted_out
|
|
38
|
+
return yield unless config.enabled.call
|
|
39
|
+
|
|
40
|
+
original_stdout = $stdout
|
|
41
|
+
original_stderr = $stderr
|
|
42
|
+
stdout_buffer = Stream.new
|
|
43
|
+
stderr_buffer = Stream.new
|
|
44
|
+
$stdout = stdout_buffer
|
|
45
|
+
$stderr = stderr_buffer
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
yield
|
|
49
|
+
ensure
|
|
50
|
+
$stdout = original_stdout
|
|
51
|
+
$stderr = original_stderr
|
|
52
|
+
stdout_buffer.close
|
|
53
|
+
stderr_buffer.close
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: angry_io
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Benjamin Quorning
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: AngryIo replaces $stdout/$stderr during tests with an IO that raises
|
|
13
|
+
on write, so accidental output fails loudly instead of cluttering CI logs. Ships
|
|
14
|
+
RSpec and Minitest adapters that self-register, gated by a configurable enabled
|
|
15
|
+
callable.
|
|
16
|
+
email:
|
|
17
|
+
- bquorning@zendesk.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- lib/angry_io.rb
|
|
26
|
+
- lib/angry_io/enable_for_ci_true.rb
|
|
27
|
+
- lib/angry_io/minitest.rb
|
|
28
|
+
- lib/angry_io/rspec.rb
|
|
29
|
+
- lib/angry_io/version.rb
|
|
30
|
+
homepage: https://github.com/bquorning/angry_io
|
|
31
|
+
licenses:
|
|
32
|
+
- MIT
|
|
33
|
+
metadata:
|
|
34
|
+
allowed_push_host: https://rubygems.org
|
|
35
|
+
homepage_uri: https://github.com/bquorning/angry_io
|
|
36
|
+
source_code_uri: https://github.com/bquorning/angry_io
|
|
37
|
+
changelog_uri: https://github.com/bquorning/angry_io/blob/main/CHANGELOG.md
|
|
38
|
+
rubygems_mfa_required: 'true'
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 3.2.0
|
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements: []
|
|
53
|
+
rubygems_version: 4.0.16
|
|
54
|
+
specification_version: 4
|
|
55
|
+
summary: An IO that raises on write, to keep tests from polluting stdout/stderr.
|
|
56
|
+
test_files: []
|