cloudinary 1.11.1 → 1.12.0

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -1
  3. data/.travis.yml +12 -6
  4. data/CHANGELOG.md +30 -0
  5. data/cloudinary.gemspec +2 -0
  6. data/lib/active_storage/blob_key.rb +20 -0
  7. data/lib/active_storage/service/cloudinary_service.rb +181 -0
  8. data/lib/cloudinary.rb +9 -4
  9. data/lib/cloudinary/api.rb +17 -4
  10. data/lib/cloudinary/auth_token.rb +6 -4
  11. data/lib/cloudinary/uploader.rb +4 -4
  12. data/lib/cloudinary/utils.rb +62 -29
  13. data/lib/cloudinary/version.rb +1 -1
  14. data/spec/active_storage/Gemfile +12 -0
  15. data/spec/active_storage/application_system_test_case.rb +5 -0
  16. data/spec/active_storage/database/create_users_migration.rb +9 -0
  17. data/spec/active_storage/database/setup.rb +7 -0
  18. data/spec/active_storage/dummy/Rakefile +5 -0
  19. data/spec/active_storage/dummy/app/assets/config/manifest.js +3 -0
  20. data/spec/active_storage/dummy/app/assets/javascripts/application.js +13 -0
  21. data/spec/active_storage/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/spec/active_storage/dummy/app/controllers/application_controller.rb +5 -0
  23. data/spec/active_storage/dummy/app/helpers/application_helper.rb +4 -0
  24. data/spec/active_storage/dummy/app/jobs/application_job.rb +4 -0
  25. data/spec/active_storage/dummy/app/models/application_record.rb +5 -0
  26. data/spec/active_storage/dummy/app/views/layouts/application.html.erb +14 -0
  27. data/spec/active_storage/dummy/bin/bundle +5 -0
  28. data/spec/active_storage/dummy/bin/rails +6 -0
  29. data/spec/active_storage/dummy/bin/rake +6 -0
  30. data/spec/active_storage/dummy/bin/yarn +11 -0
  31. data/spec/active_storage/dummy/config.ru +7 -0
  32. data/spec/active_storage/dummy/config/application.rb +22 -0
  33. data/spec/active_storage/dummy/config/boot.rb +7 -0
  34. data/spec/active_storage/dummy/config/database.yml +25 -0
  35. data/spec/active_storage/dummy/config/environment.rb +7 -0
  36. data/spec/active_storage/dummy/config/environments/development.rb +52 -0
  37. data/spec/active_storage/dummy/config/environments/production.rb +83 -0
  38. data/spec/active_storage/dummy/config/environments/test.rb +38 -0
  39. data/spec/active_storage/dummy/config/initializers/application_controller_renderer.rb +7 -0
  40. data/spec/active_storage/dummy/config/initializers/assets.rb +16 -0
  41. data/spec/active_storage/dummy/config/initializers/backtrace_silencers.rb +8 -0
  42. data/spec/active_storage/dummy/config/initializers/cookies_serializer.rb +7 -0
  43. data/spec/active_storage/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  44. data/spec/active_storage/dummy/config/initializers/inflections.rb +17 -0
  45. data/spec/active_storage/dummy/config/initializers/mime_types.rb +5 -0
  46. data/spec/active_storage/dummy/config/initializers/wrap_parameters.rb +16 -0
  47. data/spec/active_storage/dummy/config/routes.rb +4 -0
  48. data/spec/active_storage/dummy/config/secrets.yml +32 -0
  49. data/spec/active_storage/dummy/config/spring.rb +8 -0
  50. data/spec/active_storage/dummy/config/storage.yml +3 -0
  51. data/spec/active_storage/dummy/config/webpacker.yml +72 -0
  52. data/spec/active_storage/dummy/package.json +5 -0
  53. data/spec/active_storage/dummy/public/404.html +67 -0
  54. data/spec/active_storage/dummy/public/422.html +67 -0
  55. data/spec/active_storage/dummy/public/500.html +66 -0
  56. data/spec/active_storage/dummy/public/apple-touch-icon-precomposed.png +0 -0
  57. data/spec/active_storage/dummy/public/apple-touch-icon.png +0 -0
  58. data/spec/active_storage/dummy/public/favicon.ico +0 -0
  59. data/spec/active_storage/fixtures/files/colors.bmp +0 -0
  60. data/spec/active_storage/fixtures/files/empty_file.txt +0 -0
  61. data/spec/active_storage/fixtures/files/favicon.ico +0 -0
  62. data/spec/active_storage/fixtures/files/icon.psd +0 -0
  63. data/spec/active_storage/fixtures/files/icon.svg +13 -0
  64. data/spec/active_storage/fixtures/files/image.gif +0 -0
  65. data/spec/active_storage/fixtures/files/racecar.jpg +0 -0
  66. data/spec/active_storage/fixtures/files/racecar.tif +0 -0
  67. data/spec/active_storage/fixtures/files/racecar_rotated.jpg +0 -0
  68. data/spec/active_storage/fixtures/files/report.pdf +0 -0
  69. data/spec/active_storage/fixtures/files/rotated_video.mp4 +0 -0
  70. data/spec/active_storage/fixtures/files/video.mp4 +0 -0
  71. data/spec/active_storage/fixtures/files/video_with_rectangular_samples.mp4 +0 -0
  72. data/spec/active_storage/fixtures/files/video_with_undefined_display_aspect_ratio.mp4 +0 -0
  73. data/spec/active_storage/fixtures/files/video_without_video_stream.mp4 +0 -0
  74. data/spec/active_storage/service/cloudinary_service_spec.rb +92 -0
  75. data/spec/active_storage/service/configurations.yml +4 -0
  76. data/spec/active_storage/test_helper.rb +43 -0
  77. data/spec/api_spec.rb +68 -12
  78. data/spec/archive_spec.rb +17 -1
  79. data/spec/cloudinary_helper_spec.rb +2 -0
  80. data/spec/cloudinary_spec.rb +30 -6
  81. data/spec/movie.mp4 +0 -0
  82. data/spec/spec_helper.rb +9 -2
  83. data/spec/uploader_spec.rb +20 -3
  84. data/spec/utils_methods_spec.rb +29 -2
  85. data/spec/utils_spec.rb +93 -11
  86. data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +31 -1
  87. data/vendor/assets/javascripts/cloudinary/jquery.fileupload.js +24 -4
  88. data/vendor/assets/javascripts/cloudinary/jquery.ui.widget.js +741 -561
  89. data/vendor/assets/javascripts/cloudinary/load-image.all.min.js +1 -1
  90. metadata +161 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 629a48ffc8543fc7fc8da12f12d88823232fe70a08556c49b9583437bcc24c45
