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

Bot::Cobalt::Serializer - Simple serialization wrapper

SYNOPSIS

  use Bot::Cobalt::Serializer;

  ## Spawn a YAML1.0 handler:
  my $serializer = Bot::Cobalt::Serializer->new;

  ## Spawn a JSON handler
  my $serializer = Bot::Cobalt::Serializer->new('JSON');
  ## ...same as:
  my $serializer = Bot::Cobalt::Serializer->new( Format => 'JSON' );

  ## Spawn a YAML1.1 handler that logs to $core->log->crit:
  my $serializer = Bot::Cobalt::Serializer->new(
    Format => 'YAMLXS',
    Logger => $core->log,
    LogMethod => 'crit',
  );

  ## Serialize some data to our Format:
  my $ref = { Stuff => { Things => [ 'a', 'b'] } };
  my $frozen = $serializer->freeze( $ref );

  ## Turn it back into a Perl data structure:
  my $thawed = $serializer->thaw( $frozen );

  ## Serialize some $ref to a file at $path
  ## The file will be overwritten
  ## Returns false on failure
  $serializer->writefile( $path, $ref );

  ## Do the same thing, but without locking
  $serializer->writefile( $path, $ref, { Locking => 0 } );

  ## Turn a serialized file back into a $ref
  ## Boolean false on failure
  my $ref = $serializer->readfile( $path );

  ## Do the same thing, but without locking
  my $ref = $serializer->readfile( $path, { Locking => 0 } );

DESCRIPTION

Various pieces of Bot::Cobalt need to read and write serialized data from/to disk.

This simple OO frontend makes it trivially easy to work with a selection of serialization formats, automatically enabling Unicode encode/decode and optionally providing the ability to read/write files directly.

METHODS

new

  my $serializer = Bot::Cobalt::Serializer->new;
  my $serializer = Bot::Cobalt::Serializer->new( $format );
  my $serializer = Bot::Cobalt::Serializer->new( %opts );

Spawn a serializer instance. Will croak if you are missing the relevant serializer module; see "Format", below.

The default is to spawn a YAML::XS (YAML1.1) serializer with error logging to carp.

You can spawn an instance using a different Format by passing a simple scalar argument:

  $handle_syck = Bot::Cobalt::Serializer->new('YAML');
  $handle_yaml = Bot::Cobalt::Serializer->new('YAMLXS');
  $handle_json = Bot::Cobalt::Serializer->new('JSON');

Alternately, any combination of the following %opts may be specified:

  $serializer = Bot::Cobalt::Serializer->new(
    Format =>
    Logger =>
    LogMethod =>
  );

See below for descriptions.

Format

Specify an input and output serialization format; this determines the serialization method used by "writefile", "readfile", "thaw", and "freeze" methods. (You can change formats on the fly by calling Format as a method.)

Currently available formats are:

The default is YAML (YAML Ain't Markup Language) 1.1 (YAMLXS)

YAML is very powerful, and the appearance of the output makes it easy for humans to read and edit.

JSON is a more simplistic format, often more suited for network transmission and talking to other networked apps. JSON is a lot faster than YAML (assuming JSON::XS is available). It also has the benefit of being included in the Perl core as of perl-5.14.

Logger

By default, all error output is delivered via carp.

If you're not writing a Cobalt plugin, you can likely stop reading right there; that'll do for the general case, and your module or application can worry about STDERR.

However, if you'd like, you can log error messages via a specified object's interface to a logging mechanism.

Logger is used to specify an object that has a logging method of some sort.

That is to say:

  ## In a typical cobalt2 plugin . . . 
  ## assumes $core has already been set to the Cobalt core object
  ## $core provides the ->log attribute containing a Log::Handler:
  my $serializer = Bot::Cobalt::Serializer->new( Logger => $core->log );
  ## now errors will go to $core->log->$LogMethod()
  ## (log->error() by default)

  ##
  ## Meanwhile, in a stand-alone app or module . . .
  ##
  sub configure_logger {
    . . .
    ## Pick your poison ... Set up whatever logger you like
    ## Log::Handler, Log::Log4perl, Log::Log4perl::Tiny, Log::Tiny, 
    ## perhaps a custom job, whatever ...
    ## The only real requirement is that it have an OO interface
  }

  sub do_some_work {
    ## Typically, a complete logging module provides a mechanism for 
    ## easy retrieval of the log obj, such as get_logger
    ## (otherwise keeping track of it is up to you)
    my $log_obj = Log::Log4perl->get_logger('My.Logger');

    my $serializer = Bot::Cobalt::Serializer->new( Logger => $log_obj );
    ## Now errors are logged as: $log_obj->error($err)
    . . .
  }

Also see the "LogMethod" directive.

LogMethod

When using a "Logger", you can specify LogMethod to change which log method is called (typically the priority/verbosity level).

  ## A slightly lower priority logger:
  my $serializer = Bot::Cobalt::Serializer->new(
    Logger => $core,
    LogMethod => 'warn',
  );

  ## A module using a Log::Tiny logger:
  my $serializer = Bot::Cobalt::Serializer->new(
    Logger => $self->{logger_object},
    ## Log::Tiny expects uppercase log methods:
    LogMethod => 'ERROR',
  );

Defaults to error, which should work for at least Log::Handler, Log::Log4perl, and Log::Log4perl::Tiny.

freeze

Turn the specified reference $ref into the configured Format.

  my $frozen = $serializer->freeze($ref);

Upon success returns a scalar containing the serialized format, suitable for saving to disk, transmission, etc.

thaw

Turn the specified serialized data (stored in a scalar) back into a Perl data structure.

  my $ref = $serializer->thaw($data);

(Try Data::Dumper if you're not sure what your data actually looks like.)

writefile

"freeze" the specified $ref and write the serialized data to $path

  print "failed!" unless $serializer->writefile($path, $ref);

Will fail with errors if $path is not writable for whatever reason; finding out if your destination path is writable is up to you.

Locks the file by default. You can turn this behavior off:

  $serializer->writefile($path, $ref, { Locking => 0 });

IMPORTANT: Uses flock to lock the file for writing; the call is non-blocking, therefore writing to an already-locked file will fail with errors rather than waiting.

Will be false on apparent failure, probably with some carping.

readfile

Read the serialized file at the specified $path (if possible) and "thaw" the data structures back into a reference.

  my $ref = $serializer->readfile($path);

By default, attempts to gain a shared (LOCK_SH) lock on the file. You can turn this behavior off:

  $serializer->readfile($path, { Locking => 0 });

IMPORTANT: This is not a non-blocking lock. readfile will block until a lock is gained (to prevent data structures from "running dry" in between writes). This is the opposite of what "writefile" does, the general concept being that preserving the data existing on disk takes priority. Turn off Locking if this is not the behavior you want.

Will fail with errors if $path cannot be found or is unreadable.

If the file is malformed or not of the expected "Format" the parser will whine at you.

version

Obtains the backend serializer and its VERSION for the current instance.

  my ($module, $modvers) = $serializer->version;

Returns a list of two values: the module name and its version.

  ## via Devel::REPL:
  $ Bot::Cobalt::Serializer->new->version
  $VAR1 = 'YAML::Syck';
  $VAR2 = 1.19;

SEE ALSO

AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

http://www.cobaltirc.org