Catalyst::Model::Loader

思うところがあって複数のPOPOモデルを読み込んでCatalystのModelにするLoaderを作ってみた。パッケージ名がなんかしっくりこないけれど。

package Catalyst::Model::Loader;
use strict;
use warnings;
use Class::C3;

our $VERSION = '0.01';
use base 'Catalyst::Model::Loader::Base';

sub new {
    my $class = shift;
    my $self  = $class->next::method(@_);
    $self->load_services;
    $self;
}

1;
package Catalyst::Model::Loader::Base;
use strict;
use warnings;
use Carp;
use Class::C3;
use Sub::Install;
use Module::Pluggable::Object;
use UNIVERSAL::require;
use base 'Catalyst::Model';

sub load_services {
    my $self       = shift;
    my $base_class = $self->{base_class};
    croak 'base_class is required !!!' unless $base_class;
    my $finder = Module::Pluggable::Object->new( search_path => $base_class );
    foreach my $service ( $finder->plugins ) {
        $self->_install_service_as_model($service);
    }
}

sub _install_service_as_model {
    my ( $self, $service ) = @_;
    my $classname
        = $self->_convert2classname( $service, $self->{base_class} );
    my $instance = $self->_create_instance($service);
    Sub::Install::install_sub(
        {   code => sub { return $instance },
            into => $classname,
            as   => 'ACCEPT_CONTEXT',
        }
    );
}

sub _convert2classname {
    my ( $self, $service, $base_class ) = @_;
    my $class              = ref($self);
    my $short_service_name = $service;
    $short_service_name =~ s/^$base_class\:\://g;
    return "${class}::$short_service_name";
}

sub _create_instance {
    my ( $self, $adapted_class ) = @_;
    my $constructor = $self->{constructor} || 'new';
    $adapted_class->require;
    return $adapted_class->$constructor();
}

1;

__END__

# 作ってみたら、コードは違うけれど、機能的にはCatalyst::Model::DynamicAdapterと同じになってる.