cloudinary 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36330c17f742e019c7770d39fe11a5e394d69acc
4
- data.tar.gz: 511246947d366e91a8d34095f256437bde533f9e
3
+ metadata.gz: 154b7abbf7ae3c462f1277f49dfed3cfdd4ef60c
4
+ data.tar.gz: 56a961c58cc636da0830ba4e1db3f486fe1dffd4
5
5
  SHA512:
6
- metadata.gz: 4fcbb47f1760342d5e1b36e0438a605524eb20b9f0fac98521846f71102447ce6dda1ebb3f435e43d6227320bb48a65c85f5ae44da20b0c4fcb1539f7d78232a
7
- data.tar.gz: 190f0970236cb76615d6537276fdd3633297e4c749815a6c54ae985188bee5e493d778a55c60798ac3ebaf9cb14930eb2b3589f45914c69cc489d378d5b19970
6
+ metadata.gz: 7fc132e944b87f3bd829f3e45a16609c7e4743a8fa331fad6d62a490a6f1625c533ea26b9eb50d1da71123abc5948166d5b606694b4765562877d255f54f93e9
7
+ data.tar.gz: 5a1ca4908473ea368ed4a2c1753852879fc75bf3dba22819f44637728de83c6a69f0a48482b5729e46d9f582a3c70748deeaad5dc683a8a2fa831ec784c3b302
@@ -1,4 +1,43 @@
1
1
 
2
+ 1.7.0 / 2017-04-09
3
+ ==================
4
+
5
+ New functionality and features
6
+ ------------------------------
7
+
8
+ * Added resource publishing API
9
+ * `Api.publish_by_prefix`
10
+ * `Api.publish_by_tag`
11
+ * `Api.publish_by_ids`
12
+ * Support remote URLs in `Uploader.upload_large` API
13
+ * Add missing parameters to generate-archive
14
+ * `skip_transformation_name`
15
+ * `allow_missing`
16
+ * Added context API methods
17
+ * `Api.add_context`
18
+ * `Api.remove_all_context`
19
+ * Added `Uploader.remove_all_tags` method
20
+ * Support URL SEO suffix for authenticated images
21
+ * Add support of "format" parameter to responsive-breakpoints hash
22
+ * Add notification_url to update API
23
+
24
+
25
+ Other Changes
26
+ -------------
27
+
28
+ * Remove tag from test
29
+ * Change test criteria from changing versions to bytes
30
+ * Use `TRAVIS_JOB_ID` if available or random. Move auth test constants to spec_helper.
31
+ * Add test for deleting public IDs which contain commas
32
+ * Move expression and replacement to constants
33
+ * Don't normalize negative numbers
34
+ * Added generic aliasing to methods named with image
35
+ * Added Private annotation to certain utility methods
36
+ * Add `encode_context` method to `Utils`
37
+ * Escape = and | characters in context values + test
38
+ * Add more complex eager test cases
39
+ * Switch alias_method_chain to alias_method to support Rails version >5.1
40
+
2
41
  1.6.0 / 2017-03-08
3
42
  ==================
4
43
 
@@ -88,7 +88,7 @@ class Cloudinary::Api
88
88
  uri = "resources/#{resource_type}/#{type}/#{public_id}"
89
89
  update_options = {
90
90
  :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
91
- :context => Cloudinary::Utils.encode_hash(options[:context]),
91
+ :context => Cloudinary::Utils.encode_context(options[:context]),
92
92
  :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
93
93
  :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
94
94
  :moderation_status => options[:moderation_status],
@@ -98,7 +98,8 @@ class Cloudinary::Api
98
98
  :detection => options[:detection],
99
99
  :similarity_search => options[:similarity_search],
100
100
  :background_removal => options[:background_removal],
101
- :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f
101
+ :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f,
102
+ :notification_url => options[:notification_url]
102
103
  }
103
104
  call_api(:post, uri, update_options, options)
104
105
  end
@@ -357,6 +358,24 @@ class Cloudinary::Api
357
358
  transformation.is_a?(String) ? transformation : Cloudinary::Utils.generate_transformation_string(transformation.clone)
358
359
  end
359
360
 
