gemirro 1.5.0 → 2.0.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +29 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +6 -3
  5. data/Gemfile.lock +126 -0
  6. data/MANIFEST +3 -10
  7. data/README.md +1 -1
  8. data/bin/gemirro +7 -1
  9. data/gemirro.gemspec +7 -2
  10. data/lib/gemirro/cli/index.rb +14 -4
  11. data/lib/gemirro/cli/init.rb +1 -1
  12. data/lib/gemirro/cli/update.rb +6 -0
  13. data/lib/gemirro/cli.rb +3 -1
  14. data/lib/gemirro/configuration.rb +4 -12
  15. data/lib/gemirro/gems_fetcher.rb +7 -10
  16. data/lib/gemirro/indexer.rb +286 -178
  17. data/lib/gemirro/mirror_file.rb +1 -0
  18. data/lib/gemirro/server.rb +56 -181
  19. data/lib/gemirro/source.rb +2 -15
  20. data/lib/gemirro/utils.rb +130 -65
  21. data/lib/gemirro/version.rb +1 -1
  22. data/lib/gemirro/versions_fetcher.rb +9 -10
  23. data/lib/gemirro/versions_file.rb +26 -24
  24. data/lib/gemirro.rb +0 -1
  25. data/spec/gemirro/configuration_spec.rb +9 -2
  26. data/spec/gemirro/gems_fetcher_spec.rb +1 -1
  27. data/spec/gemirro/indexer_spec.rb +5 -5
  28. data/spec/gemirro/server_spec.rb +79 -73
  29. data/spec/gemirro/source_spec.rb +1 -10
  30. data/spec/gemirro/versions_fetcher_spec.rb +3 -4
  31. data/spec/gemirro/versions_file_spec.rb +8 -32
  32. data/template/config.rb +7 -1
  33. data/template/public/dist/css/gemirro.css +25 -1
  34. data/views/gem.erb +46 -37
  35. data/views/index.erb +41 -33
  36. data/views/layout.erb +4 -17
  37. data/views/not_found.erb +4 -4
  38. metadata +79 -16
  39. data/lib/gemirro/cache.rb +0 -115
  40. data/spec/gemirro/cache_spec.rb +0 -32
  41. data/template/public/dist/css/bootstrap.min.css +0 -7
  42. data/template/public/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  43. data/template/public/dist/fonts/glyphicons-halflings-regular.svg +0 -288
  44. data/template/public/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  45. data/template/public/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  46. data/template/public/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  47. data/template/public/dist/js/bootstrap.min.js +0 -7
data/lib/gemirro.rb CHANGED
@@ -20,7 +20,6 @@ $LOAD_PATH.unshift(File.expand_path('../', __FILE__)) unless $LOAD_PATH.include?
20
20
 
21
21
  require 'gemirro/version'
22
22
  require 'gemirro/configuration'
23
- require 'gemirro/cache'
24
23
  require 'gemirro/utils'
25
24
  require 'gemirro/gem'
26
25
  require 'gemirro/gem_version'
@@ -39,8 +39,8 @@ module Gemirro
39
39
  expect(Configuration.marshal_identifier).to match(/Marshal\.(\d+)\.(\d+)/)
40
40
  end
41
41
 
42
- it 'should return versions file' do
43
- expect(Configuration.versions_file).to match(/specs\.(\d+)\.(\d+).gz/)
42
+ it 'should error on version since no source is defined' do
43
+ expect(Gemirro.configuration.versions_file).to match(nil)
44
44
  end
45
45
 
46
46
  it 'should return marshal file' do
@@ -84,5 +84,12 @@ module Gemirro
84
84
  expect(result.host).to eq('https://rubygems.org')
85
85
  expect(result.name).to eq('rubygems')
86
86
  end
87
+
88
+
89
+ it 'should return versions file with source' do
90
+ result = @config.define_source('RubyGems', 'https://rubygems.org') do
91
+ end
92
+ expect(@config.versions_file).to match(/rubygems_org_versions$/)
93
+ end
87
94
  end
88
95
  end
@@ -12,7 +12,7 @@ module Gemirro
12
12
 
13
13
  before(:each) do
14
14
  @source = Source.new('RubyGems', 'https://rubygems.org')
