s3ff 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aa68843504870dde78c2146cbd742ec26a9c5826
4
+ data.tar.gz: a90a1ceb009cc4488aef06886bdac71806667a62
5
+ SHA512:
6
+ metadata.gz: dca77539e239ef3ca2c666861f75a9adac27945d461615f6bb7132bffe77aa8faf5a0ceb7c60e565edb127b9a41fced54580e97d65dc13231194a6ee6741070b
7
+ data.tar.gz: 60e7ed6509d3a9ef284c754df7380cf7b4b3f6955435790bff7c78b5fbd1cb2558dfc551d3823cef0507208cc163005aaf55af3d7e2dbc32cdfb1330563f8e1e
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # S3FF
2
+
3
+ Using `s3_file_field` with `paperclip`
4
+
5
+ ## Install
6
+
7
+ ### 1. Add javascript
8
+
9
+ in your application.js
10
+
11
+ ```
12
+ //= require s3ff
13
+ ```
14
+
15
+ ### 2. Change `file_field` input to use `s3_file_field`
16
+
17
+ ```
18
+ = form_for :user do |f|
19
+ = f.s3_file_field :avatar
20
+ ```
21
+
22
+ or if you're using `simple_form`
23
+
24
+ ```
25
+ = simple_form_for :user do |f|
26
+ = f.input :avatar do
27
+ = f.s3_file_field :avatar, :class => "form-control"
28
+ ```
29
+
30
+ ### 3. Add footer
31
+
32
+ ```
33
+ = include_s3ff_templates
34
+ ```
35
+
36
+ NOTE: Feel free to modify & render the templates manually, but keep the `s3ff_` prefixed CSS classes for our javascript to work properly.
37
+
38
+ ## What will happen
39
+
40
+ To illustate, if you have a file field like this
41
+
42
+ ```
43
+ <input type="file" name="user[avatar]">
44
+ ```
45
+
46
+ When `s3ff` kicks in, it would upgrade the field to a `s3_file_field`. When your user chooses a file, it will be uploaded, with a progress indicator, directly into your S3 bucket (see `s3_file_field` gem for configuration). Your `form` will be disabled during the upload and re-enabled once upload completes. After this process, 4 new hidden form fields will be attached to your form:
47
+
48
+ ```
49
+ <input type="file" name="user[avatar_direct_url]" value="https://....">
50
+ <input type="file" name="user[avatar_file_name]" value="face.png">
51
+ <input type="file" name="user[avatar_file_size]" value="162534">
52
+ <input type="file" name="user[avatar_content_type]" value="image/png">
53
+ ```
54
+
55
+ ## Code changes to your app
56
+
57
+ `s3ff` designed to minimize moving parts and code changes to your Rails app - all it does is give you 4 form fields in return for every direct s3 file upload that happened in your user's browser.
58
+
59
+ How you deal with these form fields are entirely up to you. Here's a simple way:
60
+
61
+ #### 1. Edit strong parameters
62
+
63
+ If your controller was specifying
64
+
65
+ ```
66
+ params.require(:user).permit(:avatar)
67
+ ```
68
+
69
+ It would need to be changed to accept the new form fields
70
+
71
+ ```
72
+ params.require(:user).permit(:avatar,
73
+ :avatar_direct_url,
74
+ :avatar_file_name,
75
+ :avatar_file_size,
76
+ :avatar_content_type
77
+ )
78
+ ```
79
+
80
+ #### 2. Upgrade model to also accept direct url
81
+
82
+ If your model was originally
83
+
84
+ ```
85
+ class User < ActiveRecord::Base
86
+ has_attached_file :avatar
87
+ end
88
+ ```
89
+
90
+ Download the file from S3 when given `avatar_direct_url`. This leave all your existing Paperclip code and logic unchanged.
91
+
92
+ ```
93
+ class User < ActiveRecord::Base
94
+ has_attached_file :avatar
95
+
96
+ attr_accessor :avatar_direct_url
97
+ def avatar_direct_url=(value)
98
+ self.avatar = open(value) if value.present?
99
+ end
100
+ end
101
+ ```
102
+
103
+
104
+
105
+ ### License
106
+
107
+ This repository is MIT-licensed.
@@ -0,0 +1,76 @@
1
+ //= require s3_file_field
2
+ //= require jsrender.min
3
+
4
+ $(function() {
5
+
6
+ // adapted from http://www.html5rocks.com/en/tutorials/cors/
7
+ function iCanHasCORSRequest() {
8
+ var xhr = new XMLHttpRequest();
9
+ return (("withCredentials" in xhr) || (typeof XDomainRequest != "undefined"));
10
+ }
11
+ if (! iCanHasCORSRequest()) return;
12
+
13
+ var ready = function() {
14
+ var s3ff_init = function () {
15
+ if ($(this).hasClass('s3ff_enabled')) return;
16
+
17
+ var that = $(this).addClass('s3ff_enabled');
18
+ var multi = that.attr('multiple');
19
+ var label = that.wrap($.templates($('#s3ff_label').html()).render()).parents('.s3ff_label').attr('for', that.attr('id'));
20
+ var wrap = that.wrap('<div></div>').parent();
21
+ var section = function(file, selector) {
22
+ var upload = label.find("#upload-" + file.unique_id);
23
+ return (selector ? upload.find(selector) : upload);
24
+ };
25
+
26
+ var s3ff_handlers = {
27
+ change: function(e, data) {
28
+ if (! multi) label.children('.s3ff_section').remove();
29
+ },
30
+ always: function(e, data) {
31
+ wrap.parents("form").find("[type='submit']").each(function() {
32
+ this.uploading = this.uploading || 0;
33
+ $(this).attr({'disabled': (--this.uploading > 0)});
34
+ });
35
+ },
36
+ add: function(e, data) {
37
+ wrap.parents("form").find("[type='submit']").each(function() {
38
+ this.uploading = this.uploading || 0;
39
+ $(this).attr({'disabled': (++this.uploading > 0)});
40
+ });
41
+ data.submit();
42
+ },
43
+ send: function(e, data) {
44
+ label.append($.templates($('#s3ff_upload').html()).render(data.files[0]));
45
+ },
46
+ done: function(e, data) {
47
+ var upload = section(data.files[0]);
48
+ var field_prefix = that.attr('name').replace(/\]$/, '');
49
+ upload.append($.templates($('#s3ff_done').html()).render(data, { field_prefix: field_prefix }));
50
+ upload.find('.s3ff_progress').hide();
51
+ },
52
+ fail: function(e, data) {
53
+ section(data.files[0], '.s3ff_progress').html($.templates($('#s3ff_fail').html()).render(data));
54
+ },
55
+ progress: function(e, data) {
56
+ var pct = (parseInt(data.loaded / data.total * 100, 10)) + "%";
57
+ section(data.files[0], '.s3ff_progress .s3ff_bar').css({width: pct}).text(pct);
58
+ }
59
+ };
60
+
61
+ var data = that.data('s3ff');
62
+ if (data) {
63
+ s3ff_handlers.send.apply(this, [null, data]);
64
+ s3ff_handlers.done.apply(this, [null, data]);
65
+ }
66
+
67
+ that.S3FileField(s3ff_handlers);
68
+ };
69
+
70
+ var selector = "[data-acl][data-aws-access-key-id]:not(.s3ff_enabled)";
71
+ $(selector).each(s3ff_init);
72
+ $(document).on("mouseenter mousedown touchstart", selector, s3ff_init);
73
+ };
74
+
75
+ $(document).on('page:change', ready);
76
+ });
@@ -0,0 +1,4 @@
1
+ module S3FF
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module S3FF
2
+ class Railtie < Rails::Railtie
3
+ initializer 'railtie.configure_rails_initialization' do |_app|
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module S3FF
2
+ VERSION = '0.9.0'
3
+ end
@@ -0,0 +1,46 @@
1
+ module S3FF
2
+ module ViewHelper
3
+ def include_s3ff_templates(map = { _direct_url: 'result.url', _file_name: 'result.filename', _file_size: 'result.filesize', _content_type: 'result.filetype' })
4
+ <<-EOM
5
+ <div style="display:none;">
6
+ <script id="s3ff_label" type="text/x-tmpl">
7
+ <label class="s3ff_label" style="display:block;"></label>
8
+ </script>
9
+ <script id="s3ff_upload" type="text/x-tmpl" data-jsv-tmpl="_0">
10
+ <div class="s3ff_section" id="upload-{{:unique_id}}" style="margin-top:1em;">
11
+ <div class="progress s3ff_progress">
12
+ <div class="progress-bar progress-bar-striped active s3ff_bar" style="width: 0%"></div>
13
+ </div>
14
+ </div>
15
+ </script>
16
+ <script id="s3ff_fail" type="text/x-tmpl">
17
+ <div class="progress-bar progress-bar-danger" style="width: 80%">
18
+ <span class="fa fa-exclamation-triangle"></span>
19
+ {{if failReason}}
20
+ {{:failReason}}
21
+ {{else}}
22
+ Upload failed!
23
+ {{/if}}
24
+ </div>
25
+ </script>
26
+ <script id="s3ff_done" type="text/x-tmpl">
27
+ <button class="close" data-dismiss="alert" style="margin-right:4px;" type="button">
28
+ <span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
29
+ </button>
30
+ <span class="form-control">
31
+ <span class="fa fa-file-o"></span>
32
+ {{:result.filename}}
33
+ {{if result.filesize}}
34
+ ({{:result.filesize}} bytes)
35
+ {{/if}}
36
+ </span>
37
+ <div style="display:none;">
38
+ #{map.collect {|k,v| "<input name=\"{{>~field_prefix}}#{k}]\" type=\"hidden\" value=\"{{:#{v}}}\" />" }.join}
39
+ </div>
40
+ </script>
41
+ </div>
42
+ EOM
43
+ .html_safe
44
+ end
45
+ end
46
+ end
data/lib/s3ff.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 's3_file_field'
2
+ require 's3ff/view_helper'
3
+ require 's3ff/railtie'
4
+ require 's3ff/engine'
5
+
6
+ ActionView::Base.send(:include, S3FF::ViewHelper)
7
+
8
+ S3FileField::FormBuilder.class_eval do
9
+ def s3_file_field_with_s3ff(method, options = {})
10
+ changes = @object.try(:changes) || {} # { attr => [old_value, new_value], ... }
11
+ if new_direct_url = changes["#{method}_direct_url"].try(:last)
12
+ # if *_direct_url_changed? it means we're re-rendering
13
+ # :. we should prepopulate the s3ff fields to avoid re-uploading
14
+ options[:data] ||= {}
15
+ options[:data].merge!({
16
+ s3ff: {
17
+ files: [{
18
+ unique_id: "#{@object_name.parameterize}#{SecureRandom.hex}",
19
+ }],
20
+ result: {
21
+ filename: changes["#{method}_file_name"].try(:last) || File.basename(new_direct_url),
22
+ filesize: changes["#{method}_file_size"].try(:last),
23
+ filetype: changes["#{method}_content_type"].try(:last),
24
+ url: new_direct_url,
25
+ }
26
+ }
27
+ })
28
+ end
29
+ s3_file_field_without_s3ff(method, options)
30
+ end
31
+ alias_method_chain :s3_file_field, :s3ff
32
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3ff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Chew Choon Keat
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: s3_file_field
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
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: rspec
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
+ description: Direct S3 upload using CORS with s3_file_field + paperclip
70
+ email:
71
+ - choonkeat@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - app/assets/javascripts/s3ff.js
78
+ - lib/s3ff.rb
79
+ - lib/s3ff/engine.rb
80
+ - lib/s3ff/railtie.rb
81
+ - lib/s3ff/version.rb
82
+ - lib/s3ff/view_helper.rb
83
+ homepage: https://github.com/choonkeat/s3ff
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.2.2
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Direct S3 upload using CORS with s3_file_field + paperclip
107
+ test_files: []