Showing posts with label fields. Show all posts
Showing posts with label fields. Show all posts

Tuesday, June 11, 2013

options passing through

I've already shown it in the examples, but here is also the official description: you can accept the arbitrary options, typically if your function is a wrapper to another function, and you just want to process a few options and let the others through. The Triead::start() is a good example, passing the options through to the main function of the thread.

You specify the acceptance of the arbitrary options by using "*" in the Opt::parse() arguments. For example:

  &Triceps::Opt::parse($myname, $opts, {
    app => [ undef, \&Triceps::Opt::ck_mandatory ],
    thread => [ undef, \&Triceps::Opt::ck_mandatory ],
    fragment => [ "", undef ],
    main => [ undef, sub { &Triceps::Opt::ck_ref(@_, "CODE") } ],
    '*' => [],
  }, @_);

The specification array for "*" is empty. The unknown options will be collected in the array referred to from $opts->{'*'}, that is @{$opts->{'*'}}.

From there on your wrapper has the choice of either passing through all the options to the wrapped function, using @_, or explicitly specifying a few options and  passing through the  rest from @{$opts->{'*'}}.

There is also the third possibility: filter out only some of the incoming options. This can be done with Opt::drop(). For example, Triead::startHere() works like this:

our @startOpts = (
  app => [ undef, \&Triceps::Opt::ck_mandatory ],
  thread => [ undef, \&Triceps::Opt::ck_mandatory ],
  fragment => [ "", undef ],
  main => [ undef, sub { &Triceps::Opt::ck_ref(@_, "CODE") } ],
);

