Add detection for commented versions of settings
This commit adds support for detecting commented versions of settings in an existing version of an inifile. If you are setting a value for a setting that isn't currently set in the file, but a commented version is found, then we add the new setting immediately following the commented version, rather than at the end of the section.
This commit is contained in:
parent
8a0d1fa1f2
commit
a45ab65930
3 changed files with 168 additions and 0 deletions
|
@ -7,6 +7,7 @@ module Util
|
|||
|
||||
SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:]+)\]\s*$/
|
||||
SETTING_REGEX = /^(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
|
||||
COMMENTED_SETTING_REGEX = /^(\s*)[#;]+(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
|
||||
|
||||
def initialize(path, key_val_separator = ' = ')
|
||||
@path = path
|
||||
|
@ -34,9 +35,31 @@ module Util
|
|||
end
|
||||
|
||||
section = @sections_hash[section_name]
|
||||
|
||||
if (section.has_existing_setting?(setting))
|
||||
update_line(section, setting, value)
|
||||
section.update_existing_setting(setting, value)
|
||||
elsif result = find_commented_setting(section, setting)
|
||||
# So, this stanza is a bit of a hack. What we're trying
|
||||
# to do here is this: for settings that don't already
|
||||
# exist, we want to take a quick peek to see if there
|
||||
# is a commented-out version of them in the section.
|
||||
# If so, we'd prefer to add the setting directly after
|
||||
# the commented line, rather than at the end of the section.
|
||||
|
||||
# If we get here then we found a commented line, so we
|
||||
# call "insert_inline_setting_line" to update the lines array
|
||||
insert_inline_setting_line(result, section, setting, value)
|
||||
|
||||
# Then, we need to tell the setting object that we hacked
|
||||
# in an inline setting
|
||||
section.insert_inline_setting(setting, value)
|
||||
|
||||
# Finally, we need to update all of the start/end line
|
||||
# numbers for all of the sections *after* the one that
|
||||
# was modified.
|
||||
section_index = @section_names.index(section_name)
|
||||
increment_section_line_numbers(section_index + 1)
|
||||
else
|
||||
section.set_additional_setting(setting, value)
|
||||
end
|
||||
|
@ -206,6 +229,35 @@ module Util
|
|||
File.readlines(path)
|
||||
end
|
||||
|
||||
# This utility method scans through the lines for a section looking for
|
||||
# commented-out versions of a setting. It returns `nil` if it doesn't
|
||||
# find one. If it does find one, then it returns a hash containing
|
||||
# two keys:
|
||||
#
|
||||
# :line_num - the line number that contains the commented version
|
||||
# of the setting
|
||||
# :match - the ruby regular expression match object, which can
|
||||
# be used to mimic the whitespace from the comment line
|
||||
def find_commented_setting(section, setting)
|
||||
return nil if section.is_new_section?
|
||||
(section.start_line..section.end_line).each do |line_num|
|
||||
if (match = COMMENTED_SETTING_REGEX.match(lines[line_num]))
|
||||
if (match[3] == setting)
|
||||
return { :match => match, :line_num => line_num }
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# This utility method is for inserting a line into the existing
|
||||
# lines array. The `result` argument is expected to be in the
|
||||
# format of the return value of `find_commented_setting`.
|
||||
def insert_inline_setting_line(result, section, setting, value)
|
||||
line_num = result[:line_num]
|
||||
match = result[:match]
|
||||
lines.insert(line_num + 1, "#{' ' * section.indentation}#{setting}#{match[4]}#{value}")
|
||||
end
|
||||
|
||||
# Utility method; given a section index (index into the @section_names
|
||||
# array), decrement the start/end line numbers for that section and all
|
||||
|
@ -217,6 +269,17 @@ module Util
|
|||
end
|
||||
end
|
||||
|
||||
# Utility method; given a section index (index into the @section_names
|
||||
# array), increment the start/end line numbers for that section and all
|
||||
# all of the other sections that appear *after* the specified section.
|
||||
def increment_section_line_numbers(section_index)
|
||||
@section_names[section_index..(@section_names.length - 1)].each do |name|
|
||||
section = @sections_hash[name]
|
||||
section.increment_line_nums
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def flush_buffer_to_file(buffer, fh)
|
||||
if buffer.length > 0
|
||||
buffer.each { |l| fh.puts(l) }
|
||||
|
|
|
@ -52,6 +52,19 @@ class IniFile
|
|||
end
|
||||
end
|
||||
|
||||
# This is a hacky method; it's basically called when we need to insert
|
||||
# a new setting but we don't want it to appear at the very end of the
|
||||
# section. Instead we hack it into the existing settings list and
|
||||
# increment our end_line number--this assumes that the caller (`ini_file`)
|
||||
# is doing some babysitting w/rt the other sections and the actual data
|
||||
# of the lines.
|
||||
def insert_inline_setting(setting_name, value)
|
||||
@existing_settings[setting_name] = value
|
||||
if @end_line
|
||||
@end_line = @end_line + 1
|
||||
end
|
||||
end
|
||||
|
||||
def set_additional_setting(setting_name, value)
|
||||
@additional_settings[setting_name] = value
|
||||
end
|
||||
|
@ -68,6 +81,18 @@ class IniFile
|
|||
end
|
||||
end
|
||||
|
||||
# Increment the start and end line numbers for the section (if they are
|
||||
# defined); this is intended to be called when an inline setting is added
|
||||
# to a section that comes before this section in the ini file.
|
||||
def increment_line_nums()
|
||||
if @start_line
|
||||
@start_line = @start_line + 1
|
||||
end
|
||||
if @end_line
|
||||
@end_line = @end_line + 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -731,4 +731,84 @@ subby=bar
|
|||
|
||||
end
|
||||
|
||||
|
||||
context "when dealing settings that have a commented version present" do
|
||||
let(:orig_content) {
|
||||
<<-EOS
|
||||
[section1]
|
||||
# foo=foovalue
|
||||
bar=barvalue
|
||||
foo = foovalue2
|
||||
|
||||
[section2]
|
||||
# foo = foovalue
|
||||
;bar=barvalue
|
||||
blah = blah
|
||||
EOS
|
||||
}
|
||||
|
||||
it "should add a new setting below a commented version of that setting" do
|
||||
resource = Puppet::Type::Ini_setting.new(
|
||||
common_params.merge(:section => 'section2', :setting => 'foo', :value => 'foo3'))
|
||||
provider = described_class.new(resource)
|
||||
provider.exists?.should be_false
|
||||
provider.create
|
||||
validate_file(<<-EOS
|
||||
[section1]
|
||||
# foo=foovalue
|
||||
bar=barvalue
|
||||
foo = foovalue2
|
||||
|
||||
[section2]
|
||||
# foo = foovalue
|
||||
foo = foo3
|
||||
;bar=barvalue
|
||||
blah = blah
|
||||
EOS
|
||||
)
|
||||
end
|
||||
|
||||
it "should update an existing setting in place, even if there is a commented version of that setting" do
|
||||
resource = Puppet::Type::Ini_setting.new(
|
||||
common_params.merge(:section => 'section1', :setting => 'foo', :value => 'foo3'))
|
||||
provider = described_class.new(resource)
|
||||
provider.exists?.should be_true
|
||||
provider.create
|
||||
validate_file(<<-EOS
|
||||
[section1]
|
||||
# foo=foovalue
|
||||
bar=barvalue
|
||||
foo = foo3
|
||||
|
||||
[section2]
|
||||
# foo = foovalue
|
||||
;bar=barvalue
|
||||
blah = blah
|
||||
EOS
|
||||
)
|
||||
end
|
||||
|
||||
it "should add a new setting below a commented version of that setting, respecting semicolons as comments" do
|
||||
resource = Puppet::Type::Ini_setting.new(
|
||||
common_params.merge(:section => 'section2', :setting => 'bar', :value => 'bar3'))
|
||||
provider = described_class.new(resource)
|
||||
provider.exists?.should be_false
|
||||
provider.create
|
||||
validate_file(<<-EOS
|
||||
[section1]
|
||||
# foo=foovalue
|
||||
bar=barvalue
|
||||
foo = foovalue2
|
||||
|
||||
[section2]
|
||||
# foo = foovalue
|
||||
;bar=barvalue
|
||||
bar=bar3
|
||||
blah = blah
|
||||
EOS
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue