catalyst patterns-yapc-eu-2016
TRANSCRIPT
![Page 1: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/1.jpg)
Catalyst Design Patterns
YAPC::EU 2016
![Page 2: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/2.jpg)
![Page 3: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/3.jpg)
Catalyst is built around and encourages the use
of design patterns.
![Page 4: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/4.jpg)
![Page 5: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/5.jpg)
– https://en.wikipedia.org/wiki/Software_design_pattern
“Design patterns are formalized best practices that the programmer can use to solve common
problems when designing an application or system.”
![Page 6: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/6.jpg)
Design Patterns in Catalyst
![Page 7: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/7.jpg)
Command
• An object wrapped around a function.
• Encapsulates / abstracts all the information needed to perform an action.
• Separate a reusable activity from class.
• Attach ‘bookkeeping’ and monitoring functions
![Page 8: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/8.jpg)
package MyApp::Controller::User;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub list :Local { my ($self, $c) = @_; }
__PACKAGE__->meta->make_immutable;
![Page 9: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/9.jpg)
package Catalyst::Action;
use Moose;
has [qw/class namespace attributes name code private_path/] => (is=>'ro', ...);
sub execute { ... } sub match { ... } sub list_extra_info { ... }
__PACKAGE__->meta->make_immutable;
![Page 10: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/10.jpg)
Examples
![Page 11: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/11.jpg)
package Catalyst::ActionRole::Scheme;
use Moose::Role;
requires 'match_args', 'match_captures';
around ['match_args','match_captures'] => sub { my ($orig, $self, $c, @args) = @_; my $scheme = lc($c->req->env->{'psgi.url_scheme'}); my $required = lc($self->scheme||’’);
return $scheme eq $required ? $self->$orig($ctx, @args) : 0; };
1;
![Page 12: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/12.jpg)
package MyApp::Controller::User;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub list :Local Does(Scheme) Scheme(https) { my ($self, $c) = @_; }
__PACKAGE__->meta->make_immutable;
![Page 13: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/13.jpg)
package MyApp::Controller::Example;
use Moose; use MooseX::MethodAttributes; use Catalyst::ActionSignatures;
extends 'Catalyst::Controller';
sub test(View $v, Model::User $user) :Local { $v->show_profile($user); }
![Page 14: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/14.jpg)
package MyApp::Controller::Example;
use Moose; use MooseX::MethodAttributes; #use Catalyst::ActionSignatures;
extends 'Catalyst::Controller';
sub test :Local { my ($self, $c, @args) = @_; my $v = $c->view; my $user = $c->model('User');
$v->show_profile($user); }
![Page 15: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/15.jpg)
???
![Page 16: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/16.jpg)
Model - View - Controller
• Implement User Interfaces.
• “Pattern of Patterns”.
• Traditionally used for GUI Desktop applications.
• For classic server side applications its more about separation of job duties.
![Page 17: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/17.jpg)
![Page 18: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/18.jpg)
Examples
![Page 19: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/19.jpg)
package MyApp::Controller::User;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub list :Local { my ($self, $c, @args) = @_; my @users = $c->model(‘Users’)->user_list; $c->forward(‘View::Users’, \@users); }
__PACKAGE__->meta->make_immutable;
![Page 20: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/20.jpg)
package MyApp::Model::Users;
use Moose;
extends ‘Catalyst::Model';
sub user_list { return (qw/ Srinivas Joe Holly
/); }
__PACKAGE__->meta->make_immutable;
![Page 21: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/21.jpg)
package MyApp::View::Users;
use Moose;
extends ‘Catalyst::View';
sub process { my ($self, $c, @users) = @_; my @list = map { “<li>$_</li>”} @users; $c->res->body(<<END);
<html> <head> <title>User List</title> </head>
<body> <ul> @list </ul> </body>
</html> END; }
__PACKAGE__->meta->make_immutable;
![Page 22: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/22.jpg)
???
![Page 23: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/23.jpg)
Chain of Responsibility
• An abstraction that decomposes a complex workflow into discrete objects.
• Promotes loose coupling of design, and simple action commands.
![Page 24: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/24.jpg)
package MyApp::Controller::Root;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub root :Chained(/) PathPart('') CaptureArgs(0) { my ($self, $c) = @_; $c->view('SummaryList', items => $c->model)); }
sub summary_list :GET Chained(root) PathPart('') Args(0) { my ($self, $c) = @_; $c->view->http_ok; }
sub add :POST Chained(root) PathPart('') Args(0) { my ($self, $c) = @_; $c->model('Form::Todo')->is_valid ? $c->view->http_ok : $c->view->http_bad_request; }
![Page 25: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/25.jpg)
???
![Page 26: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/26.jpg)
Catalyst Components
• An abstraction that decouples the creation of required objects from the code that needs those objects.
• Associated with Dependency Injection and Service Locator Patterns.
![Page 27: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/27.jpg)
package MyApp::Controller::User;
use DBI; use DBD::Pg; use MyApp::Schema;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub list :Local { my ($self, $c) = @_; my $schema = MyApp::Schema->connect( "dbi:Pg:dbname=myapp;host=localhost;port=5432", "user", "password");
my $user_rs = $schema->resultset('User'); }
![Page 28: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/28.jpg)
package MyApp::Controller::User;
use DBI; use DBD::Pg; use MyApp::Schema;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub list :Local { my ($self, $c) = @_; my $schema = MyApp::Schema->connect( "dbi:Pg:dbname=myapp;host=localhost;port=5432", "user", "password");
my $user_rs = $c->model(‘User’); }
![Page 29: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/29.jpg)
Decouple instance Creation from Usage!
![Page 30: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/30.jpg)
Program against an Interface not an implementation!
![Page 31: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/31.jpg)
package Catalyst::Component;
use Moose;
#wraps a class accessor sub config { … }
# Called by App->setup_components sub COMPONENT { my ($class, $app, $config) = @_; return $class->new($config) }
# Optional, called with $c->component sub ACCEPT_CONTEXT { my ($self, $c, @args) = @_; }
![Page 32: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/32.jpg)
All Models, Views and Controllers inherit from Catalyst::Component
![Page 33: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/33.jpg)
Component Concepts
• Lifecycle
• Dependency Management
![Page 34: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/34.jpg)
Component Lifecycle
![Page 35: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/35.jpg)
Application Scope
• Instance created once during ‘setup_components’ (via ->COMPONENT)
• Can only depend on literals and other application scoped components.
• Can’t meaningfully pass arguments to ->model / ->view / ->controller.
![Page 36: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/36.jpg)
package MyApp::Model::ApplicationMeta;
use Moose; extends ‘Catalyst::Component';
has 'version' => (is=>'ro', required=>1); has 'license' => (is=>'ro', required=>1); has 'copyright' => (is=>'ro', required=>1);
__PACKAGE__->meta->make_immutable;
![Page 37: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/37.jpg)
package MyApp::Web;
use Catalyst;
__PACKAGE__->config( 'Model::ApplicationMeta' => { version => '1.001', license => 'GLP', copyright => '2016', }, );
__PACKAGE__->setup;
![Page 38: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/38.jpg)
package MyApp::Controller::Meta;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub info :Local { my ($self, $c) = @_; my $meta = $c->model('ApplicationMeta'); $c->log->debug("Application version: ${\$meta->version}"); }
![Page 39: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/39.jpg)
Context Scope• does ACCEPT_CONTEXT, invoked during $c-
>model, $c->view (or even $c->controller).
• Can depend on literals, application scoped components and other context scoped components.
• Allows for the current context to in some way inform the component (provide dependencies, etc.)
![Page 40: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/40.jpg)
package MyApp::Model::ApplicationMeta;
use Moose; extends 'Catalyst::Component';
has 'version' => (is=>'ro', required=>1); has 'license' => (is=>'ro', required=>1); has 'copyright' => (is=>'ro', required=>1); has 'total_requests' => (is=>'ro', required=>0);
sub ACCEPT_CONTEXT { my ($self, $c, @args) = @_; return (ref $self)->new( version => $self->version, license => $self->license, copyright => $self->copyright, total_requests => ref($c)::COUNT); }
__PACKAGE__->meta->make_immutable;
![Page 41: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/41.jpg)
I’d prefer a Factory Component…
![Page 42: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/42.jpg)
package MyApp::Model::ApplicationMetaFactory;
use MyApp::ApplicationMeta; use Moose; extends 'Catalyst::Component';
has 'version' => (is=>'ro', required=>1); has 'license' => (is=>'ro', required=>1); has 'copyright' => (is=>'ro', required=>1);
sub ACCEPT_CONTEXT { my ($self, $c, @args) = @_; return MyApp::ApplicationMeta->new( version => $self->version, license => $self->license, copyright => $self->copyright, total_requests => ref($c)::COUNT); }
__PACKAGE__->meta->make_immutable;
![Page 43: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/43.jpg)
package MyApp::ApplicationMeta;
use Moo;
has 'version' => (is=>'ro', required=>1); has 'license' => (is=>'ro', required=>1); has 'copyright' => (is=>'ro', required=>1); has 'total_requests' => (is=>'ro', required=>1);
1;
![Page 44: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/44.jpg)
• Context scoped components can be used to build any type of scope (session, request, workflow, even application!)
• Allows your component to depend on other components, but out of the box the ‘wiring’ is all manual.
• Watch out for performance issues since you are often creating a lot of objects this way.
![Page 45: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/45.jpg)
‘Context’ Scoping can be used to build any other
type of Scope.
![Page 46: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/46.jpg)
“PerRequest” Lifecycle
![Page 47: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/47.jpg)
package MyApp::Model::ApplicationMetaFactory;
use Moose; use MyApp::ApplicationMeta; use Scalar::Util qw/blessed refaddr/;
extends 'Catalyst::Component';
has [‘version’, ‘license’, ‘copyright’] => (is=>'ro', required=>1);
sub ACCEPT_CONTEXT { my ($self, $c_or_app, @args) = @_; if(blessed $c_or_app) { return $c_or_app->stash->{refaddr $self} ||= $self->return_instance(ref $c_or_app); } else { return $self->return_instance($c_or_app); } }
sub return_instance { my ($self, $app) = @_; return MyApp::ApplicationMeta->new( version => $self->version, license => $self->license, copyright => $self->copyright, total_requests => $app::COUNT); }
![Page 48: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/48.jpg)
package MyApp::Model::ApplicationMetaFactory;
use Moose; use MyApp::ApplicationMeta;
extends 'Catalyst::Component'; with 'Catalyst::Component::InstancePerContext';
has [‘version’, ‘license’, ‘copyright’] => (is=>'ro', required=>1);
sub build_per_context_instance { my ($self, $c_or_app) = @_; my $app = ref($c_or_app) || $c_or_app;
return MyApp::ApplicationMeta->new( version => $self->version, license => $self->license, copyright => $self->copyright, total_requests => $app::COUNT); }
__PACKAGE__->meta->make_immutable;
![Page 49: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/49.jpg)
Lifecycle Questions?
![Page 50: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/50.jpg)
Injecting Dependencies
![Page 51: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/51.jpg)
package MyApp::LoginForm;
use HTML::FormHandler::Moose; extends 'HTML::FormHandler';
has 'user_rs' => ( is=>'ro', required=>1 );
has_field 'name' => ( type => 'Text' ); has_field 'password' => ( type => 'Password' );
1;
![Page 52: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/52.jpg)
package MyApp::Model::LoginForm;
use Moose; use MyApp::LoginForm;
extends 'Catalyst::Component';
sub ACCEPT_CONTEXT { my ($self, $c, @args) = @_; return MyApp::LoginForm->process( user_rs => $c->model(‘Schema::User’), params => $c->req->body_params); }
__PACKAGE__->meta->make_immutable;
![Page 53: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/53.jpg)
package MyApp::Controller::Login;
use Moose; use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub process_login :Path('') Args(0) { my ($self, $c) = @_; my $form = $c->model('LoginForm'); # ... }
![Page 54: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/54.jpg)
Best Practice: Don’t write a Model…
![Page 55: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/55.jpg)
…Write a Class and use a Model Adaptor or
Model Injection.
![Page 56: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/56.jpg)
Catalyst::Model::Adaptor
• Encapsulates three common patterns for adapting your class into a Catalyst model or view.
• Application, Factory, PerRequest.
![Page 57: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/57.jpg)
Catalyst::Plugin::InjectionHelpers
• Builds on the component injection introduced in Catalyst version 5.90090.
• Has Application, Factory, PerRequest and PerSession lifecycle adaptors
• Less battle tested that Catalyst::Model::Adaptor
![Page 58: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/58.jpg)
package MyApp;
use Catalyst qw/ InjectionHelpers MapComponentDependencies /;
use Catalyst::Plugin::MapComponentDependencies::Utils ':ALL';
__PACKAGE__->inject_components( 'Model::Schema' => { from_component => 'Catalyst::Model::DBIC::Schema'}, 'Model::Entities' => { from_class => 'MyApp::Entities' }, );
__PACKAGE__->config( 'Model::Entities' => { dbic_schema => FromModel 'Schema' }, 'Model::Schema' => { traits => ['Result', 'SchemaProxy'], schema_class => 'MyApp::Schema', connect_info => [ 'dbi:Pg:dbname=$ENV{DBNAME};host=$ENV{DBHOST};port=5432', '$ENV{DBUSER}', ''], }, );
__PACKAGE__->setup;
![Page 59: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/59.jpg)
package MyApp::Entities;
use Moo;
has 'dbic_schema' => (is=>'ro', required=>1);
# Stuff!
package MyApp::Controller::Example;
use Moose; use MooseX::MethodAttributes;
sub entities :Local { my ($self, $c) = @_; my $e = $c->model('Entities'); }
![Page 60: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/60.jpg)
Could be useful to encapsulate complex
controller logic.
![Page 61: Catalyst patterns-yapc-eu-2016](https://reader031.vdocument.in/reader031/viewer/2022022412/58f2ff2c1a28ab250c8b456f/html5/thumbnails/61.jpg)
• https://metacpan.org/release/Catalyst-Plugin-MapComponentDependencies
• https://metacpan.org/release/Catalyst-Plugin-InjectionHelpers
• https://metacpan.org/release/Catalyst-Model-Adaptor
• https://metacpan.org/release/Catalyst-Component-InstancePerContext
• https://metacpan.org/release/Catalyst-Model-HTMLFormhandler
• https://metacpan.org/release/Catalyst-View-Text-MicroTemplate-PerRequest
• https://metacpan.org/release/Catalyst-View-JSON-PerRequest
• https://metacpan.org/release/Catalyst-Runtime