s3_multipart 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. data/.gitignore +11 -0
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +8 -1
  4. data/README.md +150 -42
  5. data/Rakefile +10 -0
  6. data/app/controllers/s3_multipart/application_controller.rb +6 -3
  7. data/app/controllers/s3_multipart/uploads_controller.rb +30 -29
  8. data/app/models/s3_multipart/upload.rb +13 -5
  9. data/grunt.js +44 -0
  10. data/javascripts/footer.js +5 -0
  11. data/javascripts/header.js +14 -0
  12. data/javascripts/libs/underscore.js +1 -0
  13. data/javascripts/s3mp.js +329 -0
  14. data/javascripts/upload.js +76 -0
  15. data/javascripts/uploadpart.js +32 -0
  16. data/lib/generators/s3_multipart/install_generator.rb +30 -0
  17. data/lib/generators/s3_multipart/templates/add_uploader_column_to_model.rb +7 -0
  18. data/lib/generators/s3_multipart/templates/aws.yml +4 -0
  19. data/lib/generators/s3_multipart/templates/configuration_initializer.rb +8 -0
  20. data/lib/generators/s3_multipart/templates/uploader.rb +29 -0
  21. data/{db/migrate/20110727184726_create_s3_multipart_uploads.rb → lib/generators/s3_multipart/templates/uploads_table_migration.rb} +2 -0
  22. data/lib/generators/s3_multipart/uploader_generator.rb +33 -0
  23. data/lib/s3_multipart/action_view_helpers/form_helper.rb +2 -1
  24. data/lib/s3_multipart/config.rb +12 -0
  25. data/lib/s3_multipart/http/net_http.rb +6 -6
  26. data/lib/s3_multipart/railtie.rb +12 -0
  27. data/lib/s3_multipart/transfer_helpers.rb +92 -0
  28. data/lib/s3_multipart/uploader/callbacks.rb +17 -0
  29. data/lib/s3_multipart/uploader/validations.rb +9 -0
  30. data/lib/s3_multipart/uploader.rb +27 -68
  31. data/lib/s3_multipart/version.rb +1 -1
  32. data/lib/s3_multipart.rb +4 -42
  33. data/package.json +9 -0
  34. data/s3_multipart.gemspec +6 -3
  35. data/spec/integration/uploads_controller_spec.rb +2 -1
  36. data/spec/internal/app/assets/javascripts/application.js +148 -63
  37. data/spec/internal/app/assets/stylesheets/application.css.scss +384 -0
  38. data/spec/internal/app/assets/stylesheets/font-awesome.scss +499 -0
  39. data/spec/internal/app/controllers/application_controller.rb +4 -1
  40. data/spec/internal/app/controllers/pages_controller.rb +3 -7
  41. data/spec/internal/app/models/user.rb +4 -0
  42. data/spec/internal/app/models/video.rb +5 -0
  43. data/spec/internal/app/uploaders/multipart/video_uploader.rb +32 -0
  44. data/spec/internal/app/views/pages/upload.html.erb +38 -4
  45. data/spec/internal/config/routes.rb +1 -1
  46. data/spec/internal/db/schema.rb +22 -14
  47. data/spec/internal/public/fonts/FontAwesome.otf +0 -0
  48. data/spec/internal/public/fonts/fontawesome-webfont.eot +0 -0
  49. data/spec/internal/public/fonts/fontawesome-webfont.ttf +0 -0
  50. data/spec/internal/public/fonts/fontawesome-webfont.woff +0 -0
  51. data/spec/internal/tmp/cache/sass/077f66d4d9153cfd737b81ab3f4c5d5858b0db3b/_grid-background.scssc +0 -0
  52. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_appearance.scssc +0 -0
  53. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-clip.scssc +0 -0
  54. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-origin.scssc +0 -0
  55. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-size.scssc +0 -0
  56. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_border-radius.scssc +0 -0
  57. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box-shadow.scssc +0 -0
  58. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box-sizing.scssc +0 -0
  59. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box.scssc +0 -0
  60. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_columns.scssc +0 -0
  61. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_filter.scssc +0 -0
  62. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_font-face.scssc +0 -0
  63. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_hyphenation.scssc +0 -0
  64. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_images.scssc +0 -0
  65. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_inline-block.scssc +0 -0
  66. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_opacity.scssc +0 -0
  67. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_regions.scssc +0 -0
  68. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_shared.scssc +0 -0
  69. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_text-shadow.scssc +0 -0
  70. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_transform.scssc +0 -0
  71. data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_transition.scssc +0 -0
  72. data/spec/internal/tmp/cache/sass/351072894b53bf1a2d2af4bd57c177c805cf063e/_contrast.scssc +0 -0
  73. data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_css3.scssc +0 -0
  74. data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_support.scssc +0 -0
  75. data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_typography.scssc +0 -0
  76. data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_utilities.scssc +0 -0
  77. data/spec/internal/tmp/cache/sass/633bc5f017268b81a60f921876c980c4adcebb74/_base.scssc +0 -0
  78. data/spec/internal/tmp/cache/sass/633bc5f017268b81a60f921876c980c4adcebb74/_sprite-img.scssc +0 -0
  79. data/spec/internal/tmp/cache/sass/68dbce8ac3037e0cd797c9b5c6aa131096361144/_utilities.scssc +0 -0
  80. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_clearfix.scssc +0 -0
  81. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_float.scssc +0 -0
  82. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_hacks.scssc +0 -0
  83. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_min.scssc +0 -0
  84. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_reset.scssc +0 -0
  85. data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_tag-cloud.scssc +0 -0
  86. data/spec/internal/tmp/cache/sass/775792c99f07f64e98296a8dc8c9d2c74a28fd8a/_reset.scssc +0 -0
  87. data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_ellipsis.scssc +0 -0
  88. data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_force-wrap.scssc +0 -0
  89. data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_nowrap.scssc +0 -0
  90. data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_replacement.scssc +0 -0
  91. data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_bullets.scssc +0 -0
  92. data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_horizontal-list.scssc +0 -0
  93. data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_inline-block-list.scssc +0 -0
  94. data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_inline-list.scssc +0 -0
  95. data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_alternating-rows-and-columns.scssc +0 -0
  96. data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_borders.scssc +0 -0
  97. data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_scaffolding.scssc +0 -0
  98. data/spec/internal/tmp/cache/sass/bc7f11d6e8f07a22f545deb35760cfc7830f3369/application.css.scssc +0 -0
  99. data/spec/internal/tmp/cache/sass/bc7f11d6e8f07a22f545deb35760cfc7830f3369/font-awesome.scssc +0 -0
  100. data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_color.scssc +0 -0
  101. data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_general.scssc +0 -0
  102. data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_sprites.scssc +0 -0
  103. data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_tables.scssc +0 -0
  104. data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_hover-link.scssc +0 -0
  105. data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_link-colors.scssc +0 -0
  106. data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_unstyled-link.scssc +0 -0
  107. data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_links.scssc +0 -0
  108. data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_lists.scssc +0 -0
  109. data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_text.scssc +0 -0
  110. data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_vertical_rhythm.scssc +0 -0
  111. data/spec/internal/tmp/cache/sass/e2e5b9bb57a9d1b018b9f546bc8b16f6b2a627a9/_compass.scssc +0 -0
  112. data/spec/internal/tmp/cache/sass/e654955d4502954a409bf21071c0d98eb4ac2e9f/_utilities.scssc +0 -0
  113. data/spec/javascripts/UploadSpec.js +75 -0
  114. data/spec/javascripts/helpers/SpecHelper.js +9 -0
  115. data/spec/javascripts/support/jasmine.yml +84 -0
  116. data/spec/unit/upload_controller_spec.rb +31 -0
  117. data/spec/{requests/uploader_spec.rb → unit/upload_spec.rb} +9 -10
  118. data/spec/unit/uploader_module_spec.rb +31 -0
  119. data/vendor/assets/javascripts/s3_multipart/lib.js +456 -0
  120. data/vendor/assets/javascripts/s3_multipart/lib.min.js +1 -0
  121. metadata +103 -12
  122. data/.rspec +0 -1
  123. data/lib/s3_multipart/uploader/config.rb +0 -15
  124. data/spec/internal/app/assets/stylesheets/application.css +0 -0
  125. data/spec/internal/db/combustion_test.sqlite3 +0 -0
  126. data/vendor/assets/javascripts/s3_multipart/index.js +0 -1
  127. data/vendor/assets/javascripts/s3_multipart/s3_multipart.js +0 -478
