cloudinary 1.13.2 → 1.17.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab5096280e7accb34f68e18a4979931fa98f0c03bfae01913b388679e499f332
4
- data.tar.gz: 6d6afe81d600578d1741d5f4fb0b3a04c98bde8d9320cea260a4b371e8cd3a02
3
+ metadata.gz: e36bd9efd91c46b00fc0f52a3448ee076990a85b88a470e2cc8e7ccc610ed368
4
+ data.tar.gz: dbf3cb94bc5cae2b502b2ca1bea558c93c11e0f77d91fe0577e019452c2acc93
5
5
  SHA512:
6
- metadata.gz: 577d651df8534d2a1e294b6b86040507fff83d3af3176c91d436c528145459c110786ae49793a3aa589ae8d9d6107606a2854e5b494ba2207f74174390cb39e7
7
- data.tar.gz: 9e8857df349c08949ad9e3327e2d4acad3f7bd4c4f36a667a9e5940f47555df92522ddd9e5cbe55387674994364b1087913bc5e7674f530eacd35a592470ef42
6
+ metadata.gz: ba800e50f02feb8cd582a4ca42f6bdfe0fd77c6e36c9dd7e0c3314c9d944bafc451329c79276b02b417f04357aad5ff0ad71e32d3b433f36f2732bfbba70fa84
7
+ data.tar.gz: de478592b7552f4aa1b6459effe907b692d22b334b63eb1dc02e7a3ca5e0e5779c7fe82306d3211935df1964f085fdba0e3e6f159716a0055dc45e370be8f6c4
@@ -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)
@@ -0,0 +1,24 @@
1
+ ### Brief Summary of Changes
2
+ <!--
3
+ Provide some context as to what was changed, from an implementation standpoint.
4
+ -->
5
+
6
+ #### What does this PR address?
7
+ [ ] Gitub issue (Add reference - #XX)
8
+ [ ] Refactoring
9
+ [ ] New feature
10
+ [ ] Bug fix
11
+ [ ] Adds more tests
12
+
13
+ #### Are tests included?
14
+ [ ] Yes
15
+ [ ] No
16
+
17
+ #### Reviewer, Please Note:
18
+ <!--
19
+ List anything here that the reviewer should pay special attention to. This might
20
+ include, for example:
21
+ • Dependence on other PRs
22
+ • Reference to other Cloudinary SDKs
23
+ • Changes that seem arbitrary without further explanations
24
+ -->
@@ -12,4 +12,8 @@ matrix:
12
12
  rvm: ruby-2.5.3
13
13
  before_install:
14
14
  - gem install bundler
15
+
16
+ before_script: >
17
+ export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);
18
+ echo cloud_name: "$(echo $CLOUDINARY_URL | cut -d'@' -f2)"
15
19
  script: bundle exec rspec
@@ -1,4 +1,75 @@
1
1
 
2
+ 1.17.0 / 2020-08-21
3
+ ===================
4
+
5
+ New functionality and features
6
+ ------------------------------
7
+
8
+ * Add support for `eval` upload parameter
9
+ * Add support for 32-char signature length
10
+
11
+ Other Changes
12
+ -------------
13
+
14
+ * Fix escaping of query string characters in CarrierWave integration
15
+ * Fix detection integration test
16
+ * Integrate with sub-account test service
17
+ * Add pull request template
18
+
19
+ 1.16.1 / 2020-07-06
20
+ ===================
21
+
22
+ * Detect data URLs with suffix in mime type
23
+ * Fix `Invalid regular expression` error in Safari
24
+
25
+ 1.16.0 / 2020-06-29
26
+ ===================
27
+
28
+ New functionality and features
29
+ ------------------------------
30
+
31
+ * Add support for uploading `StringIO`
32
+
33
+ Other Changes
34
+ -------------
35
+
36
+ * Set default cache storage to `file` in `CarrierWave`
37
+ * Fix `normalize_expression` to ignore predefined variables
38
+ * Fix sample projects
39
+
40
+ 1.15.0 / 2020-06-11
41
+ ===================
42
+
43
+ New functionality and features
44
+ ------------------------------
45
+
46
+ * Add support for `accessibility_analysis` parameter
47
+
48
+ Other Changes
49
+ -------------
50
+ * Fix `download` function in `Cloudinary::CarrierWave`
51
+ * Fix handling of empty value in `if` parameter
52
+ * Fix consumption of configuration from environment variables
53
+
54
+ 1.14.0 / 2020-05-06
55
+ ===================
56
+
57
+ New functionality and features
58
+ ------------------------------
59
+
60
+ * Add support of global folder in ActiveStorage
61
+ * Add duration to conditions in video
62
+
63
+ Other Changes
64
+ -------------
65
+
66
+ * Fix `URI.unescape is obsolete` warning
67
+ * Fix lingering Rails 6 autoload warnings
68
+ * Fix Ruby 1.9 CI build
69
+ * Allow setting uploader timeout to `nil`
70
+ * Update link to CarrierWave integration
71
+ * Update issue templates
72
+
2
73
  1.13.2 / 2020-01-19
