cloudinary 1.11.1 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  4. data/.github/pull_request_template.md +24 -0
  5. data/.gitignore +6 -1
  6. data/.travis.yml +15 -5
  7. data/CHANGELOG.md +149 -0
  8. data/Rakefile +3 -45
  9. data/cloudinary.gemspec +20 -4
  10. data/lib/active_storage/blob_key.rb +20 -0
  11. data/lib/active_storage/service/cloudinary_service.rb +229 -0
  12. data/lib/cloudinary.rb +31 -22
  13. data/lib/cloudinary/api.rb +173 -5
  14. data/lib/cloudinary/auth_token.rb +6 -4
  15. data/lib/cloudinary/carrier_wave.rb +3 -1
  16. data/lib/cloudinary/carrier_wave/remote.rb +3 -2
  17. data/lib/cloudinary/carrier_wave/storage.rb +2 -1
  18. data/lib/cloudinary/cloudinary_controller.rb +2 -4
  19. data/lib/cloudinary/helper.rb +30 -3
  20. data/lib/cloudinary/railtie.rb +7 -3
  21. data/lib/cloudinary/uploader.rb +35 -6
  22. data/lib/cloudinary/utils.rb +112 -40
  23. data/lib/cloudinary/version.rb +1 -1
  24. data/lib/cloudinary/video_helper.rb +96 -22
  25. data/lib/tasks/cloudinary/fetch_assets.rake +48 -0
  26. data/lib/tasks/{cloudinary.rake → cloudinary/sync_static.rake} +0 -0
  27. data/tools/allocate_test_cloud.sh +9 -0
  28. data/tools/get_test_cloud.sh +9 -0
  29. data/tools/update_version +29 -11
  30. data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +48 -14
  31. data/vendor/assets/javascripts/cloudinary/jquery.fileupload.js +24 -4
  32. data/vendor/assets/javascripts/cloudinary/jquery.ui.widget.js +741 -561
  33. data/vendor/assets/javascripts/cloudinary/load-image.all.min.js +1 -1
  34. metadata +62 -67
  35. data/spec/access_control_spec.rb +0 -102
  36. data/spec/api_spec.rb +0 -567
  37. data/spec/archive_spec.rb +0 -129
  38. data/spec/auth_token_spec.rb +0 -77
  39. data/spec/cache_spec.rb +0 -109
  40. data/spec/cloudinary_helper_spec.rb +0 -325
  41. data/spec/cloudinary_spec.rb +0 -32
  42. data/spec/data/sync_static/app/assets/javascripts/1.coffee +0 -1
  43. data/spec/data/sync_static/app/assets/javascripts/1.js +0 -1
  44. data/spec/data/sync_static/app/assets/stylesheets/1.css +0 -3
  45. data/spec/docx.docx +0 -0
  46. data/spec/favicon.ico +0 -0
  47. data/spec/image_spec.rb +0 -107
  48. data/spec/logo.png +0 -0
  49. data/spec/rake_spec.rb +0 -160
  50. data/spec/sample_asset_file.tsv +0 -4
  51. data/spec/search_spec.rb +0 -109
  52. data/spec/spec_helper.rb +0 -266
  53. data/spec/storage_spec.rb +0 -44
  54. data/spec/streaminig_profiles_api_spec.rb +0 -74
  55. data/spec/support/helpers/temp_file_helpers.rb +0 -22
  56. data/spec/support/shared_contexts/rake.rb +0 -19
  57. data/spec/uploader_spec.rb +0 -392
  58. data/spec/utils_methods_spec.rb +0 -54
  59. data/spec/utils_spec.rb +0 -970
  60. data/spec/video_tag_spec.rb +0 -253
  61. data/spec/video_url_spec.rb +0 -185
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 629a48ffc8543fc7fc8da12f12d88823232fe70a08556c49b9583437bcc24c45
4
- data.tar.gz: 1a34d49c36238a396fc3d1494845f0f97ae71b18bf49175e255c9fbea239031f
3
+ metadata.gz: c9179fe1c8105281b33780687c2bed49db41a35fca2d33ff15bd7fd4635d119f
4
+ data.tar.gz: 7b3bd01f71f1012bc7f315f707450ebb65537064e004fad615d713f489c4316c
5
5
  SHA512:
