tcr_revived 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7a861087ed84e4a776b8ec9f18710eba284c88de8cc0b6db866334a6a01effe8
4
+ data.tar.gz: 2889aca62e2f9a4612255c42e7ec1c7d316f55cfad008dcc38d4b637fc6681d9
5
+ SHA512:
6
+ metadata.gz: 7cf287e455aac08c21c3133d81b8df1e4da397735e706e0b28d0eae018e5a91c6fcb7188ec4fb150da858c772fcf6c2a418e5825d35dab5e4f5c76ed2d6dd3e4
7
+ data.tar.gz: 40105382e6c1c8d100556d41073f67b5e40006b310842a0590f8075aa5a5a99e52142807371d1c99585d9abdb47ae0b136ed647c75fc77337f16c4c5f4f225d8
data/.rubocop.yml ADDED
@@ -0,0 +1,34 @@
1
+ inherit_gem:
2
+ rubocop-espago: rubocop.yml
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 2.6
6
+ Exclude:
7
+ - 'spec/**/*'
8
+
9
+ Gemspec/DependencyVersion:
10
+ Enabled: false
11
+
12
+ Style/DocumentationMethod:
13
+ Enabled: false
14
+
15
+ Style/Documentation:
16
+ Enabled: false
17
+
18
+ Style/MissingElse:
19
+ Enabled: false
20
+
21
+ Security/MarshalLoad:
22
+ Enabled: false
23
+
24
+ Style/ImplicitRuntimeError:
25
+ Enabled: false
26
+
27
+ Security/YAMLLoad:
28
+ Enabled: false
29
+
30
+ Bundler/GemComment:
31
+ Enabled: false
32
+
33
+ Bundler/GemVersion:
34
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Change Log
2
+
3
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.3.0] - 2022-10-18
9
+
10
+ This is the initial version of this fork.
11
+
12
+ ### Changed
13
+
14
+ - Refactored the code to be more legible
15
+
16
+ ### Added
17
+
18
+ - Support for Ruby 3.0
19
+ - Support for Ruby 3.1
20
+ - Rubocop configuration
21
+ - GitHub workflow for tests on 4 last major Ruby versions (2.6, 2.7, 3.0, 3.1)
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in tcr.gemspec
6
+ gemspec
7
+
8
+ gem 'bundler-audit'
9
+ gem 'rubocop-espago'
10
+ gem 'solargraph'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rob Forman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # TCR revived (TCP + VCR)
2
+
3
+ [![license](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE)
4
+ [![CI badge](https://github.com/Verseth/tcr_revived/actions/workflows/ci_ruby.yml/badge.svg)](https://github.com/Verseth/tcr_revived/actions/workflows/ci_ruby.yml)
5
+
6
+ ## Why?
7
+
8
+ The original version of this gem seemed to have been unmaintained at least since 2018.
9
+
10
+ This fork aims to bring support for Ruby 3.0 and 3.1. It should be a drop-in replacement.
11
+
12
+ ## Original docs
13
+
14
+ TCR is a *very* lightweight version of [VCR](https://github.com/vcr/vcr) for TCP sockets.
15
+
16
+ Currently used for recording 'net/smtp', 'net/imap' and 'net/ldap' interactions so only a few of the TCPSocket methods are recorded out.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ gem 'tcr_revived', require: 'tcr'
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install tcr_revived
31
+
32
+ ## Usage
33
+
34
+ ```ruby
35
+ require 'test/unit'
36
+ require 'tcr'
37
+
38
+ TCR.configure do |c|
39
+ c.cassette_library_dir = 'fixtures/tcr_cassettes'
40
+ c.hook_tcp_ports = [2525]
41
+ end
42
+
43
+ class TCRTest < Test::Unit::TestCase
44
+ def test_example_dot_com
45
+ TCR.use_cassette('mandrill_smtp') do
46
+ tcp_socket = TCPSocket.open("smtp.mandrillapp.com", 2525)
47
+ io = Net::InternetMessageIO.new(tcp_socket)
48
+ assert_match /220 smtp.mandrillapp.com ESMTP/, io.readline
49
+ end
50
+ end
51
+ end
52
+ ```
53
+
54
+ Run this test once, and TCR will record the tcp interactions to fixtures/tcr_cassettes/google_smtp.json.
55
+
56
+ ```json
57
+ [
58
+ [
59
+ [
60
+ "read",
61
+ "220 smtp.mandrillapp.com ESMTP\r\n"
62
+ ]
63
+ ]
64
+ ]
65
+ ```
66
+
67
+ Run it again, and TCR will replay the interactions from json when the tcp request is made. This test is now fast (no real TCP requests are made anymore), deterministic and accurate.
68
+
69
+ You can disable TCR hooking TCPSocket ports for a given block via `turned_off`:
70
+
71
+ ```ruby
72
+ TCR.turned_off do
73
+ tcp_socket = TCPSocket.open("smtp.mandrillapp.com", 2525)
74
+ end
75
+ ```
76
+
77
+ To make sure all external calls really happened use `hit_all` option:
78
+
79
+ ```ruby
80
+ class TCRTest < Test::Unit::TestCase
81
+ def test_example_dot_com
82
+ TCR.use_cassette('mandrill_smtp', hit_all: true) do
83
+ # There are previously recorded external calls.
84
+ # ExtraSessionsError will be raised as a result.
85
+ end
86
+ end
87
+ end
88
+ ```
89
+
90
+ You can also use the configuration option:
91
+
92
+ ```ruby
93
+ TCR.configure do |c|
94
+ c.hit_all = true
95
+ end
96
+ ```
97
+
98
+ The following storage formats are supported:
99
+
100
+ - JSON (default)
101
+ - YAML
102
+ - Marshal (recommended for binary data transfer like LDAP)
103
+
104
+ You can configure them via:
105
+
106
+ ```ruby
107
+ TCR.configure do |c|
108
+ c.format = 'json'
109
+ # or
110
+ c.format = 'yaml'
111
+ # or
112
+ c.format = 'marshal'
113
+ end
114
+ ```
115
+
116
+
117
+ ## Contributing
118
+
119
+ 1. Fork it
120
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
121
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
122
+ 4. Push to the branch (`git push origin my-new-feature`)
123
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ ::RSpec::Core::RakeTask.new('spec')
7
+
8
+ task default: :spec
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module TCR
6
+ class Cassette
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+
12
+ if recording?
13
+ verify_cassette_path_is_writable
14
+ @sessions = []
15
+ else
16
+ @sessions = unmarshal(File.read(filename))
17
+ end
18
+ end
19
+
20
+ def recording?
21
+ @recording ||= !File.exist?(filename)
22
+ end
23
+
24
+ def next_session
25
+ if recording?
26
+ @sessions << []
27
+ @sessions.last
28
+ else
29
+ raise NoMoreSessionsError if @sessions.empty?
30
+
31
+ @sessions.shift
32
+ end
33
+ end
34
+
35
+ def save
36
+ return unless recording?
37
+
38
+ File.write(filename, marshal(@sessions))
39
+ rescue Encoding::UndefinedConversionError
40
+ File.binwrite(filename, marshal(@sessions))
41
+ end
42
+
43
+ def check_hits_all_sessions
44
+ raise ExtraSessionsError if !recording? && !@sessions.empty?
45
+ end
46
+
47
+ protected
48
+
49
+ def unmarshal(content)
50
+ case TCR.configuration.format
51
+ when 'json'
52
+ JSON.parse(content)
53
+ when 'yaml'
54
+ YAML.load(content)
55
+ when 'marshal'
56
+ Marshal.load(content)
57
+ else
58
+ raise "unrecognized cassette format '#{TCR.configuration.format}'; " \
59
+ "please use one of 'json', 'yaml', or 'marshal'"
60
+ end
61
+ end
62
+
63
+ def marshal(content)
64
+ case TCR.configuration.format
65
+ when 'json'
66
+ JSON.pretty_generate(content)
67
+ when 'yaml'
68
+ YAML.dump(content)
69
+ when 'marshal'
70
+ Marshal.dump(content)
71
+ else
72
+ raise "unrecognized cassette format '#{TCR.configuration.format}'; " \
73
+ "please use one of 'json', 'yaml', or 'marshal'"
74
+ end
75
+ end
76
+
77
+ # @return [String]
78
+ def filename
79
+ ::File.expand_path("#{name}.#{TCR.configuration.format}", TCR.configuration.cassette_library_dir.to_s)
80
+ end
81
+
82
+ def verify_cassette_path_is_writable
83
+ FileUtils.mkdir_p(File.dirname(filename))
84
+ FileUtils.touch(filename)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,21 @@
1
+ # rubocop:disable Style/FrozenStringLiteralComment
2
+
3
+ module TCR
4
+ class Configuration
5
+ attr_accessor :cassette_library_dir, :hook_tcp_ports, :block_for_reads, :format, :hit_all
6
+
7
+ def initialize
8
+ reset_defaults!
9
+ end
10
+
11
+ def reset_defaults!
12
+ @cassette_library_dir = 'fixtures/tcr_cassettes'
13
+ @hook_tcp_ports = []
14
+ @block_for_reads = false
15
+ @format = 'json'
16
+ @hit_all = false
17
+ end
18
+ end
19
+ end
20
+
21
+ # rubocop:enable Style/FrozenStringLiteralComment
data/lib/tcr/errors.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TCR
4
+ class TCRError < StandardError; end
5
+ class NoCassetteError < TCRError; end
6
+ class NoMoreSessionsError < TCRError; end
7
+ class ExtraSessionsError < TCRError; end
8
+ class DirectionMismatchError < TCRError; end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # support LDAP(S) connections
4
+ if defined? ::Net::LDAP
5
+
6
+ module Net::LDAP::Connection::FixSSLSocketSyncClose
7
+ def close
8
+ super
9
+ # see: https://github.com/ruby-ldap/ruby-net-ldap/pull/314
10
+ return if io.closed?
11
+
12
+ io.close
13
+ end
14
+ end
15
+
16
+ module TCR
17
+ class RecordableTCPSocket
18
+ include Net::LDAP::Connection::GetbyteForSSLSocket
19
+ include Net::BER::BERParser
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require 'openssl'
5
+ module TCR
6
+ class RecordableTCPSocket
7
+ attr_reader :live, :socket, :recording
8
+
9
+ def initialize(address, port, cassette)
10
+ raise TCR::NoCassetteError unless TCR.cassette
11
+
12
+ @read_lock = []
13
+ @recording = cassette.next_session
14
+ @live = cassette.recording?
15
+ @config_block_for_reads = TCR.configuration.block_for_reads
16
+
17
+ if live
18
+ begin
19
+ @socket = TCPSocket.real_open(address, port)
20
+ rescue StandardError => e
21
+ recording << ['error', Marshal.dump(e)]
22
+ raise
23
+ end
24
+ else
25
+ @closed = false
26
+ check_recording_for_errors
27
+ end
28
+ end
29
+
30
+ # no-op for Ruby 3.1 compatibility
31
+ def hostname=(*args); end
32
+
33
+ def read(bytes)
34
+ _read(:read, bytes)
35
+ end
36
+
37
+ def getc(*args)
38
+ _read(:getc, *args)
39
+ end
40
+
41
+ def gets(*args)
42
+ _read(:gets, *args)
43
+ end
44
+
45
+ def read_nonblock(*args, **_rest)
46
+ _read(:read_nonblock, *args, blocking: false)
47
+ end
48
+
49
+ def print(str)
50
+ _write(:print, str)
51
+ end
52
+
53
+ def write(str)
54
+ _write(:write, str)
55
+ str.length
56
+ end
57
+
58
+ def write_nonblock(str, *_rest)
59
+ _write(:write_nonblock, str)
60
+ str.length
61
+ end
62
+
63
+ def ssl_version; end
64
+
65
+ def cipher
66
+ {}
67
+ end
68
+
69
+ def to_io
70
+ @socket.to_io if live
71
+ end
72
+
73
+ def closed?
74
+ if live
75
+ @socket.closed?
76
+ else
77
+ @closed
78
+ end
79
+ end
80
+
81
+ def close
82
+ if live
83
+ @socket.close
84
+ else
85
+ @closed = true
86
+ end
87
+ end
88
+
89
+ def setsockopt(*args); end
90
+
91
+ private
92
+
93
+ attr_reader :config_block_for_reads
94
+
95
+ def check_recording_for_errors
96
+ raise Marshal.load(recording.first.last) if recording.first.first == 'error'
97
+ end
98
+
99
+ def _intercept_socket
100
+ @socket = yield @socket if @socket
101
+ end
102
+
103
+ def _block_for_read_data
104
+ @read_lock.pop while recording.first && recording.first.first != 'read'
105
+ end
106
+
107
+ def _check_for_blocked_reads
108
+ @read_lock << 1
109
+ end
110
+
111
+ def _write(method, data)
112
+ if live
113
+ payload = data.dup unless data.is_a?(Symbol)
114
+ @socket.__send__(method, payload)
115
+ recording << ['write', data.dup]
116
+ else
117
+ direction, = recording.shift
118
+ _ensure_direction('write', direction) if direction
119
+ _check_for_blocked_reads
120
+ end
121
+ end
122
+
123
+ def _read(method, *args, blocking: true)
124
+ if live
125
+ data = @socket.__send__(method, *args)
126
+ payload = data.dup unless data.is_a?(Symbol)
127
+ recording << ['read', payload]
128
+ else
129
+ _block_for_read_data if blocking && config_block_for_reads
130
+ raise EOFError if recording.empty?
131
+
132
+ direction, data = recording.shift
133
+ _ensure_direction('read', direction)
134
+ end
135
+ data
136
+ rescue IO::EAGAINWaitReadable, OpenSSL::SSL::SSLErrorWaitReadable
137
+ retry
138
+ end
139
+
140
+ def _ensure_direction(desired, actual)
141
+ return if desired == actual
142
+
143
+ raise TCR::DirectionMismatchError, "Expected to '#{desired}' but next in recording was '#{actual}'"
144
+ end
145
+ end
146
+
147
+ class RecordableSSLSocket < SimpleDelegator
148
+ def initialize(tcr_socket)
149
+ super(tcr_socket)
150
+ tcr_socket.__send__(:_intercept_socket) do |sock|
151
+ socket = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new)
152
+ socket.sync_close = true
153
+ socket.connect
154
+ socket
155
+ end
156
+ end
157
+
158
+ def sync_close=(_arg)
159
+ true
160
+ end
161
+
162
+ def sync
163
+ self
164
+ end
165
+
166
+ def sync=(_arg)
167
+ self
168
+ end
169
+
170
+ def flush
171
+ self
172
+ end
173
+
174
+ def session
175
+ self
176
+ end
177
+
178
+ def session=(args); end
179
+
180
+ def io
181
+ self
182
+ end
183
+
184
+ def shutdown
185
+ socket.io.shutdown if live
186
+ end
187
+
188
+ def connect_nonblock(*)
189
+ self
190
+ end
191
+
192
+ def connect
193
+ self
194
+ end
195
+
196
+ def post_connection_check(*_args)
197
+ true
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TCR
4
+ VERSION = '0.3.0'
5
+ end
data/lib/tcr.rb ADDED
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tcr/cassette'
4
+ require 'tcr/configuration'
5
+ require 'tcr/errors'
6
+ require 'tcr/recordable_tcp_socket'
7
+ require 'tcr/version'
8
+ require 'socket'
9
+ require 'json'
10
+
11
+
12
+ module TCR
13
+ extend self
14
+
15
+ def configure
16
+ yield configuration
17
+ end
18
+
19
+ def configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def cassette
24
+ @cassette
25
+ end
26
+
27
+ def cassette=(val)
28
+ @cassette = val
29
+ end
30
+
31
+ def disabled
32
+ @disabled || false
33
+ end
34
+
35
+ def disabled=(val)
36
+ @disabled = val
37
+ end
38
+
39
+ def save_session; end
40
+
41
+ def use_cassette(name, options = {}, &block)
42
+ raise ArgumentError, '`TCR.use_cassette` requires a block.' unless block
43
+
44
+ TCR.cassette = Cassette.new(name)
45
+ yield
46
+ TCR.cassette.save
47
+ TCR.cassette.check_hits_all_sessions if options[:hit_all] || configuration.hit_all
48
+ ensure
49
+ TCR.cassette = nil
50
+ end
51
+
52
+ def turned_off(&block)
53
+ raise ArgumentError, '`TCR.turned_off` requires a block.' unless block
54
+
55
+ current_hook_tcp_ports = configuration.hook_tcp_ports
56
+ configuration.hook_tcp_ports = []
57
+ yield
58
+ configuration.hook_tcp_ports = current_hook_tcp_ports
59
+ end
60
+ end
61
+
62
+ # The monkey patch shim
63
+ class TCPSocket
64
+ class << self
65
+ alias_method :real_open, :open
66
+
67
+ def open(address, port, *_args)
68
+ if TCR.configuration.hook_tcp_ports.include?(port)
69
+ TCR::RecordableTCPSocket.new(address, port, TCR.cassette)
70
+ else
71
+ real_open(address, port)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ class OpenSSL::SSL::SSLSocket # rubocop:disable Style/StaticClass
78
+ class << self
79
+ def new(io, *args)
80
+ if io.is_a?(TCR::RecordableTCPSocket)
81
+ TCR::RecordableSSLSocket.new(io)
82
+ else
83
+ super
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ class Socket
90
+ class << self
91
+ alias_method :real_tcp, :tcp
92
+
93
+ def tcp(host, port, *socket_opts)
94
+ if TCR.configuration.hook_tcp_ports.include?(port)
95
+ TCR::RecordableTCPSocket.new(host, port, TCR.cassette)
96
+ else
97
+ real_tcp(host, port, *socket_opts)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = ::File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'tcr/version'
6
+
7
+ ::Gem::Specification.new do |gem|
8
+ gem.name = 'tcr_revived'
9
+ gem.version = ::TCR::VERSION
10
+ gem.authors = ['Rob Forman', 'Mateusz Drewniak']
11
+ gem.email = ['rob@robforman.com', 'matmg24@gmail.com']
12
+ gem.description = 'TCR is a lightweight VCR for TCP sockets.'
13
+ gem.summary = 'TCR is a lightweight VCR for TCP sockets.'
14
+ gem.homepage = 'https://github.com/Verseth/tcr'
15
+ gem.license = 'MIT'
16
+
17
+ gem.metadata['homepage_uri'] = gem.homepage
18
+ gem.metadata['source_code_uri'] = gem.homepage
19
+ gem.metadata['rubygems_mfa_required'] = 'true'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ gem.files = ::Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ gem.bindir = 'exe'
29
+ gem.executables = gem.files.grep(%r{\Aexe/}) { |f| ::File.basename(f) }
30
+ gem.require_paths = ['lib']
31
+ gem.required_ruby_version = '>= 2.6.10'
32
+
33
+ gem.add_development_dependency 'debug'
34
+ gem.add_development_dependency 'geminabox'
35
+ gem.add_development_dependency 'mail'
36
+ gem.add_development_dependency 'mime-types', '~>2.0'
37
+ gem.add_development_dependency 'net-imap'
38
+ gem.add_development_dependency 'net-ldap'
39
+ gem.add_development_dependency 'net-pop'
40
+ gem.add_development_dependency 'net-smtp'
41
+ gem.add_development_dependency 'rake'
42
+ gem.add_development_dependency 'rspec'
43
+ end
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tcr_revived
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Forman
8
+ - Mateusz Drewniak
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2022-10-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: debug
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: geminabox
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: mail
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: mime-types
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '2.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: net-imap
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: net-ldap
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: net-pop
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: net-smtp
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rake
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: rspec
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ description: TCR is a lightweight VCR for TCP sockets.
155
+ email:
156
+ - rob@robforman.com
157
+ - matmg24@gmail.com
158
+ executables: []
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - ".rubocop.yml"
163
+ - ".ruby-version"
164
+ - CHANGELOG.md
165
+ - Gemfile
166
+ - LICENSE.txt
167
+ - README.md
168
+ - Rakefile
169
+ - lib/tcr.rb
170
+ - lib/tcr/cassette.rb
171
+ - lib/tcr/configuration.rb
172
+ - lib/tcr/errors.rb
173
+ - lib/tcr/net/ldap.rb
174
+ - lib/tcr/recordable_tcp_socket.rb
175
+ - lib/tcr/version.rb
176
+ - tcr_revived.gemspec
177
+ homepage: https://github.com/Verseth/tcr
178
+ licenses:
179
+ - MIT
180
+ metadata:
181
+ homepage_uri: https://github.com/Verseth/tcr
182
+ source_code_uri: https://github.com/Verseth/tcr
183
+ rubygems_mfa_required: 'true'
184
+ post_install_message:
185
+ rdoc_options: []
186
+ require_paths:
187
+ - lib
188
+ required_ruby_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 2.6.10
193
+ required_rubygems_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ requirements: []
199
+ rubygems_version: 3.3.7
200
+ signing_key:
201
+ specification_version: 4
202
+ summary: TCR is a lightweight VCR for TCP sockets.
203
+ test_files: []