Use an optionally passed-in block without repeating yourself I need to open and read several files for a project. Some of them are plain text files, and I just want a standard File.open to read from. One of them is an XML file, and I want it automatically loaded into Nokogiri in the following way:

xml = File.open("#{LOCATION}/#{filename}.xml"){|f| Nokogiri::XML(f){|config| config.noblanks}}

So I created a function that takes a pattern to select a file, and then optionally opens it with a block so that I can pass in the Nokogiri block above in the case of the xml file.

How do I reduce the repetition in the code?

# in read_xml...
xml = file_from_pattern(XML_PATTERN){|f| Nokogiri::XML(f){|config| config.noblanks}}

# in read_audit...
audit_lines = file_from_pattern(AUDIT_PATTERN).readlines


def file_from_pattern(pattern)
  file_name = Dir.entries("#{LOCATION}").select{|f| f =~ pattern}[0]
  if block_given?
    yield File.open("#{LOCATION}/#{file_name}")
  else
    File.open("#{LOCATION}/#{file_name}")
  end
end
Some notes:


select {... }[0] -> detect { ... }.
Is LOCATION a string? Then, "#{LOCATION}" -> LOCATION
Use File#join instead of manual string manipulation.
The only repetition in your code is File.open("#{LOCATION}/#{file_name}"). The solution is very simple: create a local variable and use it.
Don't you check whether file_name is nil?
File#open accepts a block, why don't you simply pass it? you get the file descriptor closed for free in the block case.


I'd write:

def file_from_pattern(pattern, &amp;block)
  file_name = Dir.entries(LOCATION).detect { |f| f =~ pattern }
  if file_name
    # Refactor 1:
    # fd = File.open(File.join(LOCATION, file_name))
    # block_given? ? yield(fd) : fd
    File.open(File.join(LOCATION, file_name), &amp;block)
  end
end