s3_browser_uploads 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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/lib/s3_browser_uploads.rb +10 -0
- data/lib/s3_browser_uploads/form_definition.rb +85 -0
- data/lib/s3_browser_uploads/railtie.rb +9 -0
- data/lib/s3_browser_uploads/version.rb +3 -0
- data/lib/s3_browser_uploads/view_helpers.rb +21 -0
- data/s3_browser_uploads.gemspec +27 -0
- data/spec/form_definition_spec.rb +110 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/view_helpers_spec.rb +76 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d11949c33c4d40cd6f60105ab513b4a86e3a0174
|
4
|
+
data.tar.gz: ebc1978bc5ce720a5f935ce489d19c0359ac2502
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f28057c0ddec32a220adfbf448240a816ea1fc85c240d1a34b997cb2fc27f1255f12ebe119d3e0381bc6c4521e53c0b5ba67692fdd89737de37fefb8eb7ea598
|
7
|
+
data.tar.gz: 8ca98453654043da7aec1c5d0ff6bfde99ab6ae35012955a5fd76d51578c58b8cbf704d681347f7dc3438e320ddf4f62283b43ab225f9269214c16bf7c408976
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Frederick Cheung
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# S3BrowserUploads
|
2
|
+
|
3
|
+
Easy straight-to-s3 uploads from your browser for rails. Takes care of policy documents and all that jazz
|
4
|
+
|
5
|
+
## Example Usage
|
6
|
+
|
7
|
+
In your controller
|
8
|
+
|
9
|
+
@form_definition = S3BrowserUploads::FormDefinition.new :aws_access_key_id => 'Somekey'
|
10
|
+
@form_definition.add_field('x-amz-server-side-encryption', 'AES256')
|
11
|
+
@form_definition.add_field('key', 'users/fred/${filename}')
|
12
|
+
@form_definition.add_condition('key', 'starts-with' => 'users/fred/')
|
13
|
+
|
14
|
+
In your view
|
15
|
+
|
16
|
+
<%= s3_form @form_definition do %>
|
17
|
+
|
18
|
+
<%= file_field_tag :file %>
|
19
|
+
<%= submit_tag 'Upload' %>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
|
23
|
+
## Details
|
24
|
+
|
25
|
+
Every field in your form must be signed as part of the policy document in order for S3 to accept the upload.
|
26
|
+
|
27
|
+
Call `add_field` to add a hidden field both to the form and to the policy document. If you want to add the field yourself then call `add_condition` to add the field to the policy document. For example if you wanted to allow the user to choose the acl for the file then you would do
|
28
|
+
|
29
|
+
@form_definition.add_condition 'acl', 'starts-with' => ''
|
30
|
+
|
31
|
+
This adds `acl` to the policy document and states that it can take any value. Then you'd need to add an input to the form that would allow the user to choose the acl.
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
module S3BrowserUploads
|
5
|
+
class FormDefinition
|
6
|
+
attr_accessor :region, :aws_secret_access_key, :expires, :fields, :conditions
|
7
|
+
|
8
|
+
|
9
|
+
def aws_access_key_id= value
|
10
|
+
fields['AWSAccessKeyID'] = value #access key id is not included in the policy
|
11
|
+
end
|
12
|
+
|
13
|
+
def aws_access_key_id
|
14
|
+
fields['AWSAccessKeyID'] #access key id is not included in the policy
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def aws_session_token
|
19
|
+
fields['x-amz-security-token']
|
20
|
+
end
|
21
|
+
|
22
|
+
def aws_session_token= value
|
23
|
+
add_field 'x-amz-security-token', value
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def bucket
|
28
|
+
fields['bucket']
|
29
|
+
end
|
30
|
+
|
31
|
+
def bucket= value
|
32
|
+
add_field 'bucket', value
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_field key, value
|
36
|
+
fields[key] = value
|
37
|
+
add_condition key, value
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_condition key, condition
|
42
|
+
@conditions[key] = case condition
|
43
|
+
when String then {key => condition}
|
44
|
+
when Hash then
|
45
|
+
[condition.keys.first, "$#{key}", condition.values.first]
|
46
|
+
when Range then
|
47
|
+
[key, condition.begin, condition.end]
|
48
|
+
else
|
49
|
+
raise ArgumentError, "unknown condition type #{condition}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def restrict_content_length range
|
54
|
+
add_condition 'content-length-range', range
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(options={})
|
58
|
+
@fields = {}
|
59
|
+
@conditions = {}
|
60
|
+
options.each {|key, value| public_send("#{key}=", value)}
|
61
|
+
@digest = OpenSSL::Digest::Digest.new('sha1')
|
62
|
+
@hmac = lambda {|data| OpenSSL::HMAC.digest(@digest, @aws_secret_access_key, data)}
|
63
|
+
end
|
64
|
+
|
65
|
+
def signature
|
66
|
+
Base64.strict_encode64(@hmac[encoded_policy])
|
67
|
+
end
|
68
|
+
|
69
|
+
def endpoint
|
70
|
+
"https://#{bucket}.s3-#{region}.amazonaws.com"
|
71
|
+
end
|
72
|
+
|
73
|
+
def encoded_policy
|
74
|
+
Base64.strict_encode64(policy_document.to_json)
|
75
|
+
end
|
76
|
+
|
77
|
+
def policy_document
|
78
|
+
{
|
79
|
+
'expiration' => expires.xmlschema,
|
80
|
+
'conditions' => @conditions.values
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module S3BrowserUploads
|
2
|
+
module ViewHelpers
|
3
|
+
def s3_form form_definition, html_options={}, &block
|
4
|
+
options = {'action' => form_definition.endpoint, 'method' => 'POST', 'enctype' => "multipart/form-data",'accept-charset' => "UTF-8"}
|
5
|
+
options.merge! html_options
|
6
|
+
|
7
|
+
output = ActiveSupport::SafeBuffer.new
|
8
|
+
output.safe_concat(tag(:form, options, true).html_safe)
|
9
|
+
output << hidden_field_tag( 'x-ignore-utf8', '✓'.html_safe)
|
10
|
+
form_definition.fields.each do |name, value|
|
11
|
+
output << hidden_field_tag( name, value)
|
12
|
+
end
|
13
|
+
output << hidden_field_tag('policy', form_definition.encoded_policy)
|
14
|
+
output << hidden_field_tag('signature', form_definition.signature)
|
15
|
+
output << capture(&block)
|
16
|
+
output.safe_concat("</form>")
|
17
|
+
end
|
18
|
+
::ActionView::Base.send :include, self
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 's3_browser_uploads/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "s3_browser_uploads"
|
8
|
+
spec.version = S3BrowserUploads::VERSION
|
9
|
+
spec.authors = ["Frederick Cheung"]
|
10
|
+
spec.email = ["frederick.cheung@gmail.com"]
|
11
|
+
spec.description = %q{Easy straight-to-s3 uploads from your browser}
|
12
|
+
spec.summary = %q{Easy straight-to-s3 uploads from your browser}
|
13
|
+
spec.homepage = "https://github.com/fcheung/s3_browser_uploads"
|
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_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.13.0"
|
24
|
+
spec.add_development_dependency "rspec-rails", "~> 2.13.0"
|
25
|
+
spec.add_development_dependency "rails", "~> 3.2.13"
|
26
|
+
spec.add_development_dependency "capybara", "~> 2.1.0"
|
27
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe S3BrowserUploads::FormDefinition do
|
4
|
+
|
5
|
+
let(:expires_at) {Time.now + 1800}
|
6
|
+
[:region, :aws_access_key_id, :aws_secret_access_key, :aws_session_token, :bucket, :expires].each do |key|
|
7
|
+
it "should set #{key} from hash passed to" do
|
8
|
+
form = S3BrowserUploads::FormDefinition.new(key => 'foo')
|
9
|
+
form.send(key).should == 'foo'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'endpoint' do
|
14
|
+
it 'should return the url for the bucket' do
|
15
|
+
S3BrowserUploads::FormDefinition.new(:region => 'eu-west-1', :bucket => 'some-bucket').endpoint.should ==
|
16
|
+
"https://some-bucket.s3-eu-west-1.amazonaws.com"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'aws_session_token' do
|
21
|
+
it 'should be added as the x-amz-security-token field' do
|
22
|
+
form = S3BrowserUploads::FormDefinition.new(:region => 'eu-west-1', :bucket => 'some-bucket', :aws_session_token => 'token')
|
23
|
+
form.fields['x-amz-security-token'].should == 'token'
|
24
|
+
form.conditions['x-amz-security-token'].should == {'x-amz-security-token' => 'token'}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
describe 'encoded_policy' do
|
28
|
+
subject { S3BrowserUploads::FormDefinition.new(:region => 'eu-west-1', :bucket => 'some-bucket', :expires => expires_at)}
|
29
|
+
|
30
|
+
its(:encoded_policy) {should == Base64.strict_encode64(subject.policy_document.to_json)}
|
31
|
+
its(:encoded_policy) {should_not include("\n") }
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'signature' do
|
36
|
+
#base64 hmac of the encoded policy
|
37
|
+
subject { S3BrowserUploads::FormDefinition.new(:aws_secret_access_key => '123XYZ', :region => 'eu-west-1', :bucket => 'some-bucket', :expires => expires_at)}
|
38
|
+
its(:signature) {should == Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), '123XYZ', subject.encoded_policy))}
|
39
|
+
context 'with sample data' do
|
40
|
+
subject do
|
41
|
+
form = S3BrowserUploads::FormDefinition.new(:aws_secret_access_key => '123XYZ', :region => 'eu-west-1', :bucket => 'some-bucket', :expires => Time.utc(2012,1,1,1,1,1))
|
42
|
+
form.add_field 'acl', 'public-read'
|
43
|
+
form.add_condition 'key', 'starts-with' => 'users/fred/'
|
44
|
+
form
|
45
|
+
end
|
46
|
+
its(:signature) {should == 'uxKt7EExauvCe41l6O44RnMlZmQ='}
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'policy_document' do
|
52
|
+
let(:form) { S3BrowserUploads::FormDefinition.new(:region => 'eu-west-1', :bucket => 'some-bucket', :expires => expires_at)}
|
53
|
+
|
54
|
+
it 'should contain expiration and conditions' do
|
55
|
+
form.policy_document.keys.should =~ %w(conditions expiration)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should set expiration to the xml schema representation of the expiry date' do
|
59
|
+
form.policy_document['expiration'].should == expires_at.xmlschema
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with no conditions added' do
|
63
|
+
it 'should have a condition on the bucket' do
|
64
|
+
form.policy_document['conditions'].should == [{'bucket' => 'some-bucket'}]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with a strict condition added' do
|
69
|
+
it 'should have the condition' do
|
70
|
+
form.add_condition 'key', 'abc'
|
71
|
+
form.policy_document['conditions'].should =~ [{'bucket' => 'some-bucket'}, {'key' => 'abc'}]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'with a starts-with condition' do
|
76
|
+
it 'should have the condition' do
|
77
|
+
form.add_condition 'key', 'starts-with' => 'abc'
|
78
|
+
form.policy_document['conditions'].should =~ [{'bucket' => 'some-bucket'}, ['starts-with', '$key', 'abc']]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
context 'with a content-length condition' do
|
84
|
+
it 'should have the condition' do
|
85
|
+
form.restrict_content_length 0..1024
|
86
|
+
form.policy_document['conditions'].should =~ [{'bucket' => 'some-bucket'}, ['content-length-range', 0,1024]]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with an eq condition' do
|
91
|
+
it 'should have the condition' do
|
92
|
+
form.add_condition 'key', 'eq' => 'abc'
|
93
|
+
form.policy_document['conditions'].should =~ [{'bucket' => 'some-bucket'}, ['eq', '$key', 'abc']]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when a field has been added' do
|
98
|
+
it 'should add a strict match condition on the field' do
|
99
|
+
form.add_field('acl', 'private')
|
100
|
+
form.policy_document['conditions'].should == [{'bucket' => 'some-bucket'}, {'acl' => 'private'}]
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should the condition to be overriden' do
|
104
|
+
form.add_field('key', 'users/bob/${filename}')
|
105
|
+
form.add_condition('key', 'starts-with' => 'users/bob/')
|
106
|
+
form.policy_document['conditions'].should == [{'bucket' => 'some-bucket'}, ['starts-with', '$key', 'users/bob/']]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 's3_browser_uploads'
|
3
|
+
require 'action_controller'
|
4
|
+
module Rails
|
5
|
+
module VERSION
|
6
|
+
STRING = '3.2.13'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
require 'rspec/rails/adapters'
|
10
|
+
require 'rspec/rails/example/rails_example_group'
|
11
|
+
require 'rspec/rails/example/view_example_group'
|
12
|
+
require 'rspec/rails/example/helper_example_group'
|
13
|
+
require 's3_browser_uploads/view_helpers'
|
14
|
+
require 'capybara/rspec'
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.mock_with :rspec
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe S3BrowserUploads::ViewHelpers, :type => 'helper' do
|
4
|
+
include Capybara::RSpecMatchers
|
5
|
+
include RSpec::Rails::HelperExampleGroup
|
6
|
+
let(:form_definition) do
|
7
|
+
S3BrowserUploads::FormDefinition.new(:region => 'eu-west-1',
|
8
|
+
:bucket => 'some-bucket',
|
9
|
+
:expires => Time.now + 1800,
|
10
|
+
:aws_access_key_id => 'AnAccessKey',
|
11
|
+
:aws_secret_access_key => 'ASecretKey')
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 's3_form' do
|
15
|
+
let(:content) {helper.s3_form(form_definition) {}}
|
16
|
+
|
17
|
+
describe 'form tag' do
|
18
|
+
subject {Capybara.string(content).find('form')}
|
19
|
+
its([:action]) { should == form_definition.endpoint }
|
20
|
+
its([:enctype]) { should == 'multipart/form-data' }
|
21
|
+
its([:method]) { should == 'POST' }
|
22
|
+
|
23
|
+
context 'html options are passed' do
|
24
|
+
it 'should add them to the form' do
|
25
|
+
helper.s3_form(form_definition, :id => 'a-form'){}.should have_css 'form#a-form'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'form contents' do
|
31
|
+
subject {content}
|
32
|
+
it 'should include a utf8 enforcer tag' do
|
33
|
+
should have_hidden_input("x-ignore-utf8").with_value("\u2713")
|
34
|
+
end
|
35
|
+
|
36
|
+
it { should have_hidden_input('AWSAccessKeyID').with_value("AnAccessKey") }
|
37
|
+
it { should have_hidden_input('signature').with_value(form_definition.signature) }
|
38
|
+
it { should have_hidden_input('policy').with_value(form_definition.encoded_policy) }
|
39
|
+
it { should have_hidden_input('bucket').with_value(form_definition.bucket) }
|
40
|
+
|
41
|
+
|
42
|
+
context 'a field was added to the form' do
|
43
|
+
before(:each) { form_definition.add_field 'Content-Disposition', 'inline'}
|
44
|
+
it { should have_hidden_input('Content-Disposition').with_value('inline')}
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'the block has content' do
|
48
|
+
let(:content) {helper.s3_form(form_definition) {submit_tag 'Upload'}}
|
49
|
+
|
50
|
+
it 'should include it in the form' do
|
51
|
+
content.should have_css('input[type=submit][value=Upload]')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should include it after the autogenerated inputs' do
|
55
|
+
Capybara.string(content).all('input').last['type'].should == 'submit'
|
56
|
+
Capybara.string(content).all('input').last['value'].should == 'Upload'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
RSpec::Matchers.define :have_hidden_input do |name|
|
64
|
+
|
65
|
+
chain :with_value do |value|
|
66
|
+
@value = value
|
67
|
+
end
|
68
|
+
|
69
|
+
match do |x|
|
70
|
+
selector = "input[type=hidden][name='#{name}']"
|
71
|
+
selector += "[value='#{@value}']" if @value
|
72
|
+
x.should have_css(selector)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: s3_browser_uploads
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Frederick Cheung
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.13.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.13.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.13.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.13.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.2.13
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.2.13
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: capybara
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.1.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.1.0
|
97
|
+
description: Easy straight-to-s3 uploads from your browser
|
98
|
+
email:
|
99
|
+
- frederick.cheung@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE.txt
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/s3_browser_uploads.rb
|
110
|
+
- lib/s3_browser_uploads/form_definition.rb
|
111
|
+
- lib/s3_browser_uploads/railtie.rb
|
112
|
+
- lib/s3_browser_uploads/version.rb
|
113
|
+
- lib/s3_browser_uploads/view_helpers.rb
|
114
|
+
- s3_browser_uploads.gemspec
|
115
|
+
- spec/form_definition_spec.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
- spec/view_helpers_spec.rb
|
118
|
+
homepage: https://github.com/fcheung/s3_browser_uploads
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.0.3
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: Easy straight-to-s3 uploads from your browser
|
142
|
+
test_files:
|
143
|
+
- spec/form_definition_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
- spec/view_helpers_spec.rb
|