3
74
  ===================
4
75
 
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,26 @@ 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'
36
+ s.add_development_dependency "rspec-retry"
30
37
  s.add_development_dependency "rails", "~>5.2" if RUBY_VERSION >= "2.2.2"
38
+
39
+ s.add_development_dependency "railties", "<= 4.2.7" if RUBY_VERSION <= "1.9.3"
31
40
  s.add_development_dependency "rspec-rails"
41
+
32
42
  s.add_development_dependency "rubyzip", "<=1.2.0" # support testing Ruby 1.9
33
- s.add_development_dependency "simplecov"
43
+
44
+ if RUBY_VERSION <= "2.4.0"
45
+ s.add_development_dependency "simplecov", "<= 0.17.1" # support testing Ruby 1.9
46
+ else
47
+ s.add_development_dependency "simplecov", "> 0.18.0"
48
+ end
34
49
  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,
@@ -61,7 +70,7 @@ module ActiveStorage
61
70
  def url_for_direct_upload(key, **options)
62
71
  instrument :url, key: key do |payload|
63
72
  options = {:resource_type => resource_type(nil, key)}.merge(@options.merge(options.symbolize_keys))
64
- options[:public_id] = public_id(key)
73
+ options[:public_id] = public_id_internal(key)
65
74
  options[:context] = {active_storage_key: key}
66
75
  options.delete(:file)
67
76
  payload[:url] = api_uri("upload", options)
@@ -137,13 +146,19 @@ module ActiveStorage
137
146
 
138
147
  end
139
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
+
140
155
  private
141
156
 
142
157
  def api_uri(action, options)
143
158
  base_url = Cloudinary::Utils.cloudinary_api_url(action, options)
144
159
  upload_params = Cloudinary::Uploader.build_upload_params(options)
145
160
 
146
- upload_params.reject! {|k, v| Cloudinary::Utils.safe_blank?(v)}
161
+ upload_params.reject! { |k, v| Cloudinary::Utils.safe_blank?(v) }
147
162
  unless options[:unsigned]
148
163
  upload_params = Cloudinary::Utils.sign_request(upload_params, options)
149
164
  end
@@ -173,7 +188,7 @@ module ActiveStorage
173
188
  @formats[content_type]
174
189
  end
175
190
 
176
- def public_id(key)
191
+ def public_id_internal(key)
177
192
  # TODO: Allow custom manipulation of key to obscure how we store in Cloudinary
178
193
  key
179
194
  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,11 @@
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
+ remote_url_unsafe_chars = /([^a-zA-Z0-9_.\-\/:?&=]+)/ # In addition allow query string characters: "?","&" and "="
8
+ uri = URI.parse(Cloudinary::Utils.smart_escape(Cloudinary::Utils.smart_unescape(uri), remote_url_unsafe_chars))
8
9
  end
9
10
  return if uri.to_s.blank?
10
11
  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
@@ -37,6 +37,7 @@ class Cloudinary::Uploader
37
37
  :eager_async => Cloudinary::Utils.as_safe_bool(options[:eager_async]),
38
38
  :eager_notification_url => options[:eager_notification_url],
39
39
  :exif => Cloudinary::Utils.as_safe_bool(options[:exif]),
40
+ :eval => options[:eval],
40
41
  :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
