fog-core 1.24.0 → 1.25.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 +5 -13
- data/.rubocop.yml +20 -0
- data/changelog.md +17 -0
- data/fog-core.gemspec +2 -1
- data/lib/fog/account.rb +3 -5
- data/lib/fog/billing.rb +3 -4
- data/lib/fog/cdn.rb +3 -5
- data/lib/fog/compute.rb +17 -20
- data/lib/fog/compute/models/server.rb +21 -26
- data/lib/fog/core.rb +61 -60
- data/lib/fog/core/association.rb +15 -0
- data/lib/fog/core/associations/default.rb +21 -3
- data/lib/fog/core/associations/many_identities.rb +8 -2
- data/lib/fog/core/associations/many_models.rb +7 -2
- data/lib/fog/core/associations/one_identity.rb +6 -1
- data/lib/fog/core/associations/one_model.rb +5 -1
- data/lib/fog/core/attributes.rb +41 -44
- data/lib/fog/core/attributes/array.rb +5 -1
- data/lib/fog/core/attributes/boolean.rb +5 -1
- data/lib/fog/core/attributes/default.rb +12 -2
- data/lib/fog/core/attributes/float.rb +5 -1
- data/lib/fog/core/attributes/integer.rb +5 -1
- data/lib/fog/core/attributes/string.rb +5 -1
- data/lib/fog/core/attributes/time.rb +5 -1
- data/lib/fog/core/attributes/timestamp.rb +5 -1
- data/lib/fog/core/collection.rb +22 -27
- data/lib/fog/core/connection.rb +5 -6
- data/lib/fog/core/credentials.rb +7 -7
- data/lib/fog/core/current_machine.rb +10 -8
- data/lib/fog/core/deprecated_connection_accessors.rb +0 -1
- data/lib/fog/core/deprecation.rb +0 -2
- data/lib/fog/core/errors.rb +3 -5
- data/lib/fog/core/hmac.rb +4 -6
- data/lib/fog/core/logger.rb +10 -11
- data/lib/fog/core/mock.rb +19 -25
- data/lib/fog/core/model.rb +9 -20
- data/lib/fog/core/provider.rb +6 -9
- data/lib/fog/core/scp.rb +14 -24
- data/lib/fog/core/service.rb +28 -31
- data/lib/fog/core/ssh.rb +16 -24
- data/lib/fog/core/stringify_keys.rb +7 -9
- data/lib/fog/core/time.rb +5 -7
- data/lib/fog/core/utils.rb +24 -20
- data/lib/fog/core/uuid.rb +2 -3
- data/lib/fog/core/version.rb +3 -1
- data/lib/fog/core/wait_for.rb +2 -2
- data/lib/fog/core/wait_for_defaults.rb +13 -10
- data/lib/fog/core/whitelist_keys.rb +1 -1
- data/lib/fog/dns.rb +6 -8
- data/lib/fog/identity.rb +5 -6
- data/lib/fog/image.rb +3 -5
- data/lib/fog/metering.rb +3 -6
- data/lib/fog/monitoring.rb +3 -5
- data/lib/fog/network.rb +4 -6
- data/lib/fog/orchestration.rb +3 -5
- data/lib/fog/schema/data_validator.rb +17 -22
- data/lib/fog/storage.rb +22 -16
- data/lib/fog/support.rb +3 -6
- data/lib/fog/test_helpers.rb +10 -10
- data/lib/fog/test_helpers/collection_helper.rb +23 -43
- data/lib/fog/test_helpers/compute/flavors_helper.rb +4 -10
- data/lib/fog/test_helpers/compute/server_helper.rb +3 -9
- data/lib/fog/test_helpers/compute/servers_helper.rb +0 -4
- data/lib/fog/test_helpers/formats_helper.rb +13 -14
- data/lib/fog/test_helpers/helper.rb +9 -4
- data/lib/fog/test_helpers/mock_helper.rb +92 -94
- data/lib/fog/test_helpers/model_helper.rb +7 -15
- data/lib/fog/test_helpers/responds_to_helper.rb +1 -3
- data/lib/fog/test_helpers/succeeds_helper.rb +1 -3
- data/lib/fog/volume.rb +3 -6
- data/lib/fog/vpn.rb +3 -5
- data/lib/tasks/test_task.rb +2 -6
- data/spec/compute_spec.rb +11 -13
- data/spec/connection_spec.rb +24 -14
- data/spec/credentials_spec.rb +23 -23
- data/spec/current_machine_spec.rb +6 -6
- data/spec/fake_app/fake_service.rb +18 -0
- data/spec/fake_app/models/collection.rb +5 -0
- data/spec/fake_app/models/model.rb +2 -0
- data/spec/fake_app/requests/request.rb +11 -0
- data/spec/fog_attribute_spec.rb +178 -136
- data/spec/identity_spec.rb +11 -13
- data/spec/mocking_spec.rb +7 -8
- data/spec/service_spec.rb +21 -7
- data/spec/spec_helper.rb +14 -8
- data/spec/storage_spec.rb +25 -13
- data/spec/test_helpers/formats_helper_spec.rb +52 -52
- data/spec/test_helpers/schema_validator_spec.rb +45 -45
- data/spec/timeout_spec.rb +1 -2
- data/spec/utils_spec.rb +2 -2
- data/spec/uuid_spec.rb +1 -1
- data/spec/wait_for_spec.rb +7 -4
- metadata +57 -33
data/lib/fog/core/model.rb
CHANGED
@@ -2,7 +2,6 @@ require "fog/core/deprecated_connection_accessors"
|
|
2
2
|
|
3
3
|
module Fog
|
4
4
|
class Model
|
5
|
-
|
6
5
|
extend Fog::Attributes::ClassMethods
|
7
6
|
include Fog::Attributes::InstanceMethods
|
8
7
|
include Fog::Core::DeprecatedConnectionAccessors
|
@@ -11,7 +10,7 @@ module Fog
|
|
11
10
|
attr_reader :service
|
12
11
|
|
13
12
|
def initialize(new_attributes = {})
|
14
|
-
# TODO Remove compatibility with old connection option
|
13
|
+
# TODO: Remove compatibility with old connection option
|
15
14
|
attribs = new_attributes.clone
|
16
15
|
@service = attribs.delete(:service)
|
17
16
|
if @service.nil? && attribs[:connection]
|
@@ -27,7 +26,7 @@ module Fog
|
|
27
26
|
Thread.current[:formatador].indent do
|
28
27
|
unless self.class.attributes.empty?
|
29
28
|
data << "\n#{Thread.current[:formatador].indentation}"
|
30
|
-
data << self.class.attributes.map {|attribute| "#{attribute}=#{send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
|
29
|
+
data << self.class.attributes.map { |attribute| "#{attribute}=#{send(attribute).inspect}" }.join(",\n#{Thread.current[:formatador].indentation}")
|
31
30
|
end
|
32
31
|
end
|
33
32
|
data << "\n#{Thread.current[:formatador].indentation}>"
|
@@ -37,30 +36,32 @@ module Fog
|
|
37
36
|
def reload
|
38
37
|
requires :identity
|
39
38
|
|
40
|
-
|
39
|
+
data = begin
|
41
40
|
collection.get(identity)
|
42
41
|
rescue Excon::Errors::SocketError
|
43
42
|
nil
|
44
43
|
end
|
45
44
|
|
45
|
+
return unless data
|
46
|
+
|
46
47
|
new_attributes = data.attributes
|
47
48
|
merge_attributes(new_attributes)
|
48
49
|
self
|
49
50
|
end
|
50
51
|
|
51
|
-
def to_json(
|
52
|
+
def to_json(_options = {})
|
52
53
|
Fog::JSON.encode(attributes)
|
53
54
|
end
|
54
55
|
|
55
56
|
def symbolize_keys(hash)
|
56
57
|
return nil if hash.nil?
|
57
|
-
hash.
|
58
|
+
hash.reduce({}) do |options, (key, value)|
|
58
59
|
options[(key.to_sym rescue key) || key] = value
|
59
60
|
options
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
|
-
def wait_for(timeout=Fog.timeout, interval=1, &block)
|
64
|
+
def wait_for(timeout = Fog.timeout, interval = 1, &block)
|
64
65
|
reload_has_succeeded = false
|
65
66
|
duration = Fog.wait_for(timeout, interval) do # Note that duration = false if it times out
|
66
67
|
if reload
|
@@ -73,20 +74,8 @@ module Fog
|
|
73
74
|
if reload_has_succeeded
|
74
75
|
return duration # false if timeout; otherwise {:duration => elapsed time }
|
75
76
|
else
|
76
|
-
raise Fog::Errors::Error
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def method_missing(method_name, *args)
|
81
|
-
if self.class.aliases.include?(method_name)
|
82
|
-
send(self.class.aliases[method_name])
|
83
|
-
else
|
84
|
-
super
|
77
|
+
raise Fog::Errors::Error, "Reload failed, #{self.class} #{identity} not present."
|
85
78
|
end
|
86
79
|
end
|
87
|
-
|
88
|
-
def respond_to?(method_name, include_private = false)
|
89
|
-
super || self.class.aliases.include?(method_name)
|
90
|
-
end
|
91
80
|
end
|
92
81
|
end
|
data/lib/fog/core/provider.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
module Fog
|
2
|
+
class << self
|
3
|
+
attr_writer :providers
|
4
|
+
end
|
2
5
|
|
3
6
|
def self.providers
|
4
7
|
@providers ||= {}
|
5
8
|
end
|
6
9
|
|
7
|
-
def self.providers=(new_providers)
|
8
|
-
@providers = new_providers
|
9
|
-
end
|
10
|
-
|
11
10
|
module Provider
|
12
|
-
|
13
11
|
def self.extended(base)
|
14
|
-
provider = base.to_s.split(
|
12
|
+
provider = base.to_s.split("::").last
|
15
13
|
Fog.providers[provider.downcase.to_sym] = provider
|
16
14
|
end
|
17
15
|
|
@@ -21,14 +19,13 @@ module Fog
|
|
21
19
|
|
22
20
|
def service(new_service, constant_string)
|
23
21
|
Fog.services[new_service] ||= []
|
24
|
-
Fog.services[new_service] |= [
|
22
|
+
Fog.services[new_service] |= [to_s.split("::").last.downcase.to_sym]
|
25
23
|
@services_registry ||= {}
|
26
|
-
@services_registry[new_service] = [
|
24
|
+
@services_registry[new_service] = [to_s, constant_string].join("::")
|
27
25
|
end
|
28
26
|
|
29
27
|
def services
|
30
28
|
@services_registry.keys
|
31
29
|
end
|
32
|
-
|
33
30
|
end
|
34
31
|
end
|
data/lib/fog/core/scp.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Fog
|
2
2
|
module SCP
|
3
|
-
|
4
3
|
def self.new(address, username, options = {})
|
5
4
|
if Fog.mocking?
|
6
5
|
Fog::SCP::Mock.new(address, username, options)
|
@@ -10,7 +9,6 @@ module Fog
|
|
10
9
|
end
|
11
10
|
|
12
11
|
class Mock
|
13
|
-
|
14
12
|
def self.data
|
15
13
|
@data ||= Hash.new do |hash, key|
|
16
14
|
hash[key] = []
|
@@ -38,25 +36,23 @@ module Fog
|
|
38
36
|
:local_path => local_path,
|
39
37
|
:download_options => download_options }
|
40
38
|
end
|
41
|
-
|
42
39
|
end
|
43
40
|
|
44
41
|
class Real
|
45
|
-
|
46
42
|
def initialize(address, username, options)
|
47
|
-
require
|
43
|
+
require "net/scp"
|
48
44
|
|
49
45
|
key_manager = Net::SSH::Authentication::KeyManager.new(nil, options)
|
50
46
|
|
51
47
|
unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent
|
52
|
-
raise ArgumentError
|
48
|
+
raise ArgumentError, ":key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH"
|
53
49
|
end
|
54
50
|
|
55
51
|
options[:timeout] = 30
|
56
52
|
if options[:key_data] || options[:keys]
|
57
53
|
options[:keys_only] = true
|
58
|
-
#Explicitly set these so net-ssh doesn't add the default keys
|
59
|
-
#as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
|
54
|
+
# Explicitly set these so net-ssh doesn't add the default keys
|
55
|
+
# as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
|
60
56
|
options[:keys] = [] unless options[:keys]
|
61
57
|
options[:key_data] = [] unless options[:key_data]
|
62
58
|
end
|
@@ -67,30 +63,24 @@ module Fog
|
|
67
63
|
end
|
68
64
|
|
69
65
|
def upload(local_path, remote_path, upload_options = {}, &block)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
block.call(ch, name, sent, total) if block
|
74
|
-
end
|
66
|
+
Net::SCP.start(@address, @username, @options) do |scp|
|
67
|
+
scp.upload!(local_path, remote_path, upload_options) do |ch, name, sent, total|
|
68
|
+
block.call(ch, name, sent, total) if block
|
75
69
|
end
|
76
|
-
rescue Exception => error
|
77
|
-
raise error
|
78
70
|
end
|
71
|
+
rescue Exception => error
|
72
|
+
raise error
|
79
73
|
end
|
80
74
|
|
81
75
|
def download(remote_path, local_path, download_options = {}, &block)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
block.call(ch, name, sent, total) if block
|
86
|
-
end
|
76
|
+
Net::SCP.start(@address, @username, @options) do |scp|
|
77
|
+
scp.download!(remote_path, local_path, download_options) do |ch, name, sent, total|
|
78
|
+
block.call(ch, name, sent, total) if block
|
87
79
|
end
|
88
|
-
rescue Exception => error
|
89
|
-
raise error
|
90
80
|
end
|
81
|
+
rescue Exception => error
|
82
|
+
raise error
|
91
83
|
end
|
92
|
-
|
93
84
|
end
|
94
|
-
|
95
85
|
end
|
96
86
|
end
|
data/lib/fog/core/service.rb
CHANGED
@@ -11,7 +11,7 @@ module Fog
|
|
11
11
|
|
12
12
|
module NoLeakInspector
|
13
13
|
def inspect
|
14
|
-
"#<#{self.class}:#{
|
14
|
+
"#<#{self.class}:#{object_id} #{(instance_variables - service.secrets).map { |iv| [iv, instance_variable_get(iv).inspect].join("=") }.join(" ")}>"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -117,14 +117,12 @@ module Fog
|
|
117
117
|
end
|
118
118
|
|
119
119
|
# @deprecated
|
120
|
-
def fetch_credentials(
|
120
|
+
def fetch_credentials(_options)
|
121
121
|
# attempt to load credentials from config file
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
{}
|
127
|
-
end
|
122
|
+
Fog.credentials.reject { |key, _value| !(recognized | requirements).include?(key) }
|
123
|
+
rescue LoadError
|
124
|
+
# if there are no configured credentials, do nothing
|
125
|
+
{}
|
128
126
|
end
|
129
127
|
|
130
128
|
def setup_requirements
|
@@ -133,12 +131,13 @@ module Fog
|
|
133
131
|
end
|
134
132
|
|
135
133
|
@required ||= false
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
134
|
+
|
135
|
+
return false if @required
|
136
|
+
|
137
|
+
require_models
|
138
|
+
require_collections_and_define
|
139
|
+
require_requests_and_mock
|
140
|
+
@required = true
|
142
141
|
end
|
143
142
|
|
144
143
|
# @note This path is used to require model and collection files
|
@@ -163,13 +162,13 @@ module Fog
|
|
163
162
|
options[key] = value.to_i
|
164
163
|
else
|
165
164
|
options[key] = case value_string
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
165
|
+
when "false"
|
166
|
+
false
|
167
|
+
when "true"
|
168
|
+
true
|
169
|
+
else
|
170
|
+
value
|
171
|
+
end
|
173
172
|
end
|
174
173
|
end
|
175
174
|
end
|
@@ -202,7 +201,7 @@ module Fog
|
|
202
201
|
if args.empty?
|
203
202
|
@secrets ||= []
|
204
203
|
else
|
205
|
-
args.
|
204
|
+
args.reduce(secrets) do |secrets, secret|
|
206
205
|
secrets << "@#{secret}".to_sym
|
207
206
|
end
|
208
207
|
end
|
@@ -226,21 +225,19 @@ module Fog
|
|
226
225
|
|
227
226
|
def validate_options(options)
|
228
227
|
keys = []
|
229
|
-
|
230
|
-
unless value.nil?
|
231
|
-
keys << key
|
232
|
-
end
|
228
|
+
options.each_pair do |key, value|
|
229
|
+
keys << key unless value.nil?
|
233
230
|
end
|
234
231
|
missing = requirements - keys
|
235
232
|
|
236
233
|
unless missing.empty?
|
237
|
-
raise ArgumentError, "Missing required arguments: #{missing.join(
|
234
|
+
raise ArgumentError, "Missing required arguments: #{missing.join(", ")}"
|
238
235
|
end
|
239
236
|
|
240
237
|
unless recognizes.empty?
|
241
238
|
unrecognized = options.keys - requirements - recognized
|
242
239
|
unless unrecognized.empty?
|
243
|
-
Fog::Logger.warning("Unrecognized arguments: #{unrecognized.join(
|
240
|
+
Fog::Logger.warning("Unrecognized arguments: #{unrecognized.join(", ")}")
|
244
241
|
end
|
245
242
|
end
|
246
243
|
end
|
@@ -262,7 +259,7 @@ module Fog
|
|
262
259
|
coerce_options(prepared_settings)
|
263
260
|
end
|
264
261
|
|
265
|
-
# This will attempt to require all model files declared by the service using fog
|
262
|
+
# This will attempt to require all model files declared by the service using fog"s DSL
|
266
263
|
def require_models
|
267
264
|
models.each do |model|
|
268
265
|
require File.join(@model_path, model.to_s)
|
@@ -287,10 +284,10 @@ module Fog
|
|
287
284
|
# @param [String,Symbol] collection The name of the collection broken with underscores
|
288
285
|
# @return [String] in camel case
|
289
286
|
def camel_case_collection_name(collection)
|
290
|
-
collection.to_s.split(
|
287
|
+
collection.to_s.split("_").map(&:capitalize).join
|
291
288
|
end
|
292
289
|
|
293
|
-
# This will attempt to require all request files declared in the service using fog
|
290
|
+
# This will attempt to require all request files declared in the service using fog"s DSL
|
294
291
|
def require_requests_and_mock
|
295
292
|
requests.each do |request|
|
296
293
|
require File.join(@request_path, request.to_s)
|
data/lib/fog/core/ssh.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "delegate"
|
2
2
|
|
3
3
|
module Fog
|
4
4
|
module SSH
|
5
|
-
|
6
5
|
def self.new(address, username, options = {})
|
7
6
|
if Fog.mocking?
|
8
7
|
Fog::SSH::Mock.new(address, username, options)
|
@@ -12,7 +11,6 @@ module Fog
|
|
12
11
|
end
|
13
12
|
|
14
13
|
class Mock
|
15
|
-
|
16
14
|
def self.data
|
17
15
|
@data ||= Hash.new do |hash, key|
|
18
16
|
hash[key] = []
|
@@ -20,7 +18,7 @@ module Fog
|
|
20
18
|
end
|
21
19
|
|
22
20
|
def self.reset
|
23
|
-
@data= nil
|
21
|
+
@data = nil
|
24
22
|
end
|
25
23
|
|
26
24
|
def initialize(address, username, options)
|
@@ -29,28 +27,26 @@ module Fog
|
|
29
27
|
@options = options
|
30
28
|
end
|
31
29
|
|
32
|
-
def run(commands, &
|
33
|
-
self.class.data[@address] << {:commands => commands, :username => @username, :options => @options}
|
30
|
+
def run(commands, &_blk)
|
31
|
+
self.class.data[@address] << { :commands => commands, :username => @username, :options => @options }
|
34
32
|
end
|
35
|
-
|
36
33
|
end
|
37
34
|
|
38
35
|
class Real
|
39
|
-
|
40
36
|
def initialize(address, username, options)
|
41
|
-
require
|
37
|
+
require "net/ssh"
|
42
38
|
|
43
39
|
key_manager = Net::SSH::Authentication::KeyManager.new(nil, options)
|
44
40
|
|
45
41
|
unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent
|
46
|
-
raise ArgumentError
|
42
|
+
raise ArgumentError, ":key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH"
|
47
43
|
end
|
48
44
|
|
49
45
|
options[:timeout] ||= 30
|
50
46
|
if options[:key_data] || options[:keys]
|
51
47
|
options[:keys_only] = true
|
52
|
-
#Explicitly set these so net-ssh doesn
|
53
|
-
#as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
|
48
|
+
# Explicitly set these so net-ssh doesn"t add the default keys
|
49
|
+
# as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
|
54
50
|
options[:keys] = [] unless options[:keys]
|
55
51
|
options[:key_data] = [] unless options[:key_data]
|
56
52
|
end
|
@@ -74,22 +70,22 @@ module Fog
|
|
74
70
|
raise "Could not execute command: #{command.inspect}"
|
75
71
|
end
|
76
72
|
|
77
|
-
channel.on_data do |
|
73
|
+
channel.on_data do |_ch, data|
|
78
74
|
result.stdout << data
|
79
|
-
yield [data,
|
75
|
+
yield [data, ""] if blk
|
80
76
|
end
|
81
77
|
|
82
|
-
channel.on_extended_data do |
|
78
|
+
channel.on_extended_data do |_ch, type, data|
|
83
79
|
next unless type == 1
|
84
80
|
result.stderr << data
|
85
|
-
yield [
|
81
|
+
yield ["", data] if blk
|
86
82
|
end
|
87
83
|
|
88
|
-
channel.on_request(
|
84
|
+
channel.on_request("exit-status") do |_ch, data|
|
89
85
|
result.status = data.read_long
|
90
86
|
end
|
91
87
|
|
92
|
-
channel.on_request(
|
88
|
+
channel.on_request("exit-signal") do |_ch, _data|
|
93
89
|
result.status = 255
|
94
90
|
end
|
95
91
|
end
|
@@ -105,11 +101,9 @@ module Fog
|
|
105
101
|
end
|
106
102
|
results
|
107
103
|
end
|
108
|
-
|
109
104
|
end
|
110
105
|
|
111
106
|
class Result
|
112
|
-
|
113
107
|
attr_accessor :command, :stderr, :stdout, :status
|
114
108
|
|
115
109
|
def display_stdout
|
@@ -127,11 +121,9 @@ module Fog
|
|
127
121
|
|
128
122
|
def initialize(command)
|
129
123
|
@command = command
|
130
|
-
@stderr =
|
131
|
-
@stdout =
|
124
|
+
@stderr = ""
|
125
|
+
@stdout = ""
|
132
126
|
end
|
133
|
-
|
134
127
|
end
|
135
|
-
|
136
128
|
end
|
137
129
|
end
|
@@ -1,27 +1,25 @@
|
|
1
1
|
module Fog
|
2
2
|
module StringifyKeys
|
3
|
-
|
4
3
|
# Returns a new hash with all keys converted to strings.
|
5
4
|
def self.stringify(hash)
|
6
|
-
|
5
|
+
transform_hash(hash) do |hash, key, value|
|
7
6
|
hash[key.to_s] = value
|
8
|
-
|
7
|
+
end
|
9
8
|
end
|
10
9
|
|
11
10
|
private
|
12
11
|
|
13
12
|
# http://devblog.avdi.org/2009/11/20/hash-transforms-in-ruby/
|
14
|
-
def self.transform_hash(original, options={}, &block)
|
15
|
-
original.
|
16
|
-
value = if
|
13
|
+
def self.transform_hash(original, options = {}, &block)
|
14
|
+
original.reduce({}) do |result, (key, value)|
|
15
|
+
value = if options[:deep] && Hash === value
|
17
16
|
transform_hash(value, options, &block)
|
18
17
|
else
|
19
18
|
value
|
20
19
|
end
|
21
|
-
block.call(result,key,value)
|
20
|
+
block.call(result, key, value)
|
22
21
|
result
|
23
|
-
|
22
|
+
end
|
24
23
|
end
|
25
|
-
|
26
24
|
end
|
27
25
|
end
|