cloudinary 1.13.1 → 1.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bd2d8e8b2dc86a6dfd91a1d47cceacd541c46b71dc2ac10bb6972674067cea0
4
- data.tar.gz: a3af6a9f260fa2a3c3390e5640535ec916c3c4487a806fe9e187b620551d92d1
3
+ metadata.gz: eeda4318322d6304cf37a68c621387dc7d4382cd45d6665eddea154ce30adac3
4
+ data.tar.gz: ef675318a0c25a7e729d91ad014a8b36bad2756cafb73c20f69693b230747537
5
5
  SHA512:
6
- metadata.gz: 0761a1868f2c4fe403b874d510ea44df4b164bc1cf9f30b44d55f074b066fd1634accf98e2adf6bb3e76ee081790f26bc833781371272302cc4c9c41c7c3c190
7
- data.tar.gz: 13291d1b0e99402d7cb043fc63d200444646785f1d812e6b323c8e82db2b3034caad6b75796ddaee4d3ab0f99389fecb6445df513e09ce01e6213f492c6e7d6b
6
+ metadata.gz: c1ad5eee4b1b5eca7467e134cba0fc9516c0fe5af018637c1c0dbabc0a707a30e1cb115892de48f2acd2f8d339159842599edb7e0a1be15ea4ef391cbe569b78
7
+ data.tar.gz: 19235e8274a35ed46c302a57056c45ff885b1a59a8ad852b057bce384729b108105b9b6d53638adefc2b317cccbc3fae48f4dbae962937e3e682da962f817b86
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: Bug report
3
+ about: Bug report for Cloudinary Ruby SDK
4
+ title: ''
5
+ labels: ''
6
+ assignees: const-cloudinary
7
+
8
+ ---
9
+
10
+ ## Bug report for Cloudinary Ruby SDK
11
+ Before proceeding, please update to latest version and test if the issue persists
12
+
13
+ ## Describe the bug in a sentence or two.
14
+
15
+
16
+ ## Issue Type (Can be multiple)
17
+ [ ] Build - Can’t install or import the SDK
18
+ [ ] Performance - Performance issues
19
+ [ ] Behaviour - Functions aren’t working as expected (Such as generate URL)
20
+ [ ] Documentation - Inconsistency between the docs and behaviour
21
+ [ ] Other (Specify)
22
+
23
+ ## Steps to reproduce
24
+ … if applicable
25
+
26
+ ## Error screenshots or Stack Trace (if applicable)
27
+
28
+
29
+ ## Operating System
30
+ [ ] Linux
31
+ [ ] Windows
32
+ [ ] OSX
33
+ [ ] All
34
+
35
+ ## Environment and Libraries (fill in the version numbers)
36
+ Cloudinary Ruby SDK version - 0.0.0
37
+ Ruby Version - 0.0.0
38
+ Rails Version - 0.0.0
39
+ Other Libraries (Carrierwave, ActiveStorage, etc) - 0.0.0
40
+
41
+ ## Repository
42
+ If possible, please provide a link to a reproducible repository that showcases the problem
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: Feature request
3
+ about: Feature request for Cloudinary Ruby SDK
4
+ title: ''
5
+ labels: ''
6
+ assignees: const-cloudinary
7
+
8
+ ---
9
+
10
+ ## Feature request for Cloudinary Ruby SDK
11
+ …(If your feature is for other SDKs, please request them there)
12
+
13
+
14
+ ## Explain your use case
15
+ … (A high level explanation of why you need this feature)
16
+
17
+ ## Describe the problem you’re trying to solve
18
+ … (A more technical view of what you’d like to accomplish, and how this feature will help you achieve it)
19
+
20
+ ## Do you have a proposed solution?
21
+ … (yes, no? Please elaborate if needed)
@@ -1,4 +1,63 @@
1
1
 
