boxlet 0.2.8 → 1.0.2
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.
- 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
|