carrierwave_direct 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +356 -0
  5. data/Rakefile +12 -0
  6. data/carrierwave_direct.gemspec +31 -0
  7. data/lib/carrierwave_direct.rb +44 -0
  8. data/lib/carrierwave_direct/action_view_extensions/form_helper.rb +36 -0
  9. data/lib/carrierwave_direct/form_builder.rb +17 -0
  10. data/lib/carrierwave_direct/locale/en.rb +20 -0
  11. data/lib/carrierwave_direct/locale/en.yml +7 -0
  12. data/lib/carrierwave_direct/mount.rb +38 -0
  13. data/lib/carrierwave_direct/orm/activerecord.rb +55 -0
  14. data/lib/carrierwave_direct/test/capybara_helpers.rb +58 -0
  15. data/lib/carrierwave_direct/test/helpers.rb +32 -0
  16. data/lib/carrierwave_direct/uploader.rb +142 -0
  17. data/lib/carrierwave_direct/uploader/configuration.rb +38 -0
  18. data/lib/carrierwave_direct/validations/active_model.rb +126 -0
  19. data/lib/carrierwave_direct/version.rb +6 -0
  20. data/spec/action_view_extensions/form_helper_spec.rb +28 -0
  21. data/spec/form_builder_spec.rb +59 -0
  22. data/spec/mount_spec.rb +57 -0
  23. data/spec/orm/activerecord_spec.rb +551 -0
  24. data/spec/spec_helper.rb +5 -0
  25. data/spec/support/carrier_wave_config.rb +9 -0
  26. data/spec/support/direct_uploader.rb +4 -0
  27. data/spec/support/form_builder_helpers.rb +36 -0
  28. data/spec/support/global_helpers.rb +6 -0
  29. data/spec/support/model_helpers.rb +80 -0
  30. data/spec/support/mounted_class.rb +6 -0
  31. data/spec/support/uploader_helpers.rb +8 -0
  32. data/spec/support/view_helpers.rb +45 -0
  33. data/spec/test/capybara_helpers_spec.rb +160 -0
  34. data/spec/test/helpers_spec.rb +105 -0
  35. data/spec/uploader_spec.rb +461 -0
  36. metadata +168 -0
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+
5
+ module ActionViewExtensions
6
+ # This module creates direct upload forms to post to cloud services
7
+ #
8
+ # Example:
9
+ #
10
+ # direct_upload_form_for @video_uploader do |f|
11
+ # f.file_field :video
12
+ # f.submit
13
+ # end
14
+ #
15
+ module FormHelper
16
+
17
+ def direct_upload_form_for(record, *args, &block)
18
+ options = args.extract_options!
19
+ form_for(
20
+ record,
21
+ *(args << options.merge(
22
+ :builder => CarrierWaveDirect::FormBuilder,
23
+ :url => record.direct_fog_url,
24
+ :html => {:multipart => true},
25
+ :authenticity_token => false,
26
+ :include_id => false
27
+ )),
28
+ &block
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ ActionView::Base.send :include, CarrierWaveDirect::ActionViewExtensions::FormHelper
36
+
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+ class FormBuilder < ActionView::Helpers::FormBuilder
5
+ def file_field(method, options = {})
6
+ options.merge!(:name => "file")
7
+ hidden_field(:key, :name => "key") <<
8
+ hidden_field(:aws_access_key_id, :name => "AWSAccessKeyId") <<
9
+ hidden_field(:acl, :name => "acl") <<
10
+ hidden_field(:success_action_redirect, :name => "success_action_redirect") <<
11
+ hidden_field(:policy, :name => "policy") <<
12
+ hidden_field(:signature, :name => "signature") <<
13
+ super
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,20 @@
1
+ {
2
+ :en => {
3
+ :errors => {
4
+ :messages => {
5
+ :carrierwave_direct_filename_invalid => lambda {|key, options|
6
+ message = "is invalid"
7
+ message << ". Allowed file types are #{options[:extension_white_list].to_sentence}" if options[:extension_white_list] && options[:extension_white_list].any?
8
+ message
9
+ },
10
+ :carrierwave_direct_remote_net_url_invalid => lambda {|key, options|
11
+ message = "is invalid"
12
+ message << ". Allowed file types are #{options[:extension_white_list].to_sentence}" if options[:extension_white_list] && options[:extension_white_list].any?
13
+ message << ". Allowed url schemes are #{options[:url_scheme_white_list].to_sentence}" if options[:url_scheme_white_list] && options[:url_scheme_white_list].any?
14
+ message
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
20
+
@@ -0,0 +1,7 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ carrierwave_direct_filename_taken: filename was already taken
5
+ carrierwave_direct_upload_missing: upload is missing
6
+ carrierwave_direct_attachment_missing: attachment is missing
7
+
@@ -0,0 +1,38 @@
1
+ module CarrierWaveDirect
2
+
3
+ module Mount
4
+ def mount_uploader(column, uploader=nil, options={}, &block)
5
+ super
6
+
7
+ uploader.class_eval <<-RUBY, __FILE__, __LINE__+1
8
+ def #{column}; self; end
9
+ RUBY
10
+
11
+ self.instance_eval <<-RUBY, __FILE__, __LINE__+1
12
+ attr_accessor :remote_#{column}_net_url
13
+ RUBY
14
+
15
+ mod = Module.new
16
+ include mod
17
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
18
+
19
+ def key
20
+ send(:#{column}).key
21
+ end
22
+
23
+ def key=(k)
24
+ send(:#{column}).key = k
25
+ end
26
+
27
+ def has_#{column}_upload?
28
+ send(:#{column}).has_key?
29
+ end
30
+
31
+ def has_remote_#{column}_net_url?
32
+ send(:remote_#{column}_net_url).present?
33
+ end
34
+ RUBY
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'active_record'
4
+ require 'carrierwave_direct/validations/active_model'
5
+
6
+ module CarrierWaveDirect
7
+ module ActiveRecord
8
+ include CarrierWaveDirect::Mount
9
+
10
+ def mount_uploader(column, uploader=nil, options={}, &block)
11
+ super
12
+
13
+ uploader.instance_eval <<-RUBY, __FILE__, __LINE__+1
14
+ include ActiveModel::Conversion
15
+ extend ActiveModel::Naming
16
+ RUBY
17
+
18
+ include CarrierWaveDirect::Validations::ActiveModel
19
+
20
+ validates_is_attached column if uploader_option(column.to_sym, :validate_is_attached)
21
+ validates_is_uploaded column if uploader_option(column.to_sym, :validate_is_uploaded)
22
+ validates_filename_uniqueness_of column if uploader_option(column.to_sym, :validate_unique_filename)
23
+ validates_filename_format_of column if uploader_option(column.to_sym, :validate_filename_format)
24
+ validates_remote_net_url_format_of column if uploader_option(column.to_sym, :validate_remote_net_url_format)
25
+
26
+ self.instance_eval <<-RUBY, __FILE__, __LINE__+1
27
+ attr_accessor :skip_is_attached_validations
28
+ attr_accessible :key, :remote_#{column}_net_url
29
+ RUBY
30
+
31
+ mod = Module.new
32
+ include mod
33
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
34
+ def filename_valid?
35
+ if has_#{column}_upload?
36
+ self.skip_is_attached_validations = true
37
+ valid?
38
+ self.skip_is_attached_validations = false
39
+ column_errors = errors[:#{column}]
40
+ errors.clear
41
+ column_errors.each do |column_error|
42
+ errors.add(:#{column}, column_error)
43
+ end
44
+ errors.empty?
45
+ else
46
+ true
47
+ end
48
+ end
49
+ RUBY
50
+ end
51
+ end
52
+ end
53
+
54
+ ActiveRecord::Base.extend CarrierWaveDirect::ActiveRecord
55
+
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+ module Test
5
+ module CapybaraHelpers
6
+
7
+ include CarrierWaveDirect::Test::Helpers
8
+
9
+ def attach_file_for_direct_upload(path)
10
+ attach_file("file", path)
11
+ end
12
+
13
+ def find_key
14
+ key = page.find("input[@name='key']").value
15
+ end
16
+
17
+ def find_upload_path
18
+ page.find("input[@name='file']").value
19
+ end
20
+
21
+ def upload_directly(uploader, button_locator, options = {})
22
+ options[:success] = true unless options[:success] == false
23
+ options[:success] &&= !options[:fail]
24
+
25
+ if options[:success]
26
+ # simulate a successful upload
27
+
28
+ # form's success action redirect url
29
+ redirect_url = URI.parse(page.find("input[@name='success_action_redirect']").value)
30
+
31
+ unless options[:redirect_key]
32
+ sample_key_args = [{:base => find_key, :filename => File.basename(find_upload_path)}]
33
+ sample_key_args.unshift(uploader) if method(:sample_key).arity == -2
34
+ options[:redirect_key] = sample_key(*sample_key_args)
35
+ end
36
+
37
+ redirect_url.query = Rack::Utils.build_nested_query({
38
+ :bucket => uploader.fog_directory,
39
+ :key => options[:redirect_key],
40
+ :etag => "\"d41d8cd98f00b204e9800998ecf8427\""
41
+ })
42
+
43
+ # click the button
44
+ click_button button_locator
45
+
46
+ # simulate success redirect
47
+ visit redirect_url.to_s
48
+ else
49
+ # simulate an unsuccessful upload
50
+
51
+ # click the button
52
+ click_button button_locator
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+ module Test
5
+ module Helpers
6
+ # Example usage:
7
+
8
+ # sample_key(ImageUploader, :base => "store_dir/guid/${filename}")
9
+ # => "store_dir/guid/filename.extension"
10
+
11
+ def sample_key(uploader, options = {})
12
+ options[:valid] = true unless options[:valid] == false
13
+ options[:valid] &&= !options[:invalid]
14
+ options[:base] ||= uploader.key
15
+ if options[:filename]
16
+ filename_parts = options[:filename].split(".")
17
+ options[:extension] = filename_parts.pop if filename_parts.size > 1
18
+ options[:filename] = filename_parts.join(".")
19
+ end
20
+ options[:filename] ||= "filename"
21
+ valid_extension = uploader.extension_white_list.first if uploader.extension_white_list
22
+ options[:extension] = options[:extension] ? options[:extension].gsub(".", "") : (valid_extension || "extension")
23
+ key = options[:base].split("/")
24
+ key.pop
25
+ key.pop unless options[:valid]
26
+ key << "#{options[:filename]}.#{options[:extension]}"
27
+ key.join("/")
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+ module Uploader
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ FILENAME_WILDCARD = "${filename}"
9
+
10
+ included do
11
+ storage :fog
12
+
13
+ attr_accessor :success_action_redirect
14
+
15
+ fog_credentials.keys.each do |key|
16
+ define_method(key) do
17
+ fog_credentials[key]
18
+ end
19
+ end
20
+ end
21
+
22
+ module InstanceMethods
23
+ def direct_fog_url(options = {})
24
+ fog_uri = CarrierWave::Storage::Fog::File.new(self, nil, nil).public_url
25
+ if options[:with_path]
26
+ uri = URI.parse(fog_uri)
27
+ path = "/#{key}"
28
+ uri.path = path
29
+ fog_uri = uri.to_s
30
+ end
31
+ fog_uri
32
+ end
33
+
34
+ def guid
35
+ UUID.generate
36
+ end
37
+
38
+ def key=(k)
39
+ @key = k
40
+ update_version_keys(:with => @key)
41
+ end
42
+
43
+ def key
44
+ @key ||= "#{store_dir}/#{guid}/#{FILENAME_WILDCARD}"
45
+ end
46
+
47
+ def has_key?
48
+ @key.present? && !(@key =~ /#{Regexp.escape(FILENAME_WILDCARD)}\z/)
49
+ end
50
+
51
+ def acl
52
+ s3_access_policy.to_s.gsub('_', '-')
53
+ end
54
+
55
+ def policy(options = {})
56
+ options[:expiration] ||= self.class.upload_expiration
57
+ options[:max_file_size] ||= self.class.max_file_size
58
+
59
+ Base64.encode64(
60
+ {
61
+ 'expiration' => Time.now + options[:expiration],
62
+ 'conditions' => [
63
+ ["starts-with", "$utf8", ""],
64
+ ["starts-with", "$key", store_dir],
65
+ {"bucket" => fog_directory},
66
+ {"acl" => acl},
67
+ {"success_action_redirect" => success_action_redirect},
68
+ ["content-length-range", 1, options[:max_file_size]]
69
+ ]
70
+ }.to_json
71
+ ).gsub("\n","")
72
+ end
73
+
74
+ def signature
75
+ Base64.encode64(
76
+ OpenSSL::HMAC.digest(
77
+ OpenSSL::Digest::Digest.new('sha1'),
78
+ aws_secret_access_key, policy
79
+ )
80
+ ).gsub("\n","")
81
+ end
82
+
83
+ def persisted?
84
+ false
85
+ end
86
+
87
+ def filename
88
+ unless has_key?
89
+ # Use the attached models remote url to generate a new key otherwise return nil
90
+ remote_url = model.send("remote_#{mounted_as}_url")
91
+ remote_url ? key_from_file(remote_url.split("/").pop) : return
92
+ end
93
+
94
+ key_path = key.split("/")
95
+ filename_parts = []
96
+ filename_parts.unshift(key_path.pop)
97
+ unique_key = key_path.pop
98
+ filename_parts.unshift(unique_key) if unique_key
99
+ filename_parts.join("/")
100
+ end
101
+
102
+ def key_regexp
103
+ /\A#{store_dir}\/[a-f\d\-]+\/.+\.#{extension_regexp}\z/
104
+ end
105
+
106
+ def extension_regexp
107
+ allowed_file_types = extension_white_list
108
+ extension_regexp = allowed_file_types.present? && allowed_file_types.any? ? "(#{allowed_file_types.join("|")})" : "\\w+"
109
+ end
110
+
111
+ def url_scheme_white_list
112
+ nil
113
+ end
114
+
115
+ private
116
+
117
+ def key_from_file(fname)
118
+ new_key_parts = key.split("/")
119
+ new_key_parts.pop
120
+ new_key_parts << fname
121
+ self.key = new_key_parts.join("/")
122
+ end
123
+
124
+ # Update the versions to use this key
125
+ def update_version_keys(options)
126
+ versions.each do |name, uploader|
127
+ uploader.key = options[:with]
128
+ end
129
+ end
130
+ end # InstanceMethods
131
+
132
+ private
133
+
134
+ # Put the version name at the end of the filename since the guid is also stored
135
+ # e.g. guid/filename_thumb.jpg instead of CarrierWave's default: thumb_guid/filename.jpg
136
+ def full_filename(for_file)
137
+ extname = File.extname(for_file)
138
+ [for_file.chomp(extname), version_name].compact.join('_') << extname
139
+ end
140
+ end
141
+ end
142
+
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWaveDirect
4
+
5
+ module Uploader
6
+ module Configuration
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ add_config :validate_is_attached
11
+ add_config :validate_is_uploaded
12
+ add_config :validate_unique_filename
13
+ add_config :validate_filename_format
14
+ add_config :validate_remote_net_url_format
15
+
16
+ add_config :max_file_size
17
+ add_config :upload_expiration
18
+ reset_direct_config
19
+ end
20
+
21
+ module ClassMethods
22
+ def reset_direct_config
23
+ configure do |config|
24
+ config.validate_is_attached = false
25
+ config.validate_is_uploaded = false
26
+ config.validate_unique_filename = true
27
+ config.validate_filename_format = true
28
+ config.validate_remote_net_url_format = true
29
+
30
+ config.max_file_size = 5242880
31
+ config.upload_expiration = 36000
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+