2
+ 1.16.1 / 2020-07-06
3
+ ===================
4
+
5
+ * Detect data URLs with suffix in mime type
6
+ * Fix `Invalid regular expression` error in Safari
7
+
8
+ 1.16.0 / 2020-06-29
9
+ ===================
10
+
11
+ New functionality and features
12
+ ------------------------------
13
+
14
+ * Add support for uploading `StringIO`
15
+
16
+ Other Changes
17
+ -------------
18
+
19
+ * Set default cache storage to `file` in `CarrierWave`
20
+ * Fix `normalize_expression` to ignore predefined variables
21
+ * Fix sample projects
22
+
23
+ 1.15.0 / 2020-06-11
24
+ ===================
25
+
26
+ New functionality and features
27
+ ------------------------------
28
+
29
+ * Add support for `accessibility_analysis` parameter
30
+
31
+ Other Changes
32
+ -------------
33
+ * Fix `download` function in `Cloudinary::CarrierWave`
34
+ * Fix handling of empty value in `if` parameter
35
+ * Fix consumption of configuration from environment variables
36
+
37
+ 1.14.0 / 2020-05-06
38
+ ===================
39
+
40
+ New functionality and features
41
+ ------------------------------
42
+
43
+ * Add support of global folder in ActiveStorage
44
+ * Add duration to conditions in video
45
+
46
+ Other Changes
47
+ -------------
48
+
49
+ * Fix `URI.unescape is obsolete` warning
50
+ * Fix lingering Rails 6 autoload warnings
51
+ * Fix Ruby 1.9 CI build
52
+ * Allow setting uploader timeout to `nil`
53
+ * Update link to CarrierWave integration
54
+ * Update issue templates
55
+
56
+ 1.13.2 / 2020-01-19
57
+ ===================
58
+
59
+ * Fix asset extension detection in active storage service `url` method
60
+
2
61
  1.13.1 / 2019-11-24
3
62
  ===================
4
63
 
data/Rakefile CHANGED
@@ -1,55 +1,13 @@
1
1
  require 'bundler'
2
2
  require 'fileutils'
3
- require 'tmpdir'
4
- require 'rest_client'
5
- require 'json'
6
- require 'rubygems/package'
7
-
8
3
  require 'rspec/core/rake_task'
4
+
9
5
  RSpec::Core::RakeTask.new(:spec)
10
6
  task :default => :spec
11
7
 
12
8
  Bundler::GemHelper.install_tasks
13
9
 
14
- namespace :cloudinary do
15
- desc "Fetch the latest JavaScript library files and create the JavaScript index files"
16
- task :fetch_assets do
17
- index_files = %w[jquery.ui.widget.js jquery.iframe-transport.js jquery.fileupload.js jquery.cloudinary.js]
18
- processing_files = %w[canvas-to-blob.min.js load-image.all.min.js jquery.fileupload-process.js jquery.fileupload-image.js jquery.fileupload-validate.js]
19
- files = index_files + processing_files
20
-
21
- release = JSON(RestClient.get("https://api.github.com/repos/cloudinary/cloudinary_js/releases/latest"))
22
-
23
- FileUtils.rm_rf 'vendor/assets'
24
- html_folder = 'vendor/assets/html'
25
- FileUtils.mkdir_p html_folder
26
- js_folder = 'vendor/assets/javascripts/cloudinary'
27
- FileUtils.mkdir_p js_folder
28
-
29
- puts "Fetching cloudinary_js version #{release["tag_name"]}\n\n"
30
- sio = StringIO.new(RestClient.get(release["tarball_url"]).body)
31
- file =Zlib::GzipReader.new(sio)
32
- tar = Gem::Package::TarReader.new(file)
33
- tar.each_entry do |entry|
34
- name = File.basename(entry.full_name)
35
- if files.include? name
36
- js_full_name = File.join(js_folder, name)
37
- puts "Adding #{js_full_name}"
38
- File.write js_full_name, entry.read
39
- elsif name == 'cloudinary_cors.html'
40
- html_full_name = File.join(html_folder, name)
41
- puts "Adding #{html_full_name}"
42
- File.write html_full_name, entry.read
43
- end
44
- end
45
- puts "Creating 'index.js' and 'processing.js' files"
46
- File.open("vendor/assets/javascripts/cloudinary/index.js", "w") do |f|
47
- index_files.each { |name| f.puts "//= require ./#{name}" }
48
- end
49
- File.open("vendor/assets/javascripts/cloudinary/processing.js", "w") do |f|
50
- processing_files.each { |name| f.puts "//= require ./#{name}" }
51
- end
52
- end
10
+ path = File.expand_path(__dir__)
11
+ Dir.glob("#{path}/lib/tasks/**/*.rake").each { |f| import f }
53
12
 
54
- end
55
13
  task :build => "cloudinary:fetch_assets"
@@ -24,11 +24,25 @@ Gem::Specification.new do |s|
24
24
 
25
25
  s.add_development_dependency "actionpack"
26
26
  s.add_development_dependency "nokogiri"