15
- @versions_file = VersionsFile.new(['0.0.1', '0.0.2'])
15
+ @versions_file = VersionsFile.new(%(created_at: 2025-04-24T03:46:59Z\n---\nrack 3.0.0,3.0.1 d545a45462d63b1b4865bbb89a109366))
16
16
  @fetcher = GemsFetcher.new(@source, @versions_file)
17
17
  Gemirro.configuration.ignored_gems.clear
18
18
  end
@@ -24,7 +24,7 @@ module Gemirro
24
24
 
25
25
  dir = MirrorDirectory.new('/tmp')
26
26
  dir.add_directory('test')
27
- indexer = Indexer.new('/tmp/test')
27
+ indexer = Indexer.new
28
28
 
29
29
  Struct.new('HttpGet', :code, :body)
30
30
  http_get = Struct::HttpGet.new(200, 'bad')
@@ -43,7 +43,7 @@ module Gemirro
43
43
  allow(::Gem.configuration).to receive(:really_verbose)
44
44
  .once.and_return(true)
45
45
 
46
- indexer = Indexer.new('/tmp/test')
46
+ indexer = Indexer.new
47
47
  indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
48
48
  indexer.dest_directory = '/tmp/test'
49
49
  indexer.directory = '/tmp/gem_generate_index'
@@ -97,7 +97,7 @@ module Gemirro
97
97
  end
98
98
 
99
99
  it 'should build indices' do
100
- indexer = Indexer.new('/')
100
+ indexer = Indexer.new
101
101
  dir = MirrorDirectory.new('/')
102
102
  dir.add_directory('gems')
103
103
  dir.add_directory('quick')
@@ -140,7 +140,7 @@ module Gemirro
140
140
  end
141
141
 
142
142
  it 'should update index and exit ruby gems' do
143
- indexer = Indexer.new('/')
143
+ indexer = Indexer.new
144
144
  MirrorDirectory.new('/')
145
145
  MirrorFile.new('/specs.4.8').write('')
146
146
  expect { indexer.update_index }.to raise_error(::Gem::SystemExitException)
@@ -152,7 +152,7 @@ module Gemirro
152
152
  dir.add_directory('test/gems')
153
153
  dir.add_directory('test/quick')
154
154
 
155
- indexer = Indexer.new('/tmp/test')
155
+ indexer = Indexer.new
156
156
  indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
157
157
  indexer.dest_directory = '/tmp/test'
158
158
  indexer.directory = '/tmp/gem_generate_index'
@@ -1,7 +1,9 @@
1
1
  require 'rack/test'
2
2
  require 'json'
3
3
  require 'parallel'
4
- require 'gemirro/cache'
4
+ require 'sinatra/base'
5
+ require 'thin'
6
+ require 'base64'
5
7
  require 'gemirro/utils'
6
8
  require 'gemirro/mirror_directory'
7
9
  require 'gemirro/mirror_file'
@@ -37,15 +39,23 @@ module Gemirro
37
39
  Utils.instance_eval('@gems_orig_collection = nil')
38
40
  Utils.instance_eval('@gems_source_collection = nil')
39
41
  FakeFS::FileSystem.clone(Gemirro::Configuration.views_directory)
42
+ allow_any_instance_of(Indexer).to receive(:compress_indices)
43
+ allow_any_instance_of(Indexer).to receive(:rand).and_return('0')
44
+
45
+ source = Source.new('Rubygems', 'https://rubygems.org')
46
+ allow(Gemirro.configuration).to receive(:source).and_return(source)
40
47
  end
41
48
 
42
49
  context 'HTML render' do
43
50
  it 'should display index page' do
44
- allow(Logger).to receive(:new).twice.and_return(@fake_logger)
51
+ allow(Logger).to receive(:new).exactly(3).times.and_return(@fake_logger)
45
52
  allow(@fake_logger).to receive(:tap)
46
53
  .and_return(nil)
47
54
  .and_yield(@fake_logger)
48
55
 
56
+
57
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write('created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0\n')
58
+
49
59
  get '/'
50
60
  expect(last_response).to be_ok
51
61
  end
@@ -63,58 +73,53 @@ module Gemirro
63
73
  end
64
74
 
65
75
  it 'should display gem specifications' do
66
- marshal_dump = Marshal.dump([['volay',
67
- ::Gem::Version.create('0.1.0'),
68
- 'ruby']])
76
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0 checksum))
69
77
 
