puppet 2.6.2 → 2.6.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (83) hide show
  1. data/CHANGELOG +61 -0
  2. data/ext/envpuppet +80 -0
  3. data/ext/puppet-load.rb +62 -26
  4. data/ext/puppetstoredconfigclean.rb +0 -2
  5. data/lib/puppet.rb +1 -1
  6. data/lib/puppet/daemon.rb +2 -2
  7. data/lib/puppet/defaults.rb +10 -8
  8. data/lib/puppet/external/pson/pure/generator.rb +5 -12
  9. data/lib/puppet/indirector/catalog/compiler.rb +8 -4
  10. data/lib/puppet/network/handler/fileserver.rb +2 -0
  11. data/lib/puppet/network/server.rb +2 -2
  12. data/lib/puppet/node/environment.rb +4 -0
  13. data/lib/puppet/parser/ast/collection.rb +34 -51
  14. data/lib/puppet/parser/ast/leaf.rb +10 -2
  15. data/lib/puppet/parser/ast/resource.rb +4 -3
  16. data/lib/puppet/parser/compiler.rb +2 -2
  17. data/lib/puppet/parser/functions.rb +4 -2
  18. data/lib/puppet/parser/lexer.rb +2 -1
  19. data/lib/puppet/parser/parser_support.rb +3 -3
  20. data/lib/puppet/provider.rb +2 -2
  21. data/lib/puppet/provider/confine/exists.rb +1 -4
  22. data/lib/puppet/provider/mount.rb +1 -1
  23. data/lib/puppet/provider/nameservice.rb +3 -1
  24. data/lib/puppet/provider/package/openbsd.rb +6 -10
  25. data/lib/puppet/provider/service/freebsd.rb +4 -1
  26. data/lib/puppet/provider/service/launchd.rb +1 -1
  27. data/lib/puppet/provider/user/user_role_add.rb +8 -6
  28. data/lib/puppet/provider/user/useradd.rb +7 -8
  29. data/lib/puppet/rails.rb +2 -6
  30. data/lib/puppet/rails/host.rb +0 -72
  31. data/lib/puppet/resource.rb +22 -0
  32. data/lib/puppet/resource/type.rb +18 -13
  33. data/lib/puppet/type/exec.rb +1 -7
  34. data/lib/puppet/type/schedule.rb +5 -5
  35. data/lib/puppet/util.rb +20 -18
  36. data/lib/puppet/util/command_line.rb +1 -1
  37. data/lib/puppet/util/file_locking.rb +6 -3
  38. data/lib/puppet/util/metric.rb +1 -1
  39. data/lib/puppet/util/rdoc.rb +5 -4
  40. data/lib/puppet/util/rdoc/generators/puppet_generator.rb +6 -0
  41. data/lib/puppet/util/reference.rb +1 -10
  42. data/lib/puppet/util/suidmanager.rb +1 -1
  43. data/lib/puppet/util/zaml.rb +4 -1
  44. data/spec/integration/indirector/bucket_file/rest_spec.rb +10 -2
  45. data/spec/integration/indirector/certificate_revocation_list/rest_spec.rb +10 -2
  46. data/spec/integration/parser/functions_spec.rb +21 -0
  47. data/spec/integration/parser/ruby_manifest_spec.rb +1 -1
  48. data/spec/integration/ssl/certificate_authority_spec.rb +1 -3
  49. data/spec/integration/util/file_locking_spec.rb +31 -11
  50. data/spec/spec_helper.rb +1 -1
  51. data/spec/unit/application/apply_spec.rb +1 -1
  52. data/spec/unit/daemon_spec.rb +3 -9
  53. data/spec/unit/indirector/catalog/compiler_spec.rb +9 -8
  54. data/spec/unit/network/handler/fileserver_spec.rb +2 -4
  55. data/spec/unit/network/server_spec.rb +3 -10
  56. data/spec/unit/parser/ast/collection_spec.rb +4 -0
  57. data/spec/unit/parser/ast/leaf_spec.rb +43 -1
  58. data/spec/unit/parser/ast/resource_spec.rb +133 -88
  59. data/spec/unit/parser/compiler_spec.rb +8 -8
  60. data/spec/unit/parser/lexer_spec.rb +1 -0
  61. data/spec/unit/parser/parser_spec.rb +9 -2
  62. data/spec/unit/provider/confine/exists_spec.rb +6 -13
  63. data/spec/unit/provider/mount_spec.rb +8 -1
  64. data/spec/unit/provider/service/freebsd_spec.rb +50 -0
  65. data/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +1 -2
  66. data/spec/unit/provider/user/user_role_add_spec.rb +1 -1
  67. data/spec/unit/provider/user/useradd_spec.rb +42 -0
  68. data/spec/unit/rails_spec.rb +82 -22
  69. data/spec/unit/resource/type_spec.rb +13 -13
  70. data/spec/unit/type/schedule_spec.rb +21 -49
  71. data/spec/unit/util/command_line_spec.rb +2 -2
  72. data/spec/unit/util/file_locking_spec.rb +28 -24
  73. data/spec/unit/util/{json_spec.rb → pson_spec.rb} +17 -0
  74. data/spec/unit/util/rdoc_spec.rb +9 -1
  75. data/spec/unit/util/storage_spec.rb +2 -3
  76. data/test/other/provider.rb +1 -12
  77. data/test/other/report.rb +2 -5
  78. data/test/puppet/tc_suidmanager.rb +5 -14
  79. data/test/ral/manager/type.rb +1 -1
  80. data/test/ral/providers/provider.rb +3 -3
  81. data/test/util/metrics.rb +2 -2
  82. metadata +8 -6
  83. data/spec/integration/indirector/rest_spec.rb +0 -525
