Module: PuppetX::FileMagic
- Defined in:
- lib/puppet_x/filemagic.rb
Class Method Summary collapse
- .append(path, regex_start, flags, data, delete) ⇒ Object
-
.data2lines(data) ⇒ Object
split data (string, eg: foonbarbaz..) into an array of lines.
-
.determine_line_ending(path) ⇒ Object
read the first line of file to determine it's line endings.
-
.exists?(path, data, regex, flags, check_type, check_for_absent) ⇒ Boolean
sandwich position not implemented yet -1 TOP OF file 0 SANDWICH +1 END OF FILE.
-
.get_match_lines(file_lines, data_lines, pos) ⇒ Object
Return a count of matching lines from the file and an array of lines representing the contents of the file less any matches.
-
.get_match_regex(file_lines, regex, flags, first) ⇒ Object
return the index of the first/last match of
regex
inlines
or -1 if no match. - .gsub_match(path, regex, flags, data) ⇒ Object
- .prepend(path, regex_end, flags, data, delete) ⇒ Object
-
.readfile(path) ⇒ Object
read a text file and get rid of newlines.
- .remove_match(path, regex, flags) ⇒ Object
- .replace_match(path, regex, flags, data, insert_if_missing, insert_at) ⇒ Object
-
.writefile(path, content) ⇒ Object
(over)write the output file and print a message that we did so.
Class Method Details
.append(path, regex_start, flags, data, delete) ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/puppet_x/filemagic.rb', line 285 def self.append(path, regex_start, flags, data, delete) # write the new content in one go content = readfile(path) found_at = get_match_regex(content, regex_start, flags,true) data_lines = data2lines(data) if found_at > -1 # Discard from the end of the file all lines including and after content[found_at] content = content[0..found_at-1] end if delete data_lines.reverse.each { |line| if content[-1].strip == line.strip content.pop end } else content += data_lines end writefile(path, content) end |
.data2lines(data) ⇒ Object
split data (string, eg: foonbarbaz..) into an array of lines
13 14 15 16 17 18 19 20 21 22 |
# File 'lib/puppet_x/filemagic.rb', line 13 def self.data2lines(data) if data.nil? || data.empty? lines = [] else # support windows by chomping (CR)LF for comparison purposes lines = data.split(/\r?\n/) end lines end |
.determine_line_ending(path) ⇒ Object
read the first line of file to determine it's line endings
6 7 8 9 10 |
# File 'lib/puppet_x/filemagic.rb', line 6 def self.determine_line_ending(path) File.open(path, 'r') do |file| return file.readline[/\r?\n$/] end end |
.exists?(path, data, regex, flags, check_type, check_for_absent) ⇒ Boolean
sandwich position not implemented yet -1 TOP OF file 0 SANDWICH +1 END OF FILE
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/puppet_x/filemagic.rb', line 182 def self.exists?(path, data, regex, flags, check_type, check_for_absent) data_lines = data2lines(data) if ! check_for_absent && data_lines.size == 0 raise "you must supply a valid string of `data` to check for" end if ! File.exist?(path) Puppet.err("File missing at #{path} (file must already exist to do filemagic on it...)") # cannot possibly exist if it doesn't exist exists = false else file_lines = readfile(path) case check_type when :append, :prepend # Check for an exact match on `data` at the beginning or end of file pos = (check_type == :prepend) ? -1 : 1 lines_matched, file_lines_less_exact_match = get_match_lines(file_lines, data_lines, pos) all_lines_matched = (lines_matched == -1) if all_lines_matched exists = true else # if all lines didn't match there might me a regex we need to scan for if regex partial_match = (get_match_regex(file_lines_less_exact_match, regex, flags, true) > -1) end # Nothing found by regex.. last check - do we have a partial match on any data? if ! partial_match partial_match = (get_match_lines(file_lines_less_exact_match, data_lines, pos)[0] > 0) end exists = check_for_absent && partial_match end when :gsub exists = if (get_match_regex(file_lines, regex, flags, true) > -1) check_for_absent else ! check_for_absent end when :replace, :replace_insert # check for a 100% match on `data_lines` in `files_lines`, anywhere in the file matched_lines, file_lines_less_exact_match = get_match_lines(file_lines, data_lines, 0) all_lines_matched = (matched_lines == -1) # Also do a regex search on the file less any exact match, that we me check for any unwanted instances of # `match` that still exist in the file ("stragglers") match_count = get_match_regex(file_lines_less_exact_match, regex, flags, true) if all_lines_matched if match_count > -1 # we have a straggler - force the correct update for ensure present/absent exists = check_for_absent else exists = all_lines_matched end elsif check_type == :replace_insert # `data_lines` were not matched but they are supposed to exist exists = false else # matches were found in check_for_absent mode means we need to remove them, otherwise we exist (aka dont # need processing) if there are _no_ matches exists = check_for_absent ^ (match_count == -1) end else raise "Unsupported check type #{check_type}" end end exists end |
.get_match_lines(file_lines, data_lines, pos) ⇒ Object
Return a count of matching lines from the file and an array of lines
representing the contents of the file less any matches. the
pos
parameter can be used to anchor the search to the
beginning or end of the file if desired.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/puppet_x/filemagic.rb', line 103 def self.get_match_lines(file_lines, data_lines, pos) exact_lines_matched = 0 file_lines_less_exact_match = [] if pos == 1 # reverse match order if we are checking the end of the file data_lines_ordered = data_lines.reverse if data_lines file_lines_ordered = file_lines.reverse if file_lines else file_lines_ordered = file_lines data_lines_ordered = data_lines end # we need different indices for file_line vs data_line when we are searching with pos=0 since our data_lines are # allowed to exist _anywhere_ in the input data i = 0 j = 0 while file_lines_ordered.size && i < file_lines_ordered.size file_line = file_lines_ordered[i] data_line = data_lines_ordered[j] # check-off that each line in our data is already in the file, in the exact correct position line_matched = (file_line == data_line) # # indicies for next loop # i += 1 if (pos != 0) j = [i, data_lines_ordered.size].min elsif line_matched # we have matched data_lines inside file lines _and_ we are matching from anywhere so we have found the # start of the match, start incrementing `j` until we run out of lines / matches j = [j += 1, data_lines_ordered.size].min end # evaluate our current match, automatically taking account of position in file_lines due to our loop position if line_matched exact_lines_matched += 1 else file_lines_less_exact_match.push(file_line) end end if (exact_lines_matched == data_lines_ordered.size) && (data_lines_ordered.size > 0) # -1 indicates all lines matched lines_matched = -1 else partial_matches = 0 file_lines.each { |file_line| data_lines.each { |data_line| if file_line == data_line partial_matches += 1 end } } lines_matched = partial_matches end if pos == 1 # If we matched from the end the matched lines will be reversed - fix this in-place file_lines_less_exact_match.reverse! end return lines_matched, file_lines_less_exact_match end |
.get_match_regex(file_lines, regex, flags, first) ⇒ Object
return the index of the first/last match of regex
in
lines
or -1 if no match. Be careful with if statements using
this! It's very possible that 0 will be returned to indicate a match in
the first element of the array which is of course false
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/puppet_x/filemagic.rb', line 55 def self.get_match_regex(file_lines, regex, flags, first) found_at = -1 if regex # parse the string from puppet into a real ruby regex. Flags have to be separate for this _regex = Regexp.new(regex, flags) i = 0 if first file_lines_ordered = file_lines else file_lines_ordered = file_lines.reverse end while found_at == -1 && i < file_lines_ordered.size file_line = file_lines_ordered[i] # only interested in the the first line of data if _regex.match?(file_line) found_at = i end i += 1 end if !first && found_at > -1 # need to correct the index since we reversed the array we searched # # `V` points to the element we want in the array of `X`s # # V = 5 # XXXXXXX size == 7 # # want # # V=1 # XXXXXXX found_at = file_lines_ordered.size - found_at - 1 end end found_at end |
.gsub_match(path, regex, flags, data) ⇒ Object
361 362 363 364 365 366 367 |
# File 'lib/puppet_x/filemagic.rb', line 361 def self.gsub_match(path, regex, flags, data) # Read the file and flatten it based on the FILE's encoding (not the os...) content = readfile(path).join(determine_line_ending(path)) _regex = Regexp.new(regex, flags) content = content.gsub(_regex, data) writefile(path, content) end |
.prepend(path, regex_end, flags, data, delete) ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/puppet_x/filemagic.rb', line 256 def self.prepend(path, regex_end, flags, data, delete) # read the old content into an array and prepend the required lines content = readfile(path) found_at = get_match_regex(content, regex_end, flags, false) data_lines = data2lines(data) if found_at > -1 # Discard from the beginning of the file all lines before and including content[found_at] content = content[found_at+1..content.size-1] end if delete # If we deleted based on the match, then target is already gone, otherwise if we are removing # based on `data` remove any lines line data_lines.each { |line| if content[0] == line content.shift end } else # insert the lines at the start of the file content.unshift(data_lines) end # write the new content in one go writefile(path, content) end |
.readfile(path) ⇒ Object
read a text file and get rid of newlines
25 26 27 28 29 30 31 32 33 |
# File 'lib/puppet_x/filemagic.rb', line 25 def self.readfile(path) if File.exists?(path) file_lines = File.readlines(path).each {|l| l.chomp!} else Puppet.err("FileMagic unable to find file at #{path} - make sure it exists") file_lines = [] end file_lines end |
.remove_match(path, regex, flags) ⇒ Object
310 311 312 313 314 315 316 |
# File 'lib/puppet_x/filemagic.rb', line 310 def self.remove_match(path, regex, flags) content = readfile(path).reject { |line| _regex = Regexp.new(regex, flags) _regex.match?(line) } writefile(path, content) end |
.replace_match(path, regex, flags, data, insert_if_missing, insert_at) ⇒ Object
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/puppet_x/filemagic.rb', line 318 def self.replace_match(path, regex, flags, data, insert_if_missing, insert_at) content = [] inserted = false found = false _regex = Regexp.new(regex, flags) readfile(path).each { |line| if _regex.match?(line) if ! inserted content << data inserted = true end else if line == data found = true end content << line end } if insert_if_missing && (! (found || inserted)) case insert_at when 'top' insertion_point = 0 when 'bottom' insertion_point = content.size else line_no = Integer(insert_at) if line_no > content.size Puppet.warning( "#{path}: Cannot insert line #{line_no}, only #{content.size} lines in file") insertion_point = content.size else insertion_point = line_no end end content.insert(insertion_point, data) end writefile(path, content) end |
.writefile(path, content) ⇒ Object
(over)write the output file and print a message that we did so
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/puppet_x/filemagic.rb', line 36 def self.writefile(path, content) if File.exists?(path) File.open(path, "w") do |f| f.puts(content) end Puppet.notice("FileMagic updated: #{path}") else Puppet.err("FileMagic unable to find file at #{path} - make sure it exists") end end |