From ee1bd80621e24c38d625ff391515c8036a7c483d Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:48:06 +0000 Subject: [PATCH 01/24] convert from stubbed TCPRecordableSocket to module in a real TCPSocket --- lib/tcr.rb | 23 ++++++------------- lib/tcr/errors.rb | 1 - ...recordable_tcp_socket.rb => recordable.rb} | 21 ++++++++--------- lib/tcr/socket_extension.rb | 11 +++++++++ spec/tcr_spec.rb | 14 +++-------- 5 files changed, 30 insertions(+), 40 deletions(-) rename lib/tcr/{recordable_tcp_socket.rb => recordable.rb} (72%) create mode 100644 lib/tcr/socket_extension.rb diff --git a/lib/tcr.rb b/lib/tcr.rb index b2899e9..8d11eb7 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -1,7 +1,8 @@ require "tcr/cassette" require "tcr/configuration" require "tcr/errors" -require "tcr/recordable_tcp_socket" +require "tcr/socket_extension" +require "tcr/recordable" require "tcr/version" require "socket" require "json" @@ -37,6 +38,10 @@ def disabled=(v) def save_session end + def record_port?(port) + configuration.hook_tcp_ports.include?(port) + end + def use_cassette(name, options = {}, &block) raise ArgumentError, "`TCR.use_cassette` requires a block." unless block TCR.cassette = Cassette.new(name) @@ -53,18 +58,4 @@ def turned_off(&block) end end - -# The monkey patch shim -class TCPSocket - class << self - alias_method :real_open, :open - - def open(address, port) - if TCR.configuration.hook_tcp_ports.include?(port) - TCR::RecordableTCPSocket.new(address, port, TCR.cassette) - else - real_open(address, port) - end - end - end -end +TCPSocket.prepend(TCR::SocketExtension) diff --git a/lib/tcr/errors.rb b/lib/tcr/errors.rb index c5bb897..e0b8372 100644 --- a/lib/tcr/errors.rb +++ b/lib/tcr/errors.rb @@ -1,6 +1,5 @@ module TCR class TCRError < StandardError; end - class NoCassetteError < TCRError; end class NoMoreSessionsError < TCRError; end class DirectionMismatchError < TCRError; end end diff --git a/lib/tcr/recordable_tcp_socket.rb b/lib/tcr/recordable.rb similarity index 72% rename from lib/tcr/recordable_tcp_socket.rb rename to lib/tcr/recordable.rb index 2c5aa0b..d5a635a 100644 --- a/lib/tcr/recordable_tcp_socket.rb +++ b/lib/tcr/recordable.rb @@ -1,14 +1,11 @@ module TCR - class RecordableTCPSocket - attr_reader :live, :cassette - attr_accessor :recording - - def initialize(address, port, cassette) - raise TCR::NoCassetteError.new unless TCR.cassette + module Recordable + attr_reader :live + attr_accessor :recording, :cassette + def setup_recordable(cassette) if cassette.recording? @live = true - @socket = TCPSocket.real_open(address, port) @recording = [] else @live = false @@ -19,7 +16,7 @@ def initialize(address, port, cassette) def read_nonblock(bytes) if live - data = @socket.read_nonblock(bytes) + data = super recording << ["read", data] else direction, data = recording.shift @@ -31,7 +28,7 @@ def read_nonblock(bytes) def write(str) if live - len = @socket.write(str) + len = super recording << ["write", str] else direction, data = recording.shift @@ -44,13 +41,13 @@ def write(str) def to_io if live - @socket.to_io + super end end def closed? if live - @socket.closed? + super else false end @@ -58,7 +55,7 @@ def closed? def close if live - @socket.close + super cassette.append(recording) end end diff --git a/lib/tcr/socket_extension.rb b/lib/tcr/socket_extension.rb new file mode 100644 index 0000000..debb5be --- /dev/null +++ b/lib/tcr/socket_extension.rb @@ -0,0 +1,11 @@ +module TCR + module SocketExtension + def initialize(address, port, *args) + super + if TCR.record_port?(port) && TCR.cassette + extend(Recordable) + setup_recordable(TCR.cassette) + end + end + end +end diff --git a/spec/tcr_spec.rb b/spec/tcr_spec.rb index 3fc211a..3553e58 100644 --- a/spec/tcr_spec.rb +++ b/spec/tcr_spec.rb @@ -32,13 +32,6 @@ end end - it "raises an error if you connect to a hooked port without using a cassette" do - TCR.configure { |c| c.hook_tcp_ports = [25] } - expect { - tcp_socket = TCPSocket.open("aspmx.l.google.com", 25) - }.to raise_error(TCR::NoCassetteError) - end - describe ".turned_off" do it "requires a block to call" do expect { @@ -55,9 +48,9 @@ it "makes real TCPSocket.open calls even when hooks are setup" do TCR.configure { |c| c.hook_tcp_ports = [25] } - expect(TCPSocket).to receive(:real_open) TCR.turned_off do tcp_socket = TCPSocket.open("aspmx.l.google.com", 25) + expect(tcp_socket).not_to respond_to(:cassette) end end end @@ -107,10 +100,9 @@ end it "plays back tcp sessions without opening a real connection" do - expect(TCPSocket).to_not receive(:real_open) - TCR.use_cassette("spec/fixtures/google_smtp") do tcp_socket = TCPSocket.open("aspmx.l.google.com", 25) + expect(tcp_socket).to respond_to(:cassette) io = Net::InternetMessageIO.new(tcp_socket) line = io.readline.should include("220 mx.google.com ESMTP") end @@ -163,4 +155,4 @@ end end end -end \ No newline at end of file +end From a61be26c8ccc5a6a75044218ac294f76313aa7cc Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:48:25 +0000 Subject: [PATCH 02/24] refactor turned_off --- lib/tcr.rb | 17 +++++++++-------- spec/tcr_spec.rb | 7 ------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index 8d11eb7..be1afbf 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -9,6 +9,8 @@ module TCR + ALL_PORTS = '*' + extend self def configure @@ -35,11 +37,8 @@ def disabled=(v) @disabled = v end - def save_session - end - def record_port?(port) - configuration.hook_tcp_ports.include?(port) + !disabled && configuration.hook_tcp_ports == ALL_PORTS || configuration.hook_tcp_ports.include?(port) end def use_cassette(name, options = {}, &block) @@ -51,10 +50,12 @@ def use_cassette(name, options = {}, &block) def turned_off(&block) raise ArgumentError, "`TCR.turned_off` requires a block." unless block - current_hook_tcp_ports = configuration.hook_tcp_ports - configuration.hook_tcp_ports = [] - yield - configuration.hook_tcp_ports = current_hook_tcp_ports + begin + disabled = true + yield + ensure + disabled = false + end end end diff --git a/spec/tcr_spec.rb b/spec/tcr_spec.rb index 3553e58..a543e3d 100644 --- a/spec/tcr_spec.rb +++ b/spec/tcr_spec.rb @@ -39,13 +39,6 @@ }.to raise_error(ArgumentError) end - it "disables hooks within the block" do - TCR.configure { |c| c.hook_tcp_ports = [25] } - TCR.turned_off do - TCR.configuration.hook_tcp_ports.should == [] - end - end - it "makes real TCPSocket.open calls even when hooks are setup" do TCR.configure { |c| c.hook_tcp_ports = [25] } TCR.turned_off do From 2f45e81c3614cefeea0e04f5edfc87a6599b1097 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:49:07 +0000 Subject: [PATCH 03/24] use a class for the current recording --- lib/tcr/cassette.rb | 32 +++++++++++++++++++++++++++----- lib/tcr/recordable.rb | 9 ++------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 5ea5a0e..94900d4 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -20,14 +20,18 @@ def recording? end def next_session - session = @sessions.shift - raise NoMoreSessionsError unless session - session + if recording? + Session.new([]) + else + session = @sessions.shift + raise NoMoreSessionsError unless session + Session.new(session) + end end def append(session) raise "Can't append session unless recording" unless recording? - @sessions << session + @sessions << session.as_json File.open(filename, "w") { |f| f.write(JSON.pretty_generate(@sessions)) } end @@ -36,5 +40,23 @@ def append(session) def filename "#{TCR.configuration.cassette_library_dir}/#{name}.json" end + + class Session + def initialize(recording) + @recording = recording + end + + def <<(value) + @recording << value + end + + def shift + @recording.shift + end + + def as_json + @recording + end + end end -end \ No newline at end of file +end diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index d5a635a..d6ddf8b 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -4,13 +4,8 @@ module Recordable attr_accessor :recording, :cassette def setup_recordable(cassette) - if cassette.recording? - @live = true - @recording = [] - else - @live = false - @recording = cassette.next_session - end + @live = cassette.recording? + @recording = cassette.next_session @cassette = cassette end From 2c4387023812a17d3d2e8870ccd44c280fb15bb8 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:49:23 +0000 Subject: [PATCH 04/24] move functionality from Recordable to Cassette --- lib/tcr/cassette.rb | 36 +++++++++++++++++++++++++++++------- lib/tcr/recordable.rb | 21 ++++----------------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 94900d4..7c864ba 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -21,11 +21,11 @@ def recording? def next_session if recording? - Session.new([]) + Session.new([], true) else session = @sessions.shift raise NoMoreSessionsError unless session - Session.new(session) + Session.new(session, false) end end @@ -42,16 +42,38 @@ def filename end class Session - def initialize(recording) + def initialize(recording, live) + @live = live @recording = recording end - def <<(value) - @recording << value + def live + @live end - def shift - @recording.shift + def read + if live + data = yield + @recording << ["read", data] + else + direction, data = @recording.shift + raise TCR::DirectionMismatchError.new("Expected to 'read' but next in recording was 'write'") unless direction == "read" + end + + data + end + + def write(str) + if live + len = yield + @recording << ["write", str] + else + direction, data = @recording.shift + raise TCR::DirectionMismatchError.new("Expected to 'write' but next in recording was 'read'") unless direction == "write" + len = data.length + end + + len end def as_json diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index d6ddf8b..9b9647c 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -10,28 +10,15 @@ def setup_recordable(cassette) end def read_nonblock(bytes) - if live - data = super - recording << ["read", data] - else - direction, data = recording.shift - raise TCR::DirectionMismatchError.new("Expected to 'read' but next in recording was 'write'") unless direction == "read" + recording.read do + super end - - data end def write(str) - if live - len = super - recording << ["write", str] - else - direction, data = recording.shift - raise TCR::DirectionMismatchError.new("Expected to 'write' but next in recording was 'read'") unless direction == "write" - len = data.length + recording.write(str) do + super end - - len end def to_io From b372bc7831fe339cfe5d7720aefff9260c32159b Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:49:29 +0000 Subject: [PATCH 05/24] always write at the end of a block, even if the socket isn't closed --- lib/tcr.rb | 10 +++++++--- lib/tcr/cassette.rb | 36 ++++++++++++++++++++++++------------ lib/tcr/recordable.rb | 1 - 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index be1afbf..a80d4a2 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -43,9 +43,13 @@ def record_port?(port) def use_cassette(name, options = {}, &block) raise ArgumentError, "`TCR.use_cassette` requires a block." unless block - TCR.cassette = Cassette.new(name) - yield - TCR.cassette = nil + begin + TCR.cassette = Cassette.new(name) + yield + ensure + TCR.cassette.finish + TCR.cassette = nil + end end def turned_off(&block) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 7c864ba..e243fc7 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -4,14 +4,14 @@ class Cassette def initialize(name) @name = name + @recording = !File.exists?(filename) + end - if File.exists?(filename) - @recording = false - @contents = File.open(filename) { |f| f.read } - @sessions = JSON.parse(@contents) + def sessions + @sessions ||= if recording? + [] else - @recording = true - @sessions = [] + read_serialized_form end end @@ -21,22 +21,34 @@ def recording? def next_session if recording? - Session.new([], true) + Session.new([], true).tap do |session| + sessions << session + end else - session = @sessions.shift + session = sessions.shift raise NoMoreSessionsError unless session Session.new(session, false) end end - def append(session) - raise "Can't append session unless recording" unless recording? - @sessions << session.as_json - File.open(filename, "w") { |f| f.write(JSON.pretty_generate(@sessions)) } + def finish + if recording? + FileUtils.mkdir_p(File.dirname(filename)) + File.open(filename, "w") { |f| f.write(serialized_form) } + end end protected + def read_serialized_form + raw = File.open(filename) { |f| f.read } + JSON.parse(raw) + end + + def serialized_form + JSON.pretty_generate(sessions.map(&:as_json)) + end + def filename "#{TCR.configuration.cassette_library_dir}/#{name}.json" end diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index 9b9647c..28bfb75 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -38,7 +38,6 @@ def closed? def close if live super - cassette.append(recording) end end end From 8fadb4f74450ad012edfb226e5ea01a55f38b1ff Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:49:40 +0000 Subject: [PATCH 06/24] yield the current cassette --- lib/tcr.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index a80d4a2..58bef3e 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -45,7 +45,7 @@ def use_cassette(name, options = {}, &block) raise ArgumentError, "`TCR.use_cassette` requires a block." unless block begin TCR.cassette = Cassette.new(name) - yield + yield TCR.cassette ensure TCR.cassette.finish TCR.cassette = nil From efb6ca2d151ecd0d36bde162b62f5f9ac2ee0d15 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:49:52 +0000 Subject: [PATCH 07/24] refactoring recordable; adding some more recorded methods --- lib/tcr.rb | 1 - lib/tcr/cassette.rb | 12 ++++++++++++ lib/tcr/recordable.rb | 31 ++++++++++++++++--------------- lib/tcr/socket_extension.rb | 2 +- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index 58bef3e..da2afba 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -7,7 +7,6 @@ require "socket" require "json" - module TCR ALL_PORTS = '*' diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index e243fc7..03cd4bb 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -63,6 +63,18 @@ def live @live end + def gets + yield + end + + def connect + yield + end + + def close + yield + end + def read if live data = yield diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index 28bfb75..43a17a7 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -1,12 +1,15 @@ module TCR module Recordable - attr_reader :live - attr_accessor :recording, :cassette + attr_accessor :cassette - def setup_recordable(cassette) - @live = cassette.recording? - @recording = cassette.next_session - @cassette = cassette + def recording + @recording ||= cassette.next_session + end + + def connect + recording.connect do + super + end end def read_nonblock(bytes) @@ -15,28 +18,26 @@ def read_nonblock(bytes) end end - def write(str) - recording.write(str) do + def gets(*args) + recording.read do super end end - def to_io - if live + def write(str) + recording.write(str) do super end end - def closed? - if live + def to_io + if cassette.recording? super - else - false end end def close - if live + recording.close do super end end diff --git a/lib/tcr/socket_extension.rb b/lib/tcr/socket_extension.rb index debb5be..2446e4f 100644 --- a/lib/tcr/socket_extension.rb +++ b/lib/tcr/socket_extension.rb @@ -4,7 +4,7 @@ def initialize(address, port, *args) super if TCR.record_port?(port) && TCR.cassette extend(Recordable) - setup_recordable(TCR.cassette) + self.cassette = TCR.cassette end end end From 6cb8be0d0b1e7b6141704af167182070a96c2a21 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:50:02 +0000 Subject: [PATCH 08/24] build cassettes with a factory method --- lib/tcr.rb | 2 +- lib/tcr/cassette.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index da2afba..d77b853 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -43,7 +43,7 @@ def record_port?(port) def use_cassette(name, options = {}, &block) raise ArgumentError, "`TCR.use_cassette` requires a block." unless block begin - TCR.cassette = Cassette.new(name) + TCR.cassette = Cassette.build(name) yield TCR.cassette ensure TCR.cassette.finish diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 03cd4bb..9fb1240 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -2,6 +2,10 @@ module TCR class Cassette attr_reader :name + def self.build(name) + new(name) + end + def initialize(name) @name = name @recording = !File.exists?(filename) From a112abcf73db03862a922c98986dac4dc94b631d Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:50:21 +0000 Subject: [PATCH 09/24] split up into two types of cassettes --- lib/tcr/cassette.rb | 89 +++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 9fb1240..1818982 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -3,58 +3,83 @@ class Cassette attr_reader :name def self.build(name) - new(name) + if cassette_exists?(name) + RecordedCassette.new(name) + else + RecordingCassette.new(name) + end + end + + def self.filename(name) + "#{TCR.configuration.cassette_library_dir}/#{name}.json" + end + + def self.cassette_exists?(name) + File.exists?(filename(name)) end def initialize(name) @name = name - @recording = !File.exists?(filename) end - def sessions - @sessions ||= if recording? - [] - else - read_serialized_form - end - end + protected - def recording? - @recording + def filename + self.class.filename(name) end - def next_session - if recording? - Session.new([], true).tap do |session| - sessions << session - end - else + class RecordedCassette < Cassette + def sessions + @sessions ||= serialized_form + end + + def recording? + false + end + + def next_session session = sessions.shift raise NoMoreSessionsError unless session Session.new(session, false) end - end - def finish - if recording? - FileUtils.mkdir_p(File.dirname(filename)) - File.open(filename, "w") { |f| f.write(serialized_form) } + def finish + # no-op end - end - protected + private - def read_serialized_form - raw = File.open(filename) { |f| f.read } - JSON.parse(raw) + def serialized_form + raw = File.open(filename) { |f| f.read } + JSON.parse(raw) + end end - def serialized_form - JSON.pretty_generate(sessions.map(&:as_json)) - end + class RecordingCassette < Cassette + def sessions + @sessions ||= [] + end - def filename - "#{TCR.configuration.cassette_library_dir}/#{name}.json" + def recording? + true + end + + def next_session + Session.new([], true).tap do |session| + sessions << session + end + end + + def finish + FileUtils.mkdir_p(File.dirname(filename)) + File.open(filename, "w") { |f| f.write(serialized_form) } + end + + private + + def serialized_form + JSON.pretty_generate(sessions.map(&:as_json)) + end end class Session From 236279150e4028da017838908be1c6bfc1fa94c7 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:50:33 +0000 Subject: [PATCH 10/24] refactor cassette --- lib/tcr/cassette.rb | 106 +++++++++++++++++++++++++------------------- lib/tcr/errors.rb | 3 +- spec/tcr_spec.rb | 2 +- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 1818982..49826b3 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -40,7 +40,7 @@ def recording? def next_session session = sessions.shift raise NoMoreSessionsError unless session - Session.new(session, false) + Session.new(session) end def finish @@ -53,6 +53,40 @@ def serialized_form raw = File.open(filename) { |f| f.read } JSON.parse(raw) end + + class Session + def initialize(recording) + @recording = recording + end + + def connect + next_command('connect') + end + + def close + next_command('close') + end + + def read + next_command('read') + end + + def write(str) + data = next_command('write') do |data| + raise TCR::DataMismatchError.new("Expected to write '#{str}' but next data in recording was '#{data}'") unless str == data + end + data.length + end + + private + + def next_command(expected, expected_data=nil) + actual, data = @recording.shift + raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{actual}'") unless expected == actual + yield data if block_given? + data + end + end end class RecordingCassette < Cassette @@ -65,7 +99,7 @@ def recording? end def next_session - Session.new([], true).tap do |session| + Session.new.tap do |session| sessions << session end end @@ -80,57 +114,39 @@ def finish def serialized_form JSON.pretty_generate(sessions.map(&:as_json)) end - end - - class Session - def initialize(recording, live) - @live = live - @recording = recording - end - - def live - @live - end - - def gets - yield - end - - def connect - yield - end - def close - yield - end + class Session + def initialize + @recording = [] + end - def read - if live - data = yield - @recording << ["read", data] - else - direction, data = @recording.shift - raise TCR::DirectionMismatchError.new("Expected to 'read' but next in recording was 'write'") unless direction == "read" + def connect + yield.tap do |success| + @recording << ["connect", success] + end end - data - end + def close + yield.tap do |success| + @recording << ["close", success] + end + end - def write(str) - if live - len = yield - @recording << ["write", str] - else - direction, data = @recording.shift - raise TCR::DirectionMismatchError.new("Expected to 'write' but next in recording was 'read'") unless direction == "write" - len = data.length + def read + yield.tap do |data| + @recording << ["read", data] + end end - len - end + def write(str) + yield.tap do |len| + @recording << ["write", str] + end + end - def as_json - @recording + def as_json + @recording + end end end end diff --git a/lib/tcr/errors.rb b/lib/tcr/errors.rb index e0b8372..0451abb 100644 --- a/lib/tcr/errors.rb +++ b/lib/tcr/errors.rb @@ -1,5 +1,6 @@ module TCR class TCRError < StandardError; end class NoMoreSessionsError < TCRError; end - class DirectionMismatchError < TCRError; end + class CommandMismatchError < TCRError; end + class DataMismatchError < TCRError; end end diff --git a/spec/tcr_spec.rb b/spec/tcr_spec.rb index a543e3d..d65cb8c 100644 --- a/spec/tcr_spec.rb +++ b/spec/tcr_spec.rb @@ -108,7 +108,7 @@ io = Net::InternetMessageIO.new(tcp_socket) io.write("hi") end - }.to raise_error(TCR::DirectionMismatchError) + }.to raise_error(TCR::CommandMismatchError) end From 3962d5e49a92e47b253e3e2812a7510a0e77d4a5 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:51:07 +0000 Subject: [PATCH 11/24] refactor sessions --- lib/tcr/cassette.rb | 102 +++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 49826b3..7acda53 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -28,124 +28,118 @@ def filename self.class.filename(name) end - class RecordedCassette < Cassette + class RecordingCassette < Cassette def sessions - @sessions ||= serialized_form - end - - def recording? - false + @sessions ||= [] end def next_session - session = sessions.shift - raise NoMoreSessionsError unless session - Session.new(session) + Session.new.tap do |session| + sessions << session + end end def finish - # no-op + FileUtils.mkdir_p(File.dirname(filename)) + File.open(filename, "w") { |f| f.write(serialized_form) } end private def serialized_form - raw = File.open(filename) { |f| f.read } - JSON.parse(raw) + JSON.pretty_generate(sessions.map(&:as_json)) end class Session - def initialize(recording) - @recording = recording + def initialize + @recording = [] end def connect - next_command('connect') + yield.tap do |success| + @recording << ["connect", success] + end end def close - next_command('close') + yield.tap do |success| + @recording << ["close", success] + end end def read - next_command('read') + yield.tap do |data| + @recording << ["read", data] + end end def write(str) - data = next_command('write') do |data| - raise TCR::DataMismatchError.new("Expected to write '#{str}' but next data in recording was '#{data}'") unless str == data + yield.tap do |len| + @recording << ["write", str] end - data.length end - private - - def next_command(expected, expected_data=nil) - actual, data = @recording.shift - raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{actual}'") unless expected == actual - yield data if block_given? - data + def as_json + @recording end end end - class RecordingCassette < Cassette + class RecordedCassette < Cassette def sessions - @sessions ||= [] - end - - def recording? - true + @sessions ||= serialized_form end def next_session - Session.new.tap do |session| - sessions << session - end + session = sessions.shift + raise NoMoreSessionsError unless session + Session.new(session) end def finish - FileUtils.mkdir_p(File.dirname(filename)) - File.open(filename, "w") { |f| f.write(serialized_form) } + # no-op end private def serialized_form - JSON.pretty_generate(sessions.map(&:as_json)) + @serialized_form ||= begin + raw = File.open(filename) { |f| f.read } + JSON.parse(raw) + end end class Session - def initialize - @recording = [] + def initialize(recording) + @recording = recording end def connect - yield.tap do |success| - @recording << ["connect", success] - end + next_command('connect') end def close - yield.tap do |success| - @recording << ["close", success] - end + next_command('close') end def read - yield.tap do |data| - @recording << ["read", data] - end + next_command('read') end def write(str) - yield.tap do |len| - @recording << ["write", str] + data = next_command('write') do |data| + raise TCR::DataMismatchError.new("Expected to write '#{str}' but next data in recording was '#{data}'") unless str == data end + data.length end - def as_json - @recording + private + + def next_command(expected, expected_data=nil) + actual, data = @recording.shift + raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{actual}'") unless expected == actual + yield data if block_given? + data end end end From 327c8331d21e63122c9da5cdd81a5426d136a3ba Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:51:17 +0000 Subject: [PATCH 12/24] clean up return values a little --- lib/tcr/cassette.rb | 43 +++++++++++++++---------------- spec/fixtures/multitest-smtp.json | 6 ++++- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 7acda53..6c3ff43 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -55,33 +55,33 @@ def initialize @recording = [] end - def connect - yield.tap do |success| - @recording << ["connect", success] - end + def connect(&block) + next_command('connect', &block) end - def close - yield.tap do |success| - @recording << ["close", success] - end + def close(&block) + next_command('close', &block) end - def read - yield.tap do |data| - @recording << ["read", data] - end + def read(&block) + next_command('read', &block) end - def write(str) - yield.tap do |len| - @recording << ["write", str] - end + def write(str, &block) + next_command('write', str, &block) end def as_json @recording end + + private + + def next_command(command, *args, &block) + yield.tap do |return_value| + @recording << [command, return_value, *args] + end + end end end @@ -127,19 +127,18 @@ def read end def write(str) - data = next_command('write') do |data| + next_command('write') do |len, data| raise TCR::DataMismatchError.new("Expected to write '#{str}' but next data in recording was '#{data}'") unless str == data end - data.length end private def next_command(expected, expected_data=nil) - actual, data = @recording.shift - raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{actual}'") unless expected == actual - yield data if block_given? - data + command, return_value, *data = @recording.shift + raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{command}'") unless expected == command + yield return_value, *data if block_given? + return_value end end end diff --git a/spec/fixtures/multitest-smtp.json b/spec/fixtures/multitest-smtp.json index 5f7ca5b..c174664 100644 --- a/spec/fixtures/multitest-smtp.json +++ b/spec/fixtures/multitest-smtp.json @@ -6,6 +6,7 @@ ], [ "write", + 16, "EHLO localhost\r\n" ], [ @@ -14,6 +15,7 @@ ], [ "write", + 6, "QUIT\r\n" ], [ @@ -28,6 +30,7 @@ ], [ "write", + 16, "EHLO localhost\r\n" ], [ @@ -36,6 +39,7 @@ ], [ "write", + 6, "QUIT\r\n" ], [ @@ -43,4 +47,4 @@ "221 mta1579.mail.gq1.yahoo.com\r\n" ] ] -] \ No newline at end of file +] From fd41bfe09877eeeb44e2c03648415e34a873789e Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:51:28 +0000 Subject: [PATCH 13/24] store sessions in json attribute; paving the way for new cassette-level attributes --- lib/tcr/cassette.rb | 7 ++- spec/fixtures/google_smtp.json | 12 ++-- spec/fixtures/multitest-smtp.json | 98 ++++++++++++++++--------------- spec/fixtures/multitest.json | 22 +++---- 4 files changed, 74 insertions(+), 65 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 6c3ff43..4861f2e 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -47,7 +47,10 @@ def finish private def serialized_form - JSON.pretty_generate(sessions.map(&:as_json)) + raw = { + 'sessions' => sessions.map(&:as_json) + } + JSON.pretty_generate(raw) end class Session @@ -87,7 +90,7 @@ def next_command(command, *args, &block) class RecordedCassette < Cassette def sessions - @sessions ||= serialized_form + @sessions ||= serialized_form['sessions'] end def next_session diff --git a/spec/fixtures/google_smtp.json b/spec/fixtures/google_smtp.json index d5360a7..ce4dcec 100644 --- a/spec/fixtures/google_smtp.json +++ b/spec/fixtures/google_smtp.json @@ -1,8 +1,10 @@ -[ - [ +{ + "sessions": [ [ - "read", - "220 mx.google.com ESMTP x3si2474860qas.18 - gsmtp\r\n" + [ + "read", + "220 mx.google.com ESMTP x3si2474860qas.18 - gsmtp\r\n" + ] ] ] -] \ No newline at end of file +} diff --git a/spec/fixtures/multitest-smtp.json b/spec/fixtures/multitest-smtp.json index c174664..ce9fa92 100644 --- a/spec/fixtures/multitest-smtp.json +++ b/spec/fixtures/multitest-smtp.json @@ -1,50 +1,52 @@ -[ - [ - [ - "read", - "220 mx.google.com ESMTP d8si2472149qai.124 - gsmtp\r\n" - ], - [ - "write", - 16, - "EHLO localhost\r\n" - ], - [ - "read", - "250-mx.google.com at your service, [54.227.243.167]\r\n250-SIZE 35882577\r\n250-8BITMIME\r\n250-STARTTLS\r\n250 ENHANCEDSTATUSCODES\r\n" - ], - [ - "write", - 6, - "QUIT\r\n" - ], - [ - "read", - "221 2.0.0 closing connection d8si2472149qai.124 - gsmtp\r\n" - ] - ], - [ - [ - "read", - "220 mta1579.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" - ], - [ - "write", - 16, - "EHLO localhost\r\n" - ], - [ - "read", - "250-mta1579.mail.gq1.yahoo.com\r\n250-8BITMIME\r\n250-SIZE 41943040\r\n250 PIPELINING\r\n" - ], - [ - "write", - 6, - "QUIT\r\n" - ], - [ - "read", - "221 mta1579.mail.gq1.yahoo.com\r\n" +{ + "sessions": [ + [ + [ + "read", + "220 mx.google.com ESMTP d8si2472149qai.124 - gsmtp\r\n" + ], + [ + "write", + 16, + "EHLO localhost\r\n" + ], + [ + "read", + "250-mx.google.com at your service, [54.227.243.167]\r\n250-SIZE 35882577\r\n250-8BITMIME\r\n250-STARTTLS\r\n250 ENHANCEDSTATUSCODES\r\n" + ], + [ + "write", + 6, + "QUIT\r\n" + ], + [ + "read", + "221 2.0.0 closing connection d8si2472149qai.124 - gsmtp\r\n" + ] + ], + [ + [ + "read", + "220 mta1579.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + ], + [ + "write", + 16, + "EHLO localhost\r\n" + ], + [ + "read", + "250-mta1579.mail.gq1.yahoo.com\r\n250-8BITMIME\r\n250-SIZE 41943040\r\n250 PIPELINING\r\n" + ], + [ + "write", + 6, + "QUIT\r\n" + ], + [ + "read", + "221 mta1579.mail.gq1.yahoo.com\r\n" + ] ] ] -] +} diff --git a/spec/fixtures/multitest.json b/spec/fixtures/multitest.json index fed2325..901ed53 100644 --- a/spec/fixtures/multitest.json +++ b/spec/fixtures/multitest.json @@ -1,14 +1,16 @@ -[ - [ +{ + "sessions": [ [ - "read", - "220 mx.google.com ESMTP h5si2286277qec.54 - gsmtp\r\n" - ] - ], - [ + [ + "read", + "220 mx.google.com ESMTP h5si2286277qec.54 - gsmtp\r\n" + ] + ], [ - "read", - "220 mta1009.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + [ + "read", + "220 mta1009.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + ] ] ] -] \ No newline at end of file +} From 4b61924b1410aae25dbed1b892e97a55eaf28aad Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:51:38 +0000 Subject: [PATCH 14/24] store recordings as objects, too --- lib/tcr/cassette.rb | 6 +- spec/fixtures/google_smtp.json | 12 ++-- spec/fixtures/multitest-smtp.json | 96 ++++++++++++++++--------------- spec/fixtures/multitest.json | 24 ++++---- 4 files changed, 74 insertions(+), 64 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 4861f2e..c514b1a 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -75,7 +75,7 @@ def write(str, &block) end def as_json - @recording + {"recording" => @recording} end private @@ -113,8 +113,8 @@ def serialized_form end class Session - def initialize(recording) - @recording = recording + def initialize(raw) + @recording = raw["recording"] end def connect diff --git a/spec/fixtures/google_smtp.json b/spec/fixtures/google_smtp.json index ce4dcec..6d35909 100644 --- a/spec/fixtures/google_smtp.json +++ b/spec/fixtures/google_smtp.json @@ -1,10 +1,12 @@ { "sessions": [ - [ - [ - "read", - "220 mx.google.com ESMTP x3si2474860qas.18 - gsmtp\r\n" + { + "recording": [ + [ + "read", + "220 mx.google.com ESMTP x3si2474860qas.18 - gsmtp\r\n" + ] ] - ] + } ] } diff --git a/spec/fixtures/multitest-smtp.json b/spec/fixtures/multitest-smtp.json index ce9fa92..3d83900 100644 --- a/spec/fixtures/multitest-smtp.json +++ b/spec/fixtures/multitest-smtp.json @@ -1,52 +1,56 @@ { "sessions": [ - [ - [ - "read", - "220 mx.google.com ESMTP d8si2472149qai.124 - gsmtp\r\n" - ], - [ - "write", - 16, - "EHLO localhost\r\n" - ], - [ - "read", - "250-mx.google.com at your service, [54.227.243.167]\r\n250-SIZE 35882577\r\n250-8BITMIME\r\n250-STARTTLS\r\n250 ENHANCEDSTATUSCODES\r\n" - ], - [ - "write", - 6, - "QUIT\r\n" - ], - [ - "read", - "221 2.0.0 closing connection d8si2472149qai.124 - gsmtp\r\n" + { + "recording": [ + [ + "read", + "220 mx.google.com ESMTP d8si2472149qai.124 - gsmtp\r\n" + ], + [ + "write", + 16, + "EHLO localhost\r\n" + ], + [ + "read", + "250-mx.google.com at your service, [54.227.243.167]\r\n250-SIZE 35882577\r\n250-8BITMIME\r\n250-STARTTLS\r\n250 ENHANCEDSTATUSCODES\r\n" + ], + [ + "write", + 6, + "QUIT\r\n" + ], + [ + "read", + "221 2.0.0 closing connection d8si2472149qai.124 - gsmtp\r\n" + ] ] - ], - [ - [ - "read", - "220 mta1579.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" - ], - [ - "write", - 16, - "EHLO localhost\r\n" - ], - [ - "read", - "250-mta1579.mail.gq1.yahoo.com\r\n250-8BITMIME\r\n250-SIZE 41943040\r\n250 PIPELINING\r\n" - ], - [ - "write", - 6, - "QUIT\r\n" - ], - [ - "read", - "221 mta1579.mail.gq1.yahoo.com\r\n" + }, + { + "recording": [ + [ + "read", + "220 mta1579.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + ], + [ + "write", + 16, + "EHLO localhost\r\n" + ], + [ + "read", + "250-mta1579.mail.gq1.yahoo.com\r\n250-8BITMIME\r\n250-SIZE 41943040\r\n250 PIPELINING\r\n" + ], + [ + "write", + 6, + "QUIT\r\n" + ], + [ + "read", + "221 mta1579.mail.gq1.yahoo.com\r\n" + ] ] - ] + } ] } diff --git a/spec/fixtures/multitest.json b/spec/fixtures/multitest.json index 901ed53..60ba2ac 100644 --- a/spec/fixtures/multitest.json +++ b/spec/fixtures/multitest.json @@ -1,16 +1,20 @@ { "sessions": [ - [ - [ - "read", - "220 mx.google.com ESMTP h5si2286277qec.54 - gsmtp\r\n" + { + "recording": [ + [ + "read", + "220 mx.google.com ESMTP h5si2286277qec.54 - gsmtp\r\n" + ] ] - ], - [ - [ - "read", - "220 mta1009.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + }, + { + "recording": [ + [ + "read", + "220 mta1009.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n" + ] ] - ] + } ] } From 22c282cabb759dd1fc62904980914172388983fc Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:51:59 +0000 Subject: [PATCH 15/24] add originally_recorded_at attribute to cassettes --- lib/tcr/cassette.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index c514b1a..3ff22f6 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -29,6 +29,13 @@ def filename end class RecordingCassette < Cassette + attr_reader :originally_recorded_at + + def initialize(*args) + super + @originally_recorded_at = Time.now + end + def sessions @sessions ||= [] end @@ -48,6 +55,7 @@ def finish def serialized_form raw = { + "originally_recorded_at" => originally_recorded_at, 'sessions' => sessions.map(&:as_json) } JSON.pretty_generate(raw) @@ -93,6 +101,10 @@ def sessions @sessions ||= serialized_form['sessions'] end + def originally_recorded_at + serialized_form['originally_recorded_at'] + end + def next_session session = sessions.shift raise NoMoreSessionsError unless session From 3ee8526f4bd84f58f1780781ffcb45f73b0f596d Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:52:04 +0000 Subject: [PATCH 16/24] remove to_io --- lib/tcr/recordable.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index 43a17a7..8983955 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -30,12 +30,6 @@ def write(str) end end - def to_io - if cassette.recording? - super - end - end - def close recording.close do super From 9131fa0c2236ba49a053efda74af7af3f8e440bd Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:52:14 +0000 Subject: [PATCH 17/24] override return value for connect --- lib/tcr/cassette.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 3ff22f6..28369d5 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -67,7 +67,7 @@ def initialize end def connect(&block) - next_command('connect', &block) + next_command('connect', ret: nil, &block) end def close(&block) @@ -79,7 +79,7 @@ def read(&block) end def write(str, &block) - next_command('write', str, &block) + next_command('write', data: str, &block) end def as_json @@ -88,9 +88,10 @@ def as_json private - def next_command(command, *args, &block) + def next_command(command, options={}, &block) yield.tap do |return_value| - @recording << [command, return_value, *args] + return_value = options[:ret] if options.has_key?(:ret) + @recording << [command, return_value] + [options[:data]].compact end end end @@ -150,9 +151,9 @@ def write(str) private def next_command(expected, expected_data=nil) - command, return_value, *data = @recording.shift + command, return_value, data = @recording.shift raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{command}'") unless expected == command - yield return_value, *data if block_given? + yield return_value, data if block_given? return_value end end From c21732b826a7f539c68ea4db8956c7449edccadb Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:52:33 +0000 Subject: [PATCH 18/24] add ability to check if sessions are empty --- lib/tcr/cassette.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 28369d5..055ae2c 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -61,6 +61,10 @@ def serialized_form JSON.pretty_generate(raw) end + def empty? + true + end + class Session def initialize @recording = [] @@ -99,7 +103,7 @@ def next_command(command, options={}, &block) class RecordedCassette < Cassette def sessions - @sessions ||= serialized_form['sessions'] + @sessions ||= serialized_form['sessions'].map{|raw| Session.new(raw)} end def originally_recorded_at @@ -109,13 +113,17 @@ def originally_recorded_at def next_session session = sessions.shift raise NoMoreSessionsError unless session - Session.new(session) + session end def finish # no-op end + def empty? + sessions.all?(&:empty?) + end + private def serialized_form @@ -148,6 +156,10 @@ def write(str) end end + def empty? + @recording.empty? + end + private def next_command(expected, expected_data=nil) From 7c0cb49a7cb8fb5bb87bf4a67d2b140fe84c5d10 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:52:47 +0000 Subject: [PATCH 19/24] remove unused method param --- lib/tcr/cassette.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 055ae2c..52a01bb 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -162,7 +162,7 @@ def empty? private - def next_command(expected, expected_data=nil) + def next_command(expected) command, return_value, data = @recording.shift raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{command}'") unless expected == command yield return_value, data if block_given? From fb85c55317a9869323119ce45bc42de78d3ad92b Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 21:53:07 +0000 Subject: [PATCH 20/24] record args for read commands --- lib/tcr/cassette.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/tcr/cassette.rb b/lib/tcr/cassette.rb index 52a01bb..e2f0226 100644 --- a/lib/tcr/cassette.rb +++ b/lib/tcr/cassette.rb @@ -78,8 +78,8 @@ def close(&block) next_command('close', &block) end - def read(&block) - next_command('read', &block) + def read(*args, &block) + next_command('read', data: args, &block) end def write(str, &block) @@ -95,7 +95,7 @@ def as_json def next_command(command, options={}, &block) yield.tap do |return_value| return_value = options[:ret] if options.has_key?(:ret) - @recording << [command, return_value] + [options[:data]].compact + @recording << [command, return_value] + Array(options[:data]) end end end @@ -146,8 +146,10 @@ def close next_command('close') end - def read - next_command('read') + def read(*args) + next_command('read') do |str, *data| + raise TCR::DataMismatchError.new("Expected to read to be called with args '#{args.inspect}' but was called with '#{data.inspect}'") unless args == data + end end def write(str) @@ -165,7 +167,7 @@ def empty? def next_command(expected) command, return_value, data = @recording.shift raise TCR::CommandMismatchError.new("Expected to '#{expected}' but next in recording was '#{command}'") unless expected == command - yield return_value, data if block_given? + yield return_value, *data if block_given? return_value end end From cb7a4c0d9f29fe5dc86aae952a278651d4b50b26 Mon Sep 17 00:00:00 2001 From: Peter McCracken Date: Thu, 20 Mar 2014 22:12:00 +0000 Subject: [PATCH 21/24] apply changes to SSL sockets, too --- lib/tcr.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tcr.rb b/lib/tcr.rb index d77b853..a147d11 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -9,6 +9,7 @@ module TCR ALL_PORTS = '*' + SOCKET_CLASSES = [TCPSocket, OpenSSL::SSL::SSLSocket] extend self @@ -62,4 +63,4 @@ def turned_off(&block) end end -TCPSocket.prepend(TCR::SocketExtension) +TCR::SOCKET_CLASSES.each{|klass|klass.prepend(TCR::SocketExtension)} From 8b6e75e8ee83d841ca3a9a9096a904e430f17cd5 Mon Sep 17 00:00:00 2001 From: Nate Smith Date: Tue, 25 Mar 2014 17:02:11 -0400 Subject: [PATCH 22/24] Add `#read` to the recordable methods --- lib/tcr/recordable.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/tcr/recordable.rb b/lib/tcr/recordable.rb index 8983955..2e4f999 100644 --- a/lib/tcr/recordable.rb +++ b/lib/tcr/recordable.rb @@ -30,6 +30,12 @@ def write(str) end end + def read(*args) + recording.read do + super + end + end + def close recording.close do super From c7c5e8d848f3e31a280aa4c9b656350d8ad8baca Mon Sep 17 00:00:00 2001 From: Luke Hutscal Date: Wed, 29 Oct 2014 19:16:05 +0000 Subject: [PATCH 23/24] Extract separate extension for SSL; don't prepend to OpenSSL::SSL::Socket --- lib/tcr.rb | 7 ++++--- lib/tcr/ssl_socket_extension.rb | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 lib/tcr/ssl_socket_extension.rb diff --git a/lib/tcr.rb b/lib/tcr.rb index a147d11..0fe55c6 100644 --- a/lib/tcr.rb +++ b/lib/tcr.rb @@ -1,15 +1,15 @@ require "tcr/cassette" require "tcr/configuration" require "tcr/errors" -require "tcr/socket_extension" require "tcr/recordable" +require "tcr/socket_extension" +require "tcr/ssl_socket_extension" require "tcr/version" require "socket" require "json" module TCR ALL_PORTS = '*' - SOCKET_CLASSES = [TCPSocket, OpenSSL::SSL::SSLSocket] extend self @@ -63,4 +63,5 @@ def turned_off(&block) end end -TCR::SOCKET_CLASSES.each{|klass|klass.prepend(TCR::SocketExtension)} +TCPSocket.prepend(TCR::SocketExtension) +OpenSSL::SSL::SSLSocket.send(:include, TCR::SSLSocketExtension) diff --git a/lib/tcr/ssl_socket_extension.rb b/lib/tcr/ssl_socket_extension.rb new file mode 100644 index 0000000..93062d9 --- /dev/null +++ b/lib/tcr/ssl_socket_extension.rb @@ -0,0 +1,16 @@ +module TCR + module SSLSocketExtension + def self.included(klass) + klass.send(:alias_method, :initialize_without_tcr, :initialize) + klass.send(:alias_method, :initialize, :initialize_with_tcr) + end + + def initialize_with_tcr(s, context) + initialize_without_tcr(s, context) + if TCR.record_port?(s.remote_address.ip_port) && TCR.cassette + extend(TCR::Recordable) + self.cassette = TCR.cassette + end + end + end +end From b294f63970a024b0481382fef55a5712ff0fbc99 Mon Sep 17 00:00:00 2001 From: Luke Hutscal Date: Thu, 30 Oct 2014 18:49:12 +0000 Subject: [PATCH 24/24] Cut shopify-tcr gem --- lib/tcr/version.rb | 2 +- tcr.gemspec | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/tcr/version.rb b/lib/tcr/version.rb index c36474a..b9e6892 100644 --- a/lib/tcr/version.rb +++ b/lib/tcr/version.rb @@ -1,3 +1,3 @@ module TCR - VERSION = "0.0.4" + VERSION = "0.0.5-shopify" end diff --git a/tcr.gemspec b/tcr.gemspec index abb60c2..bcb8658 100644 --- a/tcr.gemspec +++ b/tcr.gemspec @@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'tcr/version' Gem::Specification.new do |gem| - gem.name = "tcr" + gem.name = "shopify-tcr" gem.version = TCR::VERSION - gem.authors = ["Rob Forman"] - gem.email = ["rob@robforman.com"] + gem.authors = ["Luke Hutscal"] + gem.email = 'luke.hutscal@shopify.com' gem.description = %q{TCR is a lightweight VCR for TCP sockets.} gem.summary = %q{TCR is a lightweight VCR for TCP sockets.} gem.homepage = ""