41
42
  :faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
42
43
  :folder => options[:folder],
@@ -64,6 +65,7 @@ class Cloudinary::Uploader
64
65
  :unique_filename => Cloudinary::Utils.as_safe_bool(options[:unique_filename]),
65
66
  :upload_preset => options[:upload_preset],
66
67
  :use_filename => Cloudinary::Utils.as_safe_bool(options[:use_filename]),
68
+ :accessibility_analysis => Cloudinary::Utils.as_safe_bool(options[:accessibility_analysis])
67
69
  }
68
70
  params
69
71
  end
@@ -77,6 +79,9 @@ class Cloudinary::Uploader
77
79
  params = build_upload_params(options)
78
80
  if file.is_a?(Pathname)
79
81
  params[:file] = File.open(file, "rb")
82
+ elsif file.is_a?(StringIO)
83
+ file.rewind
84
+ params[:file] = Cloudinary::Blob.new(file.read, options)
80
85
  elsif file.respond_to?(:read) || Cloudinary::Utils.is_remote?(file)
81
86
  params[:file] = file
82
87
  else
@@ -248,7 +253,7 @@ class Cloudinary::Uploader
248
253
  end
249
254
  end
250
255
 
251
- # options may include 'exclusive' (boolean) which causes clearing this tag from all other resources
256
+ # options may include 'exclusive' (boolean) which causes clearing this tag from all other resources
252
257
  def self.add_tag(tag, public_ids = [], options = {})
253
258
  exclusive = options.delete(:exclusive)
254
259
  command = exclusive ? "set_exclusive" : "add"
@@ -317,7 +322,7 @@ class Cloudinary::Uploader
317
322
  params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret)
318
323
  params[:api_key] = api_key
319
324
  end
320
- timeout = options[:timeout] || Cloudinary.config.timeout || 60
325
+ timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
321
326
 
322
327
  result = nil
323
328
 
@@ -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,10 @@ 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=]+)$)
143
+
144
+ LONG_URL_SIGNATURE_LENGTH = 32
145
+ SHORT_URL_SIGNATURE_LENGTH = 8
141
146
 
142
147
  def self.extract_config_params(options)
143
148
  options.select{|k,v| URL_KEYS.include?(k)}
@@ -300,15 +305,11 @@ class Cloudinary::Utils
300
305
  # Translates the condition if provided.
301
306
  # @return [string] "if_" + ifValue
302
307
  # @private
303
- def self.process_if(ifValue)
304
- if ifValue
305
- ifValue = normalize_expression(ifValue)
306
-
307
- ifValue = "if_" + ifValue
308
- end
308
+ def self.process_if(if_value)
309
+ "if_" + normalize_expression(if_value) unless if_value.to_s.empty?
309
310
  end
310
311
 