70
- MirrorFile.new('/var/www/gemirro/specs.4.8.gz.orig').write(marshal_dump)
71
- Struct.new('SuccessGzipReader', :read)
72
- gzip_reader = Struct::SuccessGzipReader.new(marshal_dump)
73
- MirrorDirectory.new('/var/www/gemirro')
74
- .add_directory('quick/Marshal.4.8')
75
- # rubocop:disable Metrics/LineLength
76
- MirrorFile.new('/var/www/gemirro/quick/Marshal.4.8/' \
77
- 'volay-0.1.0.gemspec.rz')
78
- .write("x\x9C\x8D\x94]\x8F\xD2@\x14\x86\x89Y\xBB\xB4|\xEC\x12\xD7h" \
79
- "\xD4h\xD3K\x13J\x01\x97\xC84n\x9A\xA8\xBBi\xE2\xC5\x06\xBB" \
80
- "{\xC3\x85)\xE5\x00\x13f:u:E\xD1\xC4\xDF\xE6\xB5\xBF\xCAiK" \
81
- "\x11\xE3GK\xEF\x98\xF7\xBC\xCFy\xCF\xC9\xCCQ=A\x0F\xAE\x80" \
82
- "\"\xF4>\x82\x00/p\xE0\v\xCC\xC2;\xC1\xDD\xA3\xFA\xF4\xA1k4" \
83
- "\x06\xA6e\xF6_(Hy\xEBa\xD55\xB4\r#\xFEV\xB1k\xDE\r\xEAdu" \
84
- "\xB7\xC0cY1U\xE4\xA1\x95\x8A\xD3C7A\xAA\x87)\xB4\x9C\x1FO" \
85
- "\xBE\xD7\xE4OA\xEA\x17\x16\x82k\xD4o\xBC\xD7\x99\xC2x\xEC" \
86
- "\xAD@\xBFe$\xA1\xA0\xC7\xDBX\x00\xD5\x05/\xBC\xEFg\xDE\x13" \
87
- "\xF8\x98`\x0E\x14B1U\xE4w\xEC\x1A\xC7\x17\xAF2\x85\xADd\xC4" \
88
- "\xBE96\x87\xF9\x1F\xEA\xDF%\x8A\x95\xE3T\x9E\xCC2\xF3i\x9B" \
89
- "\xA1\xB3\xCC\xFE\rD\x10\xCE!\f\xB6\x1A\xD2\x9C\xD0\xA7\xB2" \
90
- "\xBF\x13\x8A?\x13<\xEB\x06\x04\xA7b\xD4q\xF8\xAF&\x0E!\xDF" \
91
- ".~\xEF\xE3\xDC\xCC@\xD2Hl\#@M\x9E\x84BN\x00\x9D:\x11\a\x0E" \
92
- "\x04\xFC\x18.\xD1#g\x93\xCF\xEB\xC3\x81m\\\xC1\x97\xD9" \
93
- "\x9Af7\\\xE3l\xD7_\xBC\x02BX\"\xD23\xBB\xF9o\x83A\xB1\x12" \
94
- "\xBBe\xB7\xED\x93K\xFB\xB4\x82\xB6\x80\xA9K\xB1\x1E\x96" \
95
- "\x10\xEA\x03sP\xCD\xBFP\x16\xEE\x8D\x85\xBF\x86E\\\x96" \
96
- "\xC02G\xF9\b\xEC\x16:\x9D\xC3\x06\b\x8B\xD2\xA9\x95\x84" \
97
- "\xD9\x97\xED\xC3p\x89+\x81\xA9}\xAB`\xD9\x9D\xFF\x03\xF6" \
98
- "\xD2\xC2\xBF\xCD\xFD`\xDD\x15\x10\x97\xED\xA4.[\xAB\xC6(" \
99
- "\x94\x05B\xE3\xB1\xBC\xA5e\xF6\xC3\xAA\x11\n\xE5>A\x8CiD " \
100
- "`\x9B\xF2\x04\xE3\xCA\t\xC6\x87\by-f,`Q\xD9\x1E,sp^q\x0F" \
101
- "\x85\xD4r\x8Dg\x11\x06\xCE\xC1\xE4>\x9D\xF9\xC9\xFC\xE5" \
102
- "\xC8YR\x1F\x133`4\xBB\xF9R~\xEF:\x93\xE8\x93\\\x92\xBF\r" \
103
- "\xA3\t\xF8\x84l\xF5<\xBF\xBE\xF9\xE3Q\xD2?q,\x04\x84:\x0E" \
104
- "\xF5\xF4\x1D1\xF3\xBA\xE7+!\"\xD4\xEB-\xB1X%\xB3\x14\xD3" \
105
- "\xCB\xEDw\xEE\xBD\xFDk\xE99OSz\xF3\xEA\xFA]w7\xF5\xAF\xB5" \
106
- "\x9F+\xFEG\x96")
107
- # rubocop:enable Metrics/LineLength
78
+ get '/gem/volay'
108
79
 
