views:

37

answers:

1

I'm writing a small Ruby command-line application that uses fileutils from the standard library for file operations. Depending on how the user invokes the application, I will want to include either FileUtils, FileUtils::DryRun or FileUtils::Verbose.

Since include is private, though, I can't put the logic to choose into the object's initialize method. (That was my first thought, since then I could just pass the information about the user's choice as a parameter to new.) I've come up with two options that seem to work, but I'm not happy with either:

  1. Set a global variable in the app's namespace based on the user's choice, and then do a conditional include in the class:

    class Worker
      case App::OPTION
      when "dry-run"
        include FileUtils::DryRun
        etc.
    
  2. Create sub-classes, where the only difference is which version of FileUtils they include. Choose the appropriate one, depending on the user's choice.

    class Worker
      include FileUtils
      # shared Worker methods go here
    end
    class Worker::DryRun < Worker
      include FileUtils::DryRun
    end
    class Worker::Verbose < Worker
      include FileUtils::Verbose
    end
    

The first method seems DRY-er, but I'm hoping that there's something more straightforward that I haven't thought of.

+3  A: 

So what if it's private?

class Worker
  def initialize(verbose=false)
    if verbose
      (class <<self; include FileUtils::Verbose; end)
    else
      (class <<self; include FileUtils; end)
    end
    touch "test"
  end
end

This includes FileUtils::something in particular's Worker's metaclass - not in the main Worker class. Different workers can use different FileUtils this way.

taw
"So what" sounds right to me. (This was the *exactly* the more straightforward thing I wasn't seeing.) Thanks.
Telemachus
Oops, my mistake. The code I gave before would modify `Worker` class so all `Worker`s would use the same settings. Now it actually uses metaclass and allows pre-Worker settings.
taw
instead of `(class <<self; include FileUtils; end)` you can also use `extend FileUtils`.
Konstantin Haase
@taw - thanks for editing. I'm actually glad to have seen it both ways, since in one case I might want the `include` to be global to the class and in others on a per-instance basis.@Konstantin - thanks for the alternative syntax.
Telemachus