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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc6b91b6cf9ef96d1231d0f403e7f468fd75d1f4
4
- data.tar.gz: 9b00ac82aff5a58988d8154d91ccc8e465684d55
3
+ metadata.gz: 53f16655a84e2cec6640805c435f917ce77d482a
4
+ data.tar.gz: eaa590e6d805b01e5e488308a5bab98406c17732
5
5
  SHA512:
6
- metadata.gz: f2a1ecd0b539c510c677273bf44fac23df744c0c48ee225c13ea8dd964e2a03bfa1f9f6fb1df61ba0857b6cbafd3a725ce5cfae96d8217d17baae991b3d71789
7
- data.tar.gz: a3dbbe1fbcba59f7e70963827c17f1b506e151bc1f8c45a9300939c42aef5a5b457d8d8e15e10478e01498a00a64f6f1a400d47e0f0b5a81f77433e2a2e1c91d
6
+ metadata.gz: 2e1aae6f8f4547649c2b53af4f8771b6d15f393895e4a72eff662d7ae585d023efc06065639de25356f12b7da4608972c737259c1cb4dc25aaee0dab406f0a4e
7
+ data.tar.gz: beb7392313d97415fb62acc49073981dc9682d6eeb828a1b6dd1f8c5e57238fcc1e6263959fb0a4e10065d9d1e5292228167bbef0776545f6c32efb9a3384eb6
data/.travis.yml CHANGED
@@ -1,3 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.2.3
4
+ - 2.1.7
3
5
  - 2.0.0
6
+ - 1.9.3
7
+ before_script:
8
+ - cd test/rails-dummy
9
+ - bundle exec rake db:create db:migrate
10
+ - cd ../../
11
+ script:
12
+ - bundle exec rake test
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
- 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.
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
- 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.
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
- ## 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.)
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
- 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.
13
+ Things it does not do:
9
14
 
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.
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
- ## 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.
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
- configuration = MountableFileServer::Configuration.new stored_at: '~/uploads', mounted_at: '/uploads'
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
- storage = MountableFileServer::Storage.new config
22
- upload = MountableFileServer::Upload.new file: file_parameters[:tempfile].read, type: 'public'
27
+ gem 'mountable_file_server', '~> 0.0.2', require: 'mountable_file_server/rails'
28
+ ~~~
23
29
 
24
- identifier = storage.store_temporary upload: upload
25
- storage.move_to_permanent_storage identifier: identifier
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
- storage.path_for identifier: identifier
28
- storage.url_for identifier: identifier
36
+ ~~~ruby
37
+ Rails.application.routes.draw do
38
+ mount MountableFileServer::Endpoint, at: MountableFileServer.configuration.mounted_at
39
+ end
29
40
  ~~~
30
41
 
31
- ~~~html
32
- <input type="file" name="avatar" data-endpoint="/uploads" data-type="public">
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
- Wieso ist MFS besser?
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
- 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
92
+ `MountableFileServer::Adapter#remove_from_permanent_storage(uid)`
93
+ Removes the file from the permanent storage.
45
94
 
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.
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
- Open Fragen:
51
- Upload nur auf Gruppe X einschränken
101
+ ## Example
102
+ TBW
52
103
 
104
+ ## Deployment
105
+ TBW
53
106
 
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
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
- attr_reader :mounted_at, :stored_at
3
+ attr_accessor :mounted_at, :stored_at
4
4
 
5
- def initialize(mounted_at:, stored_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
- path = params[:file][:tempfile].path
16
- filename = params[:file][:filename]
16
+ pathname = Pathname(params[:file][:tempfile].path)
17
17
  type = params[:type]
18
- storage = Storage.new configuration
18
+ adapter = Adapter.new configuration
19
+ uid = adapter.store_temporary(pathname, type, pathname.extname)
19
20
 
20
- storage.store_temporary(path: path, type: type, filename: filename).to_str
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 :access
3
+ attr_reader :configuration
4
4
 
5
- def initialize(configuration)
6
- @access = Access.new configuration
5
+ def initialize(configuration = MountableFileServer.configuration)
6
+ @configuration = configuration
7
7
  end
8
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
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 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
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 remove_from_permanent_storage(identifier:)
36
- identifier = Identifier.new identifier
37
- path = access.path_for identifier: identifier
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
- FileUtils.rm path
26
+ source.rename destination
40
27
  end
41
28
 
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
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
@@ -1,3 +1,3 @@
1
1
  module MountableFileServer
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  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
- require "mountable_file_server/identifier"
8
- require "mountable_file_server/access"
9
- end
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 "sinatra"
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
- identifier: xhr.responseText,
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.1
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 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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/access.rb
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/identifier.rb
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,7 +0,0 @@
1
- # module MountableFileServer
2
- # module AccessHelper
3
- # def path_for_upload(filename)
4
- # File.join(MountableFileServer.configuration.mounted_at, filename)
5
- # end
6
- # end
7
- # 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