cloudinary 1.11.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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