jartools 0.0.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.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jartools.gemspec
4
+ gemspec
@@ -0,0 +1,66 @@
1
+ jartools
2
+ ========
3
+
4
+ `jartools` is a command-line utility for examining java archives (JARs
5
+ and WARs). It is a complement to (not a replacement for) the standard
6
+ `jar` utility.
7
+
8
+ It is designed to use streams in the standard unix style so it can be
9
+ composed with other stream-processing tools (e.g., `grep`, `xargs`).
10
+
11
+ Installing `jartools`
12
+ ---------------------
13
+
14
+ `jartools` is distributed as a rubygem. Install it like so:
15
+
16
+ $ gem install jartools
17
+
18
+ Depending on how your ruby is installed, you may need root privileges
19
+ to do this.
20
+
21
+ It's been tested on Ruby 1.8.7, JRuby 1.5.6, and Ruby 1.9.2.
22
+
23
+ Using `jartools`
24
+ ----------------
25
+
26
+ To see what tools are included in the version you have installed, you
27
+ can do this:
28
+
29
+ $ jartools help
30
+
31
+ To get details on a particular tool, use, e.g.:
32
+
33
+ $ jartools help packages
34
+
35
+ This online help tells you what arguments may be passed to each command.
36
+
37
+ Tools included
38
+ --------------
39
+
40
+ ### packages
41
+
42
+ Lists all the packages present in a JAR.
43
+
44
+ ### manifest
45
+
46
+ Prints the JAR's manifest (if any) to standard out.
47
+
48
+ ### diff
49
+
50
+ Does a diff of two JARs or WARs, including file content diffs and
51
+ recursive diffs of contained JARs.
52
+
53
+ Project links
54
+ -------------
55
+
56
+ * [Continuous integration](https://ctms-ci.nubic.northwestern.edu/hudson/job/jartools/)
57
+ * [Issue tracking](http://github.com/rsutphin/jartools/issues)
58
+
59
+ Non-issue questions can be sent to rhett@detailedbalance.net.
60
+
61
+ About
62
+ -----
63
+
64
+ `jartools` is copyright 2010 Rhett Sutphin. It was built at [NUBIC][].
65
+
66
+ [NUBIC]: http://www.nucats.northwestern.edu/centers/nubic
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'ci/reporter/rake/rspec'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ desc "Run all specs"
8
+ RSpec::Core::RakeTask.new('spec') do |t|
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ t.verbose = true
11
+ end
12
+
13
+ namespace :ci do
14
+ ENV["CI_REPORTS"] = "reports/spec-xml"
15
+
16
+ desc "Run specs for CI"
17
+ task :spec => ['ci:setup:rspec', 'rake:spec']
18
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jartools'
4
+
5
+ begin
6
+ JarTools::CLI.start
7
+ rescue Interrupt => e
8
+ $stderr.puts "Interrupted"
9
+ exit 1
10
+ end
@@ -0,0 +1,5 @@
1
+ module JarTools
2
+ autoload :CLI, 'jartools/cli'
3
+ autoload :Diff, 'jartools/diff'
4
+ autoload :VERSION, 'jartools/version'
5
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'jartools'
3
+ require 'thor'
4
+
5
+ require 'zip/zipfilesystem'
6
+
7
+ module JarTools
8
+ class CLI < Thor
9
+ desc "packages JARFILE", "Print the java packages found in the JAR to standard out"
10
+ long_desc <<-DESC
11
+ Prints all the java packages found in the specified JAR to standard out, one per line.
12
+ "Packages" are defined as unique paths in the JAR which contain at least one class.
13
+ DESC
14
+ def packages(jarfile)
15
+ entry_names = []
16
+ Zip::ZipFile.foreach(jarfile) { |entry|
17
+ entry_names << entry.name
18
+ }
19
+
20
+ entry_names.select { |e| e =~ /\.class$/ }.
21
+ collect { |e| e.sub(/\/[^\/]+$/, '').gsub('/', '.') }.
22
+ uniq.sort.each { |pkg| say pkg }
23
+ end
24
+
25
+ desc "manifest JARFILE", "Print the manifest found in the JAR to standard out"
26
+ long_desc <<-DESC
27
+ Prints the META-INF/MANIFEST.MF if one is found in the JAR. To ease examination,
28
+ lines are unwrapped. Use --raw to get the original manifest with wrapped lines.
29
+ DESC
30
+ method_option :raw, :type => :boolean,
31
+ :desc => "Print the manifest exactly as it appears in the JAR."
32
+ def manifest(jarfile)
33
+ content = Zip::ZipFile.open(jarfile) { |zip|
34
+ zip.file.read("META-INF/MANIFEST.MF") if zip.file.exists?("META-INF/MANIFEST.MF")
35
+ }
36
+ return unless content
37
+ if options.raw
38
+ say content
39
+ else
40
+ say content.gsub("\r\n", "\n").gsub("\r", "\n").split("\n").inject([]) { |result, raw_line|
41
+ if raw_line =~ /^\s/
42
+ result[-1] += raw_line.slice(1, raw_line.size)
43
+ else
44
+ result << raw_line
45
+ end
46
+ result
47
+ }.join("\n")
48
+ end
49
+ end
50
+
51
+ desc "diff JARFILE1 JARFILE2", "Recursively find differences in two JARs or WARs"
52
+ def diff(jar1, jar2)
53
+ Diff.diff(jar1, jar2)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,237 @@
1
+ require 'jartools'
2
+
3
+ require 'diff/lcs'
4
+ require 'diff/lcs/hunk'
5
+ require 'digest/sha1'
6
+
7
+ require 'tempfile'
8
+ require 'zip/zip'
9
+
10
+ module JarTools
11
+ module Diff
12
+ def self.diff(jar1, jar2)
13
+ self.compare(ActualFile.new(jar1), ActualFile.new(jar2), [])
14
+ end
15
+
16
+ private
17
+
18
+ ##
19
+ # @param [ActualFile,ExtractedFile] jar1
20
+ # @param [ActualFile,ExtractedFile] jar2
21
+ # @param [Array<String>] container_files
22
+ def self.compare(jar1, jar2, container_files = [], roots = [])
23
+ # skip check if the files are identical
24
+ return if jar1.sha1 == jar2.sha1
25
+ roots = [jar1, jar2] if roots.empty?
26
+
27
+ entries1 = EntryIterator.new(jar1, container_files)
28
+ entries2 = EntryIterator.new(jar2, container_files)
29
+
30
+ until entries1.exhausted? && entries2.exhausted?
31
+ if entries2.exhausted? || (!entries1.exhausted? && entries1.current < entries2.current)
32
+ puts missing_file_message(entries1.current.display_name, roots.first, roots.last)
33
+ entries1.advance
34
+ elsif entries1.exhausted? || entries1.current > entries2.current
35
+ puts missing_file_message(entries2.current.display_name, roots.last, roots.first)
36
+ entries2.advance
37
+ else
38
+ # at this point, the entry names are identical so we can
39
+ # examine just one
40
+ if entries1.current.archive?
41
+ self.compare(entries1.current.extract, entries2.current.extract,
42
+ container_files + [entries1.current.path], roots)
43
+ elsif entries1.current.extract.sha1 != entries2.current.extract.sha1
44
+ if entries1.current.binary? || entries2.current.binary?
45
+ puts mismatched_binary_file_message(entries1.current.display_name)
46
+ else
47
+ puts mismatched_text_file_message(entries1.current.display_name,
48
+ entries1.current.extract, entries2.current.extract)
49
+ end
50
+ else
51
+ # identical in both
52
+ end
53
+
54
+ entries1.advance
55
+ entries2.advance
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.missing_file_message(display_name, is_in, is_not_in)
61
+ "#{display_name} is in #{is_in.readable_filename} but not in #{is_not_in.readable_filename}"
62
+ end
63
+
64
+ def self.mismatched_binary_file_message(display_name)
65
+ "#{display_name} differs"
66
+ end
67
+
68
+ def self.mismatched_text_file_message(display_name, one, two)
69
+ diff = TextDiff.new(one.contents, two.contents)
70
+ "#{display_name} text differs:\n#{diff.text_description}\n"
71
+ end
72
+
73
+ class EntryIterator
74
+ attr_accessor :index
75
+
76
+ def initialize(jarfile, container_files)
77
+ @jarfile = jarfile
78
+ @entries = read_directory
79
+ @container_files = container_files
80
+ @index = 0
81
+ @current_entry_cache = nil
82
+ end
83
+
84
+ def current
85
+ unless exhausted?
86
+ @current_entry_cache ||=
87
+ AnalyzableEntry.new(@jarfile, @entries[index], @container_files)
88
+ end
89
+ end
90
+
91
+ def exhausted?
92
+ index >= @entries.size
93
+ end
94
+
95
+ def advance
96
+ @index += 1
97
+ @current_entry_cache = nil
98
+ end
99
+
100
+ private
101
+
102
+ def read_directory
103
+ entries = []
104
+ Zip::ZipFile.foreach(@jarfile.readable_filename) do |ze|
105
+ entries << ze.name
106
+ end
107
+ entries.sort
108
+ end
109
+
110
+ class AnalyzableEntry
111
+ include Comparable
112
+
113
+ attr_reader :path
114
+
115
+ def initialize(jarfile, path, container_files)
116
+ @jarfile = jarfile
117
+ @path = path
118
+ @container_files = container_files
119
+ end
120
+
121
+ def display_name
122
+ (@container_files + [path]).join(" | ")
123
+ end
124
+
125
+ def extract
126
+ @extract ||= Zip::ZipFile.open(@jarfile.readable_filename) do |z|
127
+ ExtractedFile.new(z.get_entry(path))
128
+ end
129
+ end
130
+
131
+ def archive?
132
+ path =~ /(zip|jar|war)$/
133
+ end
134
+
135
+ # This logic is odd, but it's what grep does, apparently
136
+ def binary?
137
+ extract.contents.match("\000")
138
+ end
139
+
140
+ def <=>(other)
141
+ path <=> other.path
142
+ end
143
+ end
144
+ end
145
+
146
+ class UniformFile
147
+ def contents
148
+ @contents ||= if RUBY_VERSION =~ /1.8/
149
+ File.read(readable_filename)
150
+ else
151
+ File.read(readable_filename, :encoding => 'binary')
152
+ end
153
+ end
154
+
155
+ def sha1
156
+ @sha1 ||= Digest::SHA1.hexdigest(contents)
157
+ end
158
+ end
159
+
160
+ class ExtractedFile < UniformFile
161
+ attr_reader :entry_path
162
+
163
+ def initialize(zip_entry)
164
+ @entry_path = zip_entry.name
165
+ @temp_file = Tempfile.new(File.basename(@entry_path))
166
+ File.open(@temp_file.path, 'w') do |f|
167
+ zip_entry.get_input_stream do |ze|
168
+ f.write ze.read
169
+ end
170
+ end
171
+ end
172
+
173
+ def readable_filename
174
+ @temp_file.path
175
+ end
176
+ end
177
+
178
+ class ActualFile < UniformFile
179
+ attr_reader :entry_path, :readable_filename
180
+
181
+ def initialize(filename)
182
+ @entry_path = @readable_filename = filename
183
+ end
184
+ end
185
+
186
+ class TextDiff
187
+ def initialize(a, b)
188
+ @s1 = a
189
+ @s2 = b
190
+ end
191
+
192
+ def different?
193
+ @s1 != @s2
194
+ end
195
+
196
+ def text_description
197
+ printable_diff if different?
198
+ end
199
+
200
+ private
201
+
202
+ # Adapted from Diff::LCS::Ldiff by way of Rspec::Expectations::Differ
203
+ def printable_diff
204
+ data_old = @s1.split(/\n/).map! { |e| e.chomp }
205
+ data_new = @s2.split(/\n/).map! { |e| e.chomp }
206
+ output = ""
207
+ diffs = ::Diff::LCS.diff(data_old, data_new)
208
+ return output if diffs.empty?
209
+ oldhunk = hunk = nil
210
+ file_length_difference = 0
211
+ diffs.each do |piece|
212
+ begin
213
+ hunk = ::Diff::LCS::Hunk.new(
214
+ data_old, data_new, piece, 3, file_length_difference
215
+ )
216
+ file_length_difference = hunk.file_length_difference
217
+ next unless oldhunk
218
+ # Hunks may overlap, which is why we need to be careful when our
219
+ # diff includes lines of context. Otherwise, we might print
220
+ # redundant lines.
221
+ if hunk.overlaps?(oldhunk)
222
+ hunk.unshift(oldhunk)
223
+ else
224
+ output << oldhunk.diff(:unified)
225
+ end
226
+ ensure
227
+ oldhunk = hunk
228
+ output << "\n"
229
+ end
230
+ end
231
+ #Handle the last remaining hunk
232
+ output << oldhunk.diff(:unified) << "\n"
233
+ output.lstrip
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,3 @@
1
+ module JarTools
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1,70 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe JarTools::CLI do
4
+ def cli_lines(*args)
5
+ capture_stdout do
6
+ JarTools::CLI.start(args)
7
+ end.gsub("\r\n", "\n").split(/\n/)
8
+ end
9
+
10
+ describe "#packages" do
11
+ before do
12
+ @result = cli_lines("packages", sample_jar("slf4j-api-1.6.0.jar"))
13
+ end
14
+
15
+ %w(org.slf4j org.slf4j.spi org.slf4j.helpers).each do |expected_pkg|
16
+ it "lists all the packages, including #{expected_pkg}" do
17
+ @result.should include(expected_pkg)
18
+ end
19
+ end
20
+
21
+ it "does not list things which are not packages" do
22
+ @result.should_not include "META-INF"
23
+ end
24
+ end
25
+
26
+ describe "#manifest" do
27
+ describe "by default" do
28
+ before do
29
+ @result = cli_lines("manifest", sample_jar("slf4j-api-1.6.0.jar"))
30
+ end
31
+
32
+ it "prints the whole manifest" do
33
+ @result.should have(16).lines
34
+ end
35
+
36
+ it "unwraps hard-wrapped lines" do
37
+ @result.find { |l| l =~ /^Export-Package/ }.should ==
38
+ "Export-Package: org.slf4j;version=1.6.0, org.slf4j.spi;version=1.6.0, org.slf4j.helpers;version=1.6.0"
39
+ end
40
+ end
41
+
42
+ describe "--raw" do
43
+ before do
44
+ @result = cli_lines("manifest", sample_jar("slf4j-api-1.6.0.jar"), "--raw")
45
+ end
46
+
47
+ it "prints the raw manifest" do
48
+ @result.should have(17).lines
49
+ end
50
+
51
+ it "leaves wrapped lines alone" do
52
+ @result.find { |l| l =~ /^Export-Package/ }.should ==
53
+ "Export-Package: org.slf4j;version=1.6.0, org.slf4j.spi;version=1.6.0, "
54
+ end
55
+ end
56
+
57
+ describe "on a JAR without a manifest" do
58
+ it "prints nothing" do
59
+ cli_lines("manifest", sample_jar("empty.jar")).should == []
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#diff" do
65
+ it "prints nothing for the same jar" do
66
+ cli_lines("diff", sample_jar("slf4j-api-1.6.0.jar"), sample_jar("slf4j-api-1.6.0.jar")).
67
+ should == []
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,133 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe JarTools::Diff do
4
+ it "reports no differences for the same JAR" do
5
+ same = sample_jar('slf4j-api-1.6.0.jar')
6
+ capture_stdout { JarTools::Diff.diff(same, same) }.should == ""
7
+ end
8
+
9
+ describe "with a text file difference" do
10
+ before do
11
+ tmpdir("sub")
12
+ tmpfile("A.txt", "Description\nFirst Letter")
13
+ tmpfile("sub/B.txt", "Description\nSecond letter")
14
+ jar1 = tmpjar("one.jar", "A.txt", "sub/B.txt")
15
+ tmpfile("sub/B.txt", "Description\n2nd letter")
16
+ jar2 = tmpjar("two.jar", "A.txt", "sub/B.txt")
17
+
18
+ @actual = capture_stdout { JarTools::Diff.diff(jar1, jar2) }
19
+ end
20
+
21
+ it "should note the difference in its text description" do
22
+ @actual.should == <<-EXPECTED
23
+ sub/B.txt text differs:
24
+ @@ -1,3 +1,3 @@
25
+ Description
26
+ -Second letter
27
+ +2nd letter
28
+
29
+ EXPECTED
30
+ end
31
+ end
32
+
33
+ describe "with a classfile difference" do
34
+ before do
35
+ tmpfile("A.java", "public class A { }");
36
+ cd(tmpdir) { system("javac A.java") }
37
+ jar1 = tmpjar("one.jar", "A.class")
38
+
39
+ tmpfile("A.java", %q(public class A { public static final String N = "A"; }))
40
+ cd(tmpdir) { system("javac A.java") }
41
+ jar2 = tmpjar("two.jar", "A.class")
42
+
43
+ @actual = capture_stdout { JarTools::Diff.diff(jar1, jar2) }
44
+ end
45
+
46
+ it "should not include a detailed diff" do
47
+ @actual.should == <<-EXPECTED
48
+ A.class differs
49
+ EXPECTED
50
+ end
51
+ end
52
+
53
+ describe "with files present in one jar but not the other" do
54
+ before do
55
+ tmpdir("sub")
56
+ tmpfile("A.txt", "Description\nFirst Letter")
57
+ tmpfile("sub/B.txt", "Description\nSecond letter")
58
+ tmpfile("sub/C.txt", "Description\Third letter")
59
+ jar1 = tmpjar("one.jar", "sub/B.txt", "sub/C.txt")
60
+ jar2 = tmpjar("two.jar", "sub/C.txt", "A.txt")
61
+
62
+ cd(tmpdir) do
63
+ @actual = capture_stdout { JarTools::Diff.diff("one.jar", "two.jar") }
64
+ end
65
+ end
66
+
67
+ it "should have an appropriate text description" do
68
+ @actual.should == <<-EXPECTED
69
+ A.txt is in two.jar but not in one.jar
70
+ sub/B.txt is in one.jar but not in two.jar
71
+ EXPECTED
72
+ end
73
+ end
74
+
75
+ describe "with a nested JAR difference" do
76
+ before do
77
+ tmpfile("same.txt", "No changes in here")
78
+ tmpfile("altered.txt", "V1")
79
+ tmpjar("nested.jar", "altered.txt")
80
+ jar1 = tmpjar("main1.jar", "nested.jar", "same.txt")
81
+
82
+ tmpfile("altered.txt", "V2")
83
+ tmpfile("extra.txt", "Not in that other one")
84
+ tmpjar("nested.jar", "altered.txt", "extra.txt")
85
+ jar2 = tmpjar("main2.jar", "nested.jar", "same.txt")
86
+
87
+ cd(tmpdir) do
88
+ @actual = capture_stdout { JarTools::Diff.diff("main1.jar", "main2.jar") }
89
+ end
90
+ end
91
+
92
+ it "should output the details of the difference" do
93
+ @actual.should == <<-EXPECTED
94
+ nested.jar | altered.txt text differs:
95
+ @@ -1,2 +1,2 @@
96
+ -V1
97
+ +V2
98
+
99
+ nested.jar | extra.txt is in main2.jar but not in main1.jar
100
+ EXPECTED
101
+ end
102
+ end
103
+
104
+ module JarTools::Diff
105
+ describe TextDiff do
106
+ describe "#different?" do
107
+ it "is true for different values" do
108
+ TextDiff.new("A", "B").should be_different
109
+ end
110
+
111
+ it "is false for identical values" do
112
+ TextDiff.new("C", "C").should_not be_different
113
+ end
114
+ end
115
+
116
+ describe "#text_description" do
117
+ it "is nil for no differences" do
118
+ TextDiff.new("ABC", "ABC").text_description.should be_nil
119
+ end
120
+
121
+ it "gives diff-style output for differences" do
122
+ TextDiff.new("A\nb\nC", "A\nB\nC").text_description.should == <<-EXPECTED
123
+ @@ -1,4 +1,4 @@
124
+ A
125
+ -b
126
+ +B
127
+ C
128
+ EXPECTED
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
Binary file
@@ -0,0 +1,70 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'rspec'
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
+
8
+ require 'jartools'
9
+ require 'fileutils'
10
+
11
+ RSpec.configure do |config|
12
+ config.include FileUtils
13
+
14
+ config.after do
15
+ if @tmpdir
16
+ rm_r @tmpdir
17
+ end
18
+ end
19
+
20
+ ##
21
+ # Returns the path to a JAR from the samples directory
22
+ def sample_jar(name)
23
+ File.join(File.expand_path("../samples", __FILE__), name)
24
+ end
25
+
26
+ ##
27
+ # Captures everything printed to stdout during the block
28
+ # and returns it as a string.
29
+ def capture_stdout
30
+ old_stdout, $stdout = $stdout, StringIO.new
31
+ begin
32
+ yield
33
+ $stdout.string
34
+ ensure
35
+ $stdout = old_stdout
36
+ end
37
+ end
38
+
39
+ def tmpdir(sub=nil)
40
+ @tmpdir ||= begin
41
+ dirname = File.expand_path("../tmp", __FILE__)
42
+ mkdir_p dirname
43
+ dirname
44
+ end
45
+ if sub
46
+ full = File.join(@tmpdir, sub)
47
+ mkdir_p full
48
+ full
49
+ else
50
+ @tmpdir
51
+ end
52
+ end
53
+
54
+ def tmpfile(basename, contents=nil)
55
+ full_path = File.join(tmpdir, basename)
56
+ File.open(full_path, 'w') do |f|
57
+ f.write(contents) if contents
58
+ end
59
+ full_path
60
+ end
61
+
62
+ def tmpjar(basename, *files)
63
+ full_path = File.join(tmpdir, basename)
64
+ cd tmpdir do
65
+ cmd = "jar Mcf '#{full_path}' '#{files.join("' '")}'"
66
+ `#{cmd}`
67
+ end
68
+ full_path
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jartools
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Rhett Sutphin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-04 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thor
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 39
30
+ segments:
31
+ - 0
32
+ - 14
33
+ - 0
34
+ version: 0.14.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rubyzip
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 51
46
+ segments:
47
+ - 0
48
+ - 9
49
+ - 4
50
+ version: 0.9.4
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 5
62
+ segments:
63
+ - 2
64
+ - 3
65
+ version: "2.3"
66
+ type: :development
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: ci_reporter
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 1
79
+ - 6
80
+ version: "1.6"
81
+ type: :development
82
+ version_requirements: *id004
83
+ description: |-
84
+ jartools provides a busybox of command-line tools
85
+ for examining the contents and metadata of java archive files (JARs and WARs).
86
+ email:
87
+ - rhett@detailedbalance.net
88
+ executables:
89
+ - jartools
90
+ extensions: []
91
+
92
+ extra_rdoc_files: []
93
+
94
+ files:
95
+ - lib/jartools/cli.rb
96
+ - lib/jartools/diff.rb
97
+ - lib/jartools/version.rb
98
+ - lib/jartools.rb
99
+ - spec/jartools/cli_spec.rb
100
+ - spec/jartools/diff_spec.rb
101
+ - spec/samples/empty.jar
102
+ - spec/samples/slf4j-api-1.6.0.jar
103
+ - spec/spec_helper.rb
104
+ - bin/jartools
105
+ - README.md
106
+ - Rakefile
107
+ - Gemfile
108
+ has_rdoc: true
109
+ homepage: http://github.com/rsutphin/jartools
110
+ licenses: []
111
+
112
+ post_install_message:
113
+ rdoc_options: []
114
+
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 3
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project:
138
+ rubygems_version: 1.3.7
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A set of command line tools for looking through java archives (JARs and WARs)
142
+ test_files:
143
+ - spec/jartools/cli_spec.rb
144
+ - spec/jartools/diff_spec.rb
145
+ - spec/samples/empty.jar
146
+ - spec/samples/slf4j-api-1.6.0.jar
147
+ - spec/spec_helper.rb