geminabox 3.1.1 → 4.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.
data/views/gem.erb CHANGED
@@ -1,40 +1,27 @@
1
- <p>
2
- <a href="/">Back to Gem Index</a>
1
+ <p class="back-link">
2
+ <a href="<%= url("/") %>">Back to Gem Index</a>
3
3
  </p>
4
4
 
5
5
  <ul class="gemlist">
6
6
  <% @gem.by_name do |name, versions| %>
7
- <li class="gem-version">
8
- <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</h2>
9
- <% versions.each.reverse_each do |version| %>
10
- <p>
11
- <code>gem install <%= h(version.name) %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= h(version.number) %>"</code>
12
- <% unless version.platform =~ /^ruby/i %>
13
- <small class="platform"><%= h(version.platform) %></small>
7
+ <li class="gem-card">
8
+ <div class="card-header">
9
+ <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</h2>
10
+ </div>
11
+ <div class="card-body">
12
+ <% versions.each.reverse_each do |version| %>
13
+ <%= erb :_version_row, layout: false, locals: { version: version } %>
14
14
  <% end %>
15
- </p>
16
- <div class="delete-form">
17
- <a class="download" href="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">download</a>
18
- <% if @allow_delete %>
19
- <form method="post" action="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">
20
- <input type="hidden" name="_method" value="DELETE" />
21
- <button type="submit">delete</button>
22
- </form>
23
- <% end %>
24
- </div>
25
- <% end %>
15
+ </div>
26
16
 
27
- <div class="details">
28
- <p>
17
+ <div class="card-footer">
29
18
  <% newest_gem = versions.newest %>
30
19
  <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
31
- <%= h(spec.description) %>
32
- <br/>
33
- <span class="author">– <%= spec.authors.map do |author|
20
+ <p><%= h(spec.description) %></p>
21
+ <p class="author">– <%= spec.authors.map do |author|
34
22
  "<a href='#{href(spec.homepage)}'>#{h(author)}</a>"
35
- end.join(', ') %></span>
23
+ end.join(', ') %></p>
36
24
  <% end %>
37
- </p>
38
25
  </div>
39
26
 
40
27
  </li>
data/views/index.erb CHANGED
@@ -1,55 +1,52 @@
1
- <p>
2
- <small>If you did not need any credentials to get to this page</small>
3
- <pre><code>gem sources -a <%= url "/" %></code></pre>
4
- <small>If you needed some credentials to get to this page</small>
5
- <pre><code>gem sources -a <%= url.to_s.gsub('://','://username:password@') %></code></pre>
6
- <% if @allow_upload %>
7
- <small>and then ...</small>
8
- <pre><code>gem install geminabox<br />gem inabox [gemfile]</code></pre>
9
- <% end %>
10
- </p>
1
+ <div class="intro">
2
+ <p class="hint">Add this server to your Gemfile, then paste gem lines from below into the block</p>
3
+ <code class="command snippet">source "<%= h(url("/")) %>" do
4
+ gem "..."
5
+ end</code>
6
+ <p class="hint">If you needed credentials to get to this page</p>
7
+ <code class="command">bundle config set --global <%= h(URI(url("/")).host) %> username:password</code>
8
+ <p class="hint">Without Bundler</p>
9
+ <code class="command">gem install GEMNAME --source <%= h(url("/")) %></code>
10
+ <% if @allow_upload %>
11
+ <p class="hint">and then ...</p>
12
+ <code class="command">gem install geminabox<br />gem inabox [gemfile]</code>
13
+ <% end %>
14
+ </div>
11
15
 
12
16
  <% if @gems.any? %>
13
- <% if @allow_upload %><a href="<%= url("/upload") %>">Upload Another Gem</a><% end %>
17
+ <% if @allow_upload %><p class="upload-link"><a href="<%= url("/upload") %>">Upload Another Gem</a></p><% end %>
14
18
 
15
- <div id="alpha_index"><%= ("a".."z").map{|i| @index_gems.include?(i) ? "<a href='#jump_#{i}'>#{i}</a>" : i }.join(" - ") %></div>
19
+ <nav id="alpha_index">
20
+ <% ("a".."z").each do |i| %>
21
+ <% if @index_gems.include?(i) %><a href="#jump_<%= i %>"><%= i %></a><% else %><span><%= i %></span><% end %>
22
+ <% end %>
23
+ </nav>
16
24
 
