Class: Tubeclip::ChainIO

Inherits:
Object
  • Object
show all
Defined in:
lib/tubeclip/chain_io.rb

Overview

Stream wrapper that reads IOs in succession. Can be fed to Net::HTTP as post body stream. We use it internally to stream file content instead of reading whole video files into memory. Strings passed to the constructor will be wrapped in StringIOs. By default it will auto-close file handles when they have been read completely to prevent our uploader from leaking file handles

chain = ChainIO.new(File.open(__FILE__), File.open(‘/etc/passwd’), “abcd”)

Direct Known Subclasses

GreedyChainIO

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*any_ios) ⇒ ChainIO

Returns a new instance of ChainIO.



12
13
14
15
# File 'lib/tubeclip/chain_io.rb', line 12

def initialize(*any_ios)
  @autoclose = true
  @chain = any_ios.flatten.map{|e| e.respond_to?(:read)  ? e : StringIO.new(e.to_s) }
end

Instance Attribute Details

#autocloseObject

Returns the value of attribute autoclose.



10
11
12
# File 'lib/tubeclip/chain_io.rb', line 10

def autoclose
  @autoclose
end

Instance Method Details

#expected_lengthObject

Predict the length of all embedded IOs. Will automatically send file size.



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/tubeclip/chain_io.rb', line 39

def expected_length
  @chain.inject(0) do | len, io |
    if io.respond_to?(:length)
      len + (io.length - io.pos)
    elsif io.is_a?(File)
      len + File.size(io.path) - io.pos
    else
      raise "Cannot predict length of #{io.inspect}"
    end
  end
end

#read(buffer_size = 1024) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/tubeclip/chain_io.rb', line 17

def read(buffer_size = 1024)
  # Read off the first element in the stack
  current_io = @chain.shift
  return false if !current_io

  buf = current_io.read(buffer_size)
  if !buf && @chain.empty? # End of streams
    release_handle(current_io) if @autoclose
    false
  elsif !buf # This IO is depleted, but next one is available
    release_handle(current_io) if @autoclose
    read(buffer_size)
  elsif buf.length < buffer_size # This IO is depleted, but we were asked for more
    release_handle(current_io) if @autoclose
    buf + (read(buffer_size - buf.length) || '') # and recurse
  else # just return the buffer
    @chain.unshift(current_io) # put the current back
    buf
  end
end