## dist_cpan-ATROXAPER-LogP6.md
## Chunk 1 of 2
[![Build Status](https://github.com/atroxaper/p6-LogP6/workflows/Ubuntu/badge.svg)](https://github.com/atroxaper/p6-LogP6/actions/workflows/ubuntu.yml)
[![Build Status](https://github.com/atroxaper/p6-LogP6/workflows/Windows/badge.svg)](https://github.com/atroxaper/p6-LogP6/actions/workflows/windows.yml)

# NAME

`LogP6` is a fully customizable and fast logging library inspired by the idea of separating
logging and its configuration. You can use it not only in apps but even in your libraries.

# TABLE OF CONTENTS

* [NAME](#name)
* [SYNOPSIS](#synopsis)
* [DESCRIPTION](#description)
  * [Features](#features)
  * [Concepts](#concepts)
  * [Example](#example)
  * [Context](#context)
  * [Writer](#writer)
  * [Filter](#filter)
  * [Nested Diagnostic Context (NDC) and Mapped Diagnostic Context (MDC)](#nested-diagnostic-context-ndc-and-mapped-diagnostic-context-mdc)
  * [Logger](#logger)
  * [Logger Wrapper](#logger-wrapper)
    * [Synchronisation of configuration and Logger instance](#synchronisation-of-configuration-and-logger-instance)
* [CONFIGURATION](#configuration)
  * [Logger retrieve](#logger-retrieve)
  * [Factory subroutines](#factory-subroutines)
  * [Configuration file](#configuration-file)
  * [Writer configuration](#writer-configuration)
    * [WriterConf](#writerconf)
    * [Standard WriterConf](#standard-writerconf)
    * [Pattern](#pattern)
    * [Async writing](#async-writing)
    * [Writer factory subroutines](#writer-factory-subroutines)
    * [Writer configuration file](#writer-configuration-file)
  * [Filter configuration](#filter-configuration)
    * [FilterConf](#filterconf)
    * [Standard FilterConf](#standard-filterconf)
    * [Filter factory subroutines](#filter-factory-subroutines)
    * [Filter configuration file](#filter-configuration-file)
  * [Defaults](#defaults)
    * [Defaults factory subroutines](#defaults-factory-subroutines)
    * [Defaults configuration file](#defaults-configuration-file)
  * [Cliche](#cliche)
    * [Cliche factory subroutines](#cliche-factory-subroutines)
    * [Cliche configuration file](#cliche-configuration-file)
  * [Default logger](#default-logger)
  * [Change configuration](#change-configuration)
* [EXAMPLES](#examples)
  * [Use an external library that uses LogP6](#use-an-external-library-that-uses-logp6)
  * [Change console application verbosity level](#change-console-application-verbosity-level)
  * [Conditional log calls](#conditional-log-calls)
  * [Associate logs with concrete user](#associate-logs-with-concrete-user)
  * [Filter log by its content](#filter-log-by-its-content)
  * [Write one log in several outputs](#write-one-log-in-several-outputs)
  * [Write custom writer handle for your need](#write-custom-writer-handle-for-your-need)
* [BEST PRACTICE](#best-practice)
* [SEE OLSO](#seeolso)
* [ROADMAP](#roadmap)
* [AUTHOR](#author)
* [COPYRIGHT AND LICENSE](#copyright-and-license)

# SYNOPSIS

A logger system has to be as transparent as possible. At the same time, it has to be fully
customizable. It has to provide a possibility to change logging logic without changing any
line of code. It is amazing if you can use logger system during developing a library and its
user do not feel discomfort of it. `LogP6` logger library is all about that.

# DESCRIPTION

## Features

1. Possibility to change logger configuration and its behavior without touching the code;
2. Configuring from the code and/or configuration files;
3. Possibility to use any IO::Handler, even for async or database work;
4. Possibility to use multiple loggers in one app;
5. Possibility to use multiply IO::Handler's in one logger;
6. Flexible filter system;
7. Stack and Map entities associated with each thread and possibility to use values from
   it in the logger;
8. Pretty fast work. Using pre-calculation as much as possible -
   logger layout pattern is parsed only once, reuse current DateTime objects and so on;
9. Possibility to use logger while development (dynamically change logger settings in runtime),
   and during production work (maximum fast, without any lock, excepts possible IO::Handle
   implementation's).

## Concepts

* `writer` - an object which knows how and where logs must be written. In a simple case -
  which file and string format pattern will be used;
* `filter` - an object which knows which logs must be written. In simple case - logs with
  which levels are allowed to pass to the `writer`;
* `cliche` - template for creating `Logger`. Contains writers, filters, and other configurations
  for future Loggers;
* `logger` - instance created using configuration from the `cliche`. Just Logger with standard
  functionality like `info()` method;
* `logger trait` - string value describes the semantic purpose of concrete Logger. For example,
  the name of the class where the logger is used or the type of logged information (for example,
  'internal-audit-subsystem'). `LogP6` uses `trait` to create the new or get already created logger;
* `cliche's matcher` - unique field of the cliche. The field may be a literal string or regex.
  If the logger `trait` satisfies the `matcher` then the cliche will be used for creating the
  logger with the trait;
* `context` - associated with each Thread object, which contains information for logging like
  your final log message, the exception you specified, log level, current date, thread name, and
  so on. Context can be used for storing some specific information you and `LogP6` library need
  while logging.

## Example

Using logger:

```
use LogP6;                     				# use library in general mode

my \log = get-logger('audit'); 				# create or get logger with 'audit' trait
log.info('property ', 'foo', ' setted as ', 5);   # log string with concatenation
log.infof('property %s setted as %d', 'foo', 5);  # log sprintf like style

```

Configure the logger in code:

```
use LogP6 :configure;   # use library in configure mode
cliche(                 # create a cliche
  :name<cl>,            # obligatory unique cliche name
  :matcher<audit>,      # obligatory matcher
  grooves => (                                          # optional list of writer-filter pairs (or their names)
    writer(:pattern('%level| %msg'), :handle($*ERR)),   # create anonymous (w/o name) writer configuration
    filter(:level($debug))));                           # create anonymous (w/o name) filter configuration

```

Configure in the configuration file style (same as above):

```
{
  "writers": [{
    "type": "std",
    "name": "w",
    "pattern": "%level | %msg",
    "handle": { "type": "std", "path": "err" }
  }],
  "filters": [{
    "type": "std",
    "name": "f",
    "level": "debug"
  }],
  "cliches": [{
    "name": "cl",
    "matcher": "audit",
    "grooves": [ "w", "f" ]
  }]
}

```

## Context

LogP6 library adds an object associated with each Thread - `Logger Context` or just `context`.
You can work with the context directly in the `filter` subsystem or in custom `writer`
implementations. Also, `logger` has methods for working with `NDC` and `MDC` (see below in
[Logger](#logger)). For more information, please look at the methods' declarators in
`LogP6::Context`.

## Writer

`Writer` is responsible for writing all corresponding data to corresponding output in some format.
It has only one method:

* `write($context){...}` - this method has to take all necessary data from the specified `context`
  and use it for writing. Note: the specified context will change its data after the method call.
  Do not cache the context itself (for example, for asynchronous writing) but only its data.

## Filter

`Filter` is responsible for deciding to allow the corresponding `writer` to write a log or not.
It has three methods:

* `do-before($context){...}` - some code which decides allow the log to be pass to the writer
  or not. If it returns True then the log will be pass to the writer. Otherwise, the log will be
  discarded.
* `reactive-level(){}` - in most cases filtering can be done only by log level. This method
  returns a log level, which allows logger to call `do-before` method. If the filtering log's level
  importance is less then returned reactive level, then the log will be discarded without calling
  `do-before` method.
* `do-after($context){}` - any code which has to be executed after the writer work in case when
  `do-before` method returns True.

## Nested Diagnostic Context (NDC) and Mapped Diagnostic Context (MDC)

There are cases when we want to trace some information through a group of log messages, but not
only in one message, for example, user id, http session number, or so. In such cases, we have to
store the information somewhere, pass it through logic subs and methods, and pass to log methods
over and over again. Since the log system has to be separated from the main program logic, then we
need a special place to store the information. That place is a `Nested Diagnostic Context`
(`NDC`) - a stack structure and a `Mapped Diagnostic Context` (`MDC`) - a map structure. You can
push/pop values in `NDC` and put/remove values in `MDC`. The standard writer has special
placeholders for message pattern (see below in [Pattern](#pattern)) for put all values from `NDC`
or some kay associated value from `MDC` to the final log message string.

## Logger

A logger is an immutable object containing zero to several pairs of `writer` and `filter`
(`grooves`). For each time you want to log some message (with or without arguments), the logger
compiles message+arguments in one message string, updates the `context`, and goes through
`grooves` - call filter's methods and, if it passes, then ask a writer to write the message. The
writer takes all necessary information such as message, log level, NDC/MDC values, current
date-time, and so from the context.

Logger has the following methods:

* `trait()` - returns logger trait;
* `ndc-push($obj)`, `ndc-pop()`, `ndc-clean()` - work with `NDC`;
* `mdc-put($key, $obj)`, `mdc-remove($key)`, `mdc-clean()` - work with `MDC`;
* `dc-copy()`, `dc-restore($dc-copy)` - make copy of `NDC` and `MDC` and restore
  them from copy. The methods are useful when you want to share NDC and MDC values
  across multiple threads.
* `trace(*@args, :$x)`, `debug(*@args, :$x)`, `info(*@args, :$x)`,
  `warn(*@args, :$x)`, `error(*@args, :$x)`, `level($level, *@args, :$x)` -
  logging the arguments with specified importance log level. `:$x` is an optional
  exception argument. `@args` - data for logging. Elements of the array will be
  concatenated with empty string;
* `tracef(*@args, :$x)`, `debugf(*@args, :$x)`, `infof(*@args, :$x)`,
  `warnf(*@args, :$x)`, `errorf(*@args, :$x)`, `levelf($level, *@args, :$x)` -
  logging the arguments with specified importance log level. `:$x` is an optional
  exception argument. `@args` - data for logging. The first element is used as
  `sprintf` format and the rest element as `sprintf` args;
* `trace-on()`, `debug-on()`, `info-on()`, `warn-on()`, `error-on()`,
  `level-on($level)` - help methods to use as condition. The method will return
  `Any` in case the specified log level if forbidden now and will return special
  object with `log(*@args, :$x)` and `logf(*@args, :$x)` methods which can be used
  for log with asked log level (see [example](#conditional-log-calls)).

## Logger Wrapper

It is a system to wrap (or decorate) logger object into another and add additional logic.
You can describe `logger wrapper factory`, which will wrap any created `logger`.

### Synchronisation of configuration and Logger instance

An example of logger wrapper usage is synchronization a logger configuration and logger instance.
It may be useful in the case of development or debug session to change logger configuration
dynamically.

Since a logger object is immutable and cannot know about configuration changes it produced,
we need a logic that checks if the user updated the corresponding configuration and updates
the logger instance.

You can specify any wrapper for logger synchronization. There is a helper class
`LogP6::Wrapper::SyncAbstract` to create your synchronization wrapper.

For now, there are only two synchronization wrappers:

* `LogP6::Wrapper::SyncTime::Wrapper` - checks the new configuration change each `X` seconds;
* `LogP6::Wrapper::SyncEach::Wrapper` - checks the new configuration change each time you use
  the logger.

# CONFIGURATION

For working with the `LogP6` library, you need to `use LogP6;` module. Without any tags,
it provides only the `get-logger($trait)` sub for retrieving a logger. `:configure` tag
provides factory subroutines for configuring loggers from the code. Another option to configure
logger is by using a configuration file.

## Logger retrieve

To retrieve a logger, you need to use `LogP6` module and call the `get-logger($trait)`
sub with the logger trait you need. Example:

```
use LogP6;

my $log = get-logger('main');
# using $log ...

```

If you did not configure a `Cliche` for a specified logger trait ('main' in the example),
the default logger would be returned (see [Default logger](#default-logger)). In other cases,
the logger created by the cliche with matcher the trait satisfy will be returned.

## Factory subroutines

`LogP6` provides subroutines for configure loggers from the code dynamically. To get access to
them, you need to `use LogP6` with `:configure` tag. There are subroutines for configuring
`filters`, `writers`, `cliches`, and any default values like `writer pattern`, `logger wrapper`,
or so. Concrete subroutines will be described in the corresponding sections below. There is a
`get-logger-pure($trait)` sub to retrieve pure logger without any wrappers. Also, five variables
for five `LogP6::Level` enum values are exported as `$trace`, `$debug`, `$info`, `$warn` and
`$error`. Example:

```
use LogP6 :configure;

set-default-wrapper(LogP6::Wrapper::SyncTime::Wrapper.new(:60seconds)); # set default wrapper
set-default-level($debug);    # set default logger level as debug
my $log = get-logger('main'); # get wrapped logger
$log.debug('msg');
my $pure-log = get-logger-pure('main'); # this logger will not synchronize its configuration

```

## Configuration file

A better alternative (especially for production using) of configuration by factory subroutines
is a configuration file. You can specify a path to it through `LOG_P6_JSON` system environment
variable. In case the variable is empty, then standard path `./log-p6.json` will be used
(if it exists). Or you can initialize `LogP6` library using `init-from-file($config-path)` factory
subroutine.

The configuration file is a `json` formatted file. Example:

```
{
  "default-pattern": "%msg",
  "default-level":  "trace",
  "default-handle": { "type": "std", "path": "err" },
  "writers": [{ "type": "std", "name": "w" }],
  "filters": [{ "type": "std", "name": "f" }],
  "cliches": [{ "name": "c2", "matcher": "main" }]
}

```

The concrete format for concrete objects will be described in the corresponding sections below.

Some objects like `writers`, `wrappers` or so have a `type` filed. Each object has its own list
of available types. There is a type that can be used in any object - `custom`. It uses to describe
the factory method or class which will be used to produce the object. It requires additional
fields:

* `require` - the name of the module with factory method or class;
* `fqn-method` or `fqn-class` - the fully qualified name of method or class in `require` module;
* `args` - list of named arguments which will be passed to `fqn-method()` or `fqn-class.new()`;
* `positional` - list of positional arguments which will be passed to `fqn-method()` or `fqn-class.new()`.

For example, creating IO::Handle by `create-handle` subroutine in `MyModule` with arguments
`:path<out.txt>, :trait<rw>`:

```
{
  "default-handle": {
    "type": "custom",
    "require": "MyModule",
    "fqn-method": "MyModule::EXPORT::DEFAULT::&create-handle",
    "args": { "path": "out.txt", "trait": "rw" }
  }
}

```

## Writer configuration

### WriterConf

`WriterConf` is a configuration object which contains all necessary information and algorithm
for creating a concrete `writer` instance. For more information, please look at the methods'
declarators in `LogP6::WriterConf`.

### Standard WriterConf

Standard `WriterConf` (`LogP6::WriterConf::Std`) makes a writer that writes log message to
abstract `IO::Handle`. It has a `pattern` - string with special placeholders for values like `ndc`,
current `Thread` name, log message, etc. `Writer` will put all necessary values into `pattern` and
write it to handle. Also, standard `WriterConf` has boolean `auto-exceptions` property - if it is
`True`, then the placeholder for exception will be concatenated to the `pattern` automatically.
Form of the exception placeholder can be configured separately (see [Defaults](#defaults) and
[Cliche](#cliche)).

### Pattern

Pattern placeholders start with `%` symbol following the placeholder name. If placeholder has
arguments, they can be passed in curly brackets following placeholder name.

The pattern can have the following placeholders:

* `%trait`, `%trait{short=[package-delimeter]number}`, `%trait{sprintf=pattern}` - for the name of
  the logger trait. Additionally, you can specify one of two options of trait representation.
  `sprintf` option is useful for traits like `database`, `audit`, or so when you want to represent
  all traits with the same length. For example, `[%trait{sprintf=%s7}]` can be converted into
  `[ audit]`. `short` option is useful for traits like `Module::Packge1::Package2::Class`. You can
  specify package delimiter (instead of `::`) and how many packages will be displayed. For example,
  `%trait{short=[.]1` can be converted into `Class`, `%trait{short=[.]-1` - into
  `Packge1.Package2.Class` and `%trait{short=[.]2.4` - into `Modu.Pack.Package2.Class`. If `number`
  is a positive integer, then only `number` right elements will be displayed. If `number` is a
  negative integer, then `|number|` left elements will be deleted. If `number` is real, then left
  elements will be cut to fractional symbols;
* `%tid` - for current `Thread` id;
* `%tname` - for current `Thread` name;
* `%msg` - for log message;
* `%ndc` - for full NDC array joined by space symbol;
* `%mdc{obj_key}` - for MDC value with `obj_key` key;
* `%x{$msg $name $trace}` - for exception. String in curly brackets is used as
  subpattern. `$msg` - optional exception message, `$name` - optional exception
  name, `$trace` - optional exception stacktrace. For example,
  `'%x{($name "$msg") Trace: $trace}'` can be converted into
  `'(X::AdHoc "test exception") Trace: ...'`;
* `%level{WARN=W DEBUG=D ERROR=E TRACE=T INFO=I length=2}` - log importance level. By default,
  the logger will use the level name in upper case, but you can specify synonyms for all or part
  of them in curly brackets in format `<LEVEL_NAME>=<sysnonym>`. You can specify a fixed length
  of the log level name. Default length is 0 - write level as is. For example
  `'[%level{WARN=hmm ERROR=alarm length=5}]'` can be converted into `'[hmm ]'`, `'[alarm]'`,
  `'[INFO ]'`, `'[DEBUG]'`;
* `%color{TRACE=yellow DEBUG=green INFO=blue WARN=magenta ERROR=red}` - colorize log string after
  that placeholder. You can specify a color for any log level. The level you not specified color will
  use its default color (as in the example above). For example, `%color{ERROR=green}` means
  `%color{TRACE=yellow DEBUG=green INFO=blue WARN=magenta ERROR=green}`. You can use `yellow`,
  `green`, `blue`, `magenta`, `green` color names or color code (more
  [information](https://misc.flogisoft.com/bash/tip_colors_and_formatting). For example
  `%color{TRACE=35 DEBUG=30;48;5;82 INFO=green}`. You can use `%color` placeholder several times;
* `%color{reset}` or `%creset` - reset log string colorizing after that
  placeholder;
* `%date{$yyyy-$yy-$MM-$MMM-$dd $hh:$mm:$ss:$mss $z}` - current date and time.
  String in curly brackets is used as
  subpattern.
  * `$yyyy`, `$yy` - year in 4 and 2 digits format;
  * `$MM`, `$MMM` - month in 2 digits and short name format;
  * `$dd` - day in 2 digits format;
  * `$hh`, `$mm`, `$ss`, `$mss` - hours, minutes, seconds and milliseconds
  * `$z` - timezone
* `%framefile` - for log caller frame file name. The same as `callframe().file`
  in log call block;
* `%frameline` - for log caller frame file line. The same as `callframe().line`
  at the same log call line;
* `%framename` - for log caller frame code name. The same as
  `callframe().code.name` in log call block;

Note that using `%framefile`, `%frameline` or `%framename` in the pattern will
slow your logging because it requires several `callframe()` calls on each
resultative log call;

### Async writing

`LogP6` provides writer and handle implementation for asynchronous writing.

You can use `LogP6::Handle::Async.new(IO::Handle :$delegate!, Scheduler :$scheduler = $*SCHEDULER)`
as a handle which will schedule `WRITE` method call of `delegate` handle.

If it is not enough to wrap a handle, then you can wrap the whole writer. Use
`LogP6::WriterConf::Async.new(LogP6::WriterConf :$delegate!, Scheduler :$scheduler = $*SCHEDULER), :$name, Bool :$need-callframe)`
as writer configuration of another configuration. The final writer will schedule the `write` method
call of `delegate` created writer with a copy of the current `logger context`. If you miss a
`:name` parameter, then `delegate`'s name will be used. Pass boolean parameter `need-callframe` if
you plan to use callframe information in the wrapped writer. Note that using callframe will slow
your logging because it requires several `callframe()` calls on each resultative log call.

### Writer factory subroutines

`LogP6` module has the following subs for manage writers configurations:

* `get-writer(Str:D $name --> LogP6::WriterConf)` - gets writer with specified name;
* `writer(:$name, :$pattern, :$handle, :$auto-exceptions, :create, :update, :replace --> LogP6::WriterConf)` -
  create, update, or replace standard `WriterConf` with a specified name. If you want to `:update`
  only concrete fields in an already created configuration, then the rest fields will not be changed.
  In the case of `:replace`, the new configuration will be created and replaced the old one. You can
  create configuration without a name - then the configuration will not be stored but only returned
  to you. The method returns the old writer configuration (`:update`, `:replace`) and the new one
  (`:create`);
* `writer(LogP6::WriterConf:D $writer-conf, :create, :replace --> LogP6::WriterConf)` - save or
  replace any implementation of `WriterConf`. The configuration name will be retrieved from the
  `$writer-conf`. The method returns the old writer configuration (`:replace`) and the new one
  (`:create`);
* `writer(:$name, :$remove --> LogP6::WriterConf)` - remove and return a configuration with
  specified name.

### Writer configuration file

In the configuration file, writer configurations have to be listed in `writers` array. Only `std`
(for standard configuration) and `custom` types are supported.

In the case of standard configuration, all fields are optional excepts `name`. The handle can be:

* `file` type for output into a file. You can specify `path`,`append` (`True` by default), and
  `out-buffer` arguments;
* `std` type for output into `$*OUT` or `$*ERR`. You can specify `path` as `out` or `err`.
* `custom` type.

In the case of the `custom` writer type, the result writer configuration has to returns not empty
name.

Example:

```
{
  "writers": [
    {"type": "std", "name": "w1", "pattern": "%msg", "handle": {"type": "std", "path": "out"}},
    {"type": "std", "name": "w2", "handle": {"type": "file", "path": "log.txt", "append": false}},
    {"type": "custom", "require": "Module", "fqn-method": "Module::EXPORT::DEFAULT::&writer", "args": { "name": "w3" }}
  ]
}

```

## Filter configuration

### FilterConf

`Filter` creates by `FilterConf` - a configuration object which contains all necessary information
and algorithm for creating a concrete `filter` instance. For more information, please look at the
methods' declarators in `LogP6::FilterConf`.

### Standard FilterConf

Standard `FilterConf` (`LogP6::FilterConf::Std`) has array for `do-before` subs and array for
`do-after` subs. `Filter` made by standard `FilterConf` calls each `do-before` sub and stop at the
first `False` returned value. If all `do-before` subs returned `True`, then the filter's
`do-before` method returns `True`. The `do-after` works in the same way. Also, there is a
`first-level-check` property. If it is set to `True`, then the sub for checking log level will be
added automatically as the first element in `do-before` array; if the property set to `False` then
the sub will be added automatically as the last element in `do-before` array.

### Filter factory subroutines

`LogP6` module has the following subs for manage filters configurations:

* `get-filter(Str:D $name --> LogP6::FilterConf)` - gets filter with specified name
* `filter(:$name, :$level, :$first-level-check, List :$before-check, List :$after-check, :create, :update, :replace --> LogP6::FilterConf)` -
  create, update, or replace standard `FilterConf` with a specified name. If you want to `:update`
  only concrete fields in already created configuration then the rest fields will not be changed.
  In the case of `:replace`, the new configuration will be created and replaced the old one. You can
  create a configuration without a name - then the configuration will not be stored but only
  returned to you. The method returns the old filter configuration (`:update`, `:replace`) and the
  new one (`:create`);
* `level($level --> LogP6::FilterConf:D)` - the short form for `filter(:level($level), :create)`;
* `filter(LogP6::FilterConf:D $filter-conf, :create, :replace)` - save or replace any
  implementation of `FilterConf`. The configuration name will be retrieved from the `$filter-conf`.
  The method returns the old filter configuration (`:replace`) and the new one (`:create`);
* `filter(:$name, :$remove)` - remove and return a configuration with specified name.

### Filter configuration file

In the configuration file, filter configurations have to be listed in the `filters` array.
Only `std` (for standard configuration) and `custom` types are supported.

In the case of standard configuration, all fields are optional excepts `name`. `before-check`
and `after-check` are arrays with `custom` typed elements.

In the case of the `custom` filter type, the result filter configuration has to returns not
empty name.

Example:

```
{
  "filters": [
    {"type": "std", "name": "f1", "level": "error", "first-level-check": false},
    {"type": "std", "name": "f2", "level": "info", "before-check": [{ "require": "MyModule", "fqn-method": "MyModule::EXPORT::DEFAULT::&before-check" }]},
    {"type": "custom", "require": "MyModule", "fqn-class": "MyModule::MyFilter", "args": { "name": "f3" }}
  ]
}

```

## Defaults

Standard filters and writers have fields and options which affect their work. Some of them you
can specify in factory subroutines or configuration file fields. If such arguments are omitted,
then the default values of it will be used. Other fields and options cannot be setter this way.
For example, the pattern for an exception that will be concatenated to the main pattern in the
standard writer when `auto-exceptions` sets to `True`
(see [Standard WriterConf](#standard-writerconf)). Such properties have default values too. All
the defaults can be set through factory subroutines or fields in the configuration file.

Configuring default values is useful in case you what to avoid many boilerplate configurations.

### Defaults factory subroutines

There are the following factory subs for set defaults values:

* `set-default-pattern(Str:D $pattern)` - set default pattern for the standard `WriterConf`.
  Default value is `'[%date{$hh:$mm:$ss}][%level] %msg'`;
* `set-default-auto-exceptions(Bool:D $auto-exceptions)` - set default `auto-exceptions` property
  value for the standard `WriterConf`. Default value is `True`;
* `set-default-handle(IO::Handle:D $handle)` - set default handle for the standard `WriterConf`.
  Default value is `$*OUT`;
* `set-default-x-pattern(Str:D $x-pattern)` - set pattern for exception that will be concatenated
  to the main pattern in standard `WriterConf` in case `auto-exceptions` sets to `True`
  (see [Standard WriterConf](#standard-writerconf)). Default value is
  `'%x{ Exception $name: $msg' ~ "\n" ~ '$trace}'`
* `set-default-level(LogP6::Level:D $level)` - set default level for the standard `WriterConf`.
  Default value is `LogP6::Level::error`;
* `set-default-first-level-check(Bool:D $first-level-check)` - set default value
  of `first-level-check` property of the standard `FilterConf`
  (see [Standard FilterConf](#standard-filterconf)). Default value is `True`;
* `set-default-wrapper(LogP6::Wrapper $wrapper)` - set wrapper for loggers
  (see [Logger Wrapper](#logger-wrapper)). Default value is
  `LogP6::Wrapper::Transparent::Wrapper.new`.

### Defaults configuration file

You can configure default values in the configuration file through the following json fields of a
root object:

* `"default-pattern": <string>` - for default pattern for writers with `std` type;
* `"default-auto-exceptions": <boolean>` - for default `auto-exceptions` field value for writers
  with `std` type;
* `"default-handle": <handle>` - for default handle for writers with `std` type;
* `"default-x-pattern": <string>` - for default exceptions pattern for writers with `std` type;
* `"default-level": <level-name>` - for default level for filters with `std` type;
* `"default-first-level-check": <boolean>` - for `first-level-check` value for filters with `std`
  type;
* `"default-wrapper": <wrapper>` - for wrapper for loggers.

`Wrapper` can be:

* `time` type for `LogP6::Wrapper::SyncTime::Wrapper`. It takes obligatory `"seconds": <num>` and
  optional `"config-path": <string>` addition fields;
* `each` type for `LogP6::Wrapper::EachTime::Wrapper`. It takes optional `"config-path": <string>`
  addition field;
* `transparent` type for `LogP6::Wrapper::Transparent::Wrapper`;
* `custom` type.

## Cliche

`Cliche` is a template for creating Logger. Each `cliche` has `cliche's matcher` - literal or
regex field. When you what to get logger for some `logger trait`, then the logger system tries
to find a `cliche` with `matcher` the `trait` satisfies (by smartmatch). If there is more than
one such cliche, then the most recent created will be picked. The picked `cliche`'s content will
be used for making the new logger.

Cliche contains writers and filters configurations pairs called `grooves` and own `defaults` values
which overrides global `defaults` values (see [Defaults](#defaults)). You can use the same writer
and/or filter in several `grooves`. If the `grooves` list is empty or missed, the created logger will
drop all logs you pass to it;

### Cliche factory subroutines

`LogP6` module has the following subs for manage cliches configurations:

* `cliche(:$name!, :$matcher!, Positional :$grooves, :$wrapper, :$default-pattern, :$default-auto-exceptions, :$default-handle, :$default-x-pattern, :$default-level, :$default-first-level-check, :create, :$replace)` -
  create or replace cliche with specified name and matcher. All passed `defaults` overrides globals
  `defaults` in within the cliche. `$grooves` is a `Positional` variable with alternating listed
  `writers` and `filters`. `$grooves` will be flatted before analysis - you can pass into it a list
  of two elements lists or any structure you want. Elements of `$grooves` can be either name of
  already stored writers and filters, already stored writers and filters with names, or writers and
  filters without names. In the last case, the writer or filter will be stored with a generated UUID
  name automatically. The method returns the old cliche (`:replace`) and the new one (`:create`);
* `cliche(LogP6::Cliche:D $cliche, :create, :replace


## dist_cpan-ATROXAPER-LogP6.md
## Chunk 2 of 2
)` - save or replace cliche;
* `cliche(:$name!, :remove)` - remove and return a cliche with specified name.

### Cliche configuration file

In the configuration file, cliches have to be listed in the `cliches` array. It has the following
fields:

* `"name": <string>` - obligatory name of cliche;
* `"matcher": <string>` - cliche matcher. If the matcher value starts and ends with `/` symbol,
  then the matcher is interpreted as regex; in another case, it is a literal;
* `"grooves": [<writer1-name>, <filter1-name>, <writer2-name>, <filter2-name>, ... ]` - grooves,
  list of writers' and filters' names;
* defaults - the same fields with the same possible values as described in
  [Defaults configuration file](#defaults-configuration-file) excepts `default-wrapper` - you need
  to use the `wrapper` field name.

Example:

```
{
  "cliches": [{
    "name": "c1", "matcher": "/bo .+ om/", "grooves": [ "w1", "f1", "w2", "f1" ],
    "wrapper": { "type": "transparent" }, "default-pattern": "%level %msg"
  }]
}

```

## Default logger

In any way you configured your cliches by the factory routines or configuration file or did not
use non of them, the `default cliche` will be in the logger library. Default cliche corresponds
to the following configuration:
`cliche(:name(''), :matcher(/.*/), grooves => (writer(:name('')), filter(:name(''))))`. In other
words, default cliche has an empty string name, matches any trait, has only one groove with empty
(uses all defaults) writer with an empty string name and with empty (uses all defaults) filter with
an empty string name. It means, by default, you do not need to configure anything at all. But you
can change the default cliche or default writer and filter by factory subroutines or in the
configuration file. Note that if the `LogP6` library does not find cliche with matcher logger trait
satisfies, then an exception will be thrown.

## Change configuration

Sometimes you may need to change logger configuration in runtime execution. It can be simply done
by factory subroutines. After calling any factory subroutine, all loggers for already used
`logger traits` will be recreated, and you can get it by `get-logger($trait)` sub. If you already
got logger, use synchronization wrapper, then the wrapper will sync the logger himself correspond
to its algorithm.

Another way of change configuration is by using configuration file modification. Changes in the
configuration file will be detected only if you are already using any of the synchronization
wrappers (in `defaults` or one of `cliches`). After any change detection, all already configured
configuration will be dropped and created new from the file.

# EXAMPLES

Lets explore a few general use cases:

## Use an external library that uses LogP6

`LogP6` can be used during library development, and a user of the library wants entirely turn off
any logs from the library. Let's imagine that all library loggers' traits start with `LIBNAME`
letters. In this case, we can create a `cliche` with corresponding `matcher` and empty `grooves` -
all library logs will be dropped.

In `Raku`:

```
use LogP6 :configure;

cliche(:name('turn off LIBNAME'), :matcher(/^LIBNAME .*/), :wrapper(LogP6::Wrapper::Transparent::Wrapper.new));

```

Or in the configuration file:

```
{ "cliches": [{"name": "turn off LIBNAME", "matcher": "/^LIBNAME .*/", "wrapper": {"type": "transparent"}}] }

```

We use wrapper without synchronization (transparent) because we do not plan to change the library
loggers' configuration.

## Change console application verbosity level

Let's imagine we are writing a console application, and we want to add the flag `--verbose` for
getting a more detailed output. Lets using a particular logger for application console output
instead of using simple `say` and change filter level according to the user's choice:

In `Raku`:

```
use LogP6 :configure;

cliche(:name<output>, :matcher<say>, grooves => (
  writer(:pattern('%msg'), :handle($*OUT)),
  filter(:name<verbosity>, :level($info))
));

sub MAIN(Bool :$verbose) {
  filter(:name<verbosity>, :level($debug), :update) if $verbose;
  my $say = get-logger('say');
  $say.info('Greetings');
  $say.debugf('You set verbose flag to %s value', $verbose);
}

```

In that case, we do not need to use the configuration file. But if you want, then you can remove
the line with `cliche` creation and add the following configuration file:

```
{
  "writers": [{ "type": "std", "name": "say", "pattern": "%msg", "handle": { "type": "std", "path": "out" }}],
  "filters": [{ "type": "std", "name": "verbosity", "level": "info"}],
  "cliches": [{ "name": "output", "matcher": "say", "grooves": [ "say", "verbosity" ]}]
}

```

## Conditional log calls

Sometimes you may need to log information that required additional calculation. It is useful
to know whether the log will be written or not before the calculation. Logger's `-on` methods
were created especially for that. It will return a particular object (or Any) with `log` and
`logf` methods you can use to log with the corresponding log level. Please look at the example
below:

```
use LogP6 :configure;

# set logger allowed level as INFO
filter(:name(''), :level($info), :update);
my $log = get-logger('condition');
my %map;
my $str;
# ...

# to-json will not be called here, because .debug-on returned Any for now
.log(to-json(%map, :pretty, :sorted-keys)) with $log.debug-on;

# from-json will be called here, because .warn-on returned a little logger
# log will be with WARN level
.log(from-json($str)<key>) with $log.warn-on;

with $log.trace-on {
  # this block will not be executed for now
  my $user-id = retrive-the-first-user-id-from-db();
  # use logf method to use sprintf-style logs
  .logf('the first user id in the database is %d', $user-id);
}

# Be careful with '.?' operator. Sins it is not an operator but syntax-sugar
# to-json will be called in any case, but log will not be written for now.
$log.debug-on.?log(to-json(%map, :pretty, :sorted-keys));

```

## Associate logs with concrete user

Let's imagine we write a server application. Many users can connect to the server simultaneously
and do some action, which produces log messages in a log file. If some exception will be caught
and log, we want to reconstruct the user's execution flow to understand what went wrong. But
needful records in the log file will be alongside logs from other users' actions. In such cases,
we need to associate each log entry with some user id. Then we can grep the log file for the user
id. For that, use `MDC`.

In `Raku`:

```
use LogP6 :configure;

cliche(:name<logfile>, :matcher<server-log>, grooves => (
  writer(
    :pattern('[%date{$hh:$mm:$ss:$mss}][user:%mdc{user-id}]: %msg'),
    :handle('logfile.log'.IO.open)),
  level($info)
));

my $server-log = get-logger('server-log');

sub database-read() { # note we do not pass $user in the sub
  $server-log.info('read from database'); # [23:35:43:1295][user:717]: read from database
  # read
  CATCH { default {
    $server-log.error('database fail', :x($_)); # [23:35:44:5432][user:717]: database fail Exception X::AdHoc "database not found" <trace>
  }}
}

sub enter(User $user) {
  $server-log.mdc-put('user-id', $user.id);
  $server-log.info('connected');     # [23:35:43:1245][user:717]: connected

  database-read();

  $server-log.info('disconnected');  # [23:35:44:9850][user:717]: disconnected
  $server-log.mdc-remove('user-id'); # it is not necessary to remove 'user-id' value from MDC
}

```

The same configuration you can write in the configuration file:

```
{
  "writers": [{ "type": "std", "name": "logfile", "pattern": "[%date{$hh:$mm:$ss:$mss}][user:%mdc{user-id}]: %msg",
    "handle": { "type": "file", "path": "logfile.log" }}],
  "filters": [{ "type": "std", "name": "logfile", "level": "info"}],
  "cliches": [{ "name": "logfile", "matcher": "server-log", "grooves": [ "logfile", "logfile" ]}]
}

```

## Filter log by its content

Imagine we have an application that may write sensible content to log files, for example,
user passwords. And we want to drop such sensible logs. We can use a particular sub in `do-before`
action of log's `filter`.

In `Raku`:

```
unit module Main;
use LogP6 :configure;

sub drop-passwords($context) is export {...}

cliche(:name<sensible>, :matcher<log>, grooves => (
  writer(:pattern('%msg'), :handle('logfile.log'.IO.open)),
  filter(:level($info), before-check => (&drop-passwords))
));

sub drop-passwords($context) {
  return False if $context.msg ~~ / password /;
  # If you want to remove a password from the log entry instead of dropping it,
  # you can remove the password from the message and store it in the context like:
  #
  # $context.msg-set(remove-password($context.msg));
  True;
}

sub connect(User $user) {
  get-logger('log').infof('user with name %s and password %s connected', $user.id, $user.passwd);
}

```

The same configuration you can write in the configuration file:

```
{
  "writers": [{ "type": "std", "name": "writer", "pattern": "%msg",
    "handle": { "type": "file", "path": "logfile.log" }}],
  "filters": [{ "type": "std", "name": "pass-filter", "level": "info",
    "before-check": [{ "require": "Main", "fqn-method": "Main::EXPORT::DEFAULT::&drop-passwords" }]}],
  "cliches": [{ "name": "logfile", "matcher": "server-log", "grooves": [ "writer", "pass-filter" ]}]
}

```

## Write one log in several outputs

Let's imagine we have an application that works with several types of a database -- for example,
Oracle and SQLite. We want to log work with the databases. But we want to store Oracle related
logs in `oracle.log` and `database.log` files, and SQLite related records only in `database.log`.
In this case, we need a straightforward logger for SQLite related logs and another one (with two
grooves) for Oracle associated logs.

In `Raku`:

```
use LogP6 :configure;

set-default-pattern('%msg');
writer(:name<database>, :handle('database.log'.IO.open));
writer(:name<oracle>,   :handle(  'oracle.log'.IO.open));
filter(:name<filter>, :level($info));
cliche(:name<oracle>, :matcher<oracle>, grooves => ('database', 'filter', 'oracle', 'filter'));
cliche(:name<sqlite>, :matcher<sqlite>, grooves => ('database', 'filter'));

sub oracle-db-fetch() {
  get-logger('oracle').info('fetch data');
  # fetch
}

sub sqlite-db-fetch() {
  get-logger('sqlite').info('fetch data');
  # fetch
}

```

The same configuration you can write in the configuration file:

```
{
  "default-pattern": "%msg",
  "writers": [
    { "name": "database", "type": "std", "handle": { "type": "file", "path": "database.log"}},
    { "name": "oracle",   "type": "std", "handle": { "type": "file", "path": "oracle.log"  }}
  ],
  "filters": [{ "name": "filter", "type": "std", "level": "info" }],
  "cliches": [
    { "name": "oracle", "matcher": "oracle", "grooves": [ "database", "filter", "oracle", "filter" ]},
    { "name": "sqlite", "matcher": "sqlite", "grooves": [ "database", "filter" ]}
  ]
}

```

## Write logs in journald

Let's imagine you want to store logs in journald service. You can use `LogP6::Writer::Journald`
module for that. For more information, please look at the writer module README.
Example of configuration:

In `Raku`:

```
use LogP6 :configure;

writer(LogP6::WriterConf::Journald.new(
  # name, pattern and auto-exceptions as in standard writer
  :name<to-journald>, :pattern('%msg'), :auto-exeptions
  # which additional information must be written
  :use-priority, # write 'PRIORITY=' field to journald automatically
  :use-mdc       # write all MDC contend as field to journald in 'key=value' format
));

```

The same configuration you can write in the configuration file:

```
{"writers": [{
  "type": "custom",
  "require": "LogP6::WriterConf::Journald",
  "fqn-class": "LogP6::WriterConf::Journald",
  "args": {
    "name": "to-journald",
    "pattern": "%msg",
    "auto-exceptions": true,
    "use-priority": true,
    "use-mdc": true
  }
}]}

```

## Write custom writer handle for your need

Sometimes you may need to write log to some exotic place. In this case you will need to implement
your own `Writer` and its `WriterConf`. In simple cases, it would be enough to implement your own
`IO::Handle` for the standard writer. For example, there is no de-facto must-use library for
working with databases yet. That is why there is no particular writer for it in `LogP6`. But let's
try to write it now:

Imagine we decided to use `SQLite` database and `DB::SQLite` library. We can ask the standard
writer to prepare SQL insert expression for us. Therefore we can only write custom `IO::Handle`.
Fortunately, it is easy in `6.d` version:

```
unit module MyDBHandle;
use DB;
use DB::SQLite;

class DBHandle is IO::Handle {
  has Str $.filename is required;
  has DB $!db;

  submethod TWEAK() {
    self.encoding: 'utf8';
    # open database file and create table for logging
    $!db = DB::SQLite.new(:$!filename);
    $!db.execute('create table if not exists logs (date text, level text, log text)');
  }

  method WRITE(IO::Handle:D: Blob:D \data --> Bool:D) {
    # decode Blob data and execute. we expect the valid sql dml expression in data.
    $!db.execute(data.decode());
    True;
  }

  method close() { $!db.finish } # close database

  method READ(|) { #`[do nothing] }

  method EOF { #`[do nothing] }
}

```

It is all we need. Now we can write the `LogP6` configuration. In `Raku`:

```
use MyDBModule;
use LogP6 :configure;

writer(
  :name<db>,
  # pattern provides us a valid sql dml expression
  :pattern('insert into logs (date, level, log) values (\'%date\', \'%level\', \'%msg%x{ - $name $msg $trace}\')'),
  # handle with corresponding database filename
  handle => DBHandle.new(:filename<database-log.sqlite>),
  # turn off auto-exceptions because it will corrupt our sql dml expression
  :!auto-exceptions
);
cliche(:name<db>, :matcher<db>, grooves => ('db', level($trace)));

my $log-db = get-logger('db');

$log-db.info('database logging works well');

```

The same configuration you can write in the configuration file:

```
{
  "writers": [{
    "type": "std",
    "name": "db",
    "pattern": "insert into logs (date, level, log) values ('%date', '%level', '%msg%x{ - $name $msg $trace}'",
    "handle": {
      "type": "custom",
      "require": "MyDBModule",
      "fqn-class": "MyDBModule::DBHandle",
      "args": {
        "filename": "database-log.sqlite"
      }
    },
    "auto-exceptions": false
  }],
  "filters": [{ "name": "filter", "type": "std", "level": "trace" }],
  "cliches": [{
    "name": "db",
    "matcher": "db",
    "grooves": [ "db", "filter" ]
  }]
}

```

## Rollover log files

Log files tend to grow in size. There is `IO::Handle::Rollover` module to prevent such uncontrolled
growth. For example, you decided to store only 30MB of logs separated into three files for
convenience. For that, you only need to create a custom handle with an `open` routine like that:

In `Raku`:

```
use IO::Handle::Rollover;
my $handle = open("log.txt", :w, :rollover, :file-size<10M>, :3history-size);

```

The same initialization you can write in the configuration file:

```
{
...
  "handle": {
    "type": "custom",
    "require": "IO::Handle::Rollover",
    "fqn-method": "IO::Handle::Rollover::EXPORT::DEFAULT::&open",
    "positional": [ "log.txt" ],
    "args": {
      "w": true,
      "rollover": true,
      "file-size": "10M",
      "history-size": 3
    }
  }
...
}

```

You can use the handle as any other output handles, for example, in LogP6 writers. For more
information, see the documentation for the `IO::Handle::Rollover` module.

# BEST PRACTICE

Try to use good traits for your loggers. If you use loggers in your library, then probably using
one prefix in all your traits is the best option. It allows users of your library to manage your
loggers easily.

Try to choose a logger trait according to logger semantic or location. For example, you can use
`$?CLASS.^name` as a logger trait in any of your classes or traits like `database`,
`user management`, or so.

If you use logger within a class then make the logger be a class field like
`has $!log = get-logger('$?CLASS.^name');` If you use logger withing a subroutines logic then make
a special sub for retrieve logger like `sub log() { state $log = get-logger('trait'); }`. Then use
it like `log.info('msg');` It prevents any side effects caused by precompilation.

# SEE OLSO

* [LogP6::Writer::Journald](https://modules.raku.org/dist/LogP6-Writer-Journald:cpan:ATROXAPER)
* [IO::Handle::Rollover](https://modules.raku.org/dist/IO-Handle-Rollover:cpan:ATROXAPER)

# ROADMAP

* Add a `Cro::Transform` for using `LogP6` in `cro` applications.

# AUTHOR

Mikhail Khorkov [atroxaper@cpan.org](mailto:atroxaper@cpan.org)

Source can be located at: [github](https://github.com/atroxaper/p6-LogP6). Comments and Pull Requests
are welcome.

# COPYRIGHT AND LICENSE

Copyright 2020 Mikhail Khorkov

This library is free software; you can redistribute it and/or modify it under
the Artistic License 2.0.


## dist_zef-librasteve-App-Crag.md
[![](https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg)](https://opensource.org/licenses/Artistic-2.0)

# raku App::Crag

Calculator using RAku Grammars for the command line

NB. The syntax for measures changed from `:<...>` to `^<...>` to avoid clashes with the colon

## Install

```
zef install --verbose App::Crag

```

## Usage

```
crag [--help] <cmd>

```

## Examples

```
[1] > crag 'say (1.6km / (60 * 60 * 1s)).in: <mph>'               #0.99mph
[2] > crag '$m=95kg; $a=^<9.81 m/s^2>; $f=$m*$a; say $f'          #931.95N
[3] > crag 'say ^<12.5 ft ±3%> .in: <mm>'                         #3810mm ±114.3
[4] > crag '$λ=2.5nm; $ν=c/$λ; say $ν.norm'                       #119.91PHz
[5] > crag '$c=^<37 °C>; $f=^<98.6 °F>; say $f cmp $c'            #Same
[6] > crag 'say @physics-constants-symbols.join: "\n"'            # ...

```

* crag goes `subst( '^<' => '♎<' )`
* crag goes `sub r( $x = 0.01 ) { $Physics::Measure::round-val = $x }`
* crag goes `subst( '§|(.+?)|' => 'Q|$0|.AST.EVAL' )`
* crag goes `subst( (\w)’^’ => $0\c[Combining Right Arrow Above] )`
* `echo RAKULANG='en_US'` for us gallons, pints, mpg, etc.

## More Info

* <https://github.com/librasteve/raku-Physics-Measure.git>
* <https://github.com/librasteve/raku-Physics-Unit.git>
* <https://github.com/librasteve/raku-Physics-Error.git>
* <https://github.com/librasteve/raku-Physics-Constants.git>
* <https://github.com/raku-community-modules/Time-Duration-Parser>
* <https://github.com/raku-community-modules/Slang-Roman>
* <https://github.com/antononcube/Raku-Chemistry-Stoichiometry>

### Copyright

copyright(c) 2023-2024 Henley Cloud Consulting Ltd.


## dist_zef-CIAvash-WebService-TMDB.md
# NAME

WebService::TMDB - A module for accessing [The Movie Database](https://themoviedb.org) data.

# SYNOPSIS

```
use WebService::TMDB;

my $tmdb = WebService::TMDB.new: :access_token<1234>;

my $search = $tmdb.search_movie: query => 'legend of 1900';
say $search.results[0].title;

my $movie = $tmdb.movie: 10775, :append(['credits']);
say $movie.title;
say $movie.credits.cast[0].name;

my $find = $tmdb.find_by: ExternalSources::IMDB, :external_id<tt4540710>;
say $find.movie_results[0].title;

```

# INSTALLATION

You need to have [Raku](https://www.raku-lang.ir/en) and [zef](https://github.com/ugexe/zef), then run:

```
zef install "WebService::TMDB:auth<zef:CIAvash>"

```

or if you have cloned the repo:

```
zef install .

```

# TESTING

```
prove -ve 'raku -I.' --ext rakutest

```

# DESCRIPTION

WebService::TMDB is a module for interfacing with the [TMDB API](https://developers.themoviedb.org/3/). An access token is required to use the API.

# ATTRIBUTES/METHODS

### has Str $.access\_token

Access token required by themoviedb.org

### has WebService::TMDB::Role::Request $.request handles('language', 'set\_language')

An object for making requests to themoviedb.org

### method find\_by

```
method find_by(
    WebService::TMDB::ExternalSources(Str) $external_source,
    Str :$external_id!
) returns WebService::TMDB::Find

```

Find movie, TV, TV season and episode by external ID

### method movie

```
method movie(
    Int $id,
    :@append where { ... }
) returns WebService::TMDB::Movie

```

Get a specific movie by its ID. Credits and external IDs can be appended to response.

### method movie\_credits

```
method movie_credits(
    Int $id
) returns WebService::TMDB::Credits

```

Get credits for a movie

### method movie\_external\_ids

```
method movie_external_ids(
    Int $id
) returns WebService::TMDB::ExternalID

```

Get external IDs for a movie

### method movie\_upcoming

```
method movie_upcoming(
    *%params
) returns WebService::TMDB::Movie::Upcoming

```

Get upcoming movies

### method movie\_now\_playing

```
method movie_now_playing(
    *%params
) returns WebService::TMDB::Movie::Upcoming

```

Get movies that are in theatres

### method search\_movie

```
method search_movie(
    *%params
) returns WebService::TMDB::Movie::Search

```

Search for movies

### method discover\_movie

```
method discover_movie(
    *%params
) returns WebService::TMDB::Movie::Search

```

Discover movies

### method tv

```
method tv(
    Int $id,
    :@append where { ... }
) returns WebService::TMDB::TV

```

Get a TV show by its ID. Credits and external IDs can be appended to response.

### method season

```
method season(
    Int :$tv,
    Int :$season,
    :@append where { ... }
) returns WebService::TMDB::TV::Season

```

Get a TV season. Credits can be appended to response.

### method episode

```
method episode(
    Int :$tv,
    Int :$season,
    Int :$episode,
    :@append where { ... }
) returns WebService::TMDB::TV::Episode

```

Get a TV episode. Credits can be appended to response.

### method tv\_airing\_today

```
method tv_airing_today(
    *%params
) returns WebService::TMDB::TV::Search

```

Get TV shows that are airing today

### method tv\_on\_the\_air

```
method tv_on_the_air(
    *%params
) returns WebService::TMDB::TV::Search

```

Get TV shows that are currently on the air

### method search\_tv

```
method search_tv(
    *%params
) returns WebService::TMDB::TV::Search

```

Search TV shows

### method discover\_tv

```
method discover_tv(
    *%params
) returns WebService::TMDB::TV::Search

```

Discover TV shows

### method person

```
method person(
    Int $id,
    :@append where { ... }
) returns WebService::TMDB::Person

```

Get a person by its ID. Movie & TV credits and external IDs can be appended to response.

### method person\_movie\_credits

```
method person_movie_credits(
    Int $id
) returns WebService::TMDB::Person::MovieCredits

```

Get a person's movie credits

### method person\_tv\_credits

```
method person_tv_credits(
    Int $id
) returns WebService::TMDB::Person::TVCredits

```

Get a person's TV credits

### method person\_external\_ids

```
method person_external_ids(
    Int $id
) returns WebService::TMDB::ExternalID

```

Get a person's external IDs

### method search\_person

```
method search_person(
    *%params
) returns WebService::TMDB::Person::Search

```

Search people

### method genre\_movie\_list

```
method genre_movie_list() returns WebService::TMDB::Genre::List

```

Get genre list for movies

### method genre\_tv\_list

```
method genre_tv_list() returns WebService::TMDB::Genre::List

```

Get genre list for TV shows

### method certification\_movie\_list

```
method certification_movie_list() returns WebService::TMDB::CertificationList

```

Get certification list for movies

### method certification\_tv\_list

```
method certification_tv_list() returns WebService::TMDB::CertificationList

```

Get certification list for TV shows

### method configuration

```
method configuration() returns WebService::TMDB::Configuration

```

Get themoviedb.org configuration. themoviedb.org recommends caching this data and checking for updates every few days.

# ERRORS

`HTTP::UserAgent` module is used with exception throwing enabled. So exceptions will be thrown in case of non-existent resources, out of range values, etc. See <http://modules.raku.org/dist/HTTP::UserAgent>.

When an exception of type `X::HTTP::Response` is caught, and the response received from themoviedb.org contains an error message, an attribute named `tmdb_status_message` or `tmdb_errors` will be added to the exception containing that error message.

# ENVIRONMENT

Some live tests will run when `NETWORK_TESTING` environment variable is set.

# REPOSITORY

<https://codeberg.org/CIAvash/WebService-TMDB>

# BUGS

<https://codeberg.org/CIAvash/WebService-TMDB/issues>

# AUTHOR

Siavash Askari Nasr - <https://siavash.askari-nasr.com>

# COPYRIGHT AND LICENSE

Copyright © Siavash Askari Nasr

WebService::TMDB is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

WebService::TMDB is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with WebService::TMDB. If not, see <http://www.gnu.org/licenses/>.


## unwrap.md
unwrap

Combined from primary sources listed below.

# [In Routine](#___top "go to top of document")[§](#(Routine)_method_unwrap "direct link")

See primary documentation
[in context](/type/Routine#method_unwrap)
for **method unwrap**.

```raku
method unwrap($wraphandle)
```

Restores the original routine after it has been wrapped with [wrap](/routine/wrap). While the signature allows any type to be passed, only the [`Routine::WrapHandle`](/type/Routine/WrapHandle) type returned from `wrap` can usefully be.


## d.md
d

Combined from primary sources listed below.

# [In IO::Special](#___top "go to top of document")[§](#(IO::Special)_method_d "direct link")

See primary documentation
[in context](/type/IO/Special#method_d)
for **method d**.

```raku
method d(IO::Special:D: --> False)
```

The 'directory' file test operator, always returns `False`.

# [In IO::Path](#___top "go to top of document")[§](#(IO::Path)_method_d "direct link")

See primary documentation
[in context](/type/IO/Path#method_d)
for **method d**.

```raku
method d(IO::Path:D: --> Bool:D)
```

Returns `True` if the invocant is a path that exists and is a directory. The method will [`fail`](/routine/fail) with [`X::IO::DoesNotExist`](/type/X/IO/DoesNotExist) if the path points to a non-existent filesystem entity.


## precompilationrepository.md
role CompUnit::PrecompilationRepository

CompUnit::PrecompilationRepository

This class provides stubs for each of the following methods. The methods are provided by sub-classes, such as `PrecompilationRepository::File`. Sub-classes are implementation dependent.

# [Methods](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#Methods "direct link")

## [method new-unit](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_new-unit "direct link")

```raku
method new-unit(| --> CompUnit::PrecompilationUnit:D)
{ ... }
```

Prepare a new implementation specific PrecompilationUnit for storage

## [method load-unit](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_load-unit "direct link")

```raku
method load-unit(CompUnit::PrecompilationId $compiler-id,
            CompUnit::PrecompilationId $precomp-id)
{ ... }
```

Load the precompilation identified by the pairing of the specified compiler and precompilation ID.

## [method load-repo-id](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_load-repo-id "direct link")

```raku
method load-repo-id(CompUnit::PrecompilationId $compiler-id,
            CompUnit::PrecompilationId $precomp-id)
{ ... }
```

Return the repository id for which the specified precomp file's dependencies have been validated

## [method store-file](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_store-file "direct link")

```raku
method store-file(CompUnit::PrecompilationId $compiler-id,
             CompUnit::PrecompilationId $precomp-id,
             IO::Path:D $path,
             :$extension = '')
{ ... }
```

Store the file at the specified path in the precompilation store, under the given compiler ID and precompilation ID.

## [method store-unit](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_store-unit "direct link")

```raku
method store-unit(CompUnit::PrecompilationId $compiler-id,
             CompUnit::PrecompilationId $precomp-id,
             CompUnit::PrecompilationUnit $unit)
{ ... }
```

Store the given precompilation unit in the precompilation store under the given compiler ID and precompilation ID.

## [method store-repo-id](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_store-repo-id "direct link")

```raku
method store-repo-id(CompUnit::PrecompilationId $compiler-id,
             CompUnit::PrecompilationId $precomp-id,
             :$repo-id!)
{ ... }
```

Store the given repo-id for a precompilation under the given compiler ID and precompilation ID.

## [method delete](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_delete "direct link")

```raku
method delete(CompUnit::PrecompilationId $compiler-id,
              CompUnit::PrecompilationId $precomp-id)
{ ... }
```

Delete an individual precompilation.

## [method delete-by-compiler](#role_CompUnit::PrecompilationRepository "go to top of document")[§](#method_delete-by-compiler "direct link")

```raku
method delete-by-compiler(CompUnit::PrecompilationId $compiler-id)
{ ... }
```

Delete all precompilations for a particular compiler.


## telemetry.md
class Telemetry

Collect performance state for analysis

```raku
class Telemetry { }
```

**Note:**  This class is a Rakudo-specific feature and not standard Raku.

On creation, a `Telemetry` object contains a snapshot of various aspects of the current state of the virtual machine. This is in itself useful, but generally one needs two snapshots for the difference (which is a [`Telemetry::Period`](/type/Telemetry/Period) object).

The Telemetry object is really a collection of snapshots taken by different "instruments". By default, the [`Telemetry::Instrument::Usage`](/type/Telemetry/Instrument/Usage) and [`Telemetry::Instrument::ThreadPool`](/type/Telemetry/Instrument/ThreadPool) instruments are activated.

The `Telemetry` (and [`Telemetry::Period`](/type/Telemetry/Period)) object also [`Associative`](/type/Associative). This means that you can treat a Telemetry object as a read-only [`Hash`](/type/Hash), with all of the data values of the instruments as keys.

You can determine which instruments `Telemetry` should use by setting the `$*SAMPLER` dynamic variable, which is a [`Telemetry::Sampler`](/type/Telemetry/Sampler) object.

Currently, the following instruments are supported by the Rakudo core:

* Telemetry::Instrument::Usage

Provides (in alphabetical order): `cpu`, `cpu-sys`, `cpu-user`, `cpus`, `id-rss`, `inb`, `invcsw`, `is-rss`, `ix-rss`, `majf`, `max-rss`, `minf`, `mrcv`, `msnd`, `nsig`, `nswp`, `volcsw`, `outb`, `util%` and `wallclock`. For complete documentation of the meaning of these data values, see [`Telemetry::Instrument::Usage`](/type/Telemetry/Instrument/Usage).

* Telemetry::Instrument::Thread

Provides (in alphabetical order): `tad`, `tcd`, `thid`, `tjd`, `tsd` and `tys`. For complete documentation of the meaning of these data values, see [`Telemetry::Instrument::Thread`](/type/Telemetry/Instrument/Thread).

* Telemetry::Instrument::ThreadPool

Provides (in alphabetical order): `atc`, `atq`, `aw`, `gtc`, `gtq`, `gw`, `s`, `ttc`, `ttq` and `tw`. For complete documentation of the meaning of these data values, see [`Telemetry::Instrument::ThreadPool`](/type/Telemetry/Instrument/ThreadPool).

* Telemetry::Instrument::AdHoc

Does not provide any data by itself: one must indicate which variables are to be monitored, which will then become available as methods with the same name on the instrument.

## [routine T](#class_Telemetry "go to top of document")[§](#routine_T "direct link")

```raku
sub T()
```

Shortcut for `Telemetry.new`. It is exported by default. Since the `Telemetry` class also provides an [`Associative`](/type/Associative) interface, one can easily interpolate multiple values in a single statement:

```raku
use Telemetry;
say "Used {T<max-rss cpu>} (KiB CPU) so far";

```

## [routine snap](#class_Telemetry "go to top of document")[§](#routine_snap "direct link")

```raku
multi snap(--> Nil)
multi snap(Str:D $message --> Nil)
multi snap(Str $message = "taking heap snapshot...", :$heap!)
multi snap(@s --> Nil)
```

The `snap` subroutine is shorthand for creating a new `Telemetry` object and pushing it to an array for later processing. It is exported by default. From release 2021.12, it returns the filename it's storing the snapshots in the case it's provided with a `:$heap` associative parameter.

```raku
use Telemetry;
my @t;
for ^5 {
    snap(@t);
    # do some stuff
    LAST snap(@t);
}

```

If no array is specified, it will use an internal array for convenience.

## [routine snapper](#class_Telemetry "go to top of document")[§](#routine_snapper "direct link")

```raku
sub snapper($sleep = 0.1, :$stop, :$reset --> Nil)
```

The `snapper` routine starts a separate thread that will call `snap` repeatedly until the end of program. It is exported by default.

By default, it will call `snap` every **0.1** second. The only positional parameter is taken to be the delay between `snap`s.

Please see the [snapper module](#module_snapper) for externally starting a snapper without having to change the code. Simply adding `-Msnapper` as a command line parameter, will then start a snapper for you.

## [routine periods](#class_Telemetry "go to top of document")[§](#routine_periods "direct link")

```raku
multi periods( --> Seq)
multi periods(@s --> Seq)
```

The `periods` subroutine processes an array of `Telemetry` objects and generates a [`Seq`](/type/Seq) of [`Telemetry::Period`](/type/Telemetry/Period) objects out of that. It is exported by default.

```raku
.<cpu wallclock>.say for periods(@t);

# OUTPUT:
# ====================
# (164 / 160)
# (23 / 21)
# (17 / 17)
# (15 / 16)
# (29 / 28)

```

If no array is specified, it will use the internal array of `snap` without parameters **and** will reset that array upon completion (so that new `snap`s can be added again).

```raku
use Telemetry;
for ^5 {
    snap;
    LAST snap;
}
say .<cpu wallclock>.join(" / ") for periods;

# OUTPUT:
# ====================
# 172 / 168
# 24 / 21
# 17 / 18
# 17 / 16
# 27 / 27

```

If only one `snap` was done, another `snap` will be done to create at least one [`Telemetry::Period`](/type/Telemetry/Period) object.

## [routine report](#class_Telemetry "go to top of document")[§](#routine_report "direct link")

```raku
multi report(:@columns, :$legend, :$header-repeat, :$csv, :@format)
```

The `report` subroutine generates a report about an array of `Telemetry` objects. It is exported by default. These can have been created by regularly calling `snap`, or by having a [snapper](/routine/snapper) running. If no positional parameter is used, it will assume the internal array to which the parameterless `snap` pushes.

Below are the additional named parameters of `report`.

* `:columns`

Specify the names of the columns to be included in the report. Names can be specified with the column name (e.g. `gw`). If not specified, defaults to what is specified in the `RAKUDO_REPORT_COLUMNS` environment variable. If that is not set either, defaults to:

｢text｣ without highlighting

```

```

wallclock util% max-rss gw gtc tw ttc aw atc

```

```

* `:header-repeat`

Specifies after how many lines the header should be repeated in the report. If not specified, defaults to what is specified in the `RAKUDO_REPORT_HEADER_REPEAT` environment variable. If that is not set either, defaults to 32.

* `:legend`

Specifies whether a legend should be added to the report. If not specified, defaults to what is specified in the `RAKUDO_REPORT_LEGEND` environment variable. If that is not set either, defaults to True.

If there are `snap`s available in the internal array at the end of the program, then `report` will be automatically generated and printed on `STDERR`.

## [module snapper](#class_Telemetry "go to top of document")[§](#module_snapper "direct link")

Start a thread taking repeated system state snapshots.

This module contains no subroutines or methods or anything. It is intended as a shortcut for starting the [snapper](/routine/snapper) subroutine of the `Telemetry` module, allowing taking snapshots of the execution of a program without needing to change the program. Simple loading the module with `-Msnapper` will do all that is needed to start the snapper, and have a report printed on STDERR upon completion of the program.

The `RAKUDO_SNAPPER` environment variable can be set to indicate the time between snapshots. If not specified, it will default to **0.1** seconds.

The `snapper` module assumes an orderly shutdown of the process. Killing the process (for instance by pressing Control-c) will **not** produce a report.

## [module safe-snapper](#class_Telemetry "go to top of document")[§](#module_safe-snapper "direct link")

Available as of the 2021.09 release of the Rakudo compiler.

This module provides a safe alternative to the `snapper` module: killing a process by pressing Control-c **will** produce a report. It is able to do so by installing a [signal handler](/type/Supply#sub_signal), which **may** interfere with normal functioning of interactive programs.

Killing a process in any other way, will **not** produce a report.

# [Typegraph](# "go to top of document")[§](#typegraphrelations "direct link")

Type relations for `Telemetry`

raku-type-graph

Telemetry
Telemetry

Any
Any

Telemetry->Any

Mu
Mu

Any->Mu

Associative
Associative

Telemetry::Period
Telemetry::Period

Telemetry::Period->Telemetry

Telemetry::Period->Associative

[Expand chart above](/assets/typegraphs/Telemetry.svg)


## dist_zef-jonathanstowe-Doublephone.md
# Doublephone

Implementation of the Double Metaphone phonetic encoding algorithm.

## Synopsis

```

use Doublephone;

say double-metaphone("SMITH"); # (SM0 XMT)
say double-metaphone("SMIHT"); # (SMT XMT)

```

## Description

This implements the [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone#Double_Metaphone)
algorithm which can be used to match similar sounding words. It is an improved version of
Metaphone (which in turn follows on from soundex,) and was first described by Lawrence Philips
in the June 2000 issue of the C/C++ Users Journal.

It differs from some other similar algorithms in that a primary and secondary code are returned
which allows the comparison of words (typically names,) with some common roots in different languages
as well as dealing with ambiguities. So for instance "SMITH", "SMYTH" and "SMYTHE" will yield
(SM0 XMT) as the primary and secondary, whereas "SCHMIDT", "SCHMIT" will yield (XMT SMT) so if a
"cross language" comparison is required then either of the primary or secondary codes can be matched
to the target primary or secondary code - this will also deal with, for example, transpositions in
typed names.

This is basically a Raku binding to the original C implementation I extracted from the Perl 5
[Text::DoubleMetaphone](https://metacpan.org/release/Text-DoubleMetaphone).

The algorithm itself isn't designed for unicode strings and making something that is is
probably best left to another module using a different technique.

Though this is described as a "phonetic" encoding it is only approximately so, rather it is
optimised for comparison and not as a guide to how something might be pronounced.

## Installation

If you have a working installation of Rakudo with `zef` installed then you should be able to install this with either:

```
zef install Doublephone

# or from a local clone

zef install .

```

Though other installers may become available that should work equally.

## Support

Almost all of the functionality of this is in the C library which has
a fairly long heritage, so is less likely to be buggy than the way in
which I am using it. Please feel free to report any bugs/send patches
or just make suggestions to <https://github.com/jonathanstowe/Doublephone/issues>

I would also like more tests for the correct output if anyone finds a good
data source for these.

# Licence and Copyright

This is free software, please see the <LICENCE> file in the distribution.

© Jonathan Stowe, 2016 - 2021

The C portions from Text::DoubleMetaphone have the following copyright text:

Copyright 2000, Maurice Aubrey [maurice@hevanet.com](mailto:maurice@hevanet.com).
All rights reserved.

This code is based heavily on the C++ implementation by
Lawrence Philips and incorporates several bug fixes courtesy
of Kevin Atkinson [kevina@users.sourceforge.net](mailto:kevina@users.sourceforge.net).

This module is free software; you may redistribute it and/or
modify it under the same terms as Perl itself.


## now.md
now

Combined from primary sources listed below.

# [In Terms](#___top "go to top of document")[§](#(Terms)_term_now "direct link")

See primary documentation
[in context](/language/terms#term_now)
for **term now**.

Returns an [`Instant`](/type/Instant) object representing the current time. It includes [leap seconds](https://en.wikipedia.org/wiki/Leap_second) and as such is a few dozen seconds larger than [time](/language/terms#term_time):

```raku
say (now - time).Int; # OUTPUT: «37␤»
```

# [In DateTime](#___top "go to top of document")[§](#(DateTime)_method_now "direct link")

See primary documentation
[in context](/type/DateTime#method_now)
for **method now**.

```raku
method now(:$timezone = $*TZ, :&formatter --> DateTime:D)
```

Creates a new `DateTime` object from the current system time. A custom [formatter](/routine/formatter) and [timezone](/routine/timezone) can be provided. The `:$timezone` is the offset **in seconds** from [GMT](https://en.wikipedia.org/wiki/Greenwich_Mean_Time) and defaults to the value of [`$*TZ` variable](/language/variables#index-entry-%24*TZ).

```raku
say DateTime.now; # OUTPUT: «2018-01-08T13:05:32.703292-06:00␤»
```

Note that one may use the methods shown below chained to the `.now` to easily express current values, e.g.,

```raku
say DateTime.now.year; # OUTPUT: «2018␤»
```


## dist_zef-lizmat-Code-Coverable.md
[![Actions Status](https://github.com/lizmat/Code-Coverable/actions/workflows/linux.yml/badge.svg)](https://github.com/lizmat/Code-Coverable/actions) [![Actions Status](https://github.com/lizmat/Code-Coverable/actions/workflows/macos.yml/badge.svg)](https://github.com/lizmat/Code-Coverable/actions) [![Actions Status](https://github.com/lizmat/Code-Coverable/actions/workflows/windows.yml/badge.svg)](https://github.com/lizmat/Code-Coverable/actions)

# NAME

Code::Coverable - Produce overview of coverable lines of code

# SYNOPSIS

```
use Code::Coverable;

say coverable-lines($path.IO);  # (3 5 11 24)

say coverable-lines($source);   # (7 9 23 25)

my @coverables = coverables(@paths);

```

# DESCRIPTION

Code::Coverable contains logic for producing code coverage reports.

This is still a work in progress. At the moment it will only export subroutines and install a script "coverage-lines".

# SUBROUTINES

## coverable-lines

```
say coverable-lines($path.IO);  # (3 5 11 24)

say coverable-lines($source);   # (7 9 23 25)

```

The `coverable-lines` subroutine takes a single argument which is either a `IO::Path` or a `Str`, uses its contents (slurped in case of an `IO::Path`), tries to build an AST from that and returns a sorted list of line numbers that appear to have coverable code.

## coverables

```
for coverables(@targets) -> $cc {
    say $cc.target;
    say $cc.key;
    say $cc.line-numbers;
    say $cc.source;
}

```

The `coverables` subroutine takes any number of positional arguments, each of which is assumed to be a target specification, which can be:

* a `Str` specification of a Raku source file
* an `IO::Path` object specifying a Raku source file
* a `use` target (identity) such as "Foo::Bar:ver<0.0.2+>"

It returns a `Seq` of `Code::Coverable` objects. Note that it **is** possible that even a single target produces more than one `Code::Coverable` object, if the source of the target has used `#line 42 filename` directives.

```
for coverables("String::Utils", :repo<.>, :raw) -> $cc {
    say $cc.target;
    say $cc.key;
    say $cc.line-numbers;
    say $cc.source;
}

```

If `use` targets (identities) are specified, a `:repo` named argument can be specified to indicate the repository to be used, which can be an object of type:

* Str - indicate a path for a ::FileSystem repo, just as with -I.
* IO::Path - indicate a path for a ::FileSystem repo
* CompUnit::Repository - the actual repo to use

The default is to use the current `$*REPO` setting to resolve any identity given.

The `raw` named argument can be used to indicate that no heuristics should be applied to mark lines (that are marked as "coverable" by the original discovery method) as **not** coverable by a set of heuristics. The default is to apply the heuristics. If a `True` value is specified, then the chance of false negatives in coverage reports is significantly increased.

If a line in the source can not be recognized automatically, then the module developer can add the exact string `# UNCOVERABLE` at the **end** of the line that can not be covered.

## key2source

```
print .slurp with key2source($key);  # show source for the given key

```

The `key2source` subroutine converts a given coverage key to a `IO::Path` of the source file (if possible), or returns `Nil`.

# CLASSES

## Code::Coverable

The `Code::Coverable` object is generally created by the `coverables` subroutine, but could also be created manually.

```
my $cc = Code::Coverable.new(
  target       => "Identity::Utils",
  line-numbers => (1,14,19,...),
  key          => "site#sources/072CEA63F659CFD963095494CEA22A46E4F93A95 (Identity::Utils)"
  source         => "/.../site/sources/072CEA63F659CFD963095494CEA22A46E4F93A95".IO,
);

```

The following public attributes are available:

### target

Mandatory: a string with the target for these coverables: this can either be `use` target (identity), or a path to a source file.

### line-numbers

Mandatory: a `List` of unique integer values of the line numbers that **could** potentially be covered in a coverage log.

### key

Optional: a string that should match in the coverage log to mark a line as being "covered". Defaults to the target given.

### source

Optional: an `IO::Path` object of the Raku source file. If not specified, will be derived from the information specified for the key.

# SCRIPTS

## coverable-lines

```
$ coverable-lines t/01-basic.rakutest
t/01-basic.rakutest: 1,3,5,7,8,9,10,13,14,15,16,17

$ coverable-lines snapper
core#sources/0D09916411FC21C59FC3C0980C68547D31562E01 (snapper): 1,8,10

```

The `coverable-lines` script accepts any number of paths or `use` targets and will attempt to produce one line of output with the coverage key and the line numbers of the lines that may appear in a coverage report.

# AUTHOR

Elizabeth Mattijsen [liz@raku.rocks](mailto:liz@raku.rocks)

# COPYRIGHT AND LICENSE

Copyright 2024, 2025 Elizabeth Mattijsen

Source can be located at: <https://github.com/lizmat/Code-Coverable> . Comments and Pull Requests are welcome.

If you like this module, or what I'm doing more generally, committing to a [small sponsorship](https://github.com/sponsors/lizmat/) would mean a great deal to me!

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.


## dist_cpan-ARNE-Game-Amazing.md
# NAME

Game::Amazing - Create, edit and check mazes for traversability

# SYNOPSIS

```
use Game::Amazing;

```

# DESCRIPTION

Game::Amazing can generate mazes, and check them for traversability (with the
Shortest Path and the Wall Follower Algorithms).

The module comes with some sample programs, including two games and a maze editor,
described in the EXAMPLES section.

See <https://raku-musings.com/amazing.html> for more information.

# METHODS

## new

Generate a new maze. There are two versions:

```
my $m = Game::Amazing::new($file);

```

This call loads an existing maze from a file. The filename must end with
«.maze».

```
my $m = Game::Amazing::new(rows => 25, cols => 25, scale => 7, ensure-traversable => False);

```

This one generates a randon maze, with the given size.

It is also possible to generate a new maze with an existing object with this one:

```
$m.new(rows => 25, cols => 25, scale => 7, ensure-traversable => False);

```

This is done by the «amazing-termbox» program when a new game is initiated, as letting
the original maze object go out of scope terminates the program (without explanation).

## new-embed

This method takes a string and uses that to build the maze. Illegal characters are ok,
as it is (mostly) meant for testing of maze transformations. An empty string will give
an empty maze.

```
my $m = Game::Amazing::new-embed("ABC\nDEF\nGHI\n");

```

## save

Save the maze. There are three (or four) versions of this method.

Tou can pass it a filehandle (to a file open for writing):

```
my IO::Handle $fh = open :w, 'my-maze.maze';
$m.save ($fh);
$fh.close;

```

Or you can specify a filename:

```
$m.save ('my-maze.maze');

```

The filename must end with «.maze».

This version has no positional argument, and will save the maze with a
randomly generated filename. The filename is returned.

```
my $filname = $m.save;
say $filename;  # -> /tmp/8spgH2MQBT.maze

```

You can add the «with-size» option to get the size of the maze added to the filename

```
my $filname = $m.save(with-size);
say $filename;  # -> /tmp/AZPNWawtTz-25x25.maze

```

## as-string

Return the maze as a single string with embedded newlines. It is used internally by the
save method, but can be used by user code as well.

```
my $string = $m.as-string;

```

## set-cell

Change the symbol for the cell with the specified row and column. This works directly
on the maze, changing the affect of future calls to the methods `get-directions`,
`has-direction`, `is-traversable` and `is-traversable-wall`.

```
$m.set-cell($row, $col, $symbol);

```

This method is used by «amazing-gtk» to change cell values in edit mode.

Note that there is not check on the legality of the new symbol, nor the length of the
value. This is on purpose, making it possible to add markup to the maze itself before
printing it. This is done by «maze-solver-spa». This is subject to change.

## get-cell

Get the symbol at the given postition.

```
my $symbol = $m.get-cell($row, $col);

```

## get-size

Get the size (number of rows and columns) of the maze.

```
my ($rows, $cols) = $m.get-size;

```

## fix-corners

This method will ensure that the maze has exactly one entrance and one exit. The randomly
generated mazes have two exit symbols, and «amazing-gtk» will change the top left one to
an entrance symbol. («amazing-termbox» checks for the coordinates and not the cell value,
and does not use this functionality.)

Saving a maze in «amazing-gtk»

```
$m.fix-corners;

```

Note that the method does not check for entrance end exits symbols in other positions in
the maze (than the four corners), but should herhaps do so.

It is possible to swap the entreance and exit. by using the *upside-down* argument.

```
$m.fix-corners(upside-down => True);

```

**More explanation**

## get-directions

This method return a string of directions from the specified cell. The letters are «N»
(north), «E» (east), «S» (south) and «W» (west).

Note that this method consider the neighbouring cells, so an exit in the current cell towards a
neighbouring cell that does not have a corresponding entrance (exit) will be ignored.

```
my $directions = $m.get-directions ($row, $col);

```

## has-direction

Check if the specified cell has an exit in the given direction, where the direction is one
of «N» (north), «E» (east), «S» (south) and «W» (west).

Note that this method looks at the current cell only, without considering the neighbouring
cellsm, so it is mainly for internal use. Use `get-directions` to get directions that
actually exist.

```
my $boolean = $.has-direction($row, $col, $direction);

```

## remove-direction

Remove the specified direction (on the form «N», «E», «S» or «W») from the specified cell.
It returns False if it was unable to change the character, and True on success.

If the cell had two exits, the result of removing one of them is an empty cell (a space
symbol).

```
my $boolean = $.remove-direction($row, $col, $direction);

```

This method does not work on the entrance or exit.

## add-direction

Add the specified direction (on the form «N», «E», «S» or «W») to the specified cell. It
returns False if it was unable to change the character, and True on success.

Nothing is done if the cell is empty (a space symbol).

```
my $boolean = $.remove-direction($row, $col, $direction);

```

This method does not work on the entrance or exit.

## toggle-direction

Remove the specified direction (on the form «N», «E», «S» or «W») to the specified cell, if it
is there, and add it otherwise.

Removing a direction from a cell with two exits removes both, and adding a direction to an
empty cell will fail.

The method returns True if it was able to change the cell.

```
my $boolean = $.toggle-direction($row, $col, $direction);

```

## is-traversable

Check if the maze is traversable, using the Shortest Path Algorithm.

```
my $boolean = $m.is-traversable;

```

The module will not calculate the value *before* you call the method. Then the
value (as well as the path or coverage map) is cached.

Use the «:force» argument to force the program to check the path again:

```
my $boolean = $m.is-traversable(:force);

```

This is used by the maze editor, whenever the user changes a symbol.

*Note that this method assumes that the entrance and exit are located in the upper left
and lower right corners (or vice versa).*

## get-path

This gives the path, if the maze is traversable, and an empty string if not. The path is
a string of directional letters. Start at the entrance and apply them one by one to get
the actual path. The length of the string gives the number of steps.

## get-coverage

This gives the coverage, i.e. a list of cells that are reachable from the entrance.
This is given as a two-dimentional array, e.g.

```
my @coverage = $m.get-coverage;
my $row = 10;
my $col = 8;
say "Been there" if @coverage[$row][$col];

```

*Note that this method assumes that the entrance is located in the upper left corner.*

## is-traversable-wall

Check if the maze is traversable, using the Wall Follower Algorithm.

Note that this method does not cache the values (as opposed to «is-traversable»).

```
my $boolean = $m.is-traversable-wall (:$get-path, :$left, :$verbose)

```

You can get the path with the «:get-path» option. The path will usually be rather convoluted,
so the result here is a coverage array regardless of traversability.

```
my ($boolean, $path) = $m.is-traversable-wall(:get-path);
my @visited = @($path);

```

The method follows the right wall by default. Specify «:left» to override this:

```
my $boolean = $m.is-traversable-wall (:left)

```

Note that the Wall Follower Algorith will return you to the entrance if the maze is
untraversable. The Left and Right variants will thus give the same result on an
untraversable maze (but from opposite directions).

It is possible to get some verbose output from the method, with the «:verbose» option.

```
my $boolean = $m.is-traversable-wall(:verbose);

```

This is normally not very useful to end users.

An example, where the `<red>` tag should not be taken literally:

```
my ($boolean, $path) = $m.is-traversable-wall(:get-path);
my @visited = @($path);
for ^$m.rows -> $row
{
  for ^$m.cols -> $col
  {
    print @visited[$row][$col]
	  ?? '<red>' ~ $m.maze[$row][$col] ~ '</red>'
	  ?? $m.maze[$row][$col];
  }
  say '';
}

```

*Note that this method assumes that the entrance and exit are located in the upper left
and lower right corners (or vice versa).*

## transform

Transform the maze in the specified way. This will generate a new maze, which is the
return value, and will not affect the current maze.

The method takes one argument, which is one of:

* **R** or **90** - rotate 90 degrees to the right
* **D** or **180** - rotate 180 degrees (down)
* **L** or **270** - rotate 90 degrees to the left
* **H** - flip horizonatally
* **V** - flip vertically

```
my $new = $m.transform("R");

```

Note that the entrance and exit symbols (which are identical) will not be fixed when
moved to the *wrong* corners, but this can be fixed by using the «corners» option:

```
my $new = $m.transform("R", :corners);

```

The corners that are un-entrancified and un-exitified get a symbol with two exits.
This will actually roundtrip, as long as the original maze does not have any spurious
exits (exits leading out of the maze). Scroll down to «An Even More Amazing Program»
in <https://raku-musings.com/amazing1.html> for more information.

# EXAMPLES

The *bin* directory has some programs that will be installed on installation. *zef*
will tell you where they are installed, so that you can choose to add the directory
to the path.

Below is a short description. You can run any of them with the «-h» command line
option to get more information.

The programs:

## mazemaker

Generate a random maze.

## maze-solver-spa

Check if a given maze is traversable, using the «Shortest Path Algorithm».

## maze-solver-wall

Check if a given maze is traversable, using the «Wall Follower Algorithm».

## maze-solver-summary

Check if one or more mazes are traversable, and report how difficult they are.

## amazing-termbox

A game. Traverse the maze in your terminal window. It sses the «Termbox» module.

## amazing-gtk

Another game. Traverse the maze in a graphical window. It uses the «Gnome::GTK»
module.

This program can also edit mazes.

## maze-transform

Transform a maze file. It supports rotation (90, 180 and 270 degrees) and flipping
(horizontal and vertical). By default it doesn't move the entrance and exit, but
this can be done with a command line option.

# AUTHOR

Arne Sommer [arne@perl6.eu](mailto:arne@perl6.eu)

# COPYRIGHT AND LICENSE

Copyright 2020 Arne Sommer

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.


## dist_zef-guifa-Intl-UserLanguage.md
![](docs/logo.png)

This is a incredibly simple module for Raku designed to do one thing and one thing
only: obtain the current user’s preferred language(s). There is no universal way
to do this, so this module aims to be the one-stop shop to get that information.

To use, simply ask for the preferred language (if you just want one) or
preferred languages (more common).

```
use Intl::UserLanguage;
user-language;  # ↪ [ast-US] (on my system)
user-languages; # ↪ [ast-US], [es-US], [en-US], [pt-PT] (on my system)
                #   (sidenote: no idea why Apple adds -US onto ast…)
                #   (sidenote: Microsoft makes it ast-Latn… weird.)

```

In truth, the preferred language is just a wrapper for calling `.head` on the
list. I'd recommend against using `user-language`, as most times when you
need the languages (HTTP request headers, localization frameworks) there needs
to be a negotiation to find a best match.

In any case, both functions allow you to supply a default code which may be a
string in BCP47 format or a LanguageTag. This is useful in case for some reason
the user’s language(s) cannot be determined, for example, if the user is
running an operating system that has not had its settings cataloged in this
module. If you do not provide a default, and no language can be found, the
*default* default language is **en** (English).

As a final option, particularly if you want to test your code with other
languages, you can override the user’s system languages:

```
use Intl::UserLanguage :override;  # imports override functions
user-languages; # ↪ [ast-US], [es-US], [en-US], [pt-PT] (on my system)
override-user-languages('jp','zh');
user-languages; # ↪ [jp], [zh]

```

The override can be cleared at any time with `clear-user-language-override`.
Note that the override is *global*, and there is no current way to lexically
scope it;

# Support

Support is current available for the following OSes:

* **macOS**
  Full list of languages (as defined in *System Preferences → Language & Region → Preferred Languages*). Paralinguistic preferences (e.g. calendar type) are not set on a per-language basis, so they carry to all languages.
* **Linux**: If `$LANGUAGE` is set, then an ordered list is provided. Otherwise, it falls back to the more universal `$LANG`, which only provides a single language.
* **Windows**: If the registry value `Languages` is set in `HKCU\Control Panel\International\User Profile`, uses the ordered list found there. Otherwise, it falls back to the registry value `LocaleName` found in at `HKCU\Control Panel\International`.

Support is not available for \*nix machines right now, but only because I am not
sure what the `$*DISTRO` value is for those systems. I imagine detection will be
similar if not identical to Linux. Please contact me with your `$*DISTRO` value
and how to detect your system language(s) and I'll gladly add it.

# Lightweight mode (under development)

If your program only needs the language code to pass it through to something that only employs strings (e.g. to directly create a , it may
be useful to `use` the module in `:light` mode.
Instead of receiving a `LanguageTag` object, you will get a `Str` that can be passed into other modules.

# Version History

* 0.4.0
  * Moved individual OS versions into separate submodules. This will be more maintainable long term
  * Adjusted OS detection for macOS (Rakudo no longer reports it as `macosx` but rather `macos`)
  * Completely rewritten Mac code to support some extended attributes.
    * Sets up a model for using NativeCall when possible, and falling back to a slower method if not (Windows will eventually adopt a similar approach)
* 0.3
  * Cache language(s) on first call to `user-language[s]`
    This should provide a substantial speed up for modules like `Intl::*` that call this frequently as a fall back.

# Licenses and Legal Stuff

This module is licensed under the Artistic License 2.0 which is included
with the source. Camelia (the butterfly) is a trademark belonging to
Larry Walls and used in accordance with his terms.


## dist_zef-raku-community-modules-Net-IMAP.md
[![Actions Status](https://github.com/raku-community-modules/Net-IMAP/workflows/test/badge.svg)](https://github.com/raku-community-modules/Net-IMAP/actions)

# Net-IMAP

An IMAP client library.

## Example Usage

```
my $i = Net::IMAP.new(:$server);
$i.authenticate($user, $pass);
$i.select('INBOX');
my @messages = $i.search(:all);
for @messages {
    say .mime.header('subject');
}

```

## Simple interface methods

* `new(:$server, :$port = 143, :$debug, :$socket, :$ssl, :$starttls, :$plain)`
* `authenticate($user, $pass)`
* `mailboxes(:$subscribed)`
* `create($mailbox)`
* `delete($mailbox)`
* `rename($old-box, $new-box)`
* `subscribe($mailbox)`
* `unsubscribe($mailbox)`
* `select($mailbox)`
* `append($message)`
* `get-message(:$sid, :$uid)`
* `search(*%params)`
* `logout()`, `quit()`

### Net::IMAP::Message methods

* `sid`
* `uid`
* `flags(@new?)`
* `copy($mailbox)`
* `delete`
* `data`
* `mime-headers`
* `mime`

## Raw interface methods

* `get-response`
* `capability`
* `noop`
* `logout`
* `starttls`
* `switch-to-ssl`
* `login($user, $pass)`
* `select($mailbox)`
* `examine($mailbox)`
* `create($mailbox)`
* `delete($mailbox)`
* `rename($oldbox, $newbox)`
* `subscribe($mailbox)`
* `unsubscribe($mailbox)`
* `list($ref, $mbox)`
* `lsub($ref, $mbox)`
* `status($mbox, $type)`
* `append($name, $message, :$flags, :$datetime)`
* `check`
* `close`
* `expunge`
* `uid-search(*%query)`
* `search(*%query)`
* `uid-fetch($seq, $items)`
* `fetch($seq, $items)`
* `uid-store($seq, $action, $values)`
* `store($seq, $action, $values)`
* `uid-copy($seq, $mbox)`
* `copy($seq, $mbox)`

# AUTHOR

Andrew Egeler

Source can be located at: <https://github.com/raku-community-modules/Net-IMAP> . Comments and Pull Requests are welcome.

# COPYRIGHT AND LICENSE

Copyright 2014 - 2018 Andrew Egeler

Copyright 2019 - 2022 Raku Community

All files in this repository are licensed under the terms of Create Commons License; for details please see the LICENSE file