foreplay 0.7.6 → 0.8.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.
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