class VCR::Cassette
The media VCR uses to store HTTP interactions for later re-use.
Constants
- VALID_RECORD_MODES
The supported record modes.
* :all -- Record every HTTP interactions; do not play any back. * :none -- Do not record any HTTP interactions; play them back. * :new_episodes -- Playback previously recorded HTTP interactions and record new ones. * :once -- Record the HTTP interactions if the cassette has not already been recorded; otherwise, playback the HTTP interactions.
Attributes
@return [Boolean, Hash] The cassette's ERB option. The file will be treated as an
ERB template if this has a truthy value. A hash, if provided, will be used as local variables for the ERB template.
@return [Array<Symbol, call>] List of request matchers. Used to find a response from an
existing HTTP interaction to play back.
@return [#to_s] The name of the cassette. Used to determine the cassette's file name. @see file
@return [Integer, nil] How frequently (in seconds) the cassette should be re-recorded.
@return [Symbol] The record mode. Determines whether the cassette records HTTP interactions,
plays them back, or does both.
Public Class Methods
@private
# File lib/vcr/deprecations.rb, line 17 def Cassette.const_missing(const) return super unless const == :MissingERBVariableError warn "WARNING: `VCR::Cassette::MissingERBVariableError` is deprecated. Use `VCR::Errors::MissingERBVariableError` instead." Errors::MissingERBVariableError end
@param (see VCR#insert_cassette) @see VCR#insert_cassette
# File lib/vcr/cassette.rb, line 45 def initialize(name, options = {}) @name = name @options = VCR.configuration.default_cassette_options.merge(options) assert_valid_options! extract_options raise_error_unless_valid_record_mode log "Initialized with options: #{@options.inspect}" end
Public Instance Methods
Ejects the current cassette. The cassette will no longer be used. In addition, any newly recorded HTTP interactions will be written to disk.
# File lib/vcr/cassette.rb, line 59 def eject write_recorded_interactions_to_disk http_interactions.assert_no_unused_interactions! unless @allow_unused_http_interactions end
@return [String] The file for this cassette. @raise [NotImplementedError] if the configured cassette persister
does not support resolving file paths.
@note VCR will take care of sanitizing the cassette name to make it a valid file name.
# File lib/vcr/cassette.rb, line 89 def file unless @persister.respond_to?(:absolute_path_to_file) raise NotImplementedError, "The configured cassette persister does not support resolving file paths" end @persister.absolute_path_to_file(storage_key) end
@private
# File lib/vcr/cassette.rb, line 65 def http_interactions @http_interactions ||= HTTPInteractionList.new should_stub_requests? ? previously_recorded_interactions : [], match_requests_on, @allow_playback_repeats, @parent_list, log_prefix end
@private
# File lib/vcr/cassette.rb, line 81 def new_recorded_interactions @new_recorded_interactions ||= [] end
@private
# File lib/vcr/cassette.rb, line 75 def record_http_interaction(interaction) log "Recorded HTTP interaction #{request_summary(interaction.request)} => #{response_summary(interaction.response)}" new_recorded_interactions << interaction end
@return [Boolean] Whether or not the cassette is recording.
# File lib/vcr/cassette.rb, line 97 def recording? case record_mode when :none; false when :once; raw_cassette_bytes.to_s.empty? else true end end
@return [Hash] The hash that will be serialized when the cassette is written to disk.
# File lib/vcr/cassette.rb, line 106 def serializable_hash { "http_interactions" => interactions_to_record.map(&:to_hash), "recorded_with" => "VCR #{VCR.version}" } end
Private Instance Methods
# File lib/vcr/cassette.rb, line 115 def assert_valid_options! invalid_options = @options.keys - [ :record, :erb, :match_requests_on, :re_record_interval, :tag, :tags, :update_content_length_header, :allow_playback_repeats, :allow_unused_http_interactions, :exclusive, :serialize_with, :preserve_exact_body_bytes, :decode_compressed_response, :persist_with ] if invalid_options.size > 0 raise ArgumentError.new("You passed the following invalid options to VCR::Cassette.new: #{invalid_options.inspect}.") end end
# File lib/vcr/cassette.rb, line 247 def deserialized_hash @deserialized_hash ||= @serializer.deserialize(raw_cassette_bytes).tap do |hash| unless hash.is_a?(Hash) && hash['http_interactions'].is_a?(Array) raise Errors::InvalidCassetteFormatError.new "#{file} does not appear to be a valid VCR 2.0 cassette. " + "VCR 1.x cassettes are not valid with VCR 2.0. When upgrading from " + "VCR 1.x, it is recommended that you delete all your existing cassettes and " + "re-record them, or use the provided vcr:migrate_cassettes rake task to migrate " + "them. For more info, see the VCR upgrade guide." end end end
# File lib/vcr/cassette.rb, line 196 def earliest_interaction_recorded_at previously_recorded_interactions.map(&:recorded_at).min end
# File lib/vcr/cassette.rb, line 128 def extract_options [:erb, :match_requests_on, :re_record_interval, :allow_playback_repeats, :allow_unused_http_interactions, :exclusive].each do |name| instance_variable_set("@#{name}", @options[name]) end assign_tags @record_mode = @options[:record] @serializer = VCR.cassette_serializers[@options[:serialize_with]] @persister = VCR.cassette_persisters[@options[:persist_with]] @record_mode = :all if should_re_record? @parent_list = @exclusive ? HTTPInteractionList::NullList : VCR.http_interactions end
# File lib/vcr/cassette.rb, line 225 def interactions_to_record merged_interactions.tap do |interactions| invoke_hook(:before_record, interactions) end end
# File lib/vcr/cassette.rb, line 239 def invoke_hook(type, interactions) interactions.delete_if do |i| i.hook_aware.tap do |hw| VCR.configuration.invoke_hook(type, hw, self) end.ignored? end end
# File lib/vcr/cassette.rb, line 260 def log_prefix @log_prefix ||= "[Cassette: '#{name}'] " end
# File lib/vcr/cassette.rb, line 212 def merged_interactions old_interactions = previously_recorded_interactions if should_remove_matching_existing_interactions? new_interaction_list = HTTPInteractionList.new(new_recorded_interactions, match_requests_on) old_interactions = old_interactions.reject do |i| new_interaction_list.response_for(i.request) end end old_interactions + new_recorded_interactions end
# File lib/vcr/cassette.rb, line 151 def previously_recorded_interactions @previously_recorded_interactions ||= if !raw_cassette_bytes.to_s.empty? deserialized_hash['http_interactions'].map { |h| HTTPInteraction.from_hash(h) }.tap do |interactions| invoke_hook(:before_playback, interactions) interactions.reject! do |i| i.request.uri.is_a?(String) && VCR.request_ignorer.ignore?(i.request) end end else [] end end
# File lib/vcr/cassette.rb, line 169 def raise_error_unless_valid_record_mode unless VALID_RECORD_MODES.include?(record_mode) raise ArgumentError.new("#{record_mode} is not a valid cassette record mode. Valid modes are: #{VALID_RECORD_MODES.inspect}") end end
# File lib/vcr/cassette.rb, line 208 def raw_cassette_bytes @raw_cassette_bytes ||= VCR::Cassette::ERBRenderer.new(@persister[storage_key], erb, name).render end
# File lib/vcr/cassette.rb, line 264 def request_summary(request) super(request, match_requests_on) end
# File lib/vcr/cassette.rb, line 175 def should_re_record? return false unless @re_record_interval previously_recorded_at = earliest_interaction_recorded_at return false unless previously_recorded_at now = Time.now (previously_recorded_at + @re_record_interval < now).tap do |value| info = "previously recorded at: '#{previously_recorded_at}'; now: '#{now}'; interval: #{@re_record_interval} seconds" if !value log "Not re-recording since the interval has not elapsed (#{info})." elsif InternetConnection.available? log "re-recording (#{info})." else log "Not re-recording because no internet connection is available (#{info})." return false end end end
# File lib/vcr/cassette.rb, line 204 def should_remove_matching_existing_interactions? record_mode == :all end
# File lib/vcr/cassette.rb, line 200 def should_stub_requests? record_mode != :all end
# File lib/vcr/cassette.rb, line 165 def storage_key @storage_key ||= [name, @serializer.file_extension].join('.') end
# File lib/vcr/cassette.rb, line 231 def write_recorded_interactions_to_disk return if new_recorded_interactions.none? hash = serializable_hash return if hash["http_interactions"].none? @persister[storage_key] = @serializer.serialize(hash) end