2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
4 # See LICENSE.txt for permissions.
14 # The format class knows the guts of the RubyGem .gem file format
15 # and provides the capability to read gem files
18 attr_accessor :spec, :file_entries, :gem_path
21 # Constructs an instance of a Format object, representing the gem's
24 # gem:: [String] The file name of the gem
26 def initialize(gem_path)
31 # Reads the named gem file and returns a Format object, representing
32 # the data from the gem file
34 # file_path:: [String] Path to the gem file
36 def self.from_file_by_path(file_path)
37 unless File.exist?(file_path)
38 raise Gem::Exception, "Cannot load gem file [#{file_path}]"
40 File.open(file_path, 'rb') do |file|
41 from_io(file, file_path)
46 # Reads a gem from an io stream and returns a Format object, representing
47 # the data from the gem file
49 # io:: [IO] Stream from which to read the gem
51 def self.from_io(io, gem_path="(io)")
52 format = self.new(gem_path)
54 format.spec = read_spec(io)
55 format.file_entries = []
56 read_files_from_gem(io) do |entry, file_data|
57 format.file_entries << [entry, file_data]
64 # Skips the Ruby self-install header. After calling this method, the
65 # IO index will be set after the Ruby code.
67 # file:: [IO] The IO to process (skip the Ruby code)
69 def self.skip_ruby(file)
73 if(line == nil || line.chomp == "__END__") then
78 if(end_seen == false) then
79 raise Gem::Exception.new("Failed to find end of ruby script while reading gem")
84 # Reads the specification YAML from the supplied IO and constructs
85 # a Gem::Specification from it. After calling this method, the
86 # IO index will be set after the specification header.
88 # file:: [IO] The IO to process
90 def self.read_spec(file)
93 read_until_dashes(file) do |line|
96 Specification.from_yaml(yaml)
97 rescue YAML::Error => e
98 raise Gem::Exception.new("Failed to parse gem specification out of gem file")
99 rescue ArgumentError => e
100 raise Gem::Exception.new("Failed to parse gem specification out of gem file")
105 # Reads lines from the supplied IO until a end-of-yaml (---) is
108 # file:: [IO] The IO to process
109 # block:: [String] The read line
111 def self.read_until_dashes(file)
112 while((line = file.gets) && line.chomp.strip != "---") do
119 # Reads the embedded file data from a gem file, yielding an entry
120 # containing metadata about the file and the file contents themselves
121 # for each file that's archived in the gem.
122 # NOTE: Many of these methods should be extracted into some kind of
123 # Gem file read/writer
125 # gem_file:: [IO] The IO to process
127 def self.read_files_from_gem(gem_file)
128 errstr = "Error reading files from gem"
131 self.read_until_dashes(gem_file) do |line|
134 header = YAML.load(header_yaml)
135 raise Gem::Exception.new(errstr) unless header
136 header.each do |entry|
138 self.read_until_dashes(gem_file) do |line|
141 yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
143 rescue Exception,Zlib::DataError => e
144 raise Gem::Exception.new(errstr)