File Coverage

blib/lib/CatalystX/Imports/Vars.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package CatalystX::Imports::Vars;
2              
3             =head1 NAME
4              
5             CatalystX::Imports::Vars - Import application variables
6              
7             =cut
8              
9 3     3   1162 use warnings;
  3         3  
  3         77  
10 3     3   10 use strict;
  3         4  
  3         55  
11              
12             =head1 BASE CLASSES
13              
14             L<CatalystX::Imports>
15              
16             =cut
17              
18 3     3   9 use base 'CatalystX::Imports';
  3         4  
  3         177  
19              
20 3     3   11 use Carp::Clan qw{ ^CatalystX::Imports(?:::|$) };
  3         5  
  3         20  
21 3     3   1628 use Data::Alias qw( alias deref );
  0            
  0            
22              
23             =head1 SYNOPSIS
24              
25             package MyApp::Controller::Users;
26             use base 'Catalyst::Controller';
27              
28             # use Vars => 1; for just $self, $ctx and @args
29             use CatalystX::Imports
30             Vars => { Stash => [qw( $user $user_rs $template )],
31             Session => [qw( $user_id )] };
32              
33             sub list: Local {
34             $template = 'list.tt';
35             $user_rs = $ctx->model('User')->search;
36             }
37              
38             sub view: Local {
39             $template = 'view.tt';
40             $user = $ctx->model('User')->find($args[0]);
41             }
42              
43             sub me: Local {
44             $ctx->forward('view', [$user_id]);
45             }
46              
47             1;
48              
49             =head1 DESCRIPTION
50              
51             This module allows you to bind various package vars in your controller
52             to specific places in the framework. By default, the variables C<$self>,
53             C<$ctx> and C<@args> are exported. They have the same value as if set
54             via
55              
56             my ($self, $ctx, @args) = @_;
57              
58             in your action.
59              
60             You can use a hash reference to specify what variables you want to bind
61             to their respective fields in the session or stash, as demonstrated in
62             the L</SYNOPSIS>.
63              
64             =cut
65              
66             =head1 METHODS
67              
68             =head2 export_into
69              
70             Exports requested variables and intalls a wrapper to fill them with their
71             respective values if needed.
72              
73             =cut
74              
75             sub export_into {
76             my ($class, $target, $args) = @_;
77              
78             # a simple '1' means only $self, $ctx and $args are requested
79             if ($args and $args == 1) {
80             $args = {};
81             }
82              
83             # by now it should be a hash reference, or we got something wrong
84             croak 'Either a 1 or a hash reference expected as argument for Vars'
85             unless $args and ref $args eq 'HASH';
86              
87             # fetch session and
88             my @session = @{ $args->{Session} || [] };
89             my @stash = @{ $args->{Stash} || [] };
90              
91             # build map of symbol hash refs, containing method, type and
92             # sym (name)
93             my @sym =
94             map { {method => $_->[0], type => $_->[2], sym => $_->[3]} }
95             map { [@$_, $class->_destruct_var_name($_->[1])] }
96             map { my $x = $_; map { [$x->[0], $_] } @{ $x->[1] } }
97             [session => \@session], [stash => \@stash];
98              
99             # export all symbols into the requesting namespace, include defaults
100             $class->export_var_into($target, $class->_destruct_var_name($_))
101             for @session, @stash, qw($self $ctx @args);
102              
103             # build and register our action wrapper
104             $class->register_action_wrap_in($target, sub {
105             my $code = shift;
106             my @wrap = @{ shift(@_) };
107             my ($self, $ctx, @args) = @_;
108              
109             # install default vars
110             no strict 'refs';
111             local *{ $target . '::self' } = \$self;
112             local *{ $target . '::ctx' } = \$ctx;
113             local *{ $target . '::args' } = \@args;
114              
115             # localise symbols to this level
116             local *{ "${target}::${_}" } for map { $_->{sym} } @sym;
117              
118             # scalar aliases
119             alias ${ "${target}::" . $_->{sym} } =
120             $ctx->can($_->{method})->($ctx)->{ $_->{sym} }
121             for grep { $_->{type} eq 'scalar' } @sym;
122              
123             # hash aliases
124             alias %{ "${target}::" . $_->{sym} } =
125             %{ $ctx->can($_->{method})->($ctx)->{ $_->{sym} } ||= {} }
126             for grep { $_->{type} eq 'hash' } @sym;
127              
128             # array aliases
129             alias @{ "${target}::" . $_->{sym} } =
130             @{ $ctx->can($_->{method})->($ctx)->{ $_->{sym} } ||= [] }
131             for grep { $_->{type} eq 'array' } @sym;
132              
133             # there are other wrappers left
134             if (my $w = shift @wrap) {
135             return $w->($code, \@wrap, @_);
136             }
137              
138             # we're the last wrapper
139             else {
140             return $code->(@_);
141             }
142             });
143              
144             return 1;
145             }
146              
147             =head2 export_var_into
148              
149             Installs a variable into a package.
150              
151             =cut
152              
153             sub export_var_into {
154             my ($class, $target, $type, $name) = @_;
155             my $target_name = "${target}::${name}";
156              
157             # initialise exported vars
158             no strict 'refs';
159             *$target_name =
160             ( $type eq 'scalar' ? \$$target_name
161             : $type eq 'array' ? \@$target_name
162             : \%$target_name );
163              
164             return 1;
165             }
166              
167             =head2 _destruct_var_name
168              
169             Takes a variable name and returns it's type (C<scalar>, C<array> or
170             C<hash>) and it's symbol parts.
171              
172             =cut
173              
174             sub _destruct_var_name {
175             my ($class, $name) = @_;
176             if ($name =~ /^([\@\%\$])(\S+)$/) {
177             my ($sigil, $id) = ($1, $2);
178             my %type = qw($ scalar % hash @ array);
179             return ($type{ $sigil }, $id);
180             }
181             else {
182             croak "Invalid identifier found: '$name'";
183             }
184             }
185              
186             =head1 DIAGNOSTICS
187              
188             =head2 Invalid identifier found: 'foo'
189              
190             You asked for the import of the var 'foo', but it is not a valid variable
191             identifier. E.g.: '@foo', '%foo' and '$foo' are valid, but '-foo', ':foo'
192             and 'foo' are not.
193              
194             =head2 Either a 1 or a hash reference expected as argument for Vars
195              
196             You can import just the default variables ($self, $ctx and @args) by
197             specifying a C<1> as a parameter ...
198              
199             use CatalystX::Imports Vars => 1;
200              
201             ... or you can give it a hash reference and tell it what you want
202             additionally ...
203              
204             use CatalystX::Imports Vars => { Stash => [qw($foo)] };
205              
206             ... but you specified something else as parameter.
207              
208             =head1 SEE ALSO
209              
210             L<Catalyst>,
211             L<CatalystX::Imports>,
212             L<CatalystX::Imports::Context>
213              
214             =head1 AUTHOR AND COPYRIGHT
215              
216             Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>
217              
218             =head1 LICENSE
219              
220             This program is free software; you can redistribute it and/or modify
221             it under the same terms as perl itself.
222              
223             =cut
224              
225              
226             1;