Skip to content

Commit 8cb25e1

Browse files
Add support for providing upload parameters in CarrierWave
1 parent 6fe3f7c commit 8cb25e1

File tree

3 files changed

+224
-15
lines changed

3 files changed

+224
-15
lines changed

lib/cloudinary/carrier_wave/process.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ def cloudinary_transformation(options)
4848
def tags(*tags)
4949
process :tags=>tags
5050
end
51+
52+
def upload_params(params={})
53+
process :upload_params => params
54+
end
5155
end
5256

5357
def set_or_yell(hash, attr, value)
@@ -135,6 +139,19 @@ def tags
135139
@tags
136140
end
137141

142+
def upload_params
143+
@upload_params ||= begin
144+
params_processors = self.all_processors.select{|processor| processor[0] == :upload_params}
145+
merged_params = {}
146+
params_processors.each do |processor|
147+
merged_params.merge!(processor[1] || {})
148+
end
149+
merged_params
150+
end
151+
raise CloudinaryException, "upload_params cannot be used in versions." if @upload_params.present? && self.version_name.present?
152+
@upload_params
153+
end
154+
138155
def requested_format
139156
format_processor = self.all_processors.find{|processor| processor[0] == :convert}
140157
if format_processor

lib/cloudinary/carrier_wave/storage.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def store!(file)
3535
params[:eager] = eager_versions.map{|version| [version.transformation, version.format]} if eager_versions.length > 0
3636
params[:type]=uploader.class.storage_type
3737

38+
# Merge any custom upload parameters
39+
custom_params = uploader.upload_params
40+
params.merge!(custom_params) if custom_params.present?
41+
3842
params[:resource_type] ||= :auto
3943
upload_method = uploader.respond_to?(:upload_chunked?) && uploader.upload_chunked? ? "upload_large" : "upload"
4044
uploader.metadata = Cloudinary::Uploader.send(upload_method, data, params)

spec/carriewave_spec.rb

Lines changed: 203 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
require 'spec_helper'
22
require 'cloudinary'
33

4+
# Add blank? method for testing
5+
class Object
6+
def blank?
7+
respond_to?(:empty?) ? !!empty? : !self
8+
end unless method_defined?(:blank?)
9+
end
10+
11+
class NilClass
12+
def blank?
13+
true
14+
end unless method_defined?(:blank?)
15+
end
16+
417
module CarrierWave
518
module Storage
619
class Abstract
@@ -11,7 +24,81 @@ def initialize(uploader)
1124
attr_accessor :uploader
1225
end
1326
end
14-
class SanitizedFile; end
27+
28+
class SanitizedFile
29+
def self.sanitize_regexp
30+
/[^a-zA-Z0-9\.\-\+_]/
31+
end
32+
end
33+
34+
module Uploader
35+
class Base
36+
attr_accessor :cache_storage
37+
38+
def self.storage(storage_class)
39+
@storage_class = storage_class
40+
end
41+
42+
def self.cache_storage
43+
@cache_storage
44+
end
45+
46+
def self.cache_storage=(storage)
47+
@cache_storage = storage
48+
end
49+
50+
def self.class_attribute(*attrs, **options)
51+
attrs.each do |attr|
52+
instance_variable_set("@#{attr}", nil)
53+
define_singleton_method attr do |value = nil|
54+
if value
55+
instance_variable_set("@#{attr}", value)
56+
else
57+
instance_variable_get("@#{attr}")
58+
end
59+
end
60+
define_method attr do
61+
self.class.send(attr)
62+
end
63+
define_method "#{attr}=" do |value|
64+
self.class.send(attr, value)
65+
end unless options[:instance_reader] == false
66+
end
67+
end
68+
69+
def self.extend(mod)
70+
super
71+
end
72+
73+
def self.processors
74+
@processors ||= []
75+
end
76+
77+
def self.process(method_name)
78+
processors << [method_name.keys.first, method_name.values.first, nil]
79+
end
80+
81+
def self.version_names
82+
[]
83+
end
84+
85+
def initialize
86+
# Mock initialization
87+
end
88+
89+
def version_name
90+
nil
91+
end
92+
93+
def versions
94+
OpenStruct.new(values: [])
95+
end
96+
97+
def transformation
98+
{}
99+
end
100+
end
101+
end
15102
end
16103

