package Mojolicious::Plugin::DbAccess;
use Mojo::Base 'Mojolicious::Plugin';


use C;
use D;



sub register {
	my( $plugin, $app, $conf ) =  @_;

	$conf =  eval{ $app->merge_config( DbAccess => $conf ) }
        // C::config->{ DbAccess };
	%$conf   or return warn "No configuration for DbAccess plugin";

	$conf->{ schema }   or die "{ DbAccess }{ schema } MUST BE configured";
	eval "use $conf->{ schema }";

	$app->helper( db            =>  sub{ D::db()  } );
	$app->helper( atomic_change =>  sub{ D::txn() } );

	$app->helper( dbdata        =>  \&db_table      );
	$app->helper( resource      =>  \&resource      );
}



sub db_table {
	my( $c, $table_name, $strict ) =  @_;
	$strict //=  'secure';

	# Get table name from last part of the caller package:
	# App::Controller::User  ->  User
	$table_name //=  ((ref $c) =~ /([^:]*)$/)[0];
	my $db_table =  $c->db->resultset( $table_name );

	# Developer should explicitly pass 'insecure' value to force full access
	# Otherwise access will go through &guard
	$strict eq 'insecure'   or return $db_table->guard( $c );

	return $db_table;
}



sub resource {
	my $c =  shift;

	my $id =  $c->param( 'id' )   or do{
		$c->stash->{ error }{ message } =  "Nothing is requested";
		$c->reply->not_found;

		return undef;
	};


	# DO NOT CACHE 'insecure' requests
	return $c->dbdata( undef, @_ )->find( $id )   if @_;



	$c->{ resource } //=  $c->dbdata->find( $id )   or do{
		$c->stash->{ error }{ message } =  "Requested resource ID:$id not_found";
		$c->reply->not_found;

		return undef;
	};

	return $c->{ resource };
}



1;

=encoding utf8

=head1 NAME

Mojolicious::Plugin::DbAccess - Mojolicious helpers for the D DB access layer

=head1 SYNOPSIS

  # Mojolicious application
  $app->plugin('DbAccess' => {
    schema => 'MyApp::Schema',
    # other DbAccess configuration keys...
  });

  # Controller
  my $rs   = $c->dbdata('User');             # guarded resultset
  my $user = $c->resource;                  # row by param('id')

  my $raw  = $c->dbdata('User', 'insecure'); # unguarded resultset

=head1 DESCRIPTION

This plugin wires L<D> into a Mojolicious application.

It installs helpers for:

- Accessing a cached schema handle
- Running atomic changes with a transaction scope guard
- Getting guarded/unguarded resultsets
- Fetching a resource by C<id> request parameter

=head1 CONFIGURATION

Configuration is stored under the C<DbAccess> key.

The plugin tries to merge the passed config via
C<< $app->merge_config(DbAccess => $conf) >> and falls back to
C<C::config-E<gt>{DbAccess}>.

The key C<schema> is required and must be a DBIx::Class schema class name.

=head1 HELPERS

=head2 db

  my $schema = $c->db;

Return the cached schema handle (L<D/db>).

=head2 atomic_change

  my $guard = $c->atomic_change;

Return a transaction scope guard (L<D/txn>).

=head2 dbdata

  my $rs = $c->dbdata;
  my $rs = $c->dbdata($table_name);
  my $rs = $c->dbdata($table_name, $strict);

Return a resultset for C<$table_name>.

If C<$table_name> is omitted, it is derived from the caller package name:

  App::Controller::User  ->  User

If C<$strict> is not C<insecure>, the resultset is passed through
C<< $rs->guard($c) >>.

=head2 resource

  my $row = $c->resource;
  my $row = $c->resource($table_name, $strict);

Fetch a row by the C<id> request parameter, using L</dbdata>.

If called without extra arguments, the result is cached on the controller
object for the duration of the request.

On missing C<id> or a missing row, sets an error message in stash and replies
with C<not_found>.

=head1 SEE ALSO

L<D>, L<Mojolicious>, L<Mojolicious::Command::db_env>,
L<Mojolicious::Command::clear_database>

=cut
