File Coverage

blib/lib/CatalystX/VirtualComponents.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package CatalystX::VirtualComponents;
2 1     1   1445 use Moose::Role;
  0            
  0            
3             use namespace::clean -except => qw(meta);
4             use Module::Pluggable::Object;
5             use Devel::InheritNamespace;
6              
7             our $VERSION = '0.00004';
8              
9             sub search_components {
10             my ($class, $namespace, @namespaces) = @_;
11              
12             my @paths =
13             map { ("${_}Base", $_) }
14             qw( ::Controller ::C ::Model ::M ::View ::V )
15             ;
16             my $config = $class->config->{ setup_components };
17             my $extra = delete $config->{ search_extra } || [];
18              
19             my $comps;
20             foreach my $ns (@namespaces) {
21             foreach my $path (@paths) {
22             my $local_namespace = ($path =~ /^(.+)Base$/) ?
23             "$namespace$1" : "$namespace$path"
24             ;
25             my $inherit_namespace = $path;
26             $inherit_namespace =~ s/^(?=::)/$ns/;
27             my @search_path = ($inherit_namespace, @$extra);
28              
29             my $loaded_comps =
30             Devel::InheritNamespace->new(
31             search_options => $config
32             )->all_modules( $local_namespace, @search_path )
33             ;
34             while (my ($comp_class, $data) = each %$loaded_comps) {
35             if ($comp_class =~ /::SUPER$/) {
36             next;
37             }
38             $comps->{$comp_class} = $data;
39             }
40             }
41             }
42              
43             return $comps;
44             }
45              
46             override setup_components => sub {
47             my $class = shift;
48              
49             my @hierarchy;
50             if (exists $class->config->{VirtualComponents}) {
51             if (exists $class->config->{VirtualComponents}->{inherit}) {
52             @hierarchy = (
53             $class,
54             @{ $class->config->{VirtualComponents}->{inherit} }
55             );
56             }
57             } else {
58             @hierarchy =
59             grep { $_->isa('Catalyst') && $_ ne 'Catalyst' }
60             $class->meta->linearized_isa
61             ;
62             }
63              
64             my $comps = $class->search_components( @hierarchy );
65              
66             foreach my $comp_class (keys %$comps) {
67             my $module = $class->setup_component($comp_class);
68             my %modules = (
69             $comp_class => $module,
70             map {
71             $_ => $class->setup_component($_)
72             } grep {
73             not exists $comps->{$_}
74             } Devel::InnerPackage::list_packages( $comp_class )
75             );
76             for my $key ( keys %modules ) {
77             $class->components->{ $key } = $modules{ $key };
78             }
79             }
80              
81             if ($class->debug) {
82             my $column_width = Catalyst::Utils::term_width() - 6;
83             my $t = Text::SimpleTable->new($column_width);
84              
85             my @virtual_components = grep { $comps->{$_}->{is_virtual} } keys %$comps;
86              
87             $t->row($_) for sort @virtual_components;
88             $class->log->debug( "Dynamically generated components:\n" . $t->draw . "\n" );
89             }
90              
91             };
92              
93             1;
94              
95             __END__
96              
97             =head1 NAME
98              
99             CatalystX::VirtualComponents - Setup Virtual Catalyst Components Based On A Parent Application Class
100              
101             =head1 SYNOPSIS
102              
103             # in your base app...
104             package MyApp;
105             use Catalyst;
106              
107             # in another app...
108             package MyApp::Extended;
109             use Moose;
110             use Catalyst qw(+CatalystX::VirtualComponents);
111            
112             extends 'MyApp';
113              
114             =head1 DESCRIPTION
115              
116             WARNING: YMMV with this module.
117              
118             This module provides a way to reuse controllers, models, and views from
119             another Catalyst application.
120              
121             =head1 HOW IT WORKS
122              
123             Suppose you have a Catalyst application with the following components:
124              
125             # Application MyApp::Base
126             MyApp::Base::Controller::Root
127             MyApp::Base::Model::DBIC
128             MyApp::Base::View::TT
129              
130             And then in MyApp::Extended, you wanted to reuse these components -- except
131             you want to customize the Root controller, and you want to add another model
132             (say, Model::XML::Feed).
133              
134             In your new app, you can skip creating MyApp::Extended::Model::DBIC and
135             MyApp::Extended::View::TT -- CatalystX::VirtualComponents will take care of
136             these.
137              
138             Just provide the customized Root controller and the new model:
139              
140             package MyApp::Extended::Controller::Root;
141             use Moose;
142              
143             BEGIN { extends 'MyApp::Base::Controller::Root' }
144              
145             sub new_action :Path('new_action') {
146             ....
147             }
148              
149             (We will skip XML::Feed, as it's just a regular model)
150              
151             Then, in MyApp::Extended
152              
153             packge MyApp::Extended;
154             use Moose;
155             use Catalyst;
156              
157             extends 'MyApp::Base';
158              
159             Note that MyApp::Extended I<inherits> from MyApp::Base. Naturally, if you
160             are inheriting from an application, you'd probably want to inherit all of
161             its controllers and such. To do this, specify CatalystX::VirtualComponents
162             in the Catalyst plugin list for MyApp::Extended:
163              
164             __PACKAGE__->setup( qw(
165             ... # your regular Catalyst plugins
166             +CatalystX::VirtualComponent
167             ) );
168              
169             When setup() is run, CatalystX::VirtualComponent will intercept the component
170             setup code and will automatically create I<virtual> subclasses for components
171             that exist in MyApp::Base, but I<not> in MyApp::Extended. In the above case,
172             MyApp::Extended::View::TT and MyApp::Extended::Model::DBIC will be created.
173              
174             MyApp::Extended::Controller::Root takes precedence over the base class, so
175             only the local component will be loaded. MyApp::Extended::Model::XML::Feed
176             only exists in the MyApp::Extended namespace, so it just works like a
177             normal Catalyst model.
178              
179             =head1 GENERATING VIRTUAL CLASSES WITHOUT INHERITANCE
180              
181             If you don't want to subclass, or use a more fine-grained control on which
182             namespaces to look for base components, specify the namespaces in a config
183             element:
184              
185             __PACKAGE__->config(
186             VirtualComponents => {
187             inherit => [
188             'NamespaceA',
189             'NamespaceB'
190             ]
191             }
192             );
193              
194             =head1 USING IN CONJUNCTION WITH CatalystX::AppBuilder
195              
196             Simply add CatalystX::VirtualComponents in the plugin list:
197              
198             package MyApp::Extended::Builder;
199             use Moose;
200              
201             extends 'CatalystX::AppBuilder';
202              
203             override _build_plugins {
204             my $plugins = super();
205             push @$plugins, '+CatalystX::VirtualComponents';
206             return $plugins;
207             };
208              
209             1;
210              
211             =head1 METHODS
212              
213             =head2 search_components($class)
214              
215             Finds the list of components for Catalyst app $class.
216              
217             =head2 setup_components()
218              
219             Overrides Catalyst's setup_components() method.
220              
221             =head1 TODO
222              
223             Documentation. Samples. Tests.
224              
225             =head1 AUTHOR
226              
227             Daisuke Maki C<< <daisuke@endeworks.jp> >>
228              
229             =head1 LICENSE
230              
231             This program is free software; you can redistribute it and/or modify it
232             under the same terms as Perl itself.
233              
234             See http://www.perl.com/perl/misc/Artistic.html
235              
236             =cut