carrierwave_direct 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/Rakefile +12 -0
- data/carrierwave_direct.gemspec +31 -0
- data/lib/carrierwave_direct.rb +44 -0
- data/lib/carrierwave_direct/action_view_extensions/form_helper.rb +36 -0
- data/lib/carrierwave_direct/form_builder.rb +17 -0
- data/lib/carrierwave_direct/locale/en.rb +20 -0
- data/lib/carrierwave_direct/locale/en.yml +7 -0
- data/lib/carrierwave_direct/mount.rb +38 -0
- data/lib/carrierwave_direct/orm/activerecord.rb +55 -0
- data/lib/carrierwave_direct/test/capybara_helpers.rb +58 -0
- data/lib/carrierwave_direct/test/helpers.rb +32 -0
- data/lib/carrierwave_direct/uploader.rb +142 -0
- data/lib/carrierwave_direct/uploader/configuration.rb +38 -0
- data/lib/carrierwave_direct/validations/active_model.rb +126 -0
- data/lib/carrierwave_direct/version.rb +6 -0
- data/spec/action_view_extensions/form_helper_spec.rb +28 -0
- data/spec/form_builder_spec.rb +59 -0
- data/spec/mount_spec.rb +57 -0
- data/spec/orm/activerecord_spec.rb +551 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/carrier_wave_config.rb +9 -0
- data/spec/support/direct_uploader.rb +4 -0
- data/spec/support/form_builder_helpers.rb +36 -0
- data/spec/support/global_helpers.rb +6 -0
- data/spec/support/model_helpers.rb +80 -0
- data/spec/support/mounted_class.rb +6 -0
- data/spec/support/uploader_helpers.rb +8 -0
- data/spec/support/view_helpers.rb +45 -0
- data/spec/test/capybara_helpers_spec.rb +160 -0
- data/spec/test/helpers_spec.rb +105 -0
- data/spec/uploader_spec.rb +461 -0
- 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,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
|
+
|