cloudinary 1.17.0 → 1.20.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 +4 -4
- data/CHANGELOG.md +61 -0
- data/cloudinary.gemspec +8 -3
- data/lib/active_storage/service/cloudinary_service.rb +32 -6
- data/lib/cloudinary.rb +45 -64
- data/lib/cloudinary/account_api.rb +231 -0
- data/lib/cloudinary/account_config.rb +30 -0
- data/lib/cloudinary/api.rb +182 -66
- data/lib/cloudinary/auth_token.rb +4 -0
- data/lib/cloudinary/base_api.rb +79 -0
- data/lib/cloudinary/base_config.rb +70 -0
- data/lib/cloudinary/config.rb +43 -0
- data/lib/cloudinary/helper.rb +1 -1
- data/lib/cloudinary/uploader.rb +33 -6
- data/lib/cloudinary/utils.rb +109 -27
- data/lib/cloudinary/version.rb +1 -1
- data/lib/cloudinary/video_helper.rb +96 -22
- data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +6 -2
- metadata +17 -12
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cloudinary
|
2
|
+
module AccountConfig
|
3
|
+
include BaseConfig
|
4
|
+
|
5
|
+
ENV_URL = "CLOUDINARY_ACCOUNT_URL"
|
6
|
+
SCHEME = "account"
|
7
|
+
|
8
|
+
def load_config_from_env
|
9
|
+
load_from_url(ENV[ENV_URL]) if ENV[ENV_URL]
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def env_url
|
15
|
+
ENV_URL
|
16
|
+
end
|
17
|
+
|
18
|
+
def expected_scheme
|
19
|
+
SCHEME
|
20
|
+
end
|
21
|
+
|
22
|
+
def config_from_parsed_url(parsed_url)
|
23
|
+
{
|
24
|
+
"account_id" => parsed_url.host,
|
25
|
+
"provisioning_api_key" => parsed_url.user,
|
26
|
+
"provisioning_api_secret" => parsed_url.password
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/cloudinary/api.rb
CHANGED
@@ -1,37 +1,28 @@
|
|
1
|
-
require 'rest_client'
|
2
|
-
require 'json'
|
3
|
-
|
4
1
|
class Cloudinary::Api
|
5
|
-
|
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
|
2
|
+
extend Cloudinary::BaseApi
|
28
3
|
|
29
4
|
def self.ping(options={})
|
30
5
|
call_api(:get, "ping", {}, options)
|
31
6
|
end
|
32
7
|
|
8
|
+
# Gets account usage details
|
9
|
+
#
|
10
|
+
# Get a report on the status of your Cloudinary account usage details, including
|
11
|
+
# storage, bandwidth, requests, number of resources, and add-on usage.
|
12
|
+
# Note that numbers are updated periodically.
|
13
|
+
#
|
14
|
+
# @see https://cloudinary.com/documentation/admin_api#get_account_usage_details Get account usage details
|
15
|
+
#
|
16
|
+
# @param [Hash] options Additional options
|
17
|
+
# @return [Cloudinary::Api::Response]
|
18
|
+
# @raise [Cloudinary::Api:Error]
|
33
19
|
def self.usage(options={})
|
34
|
-
|
20
|
+
uri = 'usage'
|
21
|
+
date = options[:date]
|
22
|
+
|
23
|
+
uri += "/#{Cloudinary::Utils.to_usage_api_date_format(date)}" unless date.nil?
|
24
|
+
|
25
|
+
call_api(:get, uri, {}, options)
|
35
26
|
end
|
36
27
|
|
37
28
|
def self.resource_types(options={})
|
@@ -91,7 +82,8 @@ class Cloudinary::Api
|
|
91
82
|
:phash,
|
92
83
|
:quality_analysis,
|
93
84
|
:derived_next_cursor,
|
94
|
-
:accessibility_analysis
|
85
|
+
:accessibility_analysis,
|
86
|
+
:versions
|
95
87
|
), options)
|
96
88
|
end
|
97
89
|
|
@@ -99,7 +91,7 @@ class Cloudinary::Api
|
|
99
91
|
resource_type = options[:resource_type] || "image"
|
100
92
|
type = options[:type] || "upload"
|
101
93
|
uri = "resources/#{resource_type}/#{type}/restore"
|
102
|
-
call_api(:post, uri, { :public_ids => public_ids }, options)
|
94
|
+
call_api(:post, uri, { :public_ids => public_ids, :versions => options[:versions] }, options)
|
103
95
|
end
|
104
96
|
|
105
97
|
def self.update(public_id, options={})
|
@@ -348,47 +340,156 @@ class Cloudinary::Api
|
|
348
340
|
call_json_api('GET', json_url, {}, 60, {})
|
349
341
|
end
|
350
342
|
|
343
|
+
# Returns a list of all metadata field definitions.
|
344
|
+
#
|
345
|
+
# @see https://cloudinary.com/documentation/admin_api#get_metadata_fields Get metadata fields API reference
|
346
|
+
#
|
347
|
+
# @param [Hash] options Additional options
|
348
|
+
# @return [Cloudinary::Api::Response]
|
349
|
+
# @raise [Cloudinary::Api::Error]
|
350
|
+
def self.list_metadata_fields(options = {})
|
351
|
+
call_metadata_api(:get, [], {}, options)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Gets a metadata field by external id.
|
355
|
+
#
|
356
|
+
# @see https://cloudinary.com/documentation/admin_api#get_a_metadata_field_by_external_id Get metadata field by external ID API reference
|
357
|
+
#
|
358
|
+
# @param [String] field_external_id The ID of the metadata field to retrieve
|
359
|
+
# @param [Hash] options Additional options
|
360
|
+
# @return [Cloudinary::Api::Response]
|
361
|
+
# @raise [Cloudinary::Api::Error]
|
362
|
+
def self.metadata_field_by_field_id(field_external_id, options = {})
|
363
|
+
uri = [field_external_id]
|
364
|
+
|
365
|
+
call_metadata_api(:get, uri, {}, options)
|
366
|
+
end
|
367
|
+
|
368
|
+
# Creates a new metadata field definition.
|
369
|
+
#
|
370
|
+
# @see https://cloudinary.com/documentation/admin_api#create_a_metadata_field Create metadata field API reference
|
371
|
+
#
|
372
|
+
# @param [Hash] field The field to add
|
373
|
+
# @param [Hash] options Additional options
|
374
|
+
# @return [Cloudinary::Api::Response]
|
375
|
+
# @raise [Cloudinary::Api::Error]
|
376
|
+
def self.add_metadata_field(field, options = {})
|
377
|
+
params = only(field, :type, :external_id, :label, :mandatory, :default_value, :validation, :datasource)
|
378
|
+
|
379
|
+
call_metadata_api(:post, [], params, options)
|
380
|
+
end
|
381
|
+
|
382
|
+
# Updates a metadata field by external id.
|
383
|
+
#
|
384
|
+
# Updates a metadata field definition (partially, no need to pass the entire object) passed as JSON data.
|
385
|
+
# See https://cloudinary.com/documentation/admin_api#generic_structure_of_a_metadata_field for the generic structure
|
386
|
+
# of a metadata field.
|
387
|
+
#
|
388
|
+
# @see https://cloudinary.com/documentation/admin_api#update_a_metadata_field_by_external_id Update metadata field API reference
|
389
|
+
#
|
390
|
+
# @param [String] field_external_id The id of the metadata field to update
|
391
|
+
# @param [Hash] field The field definition
|
392
|
+
# @param [Hash] options Additional options
|
393
|
+
# @return [Cloudinary::Api::Response]
|
394
|
+
# @raise [Cloudinary::Api::Error]
|
395
|
+
def self.update_metadata_field(field_external_id, field, options = {})
|
396
|
+
uri = [field_external_id]
|
397
|
+
params = only(field, :label, :mandatory, :default_value, :validation)
|
398
|
+
|
399
|
+
call_metadata_api(:put, uri, params, options)
|
400
|
+
end
|
401
|
+
|
402
|
+
# Deletes a metadata field definition.
|
403
|
+
#
|
404
|
+
# The field should no longer be considered a valid candidate for all other endpoints.
|
405
|
+
#
|
406
|
+
# @see https://cloudinary.com/documentation/admin_api#delete_a_metadata_field_by_external_id Delete metadata field API reference
|
407
|
+
#
|
408
|
+
# @param [String] field_external_id The external id of the field to delete
|
409
|
+
# @param [Hash] options Additional options
|
410
|
+
# @return [Cloudinary::Api::Response] A hash with a "message" key. "ok" value indicates a successful deletion
|
411
|
+
# @raise [Cloudinary::Api::Error]
|
412
|
+
def self.delete_metadata_field(field_external_id, options = {})
|
413
|
+
uri = [field_external_id]
|
414
|
+
|
415
|
+
call_metadata_api(:delete, uri, {}, options)
|
416
|
+
end
|
417
|
+
|
418
|
+
# Deletes entries in a metadata field datasource.
|
419
|
+
#
|
420
|
+
# Deletes (blocks) the datasource entries for a specified metadata field definition. Sets the state of the
|
421
|
+
# entries to inactive. This is a soft delete, the entries still exist under the hood and can be activated
|
422
|
+
# again with the restore datasource entries method.
|
423
|
+
#
|
424
|
+
# @see https://cloudinary.com/documentation/admin_api#delete_entries_in_a_metadata_field_datasource Delete entries in a metadata field datasource API reference
|
425
|
+
#
|
426
|
+
# @param [String] field_external_id The id of the field to update
|
427
|
+
# @param [Array] entries_external_id The ids of all the entries to delete from the datasource
|
428
|
+
# @param [Hash] options Additional options
|
429
|
+
# @return [Cloudinary::Api::Response] The remaining datasource entries
|
430
|
+
# @raise [Cloudinary::Api::Error]
|
431
|
+
def self.delete_datasource_entries(field_external_id, entries_external_id, options = {})
|
432
|
+
uri = [field_external_id, "datasource"]
|
433
|
+
params = {:external_ids => entries_external_id }
|
434
|
+
|
435
|
+
call_metadata_api(:delete, uri, params, options)
|
436
|
+
end
|
437
|
+
|
438
|
+
# Updates a metadata field datasource.
|
439
|
+
#
|
440
|
+
# Updates the datasource of a supported field type (currently only enum and set), passed as JSON data. The
|
441
|
+
# update is partial: datasource entries with an existing external_id will be updated and entries with new
|
442
|
+
# external_id’s (or without external_id’s) will be appended.
|
443
|
+
#
|
444
|
+
# @see https://cloudinary.com/documentation/admin_api#update_a_metadata_field_datasource Update a metadata field datasource API reference
|
445
|
+
#
|
446
|
+
# @param [String] field_external_id The external id of the field to update
|
447
|
+
# @param [Array] entries_external_id
|
448
|
+
# @param [Hash] options Additional options
|
449
|
+
# @return [Cloudinary::Api::Response]
|
450
|
+
# @raise [Cloudinary::Api::Error]
|
451
|
+
def self.update_metadata_field_datasource(field_external_id, entries_external_id, options = {})
|
452
|
+
uri = [field_external_id, "datasource"]
|
453
|
+
|
454
|
+
params = entries_external_id.each_with_object({:values => [] }) do |item, hash|
|
455
|
+
item = only(item, :external_id, :value)
|
456
|
+
hash[:values ] << item if item.present?
|
457
|
+
end
|
458
|
+
|
459
|
+
call_metadata_api(:put, uri, params, options)
|
460
|
+
end
|
461
|
+
|
462
|
+
# Restores entries in a metadata field datasource.
|
463
|
+
#
|
464
|
+
# Restores (unblocks) any previously deleted datasource entries for a specified metadata field definition.
|
465
|
+
# Sets the state of the entries to active.
|
466
|
+
#
|
467
|
+
# @see https://cloudinary.com/documentation/admin_api#restore_entries_in_a_metadata_field_datasource Restore entries in a metadata field datasource API reference
|
468
|
+
#
|
469
|
+
# @param [String] field_external_id The ID of the metadata field
|
470
|
+
# @param [Array] entries_external_ids An array of IDs of datasource entries to restore (unblock)
|
471
|
+
# @param [Hash] options Additional options
|
472
|
+
# @return [Cloudinary::Api::Response]
|
473
|
+
# @raise [Cloudinary::Api::Error]
|
474
|
+
def self.restore_metadata_field_datasource(field_external_id, entries_external_ids, options = {})
|
475
|
+
uri = [field_external_id, "datasource_restore"]
|
476
|
+
params = {:external_ids => entries_external_ids }
|
477
|
+
|
478
|
+
call_metadata_api(:post, uri, params, options)
|
479
|
+
end
|
480
|
+
|
351
481
|
protected
|
352
482
|
|
353
483
|
def self.call_api(method, uri, params, options)
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
api_url = [cloudinary, "v1_1", cloud_name, uri].join("/")
|
361
|
-
# Add authentication
|
362
|
-
api_url.sub!(%r(^(https?://)), "\\1#{api_key}:#{api_secret}@")
|
363
|
-
|
364
|
-
headers = { "User-Agent" => Cloudinary::USER_AGENT }
|
365
|
-
if options[:content_type]== :json
|
366
|
-
payload = params.to_json
|
367
|
-
headers.merge!("Content-Type"=> 'application/json', "Accept"=> 'application/json')
|
368
|
-
else
|
369
|
-
payload = params.reject { |k, v| v.nil? || v=="" }
|
370
|
-
end
|
371
|
-
call_json_api(method, api_url, payload, timeout, headers)
|
372
|
-
end
|
373
|
-
|
374
|
-
def self.call_json_api(method, api_url, payload, timeout, headers)
|
375
|
-
RestClient::Request.execute(:method => method, :url => api_url, :payload => payload, :timeout => timeout, :headers => headers) do
|
376
|
-
|response, request, tmpresult|
|
377
|
-
return Response.new(response) if response.code == 200
|
378
|
-
exception_class = case response.code
|
379
|
-
when 400 then BadRequest
|
380
|
-
when 401 then AuthorizationRequired
|
381
|
-
when 403 then NotAllowed
|
382
|
-
when 404 then NotFound
|
383
|
-
when 409 then AlreadyExists
|
384
|
-
when 420 then RateLimited
|
385
|
-
when 500 then GeneralError
|
386
|
-
else raise GeneralError.new("Server returned unexpected status code - #{response.code} - #{response.body}")
|
387
|
-
end
|
388
|
-
json = parse_json_response(response)
|
389
|
-
raise exception_class.new(json["error"]["message"])
|
484
|
+
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise('Must supply cloud_name')
|
485
|
+
api_key = options[:api_key] || Cloudinary.config.api_key || raise('Must supply api_key')
|
486
|
+
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise('Must supply api_secret')
|
487
|
+
|
488
|
+
call_cloudinary_api(method, uri, api_key, api_secret, params, options) do |cloudinary, inner_uri|
|
489
|
+
[cloudinary, 'v1_1', cloud_name, inner_uri]
|
390
490
|
end
|
391
491
|
end
|
492
|
+
|
392
493
|
def self.parse_json_response(response)
|
393
494
|
return Cloudinary::Utils.json_decode(response.body)
|
394
495
|
rescue => e
|
@@ -396,6 +497,22 @@ class Cloudinary::Api
|
|
396
497
|
raise GeneralError.new("Error parsing server response (#{response.code}) - #{response.body}. Got - #{e}")
|
397
498
|
end
|
398
499
|
|
500
|
+
# Protected function that assists with performing an API call to the metadata_fields part of the Admin API.
|
501
|
+
#
|
502
|
+
# @protected
|
503
|
+
# @param [Symbol] method The HTTP method. Valid methods: get, post, put, delete
|
504
|
+
# @param [Array] uri REST endpoint of the API (without 'metadata_fields')
|
505
|
+
# @param [Hash] params Query/body parameters passed to the method
|
506
|
+
# @param [Hash] options Additional options. Can be an override of the configuration, headers, etc.
|
507
|
+
# @return [Cloudinary::Api::Response]
|
508
|
+
# @raise [Cloudinary::Api::Error]
|
509
|
+
def self.call_metadata_api(method, uri, params, options)
|
510
|
+
options[:content_type] = :json
|
511
|
+
uri = ["metadata_fields", uri].reject(&:empty?).join("/")
|
512
|
+
|
513
|
+
call_api(method, uri, params, options)
|
514
|
+
end
|
515
|
+
|
399
516
|
def self.only(hash, *keys)
|
400
517
|
result = {}
|
401
518
|
keys.each do |key|
|
@@ -439,5 +556,4 @@ class Cloudinary::Api
|
|
439
556
|
params[by_key] = value
|
440
557
|
call_api("post", "resources/#{resource_type}/#{type}/update_access_mode", params, options)
|
441
558
|
end
|
442
|
-
|
443
559
|
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
|