17
25
  <ul class="gemlist">
18
26
 
19
27
  <% @gems.by_name do |name, versions| %>
20
- <li <%= %{id="jump_#{name[0..0].downcase}"} if @index_gems.delete(name[0..0].downcase) %> class="gem-version">
21
- <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</h2>
22
- <% versions.each.reverse_each.first(5).each do |version| %>
23
- <p>
24
- <code>gem install <%= h(version.name) %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= h(version.number) %>"</code>
25
- <% unless version.platform =~ /^ruby/i %>
26
- <small class="platform"><%= h(version.platform) %></small>
27
- <% end %>
28
- </p>
29
- <form class="delete-form" method="post" action="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">
30
- <a class="download" href="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">download</a>
31
- <% if @allow_delete %>
32
- <input type="hidden" name="_method" value="DELETE" />
33
- <button type="submit">delete</button>
34
- <% end %>
35
- </form>
36
- <% end %>
37
-
38
- <% if versions.count > 5 %>
39
- <a href="<%= h(url("/gems/#{name}")) %>" id="more_link">Older versions...</a>
40
- <% end %>
41
-
42
- <div class="details">
43
- <p>
44
- <% newest_gem = versions.newest %>
45
- <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
46
- <%= h(spec.description) %>
47
- <br/>
48
- <span class="author">– <%= spec.authors.map do |author|
49
- "<a href='#{href(spec.homepage)}'>#{h(author)}</a>"
50
- end.join(', ') %></span>
51
- <% end %>
52
- </p>
28
+ <li <%= %{id="jump_#{name[0..0].downcase}"} if @index_gems.delete(name[0..0].downcase) %> class="gem-card">
29
+ <div class="card-header" id="gem_<%= h(name) %>">
30
+ <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)<a class="permalink" href="#gem_<%= h(name) %>" title="Permalink to <%= h(name) %>" aria-label="Permalink to <%= h(name) %>"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 17H7A5 5 0 0 1 7 7h2"></path><path d="M15 7h2a5 5 0 1 1 0 10h-2"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
31
+ </div>
32
+ <div class="card-body">
33
+ <% versions.each.reverse_each.first(5).each do |version| %>
34
+ <%= erb :_version_row, layout: false, locals: { version: version } %>
35
+ <% end %>
36
+
37
+ <% if versions.count > 5 %>
38
+ <div class="more-link"><a href="<%= h(url("/gems/#{name}")) %>">Older versions...</a></div>
39
+ <% end %>
40
+ </div>
41
+
42
+ <div class="card-footer">
43
+ <% newest_gem = versions.newest %>
44
+ <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
45
+ <p><%= h(spec.description) %></p>
46
+ <p class="author">– <%= spec.authors.map do |author|
47
+ "<a href='#{href(spec.homepage)}'>#{h(author)}</a>"
48
+ end.join(', ') %></p>
49
+ <% end %>
53
50
  </div>
54
51
 
55
52
  </li>
@@ -57,6 +54,4 @@
57
54
  </ul>
58
55
  <% end %>
59
56
 
60
- <% if @allow_upload %><a href="<%= url("/upload") %>">Upload Another Gem</a><% end %>
61
-
62
- <script type="text/javascript" src="<%= url "/master.js", false %>"></script>
57
+ <% if @allow_upload %><p class="upload-link"><a href="<%= url("/upload") %>">Upload Another Gem</a></p><% end %>
data/views/layout.erb CHANGED
@@ -10,5 +10,21 @@
10
10
  <h1>Gem in a Box</h1>
11
11
  <%= yield %>
12
12
  </div>
13
+ <% if @allow_delete %>
14
+ <dialog id="delete-confirm">
15
+ <div class="delete-confirm-body">
16
+ <div class="accent"></div>
17
+ <div>
18
+ <h3></h3>
19
+ <p></p>
20
+ </div>
21
+ </div>
22
+ <div class="delete-confirm-actions">
23
+ <button type="button" class="cancel">Cancel</button>
24
+ <button type="button" class="danger">Delete this version</button>
25
+ </div>
26
+ </dialog>
27
+ <% end %>
28
+ <script type="text/javascript" src="<%= url "/master.js", false %>"></script>
13
29
  </body>
14
30
  </html>
data/views/upload.erb CHANGED
@@ -1,8 +1,6 @@
1
- <p>
2
- <small>Choose a gem file to upload ...</small>
3
- </p>
1
+ <p class="hint">Choose a gem file to upload ...</p>
4
2
  <%= @error %>
