carrierwave_direct 0.0.1

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