beaker 2.18.3 → 2.19.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 +8 -8
- data/HISTORY.md +439 -2
- data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
- data/acceptance/pre_suite/puppet_git/install.rb +6 -65
- data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
- data/beaker.gemspec +2 -0
- data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
- data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -2
- data/lib/beaker/dsl/install_utils/foss_utils.rb +97 -60
- data/lib/beaker/dsl/install_utils/pe_utils.rb +30 -53
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
- data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
- data/lib/beaker/dsl/roles.rb +20 -3
- data/lib/beaker/dsl/structure.rb +14 -3
- data/lib/beaker/host.rb +24 -3
- data/lib/beaker/host/unix/pkg.rb +9 -0
- data/lib/beaker/host/windows/exec.rb +3 -0
- data/lib/beaker/host_prebuilt_steps.rb +5 -9
- data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
- data/lib/beaker/hypervisor/docker.rb +7 -0
- data/lib/beaker/hypervisor/vmpooler.rb +4 -0
- data/lib/beaker/logger.rb +12 -1
- data/lib/beaker/options/command_line_parser.rb +9 -0
- data/lib/beaker/options/options_hash.rb +3 -296
- data/lib/beaker/options/parser.rb +12 -0
- data/lib/beaker/options/presets.rb +0 -1
- data/lib/beaker/ssh_connection.rb +48 -23
- data/lib/beaker/test_case.rb +1 -1
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
- data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +194 -49
- data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +112 -22
- data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
- data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
- data/spec/beaker/dsl/roles_spec.rb +36 -5
- data/spec/beaker/dsl/structure_spec.rb +9 -2
- data/spec/beaker/host/unix/pkg_spec.rb +26 -6
- data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
- data/spec/beaker/host_spec.rb +18 -0
- data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
- data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
- data/spec/beaker/hypervisor/docker_spec.rb +2 -1
- data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
- data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
- data/spec/beaker/logger_spec.rb +39 -0
- data/spec/beaker/options/command_line_parser_spec.rb +2 -2
- data/spec/beaker/options/options_hash_spec.rb +1 -102
- data/spec/beaker/options/parser_spec.rb +19 -0
- data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
- data/spec/beaker/options/presets_spec.rb +8 -0
- data/spec/beaker/ssh_connection_spec.rb +39 -21
- data/spec/helpers.rb +9 -3
- data/spec/mocks.rb +2 -0
- metadata +34 -11
- data/lib/beaker/answers.rb +0 -143
- data/lib/beaker/answers/version20.rb +0 -120
- data/lib/beaker/answers/version28.rb +0 -121
- data/lib/beaker/answers/version30.rb +0 -227
- data/lib/beaker/answers/version32.rb +0 -44
- data/lib/beaker/answers/version34.rb +0 -51
- data/lib/beaker/answers/version38.rb +0 -29
- data/lib/beaker/answers/version40.rb +0 -44
- data/spec/beaker/answers_spec.rb +0 -547
data/lib/beaker/host.rb
CHANGED
@@ -214,8 +214,9 @@ module Beaker
|
|
214
214
|
end
|
215
215
|
|
216
216
|
#Return the ip address of this host
|
217
|
+
#Always pull fresh, because this can sometimes change
|
217
218
|
def ip
|
218
|
-
self[
|
219
|
+
self['ip'] = get_ip
|
219
220
|
end
|
220
221
|
|
221
222
|
#@return [Boolean] true if x86_64, false otherwise
|
@@ -224,13 +225,29 @@ module Beaker
|
|
224
225
|
end
|
225
226
|
|
226
227
|
def connection
|
227
|
-
|
228
|
+
# create new connection object if necessary
|
229
|
+
@connection ||= SshConnection.connect( { :ip => self['ip'], :vmhostname => self['vmhostname'], :hostname => @name },
|
228
230
|
self['user'],
|
229
231
|
self['ssh'], { :logger => @logger } )
|
232
|
+
# update connection information
|
233
|
+
if self['ip'] && (@connection.ip != self['ip'])
|
234
|
+
@connection.ip = self['ip']
|
235
|
+
end
|
236
|
+
if self['vmhostname'] && (@connection.vmhostname != self['vmhostname'])
|
237
|
+
@connection.vmhostname = self['vmhostname']
|
238
|
+
end
|
239
|
+
if @name && (@connection.hostname != @name)
|
240
|
+
@connection.hostname = @name
|
241
|
+
end
|
242
|
+
@connection
|
230
243
|
end
|
231
244
|
|
232
245
|
def close
|
233
246
|
@connection.close if @connection
|
247
|
+
# update connection information
|
248
|
+
@connection.ip = self['ip'] if self['ip']
|
249
|
+
@connection.vmhostname = self['vmhostname'] if self['vmhostname']
|
250
|
+
@connection.hostname = @name
|
234
251
|
@connection = nil
|
235
252
|
end
|
236
253
|
|
@@ -242,7 +259,11 @@ module Beaker
|
|
242
259
|
output_callback = nil
|
243
260
|
else
|
244
261
|
@logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{cmdline}"
|
245
|
-
|
262
|
+
if @options[:color_host_output]
|
263
|
+
output_callback = logger.method(:color_host_output)
|
264
|
+
else
|
265
|
+
output_callback = logger.method(:host_output)
|
266
|
+
end
|
246
267
|
end
|
247
268
|
|
248
269
|
unless $dry_run
|
data/lib/beaker/host/unix/pkg.rb
CHANGED
@@ -65,6 +65,11 @@ module Unix::Pkg
|
|
65
65
|
execute("zypper --non-interactive in #{name}", opts)
|
66
66
|
when /el-4/
|
67
67
|
@logger.debug("Package installation not supported on rhel4")
|
68
|
+
when /fedora-22/
|
69
|
+
if version
|
70
|
+
name = "#{name}-#{version}"
|
71
|
+
end
|
72
|
+
execute("dnf -y #{cmdline_args} install #{name}", opts)
|
68
73
|
when /cisco|fedora|centos|eos|el-/
|
69
74
|
if version
|
70
75
|
name = "#{name}-#{version}"
|
@@ -121,6 +126,8 @@ module Unix::Pkg
|
|
121
126
|
execute("zypper --non-interactive rm #{name}", opts)
|
122
127
|
when /el-4/
|
123
128
|
@logger.debug("Package uninstallation not supported on rhel4")
|
129
|
+
when /fedora-22/
|
130
|
+
execute("dnf -y #{cmdline_args} remove #{name}", opts)
|
124
131
|
when /cisco|fedora|centos|eos|el-/
|
125
132
|
execute("yum -y #{cmdline_args} remove #{name}", opts)
|
126
133
|
when /ubuntu|debian|cumulus/
|
@@ -145,6 +152,8 @@ module Unix::Pkg
|
|
145
152
|
execute("zypper --non-interactive --no-gpg-checks up #{name}", opts)
|
146
153
|
when /el-4/
|
147
154
|
@logger.debug("Package upgrade is not supported on rhel4")
|
155
|
+
when /fedora-22/
|
156
|
+
execute("dnf -y #{cmdline_args} update #{name}", opts)
|
148
157
|
when /cisco|fedora|centos|eos|el-/
|
149
158
|
execute("yum -y #{cmdline_args} update #{name}", opts)
|
150
159
|
when /ubuntu|debian|cumulus/
|
@@ -3,6 +3,9 @@ module Windows::Exec
|
|
3
3
|
|
4
4
|
def reboot
|
5
5
|
exec(Beaker::Command.new('shutdown /r /t 0 /d p:4:1 /c "Beaker::Host reboot command issued"'), :expect_connection_failure => true)
|
6
|
+
# rebooting on windows is sloooooow
|
7
|
+
# give it some breathing room before attempting a reconnect
|
8
|
+
sleep(30)
|
6
9
|
end
|
7
10
|
|
8
11
|
ABS_CMD = 'c:\\\\windows\\\\system32\\\\cmd.exe'
|
@@ -341,7 +341,10 @@ module Beaker
|
|
341
341
|
def hack_etc_hosts hosts, opts
|
342
342
|
etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
|
343
343
|
hosts.each do |host|
|
344
|
-
|
344
|
+
ip = host['vm_ip'] || host['ip'].to_s
|
345
|
+
hostname = host[:vmhostname] || host.name
|
346
|
+
domain = get_domain_name(host)
|
347
|
+
etc_hosts += "#{ip}\t#{hostname}.#{domain} #{hostname}\n"
|
345
348
|
end
|
346
349
|
hosts.each do |host|
|
347
350
|
set_etc_hosts(host, etc_hosts)
|
@@ -553,14 +556,7 @@ module Beaker
|
|
553
556
|
end
|
554
557
|
# REMOVE POST BEAKER 3: backwards compatability, do some setup based upon the global type
|
555
558
|
# this is the worst and i hate it
|
556
|
-
|
557
|
-
case host[:type]
|
558
|
-
when /git|foss|aio/
|
559
|
-
Class.new.extend(Beaker::DSL).configure_foss_defaults_on(host)
|
560
|
-
when /pe/
|
561
|
-
Class.new.extend(Beaker::DSL).configure_pe_defaults_on(host)
|
562
|
-
end
|
563
|
-
end
|
559
|
+
Class.new.extend(Beaker::DSL).configure_type_defaults_on(host)
|
564
560
|
|
565
561
|
#close the host to re-establish the connection with the new sshd settings
|
566
562
|
host.close
|
@@ -127,7 +127,7 @@ module Beaker
|
|
127
127
|
|
128
128
|
# Return all instances currently on ec2.
|
129
129
|
# @see AwsSdk#instance_by_id
|
130
|
-
# @return [
|
130
|
+
# @return [AWS::EC2::InstanceCollection] An array of AWS::EC2 instance objects
|
131
131
|
def instances
|
132
132
|
@ec2.instances
|
133
133
|
end
|
@@ -142,7 +142,7 @@ module Beaker
|
|
142
142
|
|
143
143
|
# Return all VPCs currently on ec2.
|
144
144
|
# @see AwsSdk#vpc_by_id
|
145
|
-
# @return [
|
145
|
+
# @return [AWS::EC2::VPCCollection] An array of AWS::EC2 vpc objects
|
146
146
|
def vpcs
|
147
147
|
@ec2.vpcs
|
148
148
|
end
|
@@ -157,7 +157,7 @@ module Beaker
|
|
157
157
|
|
158
158
|
# Return all security groups currently on ec2.
|
159
159
|
# @see AwsSdk#security_goup_by_id
|
160
|
-
# @return [
|
160
|
+
# @return [AWS::EC2::SecurityGroupCollection] An array of AWS::EC2 security group objects
|
161
161
|
def security_groups
|
162
162
|
@ec2.security_groups
|
163
163
|
end
|
@@ -224,7 +224,7 @@ module Beaker
|
|
224
224
|
|
225
225
|
# Create an EC2 instance for host, tag it, and return it.
|
226
226
|
#
|
227
|
-
# @return [
|
227
|
+
# @return [void]
|
228
228
|
# @api private
|
229
229
|
def create_instance(host, ami_spec, subnet_id)
|
230
230
|
amitype = host['vmname'] || host['platform']
|
@@ -489,28 +489,32 @@ module Beaker
|
|
489
489
|
nil
|
490
490
|
end
|
491
491
|
|
492
|
+
# Return a valid /etc/hosts line for a given host
|
493
|
+
#
|
494
|
+
# @param [Beaker::Host] host Beaker::Host object for generating /etc/hosts entry
|
495
|
+
# @param [Symbol] interface Symbol identifies which ip should be used for host
|
496
|
+
# @return [String] formatted hosts entry for host
|
497
|
+
# @api private
|
498
|
+
def etc_hosts_entry(host, interface = :ip)
|
499
|
+
name = host.name
|
500
|
+
domain = get_domain_name(host)
|
501
|
+
ip = host[interface.to_s]
|
502
|
+
"#{ip}\t#{name} #{name}.#{domain} #{host['dns_name']}\n"
|
503
|
+
end
|
504
|
+
|
492
505
|
# Configure /etc/hosts for each node
|
493
506
|
#
|
494
507
|
# @return [void]
|
495
508
|
# @api private
|
496
509
|
def configure_hosts
|
497
510
|
@hosts.each do |host|
|
498
|
-
|
499
|
-
|
500
|
-
domain = get_domain_name(host)
|
501
|
-
ip = host['private_ip']
|
502
|
-
etc_hosts += "#{ip}\t#{name} #{name}.#{domain} #{host['dns_name']}\n"
|
503
|
-
@hosts.each do |neighbor|
|
504
|
-
if neighbor == host
|
505
|
-
next
|
506
|
-
end
|
507
|
-
name = neighbor.name
|
508
|
-
domain = get_domain_name(neighbor)
|
509
|
-
ip = neighbor['ip']
|
510
|
-
etc_hosts += "#{ip}\t#{name} #{name}.#{domain} #{neighbor['dns_name']}\n"
|
511
|
+
host_entries = @hosts.map do |h|
|
512
|
+
h == host ? etc_hosts_entry(h, :private_ip) : etc_hosts_entry(h)
|
511
513
|
end
|
512
|
-
|
514
|
+
host_entries.unshift "127.0.0.1\tlocalhost localhost.localdomain\n"
|
515
|
+
set_etc_hosts(host, host_entries.join(''))
|
513
516
|
end
|
517
|
+
nil
|
514
518
|
end
|
515
519
|
|
516
520
|
# Enables root for instances with custom username like ubuntu-amis
|
@@ -176,6 +176,13 @@ module Beaker
|
|
176
176
|
RUN apt-get update
|
177
177
|
RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::CUMULUS_PACKAGES.join(' ')}
|
178
178
|
EOF
|
179
|
+
when /fedora-22/
|
180
|
+
dockerfile += <<-EOF
|
181
|
+
RUN dnf clean all
|
182
|
+
RUN dnf install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')}
|
183
|
+
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
184
|
+
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
|
185
|
+
EOF
|
179
186
|
when /^el-/, /centos/, /fedora/, /redhat/, /eos/
|
180
187
|
dockerfile += <<-EOF
|
181
188
|
RUN yum clean all
|
@@ -194,6 +194,10 @@ module Beaker
|
|
194
194
|
http = Net::HTTP.new( uri.host, uri.port )
|
195
195
|
request = Net::HTTP::Delete.new(uri.request_uri)
|
196
196
|
|
197
|
+
if @credentials[:vmpooler_token]
|
198
|
+
request['X-AUTH-TOKEN'] = @credentials[:vmpooler_token]
|
199
|
+
end
|
200
|
+
|
197
201
|
begin
|
198
202
|
response = http.request(request)
|
199
203
|
rescue *SSH_EXCEPTIONS => e
|
data/lib/beaker/logger.rb
CHANGED
@@ -26,6 +26,7 @@ module Beaker
|
|
26
26
|
BRIGHT_MAGENTA = "\e[01;35m"
|
27
27
|
BRIGHT_CYAN = "\e[01;36m"
|
28
28
|
BRIGHT_WHITE = "\e[01;37m"
|
29
|
+
NONE = ""
|
29
30
|
|
30
31
|
# The defined log levels. Each log level also reports messages at levels lower than itself
|
31
32
|
LOG_LEVELS = {
|
@@ -186,6 +187,16 @@ module Beaker
|
|
186
187
|
optionally_color GREY, string, false
|
187
188
|
end
|
188
189
|
|
190
|
+
# Custom reporting for messages generated by host SUTs - to preserve output
|
191
|
+
# Will not print unless we are at {LOG_LEVELS} 'verbose' or higher.
|
192
|
+
# Preserves outout by not stripping out colour codes
|
193
|
+
# @param args[Array<String>] Strings to be reported
|
194
|
+
def color_host_output *args
|
195
|
+
return unless is_verbose?
|
196
|
+
string = args.join
|
197
|
+
optionally_color NONE, string, false
|
198
|
+
end
|
199
|
+
|
189
200
|
# Custom reporting for performance/sysstat messages
|
190
201
|
# Will not print unless we are at {LOG_LEVELS} 'debug' or higher.
|
191
202
|
# @param args[Array<String>] Strings to be reported
|
@@ -270,7 +281,7 @@ module Beaker
|
|
270
281
|
@destinations.each do |to|
|
271
282
|
to.print color_code if @color
|
272
283
|
to.send print_statement, convert( msg )
|
273
|
-
to.print NORMAL if @color
|
284
|
+
to.print NORMAL if @color unless color_code == NONE
|
274
285
|
end
|
275
286
|
end
|
276
287
|
|
@@ -121,6 +121,15 @@ module Beaker
|
|
121
121
|
@cmd_options[:color] = bool
|
122
122
|
end
|
123
123
|
|
124
|
+
opts.on '--[no-]color-host-output',
|
125
|
+
'Ensure SUT colored output is preserved',
|
126
|
+
'(default: false)' do |bool|
|
127
|
+
@cmd_options[:color_host_output] = bool
|
128
|
+
if bool
|
129
|
+
@cmd_options[:color_host_output] = true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
124
133
|
opts.on '--log-level LEVEL',
|
125
134
|
'Log level',
|
126
135
|
'Supported LEVEL keywords:',
|
@@ -1,44 +1,11 @@
|
|
1
|
+
require 'stringify-hash'
|
2
|
+
|
1
3
|
module Beaker
|
2
4
|
module Options
|
3
5
|
|
4
6
|
# A hash that treats Symbol and String keys interchangeably
|
5
7
|
# and recursively merges hashes
|
6
|
-
class OptionsHash <
|
7
|
-
|
8
|
-
# The dividor between elements when OptionsHash is dumped
|
9
|
-
DIV = ' '
|
10
|
-
|
11
|
-
# The end of line when dumping
|
12
|
-
EOL = "\n"
|
13
|
-
|
14
|
-
# Get value for given key, search for both k as String and k as Symbol,
|
15
|
-
# if not present return nil
|
16
|
-
#
|
17
|
-
# @param [Object] k The key to find, searches for both k as String
|
18
|
-
# and k as Symbol
|
19
|
-
#
|
20
|
-
# @example Use this method to return the value for a given key
|
21
|
-
# a['key'] = 'value'
|
22
|
-
# a['key'] == a[:key] == 'value'
|
23
|
-
#
|
24
|
-
# @return [nil, Object] Return the Object found at given key,
|
25
|
-
# or nil if no Object found
|
26
|
-
def [] k
|
27
|
-
super(k.to_s) || super(k.to_sym)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Set Symbol key to Object value
|
31
|
-
# @param [Object] k The key to associated with the value,
|
32
|
-
# converted to Symbol key
|
33
|
-
# @param [Object] v The value to store in the ObjectHash
|
34
|
-
#
|
35
|
-
# @example Use this method to set the value for a key
|
36
|
-
# a['key'] = 'value'
|
37
|
-
#
|
38
|
-
# @return [Object] Return the Object value just stored
|
39
|
-
def []=k,v
|
40
|
-
super(k.to_sym, v)
|
41
|
-
end
|
8
|
+
class OptionsHash < StringifyHash
|
42
9
|
|
43
10
|
# Determine if type of ObjectHash is pe, defaults to true
|
44
11
|
#
|
@@ -66,271 +33,11 @@ module Beaker
|
|
66
33
|
:pe
|
67
34
|
when /foss/
|
68
35
|
:foss
|
69
|
-
when /aio/
|
70
|
-
:aio
|
71
36
|
else
|
72
37
|
:foss
|
73
38
|
end
|
74
39
|
end
|
75
40
|
|
76
|
-
# Determine if key is stored in ObjectHash
|
77
|
-
# @param [Object] k The key to find in ObjectHash, searches for
|
78
|
-
# both k as String and k as Symbol
|
79
|
-
#
|
80
|
-
# @example Use this method to set the value for a key
|
81
|
-
# a['key'] = 'value'
|
82
|
-
# a.has_key[:key] == true
|
83
|
-
#
|
84
|
-
# @return [Boolean]
|
85
|
-
def has_key? k
|
86
|
-
super(k.to_s) || super(k.to_sym)
|
87
|
-
end
|
88
|
-
|
89
|
-
# Determine key=>value entry in OptionsHash, remove both value at
|
90
|
-
# String key and value at Symbol key
|
91
|
-
#
|
92
|
-
# @param [Object] k The key to delete in ObjectHash,
|
93
|
-
# deletes both k as String and k as Symbol
|
94
|
-
#
|
95
|
-
# @example Use this method to set the value for a key
|
96
|
-
# a['key'] = 'value'
|
97
|
-
# a.delete[:key] == 'value'
|
98
|
-
#
|
99
|
-
# @return [Object, nil] The Object deleted at value,
|
100
|
-
# nil if no Object deleted
|
101
|
-
def delete k
|
102
|
-
super(k.to_s) || super(k.to_sym)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Recursively merge and OptionsHash with an OptionsHash or Hash
|
106
|
-
#
|
107
|
-
# @param [OptionsHash] base The hash to merge into
|
108
|
-
# @param [OptionsHash, Hash] hash The hash to merge from
|
109
|
-
#
|
110
|
-
# @example
|
111
|
-
# base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
|
112
|
-
# hash = { :key => { :subkey1 => 'newval'} }
|
113
|
-
#
|
114
|
-
# rmerge(base, hash)
|
115
|
-
# #=> {:key =>
|
116
|
-
# {:subkey1 => 'newval',
|
117
|
-
# :subkey2 => 'subval'}}
|
118
|
-
#
|
119
|
-
# @return [OptionsHash] The combined bash and hash
|
120
|
-
def rmerge base, hash
|
121
|
-
return base unless hash.is_a?(Hash) || hash.is_a?(OptionsHash)
|
122
|
-
hash.each do |key, v|
|
123
|
-
if (base[key].is_a?(Hash) || base[key].is_a?(OptionsHash)) && (hash[key].is_a?(Hash) || hash[key].is_a?(OptionsHash))
|
124
|
-
rmerge(base[key], hash[key])
|
125
|
-
elsif hash[key].is_a?(Hash)
|
126
|
-
base[key] = OptionsHash.new.merge(hash[key])
|
127
|
-
else
|
128
|
-
base[key]= hash[key]
|
129
|
-
end
|
130
|
-
end
|
131
|
-
base
|
132
|
-
end
|
133
|
-
|
134
|
-
# Create new OptionsHash from recursively merged self with an OptionsHash or Hash
|
135
|
-
#
|
136
|
-
# @param [OptionsHash, Hash] hash The hash to merge from
|
137
|
-
#
|
138
|
-
# @example
|
139
|
-
# base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
|
140
|
-
# hash = { :key => { :subkey1 => 'newval'} }
|
141
|
-
#
|
142
|
-
# base.merge(hash)
|
143
|
-
# #=> {:key =>
|
144
|
-
# {:subkey1 => 'newval',
|
145
|
-
# :subkey2 => 'subval' }
|
146
|
-
#
|
147
|
-
# @return [OptionsHash] The combined hash
|
148
|
-
def merge hash
|
149
|
-
#make a deep copy into an empty hash object
|
150
|
-
merged_hash = rmerge(OptionsHash.new, self)
|
151
|
-
rmerge(merged_hash, hash)
|
152
|
-
end
|
153
|
-
|
154
|
-
# Recursively merge self with an OptionsHash or Hash
|
155
|
-
#
|
156
|
-
# @param [OptionsHash, Hash] hash The hash to merge from
|
157
|
-
#
|
158
|
-
# @example
|
159
|
-
# base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
|
160
|
-
# hash = { :key => { :subkey1 => 'newval'} }
|
161
|
-
#
|
162
|
-
# base.merge!(hash)
|
163
|
-
# #=> {:key =>
|
164
|
-
# {:subkey1 => 'newval',
|
165
|
-
# :subkey2 => 'subval' }
|
166
|
-
#
|
167
|
-
# @return [OptionsHash] The combined hash
|
168
|
-
def merge! hash
|
169
|
-
rmerge(self, hash)
|
170
|
-
end
|
171
|
-
|
172
|
-
# Helper for formatting collections
|
173
|
-
# Computes the indentation level for elements of the collection
|
174
|
-
# Yields indentation to block to so the caller can create
|
175
|
-
# map of element strings
|
176
|
-
# Places delimiters in the correct location
|
177
|
-
# Joins everything with correct EOL
|
178
|
-
#
|
179
|
-
#
|
180
|
-
# !@visibility private
|
181
|
-
def as_coll( opening, closing, in_lvl, in_inc, &block )
|
182
|
-
delim_indent = in_inc * in_lvl
|
183
|
-
elem_indent = in_inc * (in_lvl + 1)
|
184
|
-
|
185
|
-
open_brace = opening
|
186
|
-
close_brace = delim_indent + closing
|
187
|
-
|
188
|
-
fmtd_coll = block.call( elem_indent )
|
189
|
-
str_coll = fmtd_coll.join( ',' + EOL )
|
190
|
-
|
191
|
-
return open_brace + EOL + str_coll + EOL + close_brace
|
192
|
-
end
|
193
|
-
|
194
|
-
# Pretty prints a collection
|
195
|
-
#
|
196
|
-
# @param [Enumerable] collection The collection to be printed
|
197
|
-
# @param [Integer] in_lvl The level of indentation
|
198
|
-
# @param [String] in_inc The increment to indent
|
199
|
-
#
|
200
|
-
# @example
|
201
|
-
# base = {:key => { :subkey1 => 'subval', :subkey2 => ['subval'] }}
|
202
|
-
# self.fmt_collection( base )
|
203
|
-
# #=> '{
|
204
|
-
# "key": {
|
205
|
-
# "subkey": "subval",
|
206
|
-
# "subkey2": [
|
207
|
-
# "subval"
|
208
|
-
# ]
|
209
|
-
# }
|
210
|
-
# }'
|
211
|
-
#
|
212
|
-
# @return [String] The collection as a pretty JSON object
|
213
|
-
def fmt_collection( collection, in_lvl = 0, in_inc = DIV )
|
214
|
-
if collection.respond_to? :each_pair
|
215
|
-
string = fmt_assoc( collection, in_lvl, in_inc )
|
216
|
-
else
|
217
|
-
string = fmt_list( collection, in_lvl, in_inc )
|
218
|
-
end
|
219
|
-
|
220
|
-
return string
|
221
|
-
end
|
222
|
-
|
223
|
-
# Pretty prints an associative collection
|
224
|
-
#
|
225
|
-
# @param [#each_pair] coll The collection to be printed
|
226
|
-
# @param [Integer] in_lvl The level of indentation
|
227
|
-
# @param [String] in_inc The increment to indent
|
228
|
-
#
|
229
|
-
# @example
|
230
|
-
# base = { :key => 'value', :key2 => 'value' }
|
231
|
-
# self.fmt_assoc( base )
|
232
|
-
# #=> '{
|
233
|
-
# "key": "value",
|
234
|
-
# "key2": "value"
|
235
|
-
# }'
|
236
|
-
#
|
237
|
-
# @return [String] The collection as a pretty JSON object
|
238
|
-
def fmt_assoc( coll, in_lvl = 0, in_inc = DIV )
|
239
|
-
if coll.empty?
|
240
|
-
return '{}'
|
241
|
-
else
|
242
|
-
as_coll '{', '}', in_lvl, in_inc do |elem_indent|
|
243
|
-
coll.map do |key, value|
|
244
|
-
assoc_line = elem_indent + '"' + key.to_s + '"' + ': '
|
245
|
-
assoc_line += fmt_value( value, in_lvl, in_inc )
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# Pretty prints a list collection
|
252
|
-
#
|
253
|
-
# @param [#each] coll The collection to be printed
|
254
|
-
# @param [Integer] in_lvl The level of indentation
|
255
|
-
# @param [String] in_inc The increment to indent
|
256
|
-
#
|
257
|
-
# @example
|
258
|
-
# base = [ 'first', 'second' ]
|
259
|
-
# self.fmt_list( base )
|
260
|
-
# #=> '[
|
261
|
-
# "first",
|
262
|
-
# "second"
|
263
|
-
# ]'
|
264
|
-
#
|
265
|
-
# @return [String] The collection as a pretty JSON object
|
266
|
-
def fmt_list( coll, in_lvl = 0, in_inc = DIV )
|
267
|
-
if coll.empty?
|
268
|
-
return '[]'
|
269
|
-
else
|
270
|
-
as_coll '[', ']', in_lvl, in_inc do |indent|
|
271
|
-
coll.map do |el|
|
272
|
-
indent + fmt_value( el, in_lvl, in_inc )
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
# Chooses between collection and primitive formatting
|
279
|
-
#
|
280
|
-
# !@visibility private
|
281
|
-
def fmt_value( value, in_lvl = 0, in_inc = DIV )
|
282
|
-
if value.kind_of? Enumerable and not value.is_a? String
|
283
|
-
fmt_collection( value, in_lvl + 1, in_inc )
|
284
|
-
else
|
285
|
-
fmt_basic( value )
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
# Pretty prints primitive JSON values
|
290
|
-
#
|
291
|
-
# @param [Object] value The collection to be printed
|
292
|
-
#
|
293
|
-
# @example
|
294
|
-
# self.fmt_value( 4 )
|
295
|
-
# #=> '4'
|
296
|
-
#
|
297
|
-
# @example
|
298
|
-
# self.fmt_value( true )
|
299
|
-
# #=> 'true'
|
300
|
-
#
|
301
|
-
# @example
|
302
|
-
# self.fmt_value( nil )
|
303
|
-
# #=> 'null'
|
304
|
-
#
|
305
|
-
# @example
|
306
|
-
# self.fmt_value( 'string' )
|
307
|
-
# #=> '"string"'
|
308
|
-
#
|
309
|
-
# @return [String] The value as a valid JSON primitive
|
310
|
-
def fmt_basic( value )
|
311
|
-
case value
|
312
|
-
when Numeric, TrueClass, FalseClass then value.to_s
|
313
|
-
when NilClass then "null"
|
314
|
-
else "\"#{value}\""
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
# Pretty print the options as JSON
|
319
|
-
#
|
320
|
-
# @example
|
321
|
-
# base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
|
322
|
-
# base.dump
|
323
|
-
# #=> '{
|
324
|
-
# "key": {
|
325
|
-
# "subkey1": "subval",
|
326
|
-
# "subkey2": 2
|
327
|
-
# }
|
328
|
-
# }
|
329
|
-
#
|
330
|
-
# @return [String] The description of self
|
331
|
-
def dump
|
332
|
-
fmt_collection( self, 0, DIV )
|
333
|
-
end
|
334
41
|
end
|
335
42
|
end
|
336
43
|
end
|