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