forked from vcr/vcr
/
cassette.rb
140 lines (108 loc) · 4.58 KB
/
cassette.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
require 'fileutils'
require 'yaml'
require 'erb'
module VCR
class Cassette
class MissingERBVariableError < NameError; end
VALID_RECORD_MODES = [:all, :none, :new_episodes].freeze
attr_reader :name, :record_mode
def initialize(name, options = {})
options = VCR::Config.default_cassette_options.merge(options)
invalid_options = options.keys - [:record, :erb, :allow_real_http]
if invalid_options.size > 0
raise ArgumentError.new("You passed the following invalid options to VCR::Cassette.new: #{invalid_options.inspect}.")
end
@name = name
@record_mode = options[:record]
@erb = options[:erb]
deprecate_old_cassette_options(options)
raise_error_unless_valid_record_mode(record_mode)
set_http_connections_allowed
load_recorded_interactions
end
def eject
write_recorded_interactions_to_disk
VCR.http_stubbing_adapter.restore_stubs_checkpoint(name)
restore_http_connections_allowed
restore_ignore_localhost_for_deprecation
end
def recorded_interactions
@recorded_interactions ||= []
end
def record_http_interaction(interaction)
recorded_interactions << interaction
end
def file
File.join(VCR::Config.cassette_library_dir, "#{sanitized_name}.yml") if VCR::Config.cassette_library_dir
end
private
def sanitized_name
name.to_s.gsub(/[^\w\-\/]+/, '_')
end
def raise_error_unless_valid_record_mode(record_mode)
unless VALID_RECORD_MODES.include?(record_mode)
raise ArgumentError.new("#{record_mode} is not a valid cassette record mode. Valid options are: #{VALID_RECORD_MODES.inspect}")
end
end
def new_recorded_interactions
recorded_interactions - @original_recorded_interactions
end
def should_allow_http_connections?
[:new_episodes, :all].include?(record_mode)
end
def set_http_connections_allowed
@orig_http_connections_allowed = VCR.http_stubbing_adapter.http_connections_allowed?
VCR.http_stubbing_adapter.http_connections_allowed = should_allow_http_connections?
end
def restore_http_connections_allowed
VCR.http_stubbing_adapter.http_connections_allowed = @orig_http_connections_allowed
end
def load_recorded_interactions
VCR.http_stubbing_adapter.create_stubs_checkpoint(name)
@original_recorded_interactions = []
return if record_mode == :all
if file
@original_recorded_interactions = begin
interactions = YAML.load(raw_yaml_content)
if VCR.http_stubbing_adapter.ignore_localhost?
interactions.reject! do |i|
i.uri.is_a?(String) && VCR::LOCALHOST_ALIASES.include?(URI.parse(i.uri).host)
end
end
interactions
rescue TypeError
raise unless raw_yaml_content =~ /VCR::RecordedResponse/
raise "The VCR cassette #{sanitized_name}.yml uses an old format that is now deprecated. VCR provides a rake task to migrate your old cassettes to the new format. See http://github.com/myronmarston/vcr/blob/master/CHANGELOG.md for more info."
end if File.exist?(file)
recorded_interactions.replace(@original_recorded_interactions)
end
VCR.http_stubbing_adapter.stub_requests(recorded_interactions)
end
def raw_yaml_content
content = File.read(file)
return content unless @erb
template = ERB.new(content)
begin
return template.result unless @erb.is_a?(Hash)
# create an object with methods for each desired local variable...
local_variables = Struct.new(*@erb.keys).new(*@erb.values)
# instance_eval seems to be the only way to get the binding for ruby 1.9: http://redmine.ruby-lang.org/issues/show/2161
template.result(local_variables.instance_eval { binding })
rescue NameError => e
var_name = e.message[/undefined local variable or method `(.*)' for/, 1].to_sym
example_hash = (@erb.is_a?(Hash) ? @erb : {}).merge(var_name => 'some value')
raise MissingERBVariableError.new(
"The ERB in the #{sanitized_name}.yml cassette file references undefined variable #{var_name}. " +
"Pass it to the cassette using :erb => #{ example_hash.inspect }."
)
end
end
def write_recorded_interactions_to_disk
if VCR::Config.cassette_library_dir && new_recorded_interactions.size > 0
directory = File.dirname(file)
FileUtils.mkdir_p directory unless File.exist?(directory)
File.open(file, 'w') { |f| f.write recorded_interactions.to_yaml }
end
end
end
end