6
- metadata.gz: 9be4a6c2470fcdbab667610878db7934bff588ceda810e4917caa36b28c9c3e910214ebd4f21baca258ff643f7bd8e7277af4dda89779da356473d49b2cc73b8
7
- data.tar.gz: b308cf1059df2bc58ba103d67b845c0a3a2384a4f53e387f950057abbc502ef425506b2861b1b471689b21cd3e04f1c960fe230559ad8d5830babc203a85b2c0
6
+ metadata.gz: 347fc122e9a31aa241afa194c09cdc3c0d19e63c5a206cc7751f4939de7c273769d648c7fd33af2cc4e7505fba0a5be9d555f1c3db1e2b41d45cfb58082e1cc3
7
+ data.tar.gz: 4f04b59256afe8f23c27625d94d761e9802e2ccd91d3c3dc992eb263e3430a7a053467c203d416d379815e317924f042c9310ac0f2b4ca185630307268aa66ca
@@ -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
+ -->
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,19 @@
1
1
  language: ruby
2
2
 
3
- before_install:
4
- - gem install bundler
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
5
15
 
6
- rvm:
7
- - 1.9.3
8
- - ruby-head
16
+ before_script: >
17
+ export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);
18
+ echo cloud_name: "$(echo $CLOUDINARY_URL | cut -d'@' -f2)"
9
19
  script: bundle exec rspec
@@ -1,4 +1,153 @@
1
1
 