109
- allow(Zlib::GzipReader).to receive(:open)
110
- .once
111
- .with('/var/www/gemirro/specs.4.8.gz.orig')
112
- .and_return(gzip_reader)
80
+ expect(last_response.status).to eq(200)
81
+ expect(last_response).to be_ok
82
+ end
83
+
84
+ it 'responds to compact_index /names' do
85
+ MirrorFile.new('/var/www/gemirro/names.md5.aaa256.list').write(%(---\nvolay))
113
86
 
114
- get '/gem/volay'
87
+ get '/names'
88
+ expect(last_response.status).to eq(200)
89
+ expect(last_response).to be_ok
90
+
91
+
92
+ expect(last_response.body).to eq(%(---\nvolay))
93
+ expect(last_response.headers['etag']).to eq('"md5"')
94
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
95
+ end
96
+
97
+ it 'responds to compact_index /info/[gemname]' do
98
+ MirrorDirectory.new('/var/www/gemirro/info')
99
+ MirrorFile.new('/var/www/gemirro/info/volay.md5.aaa256.list').write('---\n 0.1.0 |checksum:sha256\n')
100
+
101
+
102
+ get '/info/volay'
115
103
  expect(last_response.status).to eq(200)
116
104
  expect(last_response).to be_ok
105
+ expect(last_response.body).to eq('---\n 0.1.0 |checksum:sha256\n')
106
+ expect(last_response.headers['etag']).to eq('"md5"')
107
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
117
108
  end
109
+
110
+
111
+ it 'responds to compact_index /versions' do
112
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0))
113
+
114
+ get '/versions'
115
+ expect(last_response.status).to eq(200)
116
+ expect(last_response).to be_ok
117
+ expect(last_response.body).to eq(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0))
118
+ expect(last_response.headers['etag']).to eq('"md5"')
119
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
120
+ end
121
+
122
+
118
123
  end
119
124
 
120
125
  context 'Download' do
@@ -150,8 +155,8 @@ module Gemirro
150
155
  allow(::Gem::SilentUI).to receive(:new).once.and_return(true)
151
156
 
152
157
  allow(Gemirro.configuration).to receive(:logger)
153
- .exactly(3).and_return(@fake_logger)
154
- allow(@fake_logger).to receive(:info).exactly(3)
158
+ .exactly(4).and_return(@fake_logger)
159
+ allow(@fake_logger).to receive(:info).exactly(4)
155
160
 
156
161
  get '/gems/gemirro-0.0.1.gem'
157
162
  expect(last_response).to_not be_ok
@@ -200,31 +205,20 @@ module Gemirro
200
205
  end
201
206
 
202
207
  context 'dependencies' do
203
- it 'should retrieve nothing' do
208
+ it 'should retrieve nothing and give 404' do
204
209
  get '/api/v1/dependencies'
205
- expect(last_response.headers['Content-Type'])
206
- .to eq('application/octet-stream')
207
- expect(last_response.body).to eq('')
208
- expect(last_response).to be_ok
210
+ expect(last_response.status).to eq(404)
211
+ expect(last_response).to_not be_ok
209
212
  end
210
213
 
211
- it 'should retrieve empty json' do
212
- get '/api/v1/dependencies.json'
213
- expect(last_response.headers['Content-Type'])
214
- .to eq('application/json')
215
- expect(last_response.body).to eq('')
216
- expect(last_response).to be_ok
217
- end
218
214
 
219
- it 'should retrieve empty json when gem was not found' do
215
+ it 'should retrieve nothing and give 404' do
220
216
  get '/api/v1/dependencies.json?gems=gemirro'
