gel 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +39 -0
  5. data/exe/gel +13 -0
  6. data/lib/gel.rb +22 -0
  7. data/lib/gel/catalog.rb +153 -0
  8. data/lib/gel/catalog/common.rb +82 -0
  9. data/lib/gel/catalog/compact_index.rb +152 -0
  10. data/lib/gel/catalog/dependency_index.rb +125 -0
  11. data/lib/gel/catalog/legacy_index.rb +157 -0
  12. data/lib/gel/catalog/marshal_hacks.rb +16 -0
  13. data/lib/gel/command.rb +86 -0
  14. data/lib/gel/command/config.rb +11 -0
  15. data/lib/gel/command/env.rb +7 -0
  16. data/lib/gel/command/exec.rb +66 -0
  17. data/lib/gel/command/help.rb +7 -0
  18. data/lib/gel/command/install.rb +7 -0
  19. data/lib/gel/command/install_gem.rb +16 -0
  20. data/lib/gel/command/lock.rb +34 -0
  21. data/lib/gel/command/ruby.rb +10 -0
  22. data/lib/gel/command/shell_setup.rb +25 -0
  23. data/lib/gel/command/stub.rb +12 -0
  24. data/lib/gel/command/update.rb +11 -0
  25. data/lib/gel/compatibility.rb +4 -0
  26. data/lib/gel/compatibility/bundler.rb +54 -0
  27. data/lib/gel/compatibility/bundler/cli.rb +6 -0
  28. data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
  29. data/lib/gel/compatibility/bundler/setup.rb +4 -0
  30. data/lib/gel/compatibility/rubygems.rb +192 -0
  31. data/lib/gel/compatibility/rubygems/command.rb +4 -0
  32. data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
  33. data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
  34. data/lib/gel/config.rb +80 -0
  35. data/lib/gel/db.rb +294 -0
  36. data/lib/gel/direct_gem.rb +29 -0
  37. data/lib/gel/environment.rb +592 -0
  38. data/lib/gel/error.rb +104 -0
  39. data/lib/gel/gemfile_parser.rb +144 -0
  40. data/lib/gel/gemspec_parser.rb +95 -0
  41. data/lib/gel/git_catalog.rb +38 -0
  42. data/lib/gel/git_depot.rb +119 -0
  43. data/lib/gel/httpool.rb +148 -0
  44. data/lib/gel/installer.rb +251 -0
  45. data/lib/gel/lock_loader.rb +164 -0
  46. data/lib/gel/lock_parser.rb +64 -0
  47. data/lib/gel/locked_store.rb +126 -0
  48. data/lib/gel/multi_store.rb +96 -0
  49. data/lib/gel/package.rb +156 -0
  50. data/lib/gel/package/inspector.rb +23 -0
  51. data/lib/gel/package/installer.rb +267 -0
  52. data/lib/gel/path_catalog.rb +44 -0
  53. data/lib/gel/pinboard.rb +140 -0
  54. data/lib/gel/pub_grub/preference_strategy.rb +82 -0
  55. data/lib/gel/pub_grub/source.rb +153 -0
  56. data/lib/gel/runtime.rb +27 -0
  57. data/lib/gel/store.rb +205 -0
  58. data/lib/gel/store_catalog.rb +31 -0
  59. data/lib/gel/store_gem.rb +80 -0
  60. data/lib/gel/stub_set.rb +51 -0
  61. data/lib/gel/support/gem_platform.rb +225 -0
  62. data/lib/gel/support/gem_requirement.rb +264 -0
  63. data/lib/gel/support/gem_version.rb +398 -0
  64. data/lib/gel/support/tar.rb +13 -0
  65. data/lib/gel/support/tar/tar_header.rb +229 -0
  66. data/lib/gel/support/tar/tar_reader.rb +123 -0
  67. data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
  68. data/lib/gel/support/tar/tar_writer.rb +339 -0
  69. data/lib/gel/tail_file.rb +205 -0
  70. data/lib/gel/version.rb +5 -0
  71. data/lib/gel/work_pool.rb +143 -0
  72. data/man/man1/gel-exec.1 +16 -0
  73. data/man/man1/gel-install.1 +16 -0
  74. data/man/man1/gel.1 +30 -0
  75. metadata +131 -0
