release 0.53
[catagits/Catalyst-Model-DBIC-Schema.git] / lib / Catalyst / Model / DBIC / Schema.pm
CommitLineData
ad91060a 1package Catalyst::Model::DBIC::Schema;
2
0fbbc8d5 3use Moose;
0f3de2c4 4use mro 'c3';
0fbbc8d5 5extends 'Catalyst::Model';
fb691af9 6with 'CatalystX::Component::Traits';
0fbbc8d5 7
726daa55 8our $VERSION = '0.53';
7dfd616a 9$VERSION = eval $VERSION;
f090a149 10
73f72d28 11use namespace::autoclean;
bd309c0c 12use Carp::Clan '^Catalyst::Model::DBIC::Schema';
bfcd6e3d 13use Data::Dumper;
2201c2e4 14use DBIx::Class ();
ad91060a 15
61ed82a5 16use Catalyst::Model::DBIC::Schema::Types
d816d7bf 17 qw/ConnectInfo LoadedClass SchemaClass Schema/;
61ed82a5 18
41bcf32f 19use MooseX::Types::Moose qw/ArrayRef Str ClassName Undef/;
0fbbc8d5 20
ad91060a 21=head1 NAME
22
23Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class
24
25=head1 SYNOPSIS
26
cbe03ea7 27First, prepare your database schema using L<DBIx::Class>, see
28L<Catalyst::Helper::Model::DBIC::Schema> for how to generate a
29L<DBIx::Class::Schema> from your database using the Helper script, and
30L<DBIx::Class::Schema::Loader::Base>.
07edc53e 31
cbe03ea7 32A typical usage of the helper script would be:
aabc1d75 33
cbe03ea7 34 script/myapp_create.pl model FilmDB DBIC::Schema MyApp::Schema::FilmDB \
35 create=static dbi:mysql:filmdb dbusername dbpass \
f24a5fbb 36 quote_names=1
aabc1d75 37
cbe03ea7 38If you are unfamiliar with L<DBIx::Class>, see L<DBIx::Class::Manual::Intro>
39first.
aabc1d75 40
cbe03ea7 41These examples assume that you already have a schema called
42C<MyApp::Schema::FilmDB>, which defines some Result classes for tables in
43C<MyApp::Schema::FilmDB::Result::Actor> and
44C<MyApp::Schema::FilmDB::Result::Film>. Either created by the helper script (as
45shown above) or manually.
aabc1d75 46
cbe03ea7 47The helper also creates a Model in C<lib/MyApp/Model/FilmDB.pm>, if you already
48have a schema you can create just the Model using:
07edc53e 49
cbe03ea7 50 script/myapp_create.pl model FilmDB DBIC::Schema MyApp::Schema::FilmDB
51 dbi:mysql:filmdb dbusername dbpass
aabc1d75 52
cbe03ea7 53The connect_info is optional and will be hardcoded into the Model if provided.
54It's better to configure it in your L<Catalyst> config file, which will also
55override any hardcoded config, see L</connect_info> for examples.
aabc1d75 56
95b41ca8 57Now you have a working Model which accesses your separate DBIC Schema. This can
58be used/accessed in the normal Catalyst manner, via C<< $c->model() >>:
aabc1d75 59
95b41ca8 60 my $db_model = $c->model('FilmDB'); # a Catalyst::Model
61 my $dbic = $c->model('FilmDB')->schema; # the actual DBIC object
aabc1d75 62
95b41ca8 63There is also a shortcut, which returns a L<DBIx::Class::ResultSet> directly,
64instead of a L<Catalyst::Model>:
07edc53e 65
95b41ca8 66 my $rs = $c->model('FilmDB::Actor');
aabc1d75 67
cbe03ea7 68See L<DBIx::Class::ResultSet> to find out more about which methods can be
69called on ResultSets.
70
71You can also define your own ResultSet methods to encapsulate the
72database/business logic of your applications. These go into, for example,
73C<lib/MyApp/Schema/FilmDB/ResultSet/Actor.pm>. The class must inherit from
74L<DBIx::Class::ResultSet> and is automatically loaded.
75
76Then call your methods like any other L<DBIx::Class::ResultSet> method:
77
78 $c->model('FilmDB::Actor')->SAG_members
aabc1d75 79
95b41ca8 80=head2 Some examples:
aabc1d75 81
f1613faa 82 # to access schema methods directly:
83 $c->model('FilmDB')->schema->source(...);
84
85 # to access the source object, resultset, and class:
07edc53e 86 $c->model('FilmDB')->source(...);
87 $c->model('FilmDB')->resultset(...);
88 $c->model('FilmDB')->class(...);
c12b7310 89
07edc53e 90 # For resultsets, there's an even quicker shortcut:
91 $c->model('FilmDB::Actor')
92 # is the same as $c->model('FilmDB')->resultset('Actor')
ad91060a 93
f1613faa 94 # To get the composed schema for making new connections:
95 my $newconn = $c->model('FilmDB')->composed_schema->connect(...);
96
97 # Or the same thing via a convenience shortcut:
98 my $newconn = $c->model('FilmDB')->connect(...);
99
100 # or, if your schema works on different storage drivers:
101 my $newconn = $c->model('FilmDB')->composed_schema->clone();
102 $newconn->storage_type('::LDAP');
103 $newconn->connection(...);
104
105 # and again, a convenience shortcut
106 my $newconn = $c->model('FilmDB')->clone();
107 $newconn->storage_type('::LDAP');
108 $newconn->connection(...);
109
95b41ca8 110To set up authentication, see L</"Setting up DBIC authentication"> below.
111
ad91060a 112=head1 DESCRIPTION
113
7b39f3f0 114This is a Catalyst Model for L<DBIx::Class::Schema>-based Models. See
ef91bcf9 115the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
116information on generating these Models via Helper scripts.
ad91060a 117
cbe03ea7 118When your Catalyst app starts up, a thin Model layer is created as an interface
119to your DBIC Schema. It should be clearly noted that the model object returned
120by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or resultset object,
121but merely a wrapper proving L<methods|/METHODS> to access the underlying
ae3d05c2 122schema.
d52bc376 123
124In addition to this model class, a shortcut class is generated for each
125source in the schema, allowing easy and direct access to a resultset of the
126corresponding type. These generated classes are even thinner than the model
127class, providing no public methods but simply hooking into Catalyst's
128model() accessor via the
129L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete
130contents of each generated class is roughly equivalent to the following:
131
132 package MyApp::Model::FilmDB::Actor
133 sub ACCEPT_CONTEXT {
134 my ($self, $c) = @_;
135 $c->model('FilmDB')->resultset('Actor');
136 }
137
138In short, there are three techniques available for obtaining a DBIC
139resultset object:
140
141 # the long way
142 my $rs = $c->model('FilmDB')->schema->resultset('Actor');
143
144 # using the shortcut method on the model object
145 my $rs = $c->model('FilmDB')->resultset('Actor');
146
147 # using the generated class directly
148 my $rs = $c->model('FilmDB::Actor');
149
c082639a 150In order to add methods to a DBIC resultset, you cannot simply add them to
151the source (row, table) definition class; you must define a separate custom
cbe03ea7 152resultset class. This is just a matter of making a
153C<lib/MyApp/Schema/ResultSet/Actor.pm> class that inherits from
154L<DBIx::Class::ResultSet>, if you are using
155L<DBIx::Class::Schema/load_namespaces>, the default for helper script generated
156schemas.
157
158See L<DBIx::Class::Manual::Cookbook/"Predefined searches">
159for information on definining your own L<DBIx::Class::ResultSet> classes for
160use with L<DBIx::Class::Schema/load_classes>, the old default.
c082639a 161
ad91060a 162=head1 CONFIG PARAMETERS
163
c4fee9b8 164=head2 schema_class
ad91060a 165
166This is the classname of your L<DBIx::Class::Schema> Schema. It needs
aabc1d75 167to be findable in C<@INC>, but it does not need to be inside the
168C<Catalyst::Model::> namespace. This parameter is required.
ad91060a 169
c4fee9b8 170=head2 connect_info
ad91060a 171
f24a5fbb 172This is a hashref or arrayref of connection parameters, which are specific to
173your C<storage_type> (see your storage type documentation for more details). If
174you only need one parameter (e.g. the DSN), you can just pass a string.
ad91060a 175
0f2fd2c0 176This is not required if C<schema_class> already has connection information
f24a5fbb 177defined inside itself (which isn't highly recommended, but can be done.)
0f2fd2c0 178
7db6da78 179For L<DBIx::Class::Storage::DBI>, which is the only supported
180C<storage_type> in L<DBIx::Class> at the time of this writing, the
181parameters are your dsn, username, password, and connect options hashref.
182
018eb0e2 183See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
184of the arguments supported.
7db6da78 185
186Examples:
187
2201c2e4 188 connect_info => {
189 dsn => 'dbi:Pg:dbname=mypgdb',
190 user => 'postgres',
191 password => ''
192 }
07edc53e 193
2201c2e4 194 connect_info => {
195 dsn => 'dbi:SQLite:dbname=foo.db',
196 on_connect_do => [
197 'PRAGMA synchronous = OFF',
198 ]
199 }
07edc53e 200
2201c2e4 201 connect_info => {
202 dsn => 'dbi:Pg:dbname=mypgdb',
203 user => 'postgres',
204 password => '',
205 pg_enable_utf8 => 1,
206 on_connect_do => [
207 'some SQL statement',
208 'another SQL statement',
209 ],
210 }
7db6da78 211
8281c933 212Or using L<Config::General>:
213
214 <Model::FilmDB>
215 schema_class MyApp::Schema::FilmDB
c34bcab6 216 traits Caching
8281c933 217 <connect_info>
2201c2e4 218 dsn dbi:Pg:dbname=mypgdb
219 user postgres
42e14c31 220 password ""
2201c2e4 221 auto_savepoint 1
f24a5fbb 222 quote_names 1
8281c933 223 on_connect_do some SQL statement
224 on_connect_do another SQL statement
225 </connect_info>
b9cc2f76 226 user_defined_schema_accessor foo
8281c933 227 </Model::FilmDB>
228
229or
230
231 <Model::FilmDB>
232 schema_class MyApp::Schema::FilmDB
233 connect_info dbi:SQLite:dbname=foo.db
234 </Model::FilmDB>
235
2201c2e4 236Or using L<YAML>:
237
238 Model::MyDB:
239 schema_class: MyDB
b9cc2f76 240 traits: Caching
2201c2e4 241 connect_info:
242 dsn: dbi:Oracle:mydb
243 user: mtfnpy
244 password: mypass
245 LongReadLen: 1000000
246 LongTruncOk: 1
b9cc2f76 247 on_connect_call: 'datetime_setup'
f24a5fbb 248 quote_names: 1
2201c2e4 249
250The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
251supported:
252
253 connect_info => [
254 'dbi:Pg:dbname=mypgdb',
255 'postgres',
256 '',
257 {
258 pg_enable_utf8 => 1,
259 },
260 {
0fbbc8d5 261 auto_savepoint => 1,
2201c2e4 262 on_connect_do => [
263 'some SQL statement',
264 'another SQL statement',
265 ],
266 }
267 ]
268
c34bcab6 269=head2 traits
0fbbc8d5 270
41bcf32f 271Array of Traits to apply to the instance. Traits are L<Moose::Role>s.
272
d816d7bf 273They are relative to the C<< MyApp::TraitFor::Model::DBIC::Schema:: >>, then
274the C<< Catalyst::TraitFor::Model::DBIC::Schema:: >> namespaces, unless
275prefixed with C<+> in which case they are taken to be a fully qualified name.
276E.g.:
2201c2e4 277
c34bcab6 278 traits Caching
fb691af9 279 traits +MyApp::TraitFor::Model::Foo
2201c2e4 280
0fbbc8d5 281A new instance is created at application time, so any consumed required
282attributes, coercions and modifiers will work.
2201c2e4 283
fb691af9 284Traits are applied at L<Catalyst::Component/COMPONENT> time using
285L<CatalystX::Component::Traits>.
0fbbc8d5 286
41bcf32f 287C<ref $self> will be an anon class if any traits are applied, C<<
288$self->_original_class_name >> will be the original class.
f090a149 289
c7d7b849 290When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and
291L</ACCEPT_CONTEXT>.
0fbbc8d5 292
c34bcab6 293Traits that come with the distribution:
0fbbc8d5 294
295=over 4
2201c2e4 296
fb691af9 297=item L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>
0fbbc8d5 298
fb691af9 299=item L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>
c4fee9b8 300
d816d7bf 301=item L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>
302
0fbbc8d5 303=back
8281c933 304
c4fee9b8 305=head2 storage_type
ad91060a 306
307Allows the use of a different C<storage_type> than what is set in your
308C<schema_class> (which in turn defaults to C<::DBI> if not set in current
f1613faa 309L<DBIx::Class>). Completely optional, and probably unnecessary for most
310people until other storage backends become available for L<DBIx::Class>.
ad91060a 311
c7d7b849 312=head1 ATTRIBUTES
313
314The keys you pass in the model configuration are available as attributes.
315
316Other attributes available:
317
318=head2 connect_info
319
320Your connect_info args normalized to hashref form (with dsn/user/password.) See
321L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
322L</connect_info>.
323
324=head2 model_name
325
326The model name L<Catalyst> uses to resolve this model, the part after
327C<::Model::> or C<::M::> in your class name. E.g. if your class name is
328C<MyApp::Model::DB> the L</model_name> will be C<DB>.
329
c7d7b849 330=head2 _default_cursor_class
331
6f6b9c2d 332What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
c7d7b849 333doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
334
fb691af9 335=head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>
336
337=head2 _original_class_name
338
339The class name of your model before any L</traits> are applied. E.g.
340C<MyApp::Model::DB>.
341
c7d7b849 342=head2 _traits
343
6f6b9c2d 344Unresolved arrayref of traits passed in the config.
c7d7b849 345
346=head2 _resolved_traits
347
348Traits you used resolved to full class names.
349
21d8159f 350=head1 CONFIGURING YOUR SCHEMA AND RESULTSETS
351
352See the documentation for
353L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
354to pass config values from your L<Catalyst> config to your
355L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.
356
ad91060a 357=head1 METHODS
358
c4fee9b8 359=head2 new
ad91060a 360
361Instantiates the Model based on the above-documented ->config parameters.
0f2fd2c0 362The only required parameter is C<schema_class>. C<connect_info> is
363required in the case that C<schema_class> does not already have connection
364information defined for it.
ad91060a 365
c4fee9b8 366=head2 schema
f1613faa 367
368Accessor which returns the connected schema being used by the this model.
369There are direct shortcuts on the model class itself for
370schema->resultset, schema->source, and schema->class.
371
c4fee9b8 372=head2 composed_schema
f1613faa 373
374Accessor which returns the composed schema, which has no connection info,
375which was used in constructing the C<schema> above. Useful for creating
376new connections based on the same schema/model. There are direct shortcuts
377from the model object for composed_schema->clone and composed_schema->connect
378
c4fee9b8 379=head2 clone
f1613faa 380
381Shortcut for ->composed_schema->clone
382
c4fee9b8 383=head2 connect
f1613faa 384
385Shortcut for ->composed_schema->connect
386
c4fee9b8 387=head2 source
c12b7310 388
f1613faa 389Shortcut for ->schema->source
390
c4fee9b8 391=head2 class
f1613faa 392
393Shortcut for ->schema->class
394
c4fee9b8 395=head2 resultset
f1613faa 396
397Shortcut for ->schema->resultset
398
d816d7bf 399=head2 txn_do
400
401Shortcut for ->schema->txn_do
402
403=head2 txn_scope_guard
404
405Shortcut for ->schema->txn_scope_guard
406
c4fee9b8 407=head2 storage
f1613faa 408
409Provides an accessor for the connected schema's storage object.
21d8159f 410
411See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.
b8427e0b 412
ad91060a 413=cut
414
c34bcab6 415has schema_class => (
0fbbc8d5 416 is => 'ro',
2fa0a1f1 417 isa => SchemaClass,
0fbbc8d5 418 coerce => 1,
419 required => 1
420);
421
c34bcab6 422has storage_type => (is => 'rw', isa => Str);
0fbbc8d5 423
18b829f0 424has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);
0fbbc8d5 425
c7d7b849 426has model_name => (
427 is => 'ro',
428 isa => Str,
429 required => 1,
430 lazy_build => 1,
431);
ad91060a 432
c34bcab6 433has _default_cursor_class => (
61ed82a5 434 is => 'ro',
7314403a 435 isa => LoadedClass,
61ed82a5 436 default => 'DBIx::Class::Storage::DBI::Cursor',
437 coerce => 1
438);
439
d816d7bf 440has schema => (is => 'rw', isa => Schema);
441
21d8159f 442my $app_class;
443
444before COMPONENT => sub {
445 $app_class = ref $_[1] || $_[1];
446};
447
448sub app_class { $app_class }
449
0fbbc8d5 450sub BUILD {
f27a05ea 451 my ($self, $args) = @_;
f2488839 452 my $class = $self->_original_class_name;
2201c2e4 453 my $schema_class = $self->schema_class;
ad91060a 454
2201c2e4 455 if( !$self->connect_info ) {
f1613faa 456 if($schema_class->storage && $schema_class->storage->connect_info) {
2201c2e4 457 $self->connect_info($schema_class->storage->connect_info);
f1613faa 458 }
459 else {
39f5f008 460 die "Either ->config->{connect_info} must be defined for $class"
460e3ac8 461 . " or $schema_class must have connect info defined on it."
462 . " Here's what we got:\n"
ae3d05c2 463 . Dumper($args);
f1613faa 464 }
7db6da78 465 }
466
0fbbc8d5 467 if (exists $self->connect_info->{cursor_class}) {
468 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
469 or croak "invalid connect_info: Cannot load your cursor_class"
470 . " ".$self->connect_info->{cursor_class}.": $@";
471 }
472
d816d7bf 473 $self->setup($args);
0fbbc8d5 474
d816d7bf 475 my $is_installed = defined $self->composed_schema;
2201c2e4 476
d816d7bf 477 $self->composed_schema($schema_class->compose_namespace($class))
478 unless $is_installed;
46a2eb0c 479
ae3d05c2 480 $self->schema($self->composed_schema->clone)
481 unless $self->schema;
f1613faa 482
2201c2e4 483 $self->schema->storage_type($self->storage_type)
484 if $self->storage_type;
7db6da78 485
2201c2e4 486 $self->schema->connection($self->connect_info);
487
d816d7bf 488 $self->_install_rs_models unless $is_installed;
2201c2e4 489}
490
491sub clone { shift->composed_schema->clone(@_); }
492
493sub connect { shift->composed_schema->connect(@_); }
494
ae3d05c2 495# some proxy methods, see also SchemaProxy
d816d7bf 496
497sub resultset { shift->schema->resultset(@_); }
498
499sub txn_do { shift->schema->txn_do(@_); }
500
501sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }
502
1d67a585 503sub storage { shift->schema->storage(@_); }
504
c4fee9b8 505=head2 setup
2201c2e4 506
e203cd42 507Called at C<BUILD> time before configuration, but after L</connect_info> is
c7d7b849 508set. To do something after configuuration use C<< after BUILD => >>.
2201c2e4 509
d816d7bf 510Receives a hashref of args passed to C<BUILD>.
511
2201c2e4 512=cut
513
0fbbc8d5 514sub setup { 1 }
2201c2e4 515
c4fee9b8 516=head2 ACCEPT_CONTEXT
2201c2e4 517
73f72d28 518Point of extension for doing things at C<< $c->model >> time with context,
519returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
520more information.
2201c2e4 521
0fbbc8d5 522=cut
2201c2e4 523
0fbbc8d5 524sub ACCEPT_CONTEXT { shift }
2201c2e4 525
526sub _install_rs_models {
527 my $self = shift;
7b1fe8c2 528 my $class = $self->_original_class_name;
2201c2e4 529
ad91060a 530 no strict 'refs';
39f5f008 531
532 my @sources = $self->schema->sources;
533
ca7cf6f0 534 unless (@sources) {
535 warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
536******************************* WARNING ***************************************
537* No sources found (did you forget to define your tables?) *
538* *
539* To turn off this warning, set the CMDS_NO_SOURCES environment variable. *
540*******************************************************************************
541EOF
542 }
39f5f008 543
544 foreach my $moniker (@sources) {
0b2a7108 545 my $classname = "${class}::$moniker";
7db6da78 546 *{"${classname}::ACCEPT_CONTEXT"} = sub {
ad91060a 547 shift;
2201c2e4 548 shift->model($self->model_name)->resultset($moniker);
ad91060a 549 }
550 }
2201c2e4 551}
ad91060a 552
61ed82a5 553sub _reset_cursor_class {
554 my $self = shift;
555
556 if ($self->storage->can('cursor_class')) {
557 $self->storage->cursor_class($self->_default_cursor_class)
558 if $self->storage->cursor_class ne $self->_default_cursor_class;
559 }
560}
561
50f488ec 562{
563 my %COMPOSED_CACHE;
564
565 sub composed_schema {
566 my $self = shift;
567 my $class = $self->_original_class_name;
568 my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
569
570 $$store = shift if @_;
571
572 return $$store
573 }
574}
575
c7d7b849 576sub _build_model_name {
577 my $self = shift;
578 my $class = $self->_original_class_name;
579 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
580
581 return $model_name;
41bcf32f 582}
583
0fbbc8d5 584__PACKAGE__->meta->make_immutable;
2201c2e4 585
ca7cf6f0 586=head1 ENVIRONMENT
587
588=over 4
589
590=item CMDS_NO_SOURCES
591
cbe03ea7 592Set this variable if you will be using schemas with no sources (Result classes)
593to disable the warning. The warning is there because having no Result classes
594is usually a mistake.
ca7cf6f0 595
596=back
597
95b41ca8 598=head1 Setting up DBIC authentication
599
600You can set this up with
601L<Catalyst::Authentication::Store::DBIx::Class> in MyApp.pm:
602
603 package MyApp;
604
605 use Catalyst qw/... Authentication .../;
606
607 ...
608
10c73a30 609 __PACKAGE__->config('Plugin::Authentication' =>
610 {
95b41ca8 611 default_realm => 'members',
10c73a30 612 members => {
613 credential => {
614 class => 'Password',
615 password_field => 'password',
616 password_type => 'hashed'
617 password_hash_type => 'SHA-256'
618 },
619 store => {
620 class => 'DBIx::Class',
621 user_model => 'DB::User',
622 role_relation => 'roles',
623 role_field => 'rolename',
95b41ca8 624 }
625 }
10c73a30 626 });
95b41ca8 627
ae3d05c2 628=head1 METHOD PROXYING
629
630The automatic proxying to the underlying L<DBIx::Class::Schema> has been
631removed as of version C<0.34>, to enable this feature add C<SchemaProxy> to
632L</traits>.
633
634See L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>.
635
ad91060a 636=head1 SEE ALSO
637
7b39f3f0 638General Catalyst Stuff:
639
640L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
641L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
642
643Stuff related to DBIC and this Model style:
644
645L<DBIx::Class>, L<DBIx::Class::Schema>,
f090a149 646L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
e203cd42 647L<CatalystX::Component::Traits>, L<MooseX::Traits::Pluggable>
ad91060a 648
c34bcab6 649Traits:
c4fee9b8 650
fb691af9 651L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>,
cbe03ea7 652L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>,
d816d7bf 653L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>,
cbe03ea7 654L<Catalyst::TraitFor::Model::DBIC::Schema::QueryLog>
c4fee9b8 655
ad91060a 656=head1 AUTHOR
657
e203cd42 658Brandon L Black C<blblack at gmail.com>
ad91060a 659
e203cd42 660=head1 CONTRIBUTORS
2ff00e2b 661
e203cd42 662caelum: Rafael Kitover C<rkitover at cpan.org>
2ff00e2b 663
4e251d1a 664dandv: Dan Dascalescu C<dandv at cpan.org>
6d9e2623 665
4e251d1a 666bluefeet: Aran Deltac C<bluefeet@cpan.org>
667
668t0m: Tomas Doran C<bobtfish@bobtfish.net>
669
670osfameron: C<osfameron@cpan.org>
49c75c04 671
cbe03ea7 672ozum: Ozum Eldogan C<ozum@ozum.net>
ce9e19dc 673
87145c6c 674Pavel I. Shaydo C<zwon@trinitum.org>
675
ad91060a 676=head1 COPYRIGHT
677
21d8159f 678Copyright (c) 2006 - 2010
4e251d1a 679the Catalyst::Model::DBIC::Schema L</AUTHOR> and L</CONTRIBUTORS>
680as listed above.
681
682=head1 LICENSE
683
6d9e2623 684This program is free software. You can redistribute it and/or modify it
ad91060a 685under the same terms as Perl itself.
686
687=cut
688
6891;
21d8159f 690# vim:sts=4 sw=4 et tw=80: