s3direct 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in s3direct.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brent Dillingham
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # S3direct
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 's3direct'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install s3direct
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,42 @@
1
+ require "active_support/all"
2
+
3
+ require "s3direct/version"
4
+ require "s3direct/file"
5
+ require "s3direct/string_interpolator"
6
+ require "s3direct/upload_request"
7
+ require "s3direct/uploadable"
8
+
9
+
10
+ module S3Direct
11
+ def self.configure
12
+ yield config
13
+ end
14
+
15
+ def self.config
16
+ @@config ||= Config.new
17
+ end
18
+
19
+ class Config
20
+ attr_accessor :bucket
21
+ attr_accessor :bucket_url
22
+ attr_accessor :access_key
23
+ attr_accessor :secret_key
24
+
25
+ attr_writer :max_upload_size
26
+ def max_upload_size
27
+ @max_upload_size ||= 1.gigabyte
28
+ end
29
+
30
+ attr_writer :default_acl
31
+ def default_acl
32
+ @default_acl ||= 'public-read'
33
+ end
34
+ end
35
+
36
+ begin
37
+ class Engine < Rails::Engine
38
+ end
39
+ rescue NameError => e
40
+ puts "Rails is not loaded => #{e.message}"
41
+ end
42
+ end
@@ -0,0 +1,72 @@
1
+ module S3Direct
2
+ class File
3
+
4
+ attr_reader :model, :identifier, :pattern
5
+
6
+ def self.sanitize_filename(name)
7
+ unless name.nil?
8
+ name.strip
9
+ end
10
+ end
11
+
12
+ def initialize(model, identifier, pattern, opts={})
13
+ setup_options opts
14
+
15
+ @model = model
16
+ @identifier = identifier
17
+ @pattern = pattern
18
+ end
19
+
20
+ def name
21
+ @model.send "#{identifier}_file"
22
+ end
23
+
24
+ def s3_path
25
+ StringInterpolator.new(model, pattern).to_s
26
+ end
27
+
28
+ def url
29
+ if exists?
30
+ ::File.join(config.bucket_url, key)
31
+ else
32
+ default_url
33
+ end
34
+ end
35
+
36
+ def upload_request(filename = name, options = {})
37
+ if filename.blank?
38
+ raise "Can't create an upload request without a filename - " +
39
+ "provide it as an argument or set #{identifier}_file on the model"
40
+ end
41
+ UploadRequest.new s3_path, self.class.sanitize_filename(filename), options
42
+ end
43
+
44
+ def key
45
+ ::File.join(s3_path, name)
46
+ end
47
+
48
+ def exists?
49
+ name.present?
50
+ end
51
+
52
+ private
53
+
54
+ def config
55
+ ::S3Direct.config
56
+ end
57
+
58
+ def options
59
+ # set up any defaults here
60
+ @options ||= {}
61
+ end
62
+
63
+ def setup_options(opts)
64
+ options.merge! opts
65
+ end
66
+
67
+ def default_url
68
+ options[:default_url]
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ module S3Direct
2
+ class StringInterpolator
3
+ DELIM = "/"
4
+
5
+ attr_reader :context, :pattern
6
+
7
+ def initialize(context, pattern)
8
+ @pattern = pattern
9
+ @context = context
10
+ end
11
+
12
+ def to_s
13
+ compile_parts.join(DELIM)
14
+ end
15
+
16
+ def compile_parts
17
+ pattern.split(DELIM).collect do |part|
18
+ if part[0] == ':'
19
+ meth = part[1, part.length - 1]
20
+ result = context.public_send meth
21
+ if result.blank?
22
+ raise ":#{meth} for path '#{pattern}' was blank in #{context.inspect}"
23
+ end
24
+ result.to_s.underscore
25
+ else
26
+ part
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,82 @@
1
+ module S3Direct
2
+ class UploadRequest
3
+ attr_reader :path, :filename, :options
4
+
5
+ def initialize(path, sanitized_filename, options = {})
6
+ @path = path
7
+ @filename = sanitized_filename
8
+ @options = options
9
+ end
10
+
11
+ def key
12
+ ::File.join(@path, @filename)
13
+ end
14
+
15
+ def to_json
16
+ data = {
17
+ url: config.bucket_url,
18
+ filename: @filename,
19
+ key: key,
20
+ policy: s3_upload_policy_document,
21
+ signature: s3_upload_signature,
22
+ acl: s3_acl,
23
+ success_action_status: "200",
24
+ 'AWSAccessKeyId' => config.access_key
25
+ }
26
+
27
+ if attachment_filename
28
+ data["Content-Disposition"] = %Q{attachment; filename="#{attachment_filename}"}
29
+ end
30
+
31
+ data.to_json
32
+ end
33
+
34
+ def attachment_filename
35
+ options[:attachment_filename].presence
36
+ end
37
+
38
+ def s3_acl
39
+ options.fetch(:acl, config.default_acl)
40
+ end
41
+
42
+ private
43
+
44
+ # generate the policy document that amazon is expecting.
45
+ def s3_upload_policy_document
46
+ policy = {
47
+ 'expiration' => 5.minutes.from_now.utc.xmlschema,
48
+ 'conditions' => [
49
+ {'bucket' => config.bucket},
50
+ {'acl' => s3_acl},
51
+ {'success_action_status' => '200'},
52
+ {'key' => key},
53
+ ['content-length-range', 0, config.max_upload_size]
54
+ ]
55
+ }
56
+
57
+ if attachment_filename
58
+ policy['conditions'] << {"Content-Disposition" => %Q{attachment; filename="#{attachment_filename}"}}
59
+ end
60
+
61
+ encode(policy.to_json)
62
+ end
63
+
64
+ # sign our request by Base64 encoding the policy document.
65
+ def s3_upload_signature
66
+ signature = OpenSSL::HMAC.digest(
67
+ OpenSSL::Digest::Digest.new('sha1'),
68
+ config.secret_key,
69
+ s3_upload_policy_document
70
+ )
71
+ encode(signature)
72
+ end
73
+
74
+ def encode(str)
75
+ Base64.encode64(str).gsub("\n",'')
76
+ end
77
+
78
+ def config
79
+ ::S3Direct.config
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ module S3Direct
2
+ module Uploadable
3
+
4
+ def has_s3_file(attr_name, pattern, options={})
5
+ define_method attr_name do
6
+ ::S3Direct::File.new(self, attr_name, pattern, options)
7
+ end
8
+ define_method "#{attr_name}_file=" do |filename|
9
+ self["#{attr_name}_file"] = ::S3Direct::File.sanitize_filename(filename)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module S3Direct
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 's3direct/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "s3direct"
8
+ spec.version = S3Direct::VERSION
9
+ spec.authors = ["Brent Dillingham"]
10
+ spec.email = ["brentdillingham@gmail.com"]
11
+ spec.summary = %q{Upload directly to S3}
12
+ spec.description = spec.summary
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activesupport', '>= 3.2.0'
22
+ spec.add_dependency 'jquery-fileupload-rails', '~> 0.4.1'
23
+ spec.add_dependency 'coffee-rails'
24
+ spec.add_dependency 'ejs'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rails", ">= 3.2.1"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec"
30
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe S3Direct::Config do
4
+ it "has a default_acl of public-read when not set" do
5
+ expect(S3Direct.config.default_acl).to eq('public-read')
6
+ end
7
+
8
+ it "allows setting a default_acl" do
9
+ begin
10
+ S3Direct.configure {|c| c.default_acl = 'authenticated-read' }
11
+ expect(S3Direct.config.default_acl).to eq('authenticated-read')
12
+ ensure
13
+ S3Direct.configure {|c| c.default_acl = nil }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe S3Direct::File do
4
+
5
+ it "mounts to an activerecord model on initialize" do
6
+ model = double(:model)
7
+ file = S3Direct::File.new(model, :my_file, "foo/bar/bat")
8
+
9
+ expect(file.model).to eql model
10
+ expect(file.identifier).to eql :my_file
11
+ expect(file.pattern).to eql "foo/bar/bat"
12
+ end
13
+
14
+ it "interpolates an s3 path based on the pattern given" do
15
+ model = double(:model, lesson_id: 1, id: 2)
16
+ file = S3Direct::File.new(model, :my_file, "lessons/:lesson_id/videos/:id")
17
+
18
+ expect(file.s3_path).to eql "lessons/1/videos/2"
19
+ end
20
+
21
+ it "allows a default url to used when a file is not present" do
22
+ model = double(:model, lesson_id: 1, id: 2, media_file: nil)
23
+ file = S3Direct::File.new(model, :media, "lessons/:lesson_id/videos/:id", default_url: "http://example.com/default.png")
24
+ expect(file.url).to eql "http://example.com/default.png"
25
+ model.stub(media_file: "foo.png")
26
+ expect(file.url).to_not eql "http://example.com/default.png"
27
+ end
28
+
29
+ end
30
+
31
+ describe S3Direct::File, "#exists?" do
32
+
33
+ it "is true if the models '{name}_file' is not nil" do
34
+ model = double :model, media_file: "foobar.png"
35
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
36
+ expect(file.exists?).to be true
37
+ end
38
+
39
+ it "is false if the models '{name}_file' is nil" do
40
+ model = double :model, media_file: nil
41
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
42
+ expect(file.exists?).to be false
43
+ end
44
+ end
45
+
46
+ describe S3Direct::File, "#upload_request" do
47
+
48
+ it "returns a s3 direct upload object" do
49
+ model = double(:model, media_file: 'foo')
50
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
51
+
52
+ expect(file.upload_request).to be_an_instance_of(S3Direct::UploadRequest)
53
+ expect(file.upload_request.key).to eql(file.key)
54
+ end
55
+
56
+ it "accepts the filename as a parameter and sanitizes it" do
57
+ model = double(:model)
58
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
59
+ expect(file.upload_request(' foo').filename).to eql 'foo'
60
+ end
61
+
62
+ it "optionally receives options to pass to the UploadRequest" do
63
+ model = double(:model)
64
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
65
+ S3Direct::UploadRequest.should_receive(:new).with(anything, "test.txt", {foo: 'bar'})
66
+ file.upload_request('test.txt', {foo: 'bar'})
67
+ end
68
+
69
+ it "raises an error if the filename is not set or provided" do
70
+ model = double(:model)
71
+ file = S3Direct::File.new(model, :media, "foo/bar/bat")
72
+ expect { file.upload_request }.to raise_error
73
+ end
74
+
75
+ end
76
+
77
+ describe S3Direct::File, "#name" do
78
+ it "returns the file name based on the pattern: '<name>_file'" do
79
+ model = double(:model)
80
+ model.stub(:avatar_file) { "my_avatar.png" }
81
+ file = S3Direct::File.new(model, :avatar, "")
82
+
83
+ expect(file.name).to eql "my_avatar.png"
84
+ end
85
+ end
86
+
87
+ describe S3Direct::File, "#url" do
88
+ context "the file exists" do
89
+ it "returns the full url of the file" do
90
+ S3Direct.config.stub(bucket_url: 'http://s3.com/mabucket/')
91
+ model = double(:model, id: 77, avatar_file: 'my_avatar.png')
92
+ file = S3Direct::File.new(model, :avatar, "my_model/:id/avatar")
93
+
94
+ expect(file.url).to eql "http://s3.com/mabucket/my_model/77/avatar/my_avatar.png"
95
+ end
96
+ end
97
+
98
+ context "the file does not exist" do
99
+ it "returns nil" do
100
+ model = double(:model)
101
+ model.stub(:avatar_file) { nil }
102
+ file = S3Direct::File.new(model, :avatar, "foo/bar/bat")
103
+
104
+ expect(file.url).to be_nil
105
+ end
106
+ end
107
+ end
108
+
109
+ describe S3Direct::File, "#key" do
110
+ it "returns the full s3 key for the file" do
111
+ model = double(:model)
112
+ model.stub(:id) { 77 }
113
+ model.stub(:avatar_file) { "my_avatar.png" }
114
+ file = S3Direct::File.new(model, :avatar, "my_model/:id/avatar/")
115
+
116
+ expect(file.key).to eql "my_model/77/avatar/my_avatar.png"
117
+ end
118
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ require_relative '../lib/s3direct'
3
+
4
+ S3Direct.configure do |config|
5
+ config.bucket_url = "http://example.com"
6
+ end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+ require "ostruct"
3
+
4
+ describe S3Direct::StringInterpolator do
5
+
6
+ it "converts :pattern tags using local context methods" do
7
+ foo = OpenStruct.new(bar: "bar", fizz: "fizzy", buzz: "buzz_buzz")
8
+ interpolation = S3Direct::StringInterpolator.new(foo, "foo/bar/:bar/fizz/:fizz/buzz/:buzz/:class")
9
+ expect(interpolation.to_s).to eq "foo/bar/bar/fizz/fizzy/buzz/buzz_buzz/open_struct"
10
+ end
11
+
12
+ it "will not re-interpolate :pattern returned from method calls" do
13
+ o = OpenStruct.new
14
+ o.security = ":test"
15
+ o.test = "boom"
16
+ interpolation = S3Direct::StringInterpolator.new(o, "/security/:security")
17
+ expect(interpolation.to_s).to eq '/security/:test'
18
+ end
19
+
20
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe S3Direct::UploadRequest, '#attachment_filename' do
4
+ it 'is nil if not provided an attachment_filename option' do
5
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'buzz.txt')
6
+ expect(upload_request.attachment_filename).to be_nil
7
+ end
8
+
9
+ it 'is the options[:attachment_filename] if provided' do
10
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'random.txt', {
11
+ attachment_filename: 'expected.txt'
12
+ })
13
+ expect(upload_request.attachment_filename).to eq('expected.txt')
14
+ end
15
+ end
16
+
17
+ describe S3Direct::UploadRequest, '#to_json' do
18
+ before do
19
+ S3Direct.config.stub(bucket_url: 'http://s3.com/mabucket/')
20
+ S3Direct.config.stub(secret_key: 'sekret')
21
+ end
22
+
23
+ context "when no attachment_filename option is given" do
24
+ before do
25
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'buzz.txt')
26
+ @data = JSON[upload_request.to_json]
27
+ end
28
+
29
+ it "does not add a Content-Disposition attachment field" do
30
+ expect(@data.has_key?("Content-Disposition")).to be_false
31
+ end
32
+
33
+ it "includes no content-disposition in the policy" do
34
+ policy = JSON[Base64.decode64 @data['policy']]
35
+ expect(policy['conditions'].include?('Content-Disposition')).to be_false
36
+ end
37
+ end
38
+
39
+ context "when an attachment_filename option is given" do
40
+ before do
41
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'buzz.txt', {
42
+ attachment_filename: 'expected.txt'
43
+ })
44
+ @data = JSON[upload_request.to_json]
45
+ end
46
+
47
+ it "adds a Content-Disposition attachment field using the option" do
48
+ expect(@data["Content-Disposition"]).to eq('attachment; filename="expected.txt"')
49
+ end
50
+
51
+ it "includes the content-disposition in the policy" do
52
+ policy = JSON[Base64.decode64 @data['policy']]
53
+ condition = policy['conditions'].detect {|c| c.is_a?(Hash) && c.keys.include?('Content-Disposition') }
54
+ expect(condition['Content-Disposition']).to eq('attachment; filename="expected.txt"')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe S3Direct::UploadRequest, '#s3_acl' do
60
+ it "uses the config default_acl by default" do
61
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'buzz.txt')
62
+ expect(upload_request.s3_acl).to eq('public-read')
63
+ end
64
+
65
+ it "uses the acl option if available" do
66
+ upload_request = S3Direct::UploadRequest.new('/foo/bar', 'buzz.txt', acl: 'authenticated-read')
67
+ expect(upload_request.s3_acl).to eq('authenticated-read')
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ module TestS3Uploadable
4
+ class Base
5
+ end
6
+
7
+ Base.extend S3Direct::Uploadable
8
+
9
+ class Foo < Base
10
+ has_s3_file :avatar, "foo/:id/avatar", default_url: "foobar.png"
11
+
12
+ def avatar_file
13
+ "avatar.png"
14
+ end
15
+
16
+ def id
17
+ 42
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ describe S3Direct::Uploadable, "#has_file" do
24
+ it "defines a method on the object to access the file" do
25
+ foo = TestS3Uploadable::Foo.new
26
+ expect(foo.avatar).to be_kind_of(S3Direct::File)
27
+ expect(foo.avatar.s3_path).to eql "foo/42/avatar"
28
+ end
29
+
30
+ it "allows for default urls" do
31
+ foo = TestS3Uploadable::Foo.new
32
+ foo.stub(:avatar_file) { nil }
33
+ expect(foo.avatar.url).to eql "foobar.png"
34
+ end
35
+ end
@@ -0,0 +1,99 @@
1
+ #= require jquery-fileupload/basic
2
+ #= require s3direct_default_template
3
+ #= require_self
4
+
5
+ class window.S3DirectUploadView
6
+ template: JST['s3_direct_default_template']
7
+
8
+ $: (selector) ->
9
+ @$el.find(selector)
10
+
11
+ uploader: ->
12
+ @$('.uploader')
13
+
14
+ fileInput: ->
15
+ @$('input[type=file]')
16
+
17
+ progressMeter: ->
18
+ @$('.progress .meter')
19
+
20
+ progressBar: ->
21
+ @$('.progress')
22
+
23
+ uploadInfo: ->
24
+ @$('.upload-info')
25
+
26
+ alertBox: ->
27
+ @$('.alert-box')
28
+
29
+ fileInfo: ->
30
+ @$('.file-info')
31
+
32
+ constructor: (options = {}) ->
33
+ @model = options.model
34
+ @fileAttribute = options.fileAttribute
35
+ @requestUrl = options.requestUrl
36
+ @$el = if options.el then $(options.el) else $('<div>')
37
+ @el = @$el[0]
38
+
39
+ render: ->
40
+ @$el.html @template(this)
41
+ @setupFileInput()
42
+ @setFileInfo @fileName()
43
+
44
+ fileName: ->
45
+ @model.get(@fileAttribute)
46
+
47
+ setupFileInput: ->
48
+ filename = null
49
+
50
+ @fileInput()
51
+ .fileupload
52
+ paramName : 'file'
53
+ autoUpload : true
54
+ dropZone : @$el
55
+ # When a file is added, make an "upload request" to our server to get the S3 bucket URL,
56
+ # policy document, signature, etc.
57
+ add: (e, data) =>
58
+ $.ajax
59
+ url: @requestUrl
60
+ data: {filename: data.files[0].name}
61
+ success: (uploadRequestObj) =>
62
+ # The sanitized version of the filename
63
+ filename = uploadRequestObj.filename
64
+ @fileInput().fileupload 'option', 'url', uploadRequestObj.url
65
+ @fileInput().fileupload 'option', 'formData', _.omit(uploadRequestObj, 'url', 'filename')
66
+ data.submit()
67
+ # Once the upload to S3 is under way, fade in the progress bar and disable the button.
68
+ send: =>
69
+ @uploader().hide()
70
+ @alertBox().remove()
71
+ @progressBar().fadeIn()
72
+ # Show an indeterminate progress bar if browser doesn't support progress (I *think* this is the right attribute to check)
73
+ @progressMeter().css(width: '100%') unless $.support.xhrFileUpload
74
+ # Update the progress bar.
75
+ progress: (e) =>
76
+ percent = Math.round(e.loaded / e.total * 100)
77
+ @progressMeter().css(width: percent + '%')
78
+ # S3 upload was successful; save the LessonMedia object on the server.
79
+ done: (e, response) =>
80
+ msg = "<b>Upload succeeded.</b>"
81
+ @showUploadMessage(msg, true)
82
+ @model.set @fileAttribute, filename
83
+ @model.save()
84
+ # S3 upload failed.
85
+ error: =>
86
+ msg = "<b>Something went wrong!</b> Your upload did not complete. Please try again."
87
+ @showUploadMessage(msg, false)
88
+ @uploader().show()
89
+
90
+ # Displays a message about the success or failure of the upload.
91
+ showUploadMessage: (msg, wasSuccess) ->
92
+ @progressBar().fadeOut 300, =>
93
+ @progressMeter().css(width: 0)
94
+ @alertBox().remove()
95
+ alertClass = if wasSuccess then 'success' else 'alert'
96
+ @uploadInfo().append("<div class='alert-box #{alertClass}'>#{msg}</div>")
97
+
98
+ setFileInfo: (name) ->
99
+ @fileInfo().text name if name
@@ -0,0 +1,14 @@
1
+ <div class="s3-direct">
2
+ <div class="uploader">
3
+ <span class="button secondary small fileinput-button">
4
+ <span>Choose File</span>
5
+ <input type="file" />
6
+ </span>
7
+ <span class="file-info">No file chosen</span>
8
+ </div>
9
+ <div class="upload-info">
10
+ <div class="progress" style="display:none">
11
+ <span class="meter" style="width:0%"></span>
12
+ </div>
13
+ </div>
14
+ </div>
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3direct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brent Dillingham
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: jquery-fileupload-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.4.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: coffee-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: ejs
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.3'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.3'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rails
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 3.2.1
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 3.2.1
110
+ - !ruby/object:Gem::Dependency
111
+ name: rake
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: Upload directly to S3
143
+ email:
144
+ - brentdillingham@gmail.com
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - .gitignore
150
+ - Gemfile
151
+ - LICENSE.txt
152
+ - README.md
153
+ - Rakefile
154
+ - lib/s3direct.rb
155
+ - lib/s3direct/file.rb
156
+ - lib/s3direct/string_interpolator.rb
157
+ - lib/s3direct/upload_request.rb
158
+ - lib/s3direct/uploadable.rb
159
+ - lib/s3direct/version.rb
160
+ - s3direct.gemspec
161
+ - spec/config_spec.rb
162
+ - spec/file_spec.rb
163
+ - spec/spec_helper.rb
164
+ - spec/string_interpolator_spec.rb
165
+ - spec/upload_request_spec.rb
166
+ - spec/uploadable_spec.rb
167
+ - vendor/assets/javascripts/s3direct.js.coffee
168
+ - vendor/assets/javascripts/s3direct_default_template.jst.ejs
169
+ homepage: ''
170
+ licenses:
171
+ - MIT
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ segments:
183
+ - 0
184
+ hash: 1023143937309546202
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ segments:
192
+ - 0
193
+ hash: 1023143937309546202
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 1.8.23
197
+ signing_key:
198
+ specification_version: 3
199
+ summary: Upload directly to S3
200
+ test_files:
201
+ - spec/config_spec.rb
202
+ - spec/file_spec.rb
203
+ - spec/spec_helper.rb
204
+ - spec/string_interpolator_spec.rb
205
+ - spec/upload_request_spec.rb
206
+ - spec/uploadable_spec.rb