mountable_file_server 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/CHANGELOG.md +8 -0
- data/README.md +84 -40
- data/lib/mountable_file_server/adapter.rb +49 -0
- data/lib/mountable_file_server/configuration.rb +2 -2
- data/lib/mountable_file_server/endpoint.rb +9 -6
- data/lib/mountable_file_server/file_accessor.rb +42 -0
- data/lib/mountable_file_server/storage.rb +19 -37
- data/lib/mountable_file_server/unique_identifier.rb +30 -0
- data/lib/mountable_file_server/uri.rb +33 -0
- data/lib/mountable_file_server/version.rb +1 -1
- data/lib/mountable_file_server.rb +18 -3
- data/mountable_file_server.gemspec +9 -8
- data/vendor/assets/javascripts/mountable_file_server.js +3 -1
- metadata +53 -38
- data/lib/mountable_file_server/access.rb +0 -44
- data/lib/mountable_file_server/access_helper.rb +0 -7
- data/lib/mountable_file_server/ajax_upload_helper.rb +0 -18
- data/lib/mountable_file_server/identifier.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53f16655a84e2cec6640805c435f917ce77d482a
|
4
|
+
data.tar.gz: eaa590e6d805b01e5e488308a5bab98406c17732
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e1aae6f8f4547649c2b53af4f8771b6d15f393895e4a72eff662d7ae585d023efc06065639de25356f12b7da4608972c737259c1cb4dc25aaee0dab406f0a4e
|
7
|
+
data.tar.gz: beb7392313d97415fb62acc49073981dc9682d6eeb828a1b6dd1f8c5e57238fcc1e6263959fb0a4e10065d9d1e5292228167bbef0776545f6c32efb9a3384eb6
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# 0.0.2 - 24.08.2015
|
2
|
+
* Internal refactorings.
|
3
|
+
* Introduce `Adapter` class to have one point of contact.
|
4
|
+
|
5
|
+
**Breaking Changes**
|
6
|
+
* Files are stored under their full unique identifier in every storage.
|
7
|
+
* Remove `Access` class.
|
8
|
+
* Signature of `Storage` methods changed.
|
data/README.md
CHANGED
@@ -1,64 +1,108 @@
|
|
1
|
-
|
1
|
+
MountableFileServer was born out of my frustations with existing Ruby based file upload solutions. It is certainly not comparable with existing feature-rich plug-and-play solutions that are tied to Ruby on Rails.
|
2
2
|
|
3
|
-
|
3
|
+
The fundamental idea is that your application should not be tied to files or handling the upload process. Instead this is handled by a dedicated server which can be run as independet process or be mounted inside your application. It accepts file uploads and returns an unique identifier for the file. This unique identifier is basically a string and the only thing your application has to remember in order to work with uploaded files.
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
* JavaScript function to upload files via AJAX.
|
6
|
+
* JavaScript events regarding the uploads various states.
|
7
|
+
* Uploaded files are stored on disk.
|
8
|
+
* Filenames are unique and random sequences of charaters.
|
9
|
+
* Uploaded files are stored temporary until they are explicitly moved to permanent storage by the application.
|
10
|
+
* Uploaded files can be of public or private nature.
|
11
|
+
* Private files can't be accessed via an URL.
|
7
12
|
|
8
|
-
|
13
|
+
Things it does not do:
|
9
14
|
|
10
|
-
|
15
|
+
* Image processing. Processing images has nothing to do with uploading files. [MountableImageProcessor]() is recommended for adding on the fly image processing to your application.
|
11
16
|
|
12
|
-
##
|
13
|
-
|
17
|
+
## Install & Setup
|
18
|
+
Add the MountableFileServer gem to your Gemfile and `bundle install`. Make sure you include `vendor/assets/javascripts/mountable_file_server.js` in your frontend code.
|
14
19
|
|
15
|
-
## Configuration
|
16
20
|
~~~ruby
|
17
|
-
|
21
|
+
gem 'mountable_file_server', '~> 0.0.2'
|
18
22
|
~~~
|
19
23
|
|
24
|
+
If your application is built upon Ruby on Rails you can add a require statement in your Gemfile. This includes an Ruby on Rails engine which allows you to require the JavaScript.
|
25
|
+
|
20
26
|
~~~ruby
|
21
|
-
|
22
|
-
|
27
|
+
gem 'mountable_file_server', '~> 0.0.2', require: 'mountable_file_server/rails'
|
28
|
+
~~~
|
23
29
|
|
24
|
-
|
25
|
-
|
30
|
+
~~~javascript
|
31
|
+
//= require mountable_file_server
|
32
|
+
~~~
|
33
|
+
|
34
|
+
Once installed it is time to run the server so file uploads can be made. The server is called `MountableFileServer::Endpoint` and is a minimal Sinatra application which can be run as usual. You can also mount an endpoint inside your existing application. With Ruby on Rails this would look like this.
|
26
35
|
|
27
|
-
|
28
|
-
|
36
|
+
~~~ruby
|
37
|
+
Rails.application.routes.draw do
|
38
|
+
mount MountableFileServer::Endpoint, at: MountableFileServer.configuration.mounted_at
|
39
|
+
end
|
29
40
|
~~~
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
## Configuration
|
43
|
+
As seen in the previous section there is a global configuration at `MountableFileServer.configuration` available.
|
44
|
+
This is an instance of the `MountableFileServer::Configration` class. The global configuration is a default argument for all classes that require access to the configuration. In situations where you have multiple endpoints with different settings you can pass in you own configuration objects instead.
|
45
|
+
|
46
|
+
The global configuration can be configured through a block.
|
47
|
+
|
48
|
+
~~~ruby
|
49
|
+
MountableFileServer.configure do |configuration|
|
50
|
+
configuration.mounted_at = '/uploads'
|
51
|
+
configuration.stored_at = '/path/to/desired/uploads/directory'
|
52
|
+
end
|
33
53
|
~~~
|
34
54
|
|
55
|
+
In order to build correct URLs for public files the `mounted_at` attribute should be set to the path where the endpoint is mounted.
|
56
|
+
|
57
|
+
The `stored_at` attribute tells the MountableFileServer where to store uploaded files. It will automatically create this and all needed subdirectories. This directory should not be within the root of your webserver. Otherwise private files are accessible to the internet.
|
58
|
+
|
59
|
+
## Usage
|
60
|
+
The idea is to upload a file with AJAX rather than sending it with the usual form submit. After the upload, an unique identifier (`uid`), is added to the form as a hidden element. Instead of dealing with a file upload your application only has to store the `uid`, a simple string value.
|
61
|
+
|
62
|
+
Using the `MountableFileServer::Adapter` your application can work with the uploaded file, the only thing needed is the `uid`.
|
63
|
+
|
64
|
+
## JavaScript API
|
65
|
+
On the JavaScript side you use `uploadFiles(element, files)` to upload files to the endpoint. The `element` argument is a DOM element that needs two data attributes. `data-endpoint` specifies where the AJAX request should be sent to, this probably matches the `mounted_at` configuration option. `data-type` tells the endpoint if the uploaded files should be treated as public or private files. Possible values are `public` or `private`.
|
66
|
+
|
67
|
+
The `files` argument is an array of `File` objects or an `FileList` object. These objects are for example returned when a user selects files using an `input` element.
|
68
|
+
|
69
|
+
Following events will be dispatched on the element that was passed to `uploadFiles`. When you are listening to one of these events you can access the described attributes on the `event.detail` object.
|
70
|
+
|
71
|
+
`upload:start` is dispatched when the upload starts.
|
72
|
+
It has the attributes `uploadId` and `file`. The `uploadId` is local and can be used to identify events in a scenario where multiple files are uploaded. The `file` attribute is the original `File` object and useful for showing a preview or other information about the file.
|
73
|
+
|
74
|
+
`upload:progress` is continuously dispatched while the upload is happening.
|
75
|
+
It has the attributes `uploadId` and `progress`. The `progress` attribute is the original [ProgressEvent](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent) object of the AJAX request.
|
76
|
+
|
77
|
+
`upload:success` is dispatched when the upload succeeded.
|
78
|
+
It has the attributes `uploadId`, `uid` and `wasLastUpload`. The `uid` attribute is the unique identifier generated by the MountableFileServer. You will want to add it to your form and store it along your other data. The `wasLastUpload` attribute indicates if this was the last upload in progress.
|
79
|
+
|
80
|
+
## Ruby API
|
81
|
+
The `MountableFileServer::Adapter` class allows you to interact with uploaded files. It takes a `MountableFileServer::Configuration` instance as argument and uses `MountableFileServer.configuration` by default.
|
82
|
+
|
83
|
+
`MountableFileServer::Adapter#store_temporary(input, type, extension)`
|
84
|
+
Stores the input as file in the temporary storage and returns the `uid` of the file. `input` can be a path to a file or an [IO](http://ruby-doc.org/core-2.2.2/IO.html) object. `type` can be `public` or `private` and the `extension` argument specifies the extension the file should have.
|
35
85
|
|
86
|
+
`MountableFileServer::Adapter#store_permanent(input, type, extension)`
|
87
|
+
Stores the input as file in the permanent storage and returns the `uid` of the file. `input` can be a path to a file or an [IO](http://ruby-doc.org/core-2.2.2/IO.html) object. `type` can be `public` or `private` and the `extension` argument specifies the extension the file should have.
|
36
88
|
|
37
|
-
|
89
|
+
`MountableFileServer::Adapter#move_to_permanent_storage(uid)`
|
90
|
+
Moves a file from the temporary storage to the permanent one. This is mostly used in a scenario where users upload files. A file uploaded through the endpoint is initially only stored in the temporary storage. The application has to move it explicitly to the permanent storage. For example after all validations passed.
|
38
91
|
|
39
|
-
|
40
|
-
|
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
|
92
|
+
`MountableFileServer::Adapter#remove_from_permanent_storage(uid)`
|
93
|
+
Removes the file from the permanent storage.
|
45
94
|
|
46
|
-
|
47
|
-
|
95
|
+
`MountableFileServer::Adapter#url_for(uid)`
|
96
|
+
Returns the URL for an uploaded file. Only works for public files, if you pass the `uid` of a private file an error will be raised.
|
48
97
|
|
98
|
+
`MountableFileServer::Adapter#pathname_for(id)`
|
99
|
+
Returns a [Pathname](http://ruby-doc.org/stdlib-2.2.2/libdoc/pathname/rdoc/Pathname.html) object for the uploaded file. The pathname will always point to the file on disk independent from the files type or current storage location.
|
49
100
|
|
50
|
-
|
51
|
-
|
101
|
+
## Example
|
102
|
+
TBW
|
52
103
|
|
104
|
+
## Deployment
|
105
|
+
TBW
|
53
106
|
|
54
|
-
|
55
|
-
|
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
|
107
|
+
## License
|
108
|
+
[MIT](https://github.com/stravid/mountable_file_server/blob/master/LICENSE.txt)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module MountableFileServer
|
2
|
+
class Adapter
|
3
|
+
attr_reader :configuration
|
4
|
+
|
5
|
+
def initialize(configuration = MountableFileServer.configuration)
|
6
|
+
@configuration = configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
def store_temporary(input, type, extension)
|
10
|
+
uid = generate_random_uid type, extension
|
11
|
+
Storage.new(configuration).store_temporary uid, input
|
12
|
+
uid
|
13
|
+
end
|
14
|
+
|
15
|
+
def store_permanent(input, type, extension)
|
16
|
+
uid = generate_random_uid type, extension
|
17
|
+
Storage.new(configuration).store_permanent uid, input
|
18
|
+
uid
|
19
|
+
end
|
20
|
+
|
21
|
+
def move_to_permanent_storage(uid)
|
22
|
+
uid = UniqueIdentifier.new uid
|
23
|
+
Storage.new(configuration).move_to_permanent_storage uid
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_from_permanent_storage(uid)
|
27
|
+
uid = UniqueIdentifier.new uid
|
28
|
+
Storage.new(configuration).remove_from_permanent_storage uid
|
29
|
+
end
|
30
|
+
|
31
|
+
def url_for(uid)
|
32
|
+
uid = UniqueIdentifier.new uid
|
33
|
+
FileAccessor.new(uid, configuration).url
|
34
|
+
end
|
35
|
+
|
36
|
+
def pathname_for(uid)
|
37
|
+
uid = UniqueIdentifier.new uid
|
38
|
+
FileAccessor.new(uid, configuration).pathname
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def generate_random_uid(type, extension)
|
43
|
+
loop do
|
44
|
+
uid = UniqueIdentifier.generate_for type, extension
|
45
|
+
break uid unless FileAccessor.new(uid, configuration).exist?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module MountableFileServer
|
2
2
|
class Configuration
|
3
|
-
|
3
|
+
attr_accessor :mounted_at, :stored_at
|
4
4
|
|
5
|
-
def initialize(mounted_at
|
5
|
+
def initialize(mounted_at = '', stored_at = '')
|
6
6
|
@mounted_at = mounted_at
|
7
7
|
@stored_at = stored_at
|
8
8
|
end
|
@@ -1,23 +1,26 @@
|
|
1
|
-
require 'sinatra'
|
1
|
+
require 'sinatra/base'
|
2
2
|
require 'url_safe_base64'
|
3
3
|
require 'mini_magick'
|
4
|
+
require 'pathname'
|
4
5
|
|
5
6
|
module MountableFileServer
|
6
7
|
class Endpoint < Sinatra::Base
|
7
8
|
attr_reader :configuration
|
8
9
|
|
9
|
-
def initialize(configuration)
|
10
|
+
def initialize(configuration = MountableFileServer.configuration)
|
10
11
|
@configuration = configuration
|
11
12
|
super
|
12
13
|
end
|
13
14
|
|
14
15
|
post '/' do
|
15
|
-
|
16
|
-
filename = params[:file][:filename]
|
16
|
+
pathname = Pathname(params[:file][:tempfile].path)
|
17
17
|
type = params[:type]
|
18
|
-
|
18
|
+
adapter = Adapter.new configuration
|
19
|
+
uid = adapter.store_temporary(pathname, type, pathname.extname)
|
19
20
|
|
20
|
-
|
21
|
+
|
22
|
+
content_type :json
|
23
|
+
{ uid: uid }.to_json
|
21
24
|
end
|
22
25
|
|
23
26
|
get '/*' do
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module MountableFileServer
|
4
|
+
MissingFile = Class.new(ArgumentError)
|
5
|
+
NotAccessibleViaURL = Class.new(ArgumentError)
|
6
|
+
|
7
|
+
class FileAccessor
|
8
|
+
attr_reader :uid, :configuration
|
9
|
+
|
10
|
+
def initialize(uid, configuration = MountableFileServer.configuration)
|
11
|
+
@uid = uid
|
12
|
+
@configuration = configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
def temporary_pathname
|
16
|
+
Pathname(configuration.stored_at) + 'tmp' + uid
|
17
|
+
end
|
18
|
+
|
19
|
+
def permanent_pathname
|
20
|
+
Pathname(configuration.stored_at) + uid.type + uid
|
21
|
+
end
|
22
|
+
|
23
|
+
def pathname
|
24
|
+
pathnames.find(-> { raise MissingFile }) { |p| p.file? }
|
25
|
+
end
|
26
|
+
|
27
|
+
def exist?
|
28
|
+
pathnames.any? { |p| p.file? }
|
29
|
+
end
|
30
|
+
|
31
|
+
def url
|
32
|
+
raise NotAccessibleViaURL unless uid.public?
|
33
|
+
|
34
|
+
URI.new (Pathname(configuration.mounted_at) + uid).to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def pathnames
|
39
|
+
[permanent_pathname, temporary_pathname]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,52 +1,34 @@
|
|
1
1
|
module MountableFileServer
|
2
2
|
class Storage
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :configuration
|
4
4
|
|
5
|
-
def initialize(configuration)
|
6
|
-
@
|
5
|
+
def initialize(configuration = MountableFileServer.configuration)
|
6
|
+
@configuration = configuration
|
7
7
|
end
|
8
8
|
|
9
|
-
def store_temporary(
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
9
|
+
def store_temporary(uid, input)
|
10
|
+
destination = FileAccessor.new(uid, configuration).temporary_pathname
|
11
|
+
destination.dirname.mkpath
|
12
|
+
IO.copy_stream input, destination
|
25
13
|
end
|
26
14
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
FileUtils.move from, to
|
15
|
+
def store_permanent(uid, input)
|
16
|
+
destination = FileAccessor.new(uid, configuration).permanent_pathname
|
17
|
+
destination.dirname.mkpath
|
18
|
+
IO.copy_stream input, destination
|
33
19
|
end
|
34
20
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
21
|
+
def move_to_permanent_storage(uid)
|
22
|
+
source = FileAccessor.new(uid, configuration).temporary_pathname
|
23
|
+
destination = FileAccessor.new(uid, configuration).permanent_pathname
|
24
|
+
destination.dirname.mkpath
|
38
25
|
|
39
|
-
|
26
|
+
source.rename destination
|
40
27
|
end
|
41
28
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
29
|
+
def remove_from_permanent_storage(uid)
|
30
|
+
source = FileAccessor.new(uid, configuration).permanent_pathname
|
31
|
+
source.delete
|
50
32
|
end
|
51
33
|
end
|
52
34
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module MountableFileServer
|
4
|
+
UnknownType = Class.new(ArgumentError)
|
5
|
+
|
6
|
+
class UniqueIdentifier < String
|
7
|
+
attr_reader :type, :filename
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
@type, @filename = /(\w+)-(.+)$/.match(string).captures
|
11
|
+
|
12
|
+
raise UnknownType.new(type) unless known_type?
|
13
|
+
|
14
|
+
super.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.generate_for(type, extension)
|
18
|
+
new "#{type}-#{SecureRandom.hex}#{extension}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def public?
|
22
|
+
type == 'public'
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def known_type?
|
27
|
+
['public', 'private'].include?(type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'pathname'
|
3
|
+
require 'url_safe_base64'
|
4
|
+
|
5
|
+
module MountableFileServer
|
6
|
+
class URI < String
|
7
|
+
def initialize(string)
|
8
|
+
super.freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def uid
|
12
|
+
UniqueIdentifier.new filename
|
13
|
+
end
|
14
|
+
|
15
|
+
def encoded_processing_instructions
|
16
|
+
/\.(\w+)\./.match(filename).captures.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def decoded_processing_instructions
|
20
|
+
UrlSafeBase64.decode64 encoded_processing_instructions
|
21
|
+
end
|
22
|
+
|
23
|
+
def filename
|
24
|
+
Pathname(::URI.parse(self).path).basename.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def resize(instructions)
|
28
|
+
encoded_instructions = UrlSafeBase64.encode64 instructions
|
29
|
+
|
30
|
+
URI.new self.gsub('.', ".#{encoded_instructions}.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -2,8 +2,23 @@ require "mountable_file_server/version"
|
|
2
2
|
|
3
3
|
module MountableFileServer
|
4
4
|
require "mountable_file_server/configuration"
|
5
|
+
require "mountable_file_server/file_accessor"
|
6
|
+
require "mountable_file_server/unique_identifier"
|
5
7
|
require "mountable_file_server/storage"
|
8
|
+
require "mountable_file_server/uri"
|
9
|
+
|
10
|
+
require "mountable_file_server/adapter"
|
6
11
|
require "mountable_file_server/endpoint"
|
7
|
-
|
8
|
-
|
9
|
-
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_writer :configuration
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.configuration
|
18
|
+
@configuration ||= Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configure
|
22
|
+
yield(configuration)
|
23
|
+
end
|
24
|
+
end
|
@@ -20,15 +20,16 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
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"
|
23
|
+
spec.add_development_dependency "minitest", "~> 5.8.0"
|
24
|
+
spec.add_development_dependency "rails", "~> 4.2.3"
|
25
|
+
spec.add_development_dependency "capybara", "~> 2.4.4"
|
26
|
+
spec.add_development_dependency "sqlite3", "~> 1.3.10"
|
27
|
+
spec.add_development_dependency "poltergeist", "~> 1.6.0"
|
28
|
+
spec.add_development_dependency "rack-test", "~> 0.6.3"
|
29
|
+
spec.add_development_dependency "bogus", "~> 0.1.6"
|
30
30
|
|
31
|
-
spec.add_runtime_dependency "
|
31
|
+
spec.add_runtime_dependency "rack", "~> 1.6"
|
32
|
+
spec.add_runtime_dependency "sinatra", "~> 1.4.5"
|
32
33
|
spec.add_runtime_dependency "mini_magick"
|
33
34
|
spec.add_runtime_dependency "url_safe_base64"
|
34
35
|
end
|
@@ -24,9 +24,11 @@
|
|
24
24
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
25
25
|
finishedUploads.push(uploadId);
|
26
26
|
|
27
|
+
response = JSON.parse(xhr.responseText);
|
28
|
+
|
27
29
|
dispatchEvent($element, 'upload:success', {
|
28
30
|
uploadId: uploadId,
|
29
|
-
|
31
|
+
uid: response.uid,
|
30
32
|
wasLastUpload: lastUploadId == finishedUploads.length
|
31
33
|
});
|
32
34
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mountable_file_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Strauß
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -42,114 +42,128 @@ dependencies:
|
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 5.8.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 5.8.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rails
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 4.2.3
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 4.2.3
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: capybara
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 2.4.4
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 2.4.4
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: sqlite3
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 1.3.10
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 1.3.10
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: poltergeist
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 1.6.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.6.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rack-test
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 0.6.3
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: 0.6.3
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: bogus
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 0.1.6
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 0.1.6
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rack
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.6'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.6'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: sinatra
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
|
-
- - "
|
157
|
+
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
159
|
+
version: 1.4.5
|
146
160
|
type: :runtime
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
|
-
- - "
|
164
|
+
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
166
|
+
version: 1.4.5
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: mini_magick
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,19 +201,20 @@ extra_rdoc_files: []
|
|
187
201
|
files:
|
188
202
|
- ".gitignore"
|
189
203
|
- ".travis.yml"
|
204
|
+
- CHANGELOG.md
|
190
205
|
- Gemfile
|
191
206
|
- LICENSE.txt
|
192
207
|
- README.md
|
193
208
|
- Rakefile
|
194
209
|
- lib/mountable_file_server.rb
|
195
|
-
- lib/mountable_file_server/
|
196
|
-
- lib/mountable_file_server/access_helper.rb
|
197
|
-
- lib/mountable_file_server/ajax_upload_helper.rb
|
210
|
+
- lib/mountable_file_server/adapter.rb
|
198
211
|
- lib/mountable_file_server/configuration.rb
|
199
212
|
- lib/mountable_file_server/endpoint.rb
|
200
|
-
- lib/mountable_file_server/
|
213
|
+
- lib/mountable_file_server/file_accessor.rb
|
201
214
|
- lib/mountable_file_server/rails.rb
|
202
215
|
- lib/mountable_file_server/storage.rb
|
216
|
+
- lib/mountable_file_server/unique_identifier.rb
|
217
|
+
- lib/mountable_file_server/uri.rb
|
203
218
|
- lib/mountable_file_server/version.rb
|
204
219
|
- mountable_file_server.gemspec
|
205
220
|
- vendor/assets/javascripts/mountable_file_server.js
|
@@ -1,44 +0,0 @@
|
|
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
|
@@ -1,18 +0,0 @@
|
|
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
|
@@ -1,37 +0,0 @@
|
|
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
|