refile 0.2.2 → 0.2.3
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/History.md +9 -0
- data/README.md +9 -1
- data/app/assets/javascripts/refile.js +52 -41
- data/app/helpers/attachment_helper.rb +6 -3
- data/lib/refile/app.rb +36 -35
- data/lib/refile/rails.rb +5 -3
- data/lib/refile/version.rb +1 -1
- data/spec/refile/app_spec.rb +9 -17
- data/spec/refile/features/normal_upload_spec.rb +11 -0
- data/spec/refile/spec_helper.rb +26 -0
- data/spec/refile/test_app.rb +10 -0
- data/spec/refile/test_app/app/assets/javascripts/application.js +37 -35
- data/spec/refile/test_app/app/views/normal_posts/show.html.erb +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cbf36645ba19dcbaad5d4b56f88a1aa647515fb
|
4
|
+
data.tar.gz: a422fc72ee72b28c0953251d9109902ad7d4f1ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bb6048ad95f7b66f847d46c6228ab82c3eb1a59006e7467627eae34d4243eb2ed6851a189eb3828ad8aa4fdcfc88bb0a9712d76d396ed7b40a20393a80f5097
|
7
|
+
data.tar.gz: 1a61ecebcb311bc6b3a7dd6b5d323195f7df557f40741cf70c8ba00dfc7f6f94de28b582e49835aeb98d633ecc59c6d5ded55a305062d6a98d73076ffd3fcba2
|
data/History.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# 0.2.3
|
2
|
+
|
3
|
+
Release date: 2014-12-08
|
4
|
+
|
5
|
+
- [ADDED] Support for passing format to processors
|
6
|
+
- [FIXED] Support IE10
|
7
|
+
- [FIXED] Gracefully degrade on IE9, IE8 and IE7
|
8
|
+
- [FIXED] Success event is fired at the appropriate time
|
9
|
+
- [FIXED] Works with apps which don't define root_url
|
data/README.md
CHANGED
@@ -124,6 +124,11 @@ Refile.cache = Refile::Backend::S3.new(prefix: "cache", **aws)
|
|
124
124
|
Refile.store = Refile::Backend::S3.new(prefix: "store", **aws)
|
125
125
|
```
|
126
126
|
|
127
|
+
And add to your Gemfile:
|
128
|
+
```ruby
|
129
|
+
gem "aws-sdk"
|
130
|
+
```
|
131
|
+
|
127
132
|
Try this in the quick start example above and your files are now uploaded to
|
128
133
|
S3.
|
129
134
|
|
@@ -401,7 +406,9 @@ cross site AJAX requests from posting to buckets. Fixing this is easy though.
|
|
401
406
|
- Open the "Permission" section
|
402
407
|
- Click "Add CORS Configuration"
|
403
408
|
|
404
|
-
The default configuration only allows "GET", you'll want to allow "POST" as
|
409
|
+
The default configuration only allows "GET", you'll want to allow "POST" as
|
410
|
+
well. You'll also want to permit the "Content-Type" header.
|
411
|
+
|
405
412
|
It could look something like this:
|
406
413
|
|
407
414
|
``` xml
|
@@ -412,6 +419,7 @@ It could look something like this:
|
|
412
419
|
<AllowedMethod>POST</AllowedMethod>
|
413
420
|
<MaxAgeSeconds>3000</MaxAgeSeconds>
|
414
421
|
<AllowedHeader>Authorization</AllowedHeader>
|
422
|
+
<AllowedHeader>Content-Type</AllowedHeader>
|
415
423
|
</CORSRule>
|
416
424
|
</CORSConfiguration>
|
417
425
|
```
|
@@ -1,50 +1,61 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
document.addEventListener("change", function(e) {
|
4
|
-
if(e.target.tagName === "INPUT" && e.target.type === "file" && e.target.dataset.direct) {
|
5
|
-
var input = e.target;
|
6
|
-
var file = input.files[0];
|
7
|
-
if(file) {
|
8
|
-
var url = e.target.dataset.url;
|
9
|
-
if(e.target.dataset.fields) {
|
10
|
-
var fields = JSON.parse(e.target.dataset.fields);
|
11
|
-
}
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
12
3
|
|
13
|
-
|
4
|
+
if(!document.addEventListener) { return }; // IE8
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
document.addEventListener("change", function(e) {
|
7
|
+
if(e.target.tagName === "INPUT" && e.target.type === "file" && e.target.getAttribute("data-direct")) {
|
8
|
+
var input = e.target;
|
9
|
+
if(!input.files) { return; } // IE9, bail out if file API is not supported.
|
10
|
+
var file = input.files[0];
|
11
|
+
|
12
|
+
var dispatchEvent = function(name, detail) {
|
13
|
+
var ev = document.createEvent('CustomEvent');
|
14
|
+
ev.initCustomEvent(name, true, false, detail);
|
15
|
+
input.dispatchEvent(ev);
|
19
16
|
}
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
input.dispatchEvent(new CustomEvent("upload:complete", { detail: xhr.responseText, bubbles: true }));
|
26
|
-
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
27
|
-
var id = input.dataset.id || JSON.parse(xhr.responseText).id;
|
28
|
-
input.dispatchEvent(new CustomEvent("upload:success", { detail: xhr.responseText, bubbles: true }));
|
29
|
-
input.previousSibling.value = id;
|
30
|
-
input.removeAttribute("name");
|
31
|
-
} else {
|
32
|
-
input.dispatchEvent(new CustomEvent("upload:failure", { detail: xhr.responseText, bubbles: true }));
|
17
|
+
|
18
|
+
if(file) {
|
19
|
+
var url = e.target.getAttribute("data-url");
|
20
|
+
if(e.target.getAttribute("data-fields")) {
|
21
|
+
var fields = JSON.parse(e.target.getAttribute("data-fields"));
|
33
22
|
}
|
34
|
-
});
|
35
23
|
|
36
|
-
|
37
|
-
|
38
|
-
|
24
|
+
var data = new FormData();
|
25
|
+
|
26
|
+
if(fields) {
|
27
|
+
Object.keys(fields).forEach(function(key) {
|
28
|
+
data.append(key, fields[key]);
|
29
|
+
});
|
39
30
|
}
|
40
|
-
|
31
|
+
data.append(input.getAttribute("data-as"), file);
|
32
|
+
|
33
|
+
var xhr = new XMLHttpRequest();
|
34
|
+
xhr.addEventListener("load", function(e) {
|
35
|
+
input.classList.remove("uploading")
|
36
|
+
dispatchEvent("upload:complete", xhr.responseText);
|
37
|
+
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
38
|
+
var id = input.getAttribute("data-id") || JSON.parse(xhr.responseText).id;
|
39
|
+
input.previousSibling.value = id;
|
40
|
+
input.removeAttribute("name");
|
41
|
+
dispatchEvent("upload:success", xhr.responseText);
|
42
|
+
} else {
|
43
|
+
dispatchEvent("upload:failure", xhr.responseText);
|
44
|
+
}
|
45
|
+
});
|
41
46
|
|
42
|
-
|
43
|
-
|
47
|
+
xhr.upload.addEventListener("progress", function(e) {
|
48
|
+
if (e.lengthComputable) {
|
49
|
+
dispatchEvent("upload:progress", e);
|
50
|
+
}
|
51
|
+
});
|
44
52
|
|
45
|
-
|
46
|
-
|
47
|
-
}
|
48
|
-
}
|
49
|
-
});
|
53
|
+
xhr.open("POST", url, true);
|
54
|
+
xhr.send(data);
|
50
55
|
|
56
|
+
input.classList.add("uploading")
|
57
|
+
dispatchEvent("upload:start");
|
58
|
+
}
|
59
|
+
}
|
60
|
+
});
|
61
|
+
})();
|
@@ -5,9 +5,12 @@ module AttachmentHelper
|
|
5
5
|
filename ||= name.to_s
|
6
6
|
|
7
7
|
backend_name = Refile.backends.key(file.backend)
|
8
|
-
host = Refile.host ||
|
8
|
+
host = Refile.host || request.base_url
|
9
9
|
|
10
|
-
|
10
|
+
filename = filename.parameterize("_")
|
11
|
+
filename << "." << format.to_s if format
|
12
|
+
|
13
|
+
File.join(host, refile_app_path, backend_name, *args.map(&:to_s), file.id, filename)
|
11
14
|
end
|
12
15
|
|
13
16
|
def attachment_image_tag(record, name, *args, fallback: nil, format: nil, **options)
|
@@ -27,7 +30,7 @@ module AttachmentHelper
|
|
27
30
|
cache = options[:object].send(:"#{method}_attachment").cache
|
28
31
|
|
29
32
|
if options[:direct]
|
30
|
-
host = Refile.host ||
|
33
|
+
host = Refile.host || request.base_url
|
31
34
|
backend_name = Refile.backends.key(cache)
|
32
35
|
|
33
36
|
options[:data] ||= {}
|
data/lib/refile/app.rb
CHANGED
@@ -27,50 +27,51 @@ module Refile
|
|
27
27
|
|
28
28
|
def call(env)
|
29
29
|
@logger.info { "Refile: #{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}" }
|
30
|
-
if env["REQUEST_METHOD"] == "GET"
|
31
|
-
backend_name, *process_args, id, filename = env["PATH_INFO"].sub(/^\//, "").split("/")
|
32
|
-
backend = Refile.backends[backend_name]
|
33
|
-
|
34
|
-
if backend and id
|
35
|
-
@logger.debug { "Refile: serving #{id.inspect} from #{backend_name} backend which is of type #{backend.class}" }
|
36
|
-
|
37
|
-
file = backend.get(id)
|
38
|
-
|
39
|
-
unless process_args.empty?
|
40
|
-
name = process_args.shift
|
41
|
-
unless Refile.processors[name]
|
42
|
-
@logger.debug { "Refile: no such processor #{name.inspect}" }
|
43
|
-
return not_found
|
44
|
-
end
|
45
|
-
file = Refile.processors[name].call(file, *process_args)
|
46
|
-
end
|
47
30
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
31
|
+
backend_name, *args = env["PATH_INFO"].sub(/^\//, "").split("/")
|
32
|
+
backend = Refile.backends[backend_name]
|
33
|
+
|
34
|
+
if env["REQUEST_METHOD"] == "GET" and backend and args.length >= 2
|
35
|
+
*process_args, id, filename = args
|
36
|
+
format = ::File.extname(filename)[1..-1]
|
37
|
+
|
38
|
+
@logger.debug { "Refile: serving #{id.inspect} from #{backend_name} backend which is of type #{backend.class}" }
|
39
|
+
|
40
|
+
file = backend.get(id)
|
41
|
+
|
42
|
+
unless process_args.empty?
|
43
|
+
name = process_args.shift
|
44
|
+
processor = Refile.processors[name]
|
45
|
+
unless processor
|
46
|
+
@logger.debug { "Refile: no such processor #{name.inspect}" }
|
52
47
|
return not_found
|
53
48
|
end
|
49
|
+
file = if format
|
50
|
+
processor.call(file, *process_args, format: format)
|
51
|
+
else
|
52
|
+
processor.call(file, *process_args)
|
53
|
+
end
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@logger.debug { "Refile: must specify backend and id" }
|
61
|
-
not_found
|
56
|
+
peek = begin
|
57
|
+
file.read(Refile.read_chunk_size)
|
58
|
+
rescue => e
|
59
|
+
log_error(e)
|
60
|
+
return not_found
|
62
61
|
end
|
63
|
-
elsif env["REQUEST_METHOD"] == "POST"
|
64
|
-
backend_name, *rest = env["PATH_INFO"].sub(/^\//, "").split("/")
|
65
|
-
backend = Refile.backends[backend_name]
|
66
62
|
|
67
|
-
|
68
|
-
|
63
|
+
headers = {}
|
64
|
+
headers["Access-Control-Allow-Origin"] = @allow_origin if @allow_origin
|
65
|
+
|
66
|
+
[200, headers, Proxy.new(peek, file)]
|
67
|
+
elsif env["REQUEST_METHOD"] == "POST" and backend and args.empty? and Refile.direct_upload.include?(backend_name)
|
68
|
+
@logger.debug { "Refile: uploading to #{backend_name} backend which is of type #{backend.class}" }
|
69
|
+
|
70
|
+
tempfile = Rack::Request.new(env).params.fetch("file").fetch(:tempfile)
|
71
|
+
file = backend.upload(tempfile)
|
69
72
|
|
70
|
-
file = backend.upload(Rack::Request.new(env).params.fetch("file").fetch(:tempfile))
|
71
73
|
[200, { "Content-Type" => "application/json" }, [{ id: file.id }.to_json]]
|
72
74
|
else
|
73
|
-
@logger.debug { "Refile: request methods other than GET and POST are not allowed" }
|
74
75
|
not_found
|
75
76
|
end
|
76
77
|
rescue => e
|
data/lib/refile/rails.rb
CHANGED
@@ -20,17 +20,19 @@ module Refile
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class Engine < Rails::Engine
|
23
|
-
initializer "refile", before: :load_environment_config do
|
23
|
+
initializer "refile.setup", before: :load_environment_config do
|
24
24
|
Refile.store ||= Refile::Backend::FileSystem.new(Rails.root.join("tmp/uploads/store").to_s)
|
25
25
|
Refile.cache ||= Refile::Backend::FileSystem.new(Rails.root.join("tmp/uploads/cache").to_s)
|
26
26
|
|
27
|
-
Refile.app = Refile::App.new(logger: Rails.logger)
|
28
|
-
|
29
27
|
ActiveSupport.on_load :active_record do
|
30
28
|
require "refile/attachment/active_record"
|
31
29
|
end
|
32
30
|
|
33
31
|
ActionView::Helpers::FormBuilder.send(:include, AttachmentFieldHelper)
|
34
32
|
end
|
33
|
+
|
34
|
+
initializer "refile.app" do
|
35
|
+
Refile.app = Refile::App.new(logger: Rails.logger)
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
data/lib/refile/version.rb
CHANGED
data/spec/refile/app_spec.rb
CHANGED
@@ -1,22 +1,5 @@
|
|
1
1
|
require "rack/test"
|
2
2
|
|
3
|
-
Refile.processor(:reverse) do |file|
|
4
|
-
StringIO.new(file.read.reverse)
|
5
|
-
end
|
6
|
-
|
7
|
-
Refile.processor(:upcase, proc { |file| StringIO.new(file.read.upcase) })
|
8
|
-
|
9
|
-
Refile.processor(:concat) do |file, *words|
|
10
|
-
content = File.read(file.download.path)
|
11
|
-
tempfile = Tempfile.new("concat")
|
12
|
-
tempfile.write(content)
|
13
|
-
words.each do |word|
|
14
|
-
tempfile.write(word)
|
15
|
-
end
|
16
|
-
tempfile.close
|
17
|
-
File.open(tempfile.path, "r")
|
18
|
-
end
|
19
|
-
|
20
3
|
describe Refile::App do
|
21
4
|
include Rack::Test::Methods
|
22
5
|
|
@@ -114,6 +97,15 @@ describe Refile::App do
|
|
114
97
|
expect(last_response.status).to eq(200)
|
115
98
|
expect(last_response.body).to eq("hellofoobarbaz")
|
116
99
|
end
|
100
|
+
|
101
|
+
it "applies processor with format" do
|
102
|
+
file = Refile.store.upload(StringIO.new("hello"))
|
103
|
+
|
104
|
+
get "/store/convert_case/#{file.id}/hello.up"
|
105
|
+
|
106
|
+
expect(last_response.status).to eq(200)
|
107
|
+
expect(last_response.body).to eq("HELLO")
|
108
|
+
end
|
117
109
|
end
|
118
110
|
|
119
111
|
describe "POST /:backend" do
|
@@ -33,4 +33,15 @@ feature "Normal HTTP Post file uploads" do
|
|
33
33
|
click_link("Document")
|
34
34
|
expect(page.source.chomp).to eq("hello")
|
35
35
|
end
|
36
|
+
|
37
|
+
scenario "Format conversion" do
|
38
|
+
visit "/normal/posts/new"
|
39
|
+
fill_in "Title", with: "A cool post"
|
40
|
+
attach_file "Document", path("hello.txt")
|
41
|
+
click_button "Create"
|
42
|
+
|
43
|
+
expect(page).to have_selector("h1", text: "A cool post")
|
44
|
+
click_link("Convert to Upper")
|
45
|
+
expect(page.source.chomp).to eq("HELLO")
|
46
|
+
end
|
36
47
|
end
|
data/spec/refile/spec_helper.rb
CHANGED
@@ -24,6 +24,32 @@ Refile.backends["limited_cache"] = FakePresignBackend.new(File.expand_path("defa
|
|
24
24
|
|
25
25
|
Refile.direct_upload = ["cache", "limited_cache"]
|
26
26
|
|
27
|
+
Refile.processor(:reverse) do |file|
|
28
|
+
StringIO.new(file.read.reverse)
|
29
|
+
end
|
30
|
+
|
31
|
+
Refile.processor(:upcase, proc { |file| StringIO.new(file.read.upcase) })
|
32
|
+
|
33
|
+
Refile.processor(:concat) do |file, *words|
|
34
|
+
content = File.read(file.download.path)
|
35
|
+
tempfile = Tempfile.new("concat")
|
36
|
+
tempfile.write(content)
|
37
|
+
words.each do |word|
|
38
|
+
tempfile.write(word)
|
39
|
+
end
|
40
|
+
tempfile.close
|
41
|
+
File.open(tempfile.path, "r")
|
42
|
+
end
|
43
|
+
|
44
|
+
Refile.processor(:convert_case) do |file, format:|
|
45
|
+
case format
|
46
|
+
when "up" then StringIO.new(file.read.upcase)
|
47
|
+
when "down" then StringIO.new(file.read.downcase)
|
48
|
+
else file
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
27
53
|
class Refile::FileDouble
|
28
54
|
def initialize(data)
|
29
55
|
@io = StringIO.new(data)
|
data/spec/refile/test_app.rb
CHANGED
@@ -39,6 +39,16 @@ require "capybara/rails"
|
|
39
39
|
require "capybara/rspec"
|
40
40
|
require "refile/spec_helper"
|
41
41
|
|
42
|
+
if ENV["SAUCE_BROWSER"]
|
43
|
+
Capybara.register_driver :selenium do |app|
|
44
|
+
url = "http://#{ENV["SAUCE_USERNAME"]}:#{ENV["SAUCE_ACCESS_KEY"]}@localhost:4445/wd/hub"
|
45
|
+
capabilities = { browserName: ENV["SAUCE_BROWSER"], version: ENV["SAUCE_VERSION"] }
|
46
|
+
driver = Capybara::Selenium::Driver.new(app, browser: :remote, url: url, desired_capabilities: capabilities)
|
47
|
+
driver.browser.file_detector = lambda { |args| args.first if File.exist?(args.first) }
|
48
|
+
driver
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
42
52
|
Capybara.configure do |config|
|
43
53
|
config.server_port = 56120
|
44
54
|
end
|
@@ -3,38 +3,40 @@
|
|
3
3
|
|
4
4
|
"use strict";
|
5
5
|
|
6
|
-
document.addEventListener
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
}
|
37
|
-
|
38
|
-
|
39
|
-
$(
|
40
|
-
|
6
|
+
if(document.addEventListener) {
|
7
|
+
document.addEventListener("DOMContentLoaded", function() {
|
8
|
+
var form = document.querySelector("form#direct");
|
9
|
+
|
10
|
+
if(form) {
|
11
|
+
var input = document.querySelector("#post_document");
|
12
|
+
|
13
|
+
form.addEventListener("upload:start", function() {
|
14
|
+
var p = document.createElement("p");
|
15
|
+
p.textContent = "Upload started";
|
16
|
+
form.appendChild(p);
|
17
|
+
});
|
18
|
+
|
19
|
+
form.addEventListener("upload:complete", function(e) {
|
20
|
+
var p = document.createElement("p");
|
21
|
+
p.textContent = "Upload complete " + e.detail;
|
22
|
+
form.appendChild(p);
|
23
|
+
});
|
24
|
+
|
25
|
+
form.addEventListener("upload:progress", function(e) {
|
26
|
+
var p = document.createElement("p");
|
27
|
+
p.textContent = "Upload progress " + e.detail.loaded + " " + e.detail.total;
|
28
|
+
form.appendChild(p);
|
29
|
+
});
|
30
|
+
|
31
|
+
form.addEventListener("upload:failure", function(e) {
|
32
|
+
var p = document.createElement("p");
|
33
|
+
p.textContent = "Upload failure " + e.detail
|
34
|
+
form.appendChild(p);
|
35
|
+
});
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
$(document).on("upload:success", "form#direct", function(e) {
|
40
|
+
$("<p></p>").text("Upload success " + e.originalEvent.detail).appendTo(this);
|
41
|
+
});
|
42
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Nicklas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -189,6 +189,7 @@ files:
|
|
189
189
|
- ".rspec"
|
190
190
|
- ".travis.yml"
|
191
191
|
- Gemfile
|
192
|
+
- History.md
|
192
193
|
- LICENSE.txt
|
193
194
|
- README.md
|
194
195
|
- Rakefile
|