221
- expect(last_response.headers['Content-Type'])
222
- .to eq('application/json')
223
- expect(last_response.body).to eq('[]')
224
- expect(last_response).to be_ok
217
+ expect(last_response.status).to eq(404)
218
+ expect(last_response).to_not be_ok
225
219
  end
226
220
 
227
- it 'should retrieve json when gem was found' do
221
+ it 'should retrieve nothing and give 404' do
228
222
  MirrorDirectory.new('/var/www/gemirro')
229
223
  .add_directory('quick/Marshal.4.8')
230
224
  # rubocop:disable Metrics/LineLength
@@ -261,16 +255,28 @@ module Gemirro
261
255
  "\x9F+\xFEG\x96")
262
256
  # rubocop:enable Metrics/LineLength
263
257
 
258
+ MirrorFile.new('/var/www/gemirro/api/v1/dependencies/volay.md5.sha.list')
259
+ .write(Marshal.dump([
260
+ {
261
+ name: 'volay',
262
+ number: "0.1.0",
263
+ platform: 'ruby',
264
+ dependencies: [
265
+ {
266
+ name: 'json',
267
+ requirement: '~> 2.1'
268
+ }
269
+ ]
270
+ }
271
+ ]))
272
+
264
273
  gem = Gemirro::GemVersion.new('volay', '0.1.0', 'ruby')
265
274
  collection = Gemirro::GemVersionCollection.new([gem])
266
275
  allow(Utils).to receive(:gems_collection)
267
276
  .and_return(collection)
268
277
  get '/api/v1/dependencies.json?gems=volay'
269
- expect(last_response.headers['Content-Type'])
270
- .to eq('application/json')
271
-
272
- expect(last_response.body).to match(/"name":"volay"/)
273
- expect(last_response).to be_ok
278
+ expect(last_response.status).to eq(404)
279
+ expect(last_response).to_not be_ok
274
280
  end
275
281
  end
276
282
  end
@@ -21,20 +21,11 @@ module Gemirro
21
21
  Struct.new('FetchVersions', :body)
22
22
  result = Struct::FetchVersions.new(true)
23
23
  allow(Http).to receive(:get).once.with(
24
- "https://rubygems.org/#{Configuration.versions_file}"
24
+ "https://rubygems.org/versions"
25
25
  ).and_return(result)
26
26
  expect(@source.fetch_versions).to be_truthy
27
27
  end
28
28
 
29
- it 'should fetch prereleases versions' do
30
- Struct.new('FetchPrereleaseVersions', :body)
31
- result = Struct::FetchPrereleaseVersions.new(true)
32
- allow(Http).to receive(:get).once.with(
33
- "https://rubygems.org/#{Configuration.prerelease_versions_file}"
34
- ).and_return(result)
35
- expect(@source.fetch_prerelease_versions).to be_truthy
36
- end
37
-
38
29
  it 'should fetch gem' do
39
30
  Struct.new('FetchGem', :body)
40
31
  result = Struct::FetchGem.new(true)
@@ -17,10 +17,9 @@ module Gemirro
17
17
 
18
18
  it 'should fetch versions' do
19
19
  allow(@source).to receive(:fetch_versions).once.and_return([])
20
- allow(@source).to receive(:fetch_prerelease_versions).once.and_return([])
21
- allow(VersionsFile).to receive(:load).with('nothing', 'nothing')
22
- allow(File).to receive(:write).twice
23
- allow(File).to receive(:read).twice.and_return('nothing')
20
+ allow(VersionsFile).to receive(:load).with('nothing')
21
+ allow(File).to receive(:write).once
22
+ allow(File).to receive(:read).once.and_return('nothing')
24
23
  expect(@fetcher.fetch).to be_nil
25
24
  end
26
25
  end
@@ -7,44 +7,20 @@ module Gemirro
7
7
  describe 'VersionsFile' do
8
8
  include FakeFS::SpecHelpers
9
9
 
10
- it 'should be initialized' do
11
- @versions_file = VersionsFile.new([['gemirro', '0.0.1'],
12
- ['gemirro', '0.0.2']])
13
- expect(@versions_file.versions).to eq([['gemirro', '0.0.1'],
14
- ['gemirro', '0.0.2']])
15
- expect(@versions_file.versions_hash).to eq(
16
- 'gemirro' => [
17
- ['gemirro', '0.0.1'],
18
- ['gemirro', '0.0.2']
19
- ]
20
- )
21
- end
22
-
23
10
  it 'should load versions file' do