@@ -0,0 +1,92 @@
1
+ module S3Multipart
2
+
3
+ # Collection of methods to be mixed in to the Upload class.
4
+ # Handle all communication with Amazon S3 servers
5
+ module TransferHelpers
6
+
7
+ def initiate(options)
8
+ real_name = options[:object_name]
9
+ unique_name = UUID.generate + real_name.match(/.[A-Za-z]+$/)[0] # clean this up later
10
+ url = "/#{unique_name}?uploads"
11
+
12
+ headers = {content_type: options[:content_type]}
13
+ headers[:authorization], headers[:date] = sign_request verb: 'POST', url: url, content_type: options[:content_type]
14
+
15
+ response = Http.post url, {headers: headers}
16
+ parsed_response_body = XmlSimple.xml_in(response.body)
17
+
18
+ return { "key" => parsed_response_body["Key"][0],
19
+ "upload_id" => parsed_response_body["UploadId"][0],
20
+ "name" => real_name }
21
+ end
22
+
23
+ def sign_batch(options)
24
+ parts = options[:content_lengths].split('-').each_with_index.map do |len, i|
25
+ sign_part(options.merge!({content_length: len, part_number: i+1}))
26
+ end
27
+ end
28
+
29
+ def sign_part(options)
30
+ url = "/#{options[:object_name]}?partNumber=#{options[:part_number]}&uploadId=#{options[:upload_id]}"
31
+ authorization, date = sign_request verb: 'PUT', url: url, content_length: options[:content_length]
32
+
33
+ return {authorization: authorization, date: date}
34
+ end
35
+
36
+ def complete(options)
37
+ options[:content_type] = "application/xml"
38
+
39
+ url = "/#{options[:object_name]}?uploadId=#{options[:upload_id]}"
40
+
41
+ body = format_part_list_in_xml(options)
42
+ headers = { content_type: options[:content_type],
43
+ content_length: options[:content_length] }
44
+
45
+ headers[:authorization], headers[:date] = sign_request verb: 'POST', url: url, content_type: options[:content_type]
46
+
47
+ response = Http.post url, {headers: headers, body: body}
48
+ parsed_response_body = XmlSimple.xml_in(response.body)
49
+
50
+ begin
51
+ return { location: parsed_response_body["Location"][0] }
52
+ rescue NoMethodError
53
+ return { error: "Upload does not exist"} if parsed_response_body["Message"].first.match("The specified upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.")
54
+ end
55
+ end
56
+
57
+ def sign_request(options)
58
+ #options.default = ""
59
+ time = Time.now.strftime("%a, %d %b %Y %T %Z")
60
+
61
+ return [calculate_authorization_hash(time, options), time]
62
+ end
63
+
64
+ private
65
+
66
+ def calculate_authorization_hash(time, options)
67
+ date = String.new(time)
68
+ date.insert(0, "\nx-amz-date:") if from_upload_part?(options) && options[:parts].nil?
69
+
70
+ unsigned_request = "#{options[:verb]}\n\n#{options[:content_type]}\n#{date}\n/#{Config.instance.bucket_name}#{options[:url]}"
71
+ signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha1', Config.instance.s3_secret_key, unsigned_request))
72
+
73
+ authorization = "AWS" + " " + Config.instance.s3_access_key + ":" + signature
74
+ end
75
+
76
+ def from_upload_part?(options)
77
+ options[:content_length].to_s.match(/^[0-9]+$/) ? true : false
78
+ end
79
+
80
+ def format_part_list_in_xml(options)
81
+ hash = Hash["Part", ""];
82
+ hash["Part"] = options[:parts].map do |part|
83
+ { "PartNumber" => part[:partNum], "ETag" => part[:ETag] }
84
+ end
85
+ hash["Part"].sort_by! {|obj| obj["PartNumber"]}
86
+
87
+ XmlSimple.xml_out(hash, { :RootName => "CompleteMultipartUpload", :AttrPrefix => true })
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,17 @@
1
+ module S3Multipart
2
+ module Uploader
3
+ module Callbacks
4
+
5
+ attr_accessor :on_begin_callback, :on_complete_callback
6
+
7
+ def on_begin(&block)
8
+ self.on_begin_callback = block
9
+ end
10
+
11
+ def on_complete(&block)
12
+ self.on_complete_callback = block
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module S3Multipart
2
+ module Uploader
3
+ module Validations
4
+
5
+ # To do
6
+
7
+ end
8
+ end
9
+ end
@@ -1,85 +1,44 @@
1
+ require "s3_multipart/uploader/callbacks"
2
+ require "s3_multipart/uploader/validations"
3
+ require 'active_support/core_ext/string'
4
+ require "digest/sha1"
5
+
1
6
  module S3Multipart