@@ -52,21 +52,17 @@ module Puppet::Rails
52
52
  args[:port] = Puppet[:dbport] unless Puppet[:dbport].to_s.empty?
53
53
  args[:username] = Puppet[:dbuser] unless Puppet[:dbuser].to_s.empty?
54
54
  args[:password] = Puppet[:dbpassword] unless Puppet[:dbpassword].to_s.empty?
55
+ args[:pool] = Puppet[:dbconnections].to_i unless Puppet[:dbconnections].to_i <= 0
55
56
  args[:database] = Puppet[:dbname]
56
57
  args[:reconnect]= true
57
58
 
58
59
  socket = Puppet[:dbsocket]
59
60
  args[:socket] = socket unless socket.to_s.empty?
60
-
61
- connections = Puppet[:dbconnections].to_i
62
- args[:pool] = connections if connections > 0
63
61
  when "oracle_enhanced":
64
62
  args[:database] = Puppet[:dbname] unless Puppet[:dbname].to_s.empty?
65
63
  args[:username] = Puppet[:dbuser] unless Puppet[:dbuser].to_s.empty?
66
64
  args[:password] = Puppet[:dbpassword] unless Puppet[:dbpassword].to_s.empty?
67
-
68
- connections = Puppet[:dbconnections].to_i
69
- args[:pool] = connections if connections > 0
65
+ args[:pool] = Puppet[:dbconnections].to_i unless Puppet[:dbconnections].to_i <= 0
70
66
  else
71
67
  raise ArgumentError, "Invalid db adapter #{adapter}"
72
68
  end
@@ -16,16 +16,6 @@ class Puppet::Rails::Host < ActiveRecord::Base
16
16
  belongs_to :source_file
17
17
  has_many :resources, :dependent => :destroy, :class_name => "Puppet::Rails::Resource"
18
18
 
19
- # If the host already exists, get rid of its objects
20
- def self.clean(host)
21
- if obj = self.find_by_name(host)
22
- obj.rails_objects.clear
23
- return obj
24
- else
25
- return nil
26
- end
27
- end
28
-
29
19
  def self.from_puppet(node)
30
20
  host = find_by_name(node.name) || new(:name => node.name)
31
21
 
@@ -38,63 +28,6 @@ class Puppet::Rails::Host < ActiveRecord::Base
38
28
  host
39
29
  end
40
30
 
