boxlet 0.2.8 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/boxlet.gemspec +1 -0
- data/config.yml +3 -0
- data/lib/boxlet.rb +21 -5
- data/lib/boxlet/app.rb +4 -1
- data/lib/boxlet/app/controller.rb +57 -33
- data/lib/boxlet/app/router.rb +4 -1
- data/lib/boxlet/app/templates.rb +12 -0
- data/lib/boxlet/app/views/gallery.html +114 -0
- data/lib/boxlet/app/views/index.html +12 -0
- data/lib/boxlet/util.rb +30 -0
- data/lib/boxlet/version.rb +1 -1
- data/lib/handlers/auth.rb +98 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0446d59f5924ddab50d982f411f5cc9d0f0c663
|
4
|
+
data.tar.gz: d8100c4c6811458fe5c1918177cb1054c738ba45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbb800ce150cef6bc836367b19d853df6c6d4be5144125c895ffb9c0dd52a8419544f310aad4fe6028b9631e67d2c77afe7af67e48656323f40fadc4b389817e
|
7
|
+
data.tar.gz: 3b8642b22e637aa03a46d151ce4d94ff2bb7c78b3327376c075a05e0fd9eda9be2526ca7e2ff4b2c411fe59c20ef788e5f0eab915afaf1cde8d0d62a3acd50c8
|
data/boxlet.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = %w[config.yml LICENSE.txt README.md Rakefile boxlet.gemspec]
|
17
17
|
spec.files += Dir.glob('lib/**/*.rb')
|
18
18
|
spec.files += Dir.glob('lib/boxlet/app/*.rb')
|
19
|
+
spec.files += Dir.glob('lib/boxlet/app/views/*.html')
|
19
20
|
|
20
21
|
spec.executables = 'boxlet'
|
21
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
data/config.yml
CHANGED
data/lib/boxlet.rb
CHANGED
@@ -12,6 +12,11 @@ module Boxlet
|
|
12
12
|
extend self
|
13
13
|
extend Boxlet::Config
|
14
14
|
|
15
|
+
PUBLIC_COMMANDS = {
|
16
|
+
run: "Run the Boxlet server",
|
17
|
+
stop: "Stop a daemonized server"
|
18
|
+
}.freeze
|
19
|
+
|
15
20
|
attr_accessor :runner, :config, :raw_params, :raw_config
|
16
21
|
|
17
22
|
def run!(argv, command='run', config_file='config.yml', &blk)
|
@@ -68,13 +73,24 @@ module Boxlet
|
|
68
73
|
def print_menu
|
69
74
|
puts "Usage: boxlet command [args]"
|
70
75
|
puts
|
71
|
-
puts "
|
72
|
-
commands_with_descriptions = App::PUBLIC_COMMANDS
|
76
|
+
puts "Common commands:"
|
77
|
+
commands_with_descriptions = PUBLIC_COMMANDS.merge(App::PUBLIC_COMMANDS)
|
73
78
|
commands = commands_with_descriptions.keys
|
74
|
-
|
75
|
-
|
76
|
-
|
79
|
+
max_command_chars = commands.sort { |a, b| a.length <=> b.length }.last.length
|
80
|
+
PUBLIC_COMMANDS.each do |command, description|
|
81
|
+
print_menu_command command, max_command_chars, description
|
82
|
+
end
|
83
|
+
|
84
|
+
puts
|
85
|
+
puts "Additional commands:"
|
86
|
+
App::PUBLIC_COMMANDS.each do |command, description|
|
87
|
+
print_menu_command command, max_command_chars, description
|
77
88
|
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def print_menu_command(command, max_command_chars, description)
|
93
|
+
puts " #{command.to_s.ljust(max_command_chars)} #{description}"
|
78
94
|
end
|
79
95
|
end
|
80
96
|
|
data/lib/boxlet/app.rb
CHANGED
@@ -4,6 +4,7 @@ require 'rack/response'
|
|
4
4
|
require 'rack/file_upload'
|
5
5
|
require 'boxlet/db'
|
6
6
|
require 'boxlet/util'
|
7
|
+
require 'handlers/auth'
|
7
8
|
require 'boxlet/app/router'
|
8
9
|
require 'boxlet/app/models'
|
9
10
|
|
@@ -29,7 +30,9 @@ module Boxlet
|
|
29
30
|
["/file_list"] => :file_list,
|
30
31
|
["/file_info"] => :file_info,
|
31
32
|
["/resync", :get] => :resync,
|
32
|
-
["/flashback", :post] => :flashback
|
33
|
+
["/flashback", :post] => :flashback,
|
34
|
+
["/gallery", :get] => :gallery,
|
35
|
+
["/gallery/images", :get] => :gallery_images,
|
33
36
|
}
|
34
37
|
end
|
35
38
|
|
@@ -3,17 +3,21 @@ require 'digest'
|
|
3
3
|
require 'thread'
|
4
4
|
require 'ImageResize'
|
5
5
|
require 'aws-sdk'
|
6
|
+
require 'boxlet/app/templates'
|
6
7
|
|
7
8
|
# routes = {
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
9
|
+
# ["/", :get] => :index,
|
10
|
+
# ["/auth"] => :auth,
|
11
|
+
# ["/register_device", :post] => :register_device,
|
12
|
+
# ["/notifications", :*] => :notifications,
|
13
|
+
# ["/stats", :post] => :stats,
|
14
|
+
# ["/push_files", :post] => :push_files,
|
15
|
+
# ["/file_list"] => :file_list,
|
16
|
+
# ["/file_info"] => :file_info,
|
17
|
+
# ["/resync", :get] => :resync,
|
18
|
+
# ["/flashback", :post] => :flashback,
|
19
|
+
# ["/gallery", :get] => :gallery,
|
20
|
+
# ["/gallery/images", :get] => :gallery_images,
|
17
21
|
# }
|
18
22
|
|
19
23
|
|
@@ -27,18 +31,25 @@ module Boxlet
|
|
27
31
|
Boxlet.log(:info, request.params)
|
28
32
|
|
29
33
|
@format = :html
|
34
|
+
@status = 200
|
35
|
+
@headers = {}
|
30
36
|
end
|
31
37
|
|
32
38
|
def action(action)
|
33
39
|
action_response = self.send(action)
|
34
40
|
set_user if action =~ /push_files|file_list|file_info/
|
35
41
|
|
36
|
-
{
|
42
|
+
{
|
43
|
+
format: @format,
|
44
|
+
content: action_response,
|
45
|
+
status: @status,
|
46
|
+
headers: @headers
|
47
|
+
}
|
37
48
|
end
|
38
49
|
|
39
50
|
# actions
|
40
51
|
def index
|
41
|
-
|
52
|
+
Templates.index
|
42
53
|
end
|
43
54
|
|
44
55
|
# def auth
|
@@ -180,6 +191,28 @@ module Boxlet
|
|
180
191
|
}).to_a)
|
181
192
|
end
|
182
193
|
|
194
|
+
def gallery
|
195
|
+
@format = :html
|
196
|
+
|
197
|
+
authorized_request do
|
198
|
+
Templates.gallery
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def gallery_images
|
203
|
+
@format = :json
|
204
|
+
|
205
|
+
authorized_request do
|
206
|
+
limit = (@params[:limit] || 50).to_i
|
207
|
+
skip = ((params[:page] || 1).to_i - 1) * limit
|
208
|
+
{
|
209
|
+
count: db.collection('assets').count(),
|
210
|
+
base_path: base_upload_path,
|
211
|
+
images: db.collection('assets').find().limit(limit).skip(skip).to_a
|
212
|
+
}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
183
216
|
|
184
217
|
private
|
185
218
|
|
@@ -192,24 +225,7 @@ module Boxlet
|
|
192
225
|
end
|
193
226
|
|
194
227
|
def user_upload_dir
|
195
|
-
|
196
|
-
user_upload_dir_name = Boxlet.config[:upload_dir] + "/" + dir_name
|
197
|
-
Dir.mkdir(user_upload_dir_name) unless File.exists?(user_upload_dir_name)
|
198
|
-
|
199
|
-
if @params[:uuid]
|
200
|
-
dir_shortname = Digest::MD5.hexdigest(dir_name)
|
201
|
-
user_upload_dir_shortname = Boxlet.config[:upload_dir] + "/" + dir_shortname
|
202
|
-
|
203
|
-
File.symlink(dir_name, user_upload_dir_shortname) if !File.symlink? user_upload_dir_shortname
|
204
|
-
|
205
|
-
if File.symlink?(user_upload_dir_shortname)
|
206
|
-
user_upload_dir_shortname
|
207
|
-
else
|
208
|
-
user_upload_dir_name
|
209
|
-
end
|
210
|
-
else
|
211
|
-
user_upload_dir_name
|
212
|
-
end
|
228
|
+
Boxlet::Util.user_upload_dir(@params[:uuid])
|
213
229
|
end
|
214
230
|
|
215
231
|
def free_space?
|
@@ -217,11 +233,19 @@ module Boxlet
|
|
217
233
|
end
|
218
234
|
|
219
235
|
def base_upload_path
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
236
|
+
Boxlet::Util.base_upload_path(@params[:uuid])
|
237
|
+
end
|
238
|
+
|
239
|
+
def authorized_request(&action)
|
240
|
+
auth = Boxlet::Handlers::Auth.new(
|
241
|
+
Boxlet.config[:gallery_username],
|
242
|
+
Boxlet.config[:gallery_password]
|
243
|
+
)
|
244
|
+
@status, @headers, content = auth.authorize(request) do
|
245
|
+
action.call
|
224
246
|
end
|
247
|
+
|
248
|
+
content
|
225
249
|
end
|
226
250
|
end
|
227
251
|
end
|
data/lib/boxlet/app/router.rb
CHANGED
@@ -23,7 +23,10 @@ module Boxlet
|
|
23
23
|
if @method == :* || (request.get? && @method == :get) || (request.post? && @method == :post)
|
24
24
|
Boxlet.log(:info, "INFO: Responding: #{@method.upcase} => #{@action}")
|
25
25
|
action_response = controller.action(@action)
|
26
|
-
response.status =
|
26
|
+
response.status = action_response[:status]
|
27
|
+
action_response[:headers].each do |key, value|
|
28
|
+
response.header[key] = value
|
29
|
+
end
|
27
30
|
else
|
28
31
|
response.status = 404
|
29
32
|
action_response = {format: :html, content: "404 not found"}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Boxlet
|
2
|
+
class Templates
|
3
|
+
def self.method_missing(method, *arguments, &block)
|
4
|
+
filename = "#{File.dirname(__FILE__)}/views/#{method}.html"
|
5
|
+
if File.exist?(filename)
|
6
|
+
File.open(filename).read
|
7
|
+
else
|
8
|
+
raise "Template file not found for #{method}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
* {
|
5
|
+
margin: 0;
|
6
|
+
padding: 0;
|
7
|
+
box-sizing: border-box;
|
8
|
+
}
|
9
|
+
|
10
|
+
#gallery {
|
11
|
+
max-width: 100%;
|
12
|
+
margin: 1em auto;
|
13
|
+
padding: 1em;
|
14
|
+
box-shadow: 2px 2px 3px #fff;
|
15
|
+
}
|
16
|
+
.image-row {
|
17
|
+
display:block;
|
18
|
+
margin: 0.25em;
|
19
|
+
}
|
20
|
+
.image-row:after {
|
21
|
+
display: block;
|
22
|
+
content: " ";
|
23
|
+
clear: both;
|
24
|
+
}
|
25
|
+
.image-row li {
|
26
|
+
background-position: center center;
|
27
|
+
background-size: cover;
|
28
|
+
cursor: pointer;
|
29
|
+
float: left;
|
30
|
+
height: 5em;
|
31
|
+
list-style: none;
|
32
|
+
margin: 0.25em;
|
33
|
+
transition: opacity 300ms;
|
34
|
+
width: 5em;
|
35
|
+
}
|
36
|
+
.image-row li:hover {
|
37
|
+
opacity: 0.5;
|
38
|
+
}
|
39
|
+
.image-row li img {
|
40
|
+
width: 100%;
|
41
|
+
}
|
42
|
+
|
43
|
+
.hero-image {
|
44
|
+
display: block;
|
45
|
+
text-align: center;
|
46
|
+
height: 80%;
|
47
|
+
}
|
48
|
+
.hero-image img {
|
49
|
+
max-width: 100%;
|
50
|
+
max-height: 100%;
|
51
|
+
}
|
52
|
+
</style>
|
53
|
+
|
54
|
+
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
|
55
|
+
<script type="text/javascript">
|
56
|
+
angular.module("gallery", []);
|
57
|
+
angular.module("gallery").controller("GalleryController", function($scope, $location, $http) {
|
58
|
+
var basePath = '';
|
59
|
+
uuid = $location.search().uuid;
|
60
|
+
|
61
|
+
$scope.page = 1;
|
62
|
+
$scope.limit = 50;
|
63
|
+
$scope.images = [];
|
64
|
+
$scope.heroImage = null;
|
65
|
+
$scope.counter = function() {
|
66
|
+
var a = [],
|
67
|
+
pages = Math.ceil($scope.count / $scope.limit);
|
68
|
+
for (var i = 1; i <= pages; i++) {
|
69
|
+
a.push(i);
|
70
|
+
}
|
71
|
+
return a;
|
72
|
+
};
|
73
|
+
|
74
|
+
$scope.imageSource = function(image, size) {
|
75
|
+
console.log( basePath + "/" + image[size])
|
76
|
+
return basePath + "/" + image[size];
|
77
|
+
};
|
78
|
+
|
79
|
+
$scope.setHeroImage = function(image) {
|
80
|
+
$scope.heroImage = image;
|
81
|
+
};
|
82
|
+
|
83
|
+
$scope.fetchImages = function() {
|
84
|
+
var imagesUrl = "/gallery/images?page=" + $scope.page + "&limit=" + $scope.limit + "&uuid=" + uuid;
|
85
|
+
console.log(imagesUrl);
|
86
|
+
|
87
|
+
$http.get(imagesUrl).then(function(results) {
|
88
|
+
basePath = results.data.base_path;
|
89
|
+
$scope.basePath = basePath;
|
90
|
+
$scope.count = results.data.count;
|
91
|
+
$scope.images = results.data.images;
|
92
|
+
$scope.heroImage = $scope.images[0];
|
93
|
+
});
|
94
|
+
}
|
95
|
+
|
96
|
+
$scope.fetchImages();
|
97
|
+
});
|
98
|
+
</script>
|
99
|
+
</head>
|
100
|
+
<body ng-app="gallery">
|
101
|
+
<div ng-controller="GalleryController">
|
102
|
+
<div class="hero-image">
|
103
|
+
<img ng-src="{{basePath}}/{{heroImage.filename}}" />
|
104
|
+
</div>
|
105
|
+
|
106
|
+
<select ng-change="fetchImages()" ng-model="page" ng-options="page for page in counter() track by page">
|
107
|
+
<option ng-repeat="page in counter()" value="page">{{page}}</option>
|
108
|
+
</select>
|
109
|
+
<ul class="image-row">
|
110
|
+
<li ng-repeat="image in images" style="background-image: url({{imageSource(image, 'thumbnail')}})" ng-click="setHeroImage(image)"></li>
|
111
|
+
</ul>
|
112
|
+
</div>
|
113
|
+
</body>
|
114
|
+
</html>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<html>
|
2
|
+
<body>
|
3
|
+
<form action="/push_files" method="post" enctype="multipart/form-data">
|
4
|
+
UUID:<input type="text" name="uuid">
|
5
|
+
<br>
|
6
|
+
Asset Path:<input type="text" name="asset_path">
|
7
|
+
<br>
|
8
|
+
<input type="file" name="file">
|
9
|
+
<input type="submit">
|
10
|
+
</form>
|
11
|
+
</body>
|
12
|
+
</html>
|
data/lib/boxlet/util.rb
CHANGED
@@ -48,5 +48,35 @@ module Boxlet
|
|
48
48
|
stat = Filesystem.stat(Boxlet.config[:file_system_root])
|
49
49
|
(stat.block_size * stat.blocks).to_mb
|
50
50
|
end
|
51
|
+
|
52
|
+
# Directory paths
|
53
|
+
def self.user_upload_dir(uuid)
|
54
|
+
dir_name = uuid || ''
|
55
|
+
user_upload_dir_name = Boxlet.config[:upload_dir] + "/" + dir_name
|
56
|
+
Dir.mkdir(user_upload_dir_name) unless File.exists?(user_upload_dir_name)
|
57
|
+
|
58
|
+
if uuid
|
59
|
+
dir_shortname = Digest::MD5.hexdigest(dir_name)
|
60
|
+
user_upload_dir_shortname = Boxlet.config[:upload_dir] + "/" + dir_shortname
|
61
|
+
|
62
|
+
File.symlink(dir_name, user_upload_dir_shortname) if !File.symlink? user_upload_dir_shortname
|
63
|
+
|
64
|
+
if File.symlink?(user_upload_dir_shortname)
|
65
|
+
user_upload_dir_shortname
|
66
|
+
else
|
67
|
+
user_upload_dir_name
|
68
|
+
end
|
69
|
+
else
|
70
|
+
user_upload_dir_name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.base_upload_path(uuid)
|
75
|
+
if Boxlet.config[:s3][:enabled]
|
76
|
+
"https://s3.amazonaws.com/#{Boxlet.config[:s3][:bucket]}/#{uuid}"
|
77
|
+
else
|
78
|
+
"#{Boxlet.config[:public_url]}/#{Boxlet.config[:upload_dir]}/#{Digest::MD5.hexdigest(uuid)}".gsub('/./', '/')
|
79
|
+
end
|
80
|
+
end
|
51
81
|
end
|
52
82
|
end
|
data/lib/boxlet/version.rb
CHANGED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Boxlet
|
2
|
+
module Handlers
|
3
|
+
class Auth
|
4
|
+
def initialize(username, password)
|
5
|
+
@username = username
|
6
|
+
@password = password
|
7
|
+
@realm = "Boxlet"
|
8
|
+
end
|
9
|
+
|
10
|
+
def authorize(request)
|
11
|
+
@auth = Request.new(request.env)
|
12
|
+
|
13
|
+
return unauthorized unless @auth.provided?
|
14
|
+
return bad_request unless @auth.basic?
|
15
|
+
|
16
|
+
if valid?(@username, @password)
|
17
|
+
headers = {'REMOTE_USER' => @username}
|
18
|
+
|
19
|
+
[200, headers, yield]
|
20
|
+
else
|
21
|
+
unauthorized
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def challenge
|
28
|
+
'Basic realm="%s"' % @realm
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?(username, password)
|
32
|
+
@auth.credentials[0] == username &&
|
33
|
+
@auth.credentials[1] == password
|
34
|
+
end
|
35
|
+
|
36
|
+
def unauthorized(www_authenticate = challenge)
|
37
|
+
content_hash = {
|
38
|
+
'CONTENT_TYPE' => 'text/plain',
|
39
|
+
'CONTENT_LENGTH' => '0',
|
40
|
+
'WWW-Authenticate' => www_authenticate.to_s
|
41
|
+
}
|
42
|
+
return [401, content_hash, []]
|
43
|
+
end
|
44
|
+
|
45
|
+
def bad_request
|
46
|
+
content_hash = {
|
47
|
+
'CONTENT_TYPE' => 'text/plain',
|
48
|
+
'CONTENT_LENGTH' => '0'
|
49
|
+
}
|
50
|
+
return [400, content_hash, []]
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
class Request
|
55
|
+
def initialize(env)
|
56
|
+
@env = env
|
57
|
+
end
|
58
|
+
|
59
|
+
def basic?
|
60
|
+
"basic" == scheme
|
61
|
+
end
|
62
|
+
|
63
|
+
def credentials
|
64
|
+
@credentials ||= params.unpack("m*").first.split(/:/, 2)
|
65
|
+
end
|
66
|
+
|
67
|
+
def username
|
68
|
+
credentials.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def provided?
|
72
|
+
!authorization_key.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def parts
|
76
|
+
@parts ||= @env[authorization_key].split(' ', 2)
|
77
|
+
end
|
78
|
+
|
79
|
+
def scheme
|
80
|
+
@scheme ||= parts.first && parts.first.downcase
|
81
|
+
end
|
82
|
+
|
83
|
+
def params
|
84
|
+
@params ||= parts.last
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
91
|
+
|
92
|
+
def authorization_key
|
93
|
+
@authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boxlet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- arktisklada
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -207,12 +207,16 @@ files:
|
|
207
207
|
- lib/boxlet/app/controller.rb
|
208
208
|
- lib/boxlet/app/models.rb
|
209
209
|
- lib/boxlet/app/router.rb
|
210
|
+
- lib/boxlet/app/templates.rb
|
211
|
+
- lib/boxlet/app/views/gallery.html
|
212
|
+
- lib/boxlet/app/views/index.html
|
210
213
|
- lib/boxlet/config.rb
|
211
214
|
- lib/boxlet/db.rb
|
212
215
|
- lib/boxlet/log.rb
|
213
216
|
- lib/boxlet/runner.rb
|
214
217
|
- lib/boxlet/util.rb
|
215
218
|
- lib/boxlet/version.rb
|
219
|
+
- lib/handlers/auth.rb
|
216
220
|
- lib/handlers/thin.rb
|
217
221
|
- lib/rack/boxlet_url_builder.rb
|
218
222
|
- lib/rack/boxlet_url_map.rb
|
@@ -237,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
241
|
version: '0'
|
238
242
|
requirements: []
|
239
243
|
rubyforge_project:
|
240
|
-
rubygems_version: 2.
|
244
|
+
rubygems_version: 2.4.8
|
241
245
|
signing_key:
|
242
246
|
specification_version: 4
|
243
247
|
summary: Upload pics from your phone
|