27
- s.add_development_dependency "rake"
27
+
28
+ if RUBY_VERSION >= "2.2.0"
29
+ s.add_development_dependency "rake", ">= 13.0.1"
30
+ else
31
+ s.add_development_dependency "rake", "<= 12.2.1"
32
+ end
33
+
28
34
  s.add_development_dependency "sqlite3"
29
35
  s.add_development_dependency "rspec", '>=3.5'
30
36
  s.add_development_dependency "rails", "~>5.2" if RUBY_VERSION >= "2.2.2"
37
+
38
+ s.add_development_dependency "railties", "<= 4.2.7" if RUBY_VERSION <= "1.9.3"
31
39
  s.add_development_dependency "rspec-rails"
40
+
32
41
  s.add_development_dependency "rubyzip", "<=1.2.0" # support testing Ruby 1.9
33
- s.add_development_dependency "simplecov"
42
+
43
+ if RUBY_VERSION <= "2.4.0"
44
+ s.add_development_dependency "simplecov", "<= 0.17.1" # support testing Ruby 1.9
45
+ else
46
+ s.add_development_dependency "simplecov", "> 0.18.0"
47
+ end
34
48
  end
@@ -12,6 +12,15 @@ unless ActiveStorage::Blob.method_defined? :original_key
12
12
  end
13
13
  end
14
14
 
15
+ module CloudinaryHelper
16
+ alias cloudinary_url_internal_original cloudinary_url_internal
17
+
18
+ def cloudinary_url_internal(source, options = {})
19
+ source = ActiveStorage::Blob.service.public_id(source) if defined? ActiveStorage::Blob.service.public_id
20
+ cloudinary_url_internal_original(source, options)
21
+ end
22
+ end
23
+
15
24
  module ActiveStorage
16
25
  class Service::CloudinaryService < Service
17
26
  module Headers
@@ -31,7 +40,7 @@ module ActiveStorage
31
40
  options = @options.merge(options)
32
41
  Cloudinary::Uploader.upload(
33
42
  io,
34
- public_id: public_id(key),
43
+ public_id: public_id_internal(key),
35
44
  resource_type: resource_type(io, key),
36
45
  context: {active_storage_key: key, checksum: checksum},
37
46
  extra_headers: extra_headers,
@@ -48,9 +57,10 @@ module ActiveStorage
48
57
  url = Cloudinary::Utils.cloudinary_url(
49
58
  public_id(key),
50
59
  resource_type: resource_type(nil, key),
51
- format: ext_for_content_type(content_type),
60
+ format: ext_for_file(filename, content_type),
52
61
  **@options.merge(options.symbolize_keys)
53
62
  )
63
+
54
64
  payload[:url] = url
55
65
 
56
66
  url
@@ -60,7 +70,7 @@ module ActiveStorage
60
70
  def url_for_direct_upload(key, **options)
61
71
  instrument :url, key: key do |payload|
62
72
  options = {:resource_type => resource_type(nil, key)}.merge(@options.merge(options.symbolize_keys))
63
- options[:public_id] = public_id(key)
73
+ options[:public_id] = public_id_internal(key)
64
74
  options[:context] = {active_storage_key: key}
65
75
  options.delete(:file)
66
76
  payload[:url] = api_uri("upload", options)
@@ -136,20 +146,41 @@ module ActiveStorage
136
146
 
137
147
  end
138
148
 
149
+ def public_id(key)
150
+ return key unless @options[:folder]
151
+
152
+ File.join(@options.fetch(:folder), public_id_internal(key))
153
+ end
154
+
139
155
  private
140
156
 
141
157
  def api_uri(action, options)
142
158
  base_url = Cloudinary::Utils.cloudinary_api_url(action, options)
143
159
  upload_params = Cloudinary::Uploader.build_upload_params(options)
144
160
 
145
- upload_params.reject! {|k, v| Cloudinary::Utils.safe_blank?(v)}
161
+ upload_params.reject! { |k, v| Cloudinary::Utils.safe_blank?(v) }
146
162
  unless options[:unsigned]
147
163
  upload_params = Cloudinary::Utils.sign_request(upload_params, options)
148
164
  end
149
165
  "#{base_url}?#{upload_params.to_query}"
150
166
  end
151
167
 
152
- def ext_for_content_type(content_type)
168
+ # Helper method for getting the filename extension.
169
+ #
170
+ # It does the best effort when original filename does not include extension, but we know the mime-type.
171
+ #
172
+ # @param [ActiveStorage::Filename] filename The original filename.
173
+ # @param [string] content_type The content type of the file.
174
+ #
175
+ # @return [string] The extension of the filename.
176
+ def ext_for_file(filename, content_type)
177
+ ext = filename.respond_to?(:extension_without_delimiter) ? filename.extension_without_delimiter : nil
178
+ return ext unless ext.blank?
179
+
180
+ # Raw files are not convertible, no extension guessing for them
181
+ return nil if content_type_to_resource_type(content_type).eql?('raw')
182
+
183
+ # Fallback when there is no extension.
153
184
  @formats ||= Hash.new do |h, key|
154
185
  ext = Rack::Mime::MIME_TYPES.invert[key]
155
186
  h[key] = ext.slice(1..-1) unless ext.nil?
@@ -157,14 +188,12 @@ module ActiveStorage
157
188
  @formats[content_type]
158
189
  end
159
190
 
160
- def public_id(key)
191
+ def public_id_internal(key)
161
192
  # TODO: Allow custom manipulation of key to obscure how we store in Cloudinary
162
193
  key
163
194
  end
164
195
 
165
- def resource_type(io, key = "")
166
- options = key.respond_to?(:attributes) ? key.attributes : {}
167
- content_type = options[:content_type] || (io.nil? ? '' : Marcel::MimeType.for(io))
196
+ def content_type_to_resource_type(content_type)
168
197
  type, subtype = content_type.split('/')
169
198
  case type
170
199
  when 'video', 'audio'
@@ -184,5 +213,11 @@ module ActiveStorage
184
213
  'image'
185
214
  end
186
215
  end
216
+
217
+ def resource_type(io, key = "")
218
+ options = key.respond_to?(:attributes) ? key.attributes : {}
219
+ content_type = options[:content_type] || (io.nil? ? '' : Marcel::MimeType.for(io))
220
+ content_type_to_resource_type(content_type)
221
+ end
187
222
  end
188
223
  end
@@ -64,19 +64,7 @@ module Cloudinary
64
64
  first_time = @@config.nil?
65
65
  @@config ||= OpenStruct.new((YAML.load(ERB.new(IO.read(config_dir.join("cloudinary.yml"))).result)[config_env] rescue {}))
66
66
 
67
- # Heroku support
68
- if first_time && ENV["CLOUDINARY_CLOUD_NAME"]
69
- set_config(
70
- "cloud_name" => ENV["CLOUDINARY_CLOUD_NAME"],
71
- "api_key" => ENV["CLOUDINARY_API_KEY"],
72
- "api_secret" => ENV["CLOUDINARY_API_SECRET"],
73
- "secure_distribution" => ENV["CLOUDINARY_SECURE_DISTRIBUTION"],
74
- "private_cdn" => ENV["CLOUDINARY_PRIVATE_CDN"].to_s == 'true',
75
- "secure" => ENV["CLOUDINARY_SECURE"].to_s == 'true'
76
- )
77
- elsif first_time && ENV["CLOUDINARY_URL"]
78
- config_from_url(ENV["CLOUDINARY_URL"])
79
- end
67
+ config_from_env if first_time
80
68
 
81
69
  set_config(new_config) if new_config
82
70
  yield(@@config) if block_given?
@@ -105,7 +93,7 @@ module Cloudinary
105
93
  if isNestedKey? key
106
94
  putNestedKey key, value
107
95
  else
108
- set_config(key => URI.decode(value))
96
+ set_config(key => Utils.smart_unescape(value))
109
97
  end
110
98
  end
111
99
  end
@@ -140,18 +128,34 @@ module Cloudinary
140
128
  end
141
129
 
142
130
  private
143
-
131
+
132
+ def self.config_from_env
133
+ # Heroku support
134
+ if ENV["CLOUDINARY_CLOUD_NAME"]
135
+ config_keys = ENV.keys.select! { |key| key.start_with? "CLOUDINARY_" }
136
+ config_keys -= ["CLOUDINARY_URL"] # ignore it when explicit options are passed
137
+ config_keys.each do |full_key|
138
+ conf_key = full_key["CLOUDINARY_".length..-1].downcase # convert "CLOUDINARY_CONFIG_NAME" to "config_name"
139
+ conf_val = ENV[full_key]
140
+ conf_val = conf_val == 'true' if %w[true false].include?(conf_val) # cast relevant boolean values
141
+ set_config(conf_key => conf_val)
142
+ end
143
+ elsif ENV["CLOUDINARY_URL"]
144
+ config_from_url(ENV["CLOUDINARY_URL"])
145
+ end
146
+ end
147
+
144
148
  def self.config_env
145
149
  return ENV["CLOUDINARY_ENV"] if ENV["CLOUDINARY_ENV"]
146
150
  return Rails.env if defined? Rails::env
147
151
  nil
148
152
  end
149
-
153
+
150
154
  def self.config_dir
151
- return Pathname.new(ENV["CLOUDINARY_CONFIG_DIR"]) if ENV["CLOUDINARY_CONFIG_DIR"]
155
+ return Pathname.new(ENV["CLOUDINARY_CONFIG_DIR"]) if ENV["CLOUDINARY_CONFIG_DIR"]
152
156
  self.app_root.join("config")
153
157
  end
154
-
158
+
155
159
  def self.set_config(new_config)
156
160
  new_config.each{|k,v| @@config.send(:"#{k}=", v) if !v.nil?}
157
161
  end
@@ -78,7 +78,7 @@ class Cloudinary::Api
78
78
  resource_type = options[:resource_type] || "image"
79
79
  type = options[:type] || "upload"
80
80
  uri = "resources/#{resource_type}/#{type}/#{public_id}"
81
- call_api(:get, uri,
81
+ call_api(:get, uri,
82
82
  only(options,
83
83
  :cinemagraph_analysis,
84
84
  :colors,
@@ -90,7 +90,8 @@ class Cloudinary::Api
90
90
  :pages,
91
91
  :phash,
92
92
  :quality_analysis,
93
- :derived_next_cursor
93
+ :derived_next_cursor,
94
+ :accessibility_analysis
94
95
  ), options)
95
96
  end
96
97
 
@@ -9,6 +9,7 @@ module Cloudinary::CarrierWave
9
9
 
10
10
  def self.included(base)
11
11
  base.storage Cloudinary::CarrierWave::Storage
12
+ base.cache_storage = :file if base.cache_storage.blank?
12
13
  base.extend ClassMethods
13
14
  base.class_attribute :metadata
14
15
  base.class_attribute :storage_type, instance_reader: false
@@ -1,10 +1,10 @@
1
1
  module Cloudinary::CarrierWave
2
2
  def download!(uri, *args)
3
- return super if !self.cloudinary_should_handle_remote?
3
+ return super unless self.cloudinary_should_handle_remote?
4
4
  if respond_to?(:process_uri)
5
5
  uri = process_uri(uri)
6
6
  else # Backward compatibility with old CarrierWave
7
- uri = URI.parse(URI.escape(URI.unescape(uri)))
7
+ uri = URI.parse(Cloudinary::Utils.smart_escape(Cloudinary::Utils.smart_unescape(uri)))
8
8
  end
9
9
  return if uri.to_s.blank?
10
10
  self.original_filename = @cache_id = @filename = File.basename(uri.path).gsub(/[^a-zA-Z0-9\.\-\+_]/, '')
@@ -1,7 +1,8 @@
1
1
  class Cloudinary::CarrierWave::Storage < ::CarrierWave::Storage::Abstract
2
2
 
3
3
  def store!(file)
4
- return if !uploader.enable_processing
4
+ return unless uploader.enable_processing
5
+
5
6
  if uploader.is_main_uploader?
6
7
  case file
7
8
  when Cloudinary::CarrierWave::PreloadedCloudinaryFile
@@ -1,13 +1,11 @@
1
1
  module Cloudinary::CloudinaryController
2
2
  protected
3
-
3
+
4
4
  def valid_cloudinary_response?
5
5
  received_signature = request.query_parameters[:signature]
6
6
  calculated_signature = Cloudinary::Utils.api_sign_request(
7
7
  request.query_parameters.select{|key, value| [:public_id, :version].include?(key.to_sym)},
8
8
  Cloudinary.config.api_secret)
9
9
  return received_signature == calculated_signature
10
- end
10
+ end
11
11
  end
12
-
13
- ActionController::Base.send :include, Cloudinary::CloudinaryController
@@ -1,8 +1,12 @@
1
1
  class Cloudinary::Railtie < Rails::Railtie
2
2
  rake_tasks do
3
- Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
4
- end
3
+ Dir[File.join(File.dirname(__FILE__),'../tasks/**/*.rake')].each { |f| load f }
4
+ end
5
5
  config.after_initialize do |app|
6
6
  ActionView::Base.send :include, CloudinaryHelper
7
7
  end
8
- end
8
+
9
+ ActiveSupport.on_load(:action_controller_base) do
10
+ ActionController::Base.send :include, Cloudinary::CloudinaryController
11
+ end
12
+ end
@@ -64,6 +64,7 @@ class Cloudinary::Uploader
64
64
  :unique_filename => Cloudinary::Utils.as_safe_bool(options[:unique_filename]),
65
65
  :upload_preset => options[:upload_preset],
66
66
  :use_filename => Cloudinary::Utils.as_safe_bool(options[:use_filename]),
67
+ :accessibility_analysis => Cloudinary::Utils.as_safe_bool(options[:accessibility_analysis])
67
68
  }
68
69
  params
69
70
  end
@@ -77,6 +78,9 @@ class Cloudinary::Uploader
77
78
  params = build_upload_params(options)
78
79
  if file.is_a?(Pathname)
79
80
  params[:file] = File.open(file, "rb")
81
+ elsif file.is_a?(StringIO)
82
+ file.rewind
83
+ params[:file] = Cloudinary::Blob.new(file.read, options)
80
84
  elsif file.respond_to?(:read) || Cloudinary::Utils.is_remote?(file)
81
85
  params[:file] = file
82
86
  else
@@ -248,7 +252,7 @@ class Cloudinary::Uploader
248
252
  end
249
253
  end
250
254
 
251
- # options may include 'exclusive' (boolean) which causes clearing this tag from all other resources
255
+ # options may include 'exclusive' (boolean) which causes clearing this tag from all other resources
252
256
  def self.add_tag(tag, public_ids = [], options = {})
253
257
  exclusive = options.delete(:exclusive)
254
258
  command = exclusive ? "set_exclusive" : "add"
@@ -317,7 +321,7 @@ class Cloudinary::Uploader
317
321
  params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret)
