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.
Files changed (63) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +439 -2
  3. data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
  4. data/acceptance/pre_suite/puppet_git/install.rb +6 -65
  5. data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
  6. data/beaker.gemspec +2 -0
  7. data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
  8. data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -2
  9. data/lib/beaker/dsl/install_utils/foss_utils.rb +97 -60
  10. data/lib/beaker/dsl/install_utils/pe_utils.rb +30 -53
  11. data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
  12. data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
  13. data/lib/beaker/dsl/roles.rb +20 -3
  14. data/lib/beaker/dsl/structure.rb +14 -3
  15. data/lib/beaker/host.rb +24 -3
  16. data/lib/beaker/host/unix/pkg.rb +9 -0
  17. data/lib/beaker/host/windows/exec.rb +3 -0
  18. data/lib/beaker/host_prebuilt_steps.rb +5 -9
  19. data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
  20. data/lib/beaker/hypervisor/docker.rb +7 -0
  21. data/lib/beaker/hypervisor/vmpooler.rb +4 -0
  22. data/lib/beaker/logger.rb +12 -1
  23. data/lib/beaker/options/command_line_parser.rb +9 -0
  24. data/lib/beaker/options/options_hash.rb +3 -296
  25. data/lib/beaker/options/parser.rb +12 -0
  26. data/lib/beaker/options/presets.rb +0 -1
  27. data/lib/beaker/ssh_connection.rb +48 -23
  28. data/lib/beaker/test_case.rb +1 -1
  29. data/lib/beaker/version.rb +1 -1
  30. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
  31. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +194 -49
  32. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +112 -22
  33. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
  34. data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
  35. data/spec/beaker/dsl/roles_spec.rb +36 -5
  36. data/spec/beaker/dsl/structure_spec.rb +9 -2
  37. data/spec/beaker/host/unix/pkg_spec.rb +26 -6
  38. data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
  39. data/spec/beaker/host_spec.rb +18 -0
  40. data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
  41. data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
  42. data/spec/beaker/hypervisor/docker_spec.rb +2 -1
  43. data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
  44. data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
  45. data/spec/beaker/logger_spec.rb +39 -0
  46. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  47. data/spec/beaker/options/options_hash_spec.rb +1 -102
  48. data/spec/beaker/options/parser_spec.rb +19 -0
  49. data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
  50. data/spec/beaker/options/presets_spec.rb +8 -0
  51. data/spec/beaker/ssh_connection_spec.rb +39 -21
  52. data/spec/helpers.rb +9 -3
  53. data/spec/mocks.rb +2 -0
  54. metadata +34 -11
  55. data/lib/beaker/answers.rb +0 -143
  56. data/lib/beaker/answers/version20.rb +0 -120
  57. data/lib/beaker/answers/version28.rb +0 -121
  58. data/lib/beaker/answers/version30.rb +0 -227
  59. data/lib/beaker/answers/version32.rb +0 -44
  60. data/lib/beaker/answers/version34.rb +0 -51
  61. data/lib/beaker/answers/version38.rb +0 -29
  62. data/lib/beaker/answers/version40.rb +0 -44
  63. data/spec/beaker/answers_spec.rb +0 -547
@@ -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[:ip] ||= get_ip
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
- @connection ||= SshConnection.connect( reachable_name,
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
- output_callback = logger.method(:host_output)
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
@@ -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
- etc_hosts += "#{host['vm_ip'] || host['ip'].to_s}\t#{host[:vmhostname] || host.name}\n"
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
- if host[:type]
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 [Array<AWS::EC2::Instance>] An array of AWS::EC2 instance objects
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 [Array<AWS::EC2::VPC>] An array of AWS::EC2 vpc objects
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 [Array<AWS::EC2::SecurityGroup>] An array of AWS::EC2 security group objects
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 [AWS::EC2::Instance)]
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
- etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
499
- name = host.name
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
- set_etc_hosts(host, etc_hosts)
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
@@ -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 < Hash
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