4
- data.tar.gz: 1a34d49c36238a396fc3d1494845f0f97ae71b18bf49175e255c9fbea239031f
3
+ metadata.gz: f3837b9826e6f31a35e478b9d8bbf81027cee43ece65f7df161b748fd5c21dd9
4
+ data.tar.gz: 3b47da18531bbfe2252f6cfe4a071bc5972c286c2f5777758d1b1a7e1a30b07e
5
5
  SHA512:
6
- metadata.gz: 9be4a6c2470fcdbab667610878db7934bff588ceda810e4917caa36b28c9c3e910214ebd4f21baca258ff643f7bd8e7277af4dda89779da356473d49b2cc73b8
7
- data.tar.gz: b308cf1059df2bc58ba103d67b845c0a3a2384a4f53e387f950057abbc502ef425506b2861b1b471689b21cd3e04f1c960fe230559ad8d5830babc203a85b2c0
6
+ metadata.gz: 34d0a6214325094a27683345da51e605b7ba6f6ba5761d4c64478dea618dbf3d4ce14a255bf69d513704d72dca6ef5a27ee4bc6e5e81d66e5548341f9e7c2e8c
7
+ data.tar.gz: 383bbafb8b06b9bda3fdedbde2f6b7248bcee0bacd5576e98b8e9b79d9ff45cd7d1025056fe1edde412fbf4a04855ba4ea3af197c260374827026f7a0b883f55
data/.gitignore CHANGED
@@ -55,4 +55,9 @@ bower.json
55
55
  .powenv
56
56
 
57
57
  !lib/
58
- cloudinary.yml
58
+ cloudinary.yml
59
+ configuration.yml
60
+
61
+ # Developer env
62
+ .idea
63
+ .vscode
@@ -1,9 +1,15 @@
1
1
  language: ruby
2
2
 