2
7
  module Uploader
3
- def initiate(options)
4
- url = "/#{options[:object_name]}?uploads"
5
-
6
- headers = {content_type: options[:content_type]}
7
- headers[:authorization], headers[:date] = sign_request verb: 'POST', url: url, content_type: options[:content_type]
8
-
9
- response = Http.post url, {headers: headers}
10
- parsed_response_body = XmlSimple.xml_in(response.body)
11
8
 
12
- return { "key" => parsed_response_body["Key"][0],
13
- "upload_id" => parsed_response_body["UploadId"][0],
14
- "name" => options[:object_name] }
9
+ class << self
10
+ attr_accessor :controllers
15
11
  end
16
12
 
17
- def sign_batch(options)
18
- parts = options[:content_lengths].split('-').each_with_index.map do |len, i|
19
- sign_part(options.merge!({content_length: len, part_number: i+1}))
20
- end
21
- end
22
-
23
- def sign_part(options)
24
- url = "/#{options[:object_name]}?partNumber=#{options[:part_number]}&uploadId=#{options[:upload_id]}"
25
- authorization, date = sign_request verb: 'PUT', url: url, content_length: options[:content_length]
26
-
27
- return {authorization: authorization, date: date}
28
- end
29
-
30
- def complete(options)
31
- options[:content_type] = "application/xml"
32
-
33
- url = "/#{options[:object_name]}?uploadId=#{options[:upload_id]}"
13
+ self.controllers = {}
34
14
 