318
322
  params[:api_key] = api_key
319
323
  end
320
- timeout = options[:timeout] || Cloudinary.config.timeout || 60
324
+ timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
321
325
 
322
326
  result = nil
323
327
 
@@ -42,6 +42,8 @@ class Cloudinary::Utils
42
42
  "page_x" => "px",
43
43
  "page_y" => "py",
44
44
  "tags" => "tags",
45
+ "initial_duration" => "idu",
46
+ "duration" => "du",
45
47
  "width" => "w"
46
48
  }
47
49
 
@@ -137,7 +139,7 @@ class Cloudinary::Utils
137
139
  zoom
138
140
  ].map(&:to_sym)
139
141
 
140
- REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^gs:|^data:([\w-]+\/[\w-]+)?(;[\w-]+=[\w-]+)*;base64,([a-zA-Z0-9\/+\n=]+)$)
142
+ REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^gs:|^data:([\w-]+\/[\w-]+(\+[\w-]+)?)?(;[\w-]+=[\w-]+)*;base64,([a-zA-Z0-9\/+\n=]+)$)
141
143
 
142
144
  def self.extract_config_params(options)
143
145
  options.select{|k,v| URL_KEYS.include?(k)}
@@ -300,15 +302,11 @@ class Cloudinary::Utils
300
302
  # Translates the condition if provided.
301
303
  # @return [string] "if_" + ifValue
302
304
  # @private
303
- def self.process_if(ifValue)
304
- if ifValue
305
- ifValue = normalize_expression(ifValue)
306
-
307
- ifValue = "if_" + ifValue
308
- end
305
+ def self.process_if(if_value)
306
+ "if_" + normalize_expression(if_value) unless if_value.to_s.empty?
309
307
  end
310
308
 
311
- EXP_REGEXP = Regexp.new(PREDEFINED_VARS.keys.join("|")+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
309
+ EXP_REGEXP = Regexp.new('(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
312
310
  EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
313
311
 
314
312
  def self.normalize_expression(expression)
@@ -563,7 +561,7 @@ class Cloudinary::Utils
563
561
  source = smart_escape(source)
564
562
  source_to_sign = source
565
563
  else
566
- source = smart_escape(URI.decode(source))
564
+ source = smart_escape(smart_unescape(source))
567
565
  source_to_sign = source
568
566
  unless url_suffix.blank?
569
567
  raise(CloudinaryException, "url_suffix should not include . or /") if url_suffix.match(%r([\./]))
@@ -767,13 +765,18 @@ class Cloudinary::Utils
767
765
  "#{public_id}#{ext}"
768
766
  end
769
767
 
770
- # Based on CGI::unescape. In addition does not escape / :
768
+ # Based on CGI::escape. In addition does not escape / :
771
769
  def self.smart_escape(string, unsafe = /([^a-zA-Z0-9_.\-\/:]+)/)
772
770
  string.gsub(unsafe) do |m|
773
771
  '%' + m.unpack('H2' * m.bytesize).join('%').upcase
774
772
  end
775
773
  end
776
774
 
775
+ # Based on CGI::unescape. In addition keeps '+' character as is
776
+ def self.smart_unescape(string)
777
+ CGI.unescape(string.sub('+', '%2B'))
778
+ end
779
+
777
780
  def self.random_public_id
778
781
  sr = defined?(ActiveSupport::SecureRandom) ? ActiveSupport::SecureRandom : SecureRandom
779
782
  sr.base64(20).downcase.gsub(/[^a-z0-9]/, "").sub(/^[0-9]+/, '')[0,20]
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.13.1"
3
+ VERSION = "1.16.1"
4
4
  end
@@ -0,0 +1,48 @@
1
+ require 'tmpdir'
2
+ require 'rest_client'
3
+ require 'json'
4
+ require 'rubygems/package'
5
+
6
+ unless Rake::Task.task_defined?('cloudinary:fetch_assets') # prevent double-loading/execution
7
+ namespace :cloudinary do
8
+ desc "Fetch the latest JavaScript library files and create the JavaScript index files"
9
+ task :fetch_assets do
10
+ index_files = %w[jquery.ui.widget.js jquery.iframe-transport.js jquery.fileupload.js jquery.cloudinary.js]
11
+ processing_files = %w[canvas-to-blob.min.js load-image.all.min.js jquery.fileupload-process.js jquery.fileupload-image.js jquery.fileupload-validate.js]
12
+ files = index_files + processing_files
13
+
14
+ release = JSON(RestClient.get("https://api.github.com/repos/cloudinary/cloudinary_js/releases/latest"))
15
+
16
+ FileUtils.rm_rf 'vendor/assets'
17
+ html_folder = 'vendor/assets/html'
18
+ FileUtils.mkdir_p html_folder
19
+ js_folder = 'vendor/assets/javascripts/cloudinary'
20
+ FileUtils.mkdir_p js_folder
21
+
22
+ puts "Fetching cloudinary_js version #{release["tag_name"]}\n\n"
23
+ sio = StringIO.new(RestClient.get(release["tarball_url"]).body)
24
+ file = Zlib::GzipReader.new(sio)
25
+ tar = Gem::Package::TarReader.new(file)
26
+ tar.each_entry do |entry|
27
+ name = File.basename(entry.full_name)
28
+ if files.include? name
29
+ js_full_name = File.join(js_folder, name)
30
+ puts "Adding #{js_full_name}"
31
+ File.write js_full_name, entry.read
32
+ elsif name == 'cloudinary_cors.html'
33
+ html_full_name = File.join(html_folder, name)
34
+ puts "Adding #{html_full_name}"
35
+ File.write html_full_name, entry.read
36
+ end
37
+ end
38
+ puts "Creating 'index.js' and 'processing.js' files"
39
+ File.open("vendor/assets/javascripts/cloudinary/index.js", "w") do |f|
40
+ index_files.each { |name| f.puts "//= require ./#{name}" }
41
+ end
42
+ File.open("vendor/assets/javascripts/cloudinary/processing.js", "w") do |f|
43
+ processing_files.each { |name| f.puts "//= require ./#{name}" }
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -164,7 +164,7 @@ var slice = [].slice,
164
164
  * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
165
165
  * @example
166
166
  *
167
- * function Foo(){};
167
+ * function Foo(){};
168
168
  * isFunction(Foo);
169
169
  * // => true
170
170
  *
@@ -1403,7 +1403,8 @@ var slice = [].slice,
1403
1403
  "*": "mul",
1404
1404
  "/": "div",
1405
1405
  "+": "add",
1406
- "-": "sub"
1406
+ "-": "sub",
1407
+ "^": "pow",
1407
1408
  };
1408
1409
 
1409
1410
 
@@ -1472,30 +1473,33 @@ var slice = [].slice,
1472
1473
  return new this(expressionStr);
1473
1474
  };
