Wednesday, July 31, 2013

Make Your Function Work Like a Collection

tl;dr; Kernel#enum_for  lets you treat your method like a collection.

I was iterating over the files in a folder, looking for those with a specific file extension. I had something like this...

  def for_all_files(f, e, &block)
    Dir[f+"/*"].each { |file| 
      if(File.directory?(file))
        for_all_files(file, e, &block)
      else
        block.call(file) if file.end_with?(e)
      end
    }
  end

Now I'm sure Ruby provides a better way to achieve this, but it worked for me... until I found I was filtering this list again in the block I was passing in.

  for_all_files(source_folder, ".ts") do |file|
    if(path_filter =~ file)
       ...
    end
  end

What I wanted was something like this:
  files(source_folder, ".ts").
    select{|file| path_filter =~ file}.each do |file|
    ...
  end

Well as per usual Ruby 2.0 has already thought of that.
 
  def files(folder, ending)
    def for_all_files(f, e, &block)
      Dir[f+"/*"].each { |file| 
        if(File.directory?(file))
          for_all_files(file, e, &block)
        else
          block.call(file) if file.end_with?(e)
        end
      }
    end
    enum_for(:for_all_files, folder, ending)
  end

Note the enum_for. It takes a symbol for a method name and a set of arguments. It returns an Enumerable that encapsulates your method.  I really like the result of this but I don't like the code. If you know a better way, let me know. Post a comment telling me how I should have written it.

Also.. I think this means I can also make it lazy like this...
  files(source_folder, ".ts").
    lazy.
    select{|file| path_filter =~ file}.
    each do |file|
    ...
  end

Nice!


Update:
On a similar theme I was wrapping up a COM API that has iterators using Count and Child(x) methods. With Enumerator.new you can easily wrap these to expose a clean ruby API.
  def child_folders(folder)
    Enumerator.new do |y|
      (1..folder.Count).each {|i| y << folder.Child(i)}
    end
  end

No comments:

GitHub Projects