use croak instead of die for user errors.
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
1 package DBIx::Class::Schema;
2
3 use strict;
4 use warnings;
5
6 use Carp qw/croak/;
7 use UNIVERSAL::require;
8
9 use base qw/DBIx::Class/;
10
11 __PACKAGE__->load_components(qw/Exception/);
12 __PACKAGE__->mk_classdata('class_mappings' => {});
13 __PACKAGE__->mk_classdata('source_registrations' => {});
14 __PACKAGE__->mk_classdata('storage_type' => 'DBI');
15 __PACKAGE__->mk_classdata('storage');
16
17 =head1 NAME
18
19 DBIx::Class::Schema - composable schemas
20
21 =head1 SYNOPSIS
22
23 in My/Schema.pm
24
25   package My::Schema;
26
27   use base qw/DBIx::Class::Schema/;
28
29   __PACKAGE__->load_classes(qw/Foo Bar Baz/);
30
31 in My/Schema/Foo.pm
32
33   package My::Schema::Foo;
34
35   use base qw/DBIx::Class/;
36
37   __PACKAGE__->load_components(qw/PK::Auto::Pg Core/); # for example
38   __PACKAGE__->table('foo');
39   ...
40
41 in My/DB.pm
42
43   use My::Schema;
44
45   My::Schema->compose_connection('My::DB', $dsn, $user, $pass, $attrs);
46
47 then in app code
48
49   my @obj = My::DB::Foo->search({}); # My::DB::Foo isa My::Schema::Foo My::DB
50
51 =head1 DESCRIPTION
52
53 Creates database classes based on a schema. This allows you to have more than
54 one concurrent connection using the same database classes, by making 
55 subclasses under a new namespace for each connection. If you only need one 
56 class, you should probably use L<DBIx::Class::DB> directly instead.
57
58 NB: If you're used to L<Class::DBI> it's worth reading the L</SYNOPSIS>
59 carefully as DBIx::Class does things a little differently. Note in
60 particular which module inherits off which.
61
62 =head1 METHODS
63
64 =head2 register_class <moniker> <component_class>
65
66 Registers a class which isa ResultSourceInstance; equivalent to calling
67
68   $schema->register_source($moniker, $class->result_source_instance);
69
70 =cut
71
72 sub register_class {
73   my ($self, $moniker, $to_register) = @_;
74   $self->register_source($moniker => $to_register->result_source_instance);
75 }
76
77 =head2 register_source <moniker> <result source>
78
79 Registers the result source in the schema with the given moniker
80
81 =cut
82
83 sub register_source {
84   my ($self, $moniker, $source) = @_;
85   my %reg = %{$self->source_registrations};
86   $reg{$moniker} = $source;
87   $self->source_registrations(\%reg);
88   $source->schema($self);
89   if ($source->result_class) {
90     my %map = %{$self->class_mappings};
91     $map{$source->result_class} = $moniker;
92     $self->class_mappings(\%map);
93   }
94
95
96 =head2 class
97
98   my $class = $schema->class('Foo');
99
100 Retrieves the result class name for a given result source
101
102 =cut
103
104 sub class {
105   my ($self, $moniker) = @_;
106   return $self->source($moniker)->result_class;
107 }
108
109 =head2 source
110
111   my $source = $schema->source('Foo');
112
113 Returns the result source object for the registered name
114
115 =cut
116
117 sub source {
118   my ($self, $moniker) = @_;
119   my $sreg = $self->source_registrations;
120   return $sreg->{$moniker} if exists $sreg->{$moniker};
121
122   # if we got here, they probably passed a full class name
123   my $mapped = $self->class_mappings->{$moniker};
124   croak "Can't find source for ${moniker}"
125     unless $mapped && exists $sreg->{$mapped};
126   return $sreg->{$mapped};
127 }
128
129 =head2 sources
130
131   my @source_monikers = $schema->sources;
132
133 Returns the source monikers of all source registrations on this schema
134
135 =cut
136
137 sub sources { return keys %{shift->source_registrations}; }
138
139 =head2 resultset
140
141   my $rs = $schema->resultset('Foo');
142
143 Returns the resultset for the registered moniker
144
145 =cut
146
147 sub resultset {
148   my ($self, $moniker) = @_;
149   return $self->source($moniker)->resultset;
150 }
151
152 =head2  load_classes [<classes>, (<class>, <class>), {<namespace> => [<classes>]}]
153
154 Uses L<Module::Find> to find all classes under the database class' namespace,
155 or uses the classes you select.  Then it loads the component (using L<use>), 
156 and registers them (using B<register_class>);
157
158 It is possible to comment out classes with a leading '#', but note that perl
159 will think it's a mistake (trying to use a comment in a qw list) so you'll
160 need to add "no warnings 'qw';" before your load_classes call.
161
162 =cut
163
164 sub load_classes {
165   my ($class, @params) = @_;
166   
167   my %comps_for;
168   
169   if (@params) {
170     foreach my $param (@params) {
171       if (ref $param eq 'ARRAY') {
172         # filter out commented entries
173         my @modules = grep { $_ !~ /^#/ } @$param;
174         
175         push (@{$comps_for{$class}}, @modules);
176       }
177       elsif (ref $param eq 'HASH') {
178         # more than one namespace possible
179         for my $comp ( keys %$param ) {
180           # filter out commented entries
181           my @modules = grep { $_ !~ /^#/ } @{$param->{$comp}};
182
183           push (@{$comps_for{$comp}}, @modules);
184         }
185       }
186       else {
187         # filter out commented entries
188         push (@{$comps_for{$class}}, $param) if $param !~ /^#/;
189       }
190     }
191   } else {
192     eval "require Module::Find;";
193     $class->throw("No arguments to load_classes and couldn't load".
194       " Module::Find ($@)") if $@;
195     my @comp = map { substr $_, length "${class}::"  } Module::Find::findallmod($class);
196     $comps_for{$class} = \@comp;
197   }
198
199   foreach my $prefix (keys %comps_for) {
200     foreach my $comp (@{$comps_for{$prefix}||[]}) {
201       my $comp_class = "${prefix}::${comp}";
202       eval "use $comp_class"; # If it fails, assume the user fixed it
203       if ($@) {
204         die $@ unless $@ =~ /Can't locate/;
205       }
206       $class->register_class($comp => $comp_class);
207     }
208   }
209 }
210
211 =head2 compose_connection <target> <@db_info>
212
213 This is the most important method in this class. it takes a target namespace,
214 as well as dbh connection info, and creates a L<DBIx::Class::DB> class as
215 well as subclasses for each of your database classes in this namespace, using
216 this connection.
217
218 It will also setup a ->class method on the target class, which lets you
219 resolve database classes based on the schema component name, for example
220
221   MyApp::DB->class('Foo') # returns MyApp::DB::Foo, 
222                           # which ISA MyApp::Schema::Foo
223
224 This is the recommended API for accessing Schema generated classes, and 
225 using it might give you instant advantages with future versions of DBIC.
226
227 WARNING: Loading components into Schema classes after compose_connection
228 may not cause them to be seen by the classes in your target namespace due
229 to the dispatch table approach used by Class::C3. If you do this you may find
230 you need to call Class::C3->reinitialize() afterwards to get the behaviour
231 you expect.
232
233 =cut
234
235 sub compose_connection {
236   my ($self, $target, @info) = @_;
237   my $base = 'DBIx::Class::ResultSetInstance';
238   $base->require;
239   my $schema = $self->compose_namespace($target, $base);
240   $schema->connection(@info);
241   foreach my $moniker ($schema->sources) {
242     my $source = $schema->source($moniker);
243     my $class = $source->result_class;
244     #warn "$moniker $class $source ".$source->storage;
245     $class->mk_classdata(result_source_instance => $source);
246     $class->mk_classdata(resultset_instance => $source->resultset);
247     $class->mk_classdata(class_resolver => $schema);
248   }
249   return $schema;
250 }
251
252 sub compose_namespace {
253   my ($self, $target, $base) = @_;
254   my %reg = %{ $self->source_registrations };
255   my %target;
256   my %map;
257   my $schema = $self->clone;
258   foreach my $moniker ($schema->sources) {
259     my $source = $schema->source($moniker);
260     my $target_class = "${target}::${moniker}";
261     $self->inject_base(
262       $target_class => $source->result_class, ($base ? $base : ())
263     );
264     $source->result_class($target_class);
265   }
266   {
267     no strict 'refs';
268     *{"${target}::schema"} =
269       sub { $schema };
270     foreach my $meth (qw/class source resultset/) {
271       *{"${target}::${meth}"} =
272         sub { shift->schema->$meth(@_) };
273     }
274   }
275   return $schema;
276 }
277
278 =head2 setup_connection_class <$target> <@info>
279
280 Sets up a database connection class to inject between the schema
281 and the subclasses the schema creates.
282
283 =cut
284
285 sub setup_connection_class {
286   my ($class, $target, @info) = @_;
287   $class->inject_base($target => 'DBIx::Class::DB');
288   #$target->load_components('DB');
289   $target->connection(@info);
290 }
291
292 =head2 connection(@args)
293
294 Instantiates a new Storage object of type storage_type and passes the
295 arguments to $storage->connection_info. Sets the connection in-place on
296 the schema.
297
298 =cut
299
300 sub connection {
301   my ($self, @info) = @_;
302   my $storage_class = 'DBIx::Class::Storage::'.$self->storage_type;
303   $storage_class->require;
304   my $storage = $storage_class->new;
305   $storage->connect_info(\@info);
306   $self->storage($storage);
307   return $self;
308 }
309
310 =head2 connect(@info)
311
312 Conveneience method, equivalent to $schema->clone->connection(@info)
313
314 =cut
315
316 sub connect { shift->clone->connection(@_) };
317
318 =head2 clone
319
320 Clones the schema and its associated result_source objects and returns the
321 copy.
322
323 =cut
324
325 sub clone {
326   my ($self) = @_;
327   my $clone = bless({ (ref $self ? %$self : ()) }, ref $self || $self);
328   foreach my $moniker ($self->sources) {
329     my $source = $self->source($moniker);
330     my $new = $source->new($source);
331     $clone->register_source($moniker => $new);
332   }
333   return $clone;
334 }
335
336 1;
337
338 =head1 AUTHORS
339
340 Matt S. Trout <mst@shadowcatsystems.co.uk>
341
342 =head1 LICENSE
343
344 You may distribute this code under the same terms as Perl itself.
345
346 =cut
347