41
- # Store our host in the database.
42
- def self.store(node, resources)
43
- args = {}
44
-
45
- host = nil
46
- railsmark "Stored node" do
47
- transaction do
48
- #unless host = find_by_name(name)
49
-
50
- debug_benchmark("Searched for host")do
51
- unless host = find_by_name(node.name)
52
- host = new(:name => node.name)
53
- end
54
- end
55
- if ip = node.parameters["ipaddress"]
56
- host.ip = ip
57
- end
58
-
59
- if env = node.environment
60
- host.environment = env
61
- end
62
-
63
- # Store the facts into the database.
64
- host.merge_facts(node.parameters)
65
-
66
- debug_benchmark("Handled resources") {
67
- host.merge_resources(resources)
68
- }
69
-
70
- host.last_compile = Time.now
71
-
72
- debug_benchmark("Saved host") {
73
- host.save
74
- }
75
- end
76
-
77
- end
78
-
79
- # This only runs if time debugging is enabled.
80
- write_benchmarks
81
-
82
- host
83
- end
84
-
85
- # Return the value of a fact.
86
- def fact(name)
87
-
88
- if fv = self.fact_values.find(
89
- :all, :include => :fact_name,
90
-
91
- :conditions => "fact_names.name = '#{name}'")
92
- return fv
93
- else
94
- return nil
95
- end
96
- end
97
-
98
31
  # returns a hash of fact_names.name => [ fact_values ] for this host.
99
32
  # Note that 'fact_values' is actually a list of the value instances, not
100
33
  # just actual values.
@@ -305,11 +238,6 @@ class Puppet::Rails::Host < ActiveRecord::Base
305
238
  end
306
239
  end
307
240
 
308
- def update_connect_time
309
- self.last_connect = Time.now
310
- save
311
- end
312
-
313
241
  def to_puppet
314
242
  node = Puppet::Node.new(self.name)
315
243
  {"ip" => "ipaddress", "environment" => "environment"}.each do |myparam, itsparam|
@@ -80,6 +80,18 @@ class Puppet::Resource
80
80
  end
81
81
  end
82
82
 
83
+ def yaml_property_munge(x)
84
+ case x
85
+ when Hash
86
+ x.inject({}) { |h,kv|
87
+ k,v = kv
88
+ h[k] = self.class.value_to_pson_data(v)
89
+ h
90
+ }
91
+ else self.class.value_to_pson_data(x)
92
+ end
93
+ end
94
+
83
95
  def to_pson(*args)
84
96
  to_pson_data_hash.to_pson(*args)
85
97
  end
@@ -154,6 +166,14 @@ class Puppet::Resource
154
166
  end
155
167
  end
156
168
 
169
+ # This stub class is only needed for serialization compatibility with 0.25.x
170
+ class Reference
171
+ attr_accessor :type,:title
172
+ def initialize(type,title)
173
+ @type,@title = type,title
174
+ end
175
+ end
176
+
157
177
  # Create our resource.
158
178
  def initialize(type, title = nil, attributes = {})
159
179
  @parameters = {}
@@ -180,6 +200,8 @@ class Puppet::Resource
180
200
  tag(self.type)
181
201
  tag(self.title) if valid_tag?(self.title)
182
202
 
203
+ @reference = Reference.new(@type,@title) # for serialization compatibility with 0.25.x
204
+
183
205
  raise ArgumentError, "Invalid resource type #{type}" if strict? and ! resource_type
184
206
  end
185
207
 
@@ -138,21 +138,15 @@ class Puppet::Resource::Type
138
138
  end
139
139
  end
140
140
 
141
- # Make an instance of our resource type. This is only possible
142
- # for those classes and nodes that don't have any arguments, and is
143
- # only useful for things like the 'include' function.
144
- def mk_plain_resource(scope)
141
+ # Make an instance of the resource type, and place it in the catalog
142
+ # if it isn't in the catalog already. This is only possible for
143
+ # classes and nodes. No parameters are be supplied--if this is a
144
+ # parameterized class, then all parameters take on their default
145
+ # values.
146
+ def ensure_in_catalog(scope)
145
147
  type == :definition and raise ArgumentError, "Cannot create resources for defined resource types"
146
148
  resource_type = type == :hostclass ? :class : :node
147
149
 
148
- # Make sure our parent class has been evaluated, if we have one.
149
- if parent
150
- parent_resource = scope.catalog.resource(resource_type, parent)
151
- unless parent_resource
152
- parent_type(scope).mk_plain_resource(scope)
153
- end
154
- end
155
-
156
150
  # Do nothing if the resource already exists; this makes sure we don't
