As per usual, I mean maybe::next::method
[catagits/Catalyst-Component-ACCEPT_CONTEXT.git] / lib / Catalyst / Component / ACCEPT_CONTEXT.pm
1 package Catalyst::Component::ACCEPT_CONTEXT;
2
3 use warnings;
4 use strict;
5 use MRO::Compat;
6 use Scalar::Util qw(weaken);
7
8 =head1 NAME
9
10 Catalyst::Component::ACCEPT_CONTEXT - Make the current Catalyst
11 request context available in Models and Views.
12
13 =head1 VERSION
14
15 Version 0.05
16
17 =cut
18
19 our $VERSION = '0.05';
20
21 =head1 SYNOPSIS
22
23 Models and Views don't usually have access to the request object,
24 since they probably don't really need it.  Sometimes, however, having
25 the request context available outside of Controllers makes your
26 application cleaner.  If that's the case, just use this module as a
27 base class:
28
29     package MyApp::Model::Foobar;
30     use base qw|Catalyst::Component::ACCEPT_CONTEXT Catalyst::Model|;
31
32 Then, you'll be able to get the current request object from within
33 your model:
34
35     sub do_something {
36         my $self = shift;
37         print "The current URL is ". $self->context->req->uri->as_string;
38     }
39
40 =head1 WARNING WARNING WARNING
41
42 Using this module is somewhat of a hack.  Changing the state of your
43 objects on every request is a pretty braindead way of doing OO.  If
44 you want your application to be brain-live, then you should use
45 L<Catalyst::Component::InstancePerContext|Catalyst::Component::InstancePerContext>.  
46
47 Instead of doing this on every request (which is basically
48 what this module does):
49
50     $my_component->context($c);
51
52 It's better to do something like this:
53     
54     package FooApp::Controller::Root;
55     use base 'Catalyst::Controller';
56     use Moose;
57     
58     with 'Catalyst::Component::InstancePerContext';
59     has 'context' => (is => 'ro');
60     
61     sub build_per_context_instance {
62         my ($self, $c, @args) = @_;
63         return $self->new({ context => $c, %$self, @args });
64     }
65     
66     sub actions :Whatever {
67         my $self = shift;
68         my $c = $self->context; # this works now
69     }
70         
71     1;
72
73 Now you get a brand new object that lasts for a single request instead
74 of changing the state of an existing one on each request.  This is
75 much cleaner OO design.
76
77 The best strategy, though, is not to use the context inside your
78 model.  It's best for your Controller to pull the necessary data from
79 the context, and pass it as arguments:
80
81    sub action :Local {
82        my ($self, $c) = @_;
83        my $foo  = $c->model('Foo');
84        my $quux = $foo->frobnicate(baz => $c->request->params->{baz});
85        $c->stash->{quux} = $quux;
86    }
87
88 This will make it Really Easy to test your components outside of
89 Catalyst, which is always good.
90
91 =head1 METHODS
92
93 =head2 context
94
95 Returns the current request context.
96
97 =cut
98
99 sub context {
100     return shift->{context};
101 }
102
103 =head2 ACCEPT_CONTEXT
104
105 Catalyst calls this method to give the current context to your model.
106 You should never call it directly.
107
108 Note that a new instance of your component isn't created.  All we do
109 here is shove C<$c> into your component.  ACCEPT_CONTEXT allows for
110 other behavior that may be more useful; if you want something else to
111 happen just implement it yourself.
112
113 See L<Catalyst::Component> for details.
114
115 =cut
116
117 sub ACCEPT_CONTEXT {
118     my $self    = shift;
119     my $context = shift;
120
121     $self->{context} = $context;
122     weaken($self->{context});
123
124     return $self->maybe::next::method($context, @_) || $self;
125 }
126
127 =head2 COMPONENT
128
129 Overridden to use initial application object as context before a request.
130
131 =cut
132
133 sub COMPONENT {
134     my $class = shift;
135     my $app   = shift;
136     my $args  = shift;
137     $args->{context} = $app;
138     weaken($args->{context}) if ref $args->{context};
139     return $class->maybe::next::method($app, $args, @_);
140 }
141
142 =head1 AUTHOR
143
144 Jonathan Rockway, C<< <jrockway at cpan.org> >>
145
146 =head1 BUGS
147
148 Please report any bugs or feature requests to
149 C<bug-catalyst-component-accept_context at rt.cpan.org>, or through the web interface at
150 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Component-ACCEPT_CONTEXT>.
151 I will be notified, and then you'll automatically be notified of progress on
152 your bug as I make changes.
153
154 =head1 SUPPORT
155
156 You can find documentation for this module with the perldoc command.
157
158     perldoc Catalyst::Component::ACCEPT_CONTEXT
159
160 You can also look for information at:
161
162 =over 4
163
164 =item * Catalyst Website
165
166 L<http://www.catalystframework.org/>
167
168 =item * AnnoCPAN: Annotated CPAN documentation
169
170 L<http://annocpan.org/dist/Catalyst-Component-ACCEPT_CONTEXT>
171
172 =item * CPAN Ratings
173
174 L<http://cpanratings.perl.org/d/Catalyst-Component-ACCEPT_CONTEXT>
175
176 =item * RT: CPAN's request tracker
177
178 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Component-ACCEPT_CONTEXT>
179
180 =item * Search CPAN
181
182 L<http://search.cpan.org/dist/Catalyst-Component-ACCEPT_CONTEXT>
183
184 =back
185
186 =head1 COPYRIGHT & LICENSE
187
188 Copyright 2007 Jonathan Rockway.
189
190 This program is free software; you can redistribute it and/or modify it
191 under the same terms as Perl itself.
192
193 =cut
194
195 1; # End of Catalyst::Component::ACCEPT_CONTEXT