Browse Source

Respect indentation / spacing for existing sections and settings

This commit adds some cosmetic functionality.  The main two
improvements are:

* We'll now pay attention to indentation within existing
  sections, and when we write new settings or update
  existing ones, we'll match the existing indentation.

* When modifying existing settings, the regex now captures
  a greater portion of the original line and preserves it.
  Basically, the original whitespacing in the line should
  remain intact and only the value should be modified.
Chris Price 11 years ago
parent
commit
c2c26de

+ 11 - 8
lib/puppet/util/ini_file.rb

@@ -6,7 +6,7 @@ module Util
   class IniFile
 
     SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:]+)\]\s*$/
-    SETTING_REGEX = /^\s*([\w\d\.\\\/\-]+)\s*=\s*([\S\s]*\S)\s*$/
+    SETTING_REGEX = /^(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
 
     def initialize(path, key_val_separator = ' = ')
       @path = path
@@ -30,7 +30,7 @@ module Util
 
     def set_value(section_name, setting, value)
       unless (@sections_hash.has_key?(section_name))
-        add_section(Section.new(section_name, nil, nil, nil))
+        add_section(Section.new(section_name, nil, nil, nil, nil))
       end
 
       section = @sections_hash[section_name]
@@ -77,7 +77,7 @@ module Util
           end
 
           section.additional_settings.each_pair do |key, value|
-            fh.puts("#{key}#{@key_val_separator}#{value}")
+            fh.puts("#{' ' * (section.indentation || 0)}#{key}#{@key_val_separator}#{value}")
           end
         end
       end
@@ -111,12 +111,15 @@ module Util
     def read_section(name, start_line, line_iter)
       settings = {}
       end_line_num = nil
+      min_indentation = nil
       while true
         line, line_num = line_iter.peek
         if (line_num.nil? or match = SECTION_REGEX.match(line))
-          return Section.new(name, start_line, end_line_num, settings)
+          return Section.new(name, start_line, end_line_num, settings, min_indentation)
         elsif (match = SETTING_REGEX.match(line))
-          settings[match[1]] = match[2]
+          settings[match[2]] = match[4]
+          indentation = match[1].length
+          min_indentation = [indentation, min_indentation || indentation].min
         end
         end_line_num = line_num
         line_iter.next
@@ -126,8 +129,8 @@ module Util
     def update_line(section, setting, value)
       (section.start_line..section.end_line).each do |line_num|
         if (match = SETTING_REGEX.match(lines[line_num]))
-          if (match[1] == setting)
-            lines[line_num] = "#{setting}#{@key_val_separator}#{value}"
+          if (match[2] == setting)
+            lines[line_num] = "#{match[1]}#{match[2]}#{match[3]}#{value}"
           end
         end
       end
@@ -136,7 +139,7 @@ module Util
     def remove_line(section, setting)
       (section.start_line..section.end_line).each do |line_num|
         if (match = SETTING_REGEX.match(lines[line_num]))
-          if (match[1] == setting)
+          if (match[2] == setting)
             lines.delete_at(line_num)
           end
         end

+ 3 - 2
lib/puppet/util/ini_file/section.rb

@@ -2,15 +2,16 @@ module Puppet
 module Util
 class IniFile
   class Section
-    def initialize(name, start_line, end_line, settings)
+    def initialize(name, start_line, end_line, settings, indentation)
       @name = name
       @start_line = start_line
       @end_line = end_line
       @existing_settings = settings.nil? ? {} : settings
       @additional_settings = {}
+      @indentation = indentation
     end
 
-    attr_reader :name, :start_line, :end_line, :additional_settings
+    attr_reader :name, :start_line, :end_line, :additional_settings, :indentation
 
     def get_value(setting_name)
       @existing_settings[setting_name] || @additional_settings[setting_name]

+ 200 - 3
spec/unit/puppet/provider/ini_setting/ruby_spec.rb

@@ -123,7 +123,7 @@ master = true
 [section2]
 
 foo= foovalue2
-baz = bazvalue2
+baz=bazvalue2
 url = http://192.168.1.1:8080
 [section:sub]
 subby=bar
@@ -154,7 +154,7 @@ foo= foovalue2
 baz=bazvalue
 url = http://192.168.1.1:8080
 [section:sub]
-subby = foo
+subby=foo
     #another comment
  ; yet another comment
       EOS
@@ -329,7 +329,7 @@ foo = http://192.168.1.1:8080
       provider.value=('yippee')
       validate_file(<<-EOS
 # This is a comment
-foo = yippee
+foo=yippee
 [section2]
 foo = http://192.168.1.1:8080
  ; yet another comment
@@ -533,4 +533,201 @@ subby=bar
     end
   end
 
+
+  context "when dealing with indentation in sections" do
+    let(:orig_content) {
+      <<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+      EOS
+    }
+
+    it "should add a missing setting at the correct indentation when the header is aligned" do
+      resource = Puppet::Type::Ini_setting.new(common_params.merge(
+                    :section => 'section1', :setting => 'yahoo', :value => 'yippee'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_nil
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+     yahoo = yippee
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+      EOS
+      )
+    end
+
+    it "should update an existing setting at the correct indentation when the header is aligned" do
+      resource = Puppet::Type::Ini_setting.new(
+          common_params.merge(:section => 'section1', :setting => 'bar', :value => 'barvalue2'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_true
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue2
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+      EOS
+      )
+    end
+
+    it "should add a missing setting at the correct indentation when the header is not aligned" do
+      resource = Puppet::Type::Ini_setting.new(common_params.merge(
+                                                   :section => 'section2', :setting => 'yahoo', :value => 'yippee'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_nil
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+  yahoo = yippee
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+      EOS
+      )
+    end
+
+    it "should update an existing setting at the correct indentation when the header is not aligned" do
+      resource = Puppet::Type::Ini_setting.new(
+          common_params.merge(:section => 'section2', :setting => 'baz', :value => 'bazvalue2'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_true
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue2
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+      EOS
+      )
+    end
+
+    it "should add a missing setting at the min indentation when the section is not aligned" do
+      resource = Puppet::Type::Ini_setting.new(
+          common_params.merge(:section => 'section:sub', :setting => 'yahoo', :value => 'yippee'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_nil
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam
+ ; yet another comment
+ yahoo = yippee
+      EOS
+      )
+    end
+
+    it "should update an existing setting at the previous indentation when the section is not aligned" do
+      resource = Puppet::Type::Ini_setting.new(
+          common_params.merge(:section => 'section:sub', :setting => 'fleezy', :value => 'flam2'))
+      provider = described_class.new(resource)
+      provider.exists?.should be_true
+      provider.create
+      validate_file(<<-EOS
+# This is a comment
+     [section1]
+     ; This is also a comment
+     foo=foovalue
+
+     bar = barvalue
+     master = true
+
+[section2]
+  foo= foovalue2
+  baz=bazvalue
+  url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+    #another comment
+  fleezy = flam2
+ ; yet another comment
+      EOS
+      )
+    end
+
+  end
+
 end