Help language development. Donate to The Perl Foundation

Config::BINDish zef:vrurg last updated on 2022-06-10

doc-src/README.rakudoc
=begin pod
=head1 NAME

C<Config::BINDish> - parse BIND9/named kind of config files

=SYNOPSIS

    my $cfg = Config::BINDish.new;
    $cfg.read: string => q:to/CFG/;
    server "s1" {
        name "my.server";
        paths {
            base "/opt/myapp";
            pool "files" static {
                "pub/img";
                "static/img";
                "docs";
            };
            pool "files" dynamic {
                "users/reports";
                "system/reports"
            }
        }
    }
    CFG
    say $cfg.top.get( :server("s1") => :paths => :pool("images") => 'root' ); # ./pub/img

    $cfg.read: file => $my-config-filename;

=head1 DISCLAIMER

This module is very much experimental in the sense of the API methods it provides. The grammar is expected to be more
stable, yet no warranties can be given at the moment.

=head1 DESCRIPTION

In this documentation I'll be referring to the configuration format implemented by the module as I<BINDish config> or
simply I<BINDish>.

=head1 EXTENSIONS

BINDish configuration parser can be augmented with 3rd-party extensions. Every extension is implemented as a role which
will be used to build the final grammar or actions classes (see L<C<Grammar>|https://docs.raku.org/type/Grammar>). The
classes are based upon
L<C<Config::BINDish::Grammar>|BINDish/Grammar.md> and
L<C<Config::BINDish::Actions>|BINDish/Actions.md>
respectively. Here is the steps C<Config::BINDish> does to build them:

=item An empty class is created which will serve as the final version
=item Extension roles are punned and added as parents to the class
=item Then the base class is added as the last parent

The order in which the extensions are added is defined by the order and the way they're registered. The later added ones
serve as the earlier parents meaning that Raku's method call dispatching will have their methods invoked first.

There are two and a half ways to extend the parser. First is to use `is BINDish-grammar` or `is BINDish-actions` trait:

    unit module Config::BINDish::Ext1;
    use Config::BINDish;
    role Grammar is BINDish-grammar {
        token value:sym<mine> {
            ...
        }
    }
    role Actions is BINDish-actions {
        method value:sym<mine>($/) {
            ...
        }
    }

In this case the role they're applied to will be auto-registered with C<Config::BINDish>. When such extension is
contained by a module then it would be added when the module is C<use>d:

    use Config::BINDish::Ext1;
    use Config::BINDish::Ext2;

I<Note> that considering the order of C<use> statements, C<Ext2> will be able to override methods of C<Ext1>.

The specificifty of using the traits is that extensions declared this way will become application-wide available. So, even
if the extension module is used by a module used by the main code, the extension will be available to any instance of
C<Config::BINDish>.

B<Note:> We say that extensions registered with the traits are registered I<statically>.

The other 1.5 ways of adding the extensions are to use C<extend-grammar> and C<extend-actions> constructor arguments or
methods with the same names:

    my $cfg = Config::BINDish.new: :extend-grammar(ExtG1, ExtG2), :extend-actions(ExtA1, ExtA2);
    $cfg.extend-grammar(ExtG3)
        .extend-actions(ExtA3);

In this case extension roles don't need the traits applied. This way we call I<dynamic> registration.

The other specific of dynamic extensions is that they will go after the static ones. I.e. in the above examples
C<ExtG*> and C<ExtA*> will be positioned before C<Ext1> and C<Ext2> in the MRO order, prioritizing the former over the
latter ones.

Why is the above called I<1.5 ways>? Because the constructor eventually uses the C<extend-*> methods with corresponding
C<:extend-*> named attributes.

See also
L<C<Config::BINDish::Grammar>|BINDish/Grammar.md> and
L<C<Config::BINDish::Actions>|BINDish/Actions.md>.

=head1 ATTRIBUTES

=head2 L<C<Config::BINDish::Grammar::Strictness>|BINDish/Grammar/Strictness.md> C<$.strict>

The default grammar strictness mode. See L<C<Config::BINDish::Grammar::Strictness>|BINDish/Grammar/Strictness.md> documentation for details.

=head2 L<C<Pair:D>|https://docs.raku.org/type/Pair> C<@.blocks>, L<C<Pair:D>|https://docs.raku.org/type/Pair> C<@.options>

These two attributes contain user-defined (pre-declared) structure of the config file. More information about them can be found in
L<Config::BINDish::Grammar|BINDish/Grammar.md>
documentation, in
L<Pre-declaration|BINDish/Grammar.md#Pre-declaration>
section.

=head2 C<$.grammar>, C<$.actions>

The final grammar and actions class versions with all registered extensions applied. Both attributes are I<lazy> and
C<clearable> in terms of L<C<AttrX::Mooish>|https://modules.raku.org/dist/AttrX::Mooish>. It means that the
following is possible:

    say $cfg.grammar.^name; # Config::BINDish::Grammar...
    $cfg.extend-grammar(MyApp::GrammarMod);
    $cfg.clear-grammar;     # Actually, extend-grammar already does this. This line is here to demo the concept only.
    say $cfg.grammar.^name; # Config::BINDish::Grammar+{MyApp::GrammarMod}...

=head2 L<C<IO::Path>|https://docs.raku.org/type/IO::Path> C<$.file>

If C<read> method was called with C<:file<...>> argument then this attribute will hold corresponding C<IO::Path> object
for the file name.

=head2 L<C<Bool>|https://docs.raku.org/type/Bool> C<$.flat = False>

If set to C<True> then L<C<Config::BINDish::Actions>|BINDish/Actions.md>
will act in flattening mode.

=head2 C<$.top>

The top node produced by the grammar actions. I.e. it is the result of C<<$<TOP>.ast>> of the
L<C<Match>|https://docs.raku.org/type/Match> object produced by grammar's C<parse> method. For
L<C<Config::BINDish::Actions>|BINDish/Actions.md> it would be an instance of
L<C<Config::BINDish::AST::TOP>|BINDish/AST/TOP.md>.
But an extension can produce something to its taste which wouldn't be an AST whatsoever. The only requirement imposed
on the object stored in the attribute is to provide C<get> method.

This attribute C<handles> method C<get>.

=head2 C<$.match>

This attribute stores the L<C<Match>|https://docs.raku.org/type/Match> object produced by grammar's C<parse> method.

=head1 METHODS

=head2 C<extend-grammar( [email protected] )>, C<extend-actions( [email protected] )>

Interface to dynamically register extensions. Take a list of roles and records them as extensions. Then it clears
C<$.grammar> or C<$.actions> attributes, respectively. Both return C<self> to allow method call chaining.

=head2 C<build-grammar()>, C<build-actions()>

Methods used by L<C<AttrX::Mooish>|https://modules.raku.org/dist/AttrX::Mooish> to lazily initialize
C<$.grammar> and C<$.actions> attributes respectively.

C<multi method read(Config::BINDish:U: |args)>

Instantiates C<Config::BINDish> class and re-invokes C<read> method on the instance with C<args> capture.

=head2 C<multi method read(IO:D(Str:D) :$file, |args)>, C<multi method read(Str:D :$string, |args)>

Parses a configuration stored either in a C<$string> or in a C<$file> and returns the resulting
L<C<Match>|https://docs.raku.org/type/Match> object. The capture C<args> is passed over to the C<parse> method of C<$.grammar> alongside with
C<$.actions>, C<$.strict>, C<$.flat>, C<%.blocks>, and C<%.options> attributes.

The method returns what is returned by grammar's C<parse> method. The same value is then stored in C<$.match>
attribute.

=head3 C<multi get(...)>

Method is handled by C<$.top> attribute. See
L<C<Config::BINDish::AST::Blockish>|BINDish/AST/Blockish.md>
for detailed method description.

=head1 EXPORTS

By default this module exports only C<BINDish-grammar> and C<BINDish-actions> traits. But if C<use>d with either "op"
or "ascii-op" positional arguments it will also export _request operator_ in either unicode or ASCII form:

    use Config::BINDish <op>;
    my $cfg = Config::BINDish.new.read(...);
    say $cfg ∷ :top-block<name> ∷ "option";

Or:

    use Config::BINDish <ascii-op>;
    my $cfg = Config::BINDish.new.read(...);
    say $cfg :: :top-block<name> :: "option";

*Note* that C<::> (ASCII version) may conflict with Raku's name resolution. Though in my tests this never happened,
I would still prefer the unicode version over the ASCII.

More information about the operator can be found in L<C<Config::BINDish::Ops>|BINDish/Ops.md>.

=head1 SEE ALSO

L<README.md|../../../README.md>

L<C<Config::BINDish::Grammar>|BINDish/Grammar.md>,
L<C<Config::BINDish::AST>|BINDish/AST.md>,
L<C<Config::BINDish::Ops>|BINDish/Ops.md>,
L<C<Config::BINDish::Expandable>|BINDish/Expandable.md>,
L<C<Config::BINDish::INET>|BINDish/INET.md>

=AUTHOR Vadim Belman <[email protected]>

=end pod