gem-compare 0.0.1 → 0.0.2

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.
@@ -0,0 +1,150 @@
1
+ require 'gemnasium/parser'
2
+ require 'rubygems/comparator/base'
3
+
4
+ class Gem::Comparator
5
+
6
+ ##
7
+ # Gem::Comparator::GemfileComparator can
8
+ # compare dependencies between gem's Gemfiles
9
+ # based on the given Gem::Package objects
10
+ #
11
+ # To compare Gemfiles it needs to extract
12
+ # gem packages to +options[:output]+
13
+
14
+ class GemfileComparator
15
+ include Gem::Comparator::Base
16
+
17
+ COMPARES = :packages
18
+
19
+ ##
20
+ # Compare Gemfiles using gem's +packages+
21
+ # and write the changes to the +report+
22
+
23
+ def compare(packages, report, options = {})
24
+ info 'Checking Gemfiles for dependencies...'
25
+ return report if options[:param] && options[:param] != 'gemfiles'
26
+
27
+ @packages = packages
28
+ all_same = true
29
+
30
+ # Check Gemfiles from older versions to newer
31
+ packages.each_with_index do |pkg, index|
32
+ unpacked_gem_dirs[@packages[index].spec.version] = extract_gem(pkg, options[:output])
33
+ next if index == 0
34
+
35
+ prev_gemfile = File.join(unpacked_gem_dirs[@packages[index-1].spec.version], 'Gemfile')
36
+ curr_gemfile = File.join(unpacked_gem_dirs[@packages[index].spec.version], 'Gemfile')
37
+
38
+ vers = "#{@packages[index-1].spec.version}->#{@packages[index].spec.version}"
39
+ report['gemfiles'][vers].set_header "#{Rainbow(packages[index-1].spec.version).blue}->" +
40
+ "#{Rainbow(packages[index].spec.version).blue}:"
41
+
42
+ if File.exist?(prev_gemfile) && File.exist?(curr_gemfile)
43
+ added, deleted, updated = compare_gemfiles(prev_gemfile, curr_gemfile)
44
+
45
+ report['gemfiles'][vers]['added'].section do
46
+ set_header '* Added:'
47
+ puts added.map { |x| "#{x.name} #{x.requirements_list}" } unless added.empty?
48
+ end
49
+ report['gemfiles'][vers]['deleted'].section do
50
+ set_header '* Deleted'
51
+ puts deleted.map { |x| "#{x.name} #{x.requirements_list}" } unless deleted.empty?
52
+ end
53
+ report['gemfiles'][vers]['updated'].section do
54
+ set_header '* Updated'
55
+ puts updated unless updated.empty?
56
+ end
57
+ all_same = false if !added.empty? || !deleted.empty?
58
+ elsif File.exist?(prev_gemfile)
59
+ report['gemfiles'][vers] << "Gemfile removed"
60
+ all_same = false
61
+ elsif File.exist?(curr_gemfile)
62
+ report['gemfiles'][vers] << "Gemfile added"
63
+ all_same = false
64
+ end
65
+ end
66
+ if all_same && options[:log_all]
67
+ report['gemfiles'].set_header "#{same} Gemfiles:"
68
+ gemfile = File.join(unpacked_gem_dirs[@packages[1].spec.version], 'Gemfile')
69
+ if File.exist? gemfile
70
+ deps = gemfile_deps(gemfile)
71
+ deps = '[]' if deps.empty?
72
+ report['gemfiles'] << deps
73
+ else
74
+ report['gemfiles'] << 'No Gemfiles'
75
+ end
76
+ elsif !all_same
77
+ report['gemfiles'].set_header "#{different} Gemfile dependencies"
78
+ end
79
+
80
+ report
81
+ end
82
+
83
+ private
84
+
85
+ ##
86
+ # Access @unpacked_gem_dirs hash that stores
87
+ # paths to the unpacked gem dirs
88
+ #
89
+ # Keys of the hash are gem's versions
90
+
91
+ def unpacked_gem_dirs
92
+ @unpacked_gem_dirs ||= {}
93
+ end
94
+
95
+ ##
96
+ # Compare two Gemfiles for dependencies
97
+ #
98
+ # Return [added, deleted, updated] deps
99
+
100
+ def compare_gemfiles(prev_gemfile, curr_gemfile)
101
+ prev_deps = gemfile_deps(prev_gemfile)
102
+ curr_deps = gemfile_deps(curr_gemfile)
103
+ added = curr_deps - prev_deps
104
+ deleted = prev_deps - curr_deps
105
+
106
+ split_dependencies(added, deleted)
107
+ end
108
+
109
+ ##
110
+ # Get the Gemfile dependencies from +gemfile+
111
+
112
+ def gemfile_deps(gemfile)
113
+ parse_gemfile(gemfile).dependencies
114
+ end
115
+
116
+ ##
117
+ # Parse +gemfile+ using Gemnasium::Parser
118
+ #
119
+ # Return Gemnasium::Parser::Gemfile
120
+
121
+ def parse_gemfile(gemfile)
122
+ Gemnasium::Parser.gemfile File.open(gemfile).read
123
+ end
124
+
125
+ ##
126
+ # Find updated dependencies between +added+ and
127
+ # +deleted+ deps and move them out to +updated+.
128
+ #
129
+ # Return [added, deleted, updated] deps
130
+
131
+ def split_dependencies(added, deleted)
132
+ updated = []
133
+ added.dup.each do |ad|
134
+ deleted.dup.each do |dd|
135
+ if ad.name == dd.name && ad.type == dd.type
136
+ unless ad.requirements_list == dd.requirements_list
137
+ updated << "#{ad.name} " +
138
+ "from: #{dd.requirements_list} " +
139
+ "to: #{ad.requirements_list}"
140
+ end
141
+ added.delete ad
142
+ deleted.delete dd
143
+ end
144
+ end
145
+ end
146
+ [added, deleted, updated]
147
+ end
148
+
149
+ end
150
+ end
@@ -0,0 +1,121 @@
1
+ class Gem::Comparator
2
+ class Report
3
+
4
+ def self.new(name = 'main')
5
+ Gem::Comparator::Report::NestedSection.new(name)
6
+ end
7
+
8
+ class NestedSection
9
+ include Gem::Comparator::Base
10
+ include Gem::UserInteraction
11
+
12
+ DEFAULT_INDENT = ' '
13
+
14
+ attr_reader :header, :messages, :parent_section, :level
15
+ attr_accessor :name, :sections
16
+
17
+ def initialize(name, parent_section = nil)
18
+ @name = name
19
+ @header = Entry.new
20
+ @messages = []
21
+ @sections = []
22
+ @level = 0
23
+
24
+ set_parent parent_section if parent_section
25
+ end
26
+
27
+ def section(&block)
28
+ instance_eval &block
29
+ end
30
+
31
+ def set_header(message)
32
+ @header = Entry.new(message)
33
+ end
34
+
35
+ def puts(message)
36
+ @messages << Entry.new(message)
37
+ end
38
+ alias_method :<<, :puts
39
+
40
+ def nest(name)
41
+ @sections.each { |s| if s.name == name; return s; end }
42
+ NestedSection.new(name, self)
43
+ end
44
+ alias_method :[], :nest
45
+
46
+ def print
47
+ all_messages.each { |m| m.print }
48
+ end
49
+
50
+ def all_messages
51
+ indent = DEFAULT_INDENT*@level
52
+
53
+ if @header.empty?
54
+ @messages.map { |m| m.set_indent!(indent) } + nested_messages
55
+ else
56
+ nested = @messages.map { |m| m.set_indent!(indent * 2) } + nested_messages
57
+ return [] if nested.empty?
58
+
59
+ @header.set_indent!(indent)
60
+ nested.unshift(@header)
61
+ end
62
+ end
63
+
64
+ def lines(line_num)
65
+ all_messages[line_num].data
66
+ end
67
+
68
+ def nested_messages
69
+ nested_messages = []
70
+ @sections.each do |section|
71
+ section.all_messages.each do |m|
72
+ nested_messages << m
73
+ end
74
+ end
75
+ nested_messages
76
+ end
77
+
78
+ private
79
+
80
+ def set_parent(parent)
81
+ parent.sections << self
82
+ @level = parent.level + 1
83
+ parent_section = parent
84
+ end
85
+
86
+ end
87
+
88
+ class Entry
89
+ include Gem::UserInteraction
90
+
91
+ attr_accessor :data, :indent
92
+
93
+ def initialize(data = '', indent = '')
94
+ @data = data
95
+ @indent = indent
96
+ end
97
+
98
+ def set_indent!(indent)
99
+ @indent = indent
100
+ self
101
+ end
102
+
103
+ def empty?
104
+ case @data
105
+ when String, Array
106
+ @data.empty?
107
+ end
108
+ end
109
+
110
+ def print
111
+ printed = case @data
112
+ when String
113
+ "#{@indent}#{@data}"
114
+ when Array
115
+ @indent + @data.join("\n#{@indent}")
116
+ end
117
+ say printed
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems/comparator/base'
2
+
3
+ class Gem::Comparator
4
+
5
+ ##
6
+ # Gem::Comparator::SpecComparator can
7
+ # compare values from the gem verions specs
8
+
9
+ class SpecComparator
10
+ include Gem::Comparator::Base
11
+
12
+ COMPARES = :specs
13
+
14
+ ##
15
+ # Compare common fields in spec
16
+
17
+ def compare(specs, report, options = {})
18
+ info 'Checking spec parameters...'
19
+
20
+ filter_params(SPEC_PARAMS, options[:param]).each do |param|
21
+ values = values_from_specs(param, specs)
22
+
23
+ # Are values the same?
24
+ if same_values?(values) && options[:log_all]
25
+ v = value(values[0])
26
+ report[param].section do
27
+ set_header "#{same} #{param}:"
28
+ puts v
29
+ end
30
+ elsif !same_values?(values)
31
+ report[param].set_header "#{different} #{param}:"
32
+ values.each_with_index do |value, index|
33
+ report[param] << \
34
+ "#{Rainbow(specs[index].version).blue}: #{value}"
35
+ end
36
+ end
37
+ end
38
+ report
39
+ end
40
+
41
+ private
42
+
43
+ def values_from_specs(param, specs)
44
+ values = []
45
+ specs.each do |s|
46
+ if s.respond_to? :"#{param}"
47
+ values << s.send(:"#{param}")
48
+ else
49
+ warn "#{s.full_name} does not respond to " +
50
+ "#{param}, skipping check"
51
+ end
52
+ end
53
+ values
54
+ end
55
+
56
+ def value(value)
57
+ case value
58
+ when Gem::Requirement
59
+ unless value.requirements.empty?
60
+ r = value.requirements[0]
61
+ "#{r[0]} #{r[1].version} "
62
+ else
63
+ '[]'
64
+ end
65
+ when String
66
+ return value unless value.empty?
67
+ ''
68
+ else
69
+ value.inspect
70
+ end
71
+ end
72
+
73
+ def same_values?(values)
74
+ values.count(values[0]) == values.size
75
+ end
76
+ end
77
+ end
@@ -1,9 +1,2 @@
1
1
  require 'rubygems/command_manager'