361
+ def self.publish_resources(options = {})
362
+ resource_type = options[:resource_type] || "image"
363
+ params = only(options, :public_ids, :prefix, :tag, :type, :overwrite, :invalidate)
364
+ call_api("post", "resources/#{resource_type}/publish_resources", params, options)
365
+ end
366
+
367
+ def self.publish_by_prefix(prefix, options = {})
368
+ return self.publish_resources(options.merge(:prefix => prefix))
369
+ end
370
+
371
+ def self.publish_by_tag(tag, options = {})
372
+ return self.publish_resources(options.merge(:tag => tag))
373
+ end
374
+
375
+ def self.publish_by_ids(publicIds, options = {})
376
+ return self.publish_resources(options.merge(:public_ids => publicIds))
377
+ end
378
+
360
379
  def self.update_resources_access_mode(access_mode, by_key, value, options = {})
361
380
  resource_type = options[:resource_type] || "image"
362
381
  type = options[:type] || "upload"
@@ -75,6 +75,7 @@ module CloudinaryHelper
75
75
  url = cloudinary_url_internal(source, options)
76
76
  image_path_without_cloudinary(url)
77
77
  end
78
+ alias_method :cl_path, :cl_image_path
78
79
 
79
80
  def image_tag_with_cloudinary(*args)
80
81
  source, options = args
@@ -200,6 +201,7 @@ module CloudinaryHelper
200
201
  def cl_unsigned_image_upload(object_name, method, upload_preset, options={})
201
202
  cl_unsigned_image_upload_tag("#{object_name}[#{method}]", upload_preset, options)
202
203
  end
204
+ alias_method :cl_unsigned_upload, :cl_unsigned_image_upload
203
205
 
204
206
  def cl_upload_url(options={})
205
207
  Cloudinary::Utils.cloudinary_api_url("upload", {:resource_type=>:auto}.merge(options))
@@ -236,6 +238,7 @@ module CloudinaryHelper
236
238
  def cl_unsigned_image_upload_tag(field, upload_preset, options={})
237
239
  cl_image_upload_tag(field, options.merge(:unsigned => true, :upload_preset => upload_preset))
238
240
  end
241
+ alias_method :cl_unsigned_upload_tag, :cl_unsigned_image_upload_tag
239
242
 
240
243
  def cl_private_download_url(public_id, format, options = {})
241
244
  Cloudinary::Utils.private_download_url(public_id, format, options)
@@ -268,13 +271,12 @@ module CloudinaryHelper
268
271
  if !method_defined?(:image_tag)
269
272
  include ActionView::Helpers::AssetTagHelper
270
273
  end
271
- if defined?(::Rails::VERSION::MAJOR) && ::Rails::VERSION::MAJOR > 2 && Cloudinary.config.enhance_image_tag
272
- alias_method_chain :image_tag, :cloudinary unless public_method_defined? :image_tag_without_cloudinary
273
- alias_method_chain :image_path, :cloudinary unless public_method_defined? :image_path_without_cloudinary
274
- else
275
- alias_method :image_tag_without_cloudinary, :image_tag unless public_method_defined? :image_tag_without_cloudinary
276
- alias_method :image_path_without_cloudinary, :image_path unless public_method_defined? :image_path_without_cloudinary
277
- end
274
+ alias_method :image_tag_without_cloudinary, :image_tag unless public_method_defined? :image_tag_without_cloudinary
275
+ alias_method :image_path_without_cloudinary, :image_path unless public_method_defined? :image_path_without_cloudinary
276
+ if Cloudinary.config.enhance_image_tag
277
+ alias_method :image_tag, :image_tag_with_cloudinary
278
+ alias_method :image_path, :image_path_with_cloudinary
279
+ end
278
280
  end
279
281
  end
280
282
 
@@ -318,6 +320,7 @@ module Cloudinary::FormBuilder
318
320
  def cl_unsigned_image_upload(method, upload_preset, options={})
319
321
  @template.cl_unsigned_image_upload(@object_name, method, upload_preset, objectify_options(options))
320
322
  end
323
+ alias_method :cl_unsigned_upload, :cl_unsigned_image_upload
321
324
  end
322
325
 
323
326
  if defined? ActionView::Helpers::AssetUrlHelper