1474
1475
 
1475
-
1476
1476
  /**
1477
1477
  * Normalize a string expression
1478
1478
  * @function Cloudinary#normalize
1479
1479
  * @param {string} expression a expression, e.g. "w gt 100", "width_gt_100", "width > 100"
1480
1480
  * @return {string} the normalized form of the value expression, e.g. "w_gt_100"
1481
1481
  */
1482
-
1483
1482
  Expression.normalize = function(expression) {
1484
- var operators, pattern, replaceRE;
1483
+ var operators, operatorsPattern, operatorsReplaceRE, predefinedVarsPattern, predefinedVarsReplaceRE;
1485
1484
  if (expression == null) {
1486
1485
  return expression;
1487
1486
  }
1488
1487
  expression = String(expression);
1489
- operators = "\\|\\||>=|<=|&&|!=|>|=|<|/|-|\\+|\\*";
1490
- pattern = "((" + operators + ")(?=[ _])|" + Object.keys(Expression.PREDEFINED_VARS).join("|") + ")";
1491
- replaceRE = new RegExp(pattern, "g");
1492
- expression = expression.replace(replaceRE, function(match) {
1493
- return Expression.OPERATORS[match] || Expression.PREDEFINED_VARS[match];
1494
- });
1488
+ operators = "\\|\\||>=|<=|&&|!=|>|=|<|/|-|\\+|\\*|\\^";
1489
+
1490
+ // operators
1491
+ operatorsPattern = "((" + operators + ")(?=[ _]))";
1492
+ operatorsReplaceRE = new RegExp(operatorsPattern, "g");
1493
+ expression = expression.replace(operatorsReplaceRE, match => Expression.OPERATORS[match]);
1494
+
1495
+ // predefined variables
1496
+ predefinedVarsPattern = "(" + Object.keys(Expression.PREDEFINED_VARS).join("|") + ")";
1497
+ predefinedVarsReplaceRE = new RegExp(predefinedVarsPattern, "g");
1498
+ expression = expression.replace(predefinedVarsReplaceRE, (match, p1, offset) => (expression[offset - 1] === '$' ? match : Expression.PREDEFINED_VARS[match]));
1499
+
1495
1500
  return expression.replace(/[ _]+/g, '_');
1496
1501
  };