sub startHere # (@opts)
{
  my $myname = "Triceps::Triead::start";
  my $opts = {};
  my @myOpts = ( # options that don't propagate through
    harvest => [ 1, undef ],
    makeApp => [ 1, undef ],
  );

  &Triceps::Opt::parse($myname, $opts, {
    @startOpts,
    @myOpts,
    '*' => [],
  }, @_);

  my @args = &Triceps::Opt::drop({
    @myOpts
  }, \@_);
  @_ = (); # workaround for threads leaking objects

  # no need to declare the Triead, since all the code executes synchronously anyway
  my $app;
  if ($opts->{makeApp}) {
    $app = &Triceps::App::make($opts->{app});
  } else {
    $app = &Triceps::App::resolve($opts->{app});
  }
  my $owner = Triceps::TrieadOwner->new(undef, undef, $app, $opts->{thread}, $opts->{fragment});
  push(@args, "owner", $owner);
  eval { &{$opts->{main}}(@args) };
...

The @startOpts are both used by the startHere() and passed through. The @myOpts are only used in startHere() and do not pass through. And the rest of the options pass through without baing used in startHere(). So the options from @myOpts get dropped from @_, and the result goes to the main thread.

The Opt::drop() takes the specification of the options to drop as a hash reference, the same as Opt::parse(). The values in the hash are not important in this case, only the keys are used. But it's simpler to store the same specification of the options and reuse it for both parse() and drop() than to write it twice.

There is also an opposite function, Opt::dropExcept(). It passes through only the listed options and drops the rest. It can come handy if your wrapper wants to pass different subsets of its incoming options to multiple functions.

The functions drop() and dropExcept() can really be used on any name-value arrays, not just the options as such. And the same goes for the Fields::filter() and friends. So you can use them interchangeably: you can use Opt::drop() on the row type specifications and Fields::filter() on the options if you feel that it makes your code simpler.

Thursday, July 26, 2012

even more stuff for 1.0

I've added the function Triceps::Fields::makeTranslation() that makes the output field filtering and renaming, similar to the one in joins, easy to insert into the random user-defined templates. Again, look in the upcoming docs, the chapter on templates for the description.

The build instructions have been completely rewritten and greatly clarified. The build configuration settings became easier too.

The Table methods for row finding and iteration now confess on errors, since checking their error codes otherwise is completely impractical.

A lot more methods have been converted to the new-style error handling, with confessing on errors.

Friday, July 6, 2012

more updates

Some more stuff has been getting cleaned up:

$unit->setTracer($tracer);

now confesses on errors, so the problem with its error checking is fixed.

The tables now allow to create rowops of rows of all matching types, not only of the equal types. The approach with matching types was not consistent with what the labels did, so I've changed it.

The TableType now has the method

$tt->getRowType() 

that has the name consistent with the tables and labels. The old method rowType() also still exists.

The Opt::ck_ref() now also accepts the subclasses of the defined classes.

The new helper Fields::isStringType() has been added.

There also have been some major addition of the examples:

I've added a pretty big example of the main loop that includes the full socket handling in the chapter on Scheduling. It's not exactly production-ready but gives some idea.

Another addition in the chapter on Scheduling is an example of a topological loop that computes the Fibonacci numbers.

Many new examples have been added to the chapter on Templates.

Saturday, May 19, 2012

The field processing helpers

The manipulation on the field lists from the joins is also available for reuse. It's grouped in the package Triceps::Fields.

The function

$result = &Triceps::Fields::isArrayType($simpleType);

checks whether the simple type is an array type, from the standpoint of representation it in Perl. The array types are those that end with "[]", except "uint8[]" (because it's represented as a Perl scalar string).

The function

@fields = &Triceps::Fields:: filter($callerDescr, \@incomingFields, \@patterns);

filters a list of fields by a pattern list of the same form as used in the join results.  For example:

my @leftfld = $self->{leftRowType}->getFieldNames();
my @res =&Triceps::Fields::filter(
  "Triceps::LookupJoin::new: option '$leftFields'",
  \@leftfld, $self->{"$leftFields"});

$callerDescr is the description of the caller used in the error messages, \@incomingFields is the reference to an array with the field names to be filtered, \@patterns is the reference to array of field name patterns. Each pattern is a string in one of the forms:

  • 'regexp' - pass through the field names matching the anchored regexp (i.e. implicitly wrapped as '^regexp$').
  • '!regexp' - throw away the field names matching the anchored regexp.
  • 'regexp/regsub' - pass through the field names matching the anchored regexp, performing a substitution on it.

If a field name doesn't match any of the patterns, it doesn't pass through the filter.

Each field is checked against each pattern in order, and the first successful match determines what happens with the field. For example, when the pattern ['!key', '.*'] is used on the field name "key", the first '!key' matches it and blocks the field from passing through the filter.

In general, quoting the patterns with single quotes is better than with double quotes, because this way the special regexp characters don't need so much escaping with backslashes. Naturally, it's better to keep the field names alphanumeric too, to avoid getting funny effects when they are used in the patterns. Some particularly useful pattern examples:

  • '.*' - pass through everything
  • '.*/second_$&/' - pass everything and prepend 'second_' to it
  • 'right_(.*)/$1/' - pass the field names starting from 'right_' and remove this prefix from them

More examples of the patterns have been shown with the joins.

The result is an array of field names after translation. That array has the same size as @incomingFields, and keeps the passed-through fields in the same order. The fields that don't pass through get replaced with undef.

If a pattern specifies a literal alphanumeric field name without any regexp wildcard (such as 'key' or '!key' or 'key/right_$&/'), this function makes sure that the field is present in the original field list. If it isn't, the function confesses. The reason is for the accidental typos in the field names not to go unnoticed. No such check is done on the general patterns, to allow the reuse of patterns on many different field lists, including those where the pattern doesn't match anything.

The function doesn't check for any duplicates in the resulting field names, nor for any funny characters in them. The reason for not checking the duplicates is that often the result is combined from multiple sets of filtered fields, and the check for duplicates makes sense only after these sets are combined.

The function

@pairs = &Triceps::Fields::filterToPairs($callerDescr,
  \@incomingFields, \@patterns);

is a version of filter() that does the same but returns the result in a different form. This time the result contains a pair of values "oldName", "newName" for each field that passes through (of course, if the field is not renamed, "oldName" and "newName" will be the same). For example, if called

@pairs = &Triceps::Fields::filterToPairs("MyTemplate result",
  ["abc", "abcd", "fgh", "jkl"], ["!abc", "a.*", "jkl/qwe"]);

the result in @pairs will be: ("abcd", "abcd", "jkl", "qwe"). The field "abcd" made through as is, the field "jkl" got renamed to "qwe". You can also put the result of filterToPairs directly into a map:

%resultFields = &Triceps::Fields::filterToPairs(...);

Other than the result format, filterToPairs() works exactly the same as filter(). It's just that sometimes one format of the result is more convenient, sometimes the other.