supportify_client 1.0.2 → 3.0.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/README.md +1 -1
- data/VERSION +1 -1
- data/lib/supportify_client.rb +11 -9
- data/lib/supportify_client/api/supportify_api.rb +222 -84
- data/lib/supportify_client/api_client.rb +100 -53
- data/lib/supportify_client/configuration.rb +35 -29
- data/lib/supportify_client/models/category.rb +125 -9
- data/lib/supportify_client/models/error.rb +125 -9
- data/lib/supportify_client/models/faq.rb +173 -22
- data/lib/supportify_client/models/info.rb +121 -10
- data/lib/supportify_client/models/info_application.rb +158 -0
- data/lib/supportify_client/models/info_supportify.rb +147 -0
- data/lib/supportify_client/models/tag.rb +125 -9
- data/lib/supportify_client/models/user.rb +121 -8
- data/supportify_client.gemspec +5 -4
- metadata +4 -3
- data/lib/supportify_client/models/base_object.rb +0 -87
@@ -7,36 +7,37 @@ require 'uri'
|
|
7
7
|
|
8
8
|
module Supportify
|
9
9
|
class ApiClient
|
10
|
-
|
11
|
-
attr_accessor :
|
10
|
+
# The Configuration object holding settings to be used in the API client.
|
11
|
+
attr_accessor :config
|
12
12
|
|
13
13
|
# Defines the headers to be used in HTTP requests of all API calls by default.
|
14
14
|
#
|
15
15
|
# @return [Hash]
|
16
16
|
attr_accessor :default_headers
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(host = nil)
|
22
|
-
@host = host || Configuration.base_url
|
23
|
-
@format = 'json'
|
18
|
+
def initialize(config = Configuration.default)
|
19
|
+
@config = config
|
24
20
|
@user_agent = "ruby-swagger-#{VERSION}"
|
25
21
|
@default_headers = {
|
26
|
-
'Content-Type' => "application
|
22
|
+
'Content-Type' => "application/json",
|
27
23
|
'User-Agent' => @user_agent
|
28
24
|
}
|
29
25
|
end
|
30
26
|
|
27
|
+
def self.default
|
28
|
+
@@default ||= ApiClient.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Call an API with given options.
|
32
|
+
#
|
33
|
+
# @return [Array<(Object, Fixnum, Hash)>] an array of 3 elements:
|
34
|
+
# the data deserialized from response body (could be nil), response status code and response headers.
|
31
35
|
def call_api(http_method, path, opts = {})
|
32
36
|
request = build_request(http_method, path, opts)
|
33
37
|
response = request.run
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
if Configuration.debugging
|
39
|
-
Configuration.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
|
39
|
+
if @config.debugging
|
40
|
+
@config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
|
40
41
|
end
|
41
42
|
|
42
43
|
unless response.success?
|
@@ -47,10 +48,11 @@ module Supportify
|
|
47
48
|
end
|
48
49
|
|
49
50
|
if opts[:return_type]
|
50
|
-
deserialize(response, opts[:return_type])
|
51
|
+
data = deserialize(response, opts[:return_type])
|
51
52
|
else
|
52
|
-
nil
|
53
|
+
data = nil
|
53
54
|
end
|
55
|
+
return data, response.code, response.headers
|
54
56
|
end
|
55
57
|
|
56
58
|
def build_request(http_method, path, opts = {})
|
@@ -69,22 +71,34 @@ module Supportify
|
|
69
71
|
:method => http_method,
|
70
72
|
:headers => header_params,
|
71
73
|
:params => query_params,
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
74
|
+
:timeout => @config.timeout,
|
75
|
+
:ssl_verifypeer => @config.verify_ssl,
|
76
|
+
:sslcert => @config.cert_file,
|
77
|
+
:sslkey => @config.key_file,
|
78
|
+
:cainfo => @config.ssl_ca_cert,
|
79
|
+
:verbose => @config.debugging
|
75
80
|
}
|
76
81
|
|
77
82
|
if [:post, :patch, :put, :delete].include?(http_method)
|
78
83
|
req_body = build_request_body(header_params, form_params, opts[:body])
|
79
84
|
req_opts.update :body => req_body
|
80
|
-
if
|
81
|
-
|
85
|
+
if @config.debugging
|
86
|
+
@config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
85
90
|
Typhoeus::Request.new(url, req_opts)
|
86
91
|
end
|
87
92
|
|
93
|
+
# Check if the given MIME is a JSON MIME.
|
94
|
+
# JSON MIME examples:
|
95
|
+
# application/json
|
96
|
+
# application/json; charset=UTF8
|
97
|
+
# APPLICATION/JSON
|
98
|
+
def json_mime?(mime)
|
99
|
+
!!(mime =~ /\Aapplication\/json(;.*)?\z/i)
|
100
|
+
end
|
101
|
+
|
88
102
|
# Deserialize the response to the given return type.
|
89
103
|
#
|
90
104
|
# @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]"
|
@@ -98,9 +112,7 @@ module Supportify
|
|
98
112
|
# ensuring a default content type
|
99
113
|
content_type = response.headers['Content-Type'] || 'application/json'
|
100
114
|
|
101
|
-
unless
|
102
|
-
fail "Content-Type is not supported: #{content_type}"
|
103
|
-
end
|
115
|
+
fail "Content-Type is not supported: #{content_type}" unless json_mime?(content_type)
|
104
116
|
|
105
117
|
begin
|
106
118
|
data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
|
@@ -158,38 +170,58 @@ module Supportify
|
|
158
170
|
# from the "Content-Disposition" header if provided, otherwise a random filename.
|
159
171
|
#
|
160
172
|
# @see Configuration#temp_folder_path
|
161
|
-
# @return [
|
173
|
+
# @return [Tempfile] the file downloaded
|
162
174
|
def download_file(response)
|
163
|
-
tmp_file = Tempfile.new '', Configuration.temp_folder_path
|
164
175
|
content_disposition = response.headers['Content-Disposition']
|
165
176
|
if content_disposition
|
166
177
|
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
|
167
|
-
|
178
|
+
prefix = sanitize_filename(filename)
|
168
179
|
else
|
169
|
-
|
180
|
+
prefix = 'download-'
|
181
|
+
end
|
182
|
+
prefix = prefix + '-' unless prefix.end_with?('-')
|
183
|
+
|
184
|
+
tempfile = nil
|
185
|
+
encoding = response.body.encoding
|
186
|
+
Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file|
|
187
|
+
file.write(response.body)
|
188
|
+
tempfile = file
|
170
189
|
end
|
171
|
-
#
|
172
|
-
|
190
|
+
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
|
191
|
+
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
|
192
|
+
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
|
193
|
+
"explicitly with `tempfile.delete`"
|
194
|
+
tempfile
|
195
|
+
end
|
173
196
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
197
|
+
# Sanitize filename by removing path.
|
198
|
+
# e.g. ../../sun.gif becomes sun.gif
|
199
|
+
#
|
200
|
+
# @param [String] filename the filename to be sanitized
|
201
|
+
# @return [String] the sanitized filename
|
202
|
+
def sanitize_filename(filename)
|
203
|
+
filename.gsub /.*[\/\\]/, ''
|
178
204
|
end
|
179
205
|
|
180
206
|
def build_request_url(path)
|
181
207
|
# Add leading and trailing slashes to path
|
182
208
|
path = "/#{path}".gsub(/\/+/, '/')
|
183
|
-
URI.encode(
|
209
|
+
URI.encode(@config.base_url + path)
|
184
210
|
end
|
185
211
|
|
186
212
|
def build_request_body(header_params, form_params, body)
|
187
213
|
# http form
|
188
214
|
if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
|
189
215
|
header_params['Content-Type'] == 'multipart/form-data'
|
190
|
-
data =
|
191
|
-
|
192
|
-
|
216
|
+
data = {}
|
217
|
+
form_params.each do |key, value|
|
218
|
+
case value
|
219
|
+
when File, Array, nil
|
220
|
+
# let typhoeus handle File, Array and nil parameters
|
221
|
+
data[key] = value
|
222
|
+
else
|
223
|
+
data[key] = value.to_s
|
224
|
+
end
|
193
225
|
end
|
194
226
|
elsif body
|
195
227
|
data = body.is_a?(String) ? body : body.to_json
|
@@ -202,7 +234,7 @@ module Supportify
|
|
202
234
|
# Update hearder and query params based on authentication settings.
|
203
235
|
def update_params_for_auth!(header_params, query_params, auth_names)
|
204
236
|
Array(auth_names).each do |auth_name|
|
205
|
-
auth_setting =
|
237
|
+
auth_setting = @config.auth_settings[auth_name]
|
206
238
|
next unless auth_setting
|
207
239
|
case auth_setting[:in]
|
208
240
|
when 'header' then header_params[auth_setting[:key]] = auth_setting[:value]
|
@@ -221,26 +253,21 @@ module Supportify
|
|
221
253
|
# @param [Array] accepts array for Accept
|
222
254
|
# @return [String] the Accept header (e.g. application/json)
|
223
255
|
def select_header_accept(accepts)
|
224
|
-
if accepts.empty?
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
else
|
229
|
-
accepts.join(',')
|
230
|
-
end
|
256
|
+
return nil if accepts.nil? || accepts.empty?
|
257
|
+
# use JSON when present, otherwise use all of the provided
|
258
|
+
json_accept = accepts.find { |s| json_mime?(s) }
|
259
|
+
return json_accept || accepts.join(',')
|
231
260
|
end
|
232
261
|
|
233
262
|
# Return Content-Type header based on an array of content types provided.
|
234
263
|
# @param [Array] content_types array for Content-Type
|
235
264
|
# @return [String] the Content-Type header (e.g. application/json)
|
236
265
|
def select_header_content_type(content_types)
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
content_types[0] # otherwise, use the first one
|
243
|
-
end
|
266
|
+
# use application/json by default
|
267
|
+
return 'application/json' if content_types.nil? || content_types.empty?
|
268
|
+
# use JSON when present, otherwise use the first one
|
269
|
+
json_content_type = content_types.find { |s| json_mime?(s) }
|
270
|
+
return json_content_type || content_types.first
|
244
271
|
end
|
245
272
|
|
246
273
|
# Convert object (array, hash, object, etc) to JSON string.
|
@@ -267,5 +294,25 @@ module Supportify
|
|
267
294
|
obj
|
268
295
|
end
|
269
296
|
end
|
297
|
+
|
298
|
+
# Build parameter value according to the given collection format.
|
299
|
+
# @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi
|
300
|
+
def build_collection_param(param, collection_format)
|
301
|
+
case collection_format
|
302
|
+
when :csv
|
303
|
+
param.join(',')
|
304
|
+
when :ssv
|
305
|
+
param.join(' ')
|
306
|
+
when :tsv
|
307
|
+
param.join("\t")
|
308
|
+
when :pipes
|
309
|
+
param.join('|')
|
310
|
+
when :multi
|
311
|
+
# return the array directly as typhoeus will handle it as expected
|
312
|
+
param
|
313
|
+
else
|
314
|
+
fail "unknown collection format: #{collection_format.inspect}"
|
315
|
+
end
|
316
|
+
end
|
270
317
|
end
|
271
318
|
end
|
@@ -1,14 +1,7 @@
|
|
1
1
|
require 'uri'
|
2
|
-
require 'singleton'
|
3
2
|
|
4
3
|
module Supportify
|
5
4
|
class Configuration
|
6
|
-
|
7
|
-
include Singleton
|
8
|
-
|
9
|
-
# Default api client
|
10
|
-
attr_accessor :api_client
|
11
|
-
|
12
5
|
# Defines url scheme
|
13
6
|
attr_accessor :scheme
|
14
7
|
|
@@ -44,6 +37,9 @@ module Supportify
|
|
44
37
|
# @return [String]
|
45
38
|
attr_accessor :password
|
46
39
|
|
40
|
+
# Defines the access token (Bearer) used with OAuth2.
|
41
|
+
attr_accessor :access_token
|
42
|
+
|
47
43
|
# Set this to enable/disable debugging. When enabled (set to true), HTTP request/response
|
48
44
|
# details will be logged with `logger.debug` (see the `logger` attribute).
|
49
45
|
# Default to false.
|
@@ -64,6 +60,11 @@ module Supportify
|
|
64
60
|
# @return [String]
|
65
61
|
attr_accessor :temp_folder_path
|
66
62
|
|
63
|
+
# The time limit for HTTP request in seconds.
|
64
|
+
# Default to 0 (never times out).
|
65
|
+
attr_accessor :timeout
|
66
|
+
|
67
|
+
### TLS/SSL
|
67
68
|
# Set this to false to skip verifying SSL certificate when calling API from https server.
|
68
69
|
# Default to true.
|
69
70
|
#
|
@@ -80,36 +81,41 @@ module Supportify
|
|
80
81
|
# https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145
|
81
82
|
attr_accessor :ssl_ca_cert
|
82
83
|
|
84
|
+
# Client certificate file (for client certificate)
|
85
|
+
attr_accessor :cert_file
|
86
|
+
|
87
|
+
# Client private key file (for client certificate)
|
88
|
+
attr_accessor :key_file
|
89
|
+
|
83
90
|
attr_accessor :inject_format
|
84
91
|
|
85
92
|
attr_accessor :force_ending_format
|
86
93
|
|
87
|
-
class << self
|
88
|
-
def method_missing(method_name, *args, &block)
|
89
|
-
config = Configuration.instance
|
90
|
-
if config.respond_to?(method_name)
|
91
|
-
config.send(method_name, *args, &block)
|
92
|
-
else
|
93
|
-
super
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
94
|
def initialize
|
99
95
|
@scheme = 'https'
|
100
96
|
@host = 'api.supportify.io'
|
101
|
-
@base_path = '/
|
97
|
+
@base_path = '/v3'
|
102
98
|
@api_key = {}
|
103
99
|
@api_key_prefix = {}
|
100
|
+
@timeout = 0
|
104
101
|
@verify_ssl = true
|
102
|
+
@cert_file = nil
|
103
|
+
@key_file = nil
|
105
104
|
@debugging = false
|
106
105
|
@inject_format = false
|
107
106
|
@force_ending_format = false
|
108
107
|
@logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
108
|
+
|
109
|
+
yield(self) if block_given?
|
110
|
+
end
|
111
|
+
|
112
|
+
# The default Configuration object.
|
113
|
+
def self.default
|
114
|
+
@@default ||= Configuration.new
|
109
115
|
end
|
110
116
|
|
111
|
-
def
|
112
|
-
|
117
|
+
def configure
|
118
|
+
yield(self) if block_given?
|
113
119
|
end
|
114
120
|
|
115
121
|
def scheme=(scheme)
|
@@ -129,7 +135,7 @@ module Supportify
|
|
129
135
|
end
|
130
136
|
|
131
137
|
def base_url
|
132
|
-
url = "#{scheme}://#{[host, base_path].join('/').gsub(/\/+/, '/')}"
|
138
|
+
url = "#{scheme}://#{[host, base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
|
133
139
|
URI.encode(url)
|
134
140
|
end
|
135
141
|
|
@@ -163,23 +169,23 @@ module Supportify
|
|
163
169
|
def application_api_key
|
164
170
|
@api_key['X-SUPPORTIFY-APPKEY']
|
165
171
|
end
|
166
|
-
|
172
|
+
|
167
173
|
# Returns Auth Settings hash for api client.
|
168
174
|
def auth_settings
|
169
175
|
{
|
170
|
-
'
|
176
|
+
'app_key' =>
|
171
177
|
{
|
172
178
|
type: 'api_key',
|
173
179
|
in: 'header',
|
174
|
-
key: 'X-SUPPORTIFY-
|
175
|
-
value: api_key_with_prefix('X-SUPPORTIFY-
|
180
|
+
key: 'X-SUPPORTIFY-APPKEY',
|
181
|
+
value: api_key_with_prefix('X-SUPPORTIFY-APPKEY')
|
176
182
|
},
|
177
|
-
'
|
183
|
+
'api_key' =>
|
178
184
|
{
|
179
185
|
type: 'api_key',
|
180
186
|
in: 'header',
|
181
|
-
key: 'X-SUPPORTIFY-
|
182
|
-
value: api_key_with_prefix('X-SUPPORTIFY-
|
187
|
+
key: 'X-SUPPORTIFY-APIKEY',
|
188
|
+
value: api_key_with_prefix('X-SUPPORTIFY-APIKEY')
|
183
189
|
},
|
184
190
|
}
|
185
191
|
end
|
@@ -1,24 +1,30 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
module Supportify
|
2
|
-
|
3
|
-
|
4
|
-
attr_accessor :id
|
5
|
-
|
4
|
+
class Category
|
5
|
+
# Unique identifier representing a specific category within an application.
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
# Description of the category.
|
9
|
+
attr_accessor :description
|
10
|
+
|
11
|
+
# Display name of the category.
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
# Attribute mapping from ruby-style variable name to JSON key.
|
6
15
|
def self.attribute_map
|
7
16
|
{
|
8
17
|
|
9
|
-
# Unique identifier representing a specific category within an application.
|
10
18
|
:'id' => :'id',
|
11
19
|
|
12
|
-
# Description of the category.
|
13
20
|
:'description' => :'description',
|
14
21
|
|
15
|
-
# Display name of the category.
|
16
22
|
:'name' => :'name'
|
17
23
|
|
18
24
|
}
|
19
25
|
end
|
20
26
|
|
21
|
-
#
|
27
|
+
# Attribute type mapping.
|
22
28
|
def self.swagger_types
|
23
29
|
{
|
24
30
|
:'id' => :'Integer',
|
@@ -29,7 +35,7 @@ module Supportify
|
|
29
35
|
end
|
30
36
|
|
31
37
|
def initialize(attributes = {})
|
32
|
-
return
|
38
|
+
return unless attributes.is_a?(Hash)
|
33
39
|
|
34
40
|
# convert string to symbol for hash key
|
35
41
|
attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
@@ -49,5 +55,115 @@ module Supportify
|
|
49
55
|
|
50
56
|
end
|
51
57
|
|
58
|
+
# Check equality by comparing each attribute.
|
59
|
+
def ==(o)
|
60
|
+
return true if self.equal?(o)
|
61
|
+
self.class == o.class &&
|
62
|
+
id == o.id &&
|
63
|
+
description == o.description &&
|
64
|
+
name == o.name
|
65
|
+
end
|
66
|
+
|
67
|
+
# @see the `==` method
|
68
|
+
def eql?(o)
|
69
|
+
self == o
|
70
|
+
end
|
71
|
+
|
72
|
+
# Calculate hash code according to all attributes.
|
73
|
+
def hash
|
74
|
+
[id, description, name].hash
|
75
|
+
end
|
76
|
+
|
77
|
+
# build the object from hash
|
78
|
+
def build_from_hash(attributes)
|
79
|
+
return nil unless attributes.is_a?(Hash)
|
80
|
+
self.class.swagger_types.each_pair do |key, type|
|
81
|
+
if type =~ /^Array<(.*)>/i
|
82
|
+
if attributes[self.class.attribute_map[key]].is_a?(Array)
|
83
|
+
self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
|
84
|
+
else
|
85
|
+
#TODO show warning in debug mode
|
86
|
+
end
|
87
|
+
elsif !attributes[self.class.attribute_map[key]].nil?
|
88
|
+
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
|
89
|
+
else
|
90
|
+
# data not found in attributes(hash), not an issue as the data can be optional
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def _deserialize(type, value)
|
98
|
+
case type.to_sym
|
99
|
+
when :DateTime
|
100
|
+
DateTime.parse(value)
|
101
|
+
when :Date
|
102
|
+
Date.parse(value)
|
103
|
+
when :String
|
104
|
+
value.to_s
|
105
|
+
when :Integer
|
106
|
+
value.to_i
|
107
|
+
when :Float
|
108
|
+
value.to_f
|
109
|
+
when :BOOLEAN
|
110
|
+
if value =~ /^(true|t|yes|y|1)$/i
|
111
|
+
true
|
112
|
+
else
|
113
|
+
false
|
114
|
+
end
|
115
|
+
when /\AArray<(?<inner_type>.+)>\z/
|
116
|
+
inner_type = Regexp.last_match[:inner_type]
|
117
|
+
value.map { |v| _deserialize(inner_type, v) }
|
118
|
+
when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
|
119
|
+
k_type = Regexp.last_match[:k_type]
|
120
|
+
v_type = Regexp.last_match[:v_type]
|
121
|
+
{}.tap do |hash|
|
122
|
+
value.each do |k, v|
|
123
|
+
hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else # model
|
127
|
+
_model = Supportify.const_get(type).new
|
128
|
+
_model.build_from_hash(value)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_s
|
133
|
+
to_hash.to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
# to_body is an alias to to_body (backward compatibility))
|
137
|
+
def to_body
|
138
|
+
to_hash
|
139
|
+
end
|
140
|
+
|
141
|
+
# return the object in the form of hash
|
142
|
+
def to_hash
|
143
|
+
hash = {}
|
144
|
+
self.class.attribute_map.each_pair do |attr, param|
|
145
|
+
value = self.send(attr)
|
146
|
+
next if value.nil?
|
147
|
+
hash[param] = _to_hash(value)
|
148
|
+
end
|
149
|
+
hash
|
150
|
+
end
|
151
|
+
|
152
|
+
# Method to output non-array value in the form of hash
|
153
|
+
# For object, use to_hash. Otherwise, just return the value
|
154
|
+
def _to_hash(value)
|
155
|
+
if value.is_a?(Array)
|
156
|
+
value.compact.map{ |v| _to_hash(v) }
|
157
|
+
elsif value.is_a?(Hash)
|
158
|
+
{}.tap do |hash|
|
159
|
+
value.each { |k, v| hash[k] = _to_hash(v) }
|
160
|
+
end
|
161
|
+
elsif value.respond_to? :to_hash
|
162
|
+
value.to_hash
|
163
|
+
else
|
164
|
+
value
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
52
168
|
end
|
53
169
|
end
|