guignol 0.1.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +2 -1
  3. data/Gemfile.lock +29 -18
  4. data/LICENCE +26 -0
  5. data/README.md +67 -38
  6. data/Rakefile +0 -26
  7. data/bin/guignol +3 -33
  8. data/guignol.gemspec +4 -30
  9. data/lib/core_ext/array/collect_key.rb +6 -0
  10. data/lib/core_ext/hash/map_to_hash.rb +31 -0
  11. data/lib/guignol.rb +15 -35
  12. data/lib/guignol/commands/base.rb +53 -66
  13. data/lib/guignol/commands/clone.rb +49 -0
  14. data/lib/guignol/commands/create.rb +14 -33
  15. data/lib/guignol/commands/execute.rb +69 -0
  16. data/lib/guignol/commands/fix_dns.rb +12 -33
  17. data/lib/guignol/commands/kill.rb +18 -36
  18. data/lib/guignol/commands/list.rb +19 -45
  19. data/lib/guignol/commands/start.rb +14 -33
  20. data/lib/guignol/commands/stop.rb +18 -37
  21. data/lib/guignol/commands/uuid.rb +19 -32
  22. data/lib/guignol/configuration.rb +43 -0
  23. data/lib/guignol/connection.rb +33 -0
  24. data/lib/guignol/env.rb +19 -0
  25. data/lib/guignol/logger.rb +29 -0
  26. data/lib/guignol/models/base.rb +125 -0
  27. data/lib/guignol/models/instance.rb +244 -0
  28. data/lib/guignol/models/volume.rb +91 -0
  29. data/lib/guignol/shell.rb +27 -42
  30. data/lib/guignol/tty_spinner.rb +6 -29
  31. data/lib/guignol/version.rb +1 -27
  32. data/spec/guignol/configuration_spec.rb +72 -0
  33. data/spec/guignol/instance_spec.rb +48 -8
  34. data/spec/guignol/volume_spec.rb +17 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/tmp/.keepme +0 -0
  37. metadata +79 -52
  38. data/lib/guignol/array/collect_key.rb +0 -32
  39. data/lib/guignol/commands.rb +0 -48
  40. data/lib/guignol/commands/help.rb +0 -77
  41. data/lib/guignol/instance.rb +0 -270
  42. data/lib/guignol/shared.rb +0 -80
  43. data/lib/guignol/volume.rb +0 -124
@@ -0,0 +1,2 @@
1
+ pkg
2
+ .DS_Store
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
- --format nested
2
+ --tty
3
+ --format progress
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- guignol (0.1.2.1)
4
+ guignol (0.3.0)
5
5
  active_support
6
- fog (~> 1.1.2)
6
+ fog (~> 1.6.0)
7
7
  parallel (~> 0.5.14)
8
- term-ansicolor
8
+ thor
9
9
  uuidtools
10
10
 
11
11
  GEM
@@ -14,28 +14,37 @@ GEM
14
14
  active_support (3.0.0)
15
15
  activesupport (= 3.0.0)
16
16
  activesupport (3.0.0)
17
- awesome_print (1.0.2)
18
- builder (3.0.0)
17
+ builder (3.1.3)
18
+ coderay (1.0.7)
19
19
  diff-lcs (1.1.3)
20
- excon (0.9.6)
21
- fog (1.1.2)
20
+ excon (0.16.4)
21
+ fog (1.6.0)
22
22
  builder
23
- excon (~> 0.9.0)
23
+ excon (~> 0.14)
24
24
  formatador (~> 0.2.0)
25
25
  mime-types
26
- multi_json (~> 1.0.3)
26
+ multi_json (~> 1.0)
27
27
  net-scp (~> 1.0.4)
28
28
  net-ssh (>= 2.1.3)
29
29
  nokogiri (~> 1.5.0)
30
30
  ruby-hmac
31
- formatador (0.2.1)
32
- mime-types (1.18)
33
- multi_json (1.0.4)
31
+ formatador (0.2.3)
32
+ jruby-pageant (1.1.1)
33
+ method_source (0.8)
34
+ mime-types (1.19)
35
+ multi_json (1.3.6)
34
36
  net-scp (1.0.4)
35
37
  net-ssh (>= 1.99.1)
36
- net-ssh (2.3.0)
37
- nokogiri (1.5.2)
38
- parallel (0.5.16)
38
+ net-ssh (2.6.0)
39
+ jruby-pageant (>= 1.1.1)
40
+ nokogiri (1.5.5)
41
+ parallel (0.5.18)
42
+ pry (0.9.10)
43
+ coderay (~> 1.0.5)
44
+ method_source (~> 0.8)
45
+ slop (~> 3.3.1)
46
+ pry-nav (0.2.2)
47
+ pry (~> 0.9.10)
39
48
  rake (0.9.2.2)