5
- <form method="POST" enctype="multipart/form-data">
3
+ <form method="POST" enctype="multipart/form-data" class="upload-form">
6
4
  <input type="file" name="file">
7
5
  <input type="submit" value="Upload">
8
- </form>
6
+ </form>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geminabox
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Lea
@@ -103,6 +103,20 @@ dependencies:
103
103
  - - ">="
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0'
106
+ - !ruby/object:Gem::Dependency
107
+ name: compact_index
108
+ requirement: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '0.15'
113
+ type: :runtime
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '0.15'
106
120
  - !ruby/object:Gem::Dependency
107
121
  name: rubygems-generate_index
108
122
  requirement: !ruby/object:Gem::Requirement
@@ -117,8 +131,22 @@ dependencies:
117
131
  - - "~>"
118
132
  - !ruby/object:Gem::Version
119
133
  version: '1.1'
120
- description: A private gem hosting and/or caching app, with client side gem push style
121
- functionality. Web UI is provided.
134
+ - !ruby/object:Gem::Dependency
135
+ name: rss
136
+ requirement: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: '0.3'
141
+ type: :runtime
142
+ prerelease: false
143
+ version_requirements: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '0.3'
148
+ description: A private gem hosting app, with client side gem push style functionality.
149
+ Web UI is provided.
122
150
  email:
123
151
  - contrib@tomlea.co.uk
124
152
  - jack@foys.net
@@ -133,8 +161,9 @@ files:
133
161
  - MIT-LICENSE
134
162
  - README.md
135
163
  - lib/geminabox.rb
164
+ - lib/geminabox/compact_index_api.rb
165
+ - lib/geminabox/compact_indexer.rb
136
166
  - lib/geminabox/disk_cache.rb
137
- - lib/geminabox/gem_list_merge.rb
138
167
  - lib/geminabox/gem_store.rb
139
168
  - lib/geminabox/gem_store_error.rb
140
169
  - lib/geminabox/gem_version.rb
@@ -146,12 +175,6 @@ files:
146
175
  - lib/geminabox/http_adapter_config_error.rb
147
176
  - lib/geminabox/incoming_gem.rb
148
177
  - lib/geminabox/indexer.rb
149
- - lib/geminabox/proxy.rb
150
- - lib/geminabox/proxy/copier.rb
151
- - lib/geminabox/proxy/file_handler.rb
152
- - lib/geminabox/proxy/hostess.rb
153
- - lib/geminabox/proxy/splicer.rb
154
- - lib/geminabox/rubygems_dependency.rb
155
178
  - lib/geminabox/server.rb
156
179
  - lib/geminabox/version.rb
157
180
  - lib/geminabox_client.rb
@@ -160,6 +183,7 @@ files:
160
183
  - public/favicon.ico
161
184
  - public/master.css
162
185
  - public/master.js
186
+ - views/_version_row.erb
163
187
  - views/atom.erb
164
188
  - views/gem.erb
165
189
  - views/index.erb
@@ -170,7 +194,6 @@ licenses:
170
194
  - MIT
171
195
  metadata:
172
196
  homepage_uri: https://github.com/geminabox/geminabox
173
- source_code_uri: https://github.com/geminabox/geminabox
174
197
  changelog_uri: https://github.com/geminabox/geminabox/releases
175
198
  rdoc_options:
