s3direct 0.1.0

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.
@@ -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