@@ -4,6 +4,8 @@ require 'json'
4
4
 
5
5
  class Cloudinary::Uploader
6
6
 
7
+ REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^data:[^;]*;base64,([a-zA-Z0-9\/+\n=]+)$)
8
+
7
9
  # @deprecated use {Cloudinary::Utils.build_eager} instead
8
10
  def self.build_eager(eager)
9
11
  Cloudinary::Utils.build_eager(eager)
@@ -25,7 +27,7 @@ class Cloudinary::Uploader
25
27
  :callback => options[:callback],
26
28
  :categorization => options[:categorization],
27
29
  :colors => Cloudinary::Utils.as_safe_bool(options[:colors]),
28
- :context => Cloudinary::Utils.encode_hash(options[:context]),
30
+ :context => Cloudinary::Utils.encode_context(options[:context]),
29
31
  :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
30
32
  :detection => options[:detection],
31
33
  :discard_original_filename => Cloudinary::Utils.as_safe_bool(options[:discard_original_filename]),
@@ -71,7 +73,7 @@ class Cloudinary::Uploader
71
73
  params = build_upload_params(options)
72
74
  if file.is_a?(Pathname)
73
75
  params[:file] = File.open(file, "rb")
74
- elsif file.respond_to?(:read) || file =~ /^ftp:|^https?:|^s3:|^data:[^;]*;base64,([a-zA-Z0-9\/+\n=]+)$/
76
+ elsif file.respond_to?(:read) || file.match(REMOTE_URL_REGEX)
75
77
  params[:file] = file
76
78
  else
77
79
  params[:file] = File.open(file, "rb")
@@ -89,7 +91,9 @@ class Cloudinary::Uploader
89
91
  public_id = public_id_or_options
90
92
  options = old_options
91
93
  end
92
- if file.is_a?(Pathname) || !file.respond_to?(:read)
94
+ if file.match(REMOTE_URL_REGEX)
95
+ return upload(file, options.merge(:public_id => public_id))
96
+ elsif file.is_a?(Pathname) || !file.respond_to?(:read)
93
97
  filename = file
94
98
  file = File.open(file, "rb")
95
99
  else
@@ -255,6 +259,10 @@ class Cloudinary::Uploader
255
259
  return self.call_tags_api(tag, "replace", public_ids, options)
256
260
  end
257
261
 
262
+ def self.remove_all_tags(public_ids = [], options = {})
263
+ return self.call_tags_api(nil, "remove_all", public_ids, options)
264
+ end
265
+
258
266
  private
259
267
 
260
268
  def self.call_tags_api(tag, command, public_ids = [], options = {})
@@ -269,6 +277,28 @@ class Cloudinary::Uploader
269
277
  end
270
278
  end
271
279
 
280
+ def self.add_context(context, public_ids = [], options = {})
281
+ return self.call_context_api(context, "add", public_ids, options)
282
+ end
283
+
284
+ def self.remove_all_context(public_ids = [], options = {})
285
+ return self.call_context_api(nil, "remove_all", public_ids, options)
286
+ end
287
+
288
+ private
289
+
290
+ def self.call_context_api(context, command, public_ids = [], options = {})
291
+ return call_api("context", options) do
292
+ {
293
+ :timestamp => (options[:timestamp] || Time.now.to_i),
294
+ :context => Cloudinary::Utils.encode_hash(context),
295
+ :public_ids => Cloudinary::Utils.build_array(public_ids),
296
+ :command => command,
297
+ :type => options[:type]
298
+ }
299
+ end
300
+ end
301
+
272
302
  def self.call_api(action, options)
273
303
  options = options.clone
274
304
  return_error = options.delete(:return_error)
@@ -205,14 +205,14 @@ class Cloudinary::Utils
205
205
  end
206
206
  end
207
207
 