3
- before_install:
4
- - gem install bundler
5
-
6
- rvm:
7
- - 1.9.3
8
- - ruby-head
3
+ matrix:
4
+ include:
5
+ - name: "Ruby 1.9"
6
+ dist: precise
7
+ rvm: 1.9.3
8
+ before_install:
9
+ - gem install bundler -v 1.17.3
10
+ - name: "Ruby 2.5.3"
11
+ dist: xenial
12
+ rvm: ruby-2.5.3
13
+ before_install:
14
+ - gem install bundler
9
15
  script: bundle exec rspec
@@ -1,3 +1,33 @@
1
+ 1.12.0 / 2019-10-02
2
+ =============
3
+
4
+ New functionality and features
5
+ ------------------------------
6
+
7
+ * Add Cloudinary service for ActiveStorage
8
+ * Add `create_folder` Admin API method
9
+ * Add `delete_folder` Admin API method
10
+ * Add `cinemagraph_analysis` to `upload`, `explicit` and `resource` API methods
11
+ * Add `font_antialiasing` and `font_hinting` text style parameters
12
+ * Add `derived_next_cursor` parameter to `resource` Admin API
13
+ * Add `next_cursor` and `max_results` for `root_folders` and `subfolders` Admin API functions
14
+ * Add `jpeg` to `IMAGE_FORMATS`
15
+ * Add `pow` transformation operator
16
+ * Add `force_version` to `cloudinary_url`
17
+ * Support per corner values for the `radius` transformation parameter
18
+ * Support using multiple resource types when generating archives
19
+ * Support Google Storage fetch URL
20
+
21
+ Other Changes
22
+ -------------
23
+ * Ensure `CLOUDINARY_URL` starts with `cloudinary://`
24
+ * Reduce memory usage in `Cloudinary::Utils.cloudinary_url`
25
+ * Encode URL in Admin API methods
26
+ * Fix base64 data validation
27
+ * Return `video` as the `resource_type` for audio files
28
+ * Add language and platform version for ruby/rails user agent
29
+ * Fix TravisCI configuration for ruby 1.9
30
+
1
31
 
2
32
  1.11.1 / 2018-12-22
3
33
  ===================
@@ -26,7 +26,9 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency "actionpack"
27
27
  s.add_development_dependency "nokogiri"
28
28
  s.add_development_dependency "rake"
29
+ s.add_development_dependency "sqlite3"
29
30
  s.add_development_dependency "rspec", '>=3.5'
31
+ s.add_development_dependency "rails", "~>5.2" if RUBY_VERSION >= "2.2.2"
30
32
  s.add_development_dependency "rspec-rails"
31
33
  s.add_development_dependency "rubyzip", "<=1.2.0" # support testing Ruby 1.9
32
34
  s.add_development_dependency "simplecov"