24
- spec = StringIO.new('w')
25
- w_gz = Zlib::GzipWriter.new(spec)
26
- w_gz.write(Marshal.dump([['gemirro', '0.0.1'],
27
- ['gemirro', '0.0.2']]))
28
- w_gz.close
29
- prerelease = StringIO.new('w')
30
- w_gz = Zlib::GzipWriter.new(prerelease)
31
- w_gz.write(Marshal.dump([['gemirro', '0.0.1.alpha1'],
32
- ['gemirro', '0.0.2.alpha2']]))
33
- w_gz.close
11
+ spec = %(created_at: 2025-01-01T00:00:00Z\n---\ngemirro 0.0.1.alpha1,0.0.1,0.0.2.alpha2,0.0.2 checksum)
34
12
 
35
- result = VersionsFile.load(spec.string, prerelease.string)
13
+ result = VersionsFile.new(spec)
36
14
  expect(result).to be_a(VersionsFile)
37
15
 
38
- expect(result.versions).to eq([['gemirro', '0.0.1'],
39
- ['gemirro', '0.0.2'],
40
- ['gemirro', '0.0.1.alpha1'],
41
- ['gemirro', '0.0.2.alpha2']])
16
+ expect(result.versions_string).to eq(%(created_at: 2025-01-01T00:00:00Z\n---\ngemirro 0.0.1.alpha1,0.0.1,0.0.2.alpha2,0.0.2 checksum))
17
+
42
18
  expect(result.versions_hash).to eq(
43
19
  'gemirro' => [
44
- ['gemirro', '0.0.1'],
45
- ['gemirro', '0.0.2'],
46
- ['gemirro', '0.0.1.alpha1'],
47
- ['gemirro', '0.0.2.alpha2']
20
+ ['gemirro', ::Gem::Version.new('0.0.1.alpha1'), 'ruby'],
21
+ ['gemirro', ::Gem::Version.new('0.0.1'), 'ruby'],
22
+ ['gemirro', ::Gem::Version.new('0.0.2.alpha2'), 'ruby'],
23
+ ['gemirro', ::Gem::Version.new('0.0.2'), 'ruby']
48
24
  ]
49
25
  )
50
26
  end
data/template/config.rb CHANGED
@@ -23,6 +23,12 @@ Gemirro.configuration.configure do
23
23
  server.access_log File.expand_path('../logs/access.log', __FILE__)
24
24
  server.error_log File.expand_path('../logs/error.log', __FILE__)
25
25
 
26
+ # Number of parallel processes while indexing. Too many will kill
27
+ # your indexing process prematurely.
28
+ #
29
+ # update_threads Etc.nprocessors - 1
30
+ # update_threads 4
31
+
26
32
  # If you don't want to generate indexes after each fetched gem.
27
33
  #
28
34
  # update_on_fetch false
@@ -54,6 +60,6 @@ Gemirro.configuration.configure do
54
60
  # All gem in the block will be downloaded with the update command.
55
61
  # Other gems will be downloaded with the server.
56
62
  define_source 'rubygems', 'https://rubygems.org' do
57
- gem 'rack', '>= 1.0.0'
63
+ gem 'rack', '>= 3.0.0'
58
64
  end
59
65
  end
