File Coverage

blib/lib/Catalyst/ControllerPerContext.pm
Criterion Covered Total %
statement 3 23 13.0
branch 0 4 0.0
condition 0 11 0.0
subroutine 1 3 33.3
pod 2 2 100.0
total 6 43 13.9


line stmt bran cond sub pod time code
1             package Catalyst::ControllerPerContext;
2              
3             our $VERSION = '0.004';
4              
5 1     1   1346 use Moose;
  1         489805  
  1         6  
6             extends 'Catalyst::Controller';
7              
8             has 'ctx' => (is=>'ro', required=>1);
9              
10             sub COMPONENT {
11 0     0 1   my ($class, $app, $args) = @_;
12 0           $args = $class->merge_config_hashes($args, $class->_config);
13              
14             ## All this crazy will probably break if you do even more insane things
15 0           my $application_self = bless $args, $class;
16 0           $application_self->{_application} = $app;
17              
18 0   0       my $action = delete $args->{action} || {};
19 0   0       my $actions = delete $args->{actions} || {};
20 0           $application_self->{actions} = $application_self->merge_config_hashes($actions, $action);
21 0   0       $application_self->{_all_actions_attributes} = delete $application_self->{actions}->{'*'} || {};
22 0   0       $application_self->{_action_role_args} = delete($application_self->{action_roles}) || [];
23 0 0         $application_self->{path_prefix} = delete $application_self->{path} if exists $application_self->{path};
24 0           $application_self->{_action_roles} = $application_self->_build__action_roles;
25 0 0         $application_self->{action_namespace} = $application_self->{namespace} if exists $application_self->{namespace};
26              
27 0           return $application_self;
28             }
29              
30             sub ACCEPT_CONTEXT {
31 0     0 1   my $application_self = shift;
32 0           my $c = shift;
33              
34 0           my $class = ref($application_self);
35 0   0       my $self = $c->stash->{"__ControllerPerContext_${class}"} ||= do {
36 0           my %args = (%$application_self, ctx=>$c, @_);
37 0           $class->new($c, \%args);
38             };
39              
40 0           return $self;
41             }
42              
43             __PACKAGE__->meta->make_immutable;
44              
45             =head1 NAME
46            
47             Catalyst::ControllerPerContext - Context Scoped Controlelrs
48              
49             =head1 SYNOPSIS
50              
51             package Example::Controller::Register;
52              
53             use Moose;
54             use MooseX::MethodAttributes;
55              
56             extends 'Catalyst::ControllerPerRequest';
57              
58             has registration => (
59             is => 'ro',
60             lazy => 1,
61             required => 1,
62             default => sub($self) { $self->ctx->Model('Users')->registration },
63             );
64              
65             sub root :Chained(/root) PathPart(register) Args(0) {
66             my ($self, $c) = @_;
67             $self->registration;
68             }
69              
70             __PACKAGE__->meta->make_immutable;
71              
72             =head1 DESCRIPTION
73              
74             Classic L<Catalyst::Controller>s are application scoped, which means we create an instance of the
75             controller when the application starts as a singleton instance, which is reused for all request
76             going forward. This has the lowest overhead. However it makes it hard to do things like use
77             controller attributes since those attributes get shared for all requests. By changing to creating
78             a new controller for each request you can then use those attributes.
79              
80             This for the most part is nothing you couldn't do with the stash but has the upside of avoiding
81             the stash typo bugs you see a lot and also the stash is shared for the entire context so stuff
82             you stuff in there might be too broadly accessible, whereas data in controller attributes is
83             scoped to the controller only.
84              
85             I consider this an experimental release to let interested people (including myself) to play with
86             the idea of per context controllers and see if it leads to new approaches and better code. Please
87             be warned that the code under the hood here is a bit of a hack up due to how we added some features
88             to Controller over time that made the assumption that an attribute is application scoped (such as
89             how we merged the action role support many years ago). If this turns out to be a good idea we'll
90             need to make deeper fixes to the base L<Catalyst::Controller> to make this right. As a result of
91             this hacking I can't be sure this controller will be a drop in replacement everywhere, especially
92             if you've doing a ton of customization to the base controller code in a custom controller subclass.
93              
94             In order to emphasize the magnitude of this crime / hack there's not really many test cased ;)
95              
96             Some of the things I'm using this to experiement with is using controller attributes to define
97             a stronger API between the controller and its views and using controller attributes as proxies
98             for the models a controller works with.
99              
100             =head1 ALSO SEE
101            
102             L<Catalyst::Runtime>, L<Catalyst::Controller>
103              
104             =head1 AUTHOR
105              
106             John Napiorkowski <jjnapiork@cpan.org>
107              
108             =head1 COPYRIGHT
109            
110             2022
111              
112             =head1 LICENSE
113              
114             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
115