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