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

erb[R]

@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.
match_requests_on[R]

@return [Array<Symbol, call>] List of request matchers. Used to find a response from an

existing HTTP interaction to play back.
name[R]

@return [#to_s] The name of the cassette. Used to determine the cassette’s file name. @see file

re_record_interval[R]

@return [Integer, nil] How frequently (in seconds) the cassette should be re-recorded.

record_mode[R]

@return [Symbol] The record mode. Determines whether the cassette records HTTP interactions,

plays them back, or does both.
tags[R]

@return [Array<Symbol>] If set, {VCR::Configuration#before_record} and

{VCR::Configuration#before_playback} hooks with a corresponding tag will apply.

Public Class Methods

const_missing(const) click to toggle source

@private

Calls superclass method
# 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
new(name, options = {}) click to toggle source

@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

eject(options = {}) click to toggle source

Ejects the current cassette. The cassette will no longer be used. In addition, any newly recorded HTTP interactions will be written to disk.

@note This is not intended to be called directly. Use ‘VCR.eject_cassette` instead.

@param (see VCR#eject_casssette) @see VCR#eject_cassette

# File lib/vcr/cassette.rb, line 64
def eject(options = {})
  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
file() click to toggle source

@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 97
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
http_interactions() click to toggle source

@private

# File lib/vcr/cassette.rb, line 73
def http_interactions
  @http_interactions ||= HTTPInteractionList.new \
    should_stub_requests? ? previously_recorded_interactions : [],
    match_requests_on,
    @allow_playback_repeats,
    @parent_list,
    log_prefix
end
new_recorded_interactions() click to toggle source

@private

# File lib/vcr/cassette.rb, line 89
def new_recorded_interactions
  @new_recorded_interactions ||= []
end
originally_recorded_at() click to toggle source

@return [Time, nil] The ‘recorded_at` time of the first HTTP interaction

or nil if the cassette has no prior HTTP interactions.

@example

VCR.use_cassette("some cassette") do |cassette|
  Timecop.freeze(cassette.originally_recorded_at || Time.now) do
    # ...
  end
end
# File lib/vcr/cassette.rb, line 131
def originally_recorded_at
  @originally_recorded_at ||= previously_recorded_interactions.map(&:recorded_at).min
end
record_http_interaction(interaction) click to toggle source

@private

# File lib/vcr/cassette.rb, line 83
def record_http_interaction(interaction)
  log "Recorded HTTP interaction #{request_summary(interaction.request)} => #{response_summary(interaction.response)}"
  new_recorded_interactions << interaction
end
recording?() click to toggle source

@return [Boolean] Whether or not the cassette is recording.

# File lib/vcr/cassette.rb, line 105
def recording?
  case record_mode
    when :none; false
    when :once; raw_cassette_bytes.to_s.empty?
    else true
  end
end
serializable_hash() click to toggle source

@return [Hash] The hash that will be serialized when the cassette is written to disk.

# File lib/vcr/cassette.rb, line 114
def serializable_hash
  {
    "http_interactions" => interactions_to_record.map(&:to_hash),
    "recorded_with"     => "VCR #{VCR.version}"
  }
end

Private Instance Methods

assert_valid_options!() click to toggle source
# File lib/vcr/cassette.rb, line 137
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
assign_tags() click to toggle source
# File lib/vcr/cassette.rb, line 165
def assign_tags
  @tags = Array(@options.fetch(:tags) { @options[:tag] })

  [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response].each do |tag|
    @tags << tag if @options[tag]
  end
end
deserialized_hash() click to toggle source
# File lib/vcr/cassette.rb, line 270
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
extract_options() click to toggle source
# File lib/vcr/cassette.rb, line 150
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
interactions_to_record() click to toggle source
# File lib/vcr/cassette.rb, line 246
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
invoke_hook(type, interactions) click to toggle source
# File lib/vcr/cassette.rb, line 262
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
log_prefix() click to toggle source
# File lib/vcr/cassette.rb, line 283
def log_prefix
  @log_prefix ||= "[Cassette: '#{name}'] "
end
merged_interactions() click to toggle source
# File lib/vcr/cassette.rb, line 233
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
previously_recorded_interactions() click to toggle source
# File lib/vcr/cassette.rb, line 173
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
raise_error_unless_valid_record_mode() click to toggle source
# File lib/vcr/cassette.rb, line 191
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
raw_cassette_bytes() click to toggle source
# File lib/vcr/cassette.rb, line 229
def raw_cassette_bytes
  @raw_cassette_bytes ||= VCR::Cassette::ERBRenderer.new(@persister[storage_key], erb, name).render
end
request_summary(request) click to toggle source
Calls superclass method VCR::Logger::Mixin#request_summary
# File lib/vcr/cassette.rb, line 287
def request_summary(request)
  super(request, match_requests_on)
end
should_assert_no_unused_interactions?() click to toggle source
# File lib/vcr/cassette.rb, line 225
def should_assert_no_unused_interactions?
  !(@allow_unused_http_interactions || $!)
end
should_re_record?() click to toggle source
# File lib/vcr/cassette.rb, line 197
def should_re_record?
  return false unless @re_record_interval
  return false unless originally_recorded_at

  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
should_remove_matching_existing_interactions?() click to toggle source
# File lib/vcr/cassette.rb, line 221
def should_remove_matching_existing_interactions?
  record_mode == :all
end
should_stub_requests?() click to toggle source
# File lib/vcr/cassette.rb, line 217
def should_stub_requests?
  record_mode != :all
end
storage_key() click to toggle source
# File lib/vcr/cassette.rb, line 187
def storage_key
  @storage_key ||= [name, @serializer.file_extension].join('.')
end
write_recorded_interactions_to_disk() click to toggle source
# File lib/vcr/cassette.rb, line 254
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