foreplay 0.7.6 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreplay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xenapto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-22 00:00:00.000000000 Z
11
+ date: 2015-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.2'
27
- - !ruby/object:Gem::Dependency
28
- name: colorize
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0.7'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0.7'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: foreman
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,134 +56,128 @@ dependencies:
70
56
  name: bundler
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - ">="
59
+ - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: '1.6'
61
+ version: '1.9'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - ">="
66
+ - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: '1.6'
68
+ version: '1.9'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rake
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
- - - ">="
73
+ - - "~>"
88
74
  - !ruby/object:Gem::Version
89
- version: '10.3'
75
+ version: '10.4'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - ">="
80
+ - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: '10.3'
82
+ version: '10.4'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rspec
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - ">="
87
+ - - "~>"
102
88
  - !ruby/object:Gem::Version
103
89
  version: '3.2'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - ">="
94
+ - - "~>"
109
95
  - !ruby/object:Gem::Version
110
96
  version: '3.2'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: cucumber
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - ">="
101
+ - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: '1.3'
103
+ version: '2.0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - ">="
108
+ - - "~>"
123
109
  - !ruby/object:Gem::Version
124
- version: '1.3'
110
+ version: '2.0'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: aruba
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
- - - ">="
115
+ - - "~>"
130
116
  - !ruby/object:Gem::Version
131
- version: '0.5'
117
+ version: '0.6'
132
118
  type: :development
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
- - - ">="
122
+ - - "~>"
137
123
  - !ruby/object:Gem::Version
138
- version: '0.5'
124
+ version: '0.6'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: gem-release
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
- - - ">="
129
+ - - "~>"
144
130
  - !ruby/object:Gem::Version
145
131
  version: '0.7'
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
- - - ">="
136
+ - - "~>"
151
137
  - !ruby/object:Gem::Version
152
138
  version: '0.7'
153
139
  - !ruby/object:Gem::Dependency
154
140
  name: simplecov
155
141
  requirement: !ruby/object:Gem::Requirement
156
142
  requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0.7'
160
- - - ">="
143
+ - - "~>"
161
144
  - !ruby/object:Gem::Version
162
- version: 0.7.1
145
+ version: '0.10'
163
146
  type: :development
164
147
  prerelease: false
165
148
  version_requirements: !ruby/object:Gem::Requirement
166
149
  requirements:
167
- - - ">="
168
- - !ruby/object:Gem::Version
169
- version: '0.7'
170
- - - ">="
150
+ - - "~>"
171
151
  - !ruby/object:Gem::Version
172
- version: 0.7.1
152
+ version: '0.10'
173
153
  - !ruby/object:Gem::Dependency
174
154
  name: coveralls
175
155
  requirement: !ruby/object:Gem::Requirement
176
156
  requirements:
177
- - - ">="
157
+ - - "~>"
178
158
  - !ruby/object:Gem::Version
179
- version: '0.7'
159
+ version: '0.8'
180
160
  type: :development
181
161
  prerelease: false
182
162
  version_requirements: !ruby/object:Gem::Requirement
183
163
  requirements:
184
- - - ">="
164
+ - - "~>"
185
165
  - !ruby/object:Gem::Version
186
- version: '0.7'
166
+ version: '0.8'
187
167
  - !ruby/object:Gem::Dependency
188
168
  name: rubocop
189
169
  requirement: !ruby/object:Gem::Requirement
190
170
  requirements:
191
- - - ">"
171
+ - - "~>"
192
172
  - !ruby/object:Gem::Version
193
- version: '0.29'
173
+ version: '0.30'
194
174
  type: :development
195
175
  prerelease: false
196
176
  version_requirements: !ruby/object:Gem::Requirement
197
177
  requirements:
198
- - - ">"
178
+ - - "~>"
199
179
  - !ruby/object:Gem::Version
200
- version: '0.29'
180
+ version: '0.30'
201
181
  description: Deploying Rails projects to Ubuntu using Foreman
202
182
  email:
