mountable_file_server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +11 -0
- data/lib/mountable_file_server.rb +9 -0
- data/lib/mountable_file_server/access.rb +44 -0
- data/lib/mountable_file_server/access_helper.rb +7 -0
- data/lib/mountable_file_server/ajax_upload_helper.rb +18 -0
- data/lib/mountable_file_server/configuration.rb +10 -0
- data/lib/mountable_file_server/endpoint.rb +55 -0
- data/lib/mountable_file_server/identifier.rb +37 -0
- data/lib/mountable_file_server/rails.rb +6 -0
- data/lib/mountable_file_server/storage.rb +52 -0
- data/lib/mountable_file_server/version.rb +3 -0
- data/mountable_file_server.gemspec +34 -0
- data/vendor/assets/javascripts/mountable_file_server.js +59 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc6b91b6cf9ef96d1231d0f403e7f468fd75d1f4
|
4
|
+
data.tar.gz: 9b00ac82aff5a58988d8154d91ccc8e465684d55
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2a1ecd0b539c510c677273bf44fac23df744c0c48ee225c13ea8dd964e2a03bfa1f9f6fb1df61ba0857b6cbafd3a725ce5cfae96d8217d17baae991b3d71789
|
7
|
+
data.tar.gz: a3dbbe1fbcba59f7e70963827c17f1b506e151bc1f8c45a9300939c42aef5a5b457d8d8e15e10478e01498a00a64f6f1a400d47e0f0b5a81f77433e2a2e1c91d
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 David Strauß
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
The fundamental idea is that your application only deals with identifiers of uploaded files in string form. No need to handle files directly. It should not complicate testing. It should allow files that are publicly accessible and private files that are not. Uploads happen via Ajax without additional work on the application developers side. It allows easy customization and adaption of the file upload process on the frontend to give a good user experience.
|
2
|
+
|
3
|
+
While it is possible to use in an Ruby on Rails application the gem itself does not depend on it and does not assume you use it.
|
4
|
+
|
5
|
+
## Concept
|
6
|
+
You mount a tiny Sinatra application, the `MountableFileServer::Endpoint`, at an URL path of your choosing. The endpoint accepts AJAX file uploads and can also deliver requested files that are already permanently stored. (Later on we take a closer look in which scenarios it makes sense to deliver file requests via the endpoint instead of your webserver.)
|
7
|
+
|
8
|
+
For every uploaded file the endpoint responds with an unique identifier, a simple string. This identifier allows you to identify an uploaded file later on. Therefore it is the only thing your application needs to know and persist. The JavaScript provided by the gem handles the AJAX file upload and writes the identifier to a hidden input field with the same `name` attribute as the original file input field. When the form is submitted your application only deals with a simple string value from a hidden input field instead of an actual file upload.
|
9
|
+
|
10
|
+
At first a newly uploaded file is only stored temporarly, when the user submits the form and your application deemed it valid and persists the data it is the applications responsibility to move the uploaded file to the permanent storage using the `MountableFileServer::Storage` class. This is necessary to prevent accumulating file garbage due to abandoned forms where an upload already happened.
|
11
|
+
|
12
|
+
## Moving Parts
|
13
|
+
`MountableFileServer::Endpoint` is a Sinatra application that can be mounted anywhere you want. It accepts uploads and responds with requested files if necessary. `MountableFileServer::Storage` is responsible for working with the actual files. Both take a `MountableFileServer::Configuration` which is used to define key settings specific to your usage scenario.
|
14
|
+
|
15
|
+
## Configuration
|
16
|
+
~~~ruby
|
17
|
+
configuration = MountableFileServer::Configuration.new stored_at: '~/uploads', mounted_at: '/uploads'
|
18
|
+
~~~
|
19
|
+
|
20
|
+
~~~ruby
|
21
|
+
storage = MountableFileServer::Storage.new config
|
22
|
+
upload = MountableFileServer::Upload.new file: file_parameters[:tempfile].read, type: 'public'
|
23
|
+
|
24
|
+
identifier = storage.store_temporary upload: upload
|
25
|
+
storage.move_to_permanent_storage identifier: identifier
|
26
|
+
|
27
|
+
storage.path_for identifier: identifier
|
28
|
+
storage.url_for identifier: identifier
|
29
|
+
~~~
|
30
|
+
|
31
|
+
~~~html
|
32
|
+
<input type="file" name="avatar" data-endpoint="/uploads" data-type="public">
|
33
|
+
~~~
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
Wieso ist MFS besser?
|
38
|
+
|
39
|
+
Losgekoppelt von der Anwendung / Active Record
|
40
|
+
Beispiel Schaltwerk beim Fahrrad im Bezug auf Versionen
|
41
|
+
Komplexe Lösungen mit hunderten Issues wo probleme erwähnt werden
|
42
|
+
Erlaubt es einfach gute UI zu machen
|
43
|
+
Upload passiert nicht im Request zum Controller
|
44
|
+
Private Uploads
|
45
|
+
|
46
|
+
Funktioniert nur wenn du Dateien auf deinem Server hast.
|
47
|
+
Nichts für Gem Installier Programmierer die erwarten das ein Gem ihren UseCase ohne zusätzlichen Aufwand löst.
|
48
|
+
|
49
|
+
|
50
|
+
Open Fragen:
|
51
|
+
Upload nur auf Gruppe X einschränken
|
52
|
+
|
53
|
+
|
54
|
+
Example1:Topic: Realestatephotography
|
55
|
+
Subtopic: Housesforsale
|
56
|
+
Angle: Picturesthatinspirebuyers Oneword: Preparation
|
57
|
+
What: Is wrong with most advertising photos? Why: Are they wrong?
|
58
|
+
How: Can they be improved?
|
59
|
+
Who: Is best to take the photos?
|
60
|
+
Objections:
|
61
|
+
Lack of time
|
62
|
+
Houses still sell Cost Deception
|
63
|
+
Example: Changingagents/changingpictures/ sale
|
64
|
+
Summary: Advantages
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "mountable_file_server/version"
|
2
|
+
|
3
|
+
module MountableFileServer
|
4
|
+
require "mountable_file_server/configuration"
|
5
|
+
require "mountable_file_server/storage"
|
6
|
+
require "mountable_file_server/endpoint"
|
7
|
+
require "mountable_file_server/identifier"
|
8
|
+
require "mountable_file_server/access"
|
9
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'url_safe_base64'
|
2
|
+
|
3
|
+
module MountableFileServer
|
4
|
+
class Access
|
5
|
+
NotAccessibleViaURL = Class.new(ArgumentError)
|
6
|
+
|
7
|
+
attr_reader :configuration
|
8
|
+
|
9
|
+
def initialize(configuration)
|
10
|
+
@configuration = configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def temporary_path_for(identifier:)
|
14
|
+
File.join configuration.stored_at, 'tmp', identifier
|
15
|
+
end
|
16
|
+
|
17
|
+
def path_for(identifier:)
|
18
|
+
identifier = Identifier.new identifier
|
19
|
+
File.join configuration.stored_at, identifier.type, identifier.filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def url_for(identifier:, resize: nil)
|
23
|
+
identifier = Identifier.new identifier
|
24
|
+
|
25
|
+
raise NotAccessibleViaURL unless identifier.public?
|
26
|
+
|
27
|
+
if resize
|
28
|
+
resize_part = UrlSafeBase64.encode64 resize
|
29
|
+
File.join configuration.mounted_at, identifier.filename.gsub('.', ".#{resize_part}.")
|
30
|
+
else
|
31
|
+
File.join configuration.mounted_at, identifier.filename
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def file_for(identifier:)
|
36
|
+
paths = [
|
37
|
+
path_for(identifier: identifier),
|
38
|
+
temporary_path_for(identifier: identifier)
|
39
|
+
]
|
40
|
+
|
41
|
+
File.new paths.find { |path| File.file? path }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# module MountableFileServer
|
2
|
+
# module AjaxUploadHelper
|
3
|
+
# module FormBuilder
|
4
|
+
# def ajax_upload_field(method, options = {})
|
5
|
+
# @template.ajax_upload_field(@object_name, method, objectify_options(options))
|
6
|
+
# end
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def ajax_upload_field(object_name, method, options = {})
|
10
|
+
# file_input = file_field(object_name, method, options.merge({ data: { 'upload-url' => MountableFileServer.public_upload_url } }))
|
11
|
+
# hidden_input = hidden_field(object_name, method, options)
|
12
|
+
# content_tag(:div, class: MountableFileServer.configuration.input_class) do
|
13
|
+
# concat file_input
|
14
|
+
# concat hidden_input
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'url_safe_base64'
|
3
|
+
require 'mini_magick'
|
4
|
+
|
5
|
+
module MountableFileServer
|
6
|
+
class Endpoint < Sinatra::Base
|
7
|
+
attr_reader :configuration
|
8
|
+
|
9
|
+
def initialize(configuration)
|
10
|
+
@configuration = configuration
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
post '/' do
|
15
|
+
path = params[:file][:tempfile].path
|
16
|
+
filename = params[:file][:filename]
|
17
|
+
type = params[:type]
|
18
|
+
storage = Storage.new configuration
|
19
|
+
|
20
|
+
storage.store_temporary(path: path, type: type, filename: filename).to_str
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/*' do
|
24
|
+
deliver_file unescape(request.path_info)
|
25
|
+
end
|
26
|
+
|
27
|
+
def deliver_file(filename)
|
28
|
+
path_to_file = File.join(configuration.stored_at, 'public', filename)
|
29
|
+
|
30
|
+
if File.file?(path_to_file)
|
31
|
+
send_file path_to_file
|
32
|
+
else
|
33
|
+
if filename.count('.') == 2
|
34
|
+
parts = filename.split('.')
|
35
|
+
base_filename = "#{parts[0]}.#{parts[2]}"
|
36
|
+
path_to_base_file = File.join(configuration.stored_at, 'public', base_filename)
|
37
|
+
|
38
|
+
if File.file?(path_to_base_file)
|
39
|
+
resize_command = UrlSafeBase64.decode64 parts[1]
|
40
|
+
image = MiniMagick::Image.open path_to_base_file
|
41
|
+
image.resize resize_command.to_s
|
42
|
+
image.write path_to_file
|
43
|
+
send_file path_to_file
|
44
|
+
else
|
45
|
+
not_found
|
46
|
+
end
|
47
|
+
else
|
48
|
+
not_found
|
49
|
+
end
|
50
|
+
|
51
|
+
not_found
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module MountableFileServer
|
4
|
+
class Identifier
|
5
|
+
attr_reader :type, :filename
|
6
|
+
|
7
|
+
def self.generate_for(filename:, type:)
|
8
|
+
extension = File.extname filename
|
9
|
+
|
10
|
+
new "#{type}-#{SecureRandom.hex}#{extension}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(identifier)
|
14
|
+
@type = /(\w+)-\w+/.match(identifier)[1]
|
15
|
+
@filename = /\w+-(.+)$/.match(identifier)[1]
|
16
|
+
|
17
|
+
raise ArgumentError.new("Unkown type `#{type}`") unless valid_type?
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_str
|
21
|
+
"#{type}-#{filename}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
to_str == other.to_str
|
26
|
+
end
|
27
|
+
|
28
|
+
def public?
|
29
|
+
type == 'public'
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def valid_type?
|
34
|
+
['public', 'private'].include?(type)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MountableFileServer
|
2
|
+
class Storage
|
3
|
+
attr_reader :access
|
4
|
+
|
5
|
+
def initialize(configuration)
|
6
|
+
@access = Access.new configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
def store_temporary(path:, type:, filename:)
|
10
|
+
file_to_be_stored = File.new path
|
11
|
+
identifier = random_identifier filename: filename, type: type
|
12
|
+
path = access.temporary_path_for identifier: identifier
|
13
|
+
|
14
|
+
File.open(path, 'wb') do |file|
|
15
|
+
file.write file_to_be_stored.read
|
16
|
+
end
|
17
|
+
|
18
|
+
identifier
|
19
|
+
end
|
20
|
+
|
21
|
+
def store_permanently(path:, type:, filename:)
|
22
|
+
identifier = store_temporary path: path, type: type, filename: filename
|
23
|
+
move_to_permanent_storage identifier: identifier
|
24
|
+
identifier
|
25
|
+
end
|
26
|
+
|
27
|
+
def move_to_permanent_storage(identifier:)
|
28
|
+
identifier = Identifier.new identifier
|
29
|
+
from = access.temporary_path_for identifier: identifier
|
30
|
+
to = access.path_for identifier: identifier
|
31
|
+
|
32
|
+
FileUtils.move from, to
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_from_permanent_storage(identifier:)
|
36
|
+
identifier = Identifier.new identifier
|
37
|
+
path = access.path_for identifier: identifier
|
38
|
+
|
39
|
+
FileUtils.rm path
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def random_identifier(filename:, type:)
|
44
|
+
loop do
|
45
|
+
identifier = Identifier.generate_for filename: filename, type: type
|
46
|
+
path = access.temporary_path_for identifier: identifier
|
47
|
+
|
48
|
+
break identifier unless File.exists?(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mountable_file_server/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mountable_file_server"
|
8
|
+
spec.version = MountableFileServer::VERSION
|
9
|
+
spec.authors = ["David Strauß"]
|
10
|
+
spec.email = ["david@strauss.io"]
|
11
|
+
spec.summary = %q{Simple mountable server that handles file uploads}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.homepage = "https://github.com/stravid/mountable_file_server"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "minitest"
|
24
|
+
spec.add_development_dependency "rails"
|
25
|
+
spec.add_development_dependency "capybara"
|
26
|
+
spec.add_development_dependency "sqlite3"
|
27
|
+
spec.add_development_dependency "poltergeist"
|
28
|
+
spec.add_development_dependency "rack-test"
|
29
|
+
spec.add_development_dependency "bogus"
|
30
|
+
|
31
|
+
spec.add_runtime_dependency "sinatra"
|
32
|
+
spec.add_runtime_dependency "mini_magick"
|
33
|
+
spec.add_runtime_dependency "url_safe_base64"
|
34
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
(function() {
|
2
|
+
var lastUploadId = 0;
|
3
|
+
var finishedUploads = [];
|
4
|
+
|
5
|
+
var dispatchEvent = function($element, name, payload) {
|
6
|
+
var event = document.createEvent('CustomEvent');
|
7
|
+
|
8
|
+
event.initCustomEvent(name, true, false, payload);
|
9
|
+
$element.dispatchEvent(event);
|
10
|
+
};
|
11
|
+
|
12
|
+
var uploadFile = function($element, file, uploadId) {
|
13
|
+
var url = $element.getAttribute('data-endpoint');
|
14
|
+
var type = $element.getAttribute('data-type');
|
15
|
+
var xhr = new XMLHttpRequest();
|
16
|
+
var formData = new FormData();
|
17
|
+
|
18
|
+
formData.append('file', file);
|
19
|
+
formData.append('type', type);
|
20
|
+
|
21
|
+
xhr.open('POST', url, true);
|
22
|
+
|
23
|
+
xhr.onreadystatechange = function() {
|
24
|
+
if (xhr.readyState === 4 && xhr.status === 200) {
|
25
|
+
finishedUploads.push(uploadId);
|
26
|
+
|
27
|
+
dispatchEvent($element, 'upload:success', {
|
28
|
+
uploadId: uploadId,
|
29
|
+
identifier: xhr.responseText,
|
30
|
+
wasLastUpload: lastUploadId == finishedUploads.length
|
31
|
+
});
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
xhr.upload.addEventListener('progress', function(progressEvent) {
|
36
|
+
if (progressEvent.lengthComputable) {
|
37
|
+
dispatchEvent($element, 'upload:progress', {
|
38
|
+
uploadId: uploadId,
|
39
|
+
progress: progressEvent
|
40
|
+
});
|
41
|
+
}
|
42
|
+
});
|
43
|
+
|
44
|
+
xhr.send(formData);
|
45
|
+
|
46
|
+
dispatchEvent($element, 'upload:start', {
|
47
|
+
uploadId: uploadId,
|
48
|
+
file: file
|
49
|
+
});
|
50
|
+
};
|
51
|
+
|
52
|
+
var internalUploadFiles = function($element, files) {
|
53
|
+
for (var i = 0; i < files.length; i++) {
|
54
|
+
uploadFile($element, files[i], ++lastUploadId);
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
window.uploadFiles = internalUploadFiles;
|
59
|
+
})();
|
metadata
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mountable_file_server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Strauß
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: capybara
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: poltergeist
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rack-test
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: bogus
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sinatra
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: mini_magick
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: url_safe_base64
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: ''
|
182
|
+
email:
|
183
|
+
- david@strauss.io
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- ".gitignore"
|
189
|
+
- ".travis.yml"
|
190
|
+
- Gemfile
|
191
|
+
- LICENSE.txt
|
192
|
+
- README.md
|
193
|
+
- Rakefile
|
194
|
+
- lib/mountable_file_server.rb
|
195
|
+
- lib/mountable_file_server/access.rb
|
196
|
+
- lib/mountable_file_server/access_helper.rb
|
197
|
+
- lib/mountable_file_server/ajax_upload_helper.rb
|
198
|
+
- lib/mountable_file_server/configuration.rb
|
199
|
+
- lib/mountable_file_server/endpoint.rb
|
200
|
+
- lib/mountable_file_server/identifier.rb
|
201
|
+
- lib/mountable_file_server/rails.rb
|
202
|
+
- lib/mountable_file_server/storage.rb
|
203
|
+
- lib/mountable_file_server/version.rb
|
204
|
+
- mountable_file_server.gemspec
|
205
|
+
- vendor/assets/javascripts/mountable_file_server.js
|
206
|
+
homepage: https://github.com/stravid/mountable_file_server
|
207
|
+
licenses:
|
208
|
+
- MIT
|
209
|
+
metadata: {}
|
210
|
+
post_install_message:
|
211
|
+
rdoc_options: []
|
212
|
+
require_paths:
|
213
|
+
- lib
|
214
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
215
|
+
requirements:
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '0'
|
219
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
224
|
+
requirements: []
|
225
|
+
rubyforge_project:
|
226
|
+
rubygems_version: 2.4.5
|
227
|
+
signing_key:
|
228
|
+
specification_version: 4
|
229
|
+
summary: Simple mountable server that handles file uploads
|
230
|
+
test_files: []
|