line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::View::BasePerRequest; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.010'; |
4
|
|
|
|
|
|
|
our $DEFAULT_FACTORY = 'Catalyst::View::BasePerRequest::Lifecycle::Request'; |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
2202061
|
use Moose; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
7
|
1
|
|
|
1
|
|
6504
|
use HTTP::Status (); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
8
|
1
|
|
|
1
|
|
9
|
use Scalar::Util (); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
14
|
|
9
|
1
|
|
|
1
|
|
5
|
use Module::Runtime (); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
671
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
extends 'Catalyst::View'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
has 'catalyst_component_name' => (is=>'ro'); |
14
|
|
|
|
|
|
|
has 'app' => (is=>'ro', required=>1); |
15
|
|
|
|
|
|
|
has 'ctx' => (is=>'ro', required=>1); |
16
|
|
|
|
|
|
|
has 'root' => (is=>'rw', required=>1, default=>sub { shift }); |
17
|
|
|
|
|
|
|
has 'parent' => (is=>'rw', predicate=>'has_parent'); |
18
|
|
|
|
|
|
|
has 'content_type' => (is=>'ro', required=>0, predicate=>'has_content_type'); |
19
|
|
|
|
|
|
|
has 'code' => (is=>'rw', predicate=>'has_code'); |
20
|
|
|
|
|
|
|
has 'status_codes' => (is=>'rw', predicate=>'has_status_codes'); |
21
|
|
|
|
|
|
|
has 'injected_views' => (is=>'rw', predicate=>'has_injected_views'); |
22
|
|
|
|
|
|
|
has 'forwarded_args' => (is=>'rw', predicate=>'has_forwarded_args'); |
23
|
|
|
|
|
|
|
|
24
|
0
|
|
|
0
|
0
|
0
|
sub application { shift->app } |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub views { |
27
|
0
|
|
|
0
|
0
|
0
|
my ($app, %views) = @_; |
28
|
0
|
|
|
|
|
0
|
$app->config->{views} = \%views; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub view { |
32
|
0
|
|
|
0
|
0
|
0
|
my ($app, $method, @args) = @_; |
33
|
0
|
0
|
|
|
|
0
|
if(scalar(@args) > 1) { |
34
|
0
|
|
|
|
|
0
|
$app->config->{views}{$method} = \@args; |
35
|
|
|
|
|
|
|
} else { |
36
|
0
|
|
|
|
|
0
|
$app->config->{views}{$method} = $args[0]; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub set_content_type { |
41
|
0
|
|
|
0
|
0
|
0
|
my ($app, $ct) = @_; |
42
|
0
|
|
|
|
|
0
|
$app->config->{content_type} = $ct; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub set_status_codes { |
46
|
0
|
|
|
0
|
0
|
0
|
my $app = shift; |
47
|
0
|
0
|
|
|
|
0
|
my $codes = ref($_[0]) ? shift : [shift]; |
48
|
0
|
|
|
|
|
0
|
$app->config->{status_codes} = $codes; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub COMPONENT { |
52
|
3
|
|
|
3
|
1
|
101852
|
my ($class, $app, $args) = @_; |
53
|
3
|
|
|
|
|
13
|
my $merged_args = $class->merge_config_hashes($class->config, $args); |
54
|
|
|
|
|
|
|
|
55
|
3
|
50
|
|
|
|
319
|
$merged_args = $class->modify_init_args($app, $merged_args) if $class->can('modify_init_args'); |
56
|
3
|
|
|
|
|
20
|
my %status_codes = $class->inject_http_status_helpers($merged_args); |
57
|
3
|
100
|
|
|
|
14
|
$merged_args->{status_codes} = \%status_codes if scalar(keys(%status_codes)); |
58
|
3
|
|
|
|
|
19
|
my @injected_views = $class->inject_view_helpers($merged_args); |
59
|
3
|
50
|
|
|
|
7
|
$merged_args->{injected_views} = \@injected_views if scalar @injected_views; |
60
|
|
|
|
|
|
|
|
61
|
3
|
|
|
|
|
17
|
my $factory_class = Module::Runtime::use_module($class->factory_class($app, $merged_args)); |
62
|
3
|
|
|
|
|
88
|
return my $factory = $class->build_factory($factory_class, $app, $merged_args); |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub inject_view_helpers { |
66
|
3
|
|
|
3
|
0
|
10
|
my ($class, $merged_args) = @_; |
67
|
3
|
50
|
|
|
|
17
|
if(my $views = $merged_args->{views}) { |
68
|
0
|
|
|
|
|
0
|
require Sub::Util; |
69
|
0
|
|
|
|
|
0
|
foreach my $method (keys %$views) { |
70
|
0
|
|
|
|
|
0
|
my ($view_name, @args_proto) = (); |
71
|
0
|
|
|
|
|
0
|
my $options_proto = $views->{$method}; |
72
|
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
0
|
my $global_args_generator; |
74
|
0
|
0
|
0
|
|
|
0
|
if( (ref($options_proto)||'') eq 'ARRAY') { |
75
|
0
|
|
|
|
|
0
|
($view_name, @args_proto) = @$options_proto; |
76
|
|
|
|
|
|
|
$global_args_generator = (ref($args_proto[0])||'') eq 'CODE' ? |
77
|
|
|
|
|
|
|
shift(@args_proto) : |
78
|
0
|
0
|
0
|
0
|
|
0
|
sub { @args_proto }; |
|
0
|
|
|
|
|
0
|
|
79
|
|
|
|
|
|
|
} else { |
80
|
0
|
|
|
|
|
0
|
$view_name = $options_proto; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
1
|
|
|
1
|
|
8
|
no strict 'refs'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2151
|
|
84
|
0
|
|
|
|
|
0
|
*{"${class}::${method}"} = Sub::Util::set_subname "${class}::${method}" => sub { |
85
|
0
|
|
|
0
|
|
0
|
my ($self, @args) = @_; |
86
|
0
|
0
|
|
|
|
0
|
my @global_args = $global_args_generator ? $global_args_generator->($self, $self->ctx, @args) : (); |
87
|
0
|
|
|
|
|
0
|
my $view = $self->ctx->view($view_name, @global_args, @args); |
88
|
|
|
|
|
|
|
|
89
|
0
|
|
|
|
|
0
|
$view->root($self->root); |
90
|
0
|
|
|
|
|
0
|
$view->parent($self); |
91
|
|
|
|
|
|
|
|
92
|
0
|
|
|
|
|
0
|
return $view; |
93
|
0
|
|
|
|
|
0
|
}; |
94
|
|
|
|
|
|
|
} |
95
|
0
|
|
|
|
|
0
|
return keys %$views; |
96
|
|
|
|
|
|
|
} |
97
|
3
|
|
|
|
|
9
|
return (); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub inject_http_status_helpers { |
101
|
3
|
|
|
3
|
0
|
10
|
my ($class, $merged_args) = @_; |
102
|
|
|
|
|
|
|
|
103
|
3
|
|
|
|
|
8
|
my %status_codes = (); |
104
|
3
|
100
|
|
|
|
10
|
if(exists $merged_args->{status_codes}) { |
105
|
2
|
|
|
|
|
4
|
%status_codes = map { $_=>1 } @{$merged_args->{status_codes}}; |
|
6
|
|
|
|
|
22
|
|
|
2
|
|
|
|
|
6
|
|
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
3
|
|
|
|
|
14
|
foreach my $helper( grep { $_=~/^http/i} @HTTP::Status::EXPORT_OK) { |
|
219
|
|
|
|
|
477
|
|
109
|
207
|
|
|
|
|
498
|
my $subname = lc $helper; |
110
|
207
|
|
|
|
|
624
|
my $code = HTTP::Status->$helper; |
111
|
207
|
100
|
|
|
|
436
|
if(scalar(keys(%status_codes))) { |
112
|
138
|
100
|
|
|
|
305
|
next unless $status_codes{$code}; |
113
|
|
|
|
|
|
|
} |
114
|
75
|
|
|
0
|
0
|
130
|
eval "sub ${\$class}::${\$subname} { return shift->respond(HTTP::Status::$helper,\\\@_) }"; |
|
75
|
|
|
0
|
0
|
179
|
|
|
75
|
|
|
0
|
0
|
3805
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
1
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
1
|
|
|
0
|
0
|
643
|
|
|
0
|
|
|
1
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
1
|
|
|
0
|
0
|
622
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
0
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
115
|
75
|
0
|
|
0
|
0
|
229
|
eval "sub ${\$class}::set_${\$subname} { |
|
75
|
0
|
|
0
|
0
|
210
|
|
|
75
|
0
|
|
0
|
0
|
5794
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
50
|
|
1
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
1
|
0
|
|
0
|
0
|
608
|
|
|
1
|
0
|
|
0
|
0
|
34
|
|
|
1
|
0
|
|
0
|
0
|
200
|
|
|
1
|
0
|
|
0
|
0
|
218
|
|
|
0
|
50
|
|
1
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
0
|
|
0
|
0
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
605
|
|
|
1
|
|
|
|
|
32
|
|
|
1
|
|
|
|
|
161
|
|
|
1
|
|
|
|
|
3
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
116
|
|
|
|
|
|
|
my (\$self, \@headers) = \@_; |
117
|
|
|
|
|
|
|
\$self->ctx->res->status(HTTP::Status::$helper); |
118
|
|
|
|
|
|
|
\$self->ctx->res->headers->push_header(\@headers) if \@headers; |
119
|
|
|
|
|
|
|
return \$self; |
120
|
|
|
|
|
|
|
}"; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
3
|
|
|
|
|
28
|
return %status_codes; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub factory_class { |
127
|
3
|
|
|
3
|
0
|
8
|
my ($class, $app, $merged_args) = @_; |
128
|
3
|
100
|
|
|
|
10
|
if(exists $merged_args->{lifecycle}) { |
129
|
1
|
|
|
|
|
11
|
my $lifecycle = $merged_args->{lifecycle}; |
130
|
1
|
50
|
|
|
|
22
|
$lifecycle = "Catalyst::View::BasePerRequest::Lifecycle::${lifecycle}" unless $lifecycle=~/^\+/; |
131
|
1
|
|
|
|
|
6
|
return $lifecycle; |
132
|
|
|
|
|
|
|
} |
133
|
2
|
|
|
|
|
10
|
return $DEFAULT_FACTORY; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub build { |
137
|
24
|
|
|
24
|
1
|
87
|
my ($class, %args) = @_; |
138
|
24
|
|
|
|
|
894
|
return $class->new(%args); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub build_factory { |
142
|
3
|
|
|
3
|
0
|
13
|
my ($class, $factory_class, $app, $merged_args) = @_; |
143
|
3
|
|
|
|
|
25
|
return my $factory = bless +{ |
144
|
|
|
|
|
|
|
class=>$class, |
145
|
|
|
|
|
|
|
app=>$app, |
146
|
|
|
|
|
|
|
merged_args=>$merged_args, |
147
|
|
|
|
|
|
|
}, $factory_class; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub render { |
151
|
0
|
|
|
0
|
0
|
0
|
my ($self, $ctx, @args) = @_; |
152
|
0
|
|
|
|
|
0
|
die "You need to write a 'render' method!"; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub process { |
156
|
1
|
|
|
1
|
1
|
1701
|
my ($self, $c, @args) = @_; |
157
|
1
|
|
|
|
|
44
|
$self->forwarded_args(\@args); |
158
|
1
|
|
|
|
|
6
|
return $self->respond(); |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub respond { |
162
|
3
|
|
|
3
|
1
|
11
|
my ($self, $status, $headers, @args) = @_; |
163
|
3
|
|
|
|
|
95
|
for my $r ($self->ctx->res) { |
164
|
3
|
100
|
66
|
|
|
132
|
$r->status($status) if $status && $r->status; # != 200; # Catalyst sets 200 |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Module::Runtime::use_module('Catalyst::View::BasePerRequest::Exception::InvalidStatusCode')->throw(status_code=>$r->status) |
167
|
3
|
50
|
33
|
|
|
519
|
if $self->has_status_codes && !$self->status_codes->{$r->status}; |
168
|
|
|
|
|
|
|
|
169
|
3
|
50
|
33
|
|
|
356
|
$r->content_type($self->content_type) if !$r->content_type && $self->has_content_type; |
170
|
3
|
100
|
|
|
|
620
|
$r->headers->push_header(@{$headers}) if $headers; |
|
2
|
|
|
|
|
198
|
|
171
|
3
|
|
|
|
|
44
|
$r->body($self->get_rendered(@args)); |
172
|
|
|
|
|
|
|
} |
173
|
3
|
|
|
|
|
36
|
return $self; # allow chaining |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub get_rendered { |
177
|
24
|
|
|
24
|
0
|
1444
|
my $self = shift; |
178
|
24
|
|
|
|
|
49
|
my @rendered = (); |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
eval { |
181
|
24
|
|
|
|
|
71
|
my @args = $self->prepare_render_args(@_); |
182
|
|
|
|
|
|
|
@rendered = map { |
183
|
24
|
100
|
66
|
|
|
95
|
Scalar::Util::blessed($_) && $_->can('get_rendered') ? $_->get_rendered : $_; |
|
24
|
|
|
|
|
1038
|
|
184
|
|
|
|
|
|
|
} $self->render(@args); |
185
|
24
|
|
|
|
|
77
|
1; |
186
|
24
|
50
|
|
|
|
49
|
} || do { |
187
|
0
|
|
|
|
|
0
|
$self->do_handle_render_exception($@); |
188
|
|
|
|
|
|
|
}; |
189
|
|
|
|
|
|
|
|
190
|
24
|
|
|
|
|
70
|
return $self->flatten_rendered_for_response_body(@rendered); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
24
|
|
|
24
|
0
|
62
|
sub flatten_rendered_for_response_body { return shift->flatten_rendered(@_) } |
194
|
|
|
|
|
|
|
|
195
|
0
|
|
|
0
|
0
|
0
|
sub render_error_class { 'Catalyst::View::BasePerRequest::Exception::RenderError' } |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub do_handle_render_exception { |
198
|
0
|
|
|
0
|
0
|
0
|
my ($self, $err) = @_; |
199
|
0
|
0
|
0
|
|
|
0
|
return $err->rethrow if Scalar::Util::blessed($err) && $err->can('rethrow'); |
200
|
0
|
|
|
|
|
0
|
my $class = Module::Runtime::use_module($self->render_error_class); |
201
|
0
|
|
|
|
|
0
|
$class->throw(render_error=>$err); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub prepare_render_args { |
205
|
24
|
|
|
24
|
0
|
54
|
my ($self, @args) = @_; |
206
|
|
|
|
|
|
|
|
207
|
24
|
100
|
|
|
|
905
|
if($self->has_code) { |
208
|
6
|
|
|
|
|
16
|
my $inner = $self->render_code; |
209
|
6
|
|
|
|
|
16
|
unshift @args, $inner; # pass any $inner as an argument to ->render() |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
24
|
|
|
|
|
725
|
return ($self->ctx, @args); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub render_code { |
216
|
6
|
|
|
6
|
0
|
12
|
my $self = shift; |
217
|
|
|
|
|
|
|
my @inner = map { |
218
|
6
|
100
|
66
|
|
|
16
|
Scalar::Util::blessed($_) && $_->can('get_rendered') ? $_->get_rendered : $_; |
|
18
|
|
|
|
|
392
|
|
219
|
|
|
|
|
|
|
} $self->execute_code_callback($self->prepare_render_code_args); |
220
|
|
|
|
|
|
|
|
221
|
6
|
|
|
|
|
20
|
my $flat = $self->flatten_rendered_for_inner_content(@inner); |
222
|
6
|
|
|
|
|
18
|
return $flat; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub execute_code_callback { |
226
|
6
|
|
|
6
|
0
|
25
|
my ($self, @args) = @_; |
227
|
6
|
|
|
|
|
201
|
return $self->code->(@args); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub prepare_render_code_args { |
231
|
6
|
|
|
6
|
0
|
13
|
my ($self) = @_; |
232
|
6
|
|
|
|
|
17
|
return $self; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
6
|
|
|
6
|
0
|
21
|
sub flatten_rendered_for_inner_content { return shift->flatten_rendered(@_) } |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub flatten_rendered { |
238
|
54
|
|
|
54
|
0
|
122
|
my $self = shift; |
239
|
54
|
|
|
|
|
96
|
return join '', grep { defined($_) } @_; |
|
66
|
|
|
|
|
932
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub content { |
243
|
12
|
|
|
12
|
1
|
1167
|
my ($self, $name, $options) = @_; |
244
|
12
|
100
|
|
|
|
38
|
my %options = $options ? %$options : (); |
245
|
12
|
100
|
|
|
|
32
|
my $default = exists($options{default}) ? $options{default} : ''; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
return exists($self->ctx->stash->{view_blocks}{$name}) ? |
248
|
12
|
100
|
|
|
|
369
|
$self->ctx->stash->{view_blocks}{$name} : |
249
|
|
|
|
|
|
|
$default; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub render_content_value { |
253
|
24
|
|
|
24
|
0
|
750
|
my $self = shift; |
254
|
24
|
100
|
100
|
|
|
88
|
if((ref($_[0])||'') eq 'CODE') { |
255
|
6
|
|
|
|
|
25
|
return $self->flatten_rendered_for_content_blocks($_[0]->($_[1])); |
256
|
|
|
|
|
|
|
} else { |
257
|
18
|
|
|
|
|
44
|
return $self->flatten_rendered_for_content_blocks(@_); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
24
|
|
|
24
|
0
|
79
|
sub flatten_rendered_for_content_blocks { return shift->flatten_rendered(@_) } |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub content_for { |
264
|
6
|
|
|
6
|
1
|
66
|
my ($self, $name, $value) = @_; |
265
|
6
|
50
|
|
|
|
19
|
Module::Runtime::use_module($self->_content_exception_class) |
266
|
|
|
|
|
|
|
->throw(content_name=>$name, content_msg=>'Content block is already defined') if $self->_content_exists($name); |
267
|
6
|
|
|
|
|
410
|
$self->ctx->stash->{view_blocks}{$name} = $self->render_content_value($value); |
268
|
6
|
|
|
|
|
377
|
return; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub content_append { |
272
|
6
|
|
|
6
|
1
|
40
|
my ($self, $name, $value) = @_; |
273
|
6
|
50
|
|
|
|
14
|
Module::Runtime::use_module($self->_content_exception_class) |
274
|
|
|
|
|
|
|
->throw(content_name=>$name, content_msg=>'Content block doesnt exist for appending') unless $self->_content_exists($name); |
275
|
6
|
|
|
|
|
549
|
$self->ctx->stash->{view_blocks}{$name} .= $self->render_content_value($value); |
276
|
6
|
|
|
|
|
18
|
return; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub content_prepend { |
280
|
6
|
|
|
6
|
0
|
40
|
my ($self, $name, $value) = @_; |
281
|
6
|
50
|
|
|
|
12
|
Module::Runtime::use_module($self->_content_exception_class) |
282
|
|
|
|
|
|
|
->throw(content_name=>$name, content_msg=>'Content block doesnt exist for prepending') unless $self->_content_exists($name); |
283
|
6
|
|
|
|
|
387
|
$self->ctx->stash->{view_blocks}{$name} = $self->render_content_value($value) . $self->ctx->stash->{view_blocks}{$name}; |
284
|
6
|
|
|
|
|
369
|
return; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub content_replace { |
289
|
0
|
|
|
0
|
1
|
0
|
my ($self, $name, $value) = @_; |
290
|
0
|
0
|
|
|
|
0
|
Module::Runtime::use_module($self->_content_exception_class) |
291
|
|
|
|
|
|
|
->throw(content_name=>$name, content_msg=>'Content block doesnt exist for replacing') unless $self->_content_exists($name); |
292
|
0
|
|
|
|
|
0
|
$self->ctx->stash->{view_blocks}{$name} = $self->render_content_value($value); |
293
|
0
|
|
|
|
|
0
|
return; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub content_around { |
297
|
6
|
|
|
6
|
1
|
55
|
my ($self, $name, $value) = @_; |
298
|
6
|
50
|
|
|
|
15
|
Module::Runtime::use_module($self->_content_exception_class) |
299
|
|
|
|
|
|
|
->throw(content_name=>$name, content_msg=>'Content block doesnt exist') unless $self->_content_exists($name); |
300
|
6
|
|
|
|
|
608
|
$self->ctx->stash->{view_blocks}{$name} = $self->render_content_value($value, $self->ctx->stash->{view_blocks}{$name}); |
301
|
6
|
|
|
|
|
373
|
return; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
0
|
|
|
0
|
|
0
|
sub _content_exception_class { return 'Catalyst::View::BasePerRequest::Exception::ContentBlockError' } |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub _content_exists { |
307
|
24
|
|
|
24
|
|
46
|
my ($self, $name) = @_; |
308
|
24
|
100
|
|
|
|
763
|
return exists $self->ctx->stash->{view_blocks}{$name} ? 1:0; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
0
|
|
|
0
|
1
|
|
sub detach { return shift->ctx->detach } |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=head1 NAME |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
Catalyst::View::Template::BasePerRequest - Catalyst base view for per request, strongly typed templates |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=head1 SYNOPSIS |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
package Example::View::Hello; |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
use Moose; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
has name => (is=>'ro', required=>1); |
328
|
|
|
|
|
|
|
has age => (is=>'ro', required=>1); |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub render { |
331
|
|
|
|
|
|
|
my ($self, $c) = @_; |
332
|
|
|
|
|
|
|
return "<div>Hello @{[ $self->name] }", |
333
|
|
|
|
|
|
|
"I see you are @{[ $self->age]} years old!</div>"; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
__PACKAGE__->config( |
337
|
|
|
|
|
|
|
content_type=>'text/html', |
338
|
|
|
|
|
|
|
status_codes=>[200] |
339
|
|
|
|
|
|
|
); |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
One way to use it in a controller: |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
package Example::Controller::Root; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
use Moose; |
348
|
|
|
|
|
|
|
use MooseX::MethodAttributes; |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
extends 'Catalyst::Controller'; |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub root :Chained(/) PathPart('') CaptureArgs(0) { } |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub hello :Chained(root) Args(0) { |
355
|
|
|
|
|
|
|
my ($self, $c) = @_; |
356
|
|
|
|
|
|
|
return $c->view(Hello => |
357
|
|
|
|
|
|
|
name => 'John', |
358
|
|
|
|
|
|
|
age => 53 |
359
|
|
|
|
|
|
|
)->http_ok; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
__PACKAGE__->config(namespace=>''); |
363
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head1 DESCRIPTION |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
B<NOTE>: This is early access code. Although it's based on several other internal projects |
368
|
|
|
|
|
|
|
which I have iterated over this concept for a number of years I still reserve the right |
369
|
|
|
|
|
|
|
to make breaking changes as needed. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
B<NOTE>: You probably won't actually use this directly, it's intended to be a base framework |
372
|
|
|
|
|
|
|
for building / prototyping strongly typed / per request views in L<Catalyst>. This |
373
|
|
|
|
|
|
|
documentation serves as an overview of the concept. In particular please note that |
374
|
|
|
|
|
|
|
this code does not address any issues around HTML / Javascript injection attacks or |
375
|
|
|
|
|
|
|
provides any auto escaping. You'll need to bake those features into whatever you |
376
|
|
|
|
|
|
|
build on top of this. Because of this the following documentation is light and is mostly |
377
|
|
|
|
|
|
|
intended to help anyone who is planning to build something on top of this framework |
378
|
|
|
|
|
|
|
rather than use it directly. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
B<NOTE>: This distribution's C</example> directory gives you a toy prototype using L<HTML::Tags> |
381
|
|
|
|
|
|
|
as the basis to a view as well as some raw examples using this code directly (again, |
382
|
|
|
|
|
|
|
not recommended for anything other than learning). |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
In a classic L<Catalyst> application with server side templates, the canonical approach |
385
|
|
|
|
|
|
|
is to use a 'view' as a sort of handler for an underlying template system (such as |
386
|
|
|
|
|
|
|
L<Template::Toolkit> or L<Xslate>) and to send data to this template by populating |
387
|
|
|
|
|
|
|
the stash. These views are very lean, and in general don't provide much in the way |
388
|
|
|
|
|
|
|
of view logic processing; they generally are just a thin proxy for the underlying |
389
|
|
|
|
|
|
|
templating system. |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
This approach has the upside of being very simple to understand and in general works |
392
|
|
|
|
|
|
|
ok with a simple websites. There are however downsides as your site becomes more |
393
|
|
|
|
|
|
|
complex. First of all the stash as a means to pass data from the Controller to the |
394
|
|
|
|
|
|
|
template can be fragile. For example just making a simple typo in the stash key |
395
|
|
|
|
|
|
|
can break your templates in ways that might not be easy to figure out. Also your |
396
|
|
|
|
|
|
|
template can't enforce its requirements very easily (and it's not easy for someone |
397
|
|
|
|
|
|
|
working in the controller to know exactly what things need to go into the stash in |
398
|
|
|
|
|
|
|
order for the template to function as desired.) The view itself has no way of |
399
|
|
|
|
|
|
|
providing view / display oriented logic; generally that logic ends up creeping back up |
400
|
|
|
|
|
|
|
into the controller in ways that break the notion of MVC's separation of concerns. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Lastly the controller doesn't have a defined API with the view. All it can ask the view |
403
|
|
|
|
|
|
|
is 'go ahead and process yourself using the current context' and all it gets back from |
404
|
|
|
|
|
|
|
the view is a string response. If the controller wishes to introspect this response |
405
|
|
|
|
|
|
|
or modify it in some way prior to it being sent back to the client, you have few options |
406
|
|
|
|
|
|
|
apart from using regular expression matching to try and extract the required information |
407
|
|
|
|
|
|
|
or to modify the response string. |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Basically the classic approach works acceptable well for a simple website but starts to |
410
|
|
|
|
|
|
|
break down as your site becomes more complicated. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
An alternative approach, which is explored in this distribution, is to have a defined view for |
413
|
|
|
|
|
|
|
each desired response and for it to define an explicit API that the controller uses to provide the required |
414
|
|
|
|
|
|
|
and optional data to the view. This defined view can further define its own methods |
415
|
|
|
|
|
|
|
used to generate suitable information for display. Such an approach is more initial work |
416
|
|
|
|
|
|
|
as well as learning for the website developers, but in the long term it can provide |
417
|
|
|
|
|
|
|
an easier path to sustainable development and maintainence with hopefully fewer bugs |
418
|
|
|
|
|
|
|
and overall site issues. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 EXAMPLE: Basic |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
The most minimal thing your view must provide in a C<render> method. This method gets |
423
|
|
|
|
|
|
|
the view object and the context (it can also receive additional arguments if this view is |
424
|
|
|
|
|
|
|
being called from other views as a wrapper or parent view; more on that later). |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
The C<render> method should return a string or array of strings suitable for the body of |
427
|
|
|
|
|
|
|
the response> B<NOTE> if you return an array of strings we flatten the array into a single |
428
|
|
|
|
|
|
|
string since the C<body> method of L<Catalyst::Response> can't take an array. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Here's a minimal example: |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
package Example::View::Hello; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
use Moose; |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub render { |
439
|
|
|
|
|
|
|
my ($self, $c) = @_; |
440
|
|
|
|
|
|
|
return "<p>Hello</p>"; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
__PACKAGE__->config(content_type=>'text/html'); |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
And here's an example view with attributes: |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
package Example::View::HelloPerson; |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
use Moose; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
has name => (is=>'ro', required=>1); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
sub render { |
456
|
|
|
|
|
|
|
my ($self, $c) = @_; |
457
|
|
|
|
|
|
|
return qq[ |
458
|
|
|
|
|
|
|
<div> |
459
|
|
|
|
|
|
|
Hello @{[ $self->name ]} |
460
|
|
|
|
|
|
|
</div>]; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
One way to invoke this view from the controller using the traditional C<forward> method: |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
package Example::Controller::Root; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
use Moose; |
470
|
|
|
|
|
|
|
use MooseX::MethodAttributes; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
extends 'Catalyst::Controller'; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub root :Chained(/) PathPart('') CaptureArgs(0) { } |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub hello :Chained(root) Args(0) { |
477
|
|
|
|
|
|
|
my ($self, $c) = @_; |
478
|
|
|
|
|
|
|
my $view = $c->view(HelloPerson => (name => 'John')); |
479
|
|
|
|
|
|
|
return $c->forward($view); |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
__PACKAGE__->config(namespace=>''); |
483
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Alternatively using L</"RESPONSE HELPERS">: |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
package Example::Controller::Root; |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
use Moose; |
490
|
|
|
|
|
|
|
use MooseX::MethodAttributes; |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
extends 'Catalyst::Controller'; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub root :Chained(/) PathPart('') CaptureArgs(0) { } |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub hello :Chained(root) Args(0) { |
497
|
|
|
|
|
|
|
my ($self, $c) = @_; |
498
|
|
|
|
|
|
|
return $c->view(HelloPerson => (name => 'John'))->http_ok; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
__PACKAGE__->config(namespace=>''); |
502
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
The following Moose attributes are considered part of this classes public API |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=head2 app |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
The string namespace of your L<Catalyst> application. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 ctx |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
The current L<Catalyst> context |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head2 root |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
The root view object (that is the top view that was called first, usually from |
519
|
|
|
|
|
|
|
the controller). |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=head2 parent |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=head2 has_parent |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
If the view was called from another view, that first view is set as the parent. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 injected_views |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 has_injected_views |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
An arrayref of the method names associated with any injected views. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=head1 METHODS |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
The following methods are considered part of this classes public API |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head2 process |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Renders a view and sets up the response object. Generally this is called from a |
540
|
|
|
|
|
|
|
controller via the C<forward> method and not directly: |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
$c->forward($view); |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=head2 respond |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
Accepts an HTTP status code and an arrayref of key / values used to set HTTP headers for a |
547
|
|
|
|
|
|
|
response. Example: |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
$view->respond(201, [ location=>$url ]); |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Returns the view object to make it easier to do method chaining |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head2 detach |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Just a shortcut to ->detach via the context |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head1 CONTENT BLOCK HELPERS |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
Content block helpers are an optional feature to make it easy to create and populate content |
560
|
|
|
|
|
|
|
areas between different views. Although you can also do this with object attributes you may |
561
|
|
|
|
|
|
|
wish to separate template / text from data. Example: |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
package Example::View::Layout; |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
use Moose; |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
has title => (is=>'ro', required=>1, default=>'Missing Title'); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
sub render { |
572
|
|
|
|
|
|
|
my ($self, $c, $inner) = @_; |
573
|
|
|
|
|
|
|
return " |
574
|
|
|
|
|
|
|
<html> |
575
|
|
|
|
|
|
|
<head> |
576
|
|
|
|
|
|
|
<title>@{[ $self->title ]}</title> |
577
|
|
|
|
|
|
|
@{[ $self->content('css') ]} |
578
|
|
|
|
|
|
|
</head> |
579
|
|
|
|
|
|
|
<body>$inner</body> |
580
|
|
|
|
|
|
|
</html>"; |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
__PACKAGE__->config(content_type=>'text/html'); |
584
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
package Example::View::Hello; |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
use Moose; |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
has name => (is=>'ro', required=>1); |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
sub render { |
595
|
|
|
|
|
|
|
my ($self, $c) = @_; |
596
|
|
|
|
|
|
|
return $c->view(Layout => title=>'Hello', sub { |
597
|
|
|
|
|
|
|
my $layout = shift; |
598
|
|
|
|
|
|
|
$self->content_for('css', "<style>...</style>"); |
599
|
|
|
|
|
|
|
return "<div>Hello @{[ $self->name ]}!</div>"; |
600
|
|
|
|
|
|
|
}); |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
__PACKAGE__->config(content_type=>'text/html', status_codes=>[200]); |
604
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=head2 content |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
Examples: |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
$self->content($name); |
611
|
|
|
|
|
|
|
$self->content($name, +{ default=>'No main content' }); |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
Gets a content block string by '$name'. If the block has not been defined returns either |
614
|
|
|
|
|
|
|
a zero length string or whatever you set the default key of the hashref options to. |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=head2 content_for |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Sets a named content block or throws an exception if the content block already exists. |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=head2 content_append |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
Appends to a named content block or throws an exception if the content block doesn't exist. |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=head2 content_replace |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
Replaces a named content block or throws an exception if the content block doesn't exist. |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=head2 content_around |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Wraps an existing content with new content. Throws an exception if the named content block doesn't exist. |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
$self->content_around('footer', sub { |
633
|
|
|
|
|
|
|
my $footer = shift; |
634
|
|
|
|
|
|
|
return "wrapped $footer end wrap"; |
635
|
|
|
|
|
|
|
}); |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head1 VIEW INJECTION |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Usually when building a website of more than toy complexity you will find that you will |
640
|
|
|
|
|
|
|
decompose your site into sub views and view wrappers. Although you can call the C<view> |
641
|
|
|
|
|
|
|
method on the context, I think its more in the spirit of the idea of a strong or structured |
642
|
|
|
|
|
|
|
view to have a view declare upfront what views its calling as sub views. That lets you |
643
|
|
|
|
|
|
|
have more central control over view initalization and decouples how you are calling your |
644
|
|
|
|
|
|
|
views from the actual underlying views. It can also tidy up some of the code and lastly |
645
|
|
|
|
|
|
|
makes it easy to immediately know what views are needed for the current one. This can |
646
|
|
|
|
|
|
|
help with later refactoring (I've worked on projects where sub views got detached from |
647
|
|
|
|
|
|
|
actual use but nobody ever cleaned them up.) |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
To inject a view into the current one, you need to declare it in configuration: |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
__PACKAGE__->config( |
652
|
|
|
|
|
|
|
content_type=>'text/html', |
653
|
|
|
|
|
|
|
status_codes=>[200,404,400], |
654
|
|
|
|
|
|
|
views=>+{ |
655
|
|
|
|
|
|
|
layout1 => [ Layout => sub { my ($self, $c) = @_; return title=>'Hey!' } ], |
656
|
|
|
|
|
|
|
layout2 => [ Layout => (title=>'Yeah!') ], |
657
|
|
|
|
|
|
|
layout3 => 'Layout', |
658
|
|
|
|
|
|
|
}, |
659
|
|
|
|
|
|
|
); |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Basically this is a hashref under the C<views> key, where each key in the hashref is the name |
662
|
|
|
|
|
|
|
of the method you are injecting into the current view which is responsible for creating the |
663
|
|
|
|
|
|
|
sub view and the value is one of three options: |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=over |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=item A scalar value |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
__PACKAGE__->config( |
670
|
|
|
|
|
|
|
content_type=>'text/html', |
671
|
|
|
|
|
|
|
status_codes=>[200,404,400], |
672
|
|
|
|
|
|
|
views=>+{ |
673
|
|
|
|
|
|
|
layout => 'Layout', |
674
|
|
|
|
|
|
|
}, |
675
|
|
|
|
|
|
|
); |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
This is the simplest option, it just injects a method that will call the named view and pass |
678
|
|
|
|
|
|
|
any arguments from the method but does not add any global arguments. |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=item An arrayref |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
__PACKAGE__->config( |
683
|
|
|
|
|
|
|
content_type=>'text/html', |
684
|
|
|
|
|
|
|
status_codes=>[200,404,400], |
685
|
|
|
|
|
|
|
views=>+{ |
686
|
|
|
|
|
|
|
layout => [ Layout => (title=>'Yeah!') ], |
687
|
|
|
|
|
|
|
}, |
688
|
|
|
|
|
|
|
); |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
This option allows you to set some argument defaults to the view called. The first item in the |
691
|
|
|
|
|
|
|
arrayref must be the real name of the view, followed by arguments which are merged with any provided |
692
|
|
|
|
|
|
|
to the method. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=item A coderef |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
__PACKAGE__->config( |
697
|
|
|
|
|
|
|
content_type=>'text/html', |
698
|
|
|
|
|
|
|
status_codes=>[200,404,400], |
699
|
|
|
|
|
|
|
views=>+{ |
700
|
|
|
|
|
|
|
layout => [ Layout => sub { my ($self, $c) = @_; return title=>'Hey!' } ], |
701
|
|
|
|
|
|
|
}, |
702
|
|
|
|
|
|
|
); |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
The most complex option, you should probably reserve for very special needs. Basically this coderef |
705
|
|
|
|
|
|
|
will be called with the current view instance and Catalyst context; it should return arguments which |
706
|
|
|
|
|
|
|
with then be merged and treated as in the arrayref option. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=back |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
Now you can call for the sub view via a simple method call on the view, rather than via the context: |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
package Example::View::Hello; |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
use Moose; |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
extends 'Catalyst::View::BasePerRequest'; |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
has name => (is=>'ro', required=>1); |
719
|
|
|
|
|
|
|
has age => (is=>'ro', required=>1); |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
sub render { |
723
|
|
|
|
|
|
|
my ($self, $c) = @_; |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
return $self->layout(title=>'Hello!', sub { |
726
|
|
|
|
|
|
|
my $layout = shift; |
727
|
|
|
|
|
|
|
return "Hello @{[$self->name]}; you are @{[$self->age]} years old!"; |
728
|
|
|
|
|
|
|
}); |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
__PACKAGE__->config( |
733
|
|
|
|
|
|
|
content_type=>'text/html', |
734
|
|
|
|
|
|
|
status_codes=>[200,404,400], |
735
|
|
|
|
|
|
|
views=>+{ |
736
|
|
|
|
|
|
|
layout => 'Layout', |
737
|
|
|
|
|
|
|
}, |
738
|
|
|
|
|
|
|
); |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=head1 RESPONSE HELPERS |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
When you create a view instance the actual response is not send to the client until |
746
|
|
|
|
|
|
|
the L</respond> method is called (either directly, via L</process> or thru the generated |
747
|
|
|
|
|
|
|
response helpers). |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Response helpers are just methods that call L</respond> with the correct status code |
750
|
|
|
|
|
|
|
and using a more easy to remember name (and possibly a more self documenting one). |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
For example: |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
$c->view('Login')->http_ok; |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
calls the L</respond> method with the expected http status code. You can also pass |
757
|
|
|
|
|
|
|
arguments to the response helper which are send to L</respond> and used to add HTTP |
758
|
|
|
|
|
|
|
headers to the response. |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
$c->view("NewUser") |
761
|
|
|
|
|
|
|
->http_created(location=>$url); |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
Please note that calling a response helper only sets up the response object, it doesn't |
764
|
|
|
|
|
|
|
stop any future actions in you controller. If you really want to stop action processing |
765
|
|
|
|
|
|
|
you'll need to call L</detach>: |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
return $c->view("Error") |
768
|
|
|
|
|
|
|
->http_bad_request |
769
|
|
|
|
|
|
|
->detach; |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
If you don't want to generate the response yet (perhaps you'll leave that to a global 'end' |
772
|
|
|
|
|
|
|
action) you can use the 'set_http_$STATUS' helpers instead which wil just set the response |
773
|
|
|
|
|
|
|
status. |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
return $c->view("Error") |
776
|
|
|
|
|
|
|
->set_http_bad_request |
777
|
|
|
|
|
|
|
->detach; |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
Response helpers are just lowercased names you'll find for the codes listed in L<HTTP::Status>. |
780
|
|
|
|
|
|
|
Some of the most common ones I find in my code: |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
http_ok |
783
|
|
|
|
|
|
|
http_created |
784
|
|
|
|
|
|
|
http_bad_request |
785
|
|
|
|
|
|
|
http_unauthorized |
786
|
|
|
|
|
|
|
http_not_found |
787
|
|
|
|
|
|
|
http_internal_server_error |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
By default we create response helpers for all the status codes in L<HTTP::Status>. However |
790
|
|
|
|
|
|
|
if you set the C<status_codes> configuration key (see L</status_codes>) you can limit the |
791
|
|
|
|
|
|
|
generated helpers to specific codes. This can be useful since most views are only meaningful |
792
|
|
|
|
|
|
|
with a limited set of response codes. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=head1 RUNTIME HOOKS |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
This class defines the following method hooks you may optionally defined in your |
797
|
|
|
|
|
|
|
view subclass in order to control or otherwise influence how the view works. |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=head2 $class->modify_init_args($app, $args) |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Runs when C<COMPONENT> is called during C<setup_components>. This gets a reference |
802
|
|
|
|
|
|
|
to the merged arguments from all configuration. You should return this reference |
803
|
|
|
|
|
|
|
after modification. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
This is for modifying or adding arguments that are application scoped rather than context |
806
|
|
|
|
|
|
|
scoped. |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
=head2 prepare_build_args |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
This method will be called (if defined) by the factory class during build time. It can be used |
811
|
|
|
|
|
|
|
to inject args and modify args. It gets the context and C<@args> as arguments and should return |
812
|
|
|
|
|
|
|
all the arguments you want to pass to C<new>. Example: |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
sub prepare_build_args { |
815
|
|
|
|
|
|
|
my ($class, $c, @args) = @_; |
816
|
|
|
|
|
|
|
# Mess with @args |
817
|
|
|
|
|
|
|
return @args; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=head2 build |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
Receives the initialization hash and should return a new instance of the the view. By default this |
823
|
|
|
|
|
|
|
just calls C<new> on the class with the hash of args but if you need to call some other method or |
824
|
|
|
|
|
|
|
have some complex initialization work that can't be handled with L</prepare_build_args> you can |
825
|
|
|
|
|
|
|
override. |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=head1 CONFIGURATION |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
This Catalyst Component supports the following configuation. |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
=head2 content_type |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
The HTTP content type of the response. For example 'text/html'. Required. |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=head2 status_codes |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
An ArrayRef of HTTP status codes used to provide response helpers. This is optional |
838
|
|
|
|
|
|
|
but it allows you to specify the permitted HTTP response codes that a template can |
839
|
|
|
|
|
|
|
generate. for example a NotFound view probably makes no sense to return anything |
840
|
|
|
|
|
|
|
other than a 404 Not Found code. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=head2 lifecycle |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
By default your view lifecycle is 'per request' which means we only build it one during the entire |
845
|
|
|
|
|
|
|
request cycle. This is handled by the lifecycle module L<Catalyst::View::BasePerRequest::Lifecycle::Request>. |
846
|
|
|
|
|
|
|
However sometimes you would like your view to be build newly each you you request it. For example |
847
|
|
|
|
|
|
|
you might have a view called from inside a loop, passing it different arguments each time. In that |
848
|
|
|
|
|
|
|
case you want the 'Factory' lifecycle, which is handled by L<Catalyst::View::BasePerRequest::Lifecycle::Factory>. |
849
|
|
|
|
|
|
|
In order to do that set this configuration value to 'Factory'. For example |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
__PACKAGE__->config( |
852
|
|
|
|
|
|
|
content_type => 'text/html', |
853
|
|
|
|
|
|
|
status_codes => [200,201,400], |
854
|
|
|
|
|
|
|
lifecycle => 'Factory', |
855
|
|
|
|
|
|
|
); |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
You can create your own lifecycle classes, but that's very advanced so for now if you |
858
|
|
|
|
|
|
|
want to do that you should review the source of the existing ones. F<D-d>or example you might |
859
|
|
|
|
|
|
|
create a view with a 'session' lifecycle, which returns the same view as long as the user |
860
|
|
|
|
|
|
|
is logged in. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=head1 ALSO SEE |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
L<Catalyst> |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=head1 AUTHORS & COPYRIGHT |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
John Napiorkowski L<email:jjnapiork@cpan.org> |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=head1 LICENSE |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
Copyright 2023, John Napiorkowski L<email:jjnapiork@cpan.org> |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
875
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
=cut |