35
- body = format_part_list_in_xml(options)
36
- headers = { content_type: options[:content_type],
37
- content_length: options[:content_length] }
38
-
39
- headers[:authorization], headers[:date] = sign_request verb: 'POST', url: url, content_type: options[:content_type]
40
-
41
- response = Http.post url, {headers: headers, body: body}
42
- response.body
43
- parsed_response_body = XmlSimple.xml_in(response.body)
44
-
45
- begin
46
- return { location: parsed_response_body["Location"][0] }
47
- rescue NoMethodError
48
- return { error: "Upload does not exist"} if parsed_response_body["Message"].first.match("The specified upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.")
49
- end
15
+ def self.serialize(controller)
16
+ controllers[controller.to_s.to_sym]
50
17
  end
51
18
 
52
- def sign_request(options)
53
- #options.default = ""
54
- time = Time.now.strftime("%a, %d %b %Y %T %Z")
55
-
56
- return [calculate_authorization_hash(time, options), time]
19
+ # What is wrong with this method?
20
+ def self.deserialize(digest)
21
+ controllers.key(digest).to_s.constantize
57
22
  end
58
23
 
59
- private
24
+ # Generated multipart upload controllers (which reside in the app/uploaders/multipart
25
+ # directory in the Rails application) extend this module.
26
+ module Core
60
27
 
