slimgems 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +120 -0
  6. data/bin/gem +25 -0
  7. data/bin/update_slimgems +35 -0
  8. data/bootstrap/Rakefile +4 -0
  9. data/hide_lib_for_update/note.txt +5 -0
  10. data/lib/gauntlet_rubygems.rb +50 -0
  11. data/lib/rbconfig/datadir.rb +20 -0
  12. data/lib/rubygems.rb +1220 -0
  13. data/lib/rubygems/builder.rb +102 -0
  14. data/lib/rubygems/command.rb +534 -0
  15. data/lib/rubygems/command_manager.rb +182 -0
  16. data/lib/rubygems/commands/build_command.rb +53 -0
  17. data/lib/rubygems/commands/cert_command.rb +86 -0
  18. data/lib/rubygems/commands/check_command.rb +80 -0
  19. data/lib/rubygems/commands/cleanup_command.rb +106 -0
  20. data/lib/rubygems/commands/contents_command.rb +98 -0
  21. data/lib/rubygems/commands/dependency_command.rb +195 -0
  22. data/lib/rubygems/commands/environment_command.rb +132 -0
  23. data/lib/rubygems/commands/fetch_command.rb +67 -0
  24. data/lib/rubygems/commands/generate_index_command.rb +133 -0
  25. data/lib/rubygems/commands/help_command.rb +172 -0
  26. data/lib/rubygems/commands/install_command.rb +178 -0
  27. data/lib/rubygems/commands/list_command.rb +35 -0
  28. data/lib/rubygems/commands/lock_command.rb +110 -0
  29. data/lib/rubygems/commands/mirror_command.rb +111 -0
  30. data/lib/rubygems/commands/outdated_command.rb +33 -0
  31. data/lib/rubygems/commands/owner_command.rb +75 -0
  32. data/lib/rubygems/commands/pristine_command.rb +93 -0
  33. data/lib/rubygems/commands/push_command.rb +56 -0
  34. data/lib/rubygems/commands/query_command.rb +280 -0
  35. data/lib/rubygems/commands/rdoc_command.rb +91 -0
  36. data/lib/rubygems/commands/search_command.rb +31 -0
  37. data/lib/rubygems/commands/server_command.rb +86 -0
  38. data/lib/rubygems/commands/setup_command.rb +387 -0
  39. data/lib/rubygems/commands/sources_command.rb +157 -0
  40. data/lib/rubygems/commands/specification_command.rb +125 -0
  41. data/lib/rubygems/commands/stale_command.rb +27 -0
  42. data/lib/rubygems/commands/uninstall_command.rb +83 -0
  43. data/lib/rubygems/commands/unpack_command.rb +121 -0
  44. data/lib/rubygems/commands/update_command.rb +212 -0
  45. data/lib/rubygems/commands/which_command.rb +86 -0
  46. data/lib/rubygems/config_file.rb +345 -0
  47. data/lib/rubygems/custom_require.rb +44 -0
  48. data/lib/rubygems/defaults.rb +101 -0
  49. data/lib/rubygems/dependency.rb +227 -0
  50. data/lib/rubygems/dependency_installer.rb +286 -0
  51. data/lib/rubygems/dependency_list.rb +208 -0
  52. data/lib/rubygems/doc_manager.rb +242 -0
  53. data/lib/rubygems/errors.rb +35 -0
  54. data/lib/rubygems/exceptions.rb +91 -0
  55. data/lib/rubygems/ext.rb +18 -0
  56. data/lib/rubygems/ext/builder.rb +56 -0
  57. data/lib/rubygems/ext/configure_builder.rb +25 -0
  58. data/lib/rubygems/ext/ext_conf_builder.rb +24 -0
  59. data/lib/rubygems/ext/rake_builder.rb +39 -0
  60. data/lib/rubygems/format.rb +81 -0
  61. data/lib/rubygems/gem_openssl.rb +92 -0
  62. data/lib/rubygems/gem_path_searcher.rb +100 -0
  63. data/lib/rubygems/gem_runner.rb +79 -0
  64. data/lib/rubygems/gemcutter_utilities.rb +49 -0
  65. data/lib/rubygems/indexer.rb +720 -0
  66. data/lib/rubygems/install_update_options.rb +125 -0
  67. data/lib/rubygems/installer.rb +604 -0
  68. data/lib/rubygems/local_remote_options.rb +135 -0
  69. data/lib/rubygems/old_format.rb +153 -0
  70. data/lib/rubygems/package.rb +97 -0
  71. data/lib/rubygems/package/f_sync_dir.rb +23 -0
  72. data/lib/rubygems/package/tar_header.rb +266 -0
  73. data/lib/rubygems/package/tar_input.rb +222 -0
  74. data/lib/rubygems/package/tar_output.rb +144 -0
  75. data/lib/rubygems/package/tar_reader.rb +106 -0
  76. data/lib/rubygems/package/tar_reader/entry.rb +141 -0
  77. data/lib/rubygems/package/tar_writer.rb +241 -0
  78. data/lib/rubygems/package_task.rb +126 -0
  79. data/lib/rubygems/platform.rb +183 -0
  80. data/lib/rubygems/remote_fetcher.rb +414 -0
  81. data/lib/rubygems/require_paths_builder.rb +18 -0
  82. data/lib/rubygems/requirement.rb +153 -0
  83. data/lib/rubygems/security.rb +814 -0
  84. data/lib/rubygems/server.rb +872 -0
  85. data/lib/rubygems/source_index.rb +597 -0
  86. data/lib/rubygems/source_info_cache.rb +395 -0
  87. data/lib/rubygems/source_info_cache_entry.rb +56 -0
  88. data/lib/rubygems/spec_fetcher.rb +337 -0
  89. data/lib/rubygems/specification.rb +1486 -0
  90. data/lib/rubygems/test_utilities.rb +147 -0
  91. data/lib/rubygems/text.rb +65 -0
  92. data/lib/rubygems/uninstaller.rb +278 -0
  93. data/lib/rubygems/user_interaction.rb +527 -0
  94. data/lib/rubygems/validator.rb +240 -0
  95. data/lib/rubygems/version.rb +316 -0
  96. data/lib/rubygems/version_option.rb +65 -0
  97. data/lib/ubygems.rb +10 -0
  98. data/setup.rb +42 -0
  99. data/test/bogussources.rb +8 -0
  100. data/test/data/gem-private_key.pem +27 -0
  101. data/test/data/gem-public_cert.pem +20 -0
  102. data/test/fake_certlib/openssl.rb +7 -0
  103. data/test/foo/discover.rb +0 -0
  104. data/test/functional.rb +92 -0
  105. data/test/gem_installer_test_case.rb +97 -0
  106. data/test/gem_package_tar_test_case.rb +132 -0
  107. data/test/gemutilities.rb +605 -0
  108. data/test/insure_session.rb +43 -0
  109. data/test/mockgemui.rb +56 -0
  110. data/test/plugin/exception/rubygems_plugin.rb +2 -0
  111. data/test/plugin/load/rubygems_plugin.rb +1 -0
  112. data/test/plugin/standarderror/rubygems_plugin.rb +2 -0
  113. data/test/private_key.pem +27 -0
  114. data/test/public_cert.pem +20 -0
  115. data/test/rubygems_plugin.rb +21 -0
  116. data/test/simple_gem.rb +66 -0
  117. data/test/test_config.rb +12 -0
  118. data/test/test_gem.rb +766 -0
  119. data/test/test_gem_builder.rb +27 -0
  120. data/test/test_gem_command.rb +178 -0
  121. data/test/test_gem_command_manager.rb +207 -0
  122. data/test/test_gem_commands_build_command.rb +74 -0
  123. data/test/test_gem_commands_cert_command.rb +124 -0
  124. data/test/test_gem_commands_check_command.rb +18 -0
  125. data/test/test_gem_commands_contents_command.rb +156 -0
  126. data/test/test_gem_commands_dependency_command.rb +216 -0
  127. data/test/test_gem_commands_environment_command.rb +144 -0
  128. data/test/test_gem_commands_fetch_command.rb +76 -0
  129. data/test/test_gem_commands_generate_index_command.rb +135 -0
  130. data/test/test_gem_commands_install_command.rb +315 -0
  131. data/test/test_gem_commands_list_command.rb +36 -0
  132. data/test/test_gem_commands_lock_command.rb +68 -0
  133. data/test/test_gem_commands_mirror_command.rb +60 -0
  134. data/test/test_gem_commands_outdated_command.rb +40 -0
  135. data/test/test_gem_commands_owner_command.rb +105 -0
  136. data/test/test_gem_commands_pristine_command.rb +108 -0
  137. data/test/test_gem_commands_push_command.rb +81 -0
  138. data/test/test_gem_commands_query_command.rb +426 -0
  139. data/test/test_gem_commands_server_command.rb +59 -0
  140. data/test/test_gem_commands_sources_command.rb +209 -0
  141. data/test/test_gem_commands_specification_command.rb +139 -0
  142. data/test/test_gem_commands_stale_command.rb +38 -0
  143. data/test/test_gem_commands_uninstall_command.rb +83 -0
  144. data/test/test_gem_commands_unpack_command.rb +199 -0
  145. data/test/test_gem_commands_update_command.rb +353 -0
  146. data/test/test_gem_commands_which_command.rb +66 -0
  147. data/test/test_gem_config_file.rb +287 -0
  148. data/test/test_gem_dependency.rb +149 -0
  149. data/test/test_gem_dependency_installer.rb +661 -0
  150. data/test/test_gem_dependency_list.rb +230 -0
  151. data/test/test_gem_doc_manager.rb +31 -0
  152. data/test/test_gem_ext_configure_builder.rb +84 -0
  153. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  154. data/test/test_gem_ext_rake_builder.rb +81 -0
  155. data/test/test_gem_format.rb +70 -0
  156. data/test/test_gem_gem_path_searcher.rb +78 -0
  157. data/test/test_gem_gem_runner.rb +45 -0
  158. data/test/test_gem_gemcutter_utilities.rb +103 -0
  159. data/test/test_gem_indexer.rb +673 -0
  160. data/test/test_gem_install_update_options.rb +68 -0
  161. data/test/test_gem_installer.rb +857 -0
  162. data/test/test_gem_local_remote_options.rb +97 -0
  163. data/test/test_gem_package_tar_header.rb +130 -0
  164. data/test/test_gem_package_tar_input.rb +112 -0
  165. data/test/test_gem_package_tar_output.rb +97 -0
  166. data/test/test_gem_package_tar_reader.rb +46 -0
  167. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  168. data/test/test_gem_package_tar_writer.rb +144 -0
  169. data/test/test_gem_package_task.rb +59 -0
  170. data/test/test_gem_platform.rb +264 -0
  171. data/test/test_gem_remote_fetcher.rb +740 -0
  172. data/test/test_gem_requirement.rb +292 -0
  173. data/test/test_gem_server.rb +356 -0
  174. data/test/test_gem_silent_ui.rb +113 -0
  175. data/test/test_gem_source_index.rb +461 -0
  176. data/test/test_gem_spec_fetcher.rb +410 -0
  177. data/test/test_gem_specification.rb +1291 -0
  178. data/test/test_gem_stream_ui.rb +218 -0
  179. data/test/test_gem_text.rb +43 -0
  180. data/test/test_gem_uninstaller.rb +146 -0
  181. data/test/test_gem_validator.rb +63 -0
  182. data/test/test_gem_version.rb +181 -0
  183. data/test/test_gem_version_option.rb +89 -0
  184. data/test/test_kernel.rb +59 -0
  185. 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 ... &infin;
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