176
199
  - "--main"
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "set"
4
-
5
- module Geminabox
6
- module GemListMerge
7
- def self.merge(local_gem_list, remote_gem_list, strategy:)
8
- strategy_for(strategy).merge(local_gem_list, remote_gem_list)
9
- end
10
-
11
- def self.strategy_for(strategy)
12
- case strategy
13
- when :local_gems_take_precedence_over_remote_gems
14
- LocalGemsTakePrecedenceOverRemoteGems
15
- when :combine_local_and_remote_gem_versions
16
- CombineLocalAndRemoteGemVersions
17
- else
18
- raise ArgumentError, "Merge strategy must be :local_gems_take_precedence_over_remote_gems (default) or :combine_local_and_remote_gem_versions"
19
- end
20
- end
21
-
22
- module LocalGemsTakePrecedenceOverRemoteGems
23
- def self.merge(local_gem_list, remote_gem_list)
24
- names = Set.new(local_gem_list.map { |gem| gem[:name] })
25
- local_gem_list + remote_gem_list.reject { |gem| names.include? gem[:name] }
26
- end
27
- end
28
-
29
- module CombineLocalAndRemoteGemVersions
30
- IGNORE_DEPENDENCIES = 0..-2
31
-
32
- def self.merge(local_gem_list, remote_gem_list)
33
- merged = local_gem_list + remote_gem_list
34
- merged.uniq! {|val| val.values[IGNORE_DEPENDENCIES] }
35
- merged.sort_by! {|x| x.values[IGNORE_DEPENDENCIES] }
36
- merged
37
- end
38
- end
39
- end
40
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
-
4
- module Geminabox
5
- module Proxy
6
- class Copier < FileHandler
7
-
8
- def self.copy(file_name)
9
- copier = new(file_name)
10
- copier.get_file
11
- copier
12
- end
13
-
14
- def get_file
15
- return true if proxy_file_exists?
16
- return copy_local if local_file_exists?
17
- get_remote
18
- end
19
-
20
- def copy_local
21
- FileUtils.cp local_path, proxy_path
22
- end
23
-
24
- def get_remote
25
- begin
26
- if rc = remote_content
27
- File.open(proxy_path, 'wb'){|f| f.write(rc) }
28
- end
29
- rescue
30
- File.unlink(proxy_path) if File.exist?(proxy_path)
31
- raise $!
32
- end
33
- end
34
-
35
- end
36
- end
37
- end
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Geminabox
4
- module Proxy
5
- class FileHandler
6
-
7
- attr_reader :file_name
8
-
9
- def initialize(file_name)
10
- @file_name = file_name
11
- # file_name comes from the request path; both paths get mkdir_p and writes.
12
- root = File.expand_path(root_path) + File::SEPARATOR
13
- unless local_path.start_with?(root) && proxy_path.start_with?(root)
14
- raise ArgumentError, "#{file_name} resolves outside #{root_path}"
15
- end
16
- ensure_destination_exists
17
- end
18
-
19
- def local_path
20
- File.expand_path(file_name, root_path)
21
- end
22
-
23
- def root_path
24
- Geminabox.data
25
- end
26
-
27
- def local_file_exists?
28
- file_exists? local_path
29
- end
30
-
31
- def proxy_file_exists?
32
- file_exists? proxy_path
33
- end
34
-
35
- def proxy_path
36
- File.expand_path(file_name, proxy_folder_path)
37
- end
38
-
39
- def file_exists?(path)
40
- File.exist? path
41
- end
42
-
43
- def proxy_folder_path
44
- File.join(root_path, proxy_folder_name)
45
- end
46
-
47
- def proxy_folder_name
48
- 'proxy'
49
- end
50
-
51
- def remote_content
52
- Geminabox.http_adapter.get_content(remote_url)
53
- rescue
54
- return nil if Geminabox.allow_remote_failure
55
- raise GemStoreError.new(500, "Unable to get content from #{remote_url}")
56
- end
57
-
58
- def remote_url
59
- URI.join(Geminabox.ruby_gems_url, file_name)
60
- end
61
-
62
- def local_content
63
- File.binread(local_path)
64
- end
65
-
66
- private
67
- def encoding
68
- "UTF-8"
69
- end
70
-
71
- def ensure_destination_exists
72
- create_local_folder unless local_folder_exists?
73
- create_proxy_folder unless proxy_folder_exists?
74
- end
75
-
76
- def proxy_file_folder
77
- File.dirname proxy_path
78
- end
79
-
80
- def proxy_folder_exists?
81
- Dir.exist?(proxy_file_folder)
82
- end
83
-
84
- def create_proxy_folder
85
- FileUtils.mkdir_p(proxy_file_folder)
86
- end
87
-
88
- def local_file_folder
89
- File.dirname local_path
90
- end
91
-
92
- def local_folder_exists?
93
- Dir.exist?(local_file_folder)
94
- end
95
-
96
- def create_local_folder
97
- FileUtils.mkdir_p(local_file_folder)
98
- end
99
-
100
- end
101
- end
102
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'sinatra/base'
4
- require 'net/http'
5
-
6
- module Geminabox
7
- module Proxy
8
- class Hostess < Sinatra::Base
9
- set :host_authorization, { permitted_hosts: [] }
10
-
11
- attr_accessor :file_handler
12
- def serve
13
- headers["Cache-Control"] = 'no-transform'
14
- if file_handler
15
- send_file file_handler.proxy_path
16
- else
17
- send_file(data_path, :type => response['Content-Type'])
18
- end
19
- end
20
-
21
- %w[specs.4.8.gz
22
- latest_specs.4.8.gz
23
- prerelease_specs.4.8.gz
24
- ].each do |index|
25
- get "/#{index}" do
26
- splice_file index
27
- content_type 'application/x-gzip'
28
- serve
29
- end
30
- end
31
-
32
- get '/quick/Marshal.4.8/*.gemspec.rz' do
33
- copy_file request.path_info[1..]
34
- content_type('application/x-deflate')
35
- serve
36
- end
37
-
38
- %w[specs.4.8
39
- latest_specs.4.8
40
- prerelease_specs.4.8
41
- ].each do |index|
42
- get "/#{index}" do
43
- splice_file index
44
- serve
45
- end
46
- end
47
-
48
- get "/gems/*.gem" do
49
- get_from_rubygems_if_not_local
50
- serve
51
- end
52
-
53
- private
54
- def get_from_rubygems_if_not_local
55
-
56
- unless File.exist?(data_path)
57
- ruby_gems_url = Geminabox.ruby_gems_url
58
- path = File.join(ruby_gems_url, *request.path_info)
59
- content = Geminabox.http_adapter.get_content(path)
60
- GemStore.create(IncomingGem.new(StringIO.new(content)))
61
- end
62
-
63
- end
64
-
65
- def splice_file(file_name)
66
- self.file_handler = Splicer.make(file_name)
67
- end
68
-
69
- def copy_file(file_name)
70
- self.file_handler = Copier.copy(file_name)
71
- rescue ArgumentError
72
- halt 404
73
- end
74
-
75
- # Rack::Protection normally strips ".." first; this holds without it.
76
- def data_path
77
- path = File.expand_path(File.join(Geminabox.data, *request.path_info))
78
- halt 404 unless path.start_with?(File.expand_path(Geminabox.data) + File::SEPARATOR)
79
- path
80
- end
81
-
82
-
83
- end
84
- end
85
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tempfile'
4
- require 'fileutils'
5
- require 'rubygems/util'
6
-
7
- module Geminabox
8
- module Proxy
9
- class Splicer < FileHandler
10
-
11
- def self.make(file_name)
12
- splicer = new(file_name)
13
- splicer.create
14
- splicer
15
- end
16
-
17
- def create
18
- if data = new_content
19
- f = Tempfile.create('geminabox')
20
- f.binmode
21
- begin
22
- f.write(data)
23
- ensure
24
- f.close rescue nil
25
- end
26
- FileUtils.mv f.path, splice_path
27
- end
28
- end
29
-
30
- def new_content
31
- if local_file_exists?
32
- merge_content
33
- else
34
- remote_content
35
- end
36
- end
37
-
38
- def splice_path
39
- proxy_path
40
- end
41
-
42
- def splice_folder_path
43
- proxy_folder_path
44
- end
45
-
46
- def splice_file_exists?
47
- file_exists? splice_path
48
- end
49
-
50
- def merge_content
51
- if gzip?
52
- merge_gziped_content
53
- else
54
- merge_text_content
55
- end
56
- end
57
-
58
- def gzip?
59
- /\.gz$/ =~ file_name
60
- end
61
-
62
- private
63
- def merge_gziped_content
64
- if rc = remote_content
65
- package(unpackage(local_content) | unpackage(rc))
66
- else
67
- local_content
68
- end
69
- end
70
-
71
- def unpackage(content)
72
- Marshal.load(Gem::Util.gunzip(content))
73
- end
74
-
75
- def package(content)
76
- Gem::Util.gzip(Marshal.dump(content))
77
- end
78
-
79
- def merge_text_content
80
- local_content.to_s + remote_content.to_s
81
- end
82
-
83
- end
84
- end
85
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Geminabox
4
- module Proxy
5
- def self.proxy_path(file)
6
- File.join File.dirname(__FILE__), 'proxy', file
7
- end
8
-
9
- autoload :Hostess, proxy_path('hostess')
10
- autoload :FileHandler, proxy_path('file_handler')
11
- autoload :Splicer, proxy_path('splicer')
12
- autoload :Copier, proxy_path('copier')
13
- end
14
- end