@@ -0,0 +1,20 @@
1
+ # Allow Blob attributes to be passed down to the service
2
+ # attributes includes
3
+ # - key - the string the BlobKey represents
4
+ # - content_type
5
+ # - filename
6
+ module ActiveStorage
7
+ class BlobKey < String
8
+ attr_reader :attributes
9
+ def initialize(attributes)
10
+ if attributes.is_a? Hash
11
+ attributes.symbolize_keys!
12
+ super(attributes[:key])
13
+ @attributes = attributes
14
+ else
15
+ super(attributes)
16
+ @attributes = {key: attributes} if attributes
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,181 @@
1
+ require 'active_storage/blob_key'
2
+ require 'cloudinary/helper'
3
+
4
+ unless ActiveStorage::Blob.method_defined? :original_key
5
+ class ActiveStorage::Blob
6
+ alias_method :original_key, :key
7
+
8
+ def key
9
+ original_key
10
+ ActiveStorage::BlobKey.new(@attributes.as_json)
11
+ end
12
+ end
13
+ end
14
+
15
+ module ActiveStorage
16
+ class Service::CloudinaryService < Service
17
+ module Headers
18
+ CONTENT_TYPE = "Content-Type".freeze
19
+ CONTENT_MD5 = "Content-MD5".freeze
20
+ end
21
+ attr_reader :upload_options
22
+
23
+ def initialize(**options)
24
+ @options = options
25
+ @helper = ActionView::Base.new
26
+ end
27
+
28
+ def upload(key, io, filename: nil, checksum: nil, **options)
29
+ instrument :upload, key: key, checksum: checksum do
30
+ begin
31
+ extra_headers = checksum.nil? ? {} : {Headers::CONTENT_MD5 => checksum}
32
+ options = @options.merge(options)
33
+ Cloudinary::Uploader.upload(
34
+ io,
35
+ public_id: public_id(key),
36
+ resource_type: resource_type(io, key),
37
+ context: {active_storage_key: key, checksum: checksum},
38
+ extra_headers: extra_headers,
39
+ **options
40
+ )
41
+ rescue CloudinaryException => e
42
+ raise ActiveStorage::IntegrityError, e.message, e.backtrace
43
+ end
44
+ end
45
+ end
46
+
47
+ def url(key, filename: nil, content_type: '', **options)
48
+ instrument :url, key: key do |payload|
49
+ url = Cloudinary::Utils.cloudinary_url(
50
+ public_id(key),
51
+ resource_type: resource_type(nil, key),
52
+ format: ext_for_content_type(content_type),
53
+ **@options.merge(options.symbolize_keys)
54
+ )
55
+ payload[:url] = url
56
+
57
+ url
58
+ end
59
+ end
60
+
61
+ def url_for_direct_upload(key, **options)
62
+ instrument :url, key: key do |payload|
63
+ options = {:resource_type => resource_type(nil, key)}.merge(@options.merge(options.symbolize_keys))
64
+ options[:public_id] = public_id(key)
65
+ options[:context] = {active_storage_key: key}
66
+ options.delete(:file)
67
+ payload[:url] = api_uri("upload", options)
68
+ end
69
+ end
70
+
71
+ def headers_for_direct_upload(key, content_type:, checksum:, **)
72
+ {
73
+ Headers::CONTENT_TYPE => content_type,
74
+ Headers::CONTENT_MD5 => checksum,
75
+ }
76
+ end
77
+
78
+ def delete(key)
79
+ instrument :delete, key: key do
80
+ Cloudinary::Uploader.destroy public_id(key), resource_type: resource_type(nil, key)
81
+ end
82
+ end
83
+
84
+ def delete_prefixed(prefix)
85
+ # This method is used by ActiveStorage to delete derived resources after the main resource was deleted.
86
+ # In Cloudinary, the derived resources are deleted automatically when the main resource is deleted.
87
+ end
88
+
89
+ def exist?(key)
90
+ instrument :exist, key: key do |payload|
91
+ begin
92
+ Cloudinary::Api.resource public_id(key), resource_type: resource_type(nil, key)
93
+ true
94
+ rescue Cloudinary::Api::NotFound => e
95
+ false
96
+ end
97
+ end
98
+ end
99
+
100
+ def download(key, &block)
101
+ url = Cloudinary::Utils.unsigned_download_url(public_id(key), resource_type: resource_type(nil, key))
102
+ uri = URI(url)
103
+ if block_given?
104
+ instrument :streaming_download, key: key do
105
+ Net::HTTP.start(uri.host, uri.port) do |http|
106
+ request = Net::HTTP::Get.new uri
107
+ http.request request do |response|
108
+ response.read_body &block
109
+ end
110
+
111
+ end
112
+ end
113
+ else
114
+ instrument :download, key: key do
115
+ res = Net::HTTP::get_response(uri)
116
+ res.body
117
+ end
118
+ end
119
+ end
120
+
121
+ # Return the partial content in the byte +range+ of the file at the +key+.
122
+ def download_chunk(key, range)
123
+ url = Cloudinary::Utils.unsigned_download_url(public_id(key), resource_type: resource_type(nil, key))
124
+ uri = URI(url)
125
+ instrument :download, key: key do
126
+ req = Net::HTTP::Get.new(uri)
127
+ range_end = case
128
+ when range.end.nil? then ''
129
+ when range.exclude_end? then range.end - 1
130
+ else range.end
131
+ end
132
+ req['range'] = "bytes=#{[range.begin, range_end].join('-')}"
133
+ res = Net::HTTP.start(uri.hostname, uri.port) do |http|
134
+ http.request(req)
135
+ end
136
+ res.body.force_encoding(Encoding::BINARY)
137
+ end
138
+
139
+ end
140
+
141
+ private
142
+
143
+ def api_uri(action, options)
144
+ base_url = Cloudinary::Utils.cloudinary_api_url(action, options)
145
+ upload_params = Cloudinary::Uploader.build_upload_params(options)
146
+
147
+ upload_params.reject! {|k, v| Cloudinary::Utils.safe_blank?(v)}
148
+ unless options[:unsigned]
149
+ upload_params = Cloudinary::Utils.sign_request(upload_params, options)
150
+ end
151
+ "#{base_url}?#{upload_params.to_query}"
152
+ end
153
+
154
+ def ext_for_content_type(content_type)
155
+ @formats ||= Hash.new do |h, key|
156
+ ext = Rack::Mime::MIME_TYPES.invert[key]
157
+ h[key] = ext.slice(1..-1) unless ext.nil?
158
+ end
159
+ @formats[content_type]
160
+ end
161
+
162
+ def public_id(key)
163
+ # TODO: Allow custom manipulation of key to obscure how we store in Cloudinary
164
+ key
165
+ end
166
+
167
+ def resource_type(io, key = "")
168
+ return 'image' unless key.respond_to? :attributes
169
+ options = key.attributes
170
+ content_type = options[:content_type] || (io.nil? ? '' : Marcel::MimeType.for(io))
171
+ case content_type.split('/')[0]
172
+ when 'video'
173
+ 'video'
174
+ when 'text'
175
+ 'raw'
176
+ else
177
+ 'image'
178
+ end
179
+ end
180
+ end
181
+ end
@@ -29,8 +29,8 @@ module Cloudinary
29
29
  OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"
