Plug in Friendly Architecture

Giacomo Stelluti Scala edited this page Aug 7, 2015 · 10 revisions

IMPORTANT NOTE: This wiki refers to latest stables, if a beta version is available in github master branch please refer to Latest Version.

The parser can be configured to ignore valid input and defer its processing to another part of the application you’re designing. This fits particularly well in a plug-in scenario.

Now suppose that your application define an option to allow loading of a particular plug-in:

class AppOptions {
  [Option('p', "plugin", Required = true, HelpText = "Plug-In to activate.")]
  public string PlugInName { get; set; }
  // Remainder omitted
}

Ever for the sake of the sample, suppose your application is able to load a plugin defined in Options::PlugInName. Hence for brevity will say also that such plug-in lives in its own DLL in a particular folder and requires its particular set of command line options.

So when a user write the following line at the command prompt:

$ coolapp --plugin dropTempTables TMP_A TMP_B --nobackup

the parser has no knowledge of what came after the plug-in name (dropTempTables in this case). The only thing it could be able to do is only to ensure whatever the input is lexically correct or not.

Enabling The Behaviour

This behaviour is disabled by default and it also not available in the pre-built singleton. To enable it you must create the parser instance by your own.

One way is to properly configure a ParserSettings:

var parser = new CommandLine.Parser(with => with.IgnoreUnknownArguments = true);

Now you can safely invoke ParseArguments as always:

var appOptions = new AppOptions();
if (parser.ParseArguments(args, appOptions)) {
  // parsing succeds
}

The plug-in of the previous example with an options class defined as follows:

class DropTempTablesOptions {
  [Option("no-backup", HelpText = "Turn-off backup log.")]
  public bool NoBackup { get; set; }

  [ValueList(typeof(List<string>))]
  public IList<string> TempTablesToDrop { get; set; }
}

can re-use the same parser instance or, depending on your design, creates a new one as long as IgnoreUnknownArguments is set to true. It follows a simplistic flow just to understand better this feature:

var appOptions = new AppOptions();
if (parser.ParseArguments(args, appOptions)) {
  // Parsing succeds; plug-in arguments ignored,
  // we can load user plugin and proceed

  if (appOptions.PlugInName.ToUpperInvariant() == "DROPTEMPTABLES") {

    // Loading specified plug-in
    var plugIn = PlugInManager.Load(appOptions.PlugInName);
    var dropTempTablesOptions = new DropTempTablesOptions();

    if (parser.ParseArguments(args, dropTempTablesOptions)) {
      // Parsing succeds; application arguments ignored,
      // we can perform plug-in related operations

      plugIn.ConfigureWith(dropTempTablesOptions);
      Console.WriteLine("Dropping DB tables ...");
      plugIn.DoJob();
    }
    // else if ...
  }
}

Note

This useful feature from one of ours main contributor is still not implemented in pre-release 2.0 (that's a deep refactoring inside/out), but it will.