208
+ EXP_REGEXP = Regexp.new(PREDEFINED_VARS.keys.join("|")+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
209
+ EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
210
+
208
211
  def self.normalize_expression(expression)
209
212
  if expression =~ /^!.+!$/ # quoted string
210
213
  expression
211
214
  else
212
- expression.to_s.gsub(
213
- Regexp.union(*PREDEFINED_VARS.keys, *CONDITIONAL_OPERATORS.keys.reverse),
214
- PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
215
- ).gsub(/[ _]+/, "_")
215
+ expression.to_s.gsub(EXP_REGEXP,EXP_REPLACEMENT).gsub(/[ _]+/, "_")
216
216
  end
217
217
  end
218
218
 
@@ -321,12 +321,13 @@ class Cloudinary::Utils
321
321
  unless breakpoint_settings.nil?
322
322
  breakpoint_settings = breakpoint_settings.clone
323
323
  transformation = breakpoint_settings.delete(:transformation) || breakpoint_settings.delete("transformation")
324
+ format = breakpoint_settings.delete(:format) || breakpoint_settings.delete("format")
324
325
  if transformation
325
- breakpoint_settings[:transformation] = Cloudinary::Utils.generate_transformation_string(transformation.clone, true)
326
+ transformation = Cloudinary::Utils.generate_transformation_string(transformation.clone, true)
326
327
  end
328
+ breakpoint_settings[:transformation] = [transformation, format].compact.join("/")
327
329
  end
328
330
  breakpoint_settings
329
-
330
331
  end.to_json
331
332
  end
332
333
 
@@ -450,6 +451,9 @@ class Cloudinary::Utils
450
451
  when resource_type.to_s == "image" && type.to_s == "private"
451
452
  resource_type = "private_images"
452
453
  type = nil
454
+ when resource_type.to_s == "image" && type.to_s == "authenticated"
455
+ resource_type = "authenticated_images"
456
+ type = nil
453
457
  when resource_type.to_s == "raw" && type.to_s == "upload"
454
458
  resource_type = "files"
455
459
  type = nil
@@ -457,7 +461,7 @@ class Cloudinary::Utils
457
461
  resource_type = "videos"
458
462
  type = nil
459
463
  else
460
- raise(CloudinaryException, "URL Suffix only supported for image/upload, image/private and raw/upload")
464
+ raise(CloudinaryException, "URL Suffix only supported for image/upload, image/private, image/authenticated, video/upload and raw/upload")
461
465
  end
462
466
  end
463
467
  if use_root_path
@@ -668,6 +672,10 @@ class Cloudinary::Utils
668
672
  end
669
673
  end
670
674
 
675
+ # encodes a hash into pipe-delimited key-value pairs string
676
+ # @hash [Hash] key-value hash to be encoded
677
+ # @return [String] a joined string of all keys and values separated by a pipe character
678
+ # @private
671
679
  def self.encode_hash(hash)
672
680
  case hash
673
681
  when Hash then hash.map{|k,v| "#{k}=#{v}"}.join("|")
@@ -676,6 +684,18 @@ class Cloudinary::Utils
676
684
  end
677
685
  end
678
686
 
687
+ # Same like encode_hash, with additional escaping of | and = characters
688
+ # @hash [Hash] key-value hash to be encoded
689
+ # @return [String] a joined string of all keys and values properly escaped and separated by a pipe character
690
+ # @private
691
+ def self.encode_context(hash)
692
+ case hash
693
+ when Hash then hash.map{|k,v| "#{k}=#{v.gsub(/([=|])/, '\\\\\1')}"}.join("|")
694
+ when nil then ""
695
+ else hash
696
+ end
697
+ end
698
+
679
699
  def self.encode_double_array(array)
680
700
  array = build_array(array)
681
701
  if array.length > 0 && array[0].is_a?(Array)
@@ -804,7 +824,9 @@ class Cloudinary::Utils
804
824
  :public_ids=>options[:public_ids] && Cloudinary::Utils.build_array(options[:public_ids]),
805
825
  :prefixes=>options[:prefixes] && Cloudinary::Utils.build_array(options[:prefixes]),
806
826
  :expires_at=>options[:expires_at],
807
- :transformations => build_eager(options[:transformations])
827
+ :transformations => build_eager(options[:transformations]),
828
+ :skip_transformation_name=>Cloudinary::Utils.as_safe_bool(options[:skip_transformation_name]),
829
+ :allow_missing=>Cloudinary::Utils.as_safe_bool(options[:allow_missing])
808
830
  }
809
831
  end
810
832
 
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.6.0"
3
+ VERSION = "1.7.0"
4
4
  end
