gem 0.0.1.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.md +75 -0
- data/bin/gem +7 -0
- data/lib/gem.rb +249 -0
- data/lib/gem/configuration.rb +43 -0
- data/lib/gem/dependency.rb +2 -0
- data/lib/gem/platform.rb +76 -0
- data/lib/gem/progressbar.rb +269 -0
- data/lib/gem/requirement.rb +9 -0
- data/lib/gem/source_index.rb +8 -0
- data/lib/gem/specification.rb +202 -0
- data/lib/gem/tar.rb +7 -0
- data/lib/gem/tar/entry.rb +145 -0
- data/lib/gem/tar/header.rb +266 -0
- data/lib/gem/tar/reader.rb +101 -0
- data/lib/gem/tar/writer.rb +240 -0
- data/lib/gem/thread_poolable.rb +59 -0
- data/lib/gem/version.rb +73 -0
- data/lib/gem/version/requirement.rb +1 -0
- data/lib/net/http/faster.rb +27 -0
- data/lib/net/http/persistent.rb +978 -0
- data/lib/net/http/persistent/ssl_reuse.rb +129 -0
- data/lib/rubygems.rb +1 -0
- data/lib/ubygems.rb +1 -0
- metadata +70 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Samuel Cochran
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Gem
|
2
|
+
|
3
|
+
Just enough not-Rubygems to index a collection of gems for download... and maybe more.
|
4
|
+
|
5
|
+
There are some severe caveats. Some are by design, others will be addressed later.
|
6
|
+
|
7
|
+
Created by [Samuel Cochran](http://sj26.com) for [Railscamp X](http://railscamps.com).
|
8
|
+
|
9
|
+
## Thoughts
|
10
|
+
|
11
|
+
Rubygems is a bit... horrible. It was a good fit for the problem it solved for a long time, but we've outgrown it. Here are a list of my thoughts on what's wrong and how we could fix it. Many of these ideas are or will permeate this `gem` gem as a (mostly backwards-compatible) drop-in replacement for rubygems.
|
12
|
+
|
13
|
+
Local index should be more granular and allow stream processing. Same with specs lists. Marshalling and constantly re-downloading a whole index, or even just a list of specs, is ludicrous at current sizes.
|
14
|
+
|
15
|
+
Break index up into more files for efficient lookup?
|
16
|
+
|
17
|
+
Learn from PIP, have quick lookups for common cases.
|
18
|
+
|
19
|
+
JSON is a better choice than YAML—it's just plain faster and becoming ubiquitous. Built-in (usually reasonably fast) support in 1.9, including streaming, which is cross-platform friendly (much moreso than YAML).
|
20
|
+
|
21
|
+
Auto-negotiated transport compression should counteract any size difference and work for partial updates.
|
22
|
+
|
23
|
+
Compression is purely a transport/storage concern.
|
24
|
+
|
25
|
+
Index journal for efficient partial updates via HTTP.
|
26
|
+
|
27
|
+
Use plain old HTTP (but the full functionality of 1.1) and static files to allow serving at edges by cloudfront, easy proxying/caching, etc.
|
28
|
+
|
29
|
+
Same directory structure and index format locally and remotely, backwards compatible:
|
30
|
+
|
31
|
+
Given the following definitions:
|
32
|
+
|
33
|
+
* `<basename>`: `<name>-<version>[-<platform>]`
|
34
|
+
* `<name>`: gem name, i.e. "rails"
|
35
|
+
* `<version>`: gem version, i.e. "3.2.2"
|
36
|
+
* `<platform>`: gem platform, omittted if platform is "ruby", the default
|
37
|
+
|
38
|
+
Proposed directory structure:
|
39
|
+
|
40
|
+
* `cache/` -> `gems/` — a symlink for old gemball location, backwards compat only.
|
41
|
+
* `index/` — index of gemspecs.
|
42
|
+
* All gemspec indexes are stored as simple tuples, `[[<name>, <version>, <platform>[, <yanked?>]]+]`
|
43
|
+
* Journalled (append-only).
|
44
|
+
* Yanked gems add another entry with <yanked?> set to true.
|
45
|
+
* `specs.json[.gz]` — index of all (non-prerelease) gemspecs
|
46
|
+
* `latest_specs.json[.gz]` — index of latest (non-prerelease) gemspec
|
47
|
+
* `prerelease_specs.json[.gz] — index of all (non-prerelease) gemspecs ([[name, version, platform]*]), journalled.
|
48
|
+
* `<name>/` — gem name specific indexes
|
49
|
+
* `specs.json[.gz]` — index of gemspecs for a gem name (for complex requirement resolution)
|
50
|
+
* `latest_specs.json[.gz]` — latest gemspec for each platform for a gem name (`gem install <name>`)
|
51
|
+
* `prerelease_specs.json[.gz]` — latest prerelease gemspec for each platform for a gem name (`gem install —prerelease <name>`)
|
52
|
+
* eventually, we might need to replicate by version segment for targeted requirements if it would provide efficiency gains — gems with many semantic versions, etc:
|
53
|
+
`version-<version-prefix>/{specs,latest_specs,prerelease_specs}.json.gz`
|
54
|
+
* could also introduce something for platform, ala:
|
55
|
+
`version-<version-prefix>/]platform-<platform>/`
|
56
|
+
* `sources/` — cached source indexes
|
57
|
+
* `<source-sha>/` — SHA of source URL (i.e. `http://rubygems.org`)
|
58
|
+
* `index/` — a cache of the top-level index/ for a particular source
|
59
|
+
* `gems/` — installed gems, backwards compatible.
|
60
|
+
* `<basename>.gem` — the gem ball
|
61
|
+
* `<basename>/` — the unpacked gem tree
|
62
|
+
* `specifications/` — gem specifications of installed gems, backwards compatible.
|
63
|
+
* `<basename>.gemspec` — ruby format, without file/test lists
|
64
|
+
|
65
|
+
Use net/http/persistent per-source for gem operations like rubygems-mirror, respecting HTTP content-type, transport-encoding (compression), range and freshness controls, and negating overhead of many small files.
|
66
|
+
|
67
|
+
Multiplex mirroring and potentially other operations over a pool of threads like rubygems-mirror.
|
68
|
+
|
69
|
+
Break old marshalling support into modules, only include them when backwards-compatible behaviour required (i.e. during upgrade, when compat is requested/configured).
|
70
|
+
|
71
|
+
Add more checks and guards to make sure the index/gemspecs/gems can't get into an invalid state.
|
72
|
+
|
73
|
+
## License
|
74
|
+
|
75
|
+
MIT (see LICENSE). Some parts adapted from Rubygems, which is under the Ruby or MIT license.
|
data/bin/gem
ADDED
data/lib/gem.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'net/http/persistent'
|
3
|
+
require 'rbconfig'
|
4
|
+
require 'time'
|
5
|
+
require 'yaml'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module Gem
|
9
|
+
VERSION = '1.8.11'.freeze
|
10
|
+
|
11
|
+
# XXX: Find methods not implemented yet
|
12
|
+
def self.method_missing symbol, *args
|
13
|
+
# XXX: WHUT, why doesn't "[athing]"[1, -1] not give "athing"? Grr.
|
14
|
+
puts "TODO: #{name}.#{symbol}(#{args.inspect.tap { |s| s.slice!(0, 1); s.slice!(-1) } })"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.[] name, version=nil, platform=nil
|
18
|
+
name.gsub! /\.gem\Z/, ""
|
19
|
+
if version.nil?
|
20
|
+
versions = Dir[File.join(path, "cache", "#{name}-*.gem")].map do |filename|
|
21
|
+
File.basename(filename)
|
22
|
+
end.map do |basename|
|
23
|
+
basename.slice(name.length + 1, basename.length - name.length - 1 - 4)
|
24
|
+
end.map do |version|
|
25
|
+
Version.new version
|
26
|
+
end.reject(&:prerelease?).sort
|
27
|
+
version = versions.last.to_s unless versions.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
specification = Specification.new name, version, platform
|
31
|
+
filename = File.join path, "cache", "#{specification.basename}.gem"
|
32
|
+
|
33
|
+
Specification.from_gem filename
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.all
|
37
|
+
# If we have an index, run the block if given
|
38
|
+
@index.gems.each(&proc) if block_given? and @index
|
39
|
+
|
40
|
+
# Otherwise build the index and run the block for each built spec
|
41
|
+
@index ||= SourceIndex.new.tap do |index|
|
42
|
+
progress = nil
|
43
|
+
Dir.foreach("gems").select do |path|
|
44
|
+
path =~ /\.gem\Z/
|
45
|
+
end.tap do |names|
|
46
|
+
progress = ProgressBar.new("Loading index", names.length)
|
47
|
+
end.each do |name|
|
48
|
+
begin
|
49
|
+
if specification = self[name].for_cache!
|
50
|
+
index.gems << specification
|
51
|
+
yield specification if block_given?
|
52
|
+
end
|
53
|
+
rescue StandardError
|
54
|
+
puts "Failed to load gem #{name.inspect}: #{$!}", $!.inspect, $!.backtrace
|
55
|
+
end
|
56
|
+
progress.inc
|
57
|
+
end
|
58
|
+
progress.finish
|
59
|
+
puts "#{index.gems.length} gems loaded into index"
|
60
|
+
index.gems.sort!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.quick_index specification
|
65
|
+
File.write("quick/#{specification.basename}.gemspec.rz", Zlib.deflate(YAML.dump(specification)))
|
66
|
+
File.write("quick/Marshal.#{marshal_version}/#{specification.basename}.gemspec.rz", Zlib.deflate(Marshal.dump(specification)))
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.index
|
70
|
+
FileUtils.mkdir_p "quick/Marshal.#{marshal_version}"
|
71
|
+
|
72
|
+
all(&method(:quick_index))
|
73
|
+
|
74
|
+
print "Marshal index... "
|
75
|
+
File.write("Marshal.#{marshal_version}.Z", Zlib.deflate(Marshal.dump(all.gems.map { |spec| [spec.basename, spec] })))
|
76
|
+
puts "done."
|
77
|
+
|
78
|
+
# deprecated: Marshal.dump(all, File.open("Marshal.#{marshal_version}", "w"))
|
79
|
+
|
80
|
+
# deprecated:
|
81
|
+
#puts "Quick index"
|
82
|
+
#File.open('quick/index', 'w') do |quick_index|
|
83
|
+
# all.gems.each do |specification|
|
84
|
+
# quick_index.write("#{specification.name.to_s}-#{specification.version.version}\n")
|
85
|
+
# end
|
86
|
+
#end
|
87
|
+
|
88
|
+
# deprecated:
|
89
|
+
#puts "Master index"
|
90
|
+
#YAML.dump(all, File.open("yaml", "w"))
|
91
|
+
#File.write("yaml.Z", Zlib.deflate(File.read("yaml")))
|
92
|
+
|
93
|
+
# un-gzipped indexes are deprecated, so generate gzipped directly:
|
94
|
+
|
95
|
+
print "Writing specs... "
|
96
|
+
Marshal.dump(all.gems.reject(&:prerelease?).map do |specification|
|
97
|
+
platform = specification.platform
|
98
|
+
platform = "ruby" if platform.nil? or platform.empty?
|
99
|
+
[specification.name, specification.version, platform]
|
100
|
+
end, Zlib::GzipWriter.new(File.open("specs.#{marshal_version}.gz", "w")))
|
101
|
+
puts "done."
|
102
|
+
|
103
|
+
print "Writing lastest_specs... "
|
104
|
+
Marshal.dump(all.gems.group_by(&:name).map do |name, specifications|
|
105
|
+
specification = specifications.reject(&:prerelease?).last
|
106
|
+
platform = specification.platform
|
107
|
+
platform = "ruby" if platform.nil? or platform.empty?
|
108
|
+
[specification.name, specification.version, platform]
|
109
|
+
end, Zlib::GzipWriter.new(File.open("latest_specs.#{marshal_version}.gz", "w")))
|
110
|
+
puts "done."
|
111
|
+
|
112
|
+
print "Writing prerelease_specs... "
|
113
|
+
Marshal.dump(all.gems.select(&:prerelease?).map do |specification|
|
114
|
+
platform = specification.platform
|
115
|
+
platform = "ruby" if platform.nil? or platform.empty?
|
116
|
+
[specification.name, specification.version, platform]
|
117
|
+
end, Zlib::GzipWriter.new(File.open("prerelease_specs.#{marshal_version}.gz", "w")))
|
118
|
+
puts "done."
|
119
|
+
|
120
|
+
# TODO: index.rss
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.mirror source=source
|
124
|
+
http = Net::HTTP::Persistent.new "gem-mirror"
|
125
|
+
print "#{File.exist? "specs.#{marshal_version}.gz" and "Updating" or "Fetching"} specifications... "
|
126
|
+
["specs", "latest_specs", "prerelease_specs"].each.in_threads do |specs_name|
|
127
|
+
path = "#{specs_name}.#{marshal_version}.gz"
|
128
|
+
uri = URI "#{source}/#{specs_name}.#{marshal_version}.gz"
|
129
|
+
headers = {}
|
130
|
+
headers['If-Modified-Since'] = File.mtime(path).rfc2822 if File.exist? path
|
131
|
+
catch :done do
|
132
|
+
loop do
|
133
|
+
request = Net::HTTP::Get.new uri.path, headers
|
134
|
+
http.request(uri, request) do |response|
|
135
|
+
puts response.inspect
|
136
|
+
if response.code == "304"
|
137
|
+
# Nothing to do, we already have latest version
|
138
|
+
throw :done
|
139
|
+
elsif response.code[0] == "3" and response["Location"]
|
140
|
+
# Redirect
|
141
|
+
url = URI.join uri.to_s, response["Location"]
|
142
|
+
elsif response.code == "206"
|
143
|
+
File.open(path, 'a') do |file|
|
144
|
+
response.read_body do |chunk|
|
145
|
+
file.write chunk
|
146
|
+
end
|
147
|
+
end
|
148
|
+
elsif response.code == "200"
|
149
|
+
File.open(path, 'w') do |file|
|
150
|
+
response.read_body do |chunk|
|
151
|
+
file.write chunk
|
152
|
+
end
|
153
|
+
end
|
154
|
+
last_modified = Time.parse response['Last-Modified']
|
155
|
+
File.utime last_modified, last_modified, path
|
156
|
+
throw :done
|
157
|
+
else
|
158
|
+
raise StandardError, "Unknown response: #{response.inspect}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
puts "done."
|
165
|
+
FileUtils.mkdir_p "gems"
|
166
|
+
progress = nil
|
167
|
+
["latest_specs", "specs", "prerelease_specs"].each do |specs_name|
|
168
|
+
Marshal.load(IO.popen("gunzip -c #{specs_name}.#{marshal_version}.gz", "r", err: nil)).tap do |tuples|
|
169
|
+
progress = ProgressBar.new("Mirroring #{specs_name.gsub('_', ' ')}", tuples.length)
|
170
|
+
end.each.in_thread_pool(of: 8) do |tuple|
|
171
|
+
name, version, platform = tuple
|
172
|
+
begin
|
173
|
+
specification = Specification.new name: name, version: version, platform: platform
|
174
|
+
path = "gems/#{specification.basename}.gem"
|
175
|
+
unless File.exist? path and Specification.try_from_gem(path)
|
176
|
+
uri = URI "#{source}/#{path}"
|
177
|
+
headers = {}
|
178
|
+
headers["Range"] = "bytes=#{File.size(path)}-" if File.exist? path
|
179
|
+
catch :done do
|
180
|
+
loop do
|
181
|
+
request = Net::HTTP::Get.new uri.path, headers
|
182
|
+
http.request uri, request do |response|
|
183
|
+
puts response.inspect
|
184
|
+
if response.code == "304"
|
185
|
+
# Nothing to do, we already have latest version
|
186
|
+
throw :done
|
187
|
+
elsif response.code[0] == "3" and response["Location"]
|
188
|
+
# Redirect
|
189
|
+
url = URI.join uri.to_s, response["Location"]
|
190
|
+
elsif response.code == "200" or response.code == "206"
|
191
|
+
# TODO: Check range properly
|
192
|
+
File.open(path, response.code == '206' ? 'a' : 'w') do |file|
|
193
|
+
response.read_body do |chunk|
|
194
|
+
file.write chunk
|
195
|
+
end
|
196
|
+
end
|
197
|
+
last_modified = Time.parse response['Last-Modified']
|
198
|
+
File.utime last_modified, last_modified, path
|
199
|
+
throw :done
|
200
|
+
else
|
201
|
+
raise StandardError, "Unknown response: #{response.inspect}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
progress.puts specification.basename
|
207
|
+
end
|
208
|
+
rescue StandardError
|
209
|
+
progress.puts "Failed to mirror gem #{name.inspect}: #{$!}", $!.inspect, $!.backtrace
|
210
|
+
end
|
211
|
+
progress.inc
|
212
|
+
end
|
213
|
+
progress.finish
|
214
|
+
end
|
215
|
+
puts "#{`/bin/ls -1f | wc -l`.to_i - 2} gems mirrored."
|
216
|
+
index
|
217
|
+
end
|
218
|
+
|
219
|
+
protected
|
220
|
+
|
221
|
+
def self.shellescape arg
|
222
|
+
if not arg.is_a? String
|
223
|
+
arg.to_s
|
224
|
+
else
|
225
|
+
arg.dup
|
226
|
+
end.tap do |arg|
|
227
|
+
# Process as a single byte sequence because not all shell
|
228
|
+
# implementations are multibyte aware.
|
229
|
+
arg.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
|
230
|
+
|
231
|
+
# A LF cannot be escaped with a backslash because a backslash + LF
|
232
|
+
# combo is regarded as line continuation and simply ignored.
|
233
|
+
arg.gsub!(/\n/, "'\n'")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
require 'gem/tar'
|
239
|
+
require 'gem/thread_poolable'
|
240
|
+
|
241
|
+
require 'gem/configuration'
|
242
|
+
require 'gem/version'
|
243
|
+
require 'gem/requirement'
|
244
|
+
require 'gem/dependency'
|
245
|
+
require 'gem/platform'
|
246
|
+
require 'gem/specification'
|
247
|
+
require 'gem/progressbar'
|
248
|
+
|
249
|
+
require 'gem/require'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Gem::Configuration
|
2
|
+
def sources
|
3
|
+
@sources ||= %w(http://rubygems.org)
|
4
|
+
end
|
5
|
+
|
6
|
+
def sources= value
|
7
|
+
@sources = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def source
|
11
|
+
sources.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def source= value
|
15
|
+
self.sources = [value]
|
16
|
+
end
|
17
|
+
|
18
|
+
def ruby_engine
|
19
|
+
if defined? RUBY_ENGINE then
|
20
|
+
RUBY_ENGINE
|
21
|
+
else
|
22
|
+
'ruby'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def path
|
27
|
+
@path ||= begin
|
28
|
+
File.join *if defined? RUBY_FRAMEWORK_VERSION
|
29
|
+
[File.dirname(RbConfig::CONFIG["sitedir"]), 'Gems', RbConfig::CONFIG["ruby_version"]]
|
30
|
+
elsif RbConfig::CONFIG["rubylibprefix"] then
|
31
|
+
[RbConfig::CONFIG["rubylibprefix"], 'gems', RbConfig::CONFIG["ruby_version"]]
|
32
|
+
else
|
33
|
+
[RbConfig::CONFIG["libdir"], ruby_engine, 'gems', RbConfig::CONFIG["ruby_version"]]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def marshal_version
|
39
|
+
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Gem.extend Gem::Configuration
|
data/lib/gem/platform.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
class Gem::Platform
|
2
|
+
RUBY = 'ruby'
|
3
|
+
|
4
|
+
attr_accessor :cpu, :os, :version
|
5
|
+
|
6
|
+
def initialize arch
|
7
|
+
case arch
|
8
|
+
when Array then
|
9
|
+
@cpu, @os, @version = arch
|
10
|
+
when String then
|
11
|
+
arch = arch.split '-'
|
12
|
+
|
13
|
+
if arch.length > 2 and arch.last !~ /\d/ then # reassemble x86-linux-gnu
|
14
|
+
extra = arch.pop
|
15
|
+
arch.last << "-#{extra}"
|
16
|
+
end
|
17
|
+
|
18
|
+
cpu = arch.shift
|
19
|
+
|
20
|
+
@cpu = case cpu
|
21
|
+
when /i\d86/ then 'x86'
|
22
|
+
else cpu
|
23
|
+
end
|
24
|
+
|
25
|
+
if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
|
26
|
+
@os, @version = arch
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
os, = arch
|
31
|
+
@cpu, os = nil, cpu if os.nil? # legacy jruby
|
32
|
+
|
33
|
+
@os, @version = case os
|
34
|
+
when /aix(\d+)/ then ['aix', $1 ]
|
35
|
+
when /cygwin/ then ['cygwin', nil]
|
36
|
+
when /darwin(\d+)?/ then ['darwin', $1 ]
|
37
|
+
when /freebsd(\d+)/ then ['freebsd', $1 ]
|
38
|
+
when /hpux(\d+)/ then ['hpux', $1 ]
|
39
|
+
when /^java$/, /^jruby$/ then ['java', nil]
|
40
|
+
when /^java([\d.]*)/ then ['java', $1 ]
|
41
|
+
when /^dotnet$/ then ['dotnet', nil]
|
42
|
+
when /^dotnet([\d.]*)/ then ['dotnet', $1 ]
|
43
|
+
when /linux/ then ['linux', $1 ]
|
44
|
+
when /mingw32/ then ['mingw32', nil]
|
45
|
+
when /(mswin\d+)(\_(\d+))?/ then
|
46
|
+
os, version = $1, $3
|
47
|
+
@cpu = 'x86' if @cpu.nil? and os =~ /32$/
|
48
|
+
[os, version]
|
49
|
+
when /netbsdelf/ then ['netbsdelf', nil]
|
50
|
+
when /openbsd(\d+\.\d+)/ then ['openbsd', $1 ]
|
51
|
+
when /solaris(\d+\.\d+)/ then ['solaris', $1 ]
|
52
|
+
# test
|
53
|
+
when /^(\w+_platform)(\d+)/ then [$1, $2 ]
|
54
|
+
else ['unknown', nil]
|
55
|
+
end
|
56
|
+
when Gem::Platform then
|
57
|
+
@cpu = arch.cpu
|
58
|
+
@os = arch.os
|
59
|
+
@version = arch.version
|
60
|
+
else
|
61
|
+
raise ArgumentError, "invalid argument #{arch.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_a
|
66
|
+
[@cpu, @os, @version]
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
to_a.compact.join '-'
|
71
|
+
end
|
72
|
+
|
73
|
+
def empty?
|
74
|
+
to_s.empty?
|
75
|
+
end
|
76
|
+
end
|