Add a load of POD
[catagits/CatalystX-DynamicComponent.git] / lib / CatalystX / DynamicComponent.pm
1 package CatalystX::DynamicComponent;
2 use MooseX::Role::Parameterized;
3 use MooseX::Types::Moose qw/Str CodeRef ArrayRef/;
4 use namespace::autoclean;
5
6 our $VERSION = 0.000001;
7
8 parameter 'name' => (
9     isa => Str,
10     required => 1,
11 );
12
13 parameter 'pre_immutable_hook' => (
14     isa => Str,
15     predicate => 'has_pre_immutable_hook',
16 );
17
18 parameter 'COMPONENT' => (
19     isa => CodeRef,
20     predicate => 'has_custom_component_method',
21 );
22
23 role {
24     my $p = shift;
25     my $name = $p->name;
26     my $pre_immutable_hook = $p->pre_immutable_hook;
27
28     method $name => sub {
29         my ($app, $name, $config, $methods) = @_;
30
31         my $appclass = blessed($app) || $app;
32         my $type = $name;
33         $type =~ s/^${appclass}:://; # FIXME - I think there is shit in C::Utils to do this.
34         $type =~ s/::.*$//;
35
36         my $meta = Moose->init_meta( for_class => $name );
37
38         my @superclasses = @{ $config->{superclasses} || [] };
39         push(@superclasses, 'Catalyst::' . $type) unless @superclasses;
40         $meta->superclasses(@superclasses);
41
42         if (my @roles = @{ $config->{roles}||[] }) {
43             Moose::Util::apply_all_roles( $name, @roles);
44         }
45
46         if ($p->has_custom_component_method) {
47             $meta->add_method(COMPONENT => $p->COMPONENT);
48         }
49
50         $app->$pre_immutable_hook($meta) if $p->has_pre_immutable_hook;
51
52         $methods ||= {};
53         foreach my $name (keys %$methods) {
54             $meta->add_method($name => $methods->{$name});
55         }
56         $meta->make_immutable;
57
58         my $instance = $app->setup_component($name);
59         $app->components->{ $name } = $instance;
60     };
61 };
62
63 1;
64
65 __END__
66
67 =head1 NAME
68
69 CatalystX::DynamicComponent - Parameterised Moose role providing functionality to build Catalyst components at runtime.
70
71 =head1 SYNOPSIS
72
73     package My::DynamicComponentType;
74     use Moose::Role;
75     use namespace::autoclean;
76
77     with 'CatalystX::DynamicComponent' => {
78         name => '_setup_one_of_my_components', # Name of injected method
79     };
80
81     after setup_components => sub { shift->_setup_all_my_components(@_); };
82
83     sub _setup_all_my_components {
84         my ($self, $c) = @_;
85         foreach my $component_name ('MyApp::Component1') {
86             my $component_config = $c->config->{$component_name};
87             # Calling this method creates a component, and registers it in your application
88             $self->_setup_one_of_my_components($component_name, $component_config);
89         }
90     }
91
92 =head1 DESCRIPTION
93
94 CatalystX::DynamicComponent aims to provide a flexible and reuseable method of building generic
95 Catalyst components and registering them with your application.
96
97 To give you this flexibility, it is implemented as a parametrised role which curries a
98 component builder into your current package at application time.
99
100 Authors of specific dynamic component builders are expected to be implemented as application class
101 roles which compose this role, but provide their own advice around the C<< setup_compontens >>
102 method, and call the curried method from this role once for each component you wish to setup.
103
104 =head1 PARAMETERS
105
106 =head2 name
107
108 B<Required> - The name of the component generator method to curry.
109
110 =head2 COMPONENT
111
112 Optional, either a L<Class::MOP::Method>, or a plain code ref of a COMPONENT method to apply to
113 the dynamically generated package before making it immutable.
114
115 =head2 pre_immutable_hook
116
117 Optional, method to call after a component has been generated, but before it is made immutable,
118 constructed, and added to your component registry.
119
120 =head1 CURRIED COMPONENT GENERATOR
121
122 =head2 ARGUMENTS
123
124 =over
125
126 =item $component_name (E.g. C<< MyApp::Controller::Foo >>)
127
128 =item $config (E.g. C<< $c->config->{$component_name} >>)
129
130 =head2 OPERATION
131
132 FIXME
133
134 =head1 TODO
135
136 =over
137
138 =item *
139
140 Better default handling of config - by default component should get config from where it normally
141 does!
142
143 =item *
144
145 Abstract handling of role application / class name. This should not just be the component config
146 by default.
147
148 =item *
149
150 Have some actual tests which test just this crap, and not all the other classes together.
151
152 =back
153
154 =head1 BUGS
155
156 Probably plenty, test suite certainly isn't comprehensive.. Patches welcome.
157
158 =head1 AUTHOR
159
160 Tomas Doran (t0m) <bobtfish@bobtfish.net>
161
162 =head1 LICENSE
163
164 This code is copyright (c) 2009 Tomas Doran. This code is licensed on the same terms as perl
165 itself.
166
167 =cut
168