2
+ 1.18.0 / 2020-09-27
3
+ ===================
4
+
5
+ New functionality and features
6
+ ------------------------------
7
+ * Add `download_folder` helper
8
+ * Add support for `sources` in `video` tag
9
+ * Add structured metadata to Admin and Upload API
10
+
11
+ Other Changes
12
+ -------------
13
+ * Fix download of a raw file in ActiveStorage
14
+ * Update embedded `jquery.cloudinary.js` to fix ES5 compatibility issue
15
+
16
+ 1.17.1 / 2020-08-25
17
+ ===================
18
+
19
+ * Fix options handling issue in SassC
20
+
21
+ 1.17.0 / 2020-08-21
22
+ ===================
23
+
24
+ New functionality and features
25
+ ------------------------------
26
+
27
+ * Add support for `eval` upload parameter
28
+ * Add support for 32-char signature length
29
+
30
+ Other Changes
31
+ -------------
32
+
33
+ * Fix escaping of query string characters in CarrierWave integration
34
+ * Fix detection integration test
35
+ * Integrate with sub-account test service
36
+ * Add pull request template
37
+
38
+ 1.16.1 / 2020-07-06
39
+ ===================
40
+
41
+ * Detect data URLs with suffix in mime type
42
+ * Fix `Invalid regular expression` error in Safari
43
+
44
+ 1.16.0 / 2020-06-29
45
+ ===================
46
+
47
+ New functionality and features
48
+ ------------------------------
49
+
50
+ * Add support for uploading `StringIO`
51
+
52
+ Other Changes
53
+ -------------
54
+
55
+ * Set default cache storage to `file` in `CarrierWave`
56
+ * Fix `normalize_expression` to ignore predefined variables
57
+ * Fix sample projects
58
+
59
+ 1.15.0 / 2020-06-11
60
+ ===================
61
+
62
+ New functionality and features
63
+ ------------------------------
64
+
65
+ * Add support for `accessibility_analysis` parameter
66
+
67
+ Other Changes
68
+ -------------
69
+ * Fix `download` function in `Cloudinary::CarrierWave`
70
+ * Fix handling of empty value in `if` parameter
71
+ * Fix consumption of configuration from environment variables
72
+
73
+ 1.14.0 / 2020-05-06
74
+ ===================
75
+
76
+ New functionality and features
77
+ ------------------------------
78
+
79
+ * Add support of global folder in ActiveStorage
80
+ * Add duration to conditions in video
81
+
82
+ Other Changes
83
+ -------------
84
+
85
+ * Fix `URI.unescape is obsolete` warning
86
+ * Fix lingering Rails 6 autoload warnings
87
+ * Fix Ruby 1.9 CI build
88
+ * Allow setting uploader timeout to `nil`
89
+ * Update link to CarrierWave integration
90
+ * Update issue templates
91
+
92
+ 1.13.2 / 2020-01-19
93
+ ===================
94
+
95
+ * Fix asset extension detection in active storage service `url` method
96
+
97
+ 1.13.1 / 2019-11-24
98
+ ===================
99
+
100
+ * Remove `test_files` from `gemspec`
101
+ * Remove redundant binary files from `active_storage` spec
102
+ * Fix `rspec` deprecation warnings in Rails 6
103
+ * Add test for uploading IO
104
+
105
+ 1.13.0 / 2019-11-14
106
+ ===================
107
+
108
+ New functionality and features
109
+ ------------------------------
110
+ * Add `SassC` `cloudinary-url` function
111
+
112
+ Other Changes
113
+ -------------
114
+
115
+ * Fix ActiveStorage download not using `ssl` for `https`
116
+ * Fix resource type detection in ActiveStorage
117
+ * Fix `storage_type` instance method in `Cloudinary::CarrierWave` module
118
+ * Fix sample project, limit sqlite3 to a compatible version
119
+
120
+ 1.12.0 / 2019-10-02
121
+ =============
122
+
123
+ New functionality and features
124
+ ------------------------------
125
+
126
+ * Add Cloudinary service for ActiveStorage
127
+ * Add `create_folder` Admin API method
128
+ * Add `delete_folder` Admin API method
129
+ * Add `cinemagraph_analysis` to `upload`, `explicit` and `resource` API methods
130
+ * Add `font_antialiasing` and `font_hinting` text style parameters
131
+ * Add `derived_next_cursor` parameter to `resource` Admin API
132
+ * Add `next_cursor` and `max_results` for `root_folders` and `subfolders` Admin API functions
133
+ * Add `jpeg` to `IMAGE_FORMATS`
134
+ * Add `pow` transformation operator
135
+ * Add `force_version` to `cloudinary_url`
136
+ * Support per corner values for the `radius` transformation parameter
137
+ * Support using multiple resource types when generating archives
138
+ * Support Google Storage fetch URL
139
+
140
+ Other Changes
141
+ -------------
142
+ * Ensure `CLOUDINARY_URL` starts with `cloudinary://`
143
+ * Reduce memory usage in `Cloudinary::Utils.cloudinary_url`
144
+ * Encode URL in Admin API methods
145
+ * Fix base64 data validation
146
+ * Return `video` as the `resource_type` for audio files
147
+ * Add language and platform version for ruby/rails user agent
148
+ * Fix TravisCI configuration for ruby 1.9
149
+
150
+
2
151
  1.11.1 / 2018-12-22
3
152
  ===================
4
153
 
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"
@@ -15,8 +15,7 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.rubyforge_project = "cloudinary"
17
17
 
18
- s.files = (`git ls-files`.split("\n") - `git ls-files samples`.split("\n")) + Dir.glob("vendor/assets/javascripts/*/*") + Dir.glob("vendor/assets/html/*")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.files = (`git ls-files`.split("\n") - `git ls-files {test,spec,features,samples}/*`.split("\n")) + Dir.glob("vendor/assets/javascripts/*/*") + Dir.glob("vendor/assets/html/*")
20
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
20
  s.require_paths = ["lib"]
22
21
 
@@ -25,9 +24,26 @@ Gem::Specification.new do |s|
25
24
 
26
25
  s.add_development_dependency "actionpack"
27
26
  s.add_development_dependency "nokogiri"
28
- 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
+
34
+ s.add_development_dependency "sqlite3"
29
35
  s.add_development_dependency "rspec", '>=3.5'
36
+ s.add_development_dependency "rspec-retry"
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"
30
40
  s.add_development_dependency "rspec-rails"
41
+
31
42
  s.add_development_dependency "rubyzip", "<=1.2.0" # support testing Ruby 1.9
