File Coverage

blib/lib/Catalyst/Plugin/CurrentComponents.pm
Criterion Covered Total %
statement 30 53 56.6
branch 12 36 33.3
condition n/a
subroutine 7 11 63.6
pod n/a
total 49 100 49.0


line stmt bran cond sub pod time code
1              
2             use Moo::Role;
3 1     1   4033 use Scalar::Util ();
  1         17247  
  1         5  
4 1     1   3408  
  1         3  
  1         945  
5             requires 'model', 'view', 'stash';
6              
7             our $VERSION = '0.010';
8              
9             has 'model_instance_from_return' => (is=>'lazy');
10              
11             if(my $config = shift->config->{'Plugin::CurrentComponents'}) {
12             return exists $config->{model_instance_from_return} ? $config->{model_instance_from_return} : 0;
13 10 50   10   146 } else {
14 10 50       863 return 0;
15             }
16 0         0 }
17              
18             has 'model_instance_from_state' => (is=>'lazy');
19              
20             if(my $config = shift->config->{'Plugin::CurrentComponents'}) {
21             return exists $config->{model_instance_from_state} ? $config->{model_instance_from_state} : 0;
22             } else {
23 0 0   0   0 return 0;
24 0 0       0 }
25             }
26 0         0  
27             has 'view_instance_from_return' => (is=>'lazy');
28              
29             if(my $config = shift->config->{'Plugin::CurrentComponents'}) {
30             return exists $config->{view_instance_from_return} ? $config->{view_instance_from_return} : 0;
31             } else {
32             return 0;
33 0 0   0   0 }
34 0 0       0 }
35              
36 0         0 my ($self, $model) = @_;
37             return unless ref $self;
38             if(defined($model)) {
39             $self->stash->{current_model} = $model;
40             }
41 3     3   351 return $self->stash->{current_model};
42 3 50       16 }
43 3 50       7  
44 3         10 my ($self, $model, @args) = @_;
45             return unless ref $self;
46 3         169 if(defined($model)) {
47             $model = $self->model($model, @args) unless ref $model;
48             $self->stash->{current_model_instance} = $model;
49             }
50 3     3   104 return $self->stash->{current_model_instance};
51 3 50       8 }
52 3 50       8  
53 3 50       9 my ($self, $view) = @_;
54 3         34 return unless ref $self;
55             if(defined($view)) {
56 3         176 $self->stash->{current_view} = $view;
57             }
58             return $self->stash->{current_view};
59             }
60 1     1   73  
61 1 50       4 my ($self, $view, @args) = @_;
62 1 50       3 return unless ref $self;
63 1         3 if(defined($view)) {
64             $view = $self->view($view, @args) unless ref $view;
65 1         61 $self->stash->{current_view_instance} = $view;
66             }
67             return $self->stash->{current_view_instance};
68             }
69 2     2   101  
70 2 50       6 my ($self, @args) = @_;
71 2 50       5 my ($view_name) = @{$self->action->attributes->{View}};
72 2 50       5 die "Action '@{[ $self->action ]}' doesn't have a 'View' attribute" unless $view_name;
73 2         7 my $view = $self->view($view_name, @args);
74             $self->stash(current_view_instance=>$view);
75 2         113 return $view;
76             }
77              
78             my ($self, @args) = @_;
79 0     0     my ($model_name) = @{$self->action->attributes->{Model}};
80 0           die "Action '@{[ $self->action ]}' doesn't have a 'Model' attribute" unless $model_name;
  0            
81 0 0         my $model = $self->model($model_name, @args);
  0            