157
151
  # get multiple copies of the class resource, which helps provide the
158
152
  # singleton nature of classes.
@@ -161,11 +155,22 @@ class Puppet::Resource::Type
161
155
  end
162
156
 
163
157
  resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self)
158
+ instantiate_resource(scope, resource)
164
159
  scope.compiler.add_resource(scope, resource)
165
- scope.catalog.tag(*resource.tags)
166
160
  resource
167
161
  end
168
162
 
163
+ def instantiate_resource(scope, resource)
164
+ # Make sure our parent class has been evaluated, if we have one.
165
+ if parent && !scope.catalog.resource(resource.type, parent)
166
+ parent_type(scope).ensure_in_catalog(scope)
167
+ end
168
+
169
+ if ['Class', 'Node'].include? resource.type
170
+ scope.catalog.tag(*resource.tags)
171
+ end
172
+ end
173
+
169
174
  def name
170
175
  return @name unless @name.is_a?(Regexp)
171
176
  @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')
@@ -553,13 +553,7 @@ module Puppet
553
553
  if self[:path]
554
554
  if Puppet.features.posix? and !File.exists?(exe)
555
555
  withenv :PATH => self[:path].join(File::PATH_SEPARATOR) do
556
- path = %x{which #{exe}}.chomp
557
- if path == ""
558
- raise ArgumentError,
559
- "Could not find command '#{exe}'"
560
- else
561
- exe = path
562
- end
556
+ exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'")
563
557
  end
564
558
  elsif Puppet.features.microsoft_windows? and !File.exists?(exe)
565
559
  self[:path].each do |path|
@@ -16,7 +16,7 @@ module Puppet
16
16
  Thus, it behooves you to use wider scheduling (e.g., over a couple of
17
17
  hours) combined with periods and repetitions. For instance, if you
18
18
  wanted to restrict certain resources to only running once, between
19
- the hours of two and 4 AM, then you would use this schedule::
19
+ the hours of two and 4 AM, then you would use this schedule:
20
20
 
21
21
  schedule { maint:
22
22
  range => \"2 - 4\",
@@ -33,7 +33,7 @@ module Puppet
33
33
  Puppet automatically creates a schedule for each valid period with the
34
34
  same name as that period (e.g., hourly and daily). Additionally,
35
35
  a schedule named *puppet* is created and used as the default,
36
- with the following attributes::
36
+ with the following attributes:
37
37
 
38
38
  schedule { puppet:
39
39
  period => hourly,
@@ -45,11 +45,11 @@ module Puppet
45
45
 
46
46
  newparam(:name) do
47
47
  desc "The name of the schedule. This name is used to retrieve the
48
- schedule when assigning it to an object::
48
+ schedule when assigning it to an object:
49
49
 
50
50
  schedule { daily:
51
51
  period => daily,
52
- range => [2, 4]
52
+ range => \"2 - 4\",
53
53
  }
54
54
 
55
55
  exec { \"/usr/bin/apt-get update\":
@@ -65,7 +65,7 @@ module Puppet
65
65
  is always a range within a 24 hour period, and hours must be
66
66
  specified in numbers between 0 and 23, inclusive. Minutes and
67
67
  seconds can be provided, using the normal colon as a separator.
68
- For instance::
68
+ For instance:
69
69
 
70
70
  schedule { maintenance:
71
71
  range => \"1:30 - 4:30\"
@@ -3,6 +3,7 @@
3
3
  require 'puppet/util/monkey_patches'
4
4
  require 'sync'
5
5
  require 'puppet/external/lock'
6
+ require 'monitor'
6
7
 
7
8
  module Puppet
8
9
  # A command failed to execute.
@@ -17,8 +18,7 @@ module Util
17
18
  require 'puppet/util/posix'
18
19
  extend Puppet::Util::POSIX
19
20
 
20
- # Create a hash to store the different sync objects.
21
- @@syncresources = {}
21
+ @@sync_objects = {}.extend MonitorMixin
22
22
 
23
23
  def self.activerecord_version
24
24
  if (defined?(::ActiveRecord) and defined?(::ActiveRecord::VERSION) and defined?(::ActiveRecord::VERSION::MAJOR) and defined?(::ActiveRecord::VERSION::MINOR))
@@ -28,10 +28,19 @@ module Util
28
28
  end
29
29
  end
30
30
 
31
- # Return the sync object associated with a given resource.
32
- def self.sync(resource)
33
- @@syncresources[resource] ||= Sync.new
34
- @@syncresources[resource]
31
+
32
+ def self.synchronize_on(x,type)
33
+ sync_object,users = 0,1
34
+ begin
35
+ @@sync_objects.synchronize {
36
+ (@@sync_objects[x] ||= [Sync.new,0])[users] += 1
37
+ }
38
+ @@sync_objects[x][sync_object].synchronize(type) { yield }
39
+ ensure
40
+ @@sync_objects.synchronize {
41
+ @@sync_objects.delete(x) unless (@@sync_objects[x][users] -= 1) > 0
42
+ }
43
+ end
35
44
  end
36
45
 
37
46
  # Change the process to a different user
@@ -189,7 +198,7 @@ module Util
189
198
  end
190
199
  end
191
200
 
192
- def binary(bin)
201
+ def which(bin)
193
202
  if bin =~ /^\//
194
203
  return bin if FileTest.file? bin and FileTest.executable? bin
195
204
  else
@@ -200,7 +209,7 @@ module Util
200
209
  end
201
210
  nil
202
211
  end
203
- module_function :binary
212
+ module_function :which
204
213
 
205
214
  # Execute the provided command in a pipe, yielding the pipe object.
206
215
  def execpipe(command, failonfail = true)
@@ -359,9 +368,7 @@ module Util
359
368
 
360
369
  # Create an exclusive lock.
361
370
  def threadlock(resource, type = Sync::EX)
362
- Puppet::Util.sync(resource).synchronize(type) do
363
- yield
364
- end
371
+ Puppet::Util.synchronize_on(resource,type) { yield }
365
372
  end
366
373
 
367
374
  # Because some modules provide their own version of this method.
@@ -371,15 +378,10 @@ module Util
371
378
 
372
379
  def memory
373
380
  unless defined?(@pmap)
374
- pmap = %x{which pmap 2>/dev/null}.chomp
375
- if $CHILD_STATUS != 0 or pmap =~ /^no/
376
- @pmap = nil
377
- else
378
- @pmap = pmap
379
- end
381
+ @pmap = which('pmap')
380
382
  end
381
383
  if @pmap
382
- return %x{pmap #{Process.pid}| grep total}.chomp.sub(/^\s*total\s+/, '').sub(/K$/, '').to_i
384
+ %x{#{@pmap} #{Process.pid}| grep total}.chomp.sub(/^\s*total\s+/, '').sub(/K$/, '').to_i
383
385
  else
384
386
  0
385
387
  end
@@ -62,7 +62,7 @@ module Puppet
62
62
  external_command = "puppet-#{subcommand_name}"
63
63
 
64
64
  require 'puppet/util'
65
- path_to_subcommand = Puppet::Util.binary( external_command )
65
+ path_to_subcommand = Puppet::Util.which( external_command )
66
66
  return false unless path_to_subcommand
67
67
 
68
68
  system( path_to_subcommand, *args )
@@ -6,7 +6,7 @@ module Puppet::Util::FileLocking
6
6
  # Create a shared lock for reading
7
7
  def readlock(file)
8
8
  raise ArgumentError, "#{file} is not a file" unless !File.exists?(file) or File.file?(file)
9
- Puppet::Util.sync(file).synchronize(Sync::SH) do
9
+ Puppet::Util.synchronize_on(file,Sync::SH) do
10
10
  File.open(file) { |f|
11
11
  f.lock_shared { |lf| yield lf }
12
12
  }
@@ -33,9 +33,12 @@ module Puppet::Util::FileLocking
33
33
  end
34
34
  end
35
35
 
36
- Puppet::Util.sync(file).synchronize(Sync::EX) do
37
- File.open(file, "w", mode) do |rf|
36
+ Puppet::Util.synchronize_on(file,Sync::EX) do
37
+ File.open(file, File::Constants::CREAT | File::Constants::WRONLY, mode) do |rf|
38
38
  rf.lock_exclusive do |lrf|
39
+ # poor's man open(2) O_EXLOCK|O_TRUNC
40
+ lrf.seek(0, IO::SEEK_SET)
41
+ lrf.truncate(0)
39
42
  yield lrf
40
43
  end
41
44
  end
@@ -64,7 +64,7 @@ class Puppet::Util::Metric
64
64
  end
65
65
 
66
66
  def graph(range = nil)
67
- unless Puppet.features.rrd?
67
+ unless Puppet.features.rrd? || Puppet.features.rrd_legacy?
68
68
  Puppet.warning "RRD library is missing; cannot graph metrics"
69
69
  return
70
70
  end
@@ -10,24 +10,25 @@ module Puppet::Util::RDoc
10
10
 
11
11
  # then rdoc
12
12
  require 'rdoc/rdoc'
13
+ require 'rdoc/options'
13
14
 
14
15
  # load our parser
15
16
  require 'puppet/util/rdoc/parser'
16
17
 
17
18
  r = RDoc::RDoc.new
18
19
 
19
- RDoc::RDoc::GENERATORS["puppet"] = RDoc::RDoc::Generator.new(
20
+ RDoc::RDoc::GENERATORS["puppet"] = RDoc::RDoc::Generator.new(
20
21
  "puppet/util/rdoc/generators/puppet_generator.rb",
21
- "PuppetGenerator".intern,
22
+ "PuppetGenerator".intern,
23
+ "puppet")
22
24
 
23
- "puppet")
24
25
  # specify our own format & where to output
25
26
  options = [ "--fmt", "puppet",
26
27
  "--quiet",
27
- "--force-update",
28
28
  "--exclude", "/modules/[^/]*/files/.*\.pp$",
29
29
  "--op", outputdir ]
30
30
 
31
+ options << "--force-update" if Options::OptionList.options.any? { |o| o[0] == "--force-update" }
31
32
  options += [ "--charset", charset] if charset
32
33
  options += files
33
34
 
@@ -88,6 +88,12 @@ module Generators
88
88
  @modules = {}
89
89
  @allclasses = {}
90
90
 
91
+ # remove unknown toplevels
92
+ # it can happen that RDoc triggers a different parser for some files (ie .c, .cc or .h)
93
+ # in this case RDoc generates a RDoc::TopLevel which we do not support in this generator
94
+ # So let's make sure we don't generate html for those.
95
+ @toplevels = @toplevels.select { |tl| tl.is_a? RDoc::PuppetTopLevel }
96
+
91
97
  # build the modules, classes and per modules classes and define list
92
98
  @toplevels.each do |toplevel|
93
99
  next unless toplevel.document_self
@@ -32,7 +32,6 @@ class Puppet::Util::Reference
32
32
  section = reference(name) or raise "Could not find section #{name}"
33
33
  depth = section.depth if section.depth < depth
34
34
  end
35
- text = "* TOC text.\n{:toc}\n\n"
36
35
  end
37
36
 
38
37
  def self.pdf(text)
@@ -40,14 +39,7 @@ class Puppet::Util::Reference
40
39
  Puppet::Util.secure_open("/tmp/puppetdoc.txt", "w") do |f|
41
40
  f.puts text
42
41
  end
43
- rst2latex = %x{which rst2latex}
44
- if $CHILD_STATUS != 0 or rst2latex =~ /no /
45
- rst2latex = %x{which rst2latex.py}
46
- end
47
- if $CHILD_STATUS != 0 or rst2latex =~ /no /
48
- raise "Could not find rst2latex"
49
- end
50
- rst2latex.chomp!
42
+ rst2latex = which('rst2latex') || which('rst2latex.py') || raise("Could not find rst2latex")
51
43
  cmd = %{#{rst2latex} /tmp/puppetdoc.txt > /tmp/puppetdoc.tex}
52
44
  Puppet::Util.secure_open("/tmp/puppetdoc.tex","w") do |f|
53
45
  # If we get here without an error, /tmp/puppetdoc.tex isn't a tricky cracker's symlink
@@ -141,7 +133,6 @@ class Puppet::Util::Reference
141
133
  # First the header
142
134
  text = h(@title, 1)
143
135
  text += "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n"
144
- text += "* TOC Text.\n{:toc}\n\n" if withcontents
145
136
 
146
137
  text += @header
147
138