cloudinary 1.9.1 → 1.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
- data/.github/pull_request_template.md +24 -0
- data/.gitignore +7 -1
- data/.travis.yml +15 -8
- data/CHANGELOG.md +261 -0
- data/README.md +3 -0
- data/Rakefile +3 -45
- data/cloudinary.gemspec +27 -20
- data/lib/active_storage/blob_key.rb +20 -0
- data/lib/active_storage/service/cloudinary_service.rb +249 -0
- data/lib/cloudinary.rb +53 -63
- data/lib/cloudinary/account_api.rb +231 -0
- data/lib/cloudinary/account_config.rb +30 -0
- data/lib/cloudinary/api.rb +228 -71
- data/lib/cloudinary/auth_token.rb +10 -4
- data/lib/cloudinary/base_api.rb +79 -0
- data/lib/cloudinary/base_config.rb +70 -0
- data/lib/cloudinary/cache.rb +38 -0
- data/lib/cloudinary/cache/breakpoints_cache.rb +31 -0
- data/lib/cloudinary/cache/key_value_cache_adapter.rb +25 -0
- data/lib/cloudinary/cache/rails_cache_adapter.rb +34 -0
- data/lib/cloudinary/cache/storage/rails_cache_storage.rb +5 -0
- data/lib/cloudinary/carrier_wave.rb +4 -2
- data/lib/cloudinary/carrier_wave/remote.rb +3 -2
- data/lib/cloudinary/carrier_wave/storage.rb +2 -1
- data/lib/cloudinary/{controller.rb → cloudinary_controller.rb} +3 -5
- data/lib/cloudinary/config.rb +43 -0
- data/lib/cloudinary/helper.rb +77 -7
- data/lib/cloudinary/migrator.rb +3 -1
- data/lib/cloudinary/railtie.rb +7 -3
- data/lib/cloudinary/responsive.rb +111 -0
- data/lib/cloudinary/uploader.rb +67 -15
- data/lib/cloudinary/utils.rb +324 -54
- data/lib/cloudinary/version.rb +1 -1
- data/lib/cloudinary/video_helper.rb +96 -22
- data/lib/tasks/cloudinary/fetch_assets.rake +48 -0
- data/lib/tasks/{cloudinary.rake → cloudinary/sync_static.rake} +0 -0
- data/tools/allocate_test_cloud.sh +9 -0
- data/tools/get_test_cloud.sh +9 -0
- data/tools/update_version +220 -0
- data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +51 -13
- data/vendor/assets/javascripts/cloudinary/jquery.fileupload.js +24 -4
- data/vendor/assets/javascripts/cloudinary/jquery.ui.widget.js +741 -561
- data/vendor/assets/javascripts/cloudinary/load-image.all.min.js +1 -1
- metadata +92 -67
- data/spec/access_control_spec.rb +0 -99
- data/spec/api_spec.rb +0 -545
- data/spec/archive_spec.rb +0 -129
- data/spec/auth_token_spec.rb +0 -79
- data/spec/cloudinary_helper_spec.rb +0 -190
- data/spec/cloudinary_spec.rb +0 -32
- data/spec/data/sync_static/app/assets/javascripts/1.coffee +0 -1
- data/spec/data/sync_static/app/assets/javascripts/1.js +0 -1
- data/spec/data/sync_static/app/assets/stylesheets/1.css +0 -3
- data/spec/docx.docx +0 -0
- data/spec/favicon.ico +0 -0
- data/spec/logo.png +0 -0
- data/spec/rake_spec.rb +0 -160
- data/spec/sample_asset_file.tsv +0 -4
- data/spec/search_spec.rb +0 -109
- data/spec/spec_helper.rb +0 -245
- data/spec/storage_spec.rb +0 -44
- data/spec/streaminig_profiles_api_spec.rb +0 -74
- data/spec/support/helpers/temp_file_helpers.rb +0 -22
- data/spec/support/shared_contexts/rake.rb +0 -19
- data/spec/uploader_spec.rb +0 -363
- data/spec/utils_methods_spec.rb +0 -54
- data/spec/utils_spec.rb +0 -906
- data/spec/video_tag_spec.rb +0 -251
- data/spec/video_url_spec.rb +0 -164
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
if RUBY_VERSION > "2"
|
3
5
|
require "ostruct"
|
@@ -10,6 +12,7 @@ module Cloudinary
|
|
10
12
|
module AuthToken
|
11
13
|
SEPARATOR = '~'
|
12
14
|
UNSAFE = /[ "#%&\'\/:;<=>?@\[\\\]^`{\|}~]/
|
15
|
+
EMPTY_TOKEN = {}.freeze
|
13
16
|
|
14
17
|
def self.generate(options = {})
|
15
18
|
key = options[:key]
|
@@ -30,6 +33,10 @@ module Cloudinary
|
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
36
|
+
if url.blank? && acl.blank?
|
37
|
+
raise 'AuthToken must contain either an acl or a url property'
|
38
|
+
end
|
39
|
+
|
33
40
|
token = []
|
34
41
|
token << "ip=#{ip}" if ip
|
35
42
|
token << "st=#{start}" if start
|
@@ -42,12 +49,11 @@ module Cloudinary
|
|
42
49
|
"#{name}=#{token.join(SEPARATOR)}"
|
43
50
|
end
|
44
51
|
|
45
|
-
|
46
52
|
# Merge token2 to token1 returning a new
|
47
53
|
# Requires to support Ruby 1.9
|
48
54
|
def self.merge_auth_token(token1, token2)
|
49
|
-
token1 = token1 ||
|
50
|
-
token2 = token2 ||
|
55
|
+
token1 = token1 || EMPTY_TOKEN
|
56
|
+
token2 = token2 || EMPTY_TOKEN
|
51
57
|
token1 = token1.respond_to?( :to_h) ? token1.to_h : token1
|
52
58
|
token2 = token2.respond_to?( :to_h) ? token2.to_h : token2
|
53
59
|
token1.merge(token2)
|
@@ -69,4 +75,4 @@ module Cloudinary
|
|
69
75
|
OpenSSL::HMAC.hexdigest(digest, bin_key, message)
|
70
76
|
end
|
71
77
|
end
|
72
|
-
end
|
78
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "rest_client"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Cloudinary::BaseApi
|
5
|
+
class Error < CloudinaryException; end
|
6
|
+
class NotFound < Error; end
|
7
|
+
class NotAllowed < Error; end
|
8
|
+
class AlreadyExists < Error; end
|
9
|
+
class RateLimited < Error; end
|
10
|
+
class BadRequest < Error; end
|
11
|
+
class GeneralError < Error; end
|
12
|
+
class AuthorizationRequired < Error; end
|
13
|
+
|
14
|
+
class Response < Hash
|
15
|
+
attr_reader :rate_limit_reset_at, :rate_limit_remaining, :rate_limit_allowed
|
16
|
+
|
17
|
+
def initialize(response=nil)
|
18
|
+
if response
|
19
|
+
# This sets the instantiated self as the response Hash
|
20
|
+
update Cloudinary::Api.parse_json_response response
|
21
|
+
|
22
|
+
@rate_limit_allowed = response.headers[:x_featureratelimit_limit].to_i if response.headers[:x_featureratelimit_limit]
|
23
|
+
@rate_limit_reset_at = Time.parse(response.headers[:x_featureratelimit_reset]) if response.headers[:x_featureratelimit_reset]
|
24
|
+
@rate_limit_remaining = response.headers[:x_featureratelimit_remaining].to_i if response.headers[:x_featureratelimit_remaining]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.extended(base)
|
30
|
+
[Error, NotFound, NotAllowed, AlreadyExists, RateLimited, BadRequest, GeneralError, AuthorizationRequired, Response].each do |constant|
|
31
|
+
base.const_set(constant.name.split("::").last, constant)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_json_api(method, api_url, payload, timeout, headers, proxy = nil, user = nil, password = nil)
|
36
|
+
RestClient::Request.execute(method: method,
|
37
|
+
url: api_url,
|
38
|
+
payload: payload,
|
39
|
+
timeout: timeout,
|
40
|
+
headers: headers,
|
41
|
+
proxy: proxy,
|
42
|
+
user: user,
|
43
|
+
password: password) do |response|
|
44
|
+
return Response.new(response) if response.code == 200
|
45
|
+
exception_class = case response.code
|
46
|
+
when 400 then BadRequest
|
47
|
+
when 401 then AuthorizationRequired
|
48
|
+
when 403 then NotAllowed
|
49
|
+
when 404 then NotFound
|
50
|
+
when 409 then AlreadyExists
|
51
|
+
when 420 then RateLimited
|
52
|
+
when 500 then GeneralError
|
53
|
+
else raise GeneralError.new("Server returned unexpected status code - #{response.code} - #{response.body}")
|
54
|
+
end
|
55
|
+
json = Cloudinary::Api.parse_json_response(response)
|
56
|
+
raise exception_class.new(json["error"]["message"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def call_cloudinary_api(method, uri, user, password, params, options, &api_url_builder)
|
63
|
+
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || 'https://api.cloudinary.com'
|
64
|
+
api_url = Cloudinary::Utils.smart_escape(api_url_builder.call(cloudinary, uri).flatten.join('/'))
|
65
|
+
timeout = options[:timeout] || Cloudinary.config.timeout || 60
|
66
|
+
proxy = options[:api_proxy] || Cloudinary.config.api_proxy
|
67
|
+
|
68
|
+
headers = { "User-Agent" => Cloudinary::USER_AGENT }
|
69
|
+
|
70
|
+
if options[:content_type] == :json
|
71
|
+
payload = params.to_json
|
72
|
+
headers.merge!("Content-Type" => "application/json", "Accept" => "application/json")
|
73
|
+
else
|
74
|
+
payload = params.reject { |_, v| v.nil? || v == "" }
|
75
|
+
end
|
76
|
+
|
77
|
+
call_json_api(method, api_url, payload, timeout, headers, proxy, user, password)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Cloudinary
|
2
|
+
module BaseConfig
|
3
|
+
def load_from_url(url)
|
4
|
+
return unless url && !url.empty?
|
5
|
+
|
6
|
+
parsed_url = URI.parse(url)
|
7
|
+
scheme = parsed_url.scheme.to_s.downcase
|
8
|
+
|
9
|
+
if expected_scheme != scheme
|
10
|
+
raise(CloudinaryException,
|
11
|
+
"Invalid #{env_url} scheme. Expecting to start with '#{expected_scheme}://'")
|
12
|
+
end
|
13
|
+
|
14
|
+
update(config_from_parsed_url(parsed_url))
|
15
|
+
setup_from_parsed_url(parsed_url)
|
16
|
+
end
|
17
|
+
|
18
|
+
def update(new_config = {})
|
19
|
+
new_config.each{ |k,v| public_send(:"#{k}=", v) unless v.nil?}
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_config_from_env
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def config_from_parsed_url(parsed_url)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def env_url
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def expected_scheme
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def put_nested_key(key, value)
|
41
|
+
chain = key.split(/[\[\]]+/).reject(&:empty?)
|
42
|
+
outer = self
|
43
|
+
lastKey = chain.pop
|
44
|
+
chain.each do |innerKey|
|
45
|
+
inner = outer[innerKey]
|
46
|
+
if inner.nil?
|
47
|
+
inner = OpenStruct.new
|
48
|
+
outer[innerKey] = inner
|
49
|
+
end
|
50
|
+
outer = inner
|
51
|
+
end
|
52
|
+
outer[lastKey] = value
|
53
|
+
end
|
54
|
+
|
55
|
+
def is_nested_key?(key)
|
56
|
+
/\w+\[\w+\]/ =~ key
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_from_parsed_url(parsed_url)
|
60
|
+
parsed_url.query.to_s.split("&").each do |param|
|
61
|
+
key, value = param.split("=")
|
62
|
+
if is_nested_key? key
|
63
|
+
put_nested_key key, value
|
64
|
+
else
|
65
|
+
update(key => Utils.smart_unescape(value))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Cloudinary
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :storage
|
8
|
+
|
9
|
+
def get(public_id, options)
|
10
|
+
if block_given?
|
11
|
+
storage.read(generate_cache_key(public_id, options)) {yield}
|
12
|
+
else
|
13
|
+
storage.read(generate_cache_key(public_id, options))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def set(public_id, options, value)
|
18
|
+
storage.write(generate_cache_key(public_id, options), value)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :fetch, :get
|
22
|
+
|
23
|
+
def flush_all
|
24
|
+
storage.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate_cache_key(public_id, options)
|
30
|
+
type = options[:type] || "upload"
|
31
|
+
resource_type = options[:resource_type] || "image"
|
32
|
+
transformation = Cloudinary::Utils.generate_transformation_string options.clone
|
33
|
+
format = options[:format]
|
34
|
+
Digest::SHA1.hexdigest [public_id, type, resource_type, transformation, format].reject(&:blank?).join('/')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Cloudinary::Cache
|
2
|
+
class BreakpointsCache
|
3
|
+
attr_accessor :adapter
|
4
|
+
|
5
|
+
def set(public_id, options, value)
|
6
|
+
upload_type, resource_type, transformation, format = options_to_parameters(options)
|
7
|
+
@adapter.set(public_id, upload_type, resource_type, transformation, format, value)
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(public_id, options)
|
12
|
+
upload_type, resource_type, transformation, format = options_to_parameters(options)
|
13
|
+
@adapter.set(public_id, upload_type, resource_type, transformation, format, &Proc.new)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(public_id, options)
|
18
|
+
upload_type, resource_type, transformation, format = options_to_parameters(options)
|
19
|
+
@adapter.get(public_id, upload_type, resource_type, transformation, format)
|
20
|
+
end
|
21
|
+
|
22
|
+
def options_to_parameters(options)
|
23
|
+
options = Cloudinary::Utils.symbolize_keys options
|
24
|
+
transformation = Cloudinary::Utils.generate_transformation_string(options)
|
25
|
+
upload_type = options[:type] || 'upload'
|
26
|
+
resource_type = options[:resource_type] || 'image'
|
27
|
+
format = options[:format] || ""
|
28
|
+
[upload_type, resource_type, transformation, format]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'digest'
|
2
|
+
module Cloudinary::Cache
|
3
|
+
class KeyValueCacheAdapter < CacheAdapter
|
4
|
+
def get(public_id, type, resource_type, transformation, format)
|
5
|
+
key = generate_cache_key(public_id, type, resource_type, transformation, format)
|
6
|
+
@storage.get(key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def set(public_id, type, resource_type, transformation, format, value)
|
10
|
+
key = generate_cache_key(public_id, type, resource_type, transformation, format)
|
11
|
+
@storage.set(key, value)
|
12
|
+
end
|
13
|
+
|
14
|
+
def flush_all()
|
15
|
+
@storage.flush_all()
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_cache_key(public_id, type, resource_type, transformation, format)
|
21
|
+
Digest::SHA1.hexdigest [public_id, type, resource_type, transformation, format].reject(&:blank?)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Cloudinary::Cache
|
2
|
+
class RailsCacheAdapter < CacheAdapter
|
3
|
+
|
4
|
+
def flush_all
|
5
|
+
end
|
6
|
+
|
7
|
+
def get(public_id, type, resource_type, transformation, format)
|
8
|
+
key = generate_cache_key(public_id, type, resource_type, transformation, format)
|
9
|
+
Rails.cache.read(key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def init
|
13
|
+
unless defined? Rails
|
14
|
+
raise CloudinaryException.new "Rails is required in order to use RailsCacheAdapter"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set(public_id, type, resource_type, transformation, format, value)
|
19
|
+
key = generate_cache_key(public_id, type, resource_type, transformation, format)
|
20
|
+
Rails.cache.write(key, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(public_id, type, resource_type, transformation, format)
|
24
|
+
key = generate_cache_key(public_id, type, resource_type, transformation, format)
|
25
|
+
Rails.cache.fetch(key, &Proc.new)
|
26
|
+
end
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate_cache_key(public_id, type, resource_type, transformation, format)
|
30
|
+
Digest::SHA1.hexdigest [public_id, type, resource_type, transformation, format].reject(&:blank?)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -3,14 +3,16 @@ require 'cloudinary/carrier_wave/process'
|
|
3
3
|
require 'cloudinary/carrier_wave/error'
|
4
4
|
require 'cloudinary/carrier_wave/remote'
|
5
5
|
require 'cloudinary/carrier_wave/preloaded'
|
6
|
-
require 'cloudinary/carrier_wave/storage'
|
6
|
+
require 'cloudinary/carrier_wave/storage' if defined?(::CarrierWave) # HACK
|
7
7
|
|
8
8
|
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
|
-
base.class_attribute :
|
14
|
+
base.class_attribute :metadata
|
15
|
+
base.class_attribute :storage_type, instance_reader: false
|
14
16
|
override_in_versions(base, :blank?, :full_public_id, :my_public_id, :all_versions_processors, :stored_version)
|
15
17
|
end
|
16
18
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Cloudinary::CarrierWave
|
2
2
|
def download!(uri, *args)
|
3
|
-
return super
|
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
|
-
|
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
|
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
|
-
module CloudinaryController
|
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, CloudinaryController
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Cloudinary
|
2
|
+
module Config
|
3
|
+
include BaseConfig
|
4
|
+
|
5
|
+
ENV_URL = "CLOUDINARY_URL"
|
6
|
+
SCHEME = "cloudinary"
|
7
|
+
|
8
|
+
def load_config_from_env
|
9
|
+
if ENV["CLOUDINARY_CLOUD_NAME"]
|
10
|
+
config_keys = ENV.keys.select! { |key| key.start_with? "CLOUDINARY_" }
|
11
|
+
config_keys -= ["CLOUDINARY_URL"] # ignore it when explicit options are passed
|
12
|
+
config_keys.each do |full_key|
|
13
|
+
conf_key = full_key["CLOUDINARY_".length..-1].downcase # convert "CLOUDINARY_CONFIG_NAME" to "config_name"
|
14
|
+
conf_val = ENV[full_key]
|
15
|
+
conf_val = conf_val == 'true' if %w[true false].include?(conf_val) # cast relevant boolean values
|
16
|
+
update(conf_key => conf_val)
|
17
|
+
end
|
18
|
+
elsif ENV[ENV_URL]
|
19
|
+
load_from_url(ENV[ENV_URL])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def env_url
|
26
|
+
ENV_URL
|
27
|
+
end
|
28
|
+
|
29
|
+
def expected_scheme
|
30
|
+
SCHEME
|
31
|
+
end
|
32
|
+
|
33
|
+
def config_from_parsed_url(parsed_url)
|
34
|
+
{
|
35
|
+
"cloud_name" => parsed_url.host,
|
36
|
+
"api_key" => parsed_url.user,
|
37
|
+
"api_secret" => parsed_url.password,
|
38
|
+
"private_cdn" => !parsed_url.path.blank?,
|
39
|
+
"secure_distribution" => parsed_url.path[1..-1]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|