61
- def calculate_authorization_hash(time, options)
62
- date = String.new(time)
63
- date.insert(0, "\nx-amz-date:") if from_upload_part?(options) && options[:parts].nil?
28
+ include S3Multipart::Uploader::Callbacks
29
+ include S3Multipart::Uploader::Validations
64
30
 
65
- unsigned_request = "#{options[:verb]}\n\n#{options[:content_type]}\n#{date}\n/#{Config.instance.bucket_name}#{options[:url]}"
66
- signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha1', Config.instance.s3_secret_key, unsigned_request))
67
-
68
- authorization = "AWS" + " " + Config.instance.s3_access_key + ":" + signature
69
- end
70
-
71
- def from_upload_part?(options)
72
- options[:content_length].to_s.match(/^[0-9]+$/) ? true : false
73
- end
31
+ def self.extended(klass)
32
+ Uploader.controllers[klass.to_s.to_sym] = Digest::SHA1.hexdigest(klass.to_s)
33
+ end
74
34
 
75
- def format_part_list_in_xml(options)
76
- hash = Hash["Part", ""];
77
- hash["Part"] = options[:parts].map do |part|
78
- { "PartNumber" => part[:partNum], "ETag" => part[:ETag] }
35
+ def attach(model)
36
+ S3Multipart::Upload.class_eval do
37
+ has_one(model)
38
+ end
79
39
  end
80
- hash["Part"].sort_by! {|obj| obj["PartNumber"]}
81
40
 
82
- XmlSimple.xml_out(hash, { :RootName => "CompleteMultipartUpload", :AttrPrefix => true })
83
41
  end
84
- end
42
+
43
+ end
85
44
  end
@@ -1,3 +1,3 @@
1
1
  module S3Multipart
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/s3_multipart.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'active_record'
3
+ # require 'active_record'
4
4
  require 'xmlsimple'
5
5
  require 'uuid'
6
6
 
@@ -8,16 +8,8 @@ module S3Multipart
8
8
 
9
9
  class << self
10
10
 
11
- # Syntax:
12
- #
13
- # S3_Multipart.configure do |config|
14
- # config.s3_access_key = ''
15
- # config.s3_secret_key = ''
16
- # config.bucket_name = ''
17
- # end
18
-
19
11
  def configure(&block)
20
- S3Multipart::Uploader::Config.configure(block)
12
+ S3Multipart::Config.configure(block)
21
13
  end
22
14
 
23
15
  def remove_unfinished_uploads(seconds=60*60*24*10)
@@ -26,41 +18,11 @@ module S3Multipart
26
18
 
27
19
  end
28
20
 
29
- module ActionControllerHelpers
30
-
31
- module AttachUploader
32
- def self.on_begin(&block)
33
- S3Multipart::Upload.class_eval do
34
- self.on_begin_callback = block
35
- def on_begin
36
- Upload.on_begin_callback.call(self)
37
- end
38
- end
39
- end
40
-
41
- def self.on_complete(&block)
42
- S3Multipart::Upload.class_eval do
43
- self.on_complete_callback = block
44
- def on_complete
45
- Upload.on_complete_callback.call(self)
46
- end
47
- end
48
- end
49
- end
50
-
51
- def attach_uploader
52
- return AttachUploader
53
- end
54
-
55
- end
56
-
57
21
  end
