fog-core 1.24.0 → 1.25.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|