2
- require 'rubygems/commands/compare_command'
3
-
4
2
  Gem::CommandManager.instance.register_command :compare
5
-
6
- Gem.post_install do |installer|
7
- #installer.spec
8
- Gem::Commands::CompareCommand.execute
9
- end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class TestDependencyComparator < TestGemComparator
4
+
5
+ def test_runtime_dependencies_comparison
6
+ assert_equal 'DIFFERENT runtime dependencies:', @report['runtime_dependency'].header.data
7
+ assert_equal 'DIFFERENT runtime dependencies:', @report['runtime_dependency'].lines(0)
8
+ assert_equal '0.0.2->0.0.3:', @report['runtime_dependency']['0.0.2->0.0.3'].header.data
9
+ assert_equal '0.0.2->0.0.3:', @report['runtime_dependency'].lines(1)
10
+ assert_equal '* Added:', @report['runtime_dependency']['0.0.2->0.0.3']['added'].lines(0)
11
+ assert_equal [], @report['runtime_dependency']['0.0.2->0.0.3']['deleted'].messages
12
+ assert_equal [], @report['runtime_dependency']['0.0.2->0.0.3']['updated'].messages
13
+ assert_equal ['rails [">= 4.0.0"] (runtime)'], @report['runtime_dependency']['0.0.2->0.0.3']['added'].lines(1)
14
+ assert_equal '0.0.3->0.0.4:', @report['runtime_dependency']['0.0.3->0.0.4'].header.data
15
+ assert_equal '0.0.3->0.0.4:', @report['runtime_dependency'].lines(4)
16
+ assert_equal [], @report['runtime_dependency']['0.0.3->0.0.4']['added'].messages
17
+ assert_equal [], @report['runtime_dependency']['0.0.3->0.0.4']['deleted'].messages
18
+ assert_equal ['rails from: [">= 4.0.0"] to: [">= 4.1.0"]'], @report['runtime_dependency']['0.0.3->0.0.4']['updated'].lines(1)
19
+ end
20
+
21
+ def test_development_dependencies_comparison
22
+ assert_equal [], @report['development_dependency']['0.0.1->0.0.2']['added'].messages
23
+ assert_equal [], @report['development_dependency']['0.0.1->0.0.2']['deleted'].messages
24
+ assert_equal [], @report['development_dependency']['0.0.1->0.0.2']['updated'].messages
25
+ assert_equal [], @report['development_dependency']['0.0.2->0.0.3']['added'].messages
26
+ assert_equal [], @report['development_dependency']['0.0.2->0.0.3']['deleted'].messages
27
+ assert_equal [], @report['development_dependency']['0.0.2->0.0.3']['updated'].messages
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class TestFileListComparator < TestGemComparator
4
+
5
+ def test_files_comparison
6
+ assert_equal 'DIFFERENT files:', @report['files'].header.data
7
+ assert_equal '0.0.1->0.0.2:', @report['files'].lines(1)
8
+ assert_equal ["CHANGELOG.md"], @report['files']['0.0.1->0.0.2']['added'].lines(1)
9
+ assert_equal [], @report['files']['0.0.1->0.0.2']['deleted'].messages
10
+ assert_equal [], @report['files']['0.0.1->0.0.2']['updated'].messages
11
+ assert_equal ["bin/lorem", "lib/lorem/utils.rb"], @report['files']['0.0.2->0.0.3']['added'].lines(1)
12
+ assert_equal [], @report['files']['0.0.2->0.0.3']['deleted'].messages
13
+ assert_equal [], @report['files']['0.0.2->0.0.3']['updated'].messages
14
+ assert_equal [], @report['files']['0.0.3->0.0.4']['added'].messages
15
+ assert_equal [], @report['files']['0.0.3->0.0.4']['deleted'].messages
16
+ assert_equal [], @report['files']['0.0.3->0.0.4']['updated'].messages
17
+ end
18
+
19
+ def test_test_files_comparison
20
+ assert_equal 'SAME test_files:', @report['test_files'].header.data
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class TestGemfileComparator < TestGemComparator
4
+
5
+ def test_gemfile_comparison
6
+ assert_equal '0.0.1->0.0.2:', @report['gemfiles']['0.0.1->0.0.2'].header.data
7
+ assert_equal [], @report['gemfiles']['0.0.1->0.0.2'].messages
8
+ assert_equal [], @report['gemfiles']['0.0.2->0.0.3'].messages
9
+ assert_equal [], @report['gemfiles']['0.0.3->0.0.4']['added'].messages
10
+ assert_equal '* Deleted', @report['gemfiles']['0.0.3->0.0.4'].lines(1)
11
+ assert_equal ['appraisal [">= 0"]'], @report['gemfiles']['0.0.3->0.0.4']['deleted'].lines(1)
12
+ assert_equal ['minitest from: [">= 0"] to: ["= 5.0.0"]'], @report['gemfiles']['0.0.3->0.0.4']['updated'].lines(1)
13
+ end
14
+ end
@@ -0,0 +1,153 @@
1
+ require 'test_helper'
2
+
3
+ class TestSpecComparator < TestGemComparator
4
+
5
+ def test_name_comparison
6
+ assert_equal 'SAME name:', @report['name'].header.data
7
+ assert_equal 'SAME name:', @report['name'].lines(0)
8
+ end
9
+
10
+ def test_version_comparison
11
+ assert_equal 'DIFFERENT version:', @report['version'].header.data
12
+ assert_equal 'DIFFERENT version:', @report['version'].lines(0)
13
+ assert_equal '0.0.1: 0.0.1', @report['version'].lines(1)
14
+ assert_equal '0.0.2: 0.0.2', @report['version'].lines(2)
15
+ assert_equal '0.0.3: 0.0.3', @report['version'].lines(3)
16
+ assert_equal '0.0.4: 0.0.4', @report['version'].lines(4)
17
+ end
18
+
19
+ def test_licenses_comparison
20
+ assert_equal 'DIFFERENT license:', @report['license'].header.data
21
+ assert_equal 'DIFFERENT licenses:', @report['licenses'].header.data
22
+ assert_equal 'DIFFERENT license:', @report['license'].lines(0)
23
+ assert_equal 'DIFFERENT licenses:', @report['licenses'].lines(0)
24
+ assert_equal '0.0.1: MIT', @report['license'].lines(1)
25
+ assert_equal '0.0.2: GPLv2', @report['license'].lines(2)
26
+ assert_equal '0.0.3: GPLv2', @report['license'].lines(3)
27
+ assert_equal '0.0.4: GPLv2', @report['license'].lines(4)
28
+ assert_equal '0.0.1: ["MIT"]', @report['licenses'].lines(1)
29
+ assert_equal '0.0.2: ["GPLv2"]', @report['licenses'].lines(2)
30
+ assert_equal '0.0.3: ["GPLv2"]', @report['licenses'].lines(3)
31
+ assert_equal '0.0.4: ["GPLv2"]', @report['licenses'].lines(4)
32
+ end
33
+
34
+ def test_authors_comparison
35
+ assert_equal 'SAME author:', @report['author'].header.data
36
+ assert_equal 'SAME authors:', @report['authors'].header.data
37
+ assert_equal 'SAME author:', @report['author'].lines(0)
38
+ assert_equal 'SAME authors:', @report['authors'].lines(0)
39
+ end
40
+
41
+ def test_email_comparison
42
+ assert_equal 'SAME email:', @report['email'].header.data
43
+ assert_equal 'SAME email:', @report['email'].lines(0)
44
+ end
45
+
46
+ def test_summary_comparison
47
+ assert_equal 'DIFFERENT summary:', @report['summary'].header.data
48
+ assert_equal 'DIFFERENT summary:', @report['summary'].lines(0)
49
+ assert_equal '0.0.1: lorem is a gem for testing gem-compare', @report['summary'].lines(1)
50
+ assert_equal '0.0.2: lorem is a gem for testing gem-compare', @report['summary'].lines(2)
51
+ assert_equal '0.0.3: lorem is a gem for testing gem-compare', @report['summary'].lines(3)
52
+ assert_equal '0.0.4: lorem is a gem for testing gem-compare plugin', @report['summary'].lines(4)
53
+ end
54
+
55
+ def test_description_comparison
56
+ assert_equal 'DIFFERENT description:', @report['description'].header.data
57
+ assert_equal 'DIFFERENT description:', @report['description'].lines(0)
58
+ assert_equal '0.0.1: lorem changes a lot so we can test gem-compare a lot', @report['description'].lines(1)
59
+ assert_equal '0.0.2: lorem changes a lot so we can test gem-compare a lot', @report['description'].lines(2)
60
+ assert_equal '0.0.3: lorem changes a lot so we can test gem-compare a lot', @report['description'].lines(3)
61
+ assert_equal '0.0.4: lorem changes a lot so we can test gem-compare plugin a lot', @report['description'].lines(4)
62
+ end
63
+
64
+ def test_homepage_comparison
65
+ assert_equal 'DIFFERENT homepage:', @report['homepage'].header.data
66
+ assert_equal 'DIFFERENT homepage:', @report['homepage'].lines(0)
67
+ assert_equal '0.0.1: ', @report['homepage'].lines(1)
68
+ assert_equal '0.0.2: ', @report['homepage'].lines(2)
69
+ assert_equal '0.0.3: http://lorem.lorem', @report['homepage'].lines(3)
70
+ assert_equal '0.0.4: http://lorem.lorem', @report['homepage'].lines(4)
71
+ end
72
+
73
+ def test_platform_comparison
74
+ assert_equal 'SAME platform:', @report['platform'].header.data
75
+ assert_equal 'SAME platform:', @report['platform'].lines(0)
76
+ end
77
+
78
+ def test_require_paths_comparison
79
+ assert_equal 'SAME require_paths:', @report['require_paths'].header.data
80
+ assert_equal 'SAME require_paths:', @report['require_paths'].lines(0)
81
+ end
82
+
83
+ def test_bindir_comparison
84
+ assert_equal 'SAME bindir:', @report['bindir'].header.data
85
+ assert_equal 'SAME bindir:', @report['bindir'].lines(0)
86
+ end
87
+
88
+ def test_rubygems_version_comparison
89
+ assert_equal 'SAME rubygems_version:', @report['rubygems_version'].header.data
90
+ assert_equal 'SAME rubygems_version:', @report['rubygems_version'].lines(0)
91
+ end
92
+
93
+ def test_cert_chain_comparison
94
+ assert_equal 'SAME cert_chain:', @report['cert_chain'].header.data
95
+ assert_equal 'SAME cert_chain:', @report['cert_chain'].lines(0)
96
+ end
97
+
98
+ def test_executables_comparison
99
+ assert_equal 'DIFFERENT executables:', @report['executables'].header.data
100
+ assert_equal 'DIFFERENT executables:', @report['executables'].lines(0)
101
+ end
102
+
103
+ def test_extensions_comparison
104
+ assert_equal 'SAME extensions:', @report['extensions'].header.data
105
+ assert_equal 'SAME extensions:', @report['extensions'].lines(0)
106
+ end
107
+
108
+ def test_metadata_comparison
109
+ assert_equal 'SAME metadata:', @report['metadata'].header.data
110
+ assert_equal 'SAME metadata:', @report['metadata'].lines(0)
111
+ end
112
+
113
+ def test_post_install_message_comparison
114
+ assert_equal 'SAME post_install_message:', @report['post_install_message'].header.data
115
+ assert_equal 'SAME post_install_message:', @report['post_install_message'].lines(0)
116
+ end
117
+
118
+ def test_rdoc_options_comparison
119
+ assert_equal 'SAME rdoc_options:', @report['rdoc_options'].header.data
120
+ assert_equal 'SAME rdoc_options:', @report['rdoc_options'].lines(0)
121
+ end
122
+
123
+ def test_required_ruby_version_comparison
124
+ assert_equal 'SAME required_ruby_version:', @report['required_ruby_version'].header.data
125
+ assert_equal 'SAME required_ruby_version:', @report['required_ruby_version'].lines(0)
126
+ end
127
+
128
+ def test_required_rubygems_version_comparison
129
+ assert_equal 'SAME required_rubygems_version:', @report['required_rubygems_version'].header.data
130
+ assert_equal 'SAME required_rubygems_version:', @report['required_rubygems_version'].lines(0)
131
+ end
132
+
133
+ def test_requirements_comparison
134
+ assert_equal 'SAME requirements:', @report['requirements'].header.data
135
+ assert_equal 'SAME requirements:', @report['requirements'].lines(0)
136
+ end
137
+
138
+ def test_signing_key_comparison
139
+ assert_equal 'SAME signing_key:', @report['signing_key'].header.data
140
+ assert_equal 'SAME signing_key:', @report['signing_key'].lines(0)
141
+ end
142
+
143
+ def test_has_rdoc_comparison
144
+ assert_equal 'SAME has_rdoc:', @report['has_rdoc'].header.data
145
+ assert_equal 'SAME has_rdoc:', @report['has_rdoc'].lines(0)
146
+ end
147
+
148
+ def test_date_comparison
149
+ assert_equal 'SAME date:', @report['date'].header.data
150
+ assert_equal 'SAME date:', @report['date'].lines(0)
151
+ end
152
+
153
+ end