@@ -6,7 +6,7 @@ describe Cloudinary::Api do
6
6
  include_context "cleanup", TIMESTAMP_TAG
7
7
  TEST_WIDTH = rand(1000)
8
8
  TEST_TRANSFOMATION = "c_scale,w_#{TEST_WIDTH}"
9
- prefix = "api_test_#{Time.now.to_i}"
9
+ prefix = "api_test_#{SUFFIX}"
10
10
  test_id_1 = "#{prefix}_1"
11
11
  test_id_2 = "#{prefix}_2"
12
12
  test_id_3 = "#{prefix}_3"
@@ -157,9 +157,11 @@ describe Cloudinary::Api do
157
157
 
158
158
  end
159
159
 
160
- it "should allow deleting resources" do
160
+ it "should allow deleting multiple resources and comma inclusive public IDs", :focus => true do
161
161
  expect(RestClient::Request).to receive(:execute).with(deep_hash_value( {[:payload, :public_ids] => ["apit_test", "test_id_2", "api_test3"]}))
162
162
  @api.delete_resources(["apit_test", "test_id_2", "api_test3"])
163
+ expect(RestClient::Request).to receive(:execute).with(deep_hash_value( {[:payload, :public_ids] => "apit_test,test_id_2,api_test3"}))
164
+ @api.delete_resources("apit_test,test_id_2,api_test3")
163
165
  end
164
166
 
165
167
  it "should allow deleting resource transformations" do
@@ -352,9 +354,13 @@ describe Cloudinary::Api do
352
354
  expect{Cloudinary::Api.update(result["public_id"], {:categorization => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value/)
353
355
  end
354
356
 
355
- it "should support requesting detection" do
356
- expect(RestClient::Request).to receive(:execute).with(deep_hash_value( [:payload, :detection] => "adv_face"))
357
- Cloudinary::Api.update("public_id", {:detection => "adv_face"})
357
+ it "should support requesting detection with server notification", :focus => true do
358
+ expected = {
359
+ [:payload, :detection] => "adv_face",
360
+ [:payload, :notification_url] => "http://example.com"
361
+ }
362
+ expect(RestClient::Request).to receive(:execute).with(deep_hash_value(expected))
363
+ Cloudinary::Api.update("public_id", {:detection => "adv_face", :notification_url => "http://example.com"})
358
364
  end
359
365
 
360
366
  it "should support requesting auto_tagging" do
@@ -437,4 +443,60 @@ describe Cloudinary::Api do
437
443
  expect(resource["access_mode"]).to eq('public')
438
444
  end
439
445
  end
446
+
447
+ context "resource of type authenticated" do
448
+ i = 0
449
+ bytes = nil
450
+ publicId = ""
451
+ publish_resource_tag = "publish_resource_tag"
452
+ before(:each) do
453
+ i += 1
454
+ result = Cloudinary::Uploader.upload TEST_IMG, type: "authenticated", tags: [TEST_TAG, TIMESTAMP_TAG, publish_resource_tag], transformation: {width: 100*i, crop: "scale"}
455
+ publicId = result["public_id"]
456
+ expect(result["type"]).to eq("authenticated")
457
+ end
458
+
459
+ it "should publish resources by ids" do
460
+ result = Cloudinary::Api.publish_by_ids( [publicId])
461
+
462
+ expect(result["published"]).to be_an_instance_of(Array)
463
+ expect(result["published"].length).to eq(1)
464
+
465
+ resource = result["published"][0]
466
+
467
+ expect(resource["public_id"]).to eq(publicId)
468
+ expect(resource["type"]).to eq('upload')
469
+
470
+ bytes = resource["bytes"]
471
+ end
472
+ it "should publish resources by prefix and overwrite" do
473
+ result = Cloudinary::Api.publish_by_prefix(publicId[0..-3], overwrite: true)
474
+
475
+ expect(result["published"]).to be_an_instance_of(Array)
476
+ expect(result["published"].length).to eq(1)
477
+
478
+ resource = result["published"][0]
479
+
480
+ expect(resource["public_id"]).to eq(publicId)
481
+ expect(resource["bytes"]).not_to eq(bytes)
482
+ expect(resource["type"]).to eq('upload')
483
+
484
+ bytes = resource["bytes"]
485
+ end
486
+ it "should publish resources by tag and overwrite" do
487
+ result = Cloudinary::Api.publish_by_tag(publish_resource_tag, overwrite: true)
488
+
489
+ expect(result["published"]).to be_an_instance_of(Array)
490
+ expect(result["published"].length).to eq(1)
491
+
492
+ resource = result["published"][0]
493
+
494
+ expect(resource["public_id"]).to eq(publicId)
495
+ expect(resource["bytes"]).not_to eq(bytes)
496
+ expect(resource["type"]).to eq('upload')
497
+
498
+ bytes = resource["bytes"]
499
+ end
500
+
501
+ end
440
502
  end
@@ -22,6 +22,12 @@ RSpec.shared_context 'archive' do
22
22
  :effect => :blackwhite
23
23
  }
24
24
  )