40
49
  rspec (2.4.0)
41
50
  rspec-core (~> 2.4.0)
@@ -46,15 +55,17 @@ GEM
46
55
  diff-lcs (~> 1.1.2)
47
56
  rspec-mocks (2.4.0)
48
57
  ruby-hmac (0.4.0)
49
- term-ansicolor (1.0.7)
50
- uuidtools (2.1.2)
58
+ slop (3.3.3)
59
+ thor (0.16.0)
60
+ uuidtools (2.1.3)
51
61
 
52
62
  PLATFORMS
53
63
  ruby
54
64
 
55
65
  DEPENDENCIES
56
- awesome_print
57
66
  bundler (>= 1.0.0)
58
67
  guignol!
68
+ pry
69
+ pry-nav
59
70
  rake
60
71
  rspec (~> 2.4.0)
data/LICENCE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2012, HouseTrip SA.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ The views and conclusions contained in the software and documentation are those
25
+ of the authors and should not be interpreted as representing official policies,
26
+ either expressed or implied, of the authors.
data/README.md CHANGED
@@ -5,9 +5,23 @@ Be the puppeteer. Order your EC2 instances to start, stop, die, or be created fr
5
5
  Released under the [Simplified BSD License](http://en.wikipedia.org/wiki/BSD_licenses#2-clause_license_.28.22Simplified_BSD_License.22_or_.22FreeBSD_License.22.29).
6
6
 
7
7
 
8
+ Guignol -- manipulate EC2 instances from your command line.
9
+
10
+ Tasks:
11
+ guignol clone SOURCE # Print a new config similar to the server named SOURCE
12
+ guignol create PATTERNS # Create and start all instances matching PATTERNS and their volumes
13
+ guignol execute COMMAND # Execute a command over SSH on instances
14
+ guignol fixdns [PATTERNS] # Make sure the DNS mappings are correct for servers matching PATTERNS
15
+ guignol help [TASK] # Describe available tasks or one specific task
16
+ guignol kill PATTERNS # Terminate all instances matching PATTERNS
17
+ guignol list [PATTERNS] # List the status of all known instances
18
+ guignol start PATTERNS # Start all instances matching PATTERNS, attach their volumes, and setup DNS records
19
+ guignol stop PATTERNS # Stop all instances matching PATTERNS, and remove DNS records
20
+ guignol uuid [COUNT] # Print random UUIDs
21
+
8
22
  ## Getting started
9
23
 
10
- Install `guignol`:
24
+ Install Guignol:
11
25
 
12
26
  $ gem install guignol
13
27
 
@@ -19,13 +33,13 @@ Start by setting up your `~/.fog`:
19
33
  :aws_access_key_id: ABCDEF....
20
34
  :aws_secret_access_key: 123456....
21
35
 
36
+ Alternatively you can pass crendentials for Guignol by setting the `AWS_SECRET_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables, or by setting `:aws_access_key_id` and `:aws_secret_access_key` in `guignol.yml` (see below).
22
37
 
23
38
 
24
39
  ## Creating, starting and stopping machines
25
40
 
26
41
  Guignol doesn't care about the list of instances that live on your EC2 account,
27
42
  only what it's configured to deal with.
28
-
29
43
  This should prevent destroying other's instances when using (for instance) a
30
44
  shared AWS/IAM account !
31
45
 
@@ -35,10 +49,13 @@ instance):
35
49
 
36
50
  # ~/.guignol.yml
37
51
  ---
38
- - :name: hello-world
52
+ hello-world:
39
53
  :uuid: AF123799-3F55-4F0B-8E58-87C67A5977BA
40
54
 
41
- Guignol will read it's configuration from `./config/guignol.yml`, `./guignol.yml`, or `~/.guignol.yml`.
55
+ Guignol will read its configuration from the first file in the following list:
56
+
57
+ - the value of the `GUIGNOL_YML` environment variable,
58
+ - `./guignol.yml`, `./config/guignol.yml`, `~/.guignol.yml`
42
59
 
43
60
  `guignol uuid` will output a new UUID if you need one.
44
61
  You can also use `uuidgen` if your distro come with it.
@@ -59,7 +76,7 @@ You can log in as soon as the command returns.
59
76
  Of course, you can `stop`, `start`, or `kill` your instance.
60
77
 
61
78
 
62
- ### One more thing
79
+ ### Parallel actions
63
80
 
64
81
  You can run any command against multiple instances by listing names and (ruby)
65
82
  regular expressions to designate lists of instances.
@@ -72,9 +89,34 @@ regular expressions to designate lists of instances.
72
89
 
73
90
  If targeting multiple machines, guignol will run **in parallel**.
74
91
 
92
+ Parallelism is disabled on Ruby 1.9.3+, as it doesn't deal well with SSL connections in that case.
93
+
94
+
95
+ ### EBS Volumes
96
+
97
+ Yes, Guignol will also create and attach your EBS volumes when starting up instances.
98
+ Just add a `:volumes` entry to your instance configuration:
99
+
100
+ :volumes:
101
+ - :name: fubar-swap
102
+ :uuid: 9D5A278E-432C-41DB-9FB5-8AF5C1BD021F
103
+ :dev: /dev/sdf
104
+ :size: 4
105
+ :delete_on_termination: true
106
+ - :name: fubar-data
107
+ :uuid: E180203F-9DE1-4C6A-B09B-33B2FAC8F36E
108
+ :dev: /dev/sdg
109
+ :size: 20
110
+ :delete_on_termination: false
111
+
112
+ Guignol will take care of creating your instances in the right availability zone if its volumes already exist.
113
+
114
+ Note that Guignol does not delete volumes when tearing down instances.
115
+
116
+
75
117
 
76
118
 
77
- ## Optional configuration
119
+ ## Optional instance configuration
78
120
 
79
121
  - `:domain`
80
122
  The machine's domain name. If specified, Guignol will setup a
@@ -96,31 +138,14 @@ If targeting multiple machines, guignol will run **in parallel**.
96
138
  - `:security_group_ids`
97
139
  A list of security groups you want your instance to be a member of.
98
140
 
141
+ - `:volumes`
142
+ A list of EBS volumes to be created if necessary, and attached to the instance.
143
+
99
144
  - `:user_data`
100
145
  A script to run when an instance is created.
101
146
 
102
-
103
-
104
- ## EBS Volumes
105
-
106
- Yes, Guignol will also create and attach your EBS volumes when starting up instances.
107
- Just add a `:volumes` entry to your instance configuration:
108
-
109
- :volumes:
110
- - :name: fubar-swap
111
- :uuid: 9D5A278E-432C-41DB-9FB5-8AF5C1BD021F
112
- :dev: /dev/sdf
113
- :size: 4
114
- :delete_on_termination: true
115
- - :name: fubar-data
116
- :uuid: E180203F-9DE1-4C6A-B09B-33B2FAC8F36E
117
- :dev: /dev/sdg
118
- :size: 20
119
- :delete_on_termination: false
120
-
121
- Guignol will take care of creating your instances in the right availability zone if its volumes already exist.
122
-
123
- Note that Guignol does not delete volumes when tearing down instances.
147
+ - `:username`
148
+ Will be used for the SSH connection performed by the `execute` command.
124
149
 
125
150
 
126
151
 
@@ -138,31 +163,35 @@ Simply
138
163
  Now you can `guignol stop` your instances from the command line when not using them and save money.
139
164
 
140
165
 
166
+ ## Logging
167
+
168
+ Guignol will log to standard output if it's a TTY, and be mostly silent otherwise.
169
+ You can direct logging to a file of your choice by setting `GUIGNOL_LOG`.
170
+
171
+
141
172
 
142
173
  ## Complete config example
143
174
 
144
- This one just contains 2 machines, `fubar.housetripdev.com.` and `gargantua.hosuetirpdev.com.`
175
+ This one just contains 1 machine, `fubar.example.com.`
145
176
 
146
177
  # ~/.guignol.yml
147
178
  ---
148
- - :name: hello-world
149
- :uuid: AF123799-3F55-4F0B-8E58-87C67A5977BA
150
- - :name: fubar
151
- :domain: housetripdev.com.
179
+ fubar:
180
+ :domain: example.com.
152
181
  :uuid: 68C3C0C2-1BA3-465F-8626-E065E4EF9048
153
182
  :region: eu-west-1
154
- :image_id: ami-15f7c961 # 32 bits
183
+ :image_id: ami-15f7c961
155
184
  :flavor_id: m1.small
156
- :key_name: jtl-laptop
185
+ :key_name: john-doe
157
186
  :security_group_ids:
158
- - sg-7e638109 # housetrip-basic
187
+ - sg-7e638abc
159
188
  :volumes:
160
- - :name: fubar-swap
189
+ fubar-swap:
161
190
  :uuid: 9D5A278E-432C-41DB-9FB5-8AF5C1BD021F
162
191
  :dev: /dev/sdf
163
192
  :size: 4
164
193
  :delete_on_termination: true
165
- - :name: fubar-data
194
+ fubar-data:
166
195
  :uuid: E180203F-9DE1-4C6A-B09B-33B2FAC8F36E
167
196
  :dev: /dev/sdg
168
197
  :size: 20
data/Rakefile CHANGED
@@ -1,29 +1,3 @@
1
- # Copyright (c) 2012, HouseTrip SA.
2
- # All rights reserved.
3
- #
4
- # Redistribution and use in source and binary forms, with or without
5
- # modification, are permitted provided that the following conditions are met:
6
- #
7
- # 1. Redistributions of source code must retain the above copyright notice, this
8
- # list of conditions and the following disclaimer.
9
- # 2. Redistributions in binary form must reproduce the above copyright notice,
10
- # this list of conditions and the following disclaimer in the documentation
11
- # and/or other materials provided with the distribution.
12
- #
13
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
- # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
- # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
- #
24
- # The views and conclusions contained in the software and documentation are those
25
- # of the authors and should not be interpreted as representing official policies,
26
- # either expressed or implied, of the authors.
27
1
 
28
2
  require 'bundler'
29
3
  require 'rspec/core/rake_task'
@@ -2,42 +2,12 @@
2
2
  #
3
3
  # Run Guignol commands
4
4
  #
5
- # Copyright (c) 2012, HouseTrip SA.
6
- # All rights reserved.
7
- #
8
- # Redistribution and use in source and binary forms, with or without
9
- # modification, are permitted provided that the following conditions are met:
10
- #
11
- # 1. Redistributions of source code must retain the above copyright notice, this
12
- # list of conditions and the following disclaimer.
13
- # 2. Redistributions in binary form must reproduce the above copyright notice,
14
- # this list of conditions and the following disclaimer in the documentation
15
- # and/or other materials provided with the distribution.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
- # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21
- # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
- #
28
- # The views and conclusions contained in the software and documentation are those
29
- # of the authors and should not be interpreted as representing official policies,
30
- # either expressed or implied, of the authors.
31
-
32
5
  File.expand_path(__FILE__ + '/../../lib').tap { |libdir| $:.include?(libdir) or $:.unshift(libdir) }
33
6
 
7
+ require 'excon'
8
+ require 'guignol/shell'
34
9
 
35
10
  # Work around SSL certificate errors. Yeah, I know it's insecure.
36
- require 'excon'
37
11
  Excon.defaults[:ssl_verify_peer] = false
38
12
 
39
-
40
- require 'guignol/shell'
41
-
42
- command = ARGV.shift
43
- Guignol::Shell.instance.execute(command, *ARGV)
13
+ Guignol::Shell.start
@@ -1,31 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # Copyright (c) 2012, HouseTrip SA.
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # 1. Redistributions of source code must retain the above copyright notice, this
9
- # list of conditions and the following disclaimer.
10
- # 2. Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- #
14
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
- # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18
- # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
- #
25
- # The views and conclusions contained in the software and documentation are those
26
- # of the authors and should not be interpreted as representing official policies,
27
- # either expressed or implied, of the authors.
28
-
29
2
  require File.expand_path("../lib/guignol/version", __FILE__)
30
3
 
31
4
  Gem::Specification.new do |s|
@@ -46,13 +19,14 @@ Gem::Specification.new do |s|
46
19
  s.add_development_dependency "bundler", ">= 1.0.0"
47
20
  s.add_development_dependency "rspec", "~> 2.4.0"
48
21
  s.add_development_dependency "rake"
49
- s.add_development_dependency "awesome_print"
22
+ s.add_development_dependency "pry"
23
+ s.add_development_dependency "pry-nav"
50
24
 
51
- s.add_dependency "fog", "~> 1.1.2"
25
+ s.add_dependency "fog", "~> 1.6.0"
52
26
  s.add_dependency "parallel", "~> 0.5.14"
53
27
  s.add_dependency "active_support"
54
- s.add_dependency "term-ansicolor"
55
28
  s.add_dependency "uuidtools"
29
+ s.add_dependency "thor"
56
30
 
57
31
  s.files = `git ls-files`.split("\n")
58
32
  s.test_files = `git ls-files -- spec/*`.split("\n")
@@ -0,0 +1,6 @@
1
+
2
+ Array.class_eval do
3
+ def collect_key(key)
4
+ collect { |item| item.kind_of?(Hash) ? item[key] : nil }
5
+ end
6
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module Hash::MapToHash
3
+ # Update all values in a hash.
4
+ # Passes +key,value+ pairs to its block, replaces the pair
5
+ # with the block's return value.
6
+ def map_to_hash(options = {}, &block)
7
+ Hash.new.tap do |result|
8
+ self.each_pair do |key,value|
9
+ value = _deep_convert(value, block) if options[:deep]
10
+ new_key, new_value = block.call(key, value)
11
+ result[new_key] = new_value
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def _deep_convert(value, block)
19
+ case value
20
+ when Hash
21
+ value.map_to_hash({ :deep => true }, &block)
22
+ when Array
23
+ value.map { |item| _deep_convert(item, block) }
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+ Hash.send(:include, self)
30
+ end
31
+