class VCR::Cassette
The media VCR uses to store HTTP interactions for later re-use.
def assert_valid_options!
def assert_valid_options! invalid_options = @options.keys - [ :record, :record_on_error, :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, :recompress_response, :persist_with, :persister_options, :clean_outdated_http_interactions, :drop_unused_requests ] if invalid_options.size > 0 raise ArgumentError.new("You passed the following invalid options to VCR::Cassette.new: #{invalid_options.inspect}.") end end
def assign_tags
def assign_tags @tags = Array(@options.fetch(:tags) { @options[:tag] }) [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response, :recompress_response].each do |tag| @tags << tag if @options[tag] end end
def deserialized_hash
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
def eject(options = {})
- See: VCR#eject_cassette -
Parameters:
-
() --
Other tags:
- Note: - This is not intended to be called directly. Use `VCR.eject_cassette` instead.
def eject(options = {}) write_recorded_interactions_to_disk if should_write_recorded_interactions_to_disk? if should_assert_no_unused_interactions? && !options[:skip_no_unused_interactions_assertion] http_interactions.assert_no_unused_interactions! end end
def extract_options
def extract_options [:record_on_error, :erb, :match_requests_on, :re_record_interval, :clean_outdated_http_interactions, :allow_playback_repeats, :allow_unused_http_interactions, :exclusive, :drop_unused_requests].each do |name| instance_variable_set("@#{name}", @options[name]) end assign_tags @serializer = VCR.cassette_serializers[@options[:serialize_with]] @persister = VCR.cassette_persisters[@options[:persist_with]] @record_mode = should_re_record?(@options[:record]) ? :all : @options[:record] @parent_list = @exclusive ? HTTPInteractionList::NullList : VCR.http_interactions end
def file
- Note: - VCR will take care of sanitizing the cassette name to make it a valid file name.
Raises:
-
(NotImplementedError)- if the configured cassette persister
Returns:
-
(String)- The file for this cassette.
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
def http_interactions
- Private: -
def http_interactions # Without this mutex, under threaded access, an HTTPInteractionList will overwrite # the first. @mutex.synchronize do @http_interactions ||= HTTPInteractionList.new \ should_stub_requests? ? previously_recorded_interactions : [], match_requests_on, @allow_playback_repeats, @parent_list, log_prefix end end
def initialize(name, options = {})
- See: VCR#insert_cassette -
Parameters:
-
() --
def initialize(name, options = {}) @name = name @options = VCR.configuration.default_cassette_options.merge(options) @mutex = Mutex.new assert_valid_options! extract_options raise_error_unless_valid_record_mode log "Initialized with options: #{@options.inspect}" end
def interactions_to_record
def interactions_to_record # We deep-dup the interactions by roundtripping them to/from a hash. # This is necessary because `before_record` can mutate the interactions. merged_interactions.map { |i| HTTPInteraction.from_hash(i.to_hash) }.tap do |interactions| invoke_hook(:before_record, interactions) end end
def invoke_hook(type, interactions)
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
def linked?
-
(Boolean)- false unless wrapped with LinkedCassette
def linked? false end
def log_prefix
def log_prefix @log_prefix ||= "[Cassette: '#{name}'] " end
def merged_interactions
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 if should_remove_unused_interactions? new_recorded_interactions else up_to_date_interactions(old_interactions) + new_recorded_interactions end end
def new_recorded_interactions
- Private: -
def new_recorded_interactions @new_recorded_interactions ||= [] end
def originally_recorded_at
-
(Time, nil)- The `recorded_at` time of the first HTTP interaction
def originally_recorded_at @originally_recorded_at ||= previously_recorded_interactions.map(&:recorded_at).min end
def previously_recorded_interactions
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
def raise_error_unless_valid_record_mode
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
def raw_cassette_bytes
def raw_cassette_bytes @raw_cassette_bytes ||= VCR::Cassette::ERBRenderer.new(@persister[storage_key], erb, name).render end
def record_http_interaction(interaction)
- Private: -
def record_http_interaction(interaction) VCR::CassetteMutex.synchronize do log "Recorded HTTP interaction #{request_summary(interaction.request)} => #{response_summary(interaction.response)}" new_recorded_interactions << interaction end end
def recording?
-
(Boolean)- Whether or not the cassette is recording.
def recording? case record_mode when :none; false when :once; raw_cassette_bytes.to_s.empty? else true end end
def request_summary(request)
def request_summary(request) super(request, match_requests_on) end
def run_failed!
- Private: -
def run_failed! @run_failed = true end
def run_failed?
- Private: -
def run_failed? @run_failed = false unless defined?(@run_failed) @run_failed end
def serializable_hash
-
(Hash)- The hash that will be serialized when the cassette is written to disk.
def serializable_hash { "http_interactions" => interactions_to_record.map(&:to_hash), "recorded_with" => "VCR #{VCR.version}" } end
def should_assert_no_unused_interactions?
def should_assert_no_unused_interactions? !(@allow_unused_http_interactions || $!) end
def should_re_record?(record_mode)
def should_re_record?(record_mode) return false unless @re_record_interval return false unless originally_recorded_at return false if record_mode == :none now = Time.now (originally_recorded_at + @re_record_interval < now).tap do |value| info = "previously recorded at: '#{originally_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
def should_remove_matching_existing_interactions?
def should_remove_matching_existing_interactions? record_mode == :all end
def should_remove_unused_interactions?
def should_remove_unused_interactions? @drop_unused_requests end
def should_stub_requests?
def should_stub_requests? record_mode != :all end
def should_write_recorded_interactions_to_disk?
def should_write_recorded_interactions_to_disk? !run_failed? || record_on_error end
def storage_key
def storage_key @storage_key ||= [name, @serializer.file_extension].join('.') end
def up_to_date_interactions(interactions)
def up_to_date_interactions(interactions) return interactions unless clean_outdated_http_interactions && re_record_interval interactions.take_while { |x| x[:recorded_at] > Time.now - re_record_interval } end
def write_recorded_interactions_to_disk
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