25
+ Cloudinary::Uploader.upload(
26
+ "http://res.cloudinary.com/demo/image/upload/sample.jpg",
27
+ :public_id => 'tag_sample_raw.jpg',
28
+ :resource_type => 'raw',
29
+ :tags => [TEST_TAG, TIMESTAMP_TAG],
30
+ )
25
31
  end
26
32
  include_context "cleanup", TIMESTAMP_TAG
27
33
  end
@@ -62,20 +68,8 @@ describe Cloudinary::Uploader do
62
68
 
63
69
  describe '.create_archive' do
64
70
  let!(:target_public_id) {
65
- "gem_test#{ rand(1000000)}"
66
- }
67
- let!(:archive_result) {
68
- Cloudinary::Uploader.create_archive(
69
- {
70
- :target_public_id => target_public_id,
71
- :public_ids => %w(tag_sample tag_samplebw),
72
- :tags => [TEST_TAG, TIMESTAMP_TAG]
73
- }.merge(options))
71
+ "gem_test#{ SUFFIX}"
74
72
  }
75
- let(:options) { { :mode => :create } }
76
- it 'should return a Hash' do
77
- expect(archive_result).to be_a(Hash)
78
- end
79
73
  expected_keys = %w(
80
74
  resource_type
81
75
  type
@@ -91,10 +85,41 @@ describe Cloudinary::Uploader do
91
85
  resource_count
92
86
  file_count
93
87
  )
94
- it "should include keys: #{expected_keys.join(', ')}" do
88
+ let!(:archive_result) {
89
+ Cloudinary::Uploader.create_archive(
90
+ {
91
+ :target_public_id => target_public_id,
92
+ :public_ids => %w(tag_sample tag_samplebw),
93
+ :tags => [TEST_TAG, TIMESTAMP_TAG],
94
+ :transformations => [{width: 100, height: 100, crop: "fill"},{effect: "grayscale"}],
95
+ :skip_transformation_name => true
96
+ }.merge(options))
97
+ }
98
+ let(:options) { { :mode => :create } }
99
+ it 'should return a Hash with suitable set of keys' do
100
+ expect(archive_result).to be_a(Hash)
95
101
  expect(archive_result.keys).to include(*expected_keys)
96
102
  end
97
103
  end
104
+ describe 'create archive based on raw resources and missing public IDs' do
105
+ let!(:target_public_id) {
106
+ "gem_test#{ SUFFIX}"
107
+ }
108
+ let!(:archive_result) {
109
+ Cloudinary::Uploader.create_archive(
110
+ {
111
+ :target_public_id => target_public_id,
112
+ :public_ids => %w(tag_sample_raw.jpg non-wxisting-resource),
113
+ :resource_type => 'raw',
114
+ :allow_missing => true
115
+ }.merge(options))
116
+ }
117
+ let(:options) { { :mode => :create } }
118
+ it 'should skip missing public IDs and successfully generate the archive containing raw resources' do
119
+ expect(archive_result).to be_a(Hash)
120
+ expect(archive_result["resource_count"]).to equal(1)
121
+ end
122
+ end
98
123
  describe '.create_zip' do
99
124
  it 'should call create_archive with "zip" format' do
100
125
  expect(Cloudinary::Uploader).to receive(:create_archive).with({ :tags => TEST_TAG }, "zip")