82 0           $self->stash(current_model_instance=>$model);
83 0           return $model;
84 0           }
85              
86             around 'execute', sub {
87             my ($orig, $self, $class, $code, @rest ) = @_;
88 0     0     my $state = $self->$orig($class, $code, @rest);
89 0            
  0            
90 0 0         if(
  0            
91 0           ($self->model_instance_from_return || $self->view_instance_from_return) &&
92 0           defined $state &&
93 0           Scalar::Util::blessed($state)
94             ) {
95             my $state_class = ref($state);
96             my $app_class = ref($self);
97             $state_class =~s/^$app_class\:\:(Model|View)\:\://;
98            
99             if($self->model_instance_from_return && $self->model($state_class)) {
100             $self->current_model_instance($state);
101             } elsif($self->view_instance_from_return && $self->view($state_class)) {
102             $self->current_view_instance($state);
103             } elsif($self->model_instance_from_state) {
104             # Its an object but its not a view, but allow it anyway. Maybe terrible
105             # idea but for backcompat at least.
106             $self->current_model_instance($state);
107             }
108             }
109              
110             return $state;
111             };
112              
113             around 'model', sub {
114             my ($orig, $self, $name, @args) = @_;
115             if(!defined($name) && ref($self)) {
116             if(
117             !defined($self->stash->{current_model_instance}) &&
118             $self->controller->can('current_model_instance')
119             ) {
120             $self->current_model_instance(
121             $self->controller->current_model_instance($self));
122             } elsif(
123             !defined($self->stash->{current_model}) &&
124             $self->controller->can('current_model')
125             ) {
126             $self->current_model($self->controller->current_model($self));
127             }
128             }
129             return $self->$orig($name, @args);
130             };
131              
132             around 'view', sub {
133             my ($orig, $self, $name, @args) = @_;
134             if(!defined($name) && ref($self)) {
135             if(
136             !defined($self->stash->{current_view_instance}) &&
137             $self->controller->can('current_view_instance')
138             ) {
139             $self->current_view_instance(
140             $self->controller->current_view_instance($self));
141             } elsif(
142             !defined($self->stash->{current_view}) &&
143             $self->controller->can('current_view')
144             ) {
145             $self->current_view($self->controller->current_view($self));
146             }
147             }
148             return $self->$orig($name, @args);
149             };
150              
151             1;
152              
153             =head1 NAME
154              
155             Catalyst::Plugin::CurrentComponents - Declare current components more easily.
156              
157             =head1 SYNOPSIS
158              
159             Use the plugin in your application class:
160              
161             package MyApp;
162             use Catalyst 'CurrentComponents';
163              
164             # Optional configuration
165             MyApp->config(
166             'Plugin::CurrentComponents' => {
167             model_instance_from_return => 1,
168             view_instance_from_return => 1,
169             },
170             );
171              
172             MyApp->setup;
173              
174             Then you can use it in your controllers:
175              
176             package MyApp::Controller::Example;
177              
178             use base 'Catalyst::Controller';
179              
180             sub current_model_instance {
181             my ($self, $c) = @_;
182             return $c->model("Form::Login", user_database => $c->model('Users'));
183             }
184              
185             sub myaction :Local {
186             my ($self, $c) = @_;
187             my $c->model; # Isa 'MyApp::Model::Form::Login', or whatever that returns;
188             }
189              
190             sub set_model :Local {
191             my ($self, $c) = @_;
192             $c->current_model_instance($c->model('Foo')); # $c->model ISA 'MyApp::Model::Foo
193             }
194              
195             sub set_view :Local {
196             my ($self, $c) = @_;
197             $c->current_view_instance($c->view('Bar')); # $c->view ISA 'MyApp::View::Bar
198             }
199              
200             =head1 DESCRIPTION
201              
202             This plugin gives you an alternative to setting the current_view|model(_instance)
203             via a controller method or via context helper methods. You may find this a
204             more readable approach than setting it via the stash.
205              
206             You may also enable a global option to set the current_model_instance or the
207             current_view_instance via the return value of an action. See L</CONFIGURATION>
208              
209             Please Seee documention about Views and Models in L<Catalyst>.
210              
211             =head1 METHODS
212              
213             This plugin adds the following methods to your context.
214              
215             =head2 current_model
216              
217             Sets $c->stash->{current_model} if an argument is passed. Always returns the
218             current value of this stash key. Expects the string name of a model.
219              
220             =head2 current_model_instance
221              
222             Sets $c->stash->{current_model_instance} if an argument is passed. Always returns the
223             current value of this stash key. Expects either the instance of an already created
224             model or can accept arguments that can be validly submitted to $c->model.
225              
226             =head2 current_view
227              
228             Sets $c->stash->{current_view} if an argument is passed. Always returns the
229             current value of this stash key. Expects the string new of a view.
230              
231             =head2 current_view_instance
232              
233             Sets $c->stash->{current_view_instance} if an argument is passed. Always returns the
234             current value of this stash key. Expects either the instance of an already created
235             view or can accept arguments that can be validly submitted to $c->view.
236              
237             =head2 build_view
238              
239             =head2 build_model
240              
241             Builds the view or model and makes it the current instance.
242              
243             =head1 CONTROLLER METHODS
244              
245             This plugin will inspect the current controller for the following methods
246              
247             =head2 current_model
248              
249             =head2 current_model_instance
250              
251             Same as the context methods, but lets you set this at a controller level. Useful
252             for base classes or roles. Example:
253              
254              
255             =head1 CONFIGURATION
256              
257             This plugin supports configuration under the "Plugin::CurrentComponents" key.
258             For example:
259              
260             MyApp->config(
261             'Plugin::CurrentComponents' => {
262             model_instance_from_return => 1,
263             view_instance_from_return => 1,
264             },
265             );
266              
267             =head2 model_instance_from_return
268              
269             Allows one to set the current_model_instance from the return value of a matched
270             action. Please note this is an experimental option which is off by default.
271             The return value must be a defined, blessed objected that ISA L<Catalyst::Model>
272             for this to work. Example:
273              
274             sub set_model_by_return :Chained(/) CaptureArgs(0) {
275             my ($self, $c) = @_;
276             return $c->model('CurrentModel'); # $c->model ISA 'MyApp::Model::CurrentModel'
277             }
278              
279             =head2 view_instance_from_return
280              
281             Allows one to set the current_view_instance from the return value of a matched
282             action. Please note this is an experimental option which is off by default.
283             The return value must be a defined, blessed objected that ISA L<Catalyst::View>
284             for this to work. Example:
285              
286             sub set_view_by_return :Chained(/) CaptureArgs(0) {
287             my ($self, $c) = @_;
288             return $c->view('CurrentView'); # $c->view ISA 'MyApp::View::CurrentView'
289             }
290              
291             =head2 model_instance_from_state
292              
293             Often you want to set your current model instance to 'any type of object'. The
294             configuration L</model_instance_from_return> expects the object to be something
295             in the 'MyApp::Model' namespace. If this is not the case you can use this option.
296              
297             sub set_model_from_resultset :Chained CaptureArgs(1) {
298             my ($self, $c, $id) = @_;
299             return $c->model("Schema::User")->find($id);
300             }
301              
302             In this case the object returned is probably a 'MyApp::Schema::Result::User' so
303             the option L</model_instance_from_return> would not have worked.
304              
305             =head1 AUTHOR
306              
307             John Napiorkowski L<email:jjnapiork@cpan.org>
308            
309             =head1 SEE ALSO
310            
311             L<Catalyst>, L<Catalyst::Response>
312              
313             =head1 COPYRIGHT & LICENSE
314            
315             Copyright 2017, John Napiorkowski L<email:jjnapiork@cpan.org>
316            
317             This library is free software; you can redistribute it and/or modify it under
318             the same terms as Perl itself.
319            
320             =cut