@@ -0,0 +1,398 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright (c) Chad Fowler, Rich Kilmer, Jim Weirich and others.
4
+ # Portions copyright (c) Engine Yard and Andre Arko
5
+ #
6
+ # Redistributed under the terms of the MIT license
7
+ #
8
+ ##
9
+ # The Version class processes string versions into comparable
10
+ # values. A version string should normally be a series of numbers
11
+ # separated by periods. Each part (digits separated by periods) is
12
+ # considered its own number, and these are used for sorting. So for
13
+ # instance, 3.10 sorts higher than 3.2 because ten is greater than
14
+ # two.
15
+ #
16
+ # If any part contains letters (currently only a-z are supported) then
17
+ # that version is considered prerelease. Versions with a prerelease
18
+ # part in the Nth part sort less than versions with N-1
19
+ # parts. Prerelease parts are sorted alphabetically using the normal
20
+ # Ruby string sorting rules. If a prerelease part contains both
21
+ # letters and numbers, it will be broken into multiple parts to
22
+ # provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is
23
+ # greater than 1.0.a9).
24
+ #
25
+ # Prereleases sort between real releases (newest to oldest):
26
+ #
27
+ # 1. 1.0
28
+ # 2. 1.0.b1
29
+ # 3. 1.0.a.2
30
+ # 4. 0.9
31
+ #
32
+ # If you want to specify a version restriction that includes both prereleases
33
+ # and regular releases of the 1.x series this is the best way:
34
+ #
35
+ # s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
36
+ #
37
+ # == How Software Changes
38
+ #
39
+ # Users expect to be able to specify a version constraint that gives them
40
+ # some reasonable expectation that new versions of a library will work with
41
+ # their software if the version constraint is true, and not work with their
42
+ # software if the version constraint is false. In other words, the perfect
43
+ # system will accept all compatible versions of the library and reject all
44
+ # incompatible versions.
45
+ #
46
+ # Libraries change in 3 ways (well, more than 3, but stay focused here!).
47
+ #
48
+ # 1. The change may be an implementation detail only and have no effect on
49
+ # the client software.
50
+ # 2. The change may add new features, but do so in a way that client software
51
+ # written to an earlier version is still compatible.
52
+ # 3. The change may change the public interface of the library in such a way
53
+ # that old software is no longer compatible.
54
+ #
55
+ # Some examples are appropriate at this point. Suppose I have a Stack class
56
+ # that supports a <tt>push</tt> and a <tt>pop</tt> method.
57
+ #
58
+ # === Examples of Category 1 changes:
59
+ #
60
+ # * Switch from an array based implementation to a linked-list based
61
+ # implementation.
62
+ # * Provide an automatic (and transparent) backing store for large stacks.
63
+ #
64
+ # === Examples of Category 2 changes might be:
65
+ #
66
+ # * Add a <tt>depth</tt> method to return the current depth of the stack.
67
+ # * Add a <tt>top</tt> method that returns the current top of stack (without
68
+ # changing the stack).
69
+ # * Change <tt>push</tt> so that it returns the item pushed (previously it
70
+ # had no usable return value).
71
+ #
72
+ # === Examples of Category 3 changes might be:
73
+ #
74
+ # * Changes <tt>pop</tt> so that it no longer returns a value (you must use
75
+ # <tt>top</tt> to get the top of the stack).
76
+ # * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.
77
+ #
78
+ # == RubyGems Rational Versioning
79
+ #
80
+ # * Versions shall be represented by three non-negative integers, separated
81
+ # by periods (e.g. 3.1.4). The first integers is the "major" version
82
+ # number, the second integer is the "minor" version number, and the third
83
+ # integer is the "build" number.
84
+ #
85
+ # * A category 1 change (implementation detail) will increment the build
86
+ # number.
87
+ #
88
+ # * A category 2 change (backwards compatible) will increment the minor
89
+ # version number and reset the build number.
90
+ #
91
+ # * A category 3 change (incompatible) will increment the major build number
92
+ # and reset the minor and build numbers.
93
+ #
94
+ # * Any "public" release of a gem should have a different version. Normally
95
+ # that means incrementing the build number. This means a developer can
96
+ # generate builds all day long, but as soon as they make a public release,
97
+ # the version must be updated.
98
+ #
99
+ # === Examples
100
+ #
101
+ # Let's work through a project lifecycle using our Stack example from above.
102
+ #
103
+ # Version 0.0.1:: The initial Stack class is release.
104
+ # Version 0.0.2:: Switched to a linked=list implementation because it is
105
+ # cooler.
106
+ # Version 0.1.0:: Added a <tt>depth</tt> method.
107
+ # Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil
108
+ # (<tt>pop</tt> used to return the old top item).
109
+ # Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it
110
+ # return nil).
111
+ # Version 1.1.1:: Fixed a bug in the linked list implementation.
112
+ # Version 1.1.2:: Fixed a bug introduced in the last fix.
113
+ #
114
+ # Client A needs a stack with basic push/pop capability. They write to the
115
+ # original interface (no <tt>top</tt>), so their version constraint looks like:
116
+ #
117
+ # gem 'stack', '>= 0.0'
118
+ #
119
+ # Essentially, any version is OK with Client A. An incompatible change to
120
+ # the library will cause them grief, but they are willing to take the chance
121
+ # (we call Client A optimistic).
122
+ #
123
+ # Client B is just like Client A except for two things: (1) They use the
124
+ # <tt>depth</tt> method and (2) they are worried about future
125
+ # incompatibilities, so they write their version constraint like this:
126
+ #
127
+ # gem 'stack', '~> 0.1'
128
+ #
129
+ # The <tt>depth</tt> method was introduced in version 0.1.0, so that version
130
+ # or anything later is fine, as long as the version stays below version 1.0
131
+ # where incompatibilities are introduced. We call Client B pessimistic
132
+ # because they are worried about incompatible future changes (it is OK to be
133
+ # pessimistic!).
134
+ #
135
+ # == Preventing Version Catastrophe:
136
+ #
137
+ # From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
138
+ #
139
+ # Let's say you're depending on the fnord gem version 2.y.z. If you
140
+ # specify your dependency as ">= 2.0.0" then, you're good, right? What
141
+ # happens if fnord 3.0 comes out and it isn't backwards compatible
142
+ # with 2.y.z? Your stuff will break as a result of using ">=". The
143
+ # better route is to specify your dependency with an "approximate" version
144
+ # specifier ("~>"). They're a tad confusing, so here is how the dependency
145
+ # specifiers work:
146
+ #
147
+ # Specification From ... To (exclusive)
148
+ # ">= 3.0" 3.0 ... &infin;
149
+ # "~> 3.0" 3.0 ... 4.0
150
+ # "~> 3.0.0" 3.0.0 ... 3.1
151
+ # "~> 3.5" 3.5 ... 4.0
152
+ # "~> 3.5.0" 3.5.0 ... 3.6
153
+ # "~> 3" 3.0 ... 4.0
154
+ #
155
+ # For the last example, single-digit versions are automatically extended with
156
+ # a zero to give a sensible result.
157
+
158
+ class Gel::Support::GemVersion
159
+ include Comparable
160
+
161
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
162
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
163
+
164
+ ##
165
+ # A string representation of this Version.
166
+
167
+ def version
168
+ @version.dup
169
+ end
170
+
171
+ alias to_s version
172
+
173
+ ##
174
+ # True if the +version+ string matches RubyGems' requirements.
175
+
176
+ def self.correct? version
177
+ !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
178
+ end
179
+
180
+ ##
181
+ # Factory method to create a Version object. Input may be a Version
182
+ # or a String. Intended to simplify client code.
183
+ #
184
+ # ver1 = Version.create('1.3.17') # -> (Version object)
185
+ # ver2 = Version.create(ver1) # -> (ver1)
186
+ # ver3 = Version.create(nil) # -> nil
187
+
188
+ def self.create input
189
+ if self === input then # check yourself before you wreck yourself
190
+ input
191
+ elsif input.nil? then
192
+ nil
193
+ else
194
+ new input
195
+ end
196
+ end
197
+
198
+ @@all = {}
199
+
200
+ def self.new version # :nodoc:
201
+ return super unless Gel::Support::GemVersion == self
202
+
203
+ @@all[version] ||= super
204
+ end
205
+
206
+ ##
207
+ # Constructs a Version from the +version+ string. A version string is a
208
+ # series of digits or ASCII letters separated by dots.
209
+
210
+ def initialize version
211
+ version = version.to_s.strip
212
+
213
+ unless self.class.correct?(version)
214
+ raise ArgumentError, "Malformed version number string #{version}"
215
+ end
216
+
217
+ # If version is an empty string convert it to 0
218
+ version = "0" if version.empty?
219
+
220
+ @version = version.gsub("-",".pre.")
221
+ @segments = nil
222
+ end
223
+
224
+ ##
225
+ # Return a new version object where the next to the last revision
226
+ # number is one greater (e.g., 5.3.1 => 5.4).
227
+ #
228
+ # Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
229
+
230
+ def bump
231
+ @bump ||= begin
232
+ segments = self.segments
233
+ segments.pop while segments.any? { |s| String === s }
234
+ segments.pop if segments.size > 1
235
+
236
+ segments[-1] = segments[-1].succ
237
+ self.class.new segments.join(".")
238
+ end
239
+ end
240
+
241
+ ##
242
+ # A Version is only eql? to another version if it's specified to the
243
+ # same precision. Version "1.0" is not the same as version "1".
244
+
245
+ def eql? other
246
+ self.class === other and @version == other._version
247
+ end
248
+
249
+ def hash # :nodoc:
250
+ canonical_segments.hash
251
+ end
252
+
253
+ def init_with coder # :nodoc:
254
+ yaml_initialize coder.tag, coder.map
255
+ end
256
+
257
+ def inspect # :nodoc:
258
+ "#<#{self.class} #{version.inspect}>"
259
+ end
260
+
261
+ ##
262
+ # Dump only the raw version string, not the complete object. It's a
263
+ # string for backwards (RubyGems 1.3.5 and earlier) compatibility.
264
+
265
+ def marshal_dump
266
+ [version]
267
+ end
268
+
269
+ ##
270
+ # Load custom marshal format. It's a string for backwards (RubyGems
271
+ # 1.3.5 and earlier) compatibility.
272
+
273
+ def marshal_load array
274
+ initialize array[0]
275
+ end
276
+
277
+ def yaml_initialize(tag, map) # :nodoc:
278
+ @version = map['version']
279
+ @segments = nil
280
+ @hash = nil
281
+ end
282
+
283
+ def to_yaml_properties # :nodoc:
284
+ ["@version"]
285
+ end
286
+
287
+ def encode_with coder # :nodoc:
288
+ coder.add 'version', @version
289
+ end
290
+
291
+ ##
292
+ # A version is considered a prerelease if it contains a letter.
293
+
294
+ def prerelease?
295
+ unless instance_variable_defined? :@prerelease
296
+ @prerelease = !!(@version =~ /[a-zA-Z]/)
297
+ end
298
+ @prerelease
299
+ end
300
+
301
+ def pretty_print q # :nodoc:
302
+ q.text "Gel::Support::GemVersion.new(#{version.inspect})"
303
+ end
304
+
305
+ ##
306
+ # The release for this version (e.g. 1.2.0.a -> 1.2.0).
307
+ # Non-prerelease versions return themselves.
308
+
309
+ def release
310
+ @release ||= if prerelease?
311
+ segments = self.segments
312
+ segments.pop while segments.any? { |s| String === s }
313
+ self.class.new segments.join('.')
314
+ else
315
+ self
316
+ end
317
+ end
318
+
319
+ def segments # :nodoc:
320
+ _segments.dup
321
+ end
322
+
323
+ ##
324
+ # A recommended version for use with a ~> Requirement.
325
+
326
+ def approximate_recommendation
327
+ segments = self.segments
328
+
329
+ segments.pop while segments.any? { |s| String === s }
330
+ segments.pop while segments.size > 2
331
+ segments.push 0 while segments.size < 2
332
+
333
+ "~> #{segments.join(".")}"
334
+ end
335
+
336
+ ##
337
+ # Compares this version with +other+ returning -1, 0, or 1 if the
338
+ # other version is larger, the same, or smaller than this
339
+ # one. Attempts to compare to something that's not a
340
+ # <tt>Gem::Version</tt> return +nil+.
341
+
342
+ def <=> other
343
+ return unless Gel::Support::GemVersion === other
344
+ return 0 if @version == other._version || canonical_segments == other.canonical_segments
345
+
346
+ lhsegments = _segments
347
+ rhsegments = other._segments
348
+
349
+ lhsize = lhsegments.size
350
+ rhsize = rhsegments.size
351
+ limit = (lhsize > rhsize ? lhsize : rhsize) - 1
352
+
353
+ i = 0
354
+
355
+ while i <= limit
356
+ lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
357
+ i += 1
358
+
359
+ next if lhs == rhs
360
+ return -1 if String === lhs && Numeric === rhs
361
+ return 1 if Numeric === lhs && String === rhs
362
+
363
+ return lhs <=> rhs
364
+ end
365
+
366
+ return 0
367
+ end
368
+
369
+ def canonical_segments
370
+ @canonical_segments ||=
371
+ _split_segments.map! do |segments|
372
+ segments.reverse_each.drop_while {|s| s == 0 }.reverse
373
+ end.reduce(&:concat)
374
+ end
375
+
376
+ protected
377
+
378
+ def _version
379
+ @version
380
+ end
381
+
382
+ def _segments
383
+ # segments is lazy so it can pick up version values that come from
384
+ # old marshaled versions, which don't go through marshal_load.
385
+ # since this version object is cached in @@all, its @segments should be frozen
386
+
387
+ @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
388
+ /^\d+$/ =~ s ? s.to_i : s
389
+ end.freeze
390
+ end
391
+
392
+ def _split_segments
393
+ string_start = _segments.index {|s| s.is_a?(String) }
394
+ string_segments = segments
395
+ numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
396
+ return numeric_segments, string_segments
397
+ end
398
+ end
@@ -0,0 +1,13 @@
1
+ module Gel::Support
2
+ module Tar
3
+ class Error < ::RuntimeError; end
4
+
5
+ class NonSeekableIO < Error; end
6
+ class TooLongFileName < Error; end
7
+ class TarInvalidError < Error; end
8
+ end
9
+ end
10
+
11
+ require_relative "tar/tar_header"
12
+ require_relative "tar/tar_reader"
13
+ require_relative "tar/tar_writer"
@@ -0,0 +1,229 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ #--
4
+ # Copyright (C) 2004 Mauricio Julio Fernández Pradier
5
+ # Redistributed under the terms of the MIT license.
6
+ #++
7
+
8
+ ##
9
+ #--
10
+ # struct tarfile_entry_posix {
11
+ # char name[100]; # ASCII + (Z unless filled)
12
+ # char mode[8]; # 0 padded, octal, null
13
+ # char uid[8]; # ditto
14
+ # char gid[8]; # ditto
15
+ # char size[12]; # 0 padded, octal, null
16
+ # char mtime[12]; # 0 padded, octal, null
17
+ # char checksum[8]; # 0 padded, octal, null, space
18
+ # char typeflag[1]; # file: "0" dir: "5"
19
+ # char linkname[100]; # ASCII + (Z unless filled)
20
+ # char magic[6]; # "ustar\0"
21
+ # char version[2]; # "00"
22
+ # char uname[32]; # ASCIIZ
23
+ # char gname[32]; # ASCIIZ
24
+ # char devmajor[8]; # 0 padded, octal, null
25
+ # char devminor[8]; # o padded, octal, null
26
+ # char prefix[155]; # ASCII + (Z unless filled)
27
+ # };
28
+ #++
29
+ # A header for a tar file
30
+
31
+ class Gel::Support::Tar::TarHeader
32
+
33
+ ##
34
+ # Fields in the tar header
35
+
36
+ FIELDS = [
37
+ :checksum,
38
+ :devmajor,
39
+ :devminor,
40
+ :gid,
41
+ :gname,
42
+ :linkname,
43
+ :magic,
44
+ :mode,
45
+ :mtime,
46
+ :name,
47
+ :prefix,
48
+ :size,
49
+ :typeflag,
50
+ :uid,
51
+ :uname,
52
+ :version,
53
+ ]
54
+
55
+ ##
56
+ # Pack format for a tar header
57
+
58
+ PACK_FORMAT = 'a100' + # name
59
+ 'a8' + # mode
60
+ 'a8' + # uid
61
+ 'a8' + # gid
62
+ 'a12' + # size
63
+ 'a12' + # mtime
64
+ 'a7a' + # chksum
65
+ 'a' + # typeflag
66
+ 'a100' + # linkname
67
+ 'a6' + # magic
68
+ 'a2' + # version
69
+ 'a32' + # uname
70
+ 'a32' + # gname
71
+ 'a8' + # devmajor
72
+ 'a8' + # devminor
73
+ 'a155' # prefix
74
+
75
+ ##
76
+ # Unpack format for a tar header
77
+
78
+ UNPACK_FORMAT = 'A100' + # name
79
+ 'A8' + # mode
80
+ 'A8' + # uid
81
+ 'A8' + # gid
82
+ 'A12' + # size
83
+ 'A12' + # mtime
84
+ 'A8' + # checksum
85
+ 'A' + # typeflag
86
+ 'A100' + # linkname
87
+ 'A6' + # magic
88
+ 'A2' + # version
89
+ 'A32' + # uname
90
+ 'A32' + # gname
91
+ 'A8' + # devmajor
92
+ 'A8' + # devminor
93
+ 'A155' # prefix
94
+
95
+ attr_reader(*FIELDS)
96
+
97
+ ##
98
+ # Creates a tar header from IO +stream+
99
+
100
+ def self.from(stream)
101
+ header = stream.read 512
102
+ empty = (header == "\0" * 512)
103
+
104
+ fields = header.unpack UNPACK_FORMAT
105
+
106
+ new :name => fields.shift,
107
+ :mode => fields.shift.oct,
108
+ :uid => fields.shift.oct,
109
+ :gid => fields.shift.oct,
110
+ :size => fields.shift.oct,
111
+ :mtime => fields.shift.oct,
112
+ :checksum => fields.shift.oct,
113
+ :typeflag => fields.shift,
114
+ :linkname => fields.shift,
115
+ :magic => fields.shift,
116
+ :version => fields.shift.oct,
117
+ :uname => fields.shift,
118
+ :gname => fields.shift,
119
+ :devmajor => fields.shift.oct,
120
+ :devminor => fields.shift.oct,
121
+ :prefix => fields.shift,
122
+
123
+ :empty => empty
124
+ end
125
+
126
+ ##
127
+ # Creates a new TarHeader using +vals+
128
+
129
+ def initialize(vals)
130
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
131
+ raise ArgumentError, ":name, :size, :prefix and :mode required"
132
+ end
133
+
134
+ vals[:uid] ||= 0
135
+ vals[:gid] ||= 0
136
+ vals[:mtime] ||= 0
137
+ vals[:checksum] ||= ""
138
+ vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
139
+ vals[:magic] ||= "ustar"
140
+ vals[:version] ||= "00"
141
+ vals[:uname] ||= "wheel"
142
+ vals[:gname] ||= "wheel"
143
+ vals[:devmajor] ||= 0
144
+ vals[:devminor] ||= 0
145
+
146
+ FIELDS.each do |name|
147
+ instance_variable_set "@#{name}", vals[name]
148
+ end
149
+
150
+ @empty = vals[:empty]
151
+ end
152
+
153
+ ##
154
+ # Is the tar entry empty?
155
+
156
+ def empty?
157
+ @empty
158
+ end
159
+
160
+ def ==(other) # :nodoc:
161
+ self.class === other and
162
+ @checksum == other.checksum and
163
+ @devmajor == other.devmajor and
164
+ @devminor == other.devminor and
165
+ @gid == other.gid and
166
+ @gname == other.gname and
167
+ @linkname == other.linkname and
168
+ @magic == other.magic and
169
+ @mode == other.mode and
170
+ @mtime == other.mtime and
171
+ @name == other.name and
172
+ @prefix == other.prefix and
173
+ @size == other.size and
174
+ @typeflag == other.typeflag and
175
+ @uid == other.uid and
176
+ @uname == other.uname and
177
+ @version == other.version
178
+ end
179
+
180
+ def to_s # :nodoc:
181
+ update_checksum
182
+ header
183
+ end
184
+
185
+ ##
186
+ # Updates the TarHeader's checksum
187
+
188
+ def update_checksum
189
+ header = header " " * 8
190
+ @checksum = oct calculate_checksum(header), 6
191
+ end
192
+
193
+ private
194
+
195
+ def calculate_checksum(header)
196
+ header.unpack("C*").inject { |a, b| a + b }
197
+ end
198
+
199
+ def header(checksum = @checksum)
200
+ header = [
201
+ name,
202
+ oct(mode, 7),
203
+ oct(uid, 7),
204
+ oct(gid, 7),
205
+ oct(size, 11),
206
+ oct(mtime, 11),
207
+ checksum,
208
+ " ",
209
+ typeflag,
210
+ linkname,
211
+ magic,
212
+ oct(version, 2),
213
+ uname,
214
+ gname,
215
+ oct(devmajor, 7),
216
+ oct(devminor, 7),
217
+ prefix
218
+ ]
219
+
220
+ header = header.pack PACK_FORMAT
221
+
222
+ header << ("\0" * ((512 - header.size) % 512))
223
+ end
224
+
225
+ def oct(num, len)
226
+ "%0#{len}o" % num
227
+ end
228
+
229
+ end