slimgems 1.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +5811 -0
- data/History.txt +887 -0
- data/LICENSE.txt +51 -0
- data/README.md +87 -0
- data/Rakefile +120 -0
- data/bin/gem +25 -0
- data/bin/update_slimgems +35 -0
- data/bootstrap/Rakefile +4 -0
- data/hide_lib_for_update/note.txt +5 -0
- data/lib/gauntlet_rubygems.rb +50 -0
- data/lib/rbconfig/datadir.rb +20 -0
- data/lib/rubygems.rb +1220 -0
- data/lib/rubygems/builder.rb +102 -0
- data/lib/rubygems/command.rb +534 -0
- data/lib/rubygems/command_manager.rb +182 -0
- data/lib/rubygems/commands/build_command.rb +53 -0
- data/lib/rubygems/commands/cert_command.rb +86 -0
- data/lib/rubygems/commands/check_command.rb +80 -0
- data/lib/rubygems/commands/cleanup_command.rb +106 -0
- data/lib/rubygems/commands/contents_command.rb +98 -0
- data/lib/rubygems/commands/dependency_command.rb +195 -0
- data/lib/rubygems/commands/environment_command.rb +132 -0
- data/lib/rubygems/commands/fetch_command.rb +67 -0
- data/lib/rubygems/commands/generate_index_command.rb +133 -0
- data/lib/rubygems/commands/help_command.rb +172 -0
- data/lib/rubygems/commands/install_command.rb +178 -0
- data/lib/rubygems/commands/list_command.rb +35 -0
- data/lib/rubygems/commands/lock_command.rb +110 -0
- data/lib/rubygems/commands/mirror_command.rb +111 -0
- data/lib/rubygems/commands/outdated_command.rb +33 -0
- data/lib/rubygems/commands/owner_command.rb +75 -0
- data/lib/rubygems/commands/pristine_command.rb +93 -0
- data/lib/rubygems/commands/push_command.rb +56 -0
- data/lib/rubygems/commands/query_command.rb +280 -0
- data/lib/rubygems/commands/rdoc_command.rb +91 -0
- data/lib/rubygems/commands/search_command.rb +31 -0
- data/lib/rubygems/commands/server_command.rb +86 -0
- data/lib/rubygems/commands/setup_command.rb +387 -0
- data/lib/rubygems/commands/sources_command.rb +157 -0
- data/lib/rubygems/commands/specification_command.rb +125 -0
- data/lib/rubygems/commands/stale_command.rb +27 -0
- data/lib/rubygems/commands/uninstall_command.rb +83 -0
- data/lib/rubygems/commands/unpack_command.rb +121 -0
- data/lib/rubygems/commands/update_command.rb +212 -0
- data/lib/rubygems/commands/which_command.rb +86 -0
- data/lib/rubygems/config_file.rb +345 -0
- data/lib/rubygems/custom_require.rb +44 -0
- data/lib/rubygems/defaults.rb +101 -0
- data/lib/rubygems/dependency.rb +227 -0
- data/lib/rubygems/dependency_installer.rb +286 -0
- data/lib/rubygems/dependency_list.rb +208 -0
- data/lib/rubygems/doc_manager.rb +242 -0
- data/lib/rubygems/errors.rb +35 -0
- data/lib/rubygems/exceptions.rb +91 -0
- data/lib/rubygems/ext.rb +18 -0
- data/lib/rubygems/ext/builder.rb +56 -0
- data/lib/rubygems/ext/configure_builder.rb +25 -0
- data/lib/rubygems/ext/ext_conf_builder.rb +24 -0
- data/lib/rubygems/ext/rake_builder.rb +39 -0
- data/lib/rubygems/format.rb +81 -0
- data/lib/rubygems/gem_openssl.rb +92 -0
- data/lib/rubygems/gem_path_searcher.rb +100 -0
- data/lib/rubygems/gem_runner.rb +79 -0
- data/lib/rubygems/gemcutter_utilities.rb +49 -0
- data/lib/rubygems/indexer.rb +720 -0
- data/lib/rubygems/install_update_options.rb +125 -0
- data/lib/rubygems/installer.rb +604 -0
- data/lib/rubygems/local_remote_options.rb +135 -0
- data/lib/rubygems/old_format.rb +153 -0
- data/lib/rubygems/package.rb +97 -0
- data/lib/rubygems/package/f_sync_dir.rb +23 -0
- data/lib/rubygems/package/tar_header.rb +266 -0
- data/lib/rubygems/package/tar_input.rb +222 -0
- data/lib/rubygems/package/tar_output.rb +144 -0
- data/lib/rubygems/package/tar_reader.rb +106 -0
- data/lib/rubygems/package/tar_reader/entry.rb +141 -0
- data/lib/rubygems/package/tar_writer.rb +241 -0
- data/lib/rubygems/package_task.rb +126 -0
- data/lib/rubygems/platform.rb +183 -0
- data/lib/rubygems/remote_fetcher.rb +414 -0
- data/lib/rubygems/require_paths_builder.rb +18 -0
- data/lib/rubygems/requirement.rb +153 -0
- data/lib/rubygems/security.rb +814 -0
- data/lib/rubygems/server.rb +872 -0
- data/lib/rubygems/source_index.rb +597 -0
- data/lib/rubygems/source_info_cache.rb +395 -0
- data/lib/rubygems/source_info_cache_entry.rb +56 -0
- data/lib/rubygems/spec_fetcher.rb +337 -0
- data/lib/rubygems/specification.rb +1486 -0
- data/lib/rubygems/test_utilities.rb +147 -0
- data/lib/rubygems/text.rb +65 -0
- data/lib/rubygems/uninstaller.rb +278 -0
- data/lib/rubygems/user_interaction.rb +527 -0
- data/lib/rubygems/validator.rb +240 -0
- data/lib/rubygems/version.rb +316 -0
- data/lib/rubygems/version_option.rb +65 -0
- data/lib/ubygems.rb +10 -0
- data/setup.rb +42 -0
- data/test/bogussources.rb +8 -0
- data/test/data/gem-private_key.pem +27 -0
- data/test/data/gem-public_cert.pem +20 -0
- data/test/fake_certlib/openssl.rb +7 -0
- data/test/foo/discover.rb +0 -0
- data/test/functional.rb +92 -0
- data/test/gem_installer_test_case.rb +97 -0
- data/test/gem_package_tar_test_case.rb +132 -0
- data/test/gemutilities.rb +605 -0
- data/test/insure_session.rb +43 -0
- data/test/mockgemui.rb +56 -0
- data/test/plugin/exception/rubygems_plugin.rb +2 -0
- data/test/plugin/load/rubygems_plugin.rb +1 -0
- data/test/plugin/standarderror/rubygems_plugin.rb +2 -0
- data/test/private_key.pem +27 -0
- data/test/public_cert.pem +20 -0
- data/test/rubygems_plugin.rb +21 -0
- data/test/simple_gem.rb +66 -0
- data/test/test_config.rb +12 -0
- data/test/test_gem.rb +766 -0
- data/test/test_gem_builder.rb +27 -0
- data/test/test_gem_command.rb +178 -0
- data/test/test_gem_command_manager.rb +207 -0
- data/test/test_gem_commands_build_command.rb +74 -0
- data/test/test_gem_commands_cert_command.rb +124 -0
- data/test/test_gem_commands_check_command.rb +18 -0
- data/test/test_gem_commands_contents_command.rb +156 -0
- data/test/test_gem_commands_dependency_command.rb +216 -0
- data/test/test_gem_commands_environment_command.rb +144 -0
- data/test/test_gem_commands_fetch_command.rb +76 -0
- data/test/test_gem_commands_generate_index_command.rb +135 -0
- data/test/test_gem_commands_install_command.rb +315 -0
- data/test/test_gem_commands_list_command.rb +36 -0
- data/test/test_gem_commands_lock_command.rb +68 -0
- data/test/test_gem_commands_mirror_command.rb +60 -0
- data/test/test_gem_commands_outdated_command.rb +40 -0
- data/test/test_gem_commands_owner_command.rb +105 -0
- data/test/test_gem_commands_pristine_command.rb +108 -0
- data/test/test_gem_commands_push_command.rb +81 -0
- data/test/test_gem_commands_query_command.rb +426 -0
- data/test/test_gem_commands_server_command.rb +59 -0
- data/test/test_gem_commands_sources_command.rb +209 -0
- data/test/test_gem_commands_specification_command.rb +139 -0
- data/test/test_gem_commands_stale_command.rb +38 -0
- data/test/test_gem_commands_uninstall_command.rb +83 -0
- data/test/test_gem_commands_unpack_command.rb +199 -0
- data/test/test_gem_commands_update_command.rb +353 -0
- data/test/test_gem_commands_which_command.rb +66 -0
- data/test/test_gem_config_file.rb +287 -0
- data/test/test_gem_dependency.rb +149 -0
- data/test/test_gem_dependency_installer.rb +661 -0
- data/test/test_gem_dependency_list.rb +230 -0
- data/test/test_gem_doc_manager.rb +31 -0
- data/test/test_gem_ext_configure_builder.rb +84 -0
- data/test/test_gem_ext_ext_conf_builder.rb +173 -0
- data/test/test_gem_ext_rake_builder.rb +81 -0
- data/test/test_gem_format.rb +70 -0
- data/test/test_gem_gem_path_searcher.rb +78 -0
- data/test/test_gem_gem_runner.rb +45 -0
- data/test/test_gem_gemcutter_utilities.rb +103 -0
- data/test/test_gem_indexer.rb +673 -0
- data/test/test_gem_install_update_options.rb +68 -0
- data/test/test_gem_installer.rb +857 -0
- data/test/test_gem_local_remote_options.rb +97 -0
- data/test/test_gem_package_tar_header.rb +130 -0
- data/test/test_gem_package_tar_input.rb +112 -0
- data/test/test_gem_package_tar_output.rb +97 -0
- data/test/test_gem_package_tar_reader.rb +46 -0
- data/test/test_gem_package_tar_reader_entry.rb +109 -0
- data/test/test_gem_package_tar_writer.rb +144 -0
- data/test/test_gem_package_task.rb +59 -0
- data/test/test_gem_platform.rb +264 -0
- data/test/test_gem_remote_fetcher.rb +740 -0
- data/test/test_gem_requirement.rb +292 -0
- data/test/test_gem_server.rb +356 -0
- data/test/test_gem_silent_ui.rb +113 -0
- data/test/test_gem_source_index.rb +461 -0
- data/test/test_gem_spec_fetcher.rb +410 -0
- data/test/test_gem_specification.rb +1291 -0
- data/test/test_gem_stream_ui.rb +218 -0
- data/test/test_gem_text.rb +43 -0
- data/test/test_gem_uninstaller.rb +146 -0
- data/test/test_gem_validator.rb +63 -0
- data/test/test_gem_version.rb +181 -0
- data/test/test_gem_version_option.rb +89 -0
- data/test/test_kernel.rb +59 -0
- metadata +413 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'find'
|
8
|
+
|
9
|
+
require 'digest'
|
10
|
+
require 'rubygems/format'
|
11
|
+
require 'rubygems/installer'
|
12
|
+
|
13
|
+
begin
|
14
|
+
gem 'test-unit'
|
15
|
+
rescue Gem::LoadError
|
16
|
+
# Ignore - use the test-unit library that's part of the standard library
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Validator performs various gem file and gem database validation
|
21
|
+
|
22
|
+
class Gem::Validator
|
23
|
+
|
24
|
+
include Gem::UserInteraction
|
25
|
+
|
26
|
+
##
|
27
|
+
# Given a gem file's contents, validates against its own MD5 checksum
|
28
|
+
# gem_data:: [String] Contents of the gem file
|
29
|
+
|
30
|
+
def verify_gem(gem_data)
|
31
|
+
raise Gem::VerificationError, 'empty gem file' if gem_data.size == 0
|
32
|
+
|
33
|
+
unless gem_data =~ /MD5SUM/ then
|
34
|
+
return # Don't worry about it...this sucks. Need to fix MD5 stuff for
|
35
|
+
# new format
|
36
|
+
# FIXME
|
37
|
+
end
|
38
|
+
|
39
|
+
sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
|
40
|
+
"MD5SUM = \"#{"F" * 32}\"")
|
41
|
+
|
42
|
+
unless Digest::MD5.hexdigest(sum_data) == $1.to_s then
|
43
|
+
raise Gem::VerificationError, 'invalid checksum for gem file'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Given the path to a gem file, validates against its own MD5 checksum
|
49
|
+
#
|
50
|
+
# gem_path:: [String] Path to gem file
|
51
|
+
|
52
|
+
def verify_gem_file(gem_path)
|
53
|
+
open gem_path, Gem.binary_mode do |file|
|
54
|
+
gem_data = file.read
|
55
|
+
verify_gem gem_data
|
56
|
+
end
|
57
|
+
rescue Errno::ENOENT, Errno::EINVAL
|
58
|
+
raise Gem::VerificationError, "missing gem file #{gem_path}"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def find_files_for_gem(gem_directory)
|
64
|
+
installed_files = []
|
65
|
+
Find.find gem_directory do |file_name|
|
66
|
+
fn = file_name[gem_directory.size..file_name.size-1].sub(/^\//, "")
|
67
|
+
installed_files << fn unless
|
68
|
+
fn =~ /CVS/ || fn.empty? || File.directory?(file_name)
|
69
|
+
end
|
70
|
+
installed_files
|
71
|
+
end
|
72
|
+
|
73
|
+
public
|
74
|
+
|
75
|
+
ErrorData = Struct.new :path, :problem
|
76
|
+
|
77
|
+
##
|
78
|
+
# Checks the gem directory for the following potential
|
79
|
+
# inconsistencies/problems:
|
80
|
+
#
|
81
|
+
# * Checksum gem itself
|
82
|
+
# * For each file in each gem, check consistency of installed versions
|
83
|
+
# * Check for files that aren't part of the gem but are in the gems directory
|
84
|
+
# * 1 cache - 1 spec - 1 directory.
|
85
|
+
#
|
86
|
+
# returns a hash of ErrorData objects, keyed on the problem gem's name.
|
87
|
+
|
88
|
+
def alien(gems=[])
|
89
|
+
errors = Hash.new { |h,k| h[k] = {} }
|
90
|
+
|
91
|
+
Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
|
92
|
+
next unless gems.include? gem_spec.name unless gems.empty?
|
93
|
+
|
94
|
+
install_dir = gem_spec.installation_path
|
95
|
+
gem_path = File.join install_dir, "cache", gem_spec.file_name
|
96
|
+
spec_path = File.join install_dir, "specifications", gem_spec.spec_name
|
97
|
+
gem_directory = gem_spec.full_gem_path
|
98
|
+
|
99
|
+
unless File.directory? gem_directory then
|
100
|
+
errors[gem_name][gem_spec.full_name] =
|
101
|
+
"Gem registered but doesn't exist at #{gem_directory}"
|
102
|
+
next
|
103
|
+
end
|
104
|
+
|
105
|
+
unless File.exist? spec_path then
|
106
|
+
errors[gem_name][spec_path] = "Spec file missing for installed gem"
|
107
|
+
end
|
108
|
+
|
109
|
+
begin
|
110
|
+
verify_gem_file(gem_path)
|
111
|
+
|
112
|
+
good, gone, unreadable = nil, nil, nil, nil
|
113
|
+
|
114
|
+
open gem_path, Gem.binary_mode do |file|
|
115
|
+
format = Gem::Format.from_file_by_path(gem_path)
|
116
|
+
|
117
|
+
good, gone = format.file_entries.partition { |entry, _|
|
118
|
+
File.exist? File.join(gem_directory, entry['path'])
|
119
|
+
}
|
120
|
+
|
121
|
+
gone.map! { |entry, _| entry['path'] }
|
122
|
+
gone.sort.each do |path|
|
123
|
+
errors[gem_name][path] = "Missing file"
|
124
|
+
end
|
125
|
+
|
126
|
+
good, unreadable = good.partition { |entry, _|
|
127
|
+
File.readable? File.join(gem_directory, entry['path'])
|
128
|
+
}
|
129
|
+
|
130
|
+
unreadable.map! { |entry, _| entry['path'] }
|
131
|
+
unreadable.sort.each do |path|
|
132
|
+
errors[gem_name][path] = "Unreadable file"
|
133
|
+
end
|
134
|
+
|
135
|
+
good.each do |entry, data|
|
136
|
+
begin
|
137
|
+
next unless data # HACK `gem check -a mkrf`
|
138
|
+
|
139
|
+
open File.join(gem_directory, entry['path']), Gem.binary_mode do |f|
|
140
|
+
unless Digest::MD5.hexdigest(f.read).to_s ==
|
141
|
+
Digest::MD5.hexdigest(data).to_s then
|
142
|
+
errors[gem_name][entry['path']] = "Modified from original"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
installed_files = find_files_for_gem(gem_directory)
|
150
|
+
good.map! { |entry, _| entry['path'] }
|
151
|
+
extras = installed_files - good - unreadable
|
152
|
+
|
153
|
+
extras.each do |extra|
|
154
|
+
errors[gem_name][extra] = "Extra file"
|
155
|
+
end
|
156
|
+
rescue Gem::VerificationError => e
|
157
|
+
errors[gem_name][gem_path] = e.message
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
errors.each do |name, subhash|
|
162
|
+
errors[name] = subhash.map { |path, msg| ErrorData.new(path, msg) }
|
163
|
+
end
|
164
|
+
|
165
|
+
errors
|
166
|
+
end
|
167
|
+
|
168
|
+
if RUBY_VERSION < '1.9' then
|
169
|
+
class TestRunner
|
170
|
+
def initialize(suite, ui)
|
171
|
+
@suite = suite
|
172
|
+
@ui = ui
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.run(suite, ui)
|
176
|
+
require 'test/unit/ui/testrunnermediator'
|
177
|
+
return new(suite, ui).start
|
178
|
+
end
|
179
|
+
|
180
|
+
def start
|
181
|
+
@mediator = Test::Unit::UI::TestRunnerMediator.new(@suite)
|
182
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:add_fault))
|
183
|
+
return @mediator.run_suite
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_fault(fault)
|
187
|
+
if Gem.configuration.verbose then
|
188
|
+
@ui.say fault.long_display
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
autoload :TestRunner, 'test/unit/ui/testrunnerutilities'
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Runs unit tests for a given gem specification
|
198
|
+
|
199
|
+
def unit_test(gem_spec)
|
200
|
+
start_dir = Dir.pwd
|
201
|
+
Dir.chdir(gem_spec.full_gem_path)
|
202
|
+
$: << gem_spec.full_gem_path
|
203
|
+
# XXX: why do we need this gem_spec when we've already got 'spec'?
|
204
|
+
test_files = gem_spec.test_files
|
205
|
+
|
206
|
+
if test_files.empty? then
|
207
|
+
say "There are no unit tests to run for #{gem_spec.full_name}"
|
208
|
+
return nil
|
209
|
+
end
|
210
|
+
|
211
|
+
gem gem_spec.name, "= #{gem_spec.version.version}"
|
212
|
+
|
213
|
+
test_files.each do |f| require f end
|
214
|
+
|
215
|
+
if RUBY_VERSION < '1.9' then
|
216
|
+
suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}")
|
217
|
+
|
218
|
+
ObjectSpace.each_object(Class) do |klass|
|
219
|
+
suite << klass.suite if (klass < Test::Unit::TestCase)
|
220
|
+
end
|
221
|
+
|
222
|
+
result = TestRunner.run suite, ui
|
223
|
+
|
224
|
+
alert_error result.to_s unless result.passed?
|
225
|
+
else
|
226
|
+
result = MiniTest::Unit.new
|
227
|
+
result.run
|
228
|
+
end
|
229
|
+
|
230
|
+
result
|
231
|
+
ensure
|
232
|
+
Dir.chdir(start_dir)
|
233
|
+
end
|
234
|
+
|
235
|
+
def remove_leading_dot_dir(path)
|
236
|
+
path.sub(/^\.\//, "")
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
@@ -0,0 +1,316 @@
|
|
1
|
+
##
|
2
|
+
# The Version class processes string versions into comparable
|
3
|
+
# values. A version string should normally be a series of numbers
|
4
|
+
# separated by periods. Each part (digits separated by periods) is
|
5
|
+
# considered its own number, and these are used for sorting. So for
|
6
|
+
# instance, 3.10 sorts higher than 3.2 because ten is greater than
|
7
|
+
# two.
|
8
|
+
#
|
9
|
+
# If any part contains letters (currently only a-z are supported) then
|
10
|
+
# that version is considered prerelease. Versions with a prerelease
|
11
|
+
# part in the Nth part sort less than versions with N-1
|
12
|
+
# parts. Prerelease parts are sorted alphabetically using the normal
|
13
|
+
# Ruby string sorting rules. If a prerelease part contains both
|
14
|
+
# letters and numbers, it will be broken into multiple parts to
|
15
|
+
# provide expected sort behavior.(1.0.a10 becomes 1.0.a.10, and is
|
16
|
+
# greater than 1.0.a9).
|
17
|
+
#
|
18
|
+
# Prereleases sort between real releases (newest to oldest):
|
19
|
+
#
|
20
|
+
# 1. 1.0
|
21
|
+
# 2. 1.0.b1
|
22
|
+
# 3. 1.0.a.2
|
23
|
+
# 4. 0.9
|
24
|
+
#
|
25
|
+
# == How Software Changes
|
26
|
+
#
|
27
|
+
# Users expect to be able to specify a version constraint that gives them
|
28
|
+
# some reasonable expectation that new versions of a library will work with
|
29
|
+
# their software if the version constraint is true, and not work with their
|
30
|
+
# software if the version constraint is false. In other words, the perfect
|
31
|
+
# system will accept all compatible versions of the library and reject all
|
32
|
+
# incompatible versions.
|
33
|
+
#
|
34
|
+
# Libraries change in 3 ways (well, more than 3, but stay focused here!).
|
35
|
+
#
|
36
|
+
# 1. The change may be an implementation detail only and have no effect on
|
37
|
+
# the client software.
|
38
|
+
# 2. The change may add new features, but do so in a way that client software
|
39
|
+
# written to an earlier version is still compatible.
|
40
|
+
# 3. The change may change the public interface of the library in such a way
|
41
|
+
# that old software is no longer compatible.
|
42
|
+
#
|
43
|
+
# Some examples are appropriate at this point. Suppose I have a Stack class
|
44
|
+
# that supports a <tt>push</tt> and a <tt>pop</tt> method.
|
45
|
+
#
|
46
|
+
# === Examples of Category 1 changes:
|
47
|
+
#
|
48
|
+
# * Switch from an array based implementation to a linked-list based
|
49
|
+
# implementation.
|
50
|
+
# * Provide an automatic (and transparent) backing store for large stacks.
|
51
|
+
#
|
52
|
+
# === Examples of Category 2 changes might be:
|
53
|
+
#
|
54
|
+
# * Add a <tt>depth</tt> method to return the current depth of the stack.
|
55
|
+
# * Add a <tt>top</tt> method that returns the current top of stack (without
|
56
|
+
# changing the stack).
|
57
|
+
# * Change <tt>push</tt> so that it returns the item pushed (previously it
|
58
|
+
# had no usable return value).
|
59
|
+
#
|
60
|
+
# === Examples of Category 3 changes might be:
|
61
|
+
#
|
62
|
+
# * Changes <tt>pop</tt> so that it no longer returns a value (you must use
|
63
|
+
# <tt>top</tt> to get the top of the stack).
|
64
|
+
# * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.
|
65
|
+
#
|
66
|
+
# == SlimGems Rational Versioning
|
67
|
+
#
|
68
|
+
# * Versions shall be represented by three non-negative integers, separated
|
69
|
+
# by periods (e.g. 3.1.4). The first integers is the "major" version
|
70
|
+
# number, the second integer is the "minor" version number, and the third
|
71
|
+
# integer is the "build" number.
|
72
|
+
#
|
73
|
+
# * A category 1 change (implementation detail) will increment the build
|
74
|
+
# number.
|
75
|
+
#
|
76
|
+
# * A category 2 change (backwards compatible) will increment the minor
|
77
|
+
# version number and reset the build number.
|
78
|
+
#
|
79
|
+
# * A category 3 change (incompatible) will increment the major build number
|
80
|
+
# and reset the minor and build numbers.
|
81
|
+
#
|
82
|
+
# * Any "public" release of a gem should have a different version. Normally
|
83
|
+
# that means incrementing the build number. This means a developer can
|
84
|
+
# generate builds all day long for himself, but as soon as he/she makes a
|
85
|
+
# public release, the version must be updated.
|
86
|
+
#
|
87
|
+
# === Examples
|
88
|
+
#
|
89
|
+
# Let's work through a project lifecycle using our Stack example from above.
|
90
|
+
#
|
91
|
+
# Version 0.0.1:: The initial Stack class is release.
|
92
|
+
# Version 0.0.2:: Switched to a linked=list implementation because it is
|
93
|
+
# cooler.
|
94
|
+
# Version 0.1.0:: Added a <tt>depth</tt> method.
|
95
|
+
# Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil
|
96
|
+
# (<tt>pop</tt> used to return the old top item).
|
97
|
+
# Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it
|
98
|
+
# return nil).
|
99
|
+
# Version 1.1.1:: Fixed a bug in the linked list implementation.
|
100
|
+
# Version 1.1.2:: Fixed a bug introduced in the last fix.
|
101
|
+
#
|
102
|
+
# Client A needs a stack with basic push/pop capability. He writes to the
|
103
|
+
# original interface (no <tt>top</tt>), so his version constraint looks
|
104
|
+
# like:
|
105
|
+
#
|
106
|
+
# gem 'stack', '~> 0.0'
|
107
|
+
#
|
108
|
+
# Essentially, any version is OK with Client A. An incompatible change to
|
109
|
+
# the library will cause him grief, but he is willing to take the chance (we
|
110
|
+
# call Client A optimistic).
|
111
|
+
#
|
112
|
+
# Client B is just like Client A except for two things: (1) He uses the
|
113
|
+
# <tt>depth</tt> method and (2) he is worried about future
|
114
|
+
# incompatibilities, so he writes his version constraint like this:
|
115
|
+
#
|
116
|
+
# gem 'stack', '~> 0.1'
|
117
|
+
#
|
118
|
+
# The <tt>depth</tt> method was introduced in version 0.1.0, so that version
|
119
|
+
# or anything later is fine, as long as the version stays below version 1.0
|
120
|
+
# where incompatibilities are introduced. We call Client B pessimistic
|
121
|
+
# because he is worried about incompatible future changes (it is OK to be
|
122
|
+
# pessimistic!).
|
123
|
+
#
|
124
|
+
# == Preventing Version Catastrophe:
|
125
|
+
#
|
126
|
+
# From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
|
127
|
+
#
|
128
|
+
# Let's say you're depending on the fnord gem version 2.y.z. If you
|
129
|
+
# specify your dependency as ">= 2.0.0" then, you're good, right? What
|
130
|
+
# happens if fnord 3.0 comes out and it isn't backwards compatible
|
131
|
+
# with 2.y.z? Your stuff will break as a result of using ">=". The
|
132
|
+
# better route is to specify your dependency with a "spermy" version
|
133
|
+
# specifier. They're a tad confusing, so here is how the dependency
|
134
|
+
# specifiers work:
|
135
|
+
#
|
136
|
+
# Specification From ... To (exclusive)
|
137
|
+
# ">= 3.0" 3.0 ... ∞
|
138
|
+
# "~> 3.0" 3.0 ... 4.0
|
139
|
+
# "~> 3.0.0" 3.0.0 ... 3.1
|
140
|
+
# "~> 3.5" 3.5 ... 4.0
|
141
|
+
# "~> 3.5.0" 3.5.0 ... 3.6
|
142
|
+
|
143
|
+
class Gem::Version
|
144
|
+
include Comparable
|
145
|
+
|
146
|
+
VERSION_PATTERN = '[0-9]+(\.[0-9a-zA-Z]+)*' # :nodoc:
|
147
|
+
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
|
148
|
+
|
149
|
+
##
|
150
|
+
# A string representation of this Version.
|
151
|
+
|
152
|
+
attr_reader :version
|
153
|
+
alias to_s version
|
154
|
+
|
155
|
+
##
|
156
|
+
# True if the +version+ string matches SlimGems' requirements.
|
157
|
+
|
158
|
+
def self.correct? version
|
159
|
+
version.to_s =~ ANCHORED_VERSION_PATTERN
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Factory method to create a Version object. Input may be a Version
|
164
|
+
# or a String. Intended to simplify client code.
|
165
|
+
#
|
166
|
+
# ver1 = Version.create('1.3.17') # -> (Version object)
|
167
|
+
# ver2 = Version.create(ver1) # -> (ver1)
|
168
|
+
# ver3 = Version.create(nil) # -> nil
|
169
|
+
|
170
|
+
def self.create input
|
171
|
+
if input.respond_to? :version then
|
172
|
+
input
|
173
|
+
elsif input.nil? then
|
174
|
+
nil
|
175
|
+
else
|
176
|
+
new input
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Constructs a Version from the +version+ string. A version string is a
|
182
|
+
# series of digits or ASCII letters separated by dots.
|
183
|
+
|
184
|
+
def initialize version
|
185
|
+
raise ArgumentError, "Malformed version number string #{version}" unless
|
186
|
+
self.class.correct?(version)
|
187
|
+
|
188
|
+
@version = version.to_s
|
189
|
+
@version.strip!
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Return a new version object where the next to the last revision
|
194
|
+
# number is one greater (e.g., 5.3.1 => 5.4).
|
195
|
+
#
|
196
|
+
# Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
|
197
|
+
|
198
|
+
def bump
|
199
|
+
segments = self.segments.dup
|
200
|
+
segments.pop while segments.any? { |s| String === s }
|
201
|
+
segments.pop if segments.size > 1
|
202
|
+
|
203
|
+
segments[-1] = segments[-1].succ
|
204
|
+
self.class.new segments.join(".")
|
205
|
+
end
|
206
|
+
|
207
|
+
##
|
208
|
+
# A Version is only eql? to another version if it's specified to the
|
209
|
+
# same precision. Version "1.0" is not the same as version "1".
|
210
|
+
|
211
|
+
def eql? other
|
212
|
+
self.class === other and @version == other.version
|
213
|
+
end
|
214
|
+
|
215
|
+
def hash # :nodoc:
|
216
|
+
@hash ||= segments.hash
|
217
|
+
end
|
218
|
+
|
219
|
+
def inspect # :nodoc:
|
220
|
+
"#<#{self.class} #{version.inspect}>"
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Dump only the raw version string, not the complete object. It's a
|
225
|
+
# string for backwards (SlimGems 1.3.5 and earlier) compatibility.
|
226
|
+
|
227
|
+
def marshal_dump
|
228
|
+
[version]
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# Load custom marshal format. It's a string for backwards (SlimGems
|
233
|
+
# 1.3.5 and earlier) compatibility.
|
234
|
+
|
235
|
+
def marshal_load array
|
236
|
+
initialize array[0]
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# A version is considered a prerelease if it contains a letter.
|
241
|
+
|
242
|
+
def prerelease?
|
243
|
+
@prerelease ||= @version =~ /[a-zA-Z]/
|
244
|
+
end
|
245
|
+
|
246
|
+
def pretty_print q # :nodoc:
|
247
|
+
q.text "Gem::Version.new(#{version.inspect})"
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# The release for this version (e.g. 1.2.0.a -> 1.2.0).
|
252
|
+
# Non-prerelease versions return themselves.
|
253
|
+
|
254
|
+
def release
|
255
|
+
return self unless prerelease?
|
256
|
+
|
257
|
+
segments = self.segments.dup
|
258
|
+
segments.pop while segments.any? { |s| String === s }
|
259
|
+
self.class.new segments.join('.')
|
260
|
+
end
|
261
|
+
|
262
|
+
def segments # :nodoc:
|
263
|
+
|
264
|
+
# segments is lazy so it can pick up version values that come from
|
265
|
+
# old marshaled versions, which don't go through marshal_load.
|
266
|
+
|
267
|
+
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
|
268
|
+
/^\d+$/ =~ s ? s.to_i : s
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
##
|
273
|
+
# A recommended version for use with a ~> Requirement.
|
274
|
+
|
275
|
+
def spermy_recommendation
|
276
|
+
segments = self.segments.dup
|
277
|
+
|
278
|
+
segments.pop while segments.any? { |s| String === s }
|
279
|
+
segments.pop while segments.size > 2
|
280
|
+
segments.push 0 while segments.size < 2
|
281
|
+
|
282
|
+
"~> #{segments.join(".")}"
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# Compares this version with +other+ returning -1, 0, or 1 if the
|
287
|
+
# other version is larger, the same, or smaller than this
|
288
|
+
# one. Attempts to compare to something that's not a
|
289
|
+
# <tt>Gem::Version</tt> return +nil+.
|
290
|
+
|
291
|
+
def <=> other
|
292
|
+
return unless Gem::Version === other
|
293
|
+
return 0 if @version == other.version
|
294
|
+
|
295
|
+
lhsegments = segments
|
296
|
+
rhsegments = other.segments
|
297
|
+
|
298
|
+
lhsize = lhsegments.size
|
299
|
+
rhsize = rhsegments.size
|
300
|
+
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
|
301
|
+
|
302
|
+
i = 0
|
303
|
+
|
304
|
+
while i <= limit
|
305
|
+
lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
|
306
|
+
i += 1
|
307
|
+
|
308
|
+
next if lhs == rhs
|
309
|
+
return -1 if String === lhs && Numeric === rhs
|
310
|
+
return 1 if Numeric === lhs && String === rhs
|
311
|
+
return lhs <=> rhs
|
312
|
+
end
|
313
|
+
|
314
|
+
return 0
|
315
|
+
end
|
316
|
+
end
|