203
183
  - developers@xenapto.com
@@ -223,14 +203,21 @@ files:
223
203
  - foreplay.rake
224
204
  - lib/foreplay.rb
225
205
  - lib/foreplay/cli.rb
226
- - lib/foreplay/deploy.rb
206
+ - lib/foreplay/engine.rb
207
+ - lib/foreplay/engine/remote.rb
208
+ - lib/foreplay/engine/role.rb
209
+ - lib/foreplay/engine/server.rb
210
+ - lib/foreplay/engine/step.rb
211
+ - lib/foreplay/engine/steps.yml
212
+ - lib/foreplay/foreplay.rb
213
+ - lib/foreplay/launcher.rb
227
214
  - lib/foreplay/setup.rb
228
215
  - lib/foreplay/setup/foreplay.yml
229
- - lib/foreplay/utility.rb
230
216
  - lib/foreplay/version.rb
217
+ - lib/string.rb
231
218
  - spec/lib/foreplay/deploy_spec.rb
219
+ - spec/lib/foreplay/engine_spec.rb
232
220
  - spec/lib/foreplay/setup_spec.rb
233
- - spec/lib/foreplay/utility_spec.rb
234
221
  - spec/spec_helper.rb
235
222
  homepage: https://github.com/Xenapto/foreplay
236
223
  licenses:
@@ -252,16 +239,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
252
239
  version: '0'
253
240
  requirements: []
254
241
  rubyforge_project:
255
- rubygems_version: 2.4.5
242
+ rubygems_version: 2.4.6
256
243
  signing_key:
257
244
  specification_version: 4
258
- summary: 'Example: foreplay push to production'
245
+ summary: 'Example: foreplay deploy production'
259
246
  test_files:
260
247
  - features/check.feature
261
248
  - features/deploy.feature
262
249
  - features/setup.feature
263
250
  - features/support/env.rb
264
251
  - spec/lib/foreplay/deploy_spec.rb
252
+ - spec/lib/foreplay/engine_spec.rb
265
253
  - spec/lib/foreplay/setup_spec.rb
266
- - spec/lib/foreplay/utility_spec.rb
267
254
  - spec/spec_helper.rb