30
30
  SHARED_CDN = AKAMAI_SHARED_CDN
31
31
 
32
- USER_AGENT = "CloudinaryRuby/" + VERSION
33
- @@user_platform = ""
32
+ USER_AGENT = "CloudinaryRuby/#{VERSION} (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
33
+ @@user_platform = defined?(Rails.version) ? "Rails/#{Rails.version}" : ""
34
34
 
35
35
  # Add platform information to the USER_AGENT header
36
36
  # This is intended for platform information and not individual applications!
@@ -44,7 +44,7 @@ module Cloudinary
44
44
 
45
45
  def self.USER_AGENT
46
46
  if @@user_platform.empty?
47
- "#{USER_AGENT}"
47
+ USER_AGENT
48
48
  else
49
49
  "#{@@user_platform} #{USER_AGENT}"
50
50
  end
@@ -86,7 +86,12 @@ module Cloudinary
86
86
 
87
87
  def self.config_from_url(url)
88
88
  @@config ||= OpenStruct.new
89
- uri = URI.parse(url)
89
+ return unless url && !url.empty?
90
+ uri = URI.parse(url)
91
+ if !uri.scheme || "cloudinary" != uri.scheme.downcase
92
+ raise(CloudinaryException,
93
+ "Invalid CLOUDINARY_URL scheme. Expecting to start with 'cloudinary://'")
94
+ end
90
95
  set_config(
91
96
  "cloud_name" => uri.host,
92
97
  "api_key" => uri.user,
@@ -79,7 +79,8 @@ class Cloudinary::Api
79
79
  type = options[:type] || "upload"
80
80
  uri = "resources/#{resource_type}/#{type}/#{public_id}"
81
81
  call_api(:get, uri,
82
- only(options,
82
+ only(options,
83
+ :cinemagraph_analysis,
83
84
  :colors,
84
85
  :coordinates,
85
86
  :exif,
@@ -88,7 +89,8 @@ class Cloudinary::Api
88
89
  :max_results,
89
90
  :pages,
90
91
  :phash,
91
- :quality_analysis
92
+ :quality_analysis,
93
+ :derived_next_cursor
92
94
  ), options)
93
95
  end
94
96
 
@@ -226,11 +228,21 @@ class Cloudinary::Api
226
228
  end
227
229
 
228
230
  def self.root_folders(options={})
229
- call_api(:get, "folders", {}, options)
231
+ params = only(options, :max_results, :next_cursor)
232
+ call_api(:get, "folders", params, options)
230
233
  end
231
234
 
232
235
  def self.subfolders(of_folder_path, options={})
233
- call_api(:get, "folders/#{of_folder_path}", {}, options)
236
+ params = only(options, :max_results, :next_cursor)
237
+ call_api(:get, "folders/#{of_folder_path}", params, options)
238
+ end
239
+
240
+ def self.delete_folder(path, options={})
241
+ call_api(:delete, "folders/#{path}", {}, options)
242
+ end
243
+
244
+ def self.create_folder(folder_name, options={})
245
+ call_api(:post, "folders/#{folder_name}", {}, options)
234
246
  end
235
247
 
236
248
  def self.upload_mappings(options={})
@@ -343,6 +355,7 @@ class Cloudinary::Api
343
355
  api_key = options[:api_key] || Cloudinary.config.api_key || raise("Must supply api_key")
344
356
  api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise("Must supply api_secret")
345
357
  timeout = options[:timeout] || Cloudinary.config.timeout || 60
358
+ uri = Cloudinary::Utils.smart_escape(uri)
346
359
  api_url = [cloudinary, "v1_1", cloud_name, uri].join("/")
347
360
  # Add authentication
348
361
  api_url.sub!(%r(^(https?://)), "\\1#{api_key}:#{api_secret}@")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  if RUBY_VERSION > "2"
3
5
  require "ostruct"
@@ -10,6 +12,7 @@ module Cloudinary
10
12
  module AuthToken
11
13
  SEPARATOR = '~'
12
14
  UNSAFE = /[ "#%&\'\/:;<=>?@\[\\\]^`{\|}~]/
15
+ EMPTY_TOKEN = {}.freeze
13
16
 
14
17
  def self.generate(options = {})
15
18
  key = options[:key]
@@ -42,12 +45,11 @@ module Cloudinary
42
45
  "#{name}=#{token.join(SEPARATOR)}"
43
46
  end
44
47
 
45
-
46
48
  # Merge token2 to token1 returning a new
47
49
  # Requires to support Ruby 1.9
48
50
  def self.merge_auth_token(token1, token2)
49
- token1 = token1 || {}
50
- token2 = token2 || {}
51
+ token1 = token1 || EMPTY_TOKEN
52
+ token2 = token2 || EMPTY_TOKEN
51
53
  token1 = token1.respond_to?( :to_h) ? token1.to_h : token1
52
54
  token2 = token2.respond_to?( :to_h) ? token2.to_h : token2
53
55
  token1.merge(token2)
@@ -69,4 +71,4 @@ module Cloudinary
69
71
  OpenSSL::HMAC.hexdigest(digest, bin_key, message)
70
72
  end
71
73
  end
72
- end
74
+ end
@@ -5,8 +5,7 @@ require 'cloudinary/cache'
5
5
 
6
6
  class Cloudinary::Uploader
7
7
 
8
- REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^data:[^;]*;base64,([a-zA-Z0-9\/+\n=]+)$)
9
-
8
+ REMOTE_URL_REGEX = Cloudinary::Utils::REMOTE_URL_REGEX
10
9
  # @deprecated use {Cloudinary::Utils.build_eager} instead
11
10
  def self.build_eager(eager)
12
11
  Cloudinary::Utils.build_eager(eager)
@@ -28,6 +27,7 @@ class Cloudinary::Uploader
28
27
  :backup => Cloudinary::Utils.as_safe_bool(options[:backup]),
29
28
  :callback => options[:callback],
30
29
  :categorization => options[:categorization],
30
+ :cinemagraph_analysis => Cloudinary::Utils.as_safe_bool(options[:cinemagraph_analysis]),
31
31
  :colors => Cloudinary::Utils.as_safe_bool(options[:colors]),
32
32
  :context => Cloudinary::Utils.encode_context(options[:context]),
33
33
  :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
@@ -77,7 +77,7 @@ class Cloudinary::Uploader
77
77
  params = build_upload_params(options)
78
78
  if file.is_a?(Pathname)
79
79
  params[:file] = File.open(file, "rb")
80
- elsif file.respond_to?(:read) || file.match(REMOTE_URL_REGEX)
80
+ elsif file.respond_to?(:read) || Cloudinary::Utils.is_remote?(file)
81
81
  params[:file] = file
82
82
  else
83
83
  params[:file] = File.open(file, "rb")
@@ -95,7 +95,7 @@ class Cloudinary::Uploader
95
95
  public_id = public_id_or_options
96
96
  options = old_options
97
97
  end
98
- if file.match(REMOTE_URL_REGEX)
98
+ if Cloudinary::Utils.is_remote?(file)
99
99
  return upload(file, options.merge(:public_id => public_id))
100
100
  elsif file.is_a?(Pathname) || !file.respond_to?(:read)
101
101
  filename = file