32
- 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
33
49
  end
@@ -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,229 @@
1
+ require 'active_storage/blob_key'
2
+ require 'cloudinary/helper'
3
+ require 'net/http'
4
+
5
+ unless ActiveStorage::Blob.method_defined? :original_key
6
+ class ActiveStorage::Blob
7
+ alias_method :original_key, :key
8
+
9
+ def key
10
+ original_key
11
+ ActiveStorage::BlobKey.new(@attributes.as_json)
12
+ end
13
+ end
14
+ end
15
+
16
+ module CloudinaryHelper
17
+ alias cloudinary_url_internal_original cloudinary_url_internal
18
+
19
+ def cloudinary_url_internal(source, options = {})
20
+ source = ActiveStorage::Blob.service.public_id(source) if defined? ActiveStorage::Blob.service.public_id
21
+ cloudinary_url_internal_original(source, options)
22
+ end
23
+ end
24
+
25
+ module ActiveStorage
26
+ class Service::CloudinaryService < Service
27
+ module Headers
28
+ CONTENT_TYPE = "Content-Type".freeze
29
+ CONTENT_MD5 = "Content-MD5".freeze
30
+ end
31
+ attr_reader :upload_options
32
+
33
+ def initialize(**options)
34
+ @options = options
35
+ end
36
+
37
+ def upload(key, io, filename: nil, checksum: nil, **options)
38
+ instrument :upload, key: key, checksum: checksum do
39
+ begin
40
+ extra_headers = checksum.nil? ? {} : {Headers::CONTENT_MD5 => checksum}
41
+ options = @options.merge(options)
42
+ Cloudinary::Uploader.upload(
43
+ io,
44
+ public_id: public_id_internal(key),
45
+ resource_type: resource_type(io, key),
46
+ context: {active_storage_key: key, checksum: checksum},
47
+ extra_headers: extra_headers,
48
+ **options
49
+ )
50
+ rescue CloudinaryException => e
51
+ raise ActiveStorage::IntegrityError, e.message, e.backtrace
52
+ end
53
+ end
54
+ end
55
+
56
+ def url(key, filename: nil, content_type: '', **options)
57
+ instrument :url, key: key do |payload|
58
+ url = Cloudinary::Utils.cloudinary_url(
59
+ public_id(key),
60
+ resource_type: resource_type(nil, key),
61
+ format: ext_for_file(key, filename, content_type),
62
+ **@options.merge(options.symbolize_keys)
63
+ )
64
+
65
+ payload[:url] = url
66
+
67
+ url
68
+ end
69
+ end
70
+
71
+ def url_for_direct_upload(key, **options)
72
+ instrument :url, key: key do |payload|
73
+ options = {:resource_type => resource_type(nil, key)}.merge(@options.merge(options.symbolize_keys))
74
+ options[:public_id] = public_id_internal(key)
75
+ options[:context] = {active_storage_key: key}
76
+ options.delete(:file)
77
+ payload[:url] = api_uri("upload", options)
78
+ end
79
+ end
80
+
81
+ def headers_for_direct_upload(key, content_type:, checksum:, **)
82
+ {
83
+ Headers::CONTENT_TYPE => content_type,
84
+ Headers::CONTENT_MD5 => checksum,
85
+ }
86
+ end
87
+
88
+ def delete(key)
89
+ instrument :delete, key: key do
90
+ Cloudinary::Uploader.destroy public_id(key), resource_type: resource_type(nil, key)
91
+ end
92
+ end
93
+
94
+ def delete_prefixed(prefix)
95
+ # This method is used by ActiveStorage to delete derived resources after the main resource was deleted.
96
+ # In Cloudinary, the derived resources are deleted automatically when the main resource is deleted.
97
+ end
98
+
99
+ def exist?(key)
100
+ instrument :exist, key: key do |payload|
101
+ begin
102
+ Cloudinary::Api.resource public_id(key), resource_type: resource_type(nil, key)
103
+ true
104
+ rescue Cloudinary::Api::NotFound => e
105
+ false
106
+ end
107
+ end
108
+ end
109
+
110
+ def download(key, &block)
111
+ uri = URI(url(key))
112
+ if block_given?
113
+ instrument :streaming_download, key: key do
114
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
115
+ request = Net::HTTP::Get.new uri
116
+ http.request request do |response|
117
+ response.read_body &block
118
+ end
119
+ end
120
+ end
121
+ else
122
+ instrument :download, key: key do
123
+ res = Net::HTTP::get_response(uri)
124
+ res.body
125
+ end
126
+ end
127
+ end
128
+
129
+ # Return the partial content in the byte +range+ of the file at the +key+.
130
+ def download_chunk(key, range)
131
+ url = Cloudinary::Utils.unsigned_download_url(public_id(key), resource_type: resource_type(nil, key))
132
+ uri = URI(url)
133
+ instrument :download, key: key do
134
+ req = Net::HTTP::Get.new(uri)
135
+ range_end = case
136
+ when range.end.nil? then ''
137
+ when range.exclude_end? then range.end - 1
138
+ else range.end
139
+ end
140
+ req['range'] = "bytes=#{[range.begin, range_end].join('-')}"
141
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
142
+ http.request(req)
143
+ end
144
+ res.body.force_encoding(Encoding::BINARY)
145
+ end
146
+
147
+ end
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
+
155
+ private
156
+
157
+ def api_uri(action, options)
158
+ base_url = Cloudinary::Utils.cloudinary_api_url(action, options)
159
+ upload_params = Cloudinary::Uploader.build_upload_params(options)
160
+
161
+ upload_params.reject! { |k, v| Cloudinary::Utils.safe_blank?(v) }
162
+ unless options[:unsigned]
163
+ upload_params = Cloudinary::Utils.sign_request(upload_params, options)
164
+ end
165
+ "#{base_url}?#{upload_params.to_query}"
166
+ end
167
+
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::BlobKey] key The blob key with attributes.
173
+ # @param [ActiveStorage::Filename] filename The original filename.
174
+ # @param [string] content_type The content type of the file.
175
+ #
176
+ # @return [string] The extension of the filename.
177
+ def ext_for_file(key, filename, content_type)
178
+ if filename.blank?
179
+ options = key.respond_to?(:attributes) ? key.attributes : {}
180
+ filename = ActiveStorage::Filename.new(options[:filename]) if options.has_key?(:filename)
181
+ end
182
+ ext = filename.respond_to?(:extension_without_delimiter) ? filename.extension_without_delimiter : nil
183
+
184
+ return ext unless ext.blank?
185
+
186
+ # Raw files are not convertible, no extension guessing for them
187
+ return nil if content_type_to_resource_type(content_type).eql?('raw')
188
+
189
+ # Fallback when there is no extension.
190
+ @formats ||= Hash.new do |h, key|
191
+ ext = Rack::Mime::MIME_TYPES.invert[key]
192
+ h[key] = ext.slice(1..-1) unless ext.nil?
193
+ end
194
+ @formats[content_type]
195
+ end
196
+
197
+ def public_id_internal(key)
198
+ # TODO: Allow custom manipulation of key to obscure how we store in Cloudinary
199
+ key
200
+ end
201
+
202
+ def content_type_to_resource_type(content_type)
203
+ type, subtype = content_type.split('/')
204
+ case type
205
+ when 'video', 'audio'
206
+ 'video'
207
+ when 'text'
208
+ 'raw'
209
+ when 'application'
210
+ case subtype
211
+ when 'pdf', 'postscript'
212
+ 'image'
213
+ when 'vnd.apple.mpegurl', 'x-mpegurl', 'mpegurl' # m3u8
214
+ 'video'
215
+ else
216
+ 'raw'
217
+ end
218
+ else
219
+ 'image'
220
+ end
221
+ end
222
+
223
+ def resource_type(io, key = "")
224
+ options = key.respond_to?(:attributes) ? key.attributes : {}
225
+ content_type = options[:content_type] || (io.nil? ? '' : Marcel::MimeType.for(io))
226
+ content_type_to_resource_type(content_type)
227
+ end
228
+ end
229
+ end