17104
RSpec.describe Cloudinary::CarrierWave do
@@ -31,11 +118,112 @@ class SanitizedFile; end
31118
subject
32119
end
33120
end
121+
122+
describe 'upload parameters' do
123+
class TestUploader < CarrierWave::Uploader::Base
124+
include Cloudinary::CarrierWave
125+
attr_accessor :enable_processing
126+
127+
def initialize(model = nil, mounted_as = nil)
128+
super()
129+
@enable_processing = true
130+
end
131+
end
132+
133+
let(:uploader) { TestUploader.new }
134+
135+
describe '#upload_params class method' do
136+
before do
137+
# Reset processors between tests
138+
TestUploader.instance_variable_set(:@processors, [])
139+
end
140+
141+
it 'allows setting upload parameters' do
142+
TestUploader.upload_params(use_filename: true, overwrite: false)
143+
instance = TestUploader.new
144+
expect(instance.upload_params).to eq(use_filename: true, overwrite: false)
145+
end
146+
147+
it 'merges multiple upload_params calls' do
148+
TestUploader.upload_params(use_filename: true)
149+
TestUploader.upload_params(overwrite: false)
150+
instance = TestUploader.new
151+
expect(instance.upload_params).to eq(use_filename: true, overwrite: false)
152+
end
153+
154+
it 'supports asset_folder parameter' do
155+
TestUploader.upload_params(asset_folder: 'my_project_assets')
156+
instance = TestUploader.new
157+
expect(instance.upload_params).to eq(asset_folder: 'my_project_assets')
158+
end
159+
160+
it 'supports display_name parameter' do
161+
TestUploader.upload_params(display_name: 'Sample Upload Test')
162+
instance = TestUploader.new
163+
expect(instance.upload_params).to eq(display_name: 'Sample Upload Test')
164+
end
165+
166+
it 'combines asset_folder and display_name with other parameters' do
167+
TestUploader.upload_params(asset_folder: 'ecommerce_project')
168+
TestUploader.upload_params(display_name: 'Product Image Upload')
169+
TestUploader.upload_params(use_filename: true)
170+
TestUploader.upload_params(unique_filename: false)
171+
instance = TestUploader.new
172+
expect(instance.upload_params).to eq(
173+
asset_folder: 'ecommerce_project',
174+
display_name: 'Product Image Upload',
175+
use_filename: true,
176+
unique_filename: false
177+
)
178+
end
179+
180+
it 'supports complex upload parameters including metadata and context' do
181+
TestUploader.upload_params(
182+
asset_folder: 'demo_project',
183+
display_name: 'CarrierWave Upload Test',
184+
use_filename: true,
185+
unique_filename: false,
186+
overwrite: true,
187+
context: { category: 'product', source: 'admin_panel' },
188+
metadata: { uploaded_by: 'admin_user', department: 'marketing' }
189+
)
190+
instance = TestUploader.new
191+
expected_params = {
192+
asset_folder: 'demo_project',
193+
display_name: 'CarrierWave Upload Test',
194+
use_filename: true,
195+
unique_filename: false,
196+
overwrite: true,
197+
context: { category: 'product', source: 'admin_panel' },
198+
metadata: { uploaded_by: 'admin_user', department: 'marketing' }
199+
}
200+
expect(instance.upload_params).to eq(expected_params)
201+
end
202+
end
203+
204+
describe '#upload_params instance method' do
205+
it 'returns empty hash when no upload params are set' do
206+
uploader_class = Class.new(CarrierWave::Uploader::Base) do
207+
include Cloudinary::CarrierWave
208+
end
209+
instance = uploader_class.new
210+
expect(instance.upload_params).to eq({})
211+
end
212+
213+
it 'prevents use in versions' do
214+
TestUploader.instance_variable_set(:@processors, [])
215+
TestUploader.upload_params(quality: 'auto')
216+
instance = TestUploader.new
217+
allow(instance).to receive(:version_name).and_return('thumb')
218+
expect { instance.upload_params }.to raise_error(CloudinaryException, "upload_params cannot be used in versions.")
219+
end
220+
end
221+
end
34222
end
35223

36224
RSpec.describe Cloudinary::PreloadedFile do
37225
let(:test_api_secret) { "X7qLTrsES31MzxxkxPPA-pAGGfU" }
38-
226+
39227
before do
40228
Cloudinary.config.update(:api_secret => test_api_secret)
41229
end
@@ -68,45 +256,45 @@ class SanitizedFile; end
68256
# So if filename is "tests/logo.png", public_id becomes "tests/logo"
69257
filename_with_format = public_id
70258
public_id_without_format = "tests/logo" # public_id without .png extension
71-
259+
72260
# Generate a valid signature using the public_id without extension
73261
# The version parsed from preloaded string will be a string, so we use string here too
74262
version_string = test_version.to_s
75263
expected_signature = Cloudinary::Utils.api_sign_request(
76-
{ :public_id => public_id_without_format, :version => version_string },
77-
test_api_secret,
78-
nil,
264+
{ :public_id => public_id_without_format, :version => version_string },
265+
test_api_secret,
266+
nil,
79267
1 # verify_api_response_signature uses version 1
80268
)
81-
82-
# Create a preloaded file string
269+
270+
# Create a preloaded file string
83271
preloaded_string = "image/upload/v#{version_string}/#{filename_with_format}##{expected_signature}"
84272
preloaded_file = Cloudinary::PreloadedFile.new(preloaded_string)
85-
273+
86274
expect(preloaded_file).to be_valid
87275
end
88276

89277
it "should fail verification with incorrect signature" do
90278
wrong_signature = "wrongsignature"
91279
preloaded_string = "image/upload/v#{test_version}/#{public_id}##{wrong_signature}"
92280
preloaded_file = Cloudinary::PreloadedFile.new(preloaded_string)
93-
281+
94282
expect(preloaded_file).not_to be_valid
95283
end
96284

97285
it "should handle raw resource type correctly" do
98286
raw_filename = "document.pdf"
99287
version_string = test_version.to_s
100288
raw_signature = Cloudinary::Utils.api_sign_request(
101-
{ :public_id => raw_filename, :version => version_string },
102-
test_api_secret,
103-
nil,
289+
{ :public_id => raw_filename, :version => version_string },
290+
test_api_secret,
291+
nil,
104292
1
105293
)
106-
294+
107295
preloaded_string = "raw/upload/v#{version_string}/#{raw_filename}##{raw_signature}"
108296
preloaded_file = Cloudinary::PreloadedFile.new(preloaded_string)
109-
297+
110298
expect(preloaded_file).to be_valid
111299
expect(preloaded_file.resource_type).to eq('raw')
112300
end

0 commit comments

Comments
 (0)