1497
1502
 
1498
-
1499
1503
  /**
1500
1504
  * Serialize the expression
1501
1505
  * @return {string} the expression as a string
@@ -3070,7 +3074,7 @@ var slice = [].slice,
3070
3074
  * @protected
3071
3075
  * @param {string} key - attribute name
3072
3076
  * @param {*|boolean} value - the value of the attribute. If the value is boolean `true`, return the key only.
3073
- * @returns {string} the attribute
3077
+ * @returns {string} the attribute
3074
3078
  *
3075
3079
  */
3076
3080
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.1
4
+ version: 1.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nadav Soferman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-11-24 00:00:00.000000000 Z
13
+ date: 2020-07-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws_cf_signer
@@ -74,14 +74,14 @@ dependencies:
74
74
  requirements:
75
75
  - - ">="
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: 13.0.1
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
- version: '0'
84
+ version: 13.0.1
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: sqlite3
87
87
  requirement: !ruby/object:Gem::Requirement
@@ -156,16 +156,16 @@ dependencies:
156
156
  name: simplecov
157
157
  requirement: !ruby/object:Gem::Requirement
158
158
  requirements:
159
- - - ">="
159
+ - - ">"
160
160
  - !ruby/object:Gem::Version
161
- version: '0'
161
+ version: 0.18.0
162
162
  type: :development
163
163
  prerelease: false
164
164
  version_requirements: !ruby/object:Gem::Requirement
165
165
  requirements:
166
- - - ">="
166
+ - - ">"
167
167
  - !ruby/object:Gem::Version
168
- version: '0'
168
+ version: 0.18.0
169
169
  description: Client library for easily using the Cloudinary service
170
170
  email:
171
171
  - nadav.soferman@cloudinary.com
@@ -175,6 +175,8 @@ executables: []
175
175
  extensions: []
176
176
  extra_rdoc_files: []
177
177
  files:
178
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
179
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
178
180
  - ".gitignore"
179
181
  - ".rspec"
180
182
  - ".travis.yml"
@@ -218,7 +220,8 @@ files:
218
220
  - lib/cloudinary/utils.rb
219
221
  - lib/cloudinary/version.rb
220
222
  - lib/cloudinary/video_helper.rb
221
- - lib/tasks/cloudinary.rake
223
+ - lib/tasks/cloudinary/fetch_assets.rake
224
+ - lib/tasks/cloudinary/sync_static.rake
222
225
  - tools/update_version
223
226
  - vendor/assets/html/cloudinary_cors.html
224
227
  - vendor/assets/javascripts/cloudinary/canvas-to-blob.min.js