58
22
 
23
+ require 's3_multipart/config'
59
24
  require 's3_multipart/railtie'
60
25
  require 's3_multipart/engine'
61
26
  require 's3_multipart/http/net_http'
62
27
  require 's3_multipart/uploader'
63
- require 's3_multipart/uploader/config'
64
-
65
-
66
- ActionController::Base.send(:include, S3Multipart::ActionControllerHelpers)
28
+ require 's3_multipart/transfer_helpers'
data/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "s3_multipart",
3
+ "version": "0.0.6",
4
+ "devDependencies": {
5
+ "grunt": "latest",
6
+ "grunt-contrib-uglify": "latest",
7
+ "grunt-jasmine-runner": "latest"
8
+ }
9
+ }
data/s3_multipart.gemspec CHANGED
@@ -2,15 +2,21 @@
2
2
 
3
3
  $:.push File.expand_path("../lib", __FILE__)
4
4
  require 's3_multipart/version'
5
+ require 'date'
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = "s3_multipart"
9
+ s.date = Date.today
8
10
  s.version = S3Multipart::VERSION
9
11
  s.authors = ["Max Gillett"]
10
12
  s.email = ["max.gillett@gmail.com"]
11
13
  s.homepage = "https://github.com/maxgillett/s3_multipart"
12
14
  s.summary = %q{Upload directly to S3 using multipart uploading}
13
15
  s.description = %q{See github for installation and configuration }
16
+ s.extra_rdoc_files = ["README.md"]
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.require_paths = ["lib"]
14
20
 
15
21
  s.add_dependency "uuid", ">= 2.3.6"
16
22
  s.add_dependency "xml-simple", ">= 1.1.2"
@@ -21,7 +27,4 @@ Gem::Specification.new do |s|
21
27
  s.add_development_dependency 'rspec'
22
28
  s.add_development_dependency 'rspec-rails'
23
29
  s.add_development_dependency 'capybara'
24
-
25
- s.files = `git ls-files`.split("\n")
26
- s.require_paths = ["lib"]
27
30
  end
@@ -1,8 +1,9 @@
1
1
  require 'spec_helper.rb'
2
+ require "digest/sha1"
2
3
 
3
4
  describe "Uploads controller" do
4
5
  it "should create an upload" do
5
- post '/s3_multipart/uploads', {object_name: "example_object", content_type: "video/x-ms-wmv"}
6
+ post '/s3_multipart/uploads', {object_name: "example_object", content_type: "video/x-ms-wmv", uploader: Digest::SHA1.hexdigest("VideoUploader")}
6
7
  parsed_body = JSON.parse(response.body)
7
8
  parsed_body.should_not eq({"error"=>"There was an error initiating the upload"})
8
9
  end
@@ -12,83 +12,168 @@
12
12
  //
13
13
  //= require underscore
14
14
  //= require_tree .
15
- //= require s3_multipart/s3_multipart
15
+ //= require jquery.ui.progressbar
16
+ //= require s3_multipart/lib
16
17
 
