Class: Archiverb

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/archiverb.rb,
lib/archiverb/ar.rb,
lib/archiverb/tar.rb,
lib/archiverb/file.rb,
lib/archiverb/stat.rb,
lib/archiverb/version.rb

Overview

Provides a common interface for working with different types of archive formats.

Examples:

arc = Archiverb::Ar.new("junk.ar", "a.txt", "b.txt", "c.txt")
arc.write("/tmp/junk.ar")  # => creates /tmp/junk.ar with {a,b,c}.txt

Direct Known Subclasses

Ar, Tar

Defined Under Namespace

Modules: Error Classes: AbstractMethod, Ar, ArgumentError, File, InvalidFormat, StandarError, Stat, Tar, WrongChksum

Constant Summary collapse

VERSION =
'0.9.0'

Instance Method Summary collapse

Constructor Details

#initialize(path_or_io = nil, *files, &blk) ⇒ Archiverb

Returns a new instance of Archiverb.

Raises:

  • (NotImplementedError)


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

def initialize(path_or_io = nil, *files, &blk)
  raise NotImplementedError if self.class == Archiverb
  @opts = files.last.is_a?(Hash) ? files.pop : {}
  files.each { |file|  add(file) }
  if block_given?
    r, w = IO.pipe
    @source = lambda do
      blk.call(w)
      w.close unless w.closed?
      r
    end
  else
    @source = lambda { path_or_io.is_a?(String) ? ::File.new(path_or_io, "a+").tap{|f| f.rewind } : path_or_io }
  end
  @out = path_or_io
  @files = {}
end

Instance Method Details

#[](file) ⇒ Object



46
47
48
# File 'lib/archiverb.rb', line 46

def [](file)
  @files[file]
end

#add(name, opts = {}, io = nil, &blk) ⇒ Object

Add a file to the archive.

Parameters:

  • (String, File, IO)
  • opts (Hash) (defaults to: {})

    options to pass to Stat.new

  • io (IO, ::File, String, StringIO) (defaults to: nil)


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
# File 'lib/archiverb.rb', line 82

def add(name, opts = {}, io = nil, &blk)
  if block_given?
    @files[name] = File.new(name, *IO.pipe, &blk)
    return self
  end

  if io
    opts, io = io, opts if io.is_a?(Hash)
  elsif !opts.is_a?(Hash)
    opts, io = {}, opts
  end

  if io
    if io.is_a?(String) || io.is_a?(StringIO) || io.is_a?(IO) || io.is_a?(::File)
      @files[name] = File.new(name, io, Stat.new(io, opts))
    else
      raise ArgumentError.new("unsupported data source: #{io.class}")
    end
  else
    case name
    when String
      fio = ::File.exists?(name) ? ::File.new(name, "r") : ""
      @files[name] = File.new(name, fio, Stat.new(fio, opts))
    when ::File
      @files[name.path] = File.new(name.path, name, Stat.new(name, opts))
    else
      opts[:name] = name.respond_to?(:path) ? name.path : name.__id__.to_s if opts[:name].nil?
      @files[opts[:name]] = File.new(opts[:name], name, Stat.new(name, opts))
    end
  end

  self
end

#countObject



60
61
62
# File 'lib/archiverb.rb', line 60

def count
  @files.keys.length
end

#each {||name, file|| ... } ⇒ Object

Iterate over each file.

Yields:

  • (|name, file|)


52
53
54
# File 'lib/archiverb.rb', line 52

def each(&blk)
  @files.each(&blk)
end

#filesObject



42
43
44
# File 'lib/archiverb.rb', line 42

def files
  @files.values
end

#namesObject



56
57
58
# File 'lib/archiverb.rb', line 56

def names
  @files.keys
end

#pathObject



38
39
40
# File 'lib/archiverb.rb', line 38

def path
  @out
end

#readObject

TODO:

take a block and yield each file as it’s read without storing it in #files

Pulls each file out of the archive and places them in #files.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/archiverb.rb', line 66

def read
  return self if @source.nil?
  io = @source.call
  io.respond_to?(:binmode) && io.binmode
  preprocess(io)
  while (header = next_header(io))
    @files[header[:name]] = File.new(header[:name], read_file(header, io), Stat.new(header))
  end
  io.close
  self
end

#write(path = @out, &blk) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/archiverb.rb', line 116

def write(path = @out, &blk)
  if block_given?
    # use a pipe instead?
    yield StringIO.new.tap { |io| write_to(io) }.string
  elsif path.is_a?(String)
    ::File.open(path, "w") { |io| write_to(io) }
  else
    path.respond_to?(:truncate) && path.truncate
    write_to(path)
  end
  self
end