tcr 0.2.1 → 0.3.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 +5 -5
- data/.github/workflows/tcr.yml +47 -0
- data/lib/tcr/recordable_tcp_socket.rb +54 -24
- data/lib/tcr/version.rb +1 -1
- data/lib/tcr.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/tcr_spec.rb +111 -79
- metadata +7 -8
- data/.travis.yml +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ab0a471bccd0a80a5d75bec6909dc521072b2ad666af34935f1d6ab596d9f2b4
|
|
4
|
+
data.tar.gz: eb6d9817d928173edd5354dc206fe7dd3e35a0f38ffde356e0e6e233e0a58431
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cedcebdd9373876a73534581ef013945ab20912ba86b71375b14de26043c135420f5140a2c1cf9d920afc9eacaa9b2f567d173c5d2525aa60db19ac97f43f606
|
|
7
|
+
data.tar.gz: d41afdda3c2d24c85e461db3247fed88ed5c9019e0929251c03b199b37dc3dc7a261796c3266041aea90ffbdf9b0c6053a2e627a9e7b7258382430db85df66a9
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: TCR Spec Suite
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
ruby:
|
|
16
|
+
- "2.0.0"
|
|
17
|
+
- "2.1.9"
|
|
18
|
+
- "2.2.10"
|
|
19
|
+
- "2.3.7"
|
|
20
|
+
- "2.4.4"
|
|
21
|
+
- "2.5.1"
|
|
22
|
+
- "2.7.3"
|
|
23
|
+
- "3.0.1"
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v2
|
|
27
|
+
|
|
28
|
+
- name: Initial setup
|
|
29
|
+
uses: ruby/setup-ruby@v1
|
|
30
|
+
with:
|
|
31
|
+
ruby-version: ${{ matrix.ruby }}
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: bundle install
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: bundle exec rspec
|
|
38
|
+
|
|
39
|
+
testall:
|
|
40
|
+
if: ${{ always() }}
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
name: Test (matrix)
|
|
43
|
+
needs: [test]
|
|
44
|
+
steps:
|
|
45
|
+
- name: Check status of all test runs
|
|
46
|
+
if: ${{ needs.test.result != 'success' }}
|
|
47
|
+
run: exit 1
|
|
@@ -5,46 +5,55 @@ require 'thread'
|
|
|
5
5
|
|
|
6
6
|
module TCR
|
|
7
7
|
class RecordableTCPSocket
|
|
8
|
-
attr_reader :live, :socket
|
|
9
|
-
attr_accessor :recording
|
|
8
|
+
attr_reader :live, :socket, :recording
|
|
10
9
|
|
|
11
10
|
def initialize(address, port, cassette)
|
|
12
11
|
raise TCR::NoCassetteError.new unless TCR.cassette
|
|
13
12
|
|
|
14
13
|
@read_lock = Queue.new
|
|
14
|
+
@recording = cassette.next_session
|
|
15
|
+
@live = cassette.recording?
|
|
15
16
|
|
|
16
|
-
if
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if live
|
|
18
|
+
begin
|
|
19
|
+
@socket = TCPSocket.real_open(address, port)
|
|
20
|
+
rescue => e
|
|
21
|
+
recording << ["error", Marshal.dump(e)]
|
|
22
|
+
raise
|
|
23
|
+
end
|
|
19
24
|
else
|
|
20
|
-
@live = false
|
|
21
25
|
@closed = false
|
|
26
|
+
check_recording_for_errors
|
|
22
27
|
end
|
|
23
|
-
@recording = cassette.next_session
|
|
24
28
|
end
|
|
25
29
|
|
|
26
|
-
def read(
|
|
27
|
-
_read(:read,
|
|
30
|
+
def read(*args)
|
|
31
|
+
_read(:read, args)
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def getc(*args)
|
|
31
|
-
_read(:getc,
|
|
35
|
+
_read(:getc, args)
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def gets(*args)
|
|
35
|
-
_read(:gets,
|
|
39
|
+
_read(:gets, args)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def read_nonblock(*args)
|
|
39
|
-
_read(:read_nonblock,
|
|
43
|
+
_read(:read_nonblock, args, blocking: false)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def print(str, *args)
|
|
47
|
+
_write(:print, str, args)
|
|
40
48
|
end
|
|
41
49
|
|
|
42
|
-
def
|
|
43
|
-
_write(:
|
|
50
|
+
def write(str, *args)
|
|
51
|
+
_write(:write, str, args)
|
|
52
|
+
str.length
|
|
44
53
|
end
|
|
45
54
|
|
|
46
|
-
def
|
|
47
|
-
_write(:
|
|
55
|
+
def write_nonblock(str, *args)
|
|
56
|
+
_write(:write_nonblock, str, args)
|
|
48
57
|
str.length
|
|
49
58
|
end
|
|
50
59
|
|
|
@@ -75,6 +84,10 @@ module TCR
|
|
|
75
84
|
|
|
76
85
|
private
|
|
77
86
|
|
|
87
|
+
def check_recording_for_errors
|
|
88
|
+
raise Marshal.load(recording.first.last) if recording.first.first == "error"
|
|
89
|
+
end
|
|
90
|
+
|
|
78
91
|
def _intercept_socket
|
|
79
92
|
if @socket
|
|
80
93
|
@socket = yield @socket
|
|
@@ -91,10 +104,10 @@ module TCR
|
|
|
91
104
|
@read_lock << 1
|
|
92
105
|
end
|
|
93
106
|
|
|
94
|
-
def _write(method, data)
|
|
107
|
+
def _write(method, data, args)
|
|
95
108
|
if live
|
|
96
109
|
payload = data.dup if !data.is_a?(Symbol)
|
|
97
|
-
|
|
110
|
+
_delegate_call(method, args.unshift(payload))
|
|
98
111
|
recording << ["write", data.dup]
|
|
99
112
|
else
|
|
100
113
|
direction, data = recording.shift
|
|
@@ -103,14 +116,11 @@ module TCR
|
|
|
103
116
|
end
|
|
104
117
|
end
|
|
105
118
|
|
|
106
|
-
def _read(method,
|
|
107
|
-
blocking = true
|
|
108
|
-
if args.last.is_a?(::Hash)
|
|
109
|
-
blocking = args.pop.fetch(:blocking, true)
|
|
110
|
-
end
|
|
119
|
+
def _read(method, args, opts = {})
|
|
120
|
+
blocking = opts.fetch(:blocking, true)
|
|
111
121
|
|
|
112
122
|
if live
|
|
113
|
-
data =
|
|
123
|
+
data = _delegate_call(method, args)
|
|
114
124
|
payload = data.dup if !data.is_a?(Symbol)
|
|
115
125
|
recording << ["read", payload]
|
|
116
126
|
else
|
|
@@ -122,6 +132,15 @@ module TCR
|
|
|
122
132
|
data
|
|
123
133
|
end
|
|
124
134
|
|
|
135
|
+
def _delegate_call(method, args)
|
|
136
|
+
if RUBY_VERSION >= "2.7" && Hash === args.last
|
|
137
|
+
kwargs = args.pop
|
|
138
|
+
@socket.__send__(method, *args, **kwargs)
|
|
139
|
+
else
|
|
140
|
+
@socket.__send__(method, *args)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
125
144
|
def _ensure_direction(desired, actual)
|
|
126
145
|
raise TCR::DirectionMismatchError.new("Expected to '#{desired}' but next in recording was '#{actual}'") unless desired == actual
|
|
127
146
|
end
|
|
@@ -138,6 +157,14 @@ module TCR
|
|
|
138
157
|
end
|
|
139
158
|
end
|
|
140
159
|
|
|
160
|
+
def ssl_version
|
|
161
|
+
""
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def cipher
|
|
165
|
+
[]
|
|
166
|
+
end
|
|
167
|
+
|
|
141
168
|
def sync_close=(arg)
|
|
142
169
|
true
|
|
143
170
|
end
|
|
@@ -161,6 +188,9 @@ module TCR
|
|
|
161
188
|
def session=(args)
|
|
162
189
|
end
|
|
163
190
|
|
|
191
|
+
def hostname=(args)
|
|
192
|
+
end
|
|
193
|
+
|
|
164
194
|
def io
|
|
165
195
|
self
|
|
166
196
|
end
|
data/lib/tcr/version.rb
CHANGED
data/lib/tcr.rb
CHANGED
|
@@ -88,11 +88,11 @@ class Socket
|
|
|
88
88
|
class << self
|
|
89
89
|
alias_method :real_tcp, :tcp
|
|
90
90
|
|
|
91
|
-
def tcp(host, port, socket_opts)
|
|
91
|
+
def tcp(host, port, *socket_opts)
|
|
92
92
|
if TCR.configuration.hook_tcp_ports.include?(port)
|
|
93
93
|
TCR::RecordableTCPSocket.new(host, port, TCR.cassette)
|
|
94
94
|
else
|
|
95
|
-
real_tcp(host, port, socket_opts)
|
|
95
|
+
real_tcp(host, port, *socket_opts)
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/tcr_spec.rb
CHANGED
|
@@ -25,54 +25,91 @@ RSpec.describe TCR do
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
describe ".configuration" do
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
it "has a default cassette location configured" do
|
|
29
|
+
TCR.configuration.cassette_library_dir.should == "fixtures/tcr_cassettes"
|
|
30
|
+
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
it "has an empty list of hook ports by default" do
|
|
33
|
+
TCR.configuration.hook_tcp_ports.should == []
|
|
34
|
+
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
it "defaults to erroring on read/write mismatch access" do
|
|
37
|
+
TCR.configuration.block_for_reads.should be_falsey
|
|
38
|
+
end
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
it "defaults to hit all to false" do
|
|
41
|
+
TCR.configuration.hit_all.should be_falsey
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
44
|
|
|
45
45
|
describe ".configure" do
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
context "with cassette_library_dir option" do
|
|
47
|
+
it "configures cassette location" do
|
|
48
|
+
expect {
|
|
49
|
+
TCR.configure { |c| c.cassette_library_dir = "some/dir" }
|
|
50
|
+
}.to change{ TCR.configuration.cassette_library_dir }.from("fixtures/tcr_cassettes").to("some/dir")
|
|
51
|
+
end
|
|
50
52
|
end
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
context "with hook_tcp_ports option" do
|
|
55
|
+
it "configures tcp ports to hook" do
|
|
56
|
+
expect {
|
|
57
|
+
TCR.configure { |c| c.hook_tcp_ports = [2525] }
|
|
58
|
+
}.to change{ TCR.configuration.hook_tcp_ports }.from([]).to([2525])
|
|
59
|
+
end
|
|
56
60
|
end
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
context "with block_for_reads option" do
|
|
63
|
+
before(:each) {
|
|
64
|
+
TCR.configure { |c|
|
|
65
|
+
c.hook_tcp_ports = [9999]
|
|
66
|
+
c.cassette_library_dir = '.'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
it "configures allowing a blocking read mode" do
|
|
71
|
+
expect {
|
|
72
|
+
TCR.configure { |c| c.block_for_reads = true }
|
|
73
|
+
}.to change{ TCR.configuration.block_for_reads }.from(false).to(true)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "blocks read thread until data is available instead of raising mismatch error" do
|
|
60
77
|
TCR.configure { |c| c.block_for_reads = true }
|
|
61
|
-
|
|
62
|
-
end
|
|
78
|
+
reads = Queue.new
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
TCR.use_cassette("spec/fixtures/block_for_reads") do
|
|
81
|
+
sock = TCPSocket.open("google.com", 9999)
|
|
82
|
+
|
|
83
|
+
t = Thread.new do
|
|
84
|
+
reads << sock.gets
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
expect(reads.size).to eq(0)
|
|
88
|
+
sock.print("hello\n")
|
|
89
|
+
t.value
|
|
90
|
+
expect(reads.size).to eq(1)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "when disabled" do
|
|
95
|
+
it "raises mismatch error" do
|
|
96
|
+
TCR.use_cassette("spec/fixtures/block_for_reads") do
|
|
97
|
+
sock = TCPSocket.open("google.com", 9999)
|
|
98
|
+
expect {
|
|
99
|
+
Timeout::timeout(1) { sock.gets }
|
|
100
|
+
}.to raise_error(TCR::DirectionMismatchError)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
68
104
|
end
|
|
69
|
-
end
|
|
70
105
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
context "with hit_all option" do
|
|
107
|
+
it "configures to check if all sessions were hit" do
|
|
108
|
+
expect {
|
|
109
|
+
TCR.configure { |c| c.hit_all = true }
|
|
110
|
+
}.to change{ TCR.configuration.hit_all }.from(false).to(true)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
76
113
|
end
|
|
77
114
|
|
|
78
115
|
describe ".turned_off" do
|
|
@@ -98,44 +135,6 @@ RSpec.describe TCR do
|
|
|
98
135
|
end
|
|
99
136
|
end
|
|
100
137
|
|
|
101
|
-
describe "block_for_reads" do
|
|
102
|
-
before(:each) {
|
|
103
|
-
TCR.configure { |c|
|
|
104
|
-
c.hook_tcp_ports = [9999]
|
|
105
|
-
c.cassette_library_dir = '.'
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
it "blocks read thread until data is available instead of raising mismatch error" do
|
|
110
|
-
TCR.configure { |c| c.block_for_reads = true }
|
|
111
|
-
reads = Queue.new
|
|
112
|
-
|
|
113
|
-
TCR.use_cassette("spec/fixtures/block_for_reads") do
|
|
114
|
-
sock = TCPSocket.open("google.com", 9999)
|
|
115
|
-
|
|
116
|
-
t = Thread.new do
|
|
117
|
-
reads << sock.gets
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
expect(reads.size).to eq(0)
|
|
121
|
-
sock.print("hello\n")
|
|
122
|
-
t.value
|
|
123
|
-
expect(reads.size).to eq(1)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
context "when disabled" do
|
|
128
|
-
it "raises mismatch error" do
|
|
129
|
-
TCR.use_cassette("spec/fixtures/block_for_reads") do
|
|
130
|
-
sock = TCPSocket.open("google.com", 9999)
|
|
131
|
-
expect {
|
|
132
|
-
Timeout::timeout(1) { sock.gets }
|
|
133
|
-
}.to raise_error(TCR::DirectionMismatchError)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
138
|
describe ".use_cassette" do
|
|
140
139
|
before(:each) {
|
|
141
140
|
TCR.configure { |c|
|
|
@@ -144,6 +143,13 @@ RSpec.describe TCR do
|
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
|
|
146
|
+
it "MUST be used when connecting to hooked ports (or else raises an error)" do
|
|
147
|
+
TCR.configure { |c| c.hook_tcp_ports = [2525] }
|
|
148
|
+
expect {
|
|
149
|
+
tcp_socket = TCPSocket.open("smtp.mandrillapp.com", 2525)
|
|
150
|
+
}.to raise_error(TCR::NoCassetteError)
|
|
151
|
+
end
|
|
152
|
+
|
|
147
153
|
it "requires a block to call" do
|
|
148
154
|
expect {
|
|
149
155
|
TCR.use_cassette("test")
|
|
@@ -416,9 +422,9 @@ RSpec.describe TCR do
|
|
|
416
422
|
it "raises an error if you try to playback more sessions than you previously recorded" do
|
|
417
423
|
expect {
|
|
418
424
|
TCR.use_cassette("spec/fixtures/multitest-smtp") do
|
|
419
|
-
smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525)
|
|
420
|
-
smtp = Net::SMTP.start("mail.smtp2go.com", 2525)
|
|
421
|
-
smtp = Net::SMTP.start("mail.smtp2go.com", 2525)
|
|
425
|
+
smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525, starttls: false)
|
|
426
|
+
smtp = Net::SMTP.start("mail.smtp2go.com", 2525, starttls: false)
|
|
427
|
+
smtp = Net::SMTP.start("mail.smtp2go.com", 2525, starttls: false)
|
|
422
428
|
end
|
|
423
429
|
}.to raise_error(TCR::NoMoreSessionsError)
|
|
424
430
|
end
|
|
@@ -426,21 +432,47 @@ RSpec.describe TCR do
|
|
|
426
432
|
it "raises an error if you try to playback less sessions than you previously recorded" do
|
|
427
433
|
expect {
|
|
428
434
|
TCR.use_cassette("spec/fixtures/multitest-extra-smtp", hit_all: true) do
|
|
429
|
-
smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525)
|
|
435
|
+
smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525, starttls: false)
|
|
430
436
|
end
|
|
431
437
|
}.to raise_error(TCR::ExtraSessionsError)
|
|
432
438
|
end
|
|
433
439
|
end
|
|
434
440
|
end
|
|
435
441
|
|
|
442
|
+
context "when TCPSocket.open raises an error during recording" do
|
|
443
|
+
before do
|
|
444
|
+
TCR.configure { |c|
|
|
445
|
+
c.format = "yaml" # JSON borks on binary strings
|
|
446
|
+
c.hook_tcp_ports = [143]
|
|
447
|
+
c.cassette_library_dir = "."
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
# record cassette
|
|
451
|
+
TCR.use_cassette("test") do
|
|
452
|
+
expect { Net::IMAP.new(nil) }.to raise_error(SystemCallError)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
it "records error to cassette" do
|
|
457
|
+
expect(File.exist?('test.yaml')).to be(true)
|
|
458
|
+
expect(File.read('test.yaml')).not_to be_empty
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
it "re-raises the error during replay" do
|
|
462
|
+
TCR.use_cassette("test") do
|
|
463
|
+
expect { Net::IMAP.new(nil) }.to raise_error(SystemCallError)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
436
468
|
it "replaces sockets created with Socket.tcp" do
|
|
437
469
|
TCR.configure { |c|
|
|
438
|
-
c.hook_tcp_ports = [
|
|
470
|
+
c.hook_tcp_ports = [80]
|
|
439
471
|
c.cassette_library_dir = "."
|
|
440
472
|
}
|
|
441
473
|
|
|
442
474
|
TCR.use_cassette("test") do
|
|
443
|
-
sock = Socket.tcp("
|
|
475
|
+
sock = Socket.tcp("google.com", 80)
|
|
444
476
|
expect(sock).to be_a(TCR::RecordableTCPSocket)
|
|
445
477
|
end
|
|
446
478
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tcr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rob Forman
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -87,9 +87,9 @@ executables: []
|
|
|
87
87
|
extensions: []
|
|
88
88
|
extra_rdoc_files: []
|
|
89
89
|
files:
|
|
90
|
+
- ".github/workflows/tcr.yml"
|
|
90
91
|
- ".gitignore"
|
|
91
92
|
- ".rvmrc"
|
|
92
|
-
- ".travis.yml"
|
|
93
93
|
- Gemfile
|
|
94
94
|
- LICENSE.txt
|
|
95
95
|
- README.md
|
|
@@ -118,7 +118,7 @@ files:
|
|
|
118
118
|
homepage: ''
|
|
119
119
|
licenses: []
|
|
120
120
|
metadata: {}
|
|
121
|
-
post_install_message:
|
|
121
|
+
post_install_message:
|
|
122
122
|
rdoc_options: []
|
|
123
123
|
require_paths:
|
|
124
124
|
- lib
|
|
@@ -133,9 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
133
133
|
- !ruby/object:Gem::Version
|
|
134
134
|
version: '0'
|
|
135
135
|
requirements: []
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
signing_key:
|
|
136
|
+
rubygems_version: 3.4.7
|
|
137
|
+
signing_key:
|
|
139
138
|
specification_version: 4
|
|
140
139
|
summary: TCR is a lightweight VCR for TCP sockets.
|
|
141
140
|
test_files:
|