@@ -1,395 +0,0 @@
1
- require 'thor/group'
2
- require 'yaml'
3
- require 'net/ssh'
4
- require 'net/ssh/shell'
5
- require 'active_support/inflector'
6
- require 'active_support/core_ext/object'
7
- require 'active_support/core_ext/hash'
8
- require 'colorize'
9
- require 'foreplay/utility'
10
-
11
- module Foreplay
12
- class Deploy < Thor::Group
13
- include Thor::Actions
14
-
15
- argument :mode, type: :string, required: true
16
- argument :environment, type: :string, required: true
17
- argument :filters, type: :hash, required: false
18
-
19
- DEFAULTS_KEY = 'defaults'
20
- INDENT = "\t"
21
-
22
- def parse
23
- # Explain what we're going to do
24
- message = "#{mode.capitalize}ing #{environment.dup.yellow} environment, "
25
- message += "#{explanatory_text(filters, 'role')}, #{explanatory_text(filters, 'server')}"
26
- puts message
27
-
28
- config_file = "#{Dir.getwd}/config/foreplay.yml"
29
-
30
- begin
31
- config_yml = File.read config_file
32
- rescue Errno::ENOENT
33
- terminate "Can't find configuration file #{config_file}.\nPlease run foreplay setup or create the file manually."
34
- end
35
-
36
- config_all = YAML.load(config_yml)
37
- config_env = config_all[environment] || {}
38
-
39
- # This environment
40
- unless config_all.key? environment
41
- terminate("No deployment configuration defined for #{environment} environment.\nCheck #{config_file}")
42
- end
43
-
44
- # Servers asked for
45
- server_filter = filters['server'].split(',') if filters.key?('server')
46
-
47
- # Establish defaults
48
- # First the default defaults
49
- defaults = {
50
- name: File.basename(Dir.getwd),
51
- environment: environment,
52
- env: { 'RAILS_ENV' => environment },
53
- port: 50_000
54
- }
55
-
56
- defaults = Foreplay::Utility.supermerge(defaults, config_all[DEFAULTS_KEY]) if config_all.key? DEFAULTS_KEY
57
- defaults = Foreplay::Utility.supermerge(defaults, config_env[DEFAULTS_KEY]) if config_env.key? DEFAULTS_KEY
58
- threads = []
59
-
60
- config_env.each do |role, additional_instructions|
61
- next if role == DEFAULTS_KEY # 'defaults' is not a role
62
- # Only deploy to the role we've specified (or all roles if none is specified)
63
- next if filters.key?('role') && filters['role'] != role
64
-
65
- instructions = Foreplay::Utility.supermerge(defaults, additional_instructions).symbolize_keys
66
- instructions[:role] = role
67
- required_keys = [:name, :environment, :role, :servers, :path, :repository]
68
-
69
- required_keys.each do |key|
70
- next if instructions.key? key
71
- terminate("Required key #{key} not found in instructions for #{environment} environment.\nCheck #{config_file}")
72
- end
73
-
74
- # Apply server filter
75
- instructions[:servers] &= server_filter if server_filter
76
-
77
- threads.concat deploy_role(instructions)
78
- end
79
-
80
- threads.each(&:join)
81
-
82
- puts mode == :deploy ? 'Finished deployment' : 'Deployment configuration check was successful'
83
- end
84
-
85
- private
86
-
87
- def deploy_role(instructions)
88
- servers = instructions[:servers]
89
- threads = []
90
- preposition = mode == :deploy ? 'to' : 'for'
91
-
92
- if servers.length > 1
93
- message = "#{mode.capitalize}ing #{instructions[:name].yellow} #{preposition} #{servers.join(', ').yellow} for the "
94
- message += "#{instructions[:role].dup.yellow} role in the #{environment.dup.yellow} environment..."
95
- puts message
96
- end
97
-
98
- servers.each { |server| threads << Thread.new { deploy_to_server server, instructions } }
99
- threads
100
- end
101
-
102
- def deploy_to_server(server_port, instructions)
103
- server, _ = server_port.split(':') # Parse server + port
104
- name = instructions[:name]
105
- role = instructions[:role]
106
- path = instructions[:path]
107
- repository = instructions[:repository]
108
- branch = instructions[:branch] || 'master'
109
- user = instructions[:user]
110
- port = instructions[:port]
111
- preposition = mode == :deploy ? 'to' : 'for'
112
-
113
- message = "#{mode.capitalize}ing #{name.yellow} #{preposition} #{server.yellow} "
114
- message += "for the #{role.dup.yellow} role in the #{environment.dup.yellow} environment"
115
- puts message
116
-
117
- # Substitute variables in the path
118
- path.gsub! '%u', user
119
- path.gsub! '%a', name
120
-
121
- # Find out which port we're currently running on
122
- current_port_file = ".foreplay/#{name}/current_port"
123
- steps = [{ command: "mkdir -p .foreplay/#{name} && touch #{current_port_file} && cat #{current_port_file}", silent: true }]
124
-
125
- current_port_string = execute_on_server(server_port, steps, instructions).strip!
126
-
127
- if current_port_string.blank?
128
- puts "#{server}#{INDENT}No instance is currently deployed"
129
- else
130
- "#{server}#{INDENT}Current instance is using port #{current_port_string}"
131
- end
132
-
133
- current_port = current_port_string.to_i
134
-
135
- # Switch ports
136
- if current_port == port
137
- current_port = port + 1000
138
- former_port = port
139
- else
140
- current_port = port
141
- former_port = port + 1000
142
- end
143
-
144
- # Contents of .foreman file
145
- current_service = "#{name}-#{current_port}"
146
- former_service = "#{name}-#{former_port}"
147
-
148
- instructions[:foreman]['app'] = current_service
149
- instructions[:foreman]['port'] = current_port
150
- instructions[:foreman]['user'] = user
151
- instructions[:foreman]['log'] = "$HOME/#{path}/#{current_port}/log"
152
-
153
- # Contents of .env file
154
- instructions[:env]['HOME'] = '$HOME'
155
- instructions[:env]['SHELL'] = '$SHELL'
156
- instructions[:env]['PATH'] = '$PATH:`which bundle`'
157
-
158
- # Commands to execute on remote server
159
- steps = [
160
- { command: "echo Foreplay version #{VERSION}",
161
- commentary: "Foreplay running from #{`hostname -f`}#{`which foreman`}" },
162
- { command: "mkdir -p #{path} && cd #{path} && rm -rf #{current_port} "\
163
- "&& git clone -b #{branch} #{repository} #{current_port}",
164
- commentary: "Cloning #{branch} branch of repository #{repository}" },
165
- { command: "rvm rvmrc trust #{current_port}",
166
- commentary: 'Trusting the .rvmrc file for the new instance' },
167
- { command: "rvm rvmrc warning ignore #{current_port}",
168
- commentary: 'Ignoring the .rvmrc warning for the new instance' },
169
- { command: 'gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys D39DC0E3',
170
- commentary: "Trusting RVM's public key",
171
- ignore_error: true },
172
- { command: "cd #{current_port} && mkdir -p tmp doc log config",
173
- commentary: 'If you have a .rvmrc file there may be a delay now while we install a new ruby' },
174
- { command: 'if [ -f .ruby-version ] ; then rvm install `cat .ruby-version` ; '\
175
- 'else echo "No .ruby-version file found" ; fi',
176
- commentary: 'If you have a .ruby-version file there may be a delay now while we install a new ruby' },
177
- { key: :env,
178
- delimiter: '=',
179
- prefix: '.',
180
- commentary: 'Building .env' },
181
- { key: :foreman,
182
- delimiter: ': ',
183
- prefix: '.',
184
- commentary: 'Building .foreman' },
185
- { key: :database,
186
- delimiter: ': ',
187
- suffix: '.yml',
188
- commentary: 'Building config/database.yml',
189
- before: ' ',
190
- header: "#{environment}:",
191
- path: 'config/' },
192
- { key: :resque,
193
- delimiter: ': ',
194
- suffix: '.yml',
195
- commentary: 'Building config/resque.yml',
196
- before: environment,
197
- path: 'config/' },
198
- { command: 'if [ -d ../cache/vendor/bundle/bundle ] ; then rm -rf ../cache/vendor/bundle/bundle'\
199
- ' ; else echo No evidence of legacy copy bug ; fi',
200
- commentary: 'Fixing legacy copy bug' },
201
- { command: 'if [ -d ../cache/vendor/bundle ] ; then rsync -aW --no-compress --delete --info=STATS3'\
202
- ' ../cache/vendor/bundle/ vendor/bundle ; else echo No bundle to restore ; fi',
203
- commentary: 'Attempting to restore bundle from cache' },
204
- { command: 'sudo ln -f `which bundle` /usr/bin/bundle || echo Using default version of bundle',
205
- commentary: 'Setting the current version of bundle to be the default' },
206
- { command: 'bundle install --deployment --clean --jobs 2 --without development test',
207
- commentary: 'Using bundler to install the required gems in deployment mode' },
208
- { command: 'mkdir -p ../cache/vendor && rsync -aW --no-compress --delete --info=STATS1'\
209
- ' vendor/bundle/ ../cache/vendor/bundle',
210
- commentary: 'Caching bundle' },
211
- { command: 'if [ -f public/assets/manifest.yml ] ; then echo "Not precompiling assets"'\
212
- " ; else RAILS_ENV=#{environment} bundle exec foreman run rake assets:precompile ; fi",
213
- commentary: 'Precompiling assets unless they were supplied' },
214
- { command: 'sudo bundle exec foreman export upstart /etc/init',
215
- commentary: "Converting #{current_service} to an upstart service" },
216
- { command: "sudo start #{current_service} || sudo restart #{current_service}",
217
- commentary: 'Starting the service',
218
- ignore_error: true },
219
- { command: "echo #{current_port} > $HOME/#{current_port_file}",
220
- commentary: "Setting the port for the new instance to #{current_port}" },
221
- { command: 'sleep 60',
222
- commentary: 'Waiting 60s to give service time to start' },
223
- { command: "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port}",
224
- commentary: "Adding firewall rule to direct incoming traffic on port 80 to port #{current_port}" },
225
- { command: "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port}",
226
- commentary: "Removing previous firewall rule directing traffic to port #{former_port}",
227
- ignore_error: true },
228
- { command: 'sudo iptables-save > /etc/iptables/rules.v4',
229
- commentary: 'Attempting to save firewall rules to /etc/iptables/rules.v4',
230
- ignore_error: true },
231
- { command: 'sudo iptables-save > /etc/iptables.up.rules',
232
- commentary: 'Attempting to save firewall rules to /etc/iptables.up.rules',
233
- ignore_error: true },
234
- { command: 'sudo iptables-save -c | egrep REDIRECT --color=never',
235
- ignore_error: true,
236
- commentary: 'Current firewall NAT configuration:' },
237
- { command: "sudo stop #{former_service} || echo 'No previous instance running'",
238
- commentary: 'Stopping the previous instance',
239
- ignore_error: true }
240
- ]
241
-
242
- execute_on_server server_port, steps, instructions
243
- end
244
-
245
- def execute_on_server(server_port, steps, instructions)
246
- user = instructions[:user]
247
- password = instructions[:password]
248
- keyfile = instructions[:keyfile]
249
- private_key = instructions[:private_key]
250
-
251
- keyfile.sub! '~', ENV['HOME'] || '/' unless keyfile.blank? # Remote shell won't expand this for us
252
-
253
- server, port = server_port.split(':') # Parse server + port
254
- port ||= 22
255
-
256
- # SSH authentication methods
257
- options = { verbose: :warn, port: port }
258
-
259
- if password.blank?
260
- # If there's no password we must supply a private key
261
- if private_key.blank?
262
- message = 'No authentication methods supplied. You must supply a private key, key file or password in the configuration file'
263
- terminate(message) if keyfile.blank?
264
- # Get the key from the key file
265
- puts "#{INDENT}Using private key from #{keyfile}"
266
- private_key = File.read keyfile
267
- else
268
- puts "#{INDENT}Using private key from the configuration file"
269
- end
270
-
271
- options[:key_data] = [private_key]
272
- else
273
- # Use the password supplied
274
- options[:password] = password
275
- end
276
-
277
- # Capture output of last command to return to the calling routine
278
- output = ''
279
-
280
- if mode == :deploy
281
- puts "#{server}#{INDENT}Connecting to #{server} on port #{port}"
282
-
283
- # SSH connection
284
- session = start_session(server, server_port, user, options)
285
-
286
- puts "#{server}#{INDENT}Successfully connected to #{server} on port #{port}"
287
-
288
- session.shell do |sh|
289
- steps.each do |step|
290
- # Output from this step
291
- output = ''
292
- previous = '' # We don't need or want the final CRLF
293
- commands = build_step server, step, instructions
294
-
295
- commands.each do |command|
296
- process = sh.execute command
297
-
298
- process.on_output do |_, o|
299
- previous = o
300
- output += previous
301
- end
302
-
303
- sh.wait!
304
-
305
- if step[:ignore_error] == true || process.exit_status == 0
306
- print output.gsub!(/^/, "#{server}#{INDENT * 2}") unless step[:silent] == true || output.blank?
307
- else
308
- terminate(output)
309
- end
310
- end
311
- end
312
- end
313
-
314
- session.close
315
- else
316
- # Deployment check: just say what we would have done
317
- puts "#{server}#{INDENT * 2}Connection options:"
318
- options.each { |k, v| puts "#{server}#{INDENT * 2}#{k}: #{v}" }
319
-
320
- puts "#{server}#{INDENT * 2}"
321
- puts "#{server}#{INDENT * 2}Deployment instructions:"
322
- steps.each do |step|
323
- commands = build_step server, step, instructions
324
- commands.each { |command| puts "#{server}#{INDENT * 2}#{command}" unless step[:silent] }
325
- end
326
- end
327
-
328
- output
329
- end
330
-
331
- def start_session(server, server_port, user, options)
332
- Net::SSH.start(server, user, options)
333
- rescue SocketError => e
334
- terminate "#{server}#{INDENT}There was a problem starting an ssh session on #{server_port}:\n#{e.message}"
335
- end
336
-
337
- def build_step(server, step, instructions)
338
- puts "#{server}#{INDENT}#{(step[:commentary] || step[:command]).yellow}" unless step[:silent] == true
339
-
340
- # Each step can be (1) a command or (2) a series of values to add to a file
341
- if step.key?(:key)
342
- if instructions.key?(step[:key])
343
- build_commands step, instructions
344
- else
345
- []
346
- end
347
- else
348
- # ...or just execute the command specified
349
- [step[:command]]
350
- end
351
- end
352
-
353
- def build_commands(step, instructions)
354
- # Add values from the config file to a file on the remote machine
355
- key = step[:key]
356
- prefix = step[:prefix] || ''
357
- suffix = step[:suffix] || ''
358
- path = step[:path] || ''
359
- before = step[:before] || ''
360
- delimiter = step[:delimiter] || ''
361
- after = step[:after] || ''
362
-
363
- step[:silent] = true
364
- filename = "#{path}#{prefix}#{key}#{suffix}"
365
-
366
- if step.key?(:header)
367
- commands = ["echo \"#{step[:header]}\" > #{filename}"]
368
- redirect = '>>'
369
- else
370
- commands = []
371
- redirect = '>'
372
- end
373
-
374
- if instructions[key].is_a? Hash
375
- instructions[key].each do |k, v|
376
- commands << "echo \"#{before}#{k}#{delimiter}#{v}#{after}\" #{redirect} #{filename}"
377
- redirect = '>>'
378
- end
379
- else
380
- commands << "echo \"#{before}#{delimiter}#{instructions[key]}#{after}\" #{redirect} #{filename}"
381
- redirect = '>>'
382
- end
383
-
384
- commands
385
- end
386
-
387
- def explanatory_text(hsh, key)
388
- hsh.key?(key) ? "#{hsh[key].dup.yellow} #{key}" : "all #{key.pluralize}"
389
- end
390
-
391
- def terminate(message)
392
- fail message
393
- end
394
- end
395
- end
@@ -1,30 +0,0 @@
1
- module Foreplay
2
- class Utility
3
- # Returns a new hash with +hash+ and +other_hash+ merged recursively, including arrays.
4
- #
5
- # h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
6
- # h2 = { x: { y: [7,8,9] }, z: 'xyz' }
7
- # h1.supermerge(h2)
8
- # #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
9
- def self.supermerge(hash, other_hash)
10
- fail 'supermerge only works if you pass two hashes. '\
11
- "You passed a #{hash.class} and a #{other_hash.class}." unless hash.is_a?(Hash) && other_hash.is_a?(Hash)
12
-
13
- new_hash = hash.deep_dup.with_indifferent_access
14
-
15
- other_hash.each_pair do |k, v|
16
- tv = new_hash[k]
17
-
18
- if tv.is_a?(Hash) && v.is_a?(Hash)
19
- new_hash[k] = Foreplay::Utility.supermerge(tv, v)
20
- elsif tv.is_a?(Array) || v.is_a?(Array)
21
- new_hash[k] = Array.wrap(tv) + Array.wrap(v)
22
- else
23
- new_hash[k] = v
24
- end
25
- end
26
-
27
- new_hash
28
- end
29
- end
30
- end