=pod

=for vim
   vim: tw=72 ts=3 sts=3 sw=3 et ai :

=encoding utf8

=head1 NAME

MojoX::MojoDbWrap - Light wrapper around Mojo::Pg/SQLite/...


=head1 VERSION

This document describes MojoX::MojoDbWrap version 0.005001.

=begin html


<a href="https://www.perl.org/">
<img alt="Perl Version" src="https://img.shields.io/badge/perl-5.24+-brightgreen.svg">
</a>
<a href="https://badge.fury.io/pl/MojoX-MojoDbWrap">
<img alt="Current CPAN version" src="https://badge.fury.io/pl/MojoX-MojoDbWrap.svg">
</a>
<a href="http://cpants.cpanauthors.org/dist/MojoX-MojoDbWrap">
<img alt="Kwalitee" src="http://cpants.cpanauthors.org/dist/MojoX-MojoDbWrap.png">
</a>
<a href="http://www.cpantesters.org/distro/O/MojoX-MojoDbWrap.html?distmat=1">
<img alt="CPAN Testers" src="https://img.shields.io/badge/cpan-testers-blue.svg">
</a>
<a href="http://matrix.cpantesters.org/?dist=MojoX-MojoDbWrap">
<img alt="CPAN Testers Matrix" src="https://img.shields.io/badge/matrix-@testers-blue.svg">
</a>

=end html

=head1 SYNOPSIS

   use MojoX::MojoDbWrap;

   # use anything good for Mojo::Pg or Mojo::SQLite constructor
   my $wrap = MojoX::MojoDbWrap->new(
      db_url         => $db_url,  # e.g. 'postgres://...'
      migrations_for => { Pg => ..., SQLite => ... },
   );

   # if using a different class, create an object and pass it as db_url
   my $mdb = My::Class->new(...);
   my $wrap = MojoX::MojoDbWrap->new(
      db_url => $mdb,
      migrations_for => { ... },
      inserter_for => {
         'My::Class' => sub ($wrapper, $tablish, $data, $opts) { ... },
      },
   );
   # Alternatively, My::Class must implement method
   # "insert_returning_id" accepting the same parameters as the inserter
   # above

   say $wrap->mdb_module; # "Mojo::Pg" or "Mojo::SQLite" or...
   say $wrap->mdb_class;  # ditto, but the class is also `require`d

   # get the underlying instance of Mojo::Pg or Mojo::SQLite or ... object
   my $mojodb = $wrap->mdb;

   # get the `db` for actual query calling (like calling the underlying
   # instances's `db` method)
   my $db = $wrap->db;

   # assuming a column called `id`, get the id of the element corresponding
   # to a condition. The `$condition` is something good for a `select` on
   # the `$wrap->db` instance.
   my $id_existing = $wrap->id_of($table, $conditon);

   # if the identifier column is called in another way, pass it with the
   # table name like this:
   my $id_existing = $wrap->id_of([$table, $id_column_name], $condition);
   # also with optional $options as last parameter

   # ensure there's something - either get the id, or insert stuff and
   # get the id back anyway
   my $id_ensure = $wrap->id_or_insert($table, $condition, $default);

   # wrapper around $wrap->mdb->db->select(...)
$wrap->select(@select_args...);

   # do the init with the relevant migration, if provided
   $wrap->init;

   # extensibility
   my $wrap = MojoX::MojoDbWrap->new(
      db_url => $whatever,
      wrappers => [
         {
            class => 'My::Class1',
            create => sub ($class, $db_url) { ... },
            insert => sub (@args) { ... },
         },
         {
            class => 'My::Class1',
            create => sub ($class, $db_url) { ... },
            insert => sub (@args) { ... },
         },
         ...
      ],
   );

=head1 DESCRIPTION

This module acts as a light wrapper around classes like L<Mojo::Pg> or
L<Mojo::SQLite>, which provide a much overlapping interface with the
notable difference regarding how to take the identifier of a new record
upon its creation. While at it, this class also provides a couple of
useful functions like C<select>.

=head1 INTERFACE

=head2 db

   my $db = $wrap->new;

Same as C<< $wrap->mdb->db >>.

=head2 db_url

   my $url = $wrap->db_url;

The URL provided in L</new>.

=head2 id_of

   my $id = $wrap->id_of($tablish, $cond);  # OR
   my $id = $wrap->id_of($tablish, $cond, $opts);

Get the identifier of a record result of searching for condition
C<$cond>, optionally with options in C<$opts>.

Parameter C<$tablish> can either be a string, holding the name of the
table where to run the query, or an array reference that is supposed to
carry two strings, the first being the table name and the second being
the name of the identifier column.

=head2 id_or_insert

   my $id = $wrap->id_or_insert($tablish, $cond, $default); # OR
   my $id = $wrap->id_or_insert($tablish, $cond, $default, $opts);

Retrieve the identifier of the record result of searching for condition
C<$cond>, creating the record in case it is missing with values in
C<$default>.

