The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Mongoose::Role::Naming;
$Mongoose::Role::Naming::VERSION = '1.02';
use Moose::Role;
use Moose::Util::TypeConstraints;

# naming templates
my %naming_template = (
    same       => sub { $_[0] },
    short      => sub { $_[0] =~ s{^.*\:\:(.*?)$}{$1}g; $_[0] },
    plural     => sub { $_[0] =~ s{^.*\:\:(.*?)$}{$1}g; lc "$_[0]s" },
    decamel    => sub { $_[0] =~ s{([a-z])([A-Z])}{$1_$2}g; lc $_[0] },
    undercolon => sub { $_[0] =~ s{\:\:}{_}g; lc $_[0] },
    lower      => sub { lc $_[0] },
    lc         => sub { lc $_[0] },
    upper      => sub { uc $_[0] },
    uc         => sub { uc $_[0] },
    default => sub {
        $_[0] =~ s{([a-z])([A-Z])}{$1_$2}g;
        $_[0] =~ s{\:\:}{_}g;
        lc $_[0];
    }
);

subtype 'Mongoose::CodeRef' => as 'CodeRef';
coerce 'Mongoose::CodeRef'
    => from 'Str' => via {
        my $template = $naming_template{ $_[0] }
            or die "naming template '$_[0]' not found";
        return $template;
    }
    => from 'ArrayRef' => via {
        my @filters;
        for( @{ $_[0] } ) {
            my $template = $naming_template{ $_ }
                or die "naming template '$_' not found";
            # add filter to list
            push @filters, sub {
                my $name = shift;
                return $template->($name);
            }
        }
        # now, accumulate all filters
        return sub {
            my $name = shift;
            map { $name = $_->($name) } @filters;
            return $name;
        }
    };

has 'naming' => (
    is      => 'rw',
    isa     => 'Mongoose::CodeRef',
    coerce  => 1,
    default => sub {$naming_template{default}}
);

1;

=head1 NAME

Mongoose::Role::Naming

=head1 DESCRIPTION

This role implement class to collection name methods for Mongoose objects.

=cut

=head2 naming

By default will compose the MongoDB collection name from your package name
by replacing double-colon C<::> with underscores C<_>, separating camel-case,
such as C<aB> with C<a_b> and uppercase with lowercase letters.

This behaviour can be changed by choosing a named method or by setting
the collection naming routine with a C<closure>.

This are the available named methods:

     named method | package name          | collection
    --------------+-----------------------+-----------------------
     short        | MyApp::Schema::FooBar | foobar
     plural       | MyApp::Schema::FooBar | foobars
     decamel      | MyApp::Schema::FooBar | foo_bar
     lower        | MyApp::Schema::FooBar | myapp::schema::author
     upper        | MyApp::Schema::FooBar | MYAPP::SCHEMA::AUTHOR
     undercolon   | MyApp::Schema::FooBar | myapp_schema_foobar
     default      | MyApp::Schema::FooBar | myapp_schema_foo_bar
     none         | MyApp::Schema::Author | MyApp::Schema::Author

You can choose a predefined naming method

    Mongoose->naming( 'plural' );

... or combine them

    Mongoose->naming( ['decamel','plural' ] );  # same as 'shorties'

If you set a closure it will receive the package name as it only parameter and
should return the collection name.

    # plain lowercase
    Mongoose->naming( sub { lc(shift) } );