rubygems-update 0.8.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubygems-update might be problematic. Click here for more details.

Files changed (96) hide show
  1. data/ChangeLog +2335 -0
  2. data/README +54 -0
  3. data/Rakefile +293 -0
  4. data/Releases +98 -0
  5. data/TODO +7 -0
  6. data/bin/gem +11 -0
  7. data/bin/gem_server +111 -0
  8. data/bin/generate_yaml_index.rb +58 -0
  9. data/bin/update_rubygems +18 -0
  10. data/doc/doc.css +73 -0
  11. data/doc/makedoc.rb +4 -0
  12. data/examples/application/an-app.gemspec +26 -0
  13. data/examples/application/bin/myapp +3 -0
  14. data/examples/application/lib/somefunctionality.rb +3 -0
  15. data/gemspecs/README +4 -0
  16. data/gemspecs/cgikit-1.1.0.gemspec +18 -0
  17. data/gemspecs/jabber4r.gemspec +26 -0
  18. data/gemspecs/linguistics.gemspec +22 -0
  19. data/gemspecs/ook.gemspec +21 -0
  20. data/gemspecs/progressbar.gemspec +22 -0
  21. data/gemspecs/redcloth.gemspec +22 -0
  22. data/gemspecs/rublog.gemspec +23 -0
  23. data/gemspecs/ruby-doom.gemspec +21 -0
  24. data/gemspecs/rubyjdwp.gemspec +21 -0
  25. data/gemspecs/statistics.gemspec +21 -0
  26. data/lib/rubygems.rb +353 -0
  27. data/lib/rubygems/builder.rb +54 -0
  28. data/lib/rubygems/cmd_manager.rb +127 -0
  29. data/lib/rubygems/command.rb +191 -0
  30. data/lib/rubygems/config_file.rb +57 -0
  31. data/lib/rubygems/doc_manager.rb +94 -0
  32. data/lib/rubygems/format.rb +65 -0
  33. data/lib/rubygems/gem_commands.rb +925 -0
  34. data/lib/rubygems/gem_runner.rb +23 -0
  35. data/lib/rubygems/installer.rb +621 -0
  36. data/lib/rubygems/loadpath_manager.rb +108 -0
  37. data/lib/rubygems/old_format.rb +150 -0
  38. data/lib/rubygems/open-uri.rb +604 -0
  39. data/lib/rubygems/package.rb +740 -0
  40. data/lib/rubygems/remote_installer.rb +499 -0
  41. data/lib/rubygems/rubygems_version.rb +6 -0
  42. data/lib/rubygems/source_index.rb +130 -0
  43. data/lib/rubygems/specification.rb +613 -0
  44. data/lib/rubygems/user_interaction.rb +176 -0
  45. data/lib/rubygems/validator.rb +148 -0
  46. data/lib/rubygems/version.rb +279 -0
  47. data/lib/ubygems.rb +4 -0
  48. data/pkgs/sources/lib/sources.rb +6 -0
  49. data/pkgs/sources/sources.gemspec +14 -0
  50. data/post-install.rb +75 -0
  51. data/redist/session.gem +433 -0
  52. data/scripts/buildtests.rb +25 -0
  53. data/scripts/gemdoc.rb +62 -0
  54. data/scripts/runtest.rb +17 -0
  55. data/scripts/specdoc.rb +164 -0
  56. data/setup.rb +1360 -0
  57. data/test/bogussources.rb +5 -0
  58. data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
  59. data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
  60. data/test/data/lib/code.rb +1 -0
  61. data/test/data/one/README.one +1 -0
  62. data/test/data/one/lib/one.rb +3 -0
  63. data/test/data/one/one.gemspec +17 -0
  64. data/test/data/one/one.yaml +40 -0
  65. data/test/functional.rb +145 -0
  66. data/test/gemenvironment.rb +45 -0
  67. data/test/gemutilities.rb +18 -0
  68. data/test/insure_session.rb +46 -0
  69. data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
  70. data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
  71. data/test/mockgemui.rb +45 -0
  72. data/test/onegem.rb +23 -0
  73. data/test/simple_gem.rb +66 -0
  74. data/test/test_builder.rb +13 -0
  75. data/test/test_cached_fetcher.rb +60 -0
  76. data/test/test_check_command.rb +28 -0
  77. data/test/test_command.rb +130 -0
  78. data/test/test_configfile.rb +36 -0
  79. data/test/test_format.rb +70 -0
  80. data/test/test_gemloadpaths.rb +45 -0
  81. data/test/test_gempaths.rb +115 -0
  82. data/test/test_loadmanager.rb +40 -0
  83. data/test/test_local_cache.rb +157 -0
  84. data/test/test_package.rb +600 -0
  85. data/test/test_parse_commands.rb +179 -0
  86. data/test/test_process_commands.rb +21 -0
  87. data/test/test_remote_fetcher.rb +162 -0
  88. data/test/test_remote_installer.rb +154 -0
  89. data/test/test_source_index.rb +58 -0
  90. data/test/test_specification.rb +286 -0
  91. data/test/test_validator.rb +53 -0
  92. data/test/test_version_comparison.rb +204 -0
  93. data/test/testgem.rc +6 -0
  94. data/test/user_capture.rb +1 -0
  95. data/test/yaml_data.rb +59 -0
  96. metadata +151 -0
