#841 Proposal: fand to util

brian Mon 30 Nov 2009

As part of the webmod rework, I've been re-evalating the fand pod. Originally I envisioned this API as a daemon framework, but after several years it is looking like things are so simple, there really isn't a need for a "framework" per se.

There are two classes in this pod:

Both of these classes are useful utils, but I am not sure it makes sense to have a fand pod in its own right. I propose the following:

  • rename fand pod to util as catch-all for utility classes like these
  • add AbstractMain class for common handling of command line parameters, usage, etc
  • rework BootScript to be DaemonMain which subclasses from AbstractMain

lbertrand Mon 30 Nov 2009

I have no issue with the proposed changes...

But, I don't really like a util package with a lot of everything in it, in general... Why not having FileLogger part of the log pod? For the BootScript, I don't know but I am sure there is a better pod for it than a generic util.

brian Mon 30 Nov 2009

But, I don't really like a util package with a lot of everything in it, in general

I don't love the idea of a generic util pod either, but I think it makes sense to have a nice catch all for stuff like this versus a bunch of little pods that might only have one or two classes (like fand).

There are lots of little things I keep re-inventing b/c I haven't had a good place to stick stuff, and util might be a nice misc pod.

The restriction will be that util has no dependencies other than sys. Then you can depend on util for your own pods without pulling in anything else.

lbertrand Mon 30 Nov 2009

As long as util does not grow too much I think it is ok...

But first thing should be: can this class be in another pod, if not, this is really a util class, stick it in the util pod.

brian Tue 1 Dec 2009

Promoted to ticket #841 and assigned to brian

brian Tue 1 Dec 2009

This is what I am thinking for AbstractMain - make it easy to define your options and arguments as fields annotated with facets:

class MyMain : AbstractMain
{
  @option="HTTP port for server"
  Int port := 80

  @option="Debug mode"
  Bool debug := false

  @requiredArgument="File to run"
  File? file

  override Int run() { ... }
}

The base class would handle parsing options, usage, etc by introspecting the facets:

C:> fan MyMain -?
usage MyMain [options] <file>
Arguments
  file         File to run
Options
  -port <Int>  HTTP port for server (default 80)
  -debug       Debug mode

C:> fan MyMain -debug -port 8080 somefile.txt

Does everybody like that design?

KevinKelley Tue 1 Dec 2009

nice.

andy Tue 1 Dec 2009

I like that - can you use osPath for specifying files?

ivan Tue 1 Dec 2009

I like it!

Few questions:

  • will optional arguments support list types as well?
  • can there be more than one required arg? If so, how parameter order is determined?

brian Tue 1 Dec 2009

  • Options will not support lists, but can support any type with a fromStr factory
  • Arguments are ordered by the field declaration order
  • The last argument can be a list
  • File (or File[]) will take a Uri or an osPath
  • Decided to just stick service stuff in AbstractMain versus having a separate DaemonMain

qualidafial Tue 1 Dec 2009

Can we have aliases for options? e.g. so that -o and -output are synonymous.

brian Tue 1 Dec 2009

Can we have aliases for options? e.g. so that -o and -output are synonymous.

Seems like that would pretty easy to add with a Str[] optAliases facet, and probably makes sense since I always alias -help, -h, and -?.

qualidafial Tue 1 Dec 2009

Seems like that would pretty easy to add with a Str[] optAliases facet, and probably makes sense since I always alias -help, -h, and -?.

Assuming I understand you this would make the code look like:

@option="Display help message"
@optAliases=["h","?"]
Bool help := false

+1, I like it.

However I'm curious about the reasoning for deciding to leave the service stuff in AbstractMain rather than separating it out into DaemonMain. That seemed like a good separation of concerns to me.

brian Tue 1 Dec 2009

However I'm curious about the reasoning for deciding to leave the service stuff in AbstractMain rather than separating it out into DaemonMain.

I think the only thing DaemonMain would add is a method to declare the services, then put the main thread to sleep. Because non-daemons might want to spin up services too, I think it the service spin up really does belong in AbstractMain.

qualidafial Tue 1 Dec 2009

What about putting the service spin-up in ServiceMain : AbstractMain, and the daemon stuff in a DaemonMain : ServiceMain?

brian Tue 1 Dec 2009

Basically everything now just boils down one extra method called runServices on AbstractMain that you can call from your run method. So here is what the new web server hello world looks like with port opt:

class WebHello : AbstractMain
{
  @opt="http port"
  Int port := 8080

  override Void run()
  {
    wisp := WispService
    {
      it.port = this.port
      it.root = HelloMod()
    }
    runServices([wisp])
  }
}

andy Tue 1 Dec 2009

What about putting the service spin-up in ServiceMain : AbstractMain, and the daemon stuff in a DaemonMain : ServiceMain

Agree with Brian, its such a small tweak to a utility class, I'd keep it simple and just stick everything on AbstractMain.

brian Tue 1 Dec 2009

Ticket resolved in 1.0.48

I've pushed these changes to hg.

The AbstractMain class is pretty slick for easily setting up your options and arguments. Also take a look at new example script "examples/util/main.fan" and the updated "example/web" scripts (which now all take a port option).

If you take AbstractMain for a test drive, let me know how it works for you.

Login or Signup to reply.