C<$opts> is an optional hash that can contain options associated to keys
C<select> (for searching the identifier using L</id_of>) and C<insert>
(in case we need to insert a new record using C<$default>).

Parameter C<$tablish> is the same as in L</id_of> (and used to call that
method).

=head2 init

   $wrap->init; # OR
   $wrap->init($name);

If available via L</migrations_for>, runs the migrations based on the
specific class. C<$name> defaults to the string C<migrations>.

=head2 insert

   my $id = $wrap->insert($tablish, $data); # OR
   my $id = $wrap->insert($tablish, $data, $opts);

Insert a new record based on C<$data>, returning the identifier of that
new record.

C<$opts> is an optional hash that can contain options associated to keys
C<select> (for searching the identifier using L</id_of>) and C<insert>
(in case we need to insert a new record using C<$default>).

Parameter C<$tablish> is the same as in L</id_of>; in particular, the
newly inserted record is supposed to have an C<id> column by default,
but you can pass an array reference with the first parameter as the
table name and the second parameter as the identifier column name to
change this.

=head2 mdb

   my $mdb = $wrap->mdb;

Get the wrapped object (i.e. an instance of L<Mojo::Pg>,
L<Mojo::SQLite>, or whatever else was instantiated through any of the
C<wrappers> provided in L</new>).

=head2 mdb_class

   my $mdb = $wrap->mdb_class;

The class of the wrapped object. Technically the wrapped object I<might>
be of another class, this is what is passed in the C<class> slot of the
selected wrapper in C<wrappers> as passed to L</new>.

The value of this method is used to select the relevant migration when
calling L</init>.

=head2 mdb_module

   my $mdb = $wrap->mdb_module;

Alias for L</mdb_class>.

=head2 migrations_for

   my $hashref = $wrap->migrations_for;

Retrieve a hash reference with keys corresponding to the supported
classes for wrapped objects, and values corresponding to the
I<migrations>.

=head2 new

   my $wrap = MojoX::MojoDbWrap->new(%args); # OR
   my $wrap = MojoX::MojoDbWrap->new(\%args);

Create a new instance. Keys in C<%args> can be used to set L</db_url>
(required) and L</migrations_for>, as well as an array reference for
C<wrappers> as explained below.

Parameter C<db_url> is in general supposed to be a string that can be
used to instantiate a L<Mojo::Pg> object (if it starts with
C<postgres://> or C<postgresql://>) or a L<Mojo::SQLite> object (any
other string). These are the two classes wrapped by default.

Parameter C<wrappers> allows providing a list of wrappers in case you
want to support further classes too. This list is given as an array
reference holding hashes with the following keys:

=over

=item C<class>

the name of the class, which in case of selection will also be used to
select a migration.

=item C<create>

a sub reference for creating a new instance, it will be called like
this:

   $sub->($class, $db_url)

with C<$class> the same as C<class> above and C<$db_url> the same as the
parameter C<db_url>.

The sub can return either C<undef>, in case it is not capable of
handling C<$db_url>, or an object that becomes the wrapped instance (as
returned by L</mdb>).

=item C<insert>

a sub reference for inserting a new record. It will be called like this:

   $sub->($wrap, $tablish, $data, $opts)

where C<$wrap> is a reference to the MojoX::MojoDbWrap object,
C<$tablish> is the name of the table or an array with the name of the
table and the name of the identifier column (much like L</id_of> and
L</id_or_insert>), C<$data> is the data to use for the insert and
C<$opts> are any insert options passed by L</id_or_insert>.

The return value must be the identifier of the newly inserted record.

=back

When needed the wrapper instantiation, items in C<wrappers> are used in
succession to call the C<create> sub, until one returns a true value
that is taken as the wrapped object. If none is matched, L<Mojo::Pg> and
L<Mojo::SQLite> are tried last, in ths order.

=head2 select

   my $x = $wrap->select(@args);

Wrapper around C<< $wrap->mdb->db->select(@args) >>.


=head2 upsert

   $wrap->upsert($tablish, $data, $opts);

Insert a new record, or update it if already present.

C<$tablish> is the same as L</id_of>, i.e. either a column name or an
array reference with the table name and the identifier column name.

The assumption is that there is a single column where the conflict in
insertion can arise and that column name is passed as the value of key
C<conflicting> in C<$opts>, like this:

   $wrap->upsert($tablish, $data, { conflicting => 'name', ... });

This will generate an C<on_conflict> option passed to L</insert>,
setting all values I<except> the conflicting one. If you need a more
nuanced I<upsert>, generate the C<on_conflict> option yourself and pass
it in the C<$opts> (it's still suggested to call this method instead of
L<insert> directly, so that you're explicit about your goal).


=head1 BUGS AND LIMITATIONS

Minimul perl version 5.24.




=head1 AUTHOR

Flavio Poletti <flavio@polettix.it>

=head1 COPYRIGHT AND LICENSE

Copyright 2026 by Flavio Poletti <flavio@polettix.it>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


=cut