311
- EXP_REGEXP = Regexp.new(PREDEFINED_VARS.keys.join("|")+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
312
+ EXP_REGEXP = Regexp.new('(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
312
313
  EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
313
314
 
314
315
  def self.normalize_expression(expression)
@@ -499,6 +500,7 @@ class Cloudinary::Utils
499
500
  url_suffix = options.delete(:url_suffix)
500
501
  use_root_path = config_option_consume(options, :use_root_path)
501
502
  auth_token = config_option_consume(options, :auth_token)
503
+ long_url_signature = config_option_consume(options, :long_url_signature)
502
504
  unless auth_token == false
503
505
  auth_token = Cloudinary::AuthToken.merge_auth_token(Cloudinary.config.auth_token, auth_token)
504
506
  end
@@ -543,7 +545,7 @@ class Cloudinary::Utils
543
545
  raise(CloudinaryException, "Must supply api_secret") if (secret.nil? || secret.empty?)
544
546
  to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
545
547
  to_sign = fully_unescape(to_sign)
546
- signature = 's--' + Base64.urlsafe_encode64(Digest::SHA1.digest(to_sign + secret))[0,8] + '--'
548
+ signature = compute_signature(to_sign, secret, long_url_signature)
547
549
  end
548
550
 
549
551
  prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
@@ -563,7 +565,7 @@ class Cloudinary::Utils
563
565
  source = smart_escape(source)
564
566
  source_to_sign = source
565
567
  else
566
- source = smart_escape(URI.decode(source))
568
+ source = smart_escape(smart_unescape(source))
567
569
  source_to_sign = source
568
570
  unless url_suffix.blank?
569
571
  raise(CloudinaryException, "url_suffix should not include . or /") if url_suffix.match(%r([\./]))
@@ -767,13 +769,18 @@ class Cloudinary::Utils
767
769
  "#{public_id}#{ext}"
768
770
  end
769
771
 
770
- # Based on CGI::unescape. In addition does not escape / :
772
+ # Based on CGI::escape. In addition does not escape / :
771
773
  def self.smart_escape(string, unsafe = /([^a-zA-Z0-9_.\-\/:]+)/)
772
774
  string.gsub(unsafe) do |m|
773
775
  '%' + m.unpack('H2' * m.bytesize).join('%').upcase
774
776
  end
775
777
  end
776
778
 
779
+ # Based on CGI::unescape. In addition keeps '+' character as is
780
+ def self.smart_unescape(string)
781
+ CGI.unescape(string.sub('+', '%2B'))
782
+ end
783
+
777
784
  def self.random_public_id
778
785
  sr = defined?(ActiveSupport::SecureRandom) ? ActiveSupport::SecureRandom : SecureRandom
779
786
  sr.base64(20).downcase.gsub(/[^a-z0-9]/, "").sub(/^[0-9]+/, '')[0,20]
@@ -1129,4 +1136,24 @@ class Cloudinary::Utils
1129
1136
  def self.is_remote?(url)
1130
1137
  REMOTE_URL_REGEX === url
1131
1138
  end
1139
+
1140
+ # Computes a short or long signature based on a message and secret
1141
+ # @param [String] message The string to sign
1142
+ # @param [String] secret A secret that will be added to the message when signing
1143
+ # @param [Boolean] long_signature Whether to create a short or long signature
1144
+ # @return [String] Properly formatted signature
1145
+ def self.compute_signature(message, secret, long_url_signature)
1146
+ combined_message_secret = message + secret
1147
+
1148
+ algo, signature_length =
1149
+ if long_url_signature
1150
+ [Digest::SHA256, LONG_URL_SIGNATURE_LENGTH]
1151
+ else
1152
+ [Digest::SHA1, SHORT_URL_SIGNATURE_LENGTH]
1153
+ end
1154
+
1155
+ "s--#{Base64.urlsafe_encode64(algo.digest(combined_message_secret))[0, signature_length]}--"
1156
+ end
1157
+
1158
+ private_class_method :compute_signature
1132
1159
  end
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.13.2"
3
+ VERSION = "1.17.0"
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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ API_ENDPOINT="https://sub-account-testing.cloudinary.com/create_sub_account"
4
+
5
+ SDK_NAME="${1}"
6
+
7
+ CLOUD_DETAILS=$(curl -sS -d "{\"prefix\" : \"${SDK_NAME}\"}" "${API_ENDPOINT}")
8
+
9
+ echo "${CLOUD_DETAILS}" | ruby -e "require 'json'; c=JSON.parse(ARGF.read)['payload']; puts 'cloudinary://' + c['cloudApiKey'] + ':'+ c['cloudApiSecret'] + '@' + c['cloudName']"
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4
+
5
+ RUBY_VER=$(ruby -v | head -n 1 | cut -d ' ' -f 2);
6
+ SDK_VER=$(grep -oiP '(?<=VERSION = ")([a-zA-Z0-9\-.]+)(?=")' lib/cloudinary/version.rb)
7
+
8
+
9
+ bash "${DIR}"/allocate_test_cloud.sh "Ruby ${RUBY_VER} SDK ${SDK_VER}"
@@ -12,6 +12,8 @@ QUOTE=
12
12
 
13
13
  NEW_VERSION=
14
14
 
15
+ UPDATE_ONLY=false
16
+
15
17
  function echo_err
16
18
  {
17
19
  echo "$@" 1>&2;
@@ -23,6 +25,7 @@ function usage
23
25
  echo " -v | --version <version> set a new version"
24
26
  echo " -c | --current show current version"
25
27
  echo " -d | --dry-run print the commands without executing them"
28
+ echo " -u | --update-only only update the version"
26
29
  echo " -h | --help print this information and exit"
27
30
  echo
28
31
  echo "For example: $0 -v 1.2.3"
@@ -30,7 +33,7 @@ function usage
30
33
 
31
34
  function process_arguments
32
35
  {
33
- while [ "$1" != "" ]; do
36
+ while [[ "$1" != "" ]]; do
34
37
  case $1 in
35
38
  -v | --version )
36
39
  shift
@@ -53,6 +56,11 @@ function process_arguments
53
56
  echo "Dry Run"
54
57
  echo ""
55
58
  ;;
59
+ -u | --update-only )
60
+ UPDATE_ONLY=true
61
+ echo "Only update version"
62
+ echo ""
63
+ ;;
56
64
  -h | --help )
57
65
  usage; return 0
58
66
  ;;
@@ -72,7 +80,7 @@ function pushd
72
80
  # Intentionally make popd silent
73
81
  function popd
74
82
  {
75
- command popd "$@" > /dev/null
83
+ command popd > /dev/null
76
84
  }
77
85
 
78
86
  # Check if one version is less than or equal than other
@@ -82,7 +90,7 @@ function popd
82
90
  # ver_lte 1.2.4 1.2.3 && echo "yes" || echo "no" # no
83
91
  function ver_lte
84
92
  {
85
- [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
93
+ [[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]]
86
94
  }
87
95
 
88
96
  # Extract the last entry or entry for a given version
@@ -108,6 +116,10 @@ function verify_dependencies
108
116
  return 1
109
117
  fi
110
118
 
119
+ if [[ "${UPDATE_ONLY}" = true ]]; then
120
+ return 0;
121
+ fi
122
+
111
123
  if [[ -z "$(type -t git-changelog)" ]]
112
124
  then
113
125
  echo_err "git-extras packages is not installed."
@@ -127,25 +139,26 @@ function safe_replace
127
139
 
128
140
  grep -q "${old}" "${file}" || { echo_err "${old} was not found in ${file}"; return 1; }
129
141
 
130
- ${CMD_PREFIX} sed -E -i '.bak' "${QUOTE}s/${old}/${new}/${QUOTE}" "${file}"
142
+ ${CMD_PREFIX} sed -i.bak -e "${QUOTE}s/${old}/${new}/${QUOTE}" -- "${file}" && rm -- "${file}.bak"
131
143
  }
132
144
 
133
145
  function current_version
134
146
  {
135
- grep -oiP '(?<=VERSION = ")([0-9.]+[^ ]*)(?=")' lib/cloudinary/version.rb
147
+ grep -oiP '(?<=VERSION = ")([a-zA-Z0-9\-.]+)(?=")' lib/cloudinary/version.rb
136
148
  }
137
149
 
138
150
  function update_version
139
151
  {
140
- if [ -z "${NEW_VERSION}" ]; then
152
+ if [[ -z "${NEW_VERSION}" ]]; then
141
153
  usage; return 1
142
154
  fi
143
155
 
144
156
  # Enter git root
145
157
  pushd $(git rev-parse --show-toplevel)
158
+
146
159
  local current_version=$(current_version)
147
160
 
148
- if [ -z "${current_version}" ]; then
161
+ if [[ -z "${current_version}" ]]; then
149
162
  echo_err "Failed getting current version, please check directory structure and/or contact developer"
150
163
  return 1
151
164
  fi
@@ -166,12 +179,17 @@ function update_version
166
179
  lib/cloudinary/version.rb\
167
180
  || return 1
168
181
 
169
- ${CMD_PREFIX} git changelog -t ${NEW_VERSION} || true
182
+ if [[ "${UPDATE_ONLY}" = true ]]; then
183
+ popd;
184
+ return 0;
185
+ fi
186
+
187
+ ${CMD_PREFIX} git changelog -t "${NEW_VERSION}" || true
170
188
 
171
189
  echo ""
172
190
  echo "# After editing CHANGELOG.md, optionally review changes and issue these commands:"
173
191
  echo git add lib/cloudinary/version.rb CHANGELOG.md
174
- echo git commit -m \"Version ${NEW_VERSION}\"
192
+ echo git commit -m "\"Version ${NEW_VERSION}\""
175
193
  echo sed -e "'1,/^${NEW_VERSION//./\\.}/d'" \
176
194
  -e "'/^=/d'" \
177
195
  -e "'/^$/d'" \
@@ -180,7 +198,7 @@ function update_version
180
198
  \| git tag -a "'${NEW_VERSION}'" --file=-
181
199
 
182
200
  # Don't run those commands on dry run
183
- [ -n "${CMD_PREFIX}" ] && { popd; return 0; }
201
+ [[ -n "${CMD_PREFIX}" ]] && { popd; return 0; }
184
202
 
185
203
  echo ""
186
204
  read -p "Run the above commands automatically? (y/N): " confirm && [[ ${confirm} == [yY] || ${confirm} == [yY][eE][sS] ]] || { popd; return 0; }
@@ -197,6 +215,6 @@ function update_version
197
215
  popd
198
216
  }
199
217
 
218
+ process_arguments "$@"
200
219
  verify_dependencies
201
- process_arguments $*
202
220
  update_version
@@ -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,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.2
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nadav Soferman
8
8
  - Itai Lahan
9
9
  - Tal Lev-Ami
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-01-19 00:00:00.000000000 Z
13
+ date: 2020-08-21 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
@@ -110,6 +110,20 @@ dependencies:
110
110
  - - ">="
111
111
  - !ruby/object:Gem::Version
112
112
  version: '3.5'
113
+ - !ruby/object:Gem::Dependency
114
+ name: rspec-retry
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
113
127
  - !ruby/object:Gem::Dependency
114
128
  name: rails
115
129
  requirement: !ruby/object:Gem::Requirement
@@ -156,16 +170,16 @@ dependencies:
156
170
  name: simplecov
157
171
  requirement: !ruby/object:Gem::Requirement
158
172
  requirements:
159
- - - ">="
173
+ - - ">"
160
174
  - !ruby/object:Gem::Version
161
- version: '0'
175
+ version: 0.18.0
162
176
  type: :development
163
177
  prerelease: false
164
178
  version_requirements: !ruby/object:Gem::Requirement
165
179
  requirements:
166
- - - ">="
180
+ - - ">"
167
181
  - !ruby/object:Gem::Version
168
- version: '0'
182
+ version: 0.18.0
169
183
  description: Client library for easily using the Cloudinary service
170
184
  email:
171
185
  - nadav.soferman@cloudinary.com
@@ -175,6 +189,9 @@ executables: []
175
189
  extensions: []
176
190
  extra_rdoc_files: []
177
191
  files:
192
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
193
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
194
+ - ".github/pull_request_template.md"
178
195
  - ".gitignore"
179
196
  - ".rspec"
180
197
  - ".travis.yml"
@@ -218,7 +235,10 @@ files:
218
235
  - lib/cloudinary/utils.rb
219
236
  - lib/cloudinary/version.rb
220
237
  - lib/cloudinary/video_helper.rb
221
- - lib/tasks/cloudinary.rake
238
+ - lib/tasks/cloudinary/fetch_assets.rake
239
+ - lib/tasks/cloudinary/sync_static.rake
240
+ - tools/allocate_test_cloud.sh
241
+ - tools/get_test_cloud.sh
222
242
  - tools/update_version
223
243
  - vendor/assets/html/cloudinary_cors.html
224
244
  - vendor/assets/javascripts/cloudinary/canvas-to-blob.min.js
@@ -236,7 +256,7 @@ homepage: http://cloudinary.com
236
256
  licenses:
237
257
  - MIT
238
258
  metadata: {}
239
- post_install_message:
259
+ post_install_message:
240
260
  rdoc_options: []
241
261
  require_paths:
242
262
  - lib
@@ -251,8 +271,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
271
  - !ruby/object:Gem::Version
252
272
  version: '0'
253
273
  requirements: []
254
- rubygems_version: 3.0.6
255
- signing_key:
274
+ rubygems_version: 3.1.4
275
+ signing_key:
256
276
  specification_version: 4
257
277
  summary: Client library for easily using the Cloudinary service
258
278
  test_files: []