ditto
[catagits/Catalyst-Runtime.git] / lib / Catalyst / IOC.pm
1 package Catalyst::IOC;
2 use strict;
3 use warnings;
4 use Bread::Board qw/depends_on/;
5 use Catalyst::IOC::ConstructorInjection;
6 no strict 'refs';
7
8 use Sub::Exporter -setup => {
9     exports => [qw/
10         depends_on
11         component
12         model
13         view
14         controller
15         container
16     /],
17     groups  => { default => [qw/
18         depends_on
19         component
20         model
21         view
22         controller
23         container
24     /]},
25 };
26
27 sub container (&) {
28     my $code = shift;
29     my $caller = caller;
30     ${"${caller}::customise_container"} = sub {
31         local ${"${caller}::current_container"} = shift;
32         $code->();
33     };
34 }
35
36 sub model (&)      { _subcontainer( shift, caller, 'model' )      }
37 sub view (&)       { _subcontainer( shift, caller, 'view' )       }
38 sub controller (&) { _subcontainer( shift, caller, 'controller' ) }
39
40 sub _subcontainer (&$$) {
41     my ( $code, $caller, $subcontainer ) = @_;
42     local ${"${caller}::current_container"} =
43         ${"${caller}::current_container"}->get_sub_container($subcontainer);
44     $code->();
45 }
46
47 sub component ($;%) {
48     my ($name, %args) = @_;
49     my $caller = caller;
50     $args{dependencies} ||= {};
51     $args{dependencies}{application_name} = depends_on( '/application_name' );
52
53     my $lifecycle = $args{lifecycle};
54     my %catalyst_lifecycles = map { $_ => 1 } qw/ COMPONENTSingleton Request /;
55     $args{lifecycle} = $lifecycle
56                      ? $catalyst_lifecycles{$lifecycle} ? "+Catalyst::IOC::LifeCycle::$lifecycle" : $lifecycle
57                      : 'Singleton'
58                      ;
59
60     # FIXME - check $args{type} here!
61
62     my $component_name = join '::', (
63         ${"${caller}::current_container"}->resolve(service => '/application_name'),
64         ucfirst(${"${caller}::current_container"}->name),
65         $name
66     );
67
68     my $service = Catalyst::IOC::ConstructorInjection->new(
69         %args,
70         name => $name,
71         catalyst_component_name => $component_name,
72     );
73     ${"${caller}::current_container"}->add_service($service);
74 }
75
76 1;
77
78 __END__
79
80 =pod
81
82 =head1 NAME
83
84 Catalyst::IOC - IOC for Catalyst, based on Bread::Board
85
86 =head1 SYNOPSIS
87
88     package MyApp::Container;
89     use Moose;
90     use Catalyst::IOC;
91     extends 'Catalyst::IOC::Container';
92
93     container {
94         model {
95             # default component
96             component Foo => ();
97
98             # model Bar needs model Foo to be built before
99             # and Bar's constructor gets Foo as a parameter
100             component Bar => ( dependencies => [
101                 depends_on('/model/Foo'),
102             ]);
103
104             # Baz is rebuilt once per HTTP request
105             component Baz => ( lifecycle => 'Request' );
106
107             # built only once per application life time
108             component Quux => ( lifecycle => 'Singleton' );
109
110             # built once per app life time and uses an external model,
111             # outside the default directory
112             # no need for wrappers or Catalyst::Model::Adaptor
113             component Fnar => (
114                 lifecycle => 'Singleton',
115                 class => 'My::External::Class',
116             );
117         };
118         view {
119             component HTML => ();
120         };
121         controller {
122             component Root => ();
123         };
124     }
125
126 =head1 DESCRIPTION
127
128 Catalyst::IOC provides "sugar" methods to extend the behavior of the default
129 Catalyst container.
130
131 =head1 METHODS
132
133 =head2 container
134
135 Sets up the root container to be customised.
136
137 =head2 model
138
139 Sets up the model container to be customised.
140
141 =head2 view
142
143 Sets up the view container to be customised.
144
145 =head2 controller
146
147 Sets up the controller container to be customised.
148
149 =head2 component
150
151 Adds a component to the subcontainer. Works like L<Bread::Board::service>.
152
153 =head1 AUTHORS
154
155 Catalyst Contributors, see Catalyst.pm
156
157 =head1 SEE ALSO
158
159 L<Bread::Board>
160
161 =head1 COPYRIGHT
162
163 This library is free software. You can redistribute it and/or modify it under
164 the same terms as Perl itself.
165
166 =cut