17
18
  $(function() {
18
- $(".submit-button").click(function() {
19
- window.SS3MP = new window.S3MP({
20
- bucket: 'bitcast-bucket',
21
- fileSelector: "#uploader",
22
- onStart: function(num) {
23
- console.log("File "+num+" has started uploading")
24
- },
25
- onComplete: function(num) {
26
- console.log("File "+num+" successfully uploaded")
27
- },
28
- onPause: function(num) {
29
- console.log("File "+num+" has been paused")
30
- },
31
- onCancel: function(num) {
32
- console.log("File upload "+num+" was canceled")
33
- },
34
- onError: function() {
35
- console.log("There was an error")
36
- },
37
- onProgress: function(num, size, done, percent, speed) {
38
- console.log("File %d is %f percent done (%f of %f total) and uploading at %s", num, percent, done, size, speed);
19
+ var file_list, s3mp;
20
+
21
+ $(".submit-button").click(function() {
22
+ s3mp = new window.S3MP({
23
+ bucket: 'bitcast-bucket',
24
+ fileInputElement: "#uploader",
25
+ fileList: file_list,
26
+ onStart: function(upload) {
27
+ var id = upload.id
28
+ , key = upload.key;
29
+
30
+ // Insert the upload details form if only one file upload is going on
31
+ if (file_list.length === 1) {
32
+ // $.ajax({
33
+ // url: "/videos/"+id+"/settings",
34
+ // cache: false,
35
+ // success: function(html){
36
+ // $(".upload-form").append(html);
37
+ // $('.edit_video').bind('ajax:success', function(evt, data, status, xhr){
38
+ // alert("Video updated successfully");
39
+ // })
40
+ // }
41
+ // });
39
42
  }
40
- });
41
- console.log(SS3MP);
43
+
44
+ // Hide the upload button + list, and insert the progress bar
45
+ $(".upload-wrapper, .upload-list").hide();
46
+ $(".upload-list").after('<div class="progress-bar-'+key+'"></div>')
47
+ $(".progress-bar-"+key).progressbar({ max: 100 })
48
+ .after('<div class="progress-bar-info progress-bar-info-'+key+'"><span class="name">'+upload.name+'</span><span class="speed"></span></div>');
49
+
50
+ console.log("File "+key+" has started uploading")
51
+ },
52
+ onComplete: function(upload) {
53
+ $('.progress-bar-'+upload.key).progressbar({ value: 100 });
54
+ $('.progress-bar-info-'+upload.key)
55
+ .find(".speed").html("100% ("+(upload.size/1000000).toFixed(1)+" MB of "+(upload.size/1000000).toFixed(1)+" MB)");
56
+
57
+ console.log("File "+upload.key+" successfully uploaded")
58
+ },
59
+ onPause: function(key) {
60
+ console.log("File "+key+" has been paused")
61
+ },
62
+ onResume: function(key) {
63
+ console.log("File "+key+" has been resumed")
64
+ },
65
+ onCancel: function(key) {
66
+ console.log("File upload "+key+" was canceled")
67
+ },
68
+ onError: function() {
69
+ console.log("There was an error")
70
+ },
71
+ onProgress: function(key, size, done, percent, speed) {
72
+ $('.progress-bar-'+key).progressbar({ value: percent });
73
+ $('.progress-bar-info-'+key)
74
+ .find(".speed").html(percent.toFixed(1)+"% ("+(done/1000000).toFixed(1)+" MB of "+(size/1000000).toFixed(1)+" MB) at "+(speed/1000).toFixed(0)+" kbps");
75
+ console.log("File %d is %f percent done (%f of %f total) and uploading at %s", key, percent, done, size, speed);
76
+ }
42
77
  });
78
+ });
79
+
80
+ // Empty array to store all the files (cannot store in FileList b/c it is read only)
81
+ file_list = [];
82
+
83
+ // Code to handle upload buttons + the upload list
84
+ (function() {
85
+ var uploader, cbs;
86
+
87
+ // Reference to uploader file element
88
+ uploader = document.getElementById("uploader");
89
+
90
+ // Callback functions
91
+ cbs = {
92
+
93
+ moveFileInputEl: _.throttle(function(e) {
94
+ var offset = { left: $(uploader).parent().offset().left, top: $(uploader).parent().offset().top };
95
+ uploader.style.left = e.pageX - offset.left - 100 + "px";
96
+ uploader.style.top = e.pageY - offset.top - 5 + "px";
97
+ },50),
98
+
99
+ addActiveClass: function() {
100
+ $(".upload-button").addClass("active");
101
+ },
43
102
 
103
+ removeActiveClass: function() {
104
+ $(".upload-button").removeClass("active");
105
+ },
44
106
 
45
- // (function() {
46
- // var uploader, adjust_uploader_position, reset_uploader_position
107
+ updateFileList: function() {
108
+ var upload_list, clone, size, num;
47
109
 
48
- // uploader = document.getElementById("uploader");
110
+ $("#uploader, .upload-button").hide();
111
+ $(".submit-button").show();
49
112
 
50
- // // Callback functions
51
- // mousemove_fn = _.throttle(function(e) {
52
- // uploader.style.left = e.pageX-280 +"px";
53
- // uploader.style.top = "0px";
54
- // },10);
55
- // mouseover_fn = function() {
56
- // $(".upload-button").addClass("active");
57
- // };
58
- // mouseleave_fn = function() {
59
- // $(".upload-button").removeClass("active");
60
- // }
61
- // change_fn = function() {
62
- // var upload_list, clone, size;
113
+ upload_list = $(".upload-list")
114
+ upload_list.show();
63
115
 
64
- // $(".upload-wrapper").hide();
65
- // $(".submit-button").show();
116
+ _.each($("#uploader").get(0).files, function(val, key, list) {
117
+ file_list.push(val);
118
+
119
+ size = (val.size/1000000).toFixed(1);
120
+
121
+ if (upload_list.find("li").length === 2 && upload_list.find("li").data("num") === undefined) {
122
+ clone = upload_list.find("li:first");
123
+ } else {
124
+ clone = upload_list.find("li:first").clone()
125
+ }
126
+
127
+ clone.find(".name").text(val.name);
128
+ clone.find(".size").text(size + " MB");
129
+
130
+ clone.insertBefore(".upload-list ul .select-another-video").attr("data-num", key);
131
+ });
132
+
133
+ num = file_list.length
134
+ if (num > 1) {
135
+ $(".upload-list .total").text(num+" files");
136
+ }
137
+ },
138
+
139
+ removeFile: function(e) {
140
+ var li = $(this).parent();
141
+ file_list[li.data("num")] = null;
142
+ li.remove();
143
+
144
+ // Update "total" span element
145
+ var num = _.without(file_list, null).length;
146
+ if (num > 1) {
147
+ $(".upload-list .total").text(num+" files");
148
+ }
149
+ if (num == 1) {
150
+ $(".upload-list .total").text("1 file");
151
+ }
152
+ },
66
153
 
67
- // upload_list = $(".upload-list")
68
- // upload_list.show();
154
+ pauseAll: function(e) {
155
+ _.each(file_list, function(file, key) {
156
+ s3mp.pause(key);
157
+ });
158
+ },
69
159
 
70
- // _.each($("#uploader").get(0).files, function(val, key, list) {
71
- // size = (val.size/1000000).toFixed(2);
160
+ resumeAll: function(e) {
161
+ _.each(file_list, function(file, key) {
162
+ s3mp.resume(key);
163
+ });
164
+ }
72
165
 
73
- // if (upload_list.find("li").length === 1) {
74
- // clone = upload_list.find("li");
75
- // } else {
76
- // clone = upload_list.find("li:first").clone()
77
- // }
166
+ }
78
167
 
79
- // clone.find(".name").text(val.name)
80
- // clone.find(".size").text(size + " mb")
168
+ $(".upload-wrapper")
169
+ .mouseover(cbs.addActiveClass)
170
+ .mouseleave(cbs.removeActiveClass)
171
+ .mousemove(cbs.moveFileInputEl)
172
+ .live('change',cbs.updateFileList);
81
173
 
82
- // upload_list.find("ul").append(clone)
83
- // });
84
- // }
174
+ $(".upload-list").on("click", ".delete", cbs.removeFile);
85
175
 
86
- // $(".upload-wrapper")
87
- // .mouseover(mouseover_fn)
88
- // .mouseleave(mouseleave_fn)
89
- // .mousemove(mousemove_fn)
90
- // .live('change',change_fn);
176
+ })();
91
177
 
92
- // })();
178
+ });
93
179
 
94
- });