@@ -0,0 +1,176 @@
1
+ module Gem
2
+
3
+ ##
4
+ # Module that defines the default UserInteraction. Any class
5
+ # including this module will have access to the +ui+ method that
6
+ # returns the default UI.
7
+ module DefaultUserInteraction
8
+
9
+ # Return the default UI.
10
+ def ui
11
+ DefaultUserInteraction.ui
12
+ end
13
+
14
+ # Set the default UI. If the default UI is never explicity set, a
15
+ # simple console based UserInteraction will be used automatically.
16
+ def ui=(new_ui)
17
+ DefaultUserInteraction.ui = new_ui
18
+ end
19
+
20
+ def use_ui(new_ui, &block)
21
+ DefaultUserInteraction.use_ui(new_ui, &block)
22
+ end
23
+
24
+ # The default UI is a class variable of the singleton class for
25
+ # this module.
26
+ class << self
27
+ def ui
28
+ @ui ||= Gem::ConsoleUI.new
29
+ end
30
+ def ui=(new_ui)
31
+ @ui = new_ui
32
+ end
33
+ def use_ui(new_ui)
34
+ old_ui = @ui
35
+ @ui = new_ui
36
+ yield
37
+ ensure
38
+ @ui = old_ui
39
+ end
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Make the default UI accessable without the "ui." prefix. Classes
45
+ # including this module may use the interaction methods on the
46
+ # default UI directly. Classes may also reference the +ui+ and
47
+ # <tt>ui=</tt> methods.
48
+ #
49
+ # Example:
50
+ #
51
+ # class X
52
+ # include Gem::UserInteraction
53
+ #
54
+ # def get_answer
55
+ # n = ask("What is the meaning of life?")
56
+ # end
57
+ # end
58
+ module UserInteraction
59
+ include DefaultUserInteraction
60
+ [
61
+ :choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
62
+ :alert_error, :terminate_interaction!, :terminate_interaction
63
+ ].each do |methname|
64
+ class_eval %{
65
+ def #{methname}(*args)
66
+ ui.#{methname}(*args)
67
+ end
68
+ }
69
+ end
70
+ end
71
+
72
+ ##
73
+ # StreamUI implements a simple stream based user interface.
74
+ class StreamUI
75
+ def initialize(in_stream, out_stream, err_stream=STDERR)
76
+ @ins = in_stream
77
+ @outs = out_stream
78
+ @errs = err_stream
79
+ end
80
+
81
+ # Choose from a list of options. +question+ is a prompt displayed
82
+ # above the list. +list+ is a list of option strings. Returns
83
+ # the pair [option_name, option_index].
84
+ def choose_from_list(question, list)
85
+ @outs.puts question
86
+ list.each_with_index do |item, index|
87
+ @outs.puts " #{index+1}. #{item}"
88
+ end
89
+ @outs.print "> "
90
+ @outs.flush
91
+ result = @ins.gets.strip.to_i - 1
92
+ return list[result], result
93
+ end
94
+
95
+ # Ask a question. Returns a true for yes, false for no.
96
+ def ask_yes_no(question, default=nil)
97
+ qstr = case default
98
+ when nil
99
+ 'yn'
100
+ when true
101
+ 'Yn'
102
+ else
103
+ 'yN'
104
+ end
105
+ result = nil
106
+ while result.nil?
107
+ result = ask("#{question} [#{qstr}]")
108
+ result = case result
109
+ when /^[Yy].*/
110
+ true
111
+ when /^[Nn].*/
112
+ false
113
+ else
114
+ default
115
+ end
116
+ end
117
+ return result
118
+ end
119
+
120
+ # Ask a question. Returns an answer.
121
+ def ask(question)
122
+ @outs.print(question + " ")
123
+ @outs.flush
124
+ result = @ins.gets
125
+ result.chomp! if result
126
+ result
127
+ end
128
+
129
+ # Display a statement.
130
+ def say(statement="")
131
+ @outs.puts statement
132
+ end
133
+
134
+ # Display an informational alert.
135
+ def alert(statement, question=nil)
136
+ @outs.puts "INFO: #{statement}"
137
+ return ask(question) if question
138
+ end
139
+
140
+ # Display a warning in a location expected to get error messages.
141
+ def alert_warning(statement, question=nil)
142
+ @errs.puts "WARNING: #{statement}"
143
+ ask(question) if question
144
+ end
145
+
146
+ # Display an error message in a location expected to get error
147
+ # messages.
148
+ def alert_error(statement, question=nil)
149
+ @errs.puts "ERROR: #{statement}"
150
+ ask(question) if question
151
+ end
152
+
153
+ # Terminate the application immediately without running any exit
154
+ # handlers.
155
+ def terminate_interaction!(status=-1)
156
+ exit!(status)
157
+ end
158
+
159
+ # Terminate the appliation normally, running any exit handlers
160
+ # that might have been defined.
161
+ def terminate_interaction(status=0)
162
+ exit(status)
163
+ end
164
+ end
165
+
166
+
167
+ ##
168
+ # Subclass of StreamUI that instantiates the user interaction using
169
+ # standard in, out and error.
170
+ class ConsoleUI < StreamUI
171
+ def initialize
172
+ super(STDIN, STDOUT, STDERR)
173
+ end
174
+ end
175
+ end
176
+
@@ -0,0 +1,148 @@
1
+ module Gem
2
+
3
+ class VerificationError < Gem::Exception; end
4
+
5
+ ##
6
+ # Validator performs various gem file and gem database validation
7
+ class Validator
8
+ include UserInteraction
9
+
10
+ ##
11
+ # Given a gem file's contents, validates against its own MD5 checksum
12
+ # gem_data:: [String] Contents of the gem file
13
+ def verify_gem(gem_data)
14
+ if(gem_data.size == 0) then
15
+ raise VerificationError.new("Empty Gem file")
16
+ end
17
+ require 'md5'
18
+ unless(gem_data =~ /MD5SUM/m)
19
+ return # Don't worry about it...this sucks. Need to fix MD5 stuff for
20
+ # new format
21
+ # FIXME
22
+ end
23
+ unless (MD5.md5(gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/, "MD5SUM = \"" + ("F" * 32) + "\"")) == $1.to_s)
24
+ raise VerificationError.new("Invalid checksum for Gem file")
25
+ end
26
+ end
27
+
28
+ ##
29
+ # Given the path to a gem file, validates against its own MD5 checksum
30
+ #
31
+ # gem_path:: [String] Path to gem file
32
+ def verify_gem_file(gem_path)
33
+ begin
34
+ File.open(gem_path, 'rb') do |file|
35
+ gem_data = file.read
36
+ verify_gem(gem_data)
37
+ end
38
+ rescue Errno::ENOENT
39
+ raise Gem::VerificationError.new("Missing gem file #{gem_path}")
40
+ end
41
+ end
42
+
43
+ private
44
+ def find_files_for_gem(gem_directory)
45
+ installed_files = []
46
+ Find.find(gem_directory) {|file_name|
47
+ fn = file_name.slice((gem_directory.size)..(file_name.size-1)).sub(/^\//, "")
48
+ if(!(fn =~ /CVS/ || File.directory?(fn) || fn == "")) then
49
+ installed_files << fn
50
+ end
51
+
52
+ }
53
+ installed_files
54
+ end
55
+
56
+
57
+ public
58
+ ErrorData = Struct.new(:path, :problem)
59
+
60
+ ##
61
+ # Checks the gem directory for the following potential
62
+ # inconsistencies/problems:
63
+ # * Checksum gem itself
64
+ # * For each file in each gem, check consistency of installed versions
65
+ # * Check for files that aren't part of the gem but are in the gems directory
66
+ # * 1 cache - 1 spec - 1 directory.
67
+ #
68
+ # returns a hash of ErrorData objects, keyed on the problem gem's name.
69
+ def alien
70
+ require 'rubygems/installer'
71
+ require 'find'
72
+ require 'md5'
73
+ errors = {}
74
+ Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
75
+ errors[gem_name] ||= []
76
+ gem_path = File.join(Gem.dir, "cache", gem_spec.full_name) + ".gem"
77
+ spec_path = File.join(Gem.dir, "specifications", gem_spec.full_name) + ".gemspec"
78
+ gem_directory = File.join(Gem.dir, "gems", gem_spec.full_name)
79
+ installed_files = find_files_for_gem(gem_directory)
80
+
81
+ if(!File.exist?(spec_path)) then
82
+ errors[gem_name] << ErrorData.new(spec_path, "Spec file doesn't exist for installed gem")
83
+ end
84
+
85
+ begin
86
+ require 'rubygems/format.rb'
87
+ verify_gem_file(gem_path)
88
+ File.open(gem_path) do |file|
89
+ format = Gem::Format.from_file_by_path(gem_path)
90
+ format.file_entries.each do |entry, data|
91
+ # Found this file. Delete it from list
92
+ installed_files.delete remove_leading_dot_dir(entry['path'])
93
+ File.open(File.join(gem_directory, entry['path']), 'rb') do |f|
94
+ unless MD5.md5(f.read).to_s == MD5.md5(data).to_s
95
+ errors[gem_name] << ErrorData.new(entry['path'], "installed file doesn't match original from gem")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ rescue VerificationError => e
101
+ errors[gem_name] << ErrorData.new(gem_path, e.message)
102
+ end
103
+ # Clean out directories that weren't explicitly included in the gemspec
104
+ # FIXME: This still allows arbitrary incorrect directories.
105
+ installed_files.delete_if {|potential_directory|
106
+ File.directory?(File.join(gem_directory, potential_directory))
107
+ }
108
+ if(installed_files.size > 0) then
109
+ errors[gem_name] << ErrorData.new(gem_path, "Unmanaged files in gem: #{installed_files.inspect}")
110
+ end
111
+ end
112
+ errors
113
+ end
114
+
115
+ ##
116
+ # Runs unit tests for a given gem specification
117
+ def unit_test(gem_spec)
118
+ start_dir = Dir.pwd
119
+ Dir.chdir(gem_spec.full_gem_path)
120
+ $: << File.join(Gem.dir, "gems", gem_spec.full_name)
121
+ # XXX: why do we need this gem_spec when we've already got 'spec'?
122
+ test_files = gem_spec.test_files
123
+ if test_files.empty?
124
+ say "There are no unit tests to run for #{gem_spec.name}-#{gem_spec.version}"
125
+ return
126
+ end
127
+ require_gem gem_spec.name, "= #{gem_spec.version.version}"
128
+ test_files.each do |f| require f end
129
+ require 'test/unit/ui/console/testrunner'
130
+ suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}")
131
+ ObjectSpace.each_object(Class) do |klass|
132
+ suite << klass.suite if (klass < Test::Unit::TestCase)
133
+ end
134
+ result = Test::Unit::UI::Console::TestRunner.run(suite, Test::Unit::UI::SILENT)
135
+ unless result.passed?
136
+ alert_error(result.to_s)
137
+ #unless ask_yes_no(result.to_s + "...keep Gem?", true) then
138
+ #Gem::Uninstaller.new(gem_spec.name, gem_spec.version.version).uninstall
139
+ #end
140
+ end
141
+ Dir.chdir(start_dir)
142
+ end
143
+
144
+ def remove_leading_dot_dir(path)
145
+ path.sub(/^\.\//, "")
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,279 @@
1
+ module Gem
2
+
3
+ ##
4
+ # The Dependency class holds a Gem name and Version::Requirement
5
+ #
6
+ class Dependency
7
+ attr_accessor :name, :version_requirements
8
+
9
+ ##
10
+ # Constructs the dependency
11
+ #
12
+ # name:: [String] name of the Gem
13
+ # version_requirements:: [String Array] version requirement (e.g. ["> 1.2"])
14
+ #
15
+ def initialize(name, version_requirements)
16
+ @name = name
17
+ @version_requirements = Version::Requirement.new(version_requirements)
18
+ @version_requirement = nil # Avoid warnings.
19
+ end
20
+
21
+ undef version_requirements
22
+ def version_requirements
23
+ normalize if @version_requirement
24
+ @version_requirements
25
+ end
26
+
27
+ def requirement_list
28
+ version_requirements.as_list
29
+ end
30
+
31
+ alias requirements_list requirement_list
32
+
33
+ def normalize
34
+ ver = @version_requirement.instance_eval { @version }
35
+ @version_requirements = Version::Requirement.new([ver])
36
+ @version_requirement = nil
37
+ end
38
+
39
+ def to_s
40
+ "#{name} (#{version_requirements})"
41
+ end
42
+
43
+ def ==(other)
44
+ self.name = other.name and
45
+ self.version_requirements == other.version_requirements
46
+ end
47
+ end
48
+
49
+ ##
50
+ # The Version class processes string versions into comparable values
51
+ #
52
+ class Version
53
+ include Comparable
54
+
55
+ attr_accessor :version
56
+
57
+ NUM_RE = /\s*(\d+(\.\d+)*)*\s*/
58
+
59
+ ##
60
+ # Checks if version string is valid format
61
+ #
62
+ # str:: [String] the version string
63
+ # return:: [Boolean] true if the string format is correct, otherwise false
64
+ #
65
+ def self.correct?(str)
66
+ /^#{NUM_RE}$/.match(str)
67
+ end
68
+
69
+ ##
70
+ # Factory method to create a Version object. Input may be a Version or a
71
+ # String. Intended to simplify client code.
72
+ #
73
+ # ver1 = Version.create('1.3.17') # -> (Version object)
74
+ # ver2 = Version.create(ver1) # -> (ver1)
75
+ # ver3 = Version.create(nil) # -> nil
76
+ #
77
+ def self.create(input)
78
+ if input.respond_to? :version
79
+ return input
80
+ elsif input.nil?
81
+ return nil
82
+ else
83
+ return Version.new(input)
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Constructs a version from the supplied string
89
+ #
90
+ # version:: [String] The version string. Format is digit.digit...
91
+ #
92
+ def initialize(version)
93
+ raise ArgumentError,
94
+ "Malformed version number string #{version}" unless Version.correct?(version)
95
+ @version = version
96
+ end
97
+
98
+ ##
99
+ # Returns the text representation of the version
100
+ #
101
+ # return:: [String] version as string
102
+ #
103
+ def to_s
104
+ @version
105
+ end
106
+
107
+ ##
108
+ # Convert version to integer array
109
+ #
110
+ # return:: [Array] list of integers
111
+ #
112
+ def to_ints
113
+ @version.scan(/\d+/).map {|s| s.to_i}
114
+ end
115
+
116
+ ##
117
+ # Compares two versions
118
+ #
119
+ # other:: [Version or .to_ints] other version to compare to
120
+ # return:: [Fixnum] -1, 0, 1
121
+ #
122
+ def <=>(other)
123
+ return 1 unless other
124
+ rnums, vnums = to_ints, other.to_ints
125
+ [rnums.size, vnums.size].max.times {|i|
126
+ rnums[i] ||= 0
127
+ vnums[i] ||= 0
128
+ }
129
+
130
+ begin
131
+ r,v = rnums.shift, vnums.shift
132
+ end until (r != v || rnums.empty?)
133
+
134
+ return r <=> v
135
+ end
136
+
137
+ # Return a new version object where the next to the last revision
138
+ # number is one greater. (e.g. 5.3.1 => 5.4)
139
+ def bump
140
+ ints = to_ints
141
+ ints.pop if ints.size > 1
142
+ ints[-1] += 1
143
+ self.class.new(ints.join("."))
144
+ end
145
+
146
+ ##
147
+ # Requirement version includes a prefaced comparator in addition
148
+ # to a version number.
149
+ #
150
+ # A Requirement object can actually contain multiple, er, requirements, as
151
+ # in (> 1.2, < 2.0).
152
+ #
153
+ class Requirement
154
+ include Comparable
155
+
156
+ OPS = {
157
+ "=" => lambda { |v, r| v == r },
158
+ "!=" => lambda { |v, r| v != r },
159
+ ">" => lambda { |v, r| v > r },
160
+ "<" => lambda { |v, r| v < r },
161
+ ">=" => lambda { |v, r| v >= r },
162
+ "<=" => lambda { |v, r| v <= r },
163
+ "~>" => lambda { |v, r| v >= r && v < r.bump }
164
+ }
165
+
166
+ OP_RE = Regexp.new(OPS.keys.collect{|k| Regexp.quote(k)}.join("|"))
167
+ REQ_RE = /\s*(#{OP_RE})\s*/
168
+
169
+ ##
170
+ # Factory method to create a Version::Requirement object. Input may be a
171
+ # Version, a String, or nil. Intended to simplify client code.
172
+ #
173
+ # If the input is "weird", the default version requirement is returned.
174
+ #
175
+ def self.create(input)
176
+ if input.kind_of?(Requirement)
177
+ return input
178
+ elsif input.kind_of?(Array)
179
+ return self.new(input)
180
+ elsif input.respond_to? :to_str
181
+ return self.new([input.to_str])
182
+ else
183
+ return self.default
184
+ end
185
+ end
186
+
187
+ ##
188
+ # A default "version requirement" can surely _only_ be '> 0'.
189
+ #
190
+ def self.default
191
+ self.new(['> 0.0.0'])
192
+ end
193
+
194
+ ##
195
+ # Constructs a version requirement instance
196
+ #
197
+ # str:: [String Array] the version requirement string (e.g. ["> 1.23"])
198
+ #
199
+ def initialize(reqs)
200
+ @requirements = reqs.collect do |rq|
201
+ op, version_string = parse(rq)
202
+ [op, Version.new(version_string)]
203
+ end
204
+ @version = nil # Avoid warnings.
205
+ end
206
+
207
+ ##
208
+ # Overrides to check for comparator
209
+ #
210
+ # str:: [String] the version requirement string
211
+ # return:: [Boolean] true if the string format is correct, otherwise false
212
+ #
213
+ def correct?(str)
214
+ /^#{REQ_RE}#{NUM_RE}$/.match(str)
215
+ end
216
+
217
+ def to_s
218
+ as_list.join(", ")
219
+ end
220
+
221
+ def as_list
222
+ normalize
223
+ @requirements.collect { |req|
224
+ "#{req[0]} #{req[1]}"
225
+ }
226
+ end
227
+
228
+ def normalize
229
+ return if @version.nil?
230
+ @requirements = [parse(@version)]
231
+ @nums = nil
232
+ @version = nil
233
+ @op = nil
234
+ end
235
+
236
+ ##
237
+ # Is the requirement satifised by +version+.
238
+ #
239
+ # version:: [Gem::Version] the version to compare against
240
+ # return:: [Boolean] true if this requirement is satisfied by
241
+ # the version, otherwise false
242
+ #
243
+ def satisfied_by?(version)
244
+ normalize
245
+ @requirements.all? { |op, rv| satisfy?(op, version, rv) }
246
+ end
247
+
248
+ private
249
+
250
+ ##
251
+ # Is "version op required_version" satisfied?
252
+ #
253
+ def satisfy?(op, version, required_version)
254
+ OPS[op].call(version, required_version)
255
+ end
256
+
257
+ ##
258
+ # Parse the version requirement string. Return the operator and
259
+ # version strings.
260
+ #
261
+ def parse(str)
262
+ if md = /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/.match(str)
263
+ [md[1], md[2]]
264
+ elsif md = /^\s*([0-9.]+)\s*$/.match(str)
265
+ ["=", md[1]]
266
+ elsif md = /^\s*(#{OP_RE})\s*$/.match(str)
267
+ [md[1], "0"]
268
+ else
269
+ fail ArgumentError, "Illformed requirement [#{str}]"
270
+ end
271
+ end
272
+
273
+ def <=>(other)
274
+ to_s <=> other.to_s
275
+ end
276
+
277
+ end
278
+ end
279
+ end