cloudinary 1.17.0 → 1.20.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|