The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Text::ANSITable::SQLStyleSheet - Pretty tables with SQL-generated styles

SYNOPSIS

  use Text::ANSITable::SQLStyleSheet;
  use DBI;

  my $dbh = DBI->connect('dbi:SQLite:dbname=:memory:');

  my $sth = $dbh->prepare(q{
    WITH RECURSIVE
    ints AS (
      SELECT 1 AS value
      UNION ALL
      SELECT value + 1 AS value FROM ints
    )
    SELECT value FROM ints LIMIT 10
  });

  $sth->execute();

  my $t = Text::ANSITable::SQLStyleSheet->from_sth($sth, q{
    SELECT
      *,
      JSON_OBJECT(
        'fgcolor',
        PRINTF(
          '%02x%02x%02x',
          ABS(RANDOM()) % 256,
          ABS(RANDOM()) % 256,
          ABS(RANDOM()) % 256
        )
      ) AS __row_style
    FROM
      data
  });

  # a table with integers in random colours.
  print $t->draw;

DESCRIPTION

When you frequently look at report tables from SQL queries in your terminal and wish for a little bit of extra style, this module allows you to specify styles as (part of) SQL queries.

You can do this either quick and dirty in your data queries, or by letting this module store your data temporarily in an in-memory SQLite database before your "style sheet" is applied.

CONSTRUCTOR

from_sth( $sth, $query )

Fetches all rows from $sth into a data table in a temporary in-memory SQLite database and then executes $query in that database.

The $query argument is optional; if omitted, data and styles are taken directly from $sth as if you had called

  from_sth($sth, 'SELECT * FROM data')

but no temporary database is created. This tight coupling between data and style computation can be more convenient in some situations.

NOTE: While $query will always be executed against SQLite, this module does not care which database driver $sth is associated with. It does try to create the temporary table with the right type affinity so SQLite does not suddenly treat integers as strings or otherwise, but that depends on cooperation on part of the driver.

The style sheet query is expected to add columns named __column_style, __row_style, __cell_style to the result set. Values in these columns are JSON-encoded objects, see the template below for reference. The structure of the JSON objects mirrors the configurable styles that Text::ANSITable supports. Styles with a NULL value are ignored and are not passed to Text::ANSITable. All style columns are optional. Column styles are taken only from the first row.

In addition to the styles supported by Text::ANSITable, this module supports an additional pseudo-style for cells named value. If specified, the value overrides the value that would otherwise be used for the cell. This allows you, for instance, to work with the full data in the "style sheet", and abbreviate or otherwise transform it for display.

The $query argument can also be a CODE reference. The code will be executed after the temporary database has been created with the database handle as only argument, and is expected to return an executed statement handle. That gives callers a chance to install additional functions onto the handle or pass arguments to the query.

  my $t = Text::ANSITable::SQLStyleSheet->from_sth($sth, sub {
    my ($dbh) = @_;

    $dbh->sqlite_create_function('truncate', 2, sub {
      my ($string, $max_length) = @_;
      ...
    });

    my $sth = $dbh->prepare(q{
      WITH 
      args AS (
        SELECT ? AS max_length
      )
      SELECT
        ... truncate(long_text, args.max_length) ...
      FROM  
        data
          JOIN args
    });

    $sth->execute( 100 );

    return $sth;
  });

The return value is a Text::ANSITable object.

TEMPLATE FOR SQLITE

  WITH 
  data AS (
    SELECT
    ...
  )
  SELECT
    *
    ,
    JSON_OBJECT(
      'column_name',
      JSON_OBJECT(
        -- 'fgcolor', NULL,
        -- 'bgcolor', NULL,
        -- 'align', NULL,
        -- 'valign', NULL,
        -- 'formats', NULL
        -- pseudo-style not passed to Text::ANSITable
        -- 'value', NULL 
      )
      ,
      'other_column',
      JSON_OBJECT(
        ...
      )
    ) AS __cell_style
    ,
    JSON_OBJECT(
      -- 'align', NULL,
      -- 'valign', NULL,
      -- 'height', NULL,
      -- 'vpad', NULL,
      -- 'tpad', NULL,
      -- 'bpad', NULL,
      -- 'fgcolor', NULL,
      -- 'bgcolor', NULL
    ) AS __row_style
    ,
    JSON_OBJECT(
      'column_name',
      JSON_OBJECT(
        -- 'align', NULL,
        -- 'valign', NULL,
        -- 'pad', NULL,
        -- 'lpad', NULL,
        -- 'rpad', NULL,
        -- 'width', NULL,
        -- 'formats', NULL,
        -- 'fgcolor', NULL,
        -- 'bgcolor', NULL,
        -- 'type', NULL,
        -- 'wrap', NULL
      )
      ,
      'other_column',
      JSON_OBJECT(
        ...
      )
    ) AS __column_style
  FROM
    data

TODO

Unfortunately https://github.com/DBD-SQLite/DBD-SQLite/issues/36 affects this module when your data query (the $sth handle you pass in) is executed against a SQLite database. The columns in the temporary database might then be associated with the wrong column affinity, which can result in odd behavior in your style sheet query.

BUG REPORTS

SEE ALSO

  * Text::ANSITable

AUTHOR / COPYRIGHT / LICENSE

  Copyright (c) 2018 Bjoern Hoehrmann <bjoern@hoehrmann.de>.
  This module is licensed under the same terms as Perl itself.