@@ -1 +1,25 @@
1
- .pull-none {float:none;}
1
+ body { max-width: 620px; margin: 0 auto; padding: 0; font-family: sans-serif; }
2
+
3
+ dl { display: grid; grid-template-columns: 1fr min-content; }
4
+
5
+ dt { grid-column: 1; margin: 0; padding: 8px 0; border-bottom: 1px solid #e2e2e2; }
6
+ dd { grid-column: 2; margin: 0; padding: 8px 0; border-bottom: 1px solid #e2e2e2; }
7
+
8
+ dd.full { grid-column: 1/2; border-bottom: 0; }
9
+ dd.description, dd.authors, dd.dependencies { grid-column: 1/2; border-bottom: 0; }
10
+
11
+
12
+ a, a:visited, a:active, a:hover { color: #007bff; }
13
+ .btn { color: #fff; background: #007bff; padding: 5px; border-radius: 4px; text-decoration: none; }
14
+ .btn:visited, .btn:active, .btn:hover { color: #fff; }
15
+
16
+
17
+ ul { list-style-type: none; margin: 0; padding: 0 0 0 1em; }
18
+
19
+
20
+ @media (prefers-color-scheme: dark) {
21
+ html, body { background: #16161d; color: #fff; }
22
+ a, a:visited, a:active, a:hover { color: lightskyblue; }
23
+ .btn { color: #16161d; background: lightskyblue; padding: 5px; border-radius: 4px; text-decoration: none; }
24
+ .btn:visited, .btn:active, .btn:hover { color: #16161d; }
25
+ }
data/views/gem.erb CHANGED
@@ -1,58 +1,67 @@
1
- <div class="col-lg-12">
2
- <div class="col-lg-3 center-block pull-none text-center">
1
+
3
2
  <a class="btn btn-default" href="<%= url '/'%>">Back to Gem Index</a>
4
3
  </div>
5
4
  </div>
6
5
 
7
- <div class="col-lg-12">
6
+
7
+ <article>
8
8
  <% gem.by_name do |name, versions| %>
9
- <div class="col-lg-6 center-block pull-none">
10
- <div class="panel panel-info">
11
- <div class="panel-heading">
12
- <a href="<%= url("gem/#{name}") %>">
13
- <h2 class="panel-title"><%= escape(name) %> <span class="badge pull-right"><%= escape(versions.newest.number) %></span></h2>
14
- </a>
15
- </div>
16
- <div class="panel-body">
17
- <% newest_gem = versions.newest %>
18
- <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
19
- <p><%= escape(spec.description) %></p>
9
+ <dl>
10
+ <dt>
11
+ <h2><%= Rack::Utils.escape_html(name) %></h2>
12
+ </dt>
13
+ <dd>
14
+ <h2><%= Rack::Utils.escape_html(versions.newest.number) %></h2>
15
+ </dd>
16
+ <% newest_gem = versions.newest %>
17
+ <% spec = Gemirro::Utils.spec_for(name, newest_gem.number, newest_gem.platform) %>
18
+ <% if spec %>
19
+ <dd class="description">
20
+ <%= Rack::Utils.escape_html(spec.description) %>
21
+ </dd>
20
22
 
23
+ <% if spec.dependencies.size > 0 %>
24
+ <dd class="dependencies">
21
25
  <h3>Dependencies</h3>
22
- <ul class="list-group">
26
+ <ul>
23
27
  <% spec.dependencies.each do |dependency| %>
24
28
  <li class="list-group-item">
25
- <a href="<%= url("gem/#{dependency.name}") %>"><%= escape([dependency.name, dependency.requirement].join(' ')) %></a>
29
+ <a href="<%= url("gem/#{dependency.name}") %>"><%= Rack::Utils.escape_html([dependency.name, dependency.requirement].join(' ')) %></a>
26
30
  </li>
27
31
  <% end %>
28
32
  </ul>
33
+ </dd>
34
+ <% end %>
29
35
 
36
+ <% if spec.authors.size > 0 %>
37
+ <dd class="authors">
30
38
  <h3>Authors</h3>
31
- <ul class="list-group">
39
+ <ul>
32
40
  <% spec.authors.each do |author| %>
33
41
  <li class="list-group-item">
34
- <a href="<%= homepage(spec) %>"><%= escape(author) %></a>
42
+ <a href="<%= URI.parse(Addressable::URI.escape(spec.homepage)) %>"><%= Rack::Utils.escape_html(author) %></a>
35
43
  </li>
36
44
  <% end %>
37
45
  </ul>
38
- <% end %>
39
- </div>
46
+ </dd>
47
+ <% end %>
48
+ <% end %>
40
49
 
41
- <ul class="list-group">
42
- <% versions.each.reverse_each do |version| %>
43
- <li class="list-group-item clearfix">
44
- <p class="pull-left">
45
- <code>gem install <%= escape(version.name) %> -v "<%= escape(version.number) %>"</code>
46
- <% unless version.platform =~ /^ruby/i %>
47
- <small class="platform"><%= escape(version.platform) %></small>
48
- <% end %>
49
- </p>
50
- <div class="pull-right">
51
- <a class="btn btn-primary btn-sm" href="<%= url("/gems/#{version.gemfile_name}.gem") %>">Download</a>
52
- </div>
53
- </li>
54
- <% end %>
55
- </ul>
56
- </div>
50
+ <% versions.each.reverse_each do |version| %>
51
+ <dt>
52
+ <code>
53
+ gem install
54
+ <%= Rack::Utils.escape_html(version.name) %>
55
+ --version "<%= Rack::Utils.escape_html(version.number) %>"
56
+ <% unless version.platform =~ /^ruby/i %>
57
+ --platform <%= Rack::Utils.escape_html(version.platform) %>
58
+ <% end %>
59
+ </code>
60
+ </dt>
61
+ <dd>
62
+ <a class="btn" href="<%= url("/gems/#{version.gemfile_name}.gem") %>">Download</a>
63
+ </dd>
64
+ <% end %>
65
+ </dl>
57
66
  <% end %>
58
- </div>
67
+ </article>
data/views/index.erb CHANGED
@@ -1,38 +1,46 @@
1
1
 
2
- <div class="col-lg-12">
3
- <div class="col-lg-3 center-block pull-none">
4
- <pre class="bg-info"><code>gem sources -a <%= url("/") %></code></pre>
5
- </div>
6
- </div>
2
+ <code>gem sources -a <%= url("/") %></code>
7
3
 
8
- <div class="col-lg-12">
9
- <% if gems.any? %>
10
- <div class="col-lg-6 center-block pull-none">
11
- <% gems.by_name do |name, versions| %>
12
- <div class="panel panel-info">
13
- <div class="panel-heading">
14
- <a href="<%= url("gem/#{name}") %>">
15
- <h2 class="panel-title"><%= escape(name) %> <span class="badge pull-right"><%= escape(versions.newest.number) %></span></h2>
16
- </a>
17
- </div>
4
+ <% if gems.any? %>
5
+ <article>
6
+ <% gems.by_name do |name, versions| %>
7
+ <dl>
8
+ <dt>
9
+ <a href="<%= url("gem/#{name}") %>">
10
+ <h2><%= Rack::Utils.escape_html(name) %></h2>
11
+ </a>
12
+ </dt>
13
+ <dd>
14
+ <h2><%= Rack::Utils.escape_html(versions.newest.number) %></h2>
15
+ </dd>
16
+
17
+ <% spec = Gemirro::Utils.spec_for(name, versions.newest.number, versions.newest.platform) %>
18
+ <% if spec.is_a?(::Gem::Specification) %>
19
+ <dd class="description">
20
+ <%= Rack::Utils.escape_html(spec.description) %>
21
+ </dd>
22
+ <% end %>
18
23
 
19
- <div class="panel-body">
20
- <% spec = spec_for(name, versions.newest.number) %>
21
- <% if spec.is_a?(::Gem::Specification) %>
22
- <%= escape(spec.description) %>
23
- <% end %>
24
-
25
- <% versions.reverse_each.first(5).each do |version| %>
26
- <p>
27
- <code>gem install <%= escape(version.name) %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= escape(version.number) %>"</code>
24
+ <% versions.reverse_each.first(5).each do |version| %>
25
+ <dt class="version">
26
+ <code>
27
+ gem install
28
+ <%= Rack::Utils.escape_html(version.name) %>
29
+ <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %>
30
+ --version "<%= Rack::Utils.escape_html(version.number) %>"
28
31
  <% unless version.platform =~ /^ruby/i %>
29
- <small class="platform"><%= escape(version.platform) %></small>
32
+ --platform <%= Rack::Utils.escape_html(version.platform) %></small>
30
33
  <% end %>
31
- </p>
32
- <% end %>
33
- </div>
34
- </div>
35
- <% end %>
36
- </div>
37
- <% end %>
38
- </div>
34
+ </code>
35
+ </dt>
36
+ <dd>
37
+ <a class="btn btn-primary btn-sm" href="<%= url("/gems/#{version.gemfile_name}.gem") %>">Download</a>
38
+ </dd>
39
+ <% end %>
40
+ <% if versions.size > 5 %>
41
+ <dd class="full"><a href="<%= url("gem/#{name}") %>"><%= "And %d More..." % [versions.size - 5] %></a></dd>
42
+ <% end %>
43
+ </dl>
44
+ <% end %>
45
+ </article>
46
+ <% end %>