release 0.46
[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
fb920729 8our $VERSION = '0.46';
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 \
36 quote_char='`' name_sep='.'
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
172This is an arrayref of connection parameters, which are specific to your
b9a72351 173C<storage_type> (see your storage type documentation for more details).
174If you only need one parameter (e.g. the DSN), you can just pass a string
175instead of an arrayref.
ad91060a 176
0f2fd2c0 177This is not required if C<schema_class> already has connection information
d89e6c8a 178defined inside itself (which isn't highly recommended, but can be done)
0f2fd2c0 179
7db6da78 180For L<DBIx::Class::Storage::DBI>, which is the only supported
181C<storage_type> in L<DBIx::Class> at the time of this writing, the
182parameters are your dsn, username, password, and connect options hashref.
183
018eb0e2 184See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
185of the arguments supported.
7db6da78 186
187Examples:
188
2201c2e4 189 connect_info => {
190 dsn => 'dbi:Pg:dbname=mypgdb',
191 user => 'postgres',
192 password => ''
193 }
07edc53e 194
2201c2e4 195 connect_info => {
196 dsn => 'dbi:SQLite:dbname=foo.db',
197 on_connect_do => [
198 'PRAGMA synchronous = OFF',
199 ]
200 }
07edc53e 201
2201c2e4 202 connect_info => {
203 dsn => 'dbi:Pg:dbname=mypgdb',
204 user => 'postgres',
205 password => '',
206 pg_enable_utf8 => 1,
207 on_connect_do => [
208 'some SQL statement',
209 'another SQL statement',
210 ],
211 }
7db6da78 212
8281c933 213Or using L<Config::General>:
214
215 <Model::FilmDB>
216 schema_class MyApp::Schema::FilmDB
c34bcab6 217 traits Caching
8281c933 218 <connect_info>
2201c2e4 219 dsn dbi:Pg:dbname=mypgdb
220 user postgres
42e14c31 221 password ""
2201c2e4 222 auto_savepoint 1
a75b6e58 223 quote_char """
8281c933 224 on_connect_do some SQL statement
225 on_connect_do another SQL statement
226 </connect_info>
b9cc2f76 227 user_defined_schema_accessor foo
8281c933 228 </Model::FilmDB>
229
230or
231
232 <Model::FilmDB>
233 schema_class MyApp::Schema::FilmDB
234 connect_info dbi:SQLite:dbname=foo.db
235 </Model::FilmDB>
236
2201c2e4 237Or using L<YAML>:
238
239 Model::MyDB:
240 schema_class: MyDB
b9cc2f76 241 traits: Caching
2201c2e4 242 connect_info:
243 dsn: dbi:Oracle:mydb
244 user: mtfnpy
245 password: mypass
246 LongReadLen: 1000000
247 LongTruncOk: 1
b9cc2f76 248 on_connect_call: 'datetime_setup'
a75b6e58 249 quote_char: '"'
2201c2e4 250
251The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also
252supported:
253
254 connect_info => [
255 'dbi:Pg:dbname=mypgdb',
256 'postgres',
257 '',
258 {
259 pg_enable_utf8 => 1,
260 },
261 {
0fbbc8d5 262 auto_savepoint => 1,
2201c2e4 263 on_connect_do => [
264 'some SQL statement',
265 'another SQL statement',
266 ],
267 }
268 ]
269
c34bcab6 270=head2 traits
0fbbc8d5 271
41bcf32f 272Array of Traits to apply to the instance. Traits are L<Moose::Role>s.
273
d816d7bf 274They are relative to the C<< MyApp::TraitFor::Model::DBIC::Schema:: >>, then
275the C<< Catalyst::TraitFor::Model::DBIC::Schema:: >> namespaces, unless
276prefixed with C<+> in which case they are taken to be a fully qualified name.
277E.g.:
2201c2e4 278
c34bcab6 279 traits Caching
fb691af9 280 traits +MyApp::TraitFor::Model::Foo
2201c2e4 281
0fbbc8d5 282A new instance is created at application time, so any consumed required
283attributes, coercions and modifiers will work.
2201c2e4 284
fb691af9 285Traits are applied at L<Catalyst::Component/COMPONENT> time using
286L<CatalystX::Component::Traits>.
0fbbc8d5 287
41bcf32f 288C<ref $self> will be an anon class if any traits are applied, C<<
289$self->_original_class_name >> will be the original class.
f090a149 290
c7d7b849 291When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and
292L</ACCEPT_CONTEXT>.
0fbbc8d5 293
c34bcab6 294Traits that come with the distribution:
0fbbc8d5 295
296=over 4
2201c2e4 297
fb691af9 298=item L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>
0fbbc8d5 299
fb691af9 300=item L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>
c4fee9b8 301
d816d7bf 302=item L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>
303
0fbbc8d5 304=back
8281c933 305
c4fee9b8 306=head2 storage_type
ad91060a 307
308Allows the use of a different C<storage_type> than what is set in your
309C<schema_class> (which in turn defaults to C<::DBI> if not set in current
f1613faa 310L<DBIx::Class>). Completely optional, and probably unnecessary for most
311people until other storage backends become available for L<DBIx::Class>.
ad91060a 312
c7d7b849 313=head1 ATTRIBUTES
314
315The keys you pass in the model configuration are available as attributes.
316
317Other attributes available:
318
319=head2 connect_info
320
321Your connect_info args normalized to hashref form (with dsn/user/password.) See
322L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
323L</connect_info>.
324
325=head2 model_name
326
327The model name L<Catalyst> uses to resolve this model, the part after
328C<::Model::> or C<::M::> in your class name. E.g. if your class name is
329C<MyApp::Model::DB> the L</model_name> will be C<DB>.
330
c7d7b849 331=head2 _default_cursor_class
332
6f6b9c2d 333What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
c7d7b849 334doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
335
fb691af9 336=head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>
337
338=head2 _original_class_name
339
340The class name of your model before any L</traits> are applied. E.g.
341C<MyApp::Model::DB>.
342
c7d7b849 343=head2 _traits
344
6f6b9c2d 345Unresolved arrayref of traits passed in the config.
c7d7b849 346
347=head2 _resolved_traits
348
349Traits you used resolved to full class names.
350
21d8159f 351=head1 CONFIGURING YOUR SCHEMA AND RESULTSETS
352
353See the documentation for
354L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
355to pass config values from your L<Catalyst> config to your
356L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.
357
ad91060a 358=head1 METHODS
359
c4fee9b8 360=head2 new
ad91060a 361
362Instantiates the Model based on the above-documented ->config parameters.
0f2fd2c0 363The only required parameter is C<schema_class>. C<connect_info> is
364required in the case that C<schema_class> does not already have connection
365information defined for it.
ad91060a 366
c4fee9b8 367=head2 schema
f1613faa 368
369Accessor which returns the connected schema being used by the this model.
370There are direct shortcuts on the model class itself for
371schema->resultset, schema->source, and schema->class.
372
c4fee9b8 373=head2 composed_schema
f1613faa 374
375Accessor which returns the composed schema, which has no connection info,
376which was used in constructing the C<schema> above. Useful for creating
377new connections based on the same schema/model. There are direct shortcuts
378from the model object for composed_schema->clone and composed_schema->connect
379
c4fee9b8 380=head2 clone
f1613faa 381
382Shortcut for ->composed_schema->clone
383
c4fee9b8 384=head2 connect
f1613faa 385
386Shortcut for ->composed_schema->connect
387
c4fee9b8 388=head2 source
c12b7310 389
f1613faa 390Shortcut for ->schema->source
391
c4fee9b8 392=head2 class
f1613faa 393
394Shortcut for ->schema->class
395
c4fee9b8 396=head2 resultset
f1613faa 397
398Shortcut for ->schema->resultset
399
d816d7bf 400=head2 txn_do
401
402Shortcut for ->schema->txn_do
403
404=head2 txn_scope_guard
405
406Shortcut for ->schema->txn_scope_guard
407
c4fee9b8 408=head2 storage
f1613faa 409
410Provides an accessor for the connected schema's storage object.
21d8159f 411
412See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.
b8427e0b 413
ad91060a 414=cut
415
c34bcab6 416has schema_class => (
0fbbc8d5 417 is => 'ro',
2fa0a1f1 418 isa => SchemaClass,
0fbbc8d5 419 coerce => 1,
420 required => 1
421);
422
c34bcab6 423has storage_type => (is => 'rw', isa => Str);
0fbbc8d5 424
18b829f0 425has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);
0fbbc8d5 426
c7d7b849 427has model_name => (
428 is => 'ro',
429 isa => Str,
430 required => 1,
431 lazy_build => 1,
432);
ad91060a 433
c34bcab6 434has _default_cursor_class => (
61ed82a5 435 is => 'ro',
7314403a 436 isa => LoadedClass,
61ed82a5 437 default => 'DBIx::Class::Storage::DBI::Cursor',
438 coerce => 1
439);
440
d816d7bf 441has schema => (is => 'rw', isa => Schema);
442
21d8159f 443my $app_class;
444
445before COMPONENT => sub {
446 $app_class = ref $_[1] || $_[1];
447};
448
449sub app_class { $app_class }
450
0fbbc8d5 451sub BUILD {
f27a05ea 452 my ($self, $args) = @_;
f2488839 453 my $class = $self->_original_class_name;
2201c2e4 454 my $schema_class = $self->schema_class;
ad91060a 455
2201c2e4 456 if( !$self->connect_info ) {
f1613faa 457 if($schema_class->storage && $schema_class->storage->connect_info) {
2201c2e4 458 $self->connect_info($schema_class->storage->connect_info);
f1613faa 459 }
460 else {
39f5f008 461 die "Either ->config->{connect_info} must be defined for $class"
460e3ac8 462 . " or $schema_class must have connect info defined on it."
463 . " Here's what we got:\n"
ae3d05c2 464 . Dumper($args);
f1613faa 465 }
7db6da78 466 }
467
0fbbc8d5 468 if (exists $self->connect_info->{cursor_class}) {
469 eval { Class::MOP::load_class($self->connect_info->{cursor_class}) }
470 or croak "invalid connect_info: Cannot load your cursor_class"
471 . " ".$self->connect_info->{cursor_class}.": $@";
472 }
473
d816d7bf 474 $self->setup($args);
0fbbc8d5 475
d816d7bf 476 my $is_installed = defined $self->composed_schema;
2201c2e4 477
d816d7bf 478 $self->composed_schema($schema_class->compose_namespace($class))
479 unless $is_installed;
46a2eb0c 480
ae3d05c2 481 $self->schema($self->composed_schema->clone)
482 unless $self->schema;
f1613faa 483
2201c2e4 484 $self->schema->storage_type($self->storage_type)
485 if $self->storage_type;
7db6da78 486
2201c2e4 487 $self->schema->connection($self->connect_info);
488
d816d7bf 489 $self->_install_rs_models unless $is_installed;
2201c2e4 490}
491
492sub clone { shift->composed_schema->clone(@_); }
493
494sub connect { shift->composed_schema->connect(@_); }
495
ae3d05c2 496# some proxy methods, see also SchemaProxy
d816d7bf 497
498sub resultset { shift->schema->resultset(@_); }
499
500sub txn_do { shift->schema->txn_do(@_); }
501
502sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }
503
1d67a585 504sub storage { shift->schema->storage(@_); }
505
c4fee9b8 506=head2 setup
2201c2e4 507
e203cd42 508Called at C<BUILD> time before configuration, but after L</connect_info> is
c7d7b849 509set. To do something after configuuration use C<< after BUILD => >>.
2201c2e4 510
d816d7bf 511Receives a hashref of args passed to C<BUILD>.
512
2201c2e4 513=cut
514
0fbbc8d5 515sub setup { 1 }
2201c2e4 516
c4fee9b8 517=head2 ACCEPT_CONTEXT
2201c2e4 518
73f72d28 519Point of extension for doing things at C<< $c->model >> time with context,
520returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
521more information.
2201c2e4 522
0fbbc8d5 523=cut
2201c2e4 524
0fbbc8d5 525sub ACCEPT_CONTEXT { shift }
2201c2e4 526
527sub _install_rs_models {
528 my $self = shift;
7b1fe8c2 529 my $class = $self->_original_class_name;
2201c2e4 530
ad91060a 531 no strict 'refs';
39f5f008 532
533 my @sources = $self->schema->sources;
534
ca7cf6f0 535 unless (@sources) {
536 warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
537******************************* WARNING ***************************************
538* No sources found (did you forget to define your tables?) *
539* *
540* To turn off this warning, set the CMDS_NO_SOURCES environment variable. *
541*******************************************************************************
542EOF
543 }
39f5f008 544
545 foreach my $moniker (@sources) {
0b2a7108 546 my $classname = "${class}::$moniker";
7db6da78 547 *{"${classname}::ACCEPT_CONTEXT"} = sub {
ad91060a 548 shift;
2201c2e4 549 shift->model($self->model_name)->resultset($moniker);
ad91060a 550 }
551 }
2201c2e4 552}
ad91060a 553
61ed82a5 554sub _reset_cursor_class {
555 my $self = shift;
556
557 if ($self->storage->can('cursor_class')) {
558 $self->storage->cursor_class($self->_default_cursor_class)
559 if $self->storage->cursor_class ne $self->_default_cursor_class;
560 }
561}
562
50f488ec 563{
564 my %COMPOSED_CACHE;
565
566 sub composed_schema {
567 my $self = shift;
568 my $class = $self->_original_class_name;
569 my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
570
571 $$store = shift if @_;
572
573 return $$store
574 }
575}
576
c7d7b849 577sub _build_model_name {
578 my $self = shift;
579 my $class = $self->_original_class_name;
580 (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
581
582 return $model_name;
41bcf32f 583}
584
0fbbc8d5 585__PACKAGE__->meta->make_immutable;
2201c2e4 586
ca7cf6f0 587=head1 ENVIRONMENT
588
589=over 4
590
591=item CMDS_NO_SOURCES
592
cbe03ea7 593Set this variable if you will be using schemas with no sources (Result classes)
594to disable the warning. The warning is there because having no Result classes
595is usually a mistake.
ca7cf6f0 596
597=back
598
95b41ca8 599=head1 Setting up DBIC authentication
600
601You can set this up with
602L<Catalyst::Authentication::Store::DBIx::Class> in MyApp.pm:
603
604 package MyApp;
605
606 use Catalyst qw/... Authentication .../;
607
608 ...
609
10c73a30 610 __PACKAGE__->config('Plugin::Authentication' =>
611 {
95b41ca8 612 default_realm => 'members',
10c73a30 613 members => {
614 credential => {
615 class => 'Password',
616 password_field => 'password',
617 password_type => 'hashed'
618 password_hash_type => 'SHA-256'
619 },
620 store => {
621 class => 'DBIx::Class',
622 user_model => 'DB::User',
623 role_relation => 'roles',
624 role_field => 'rolename',
95b41ca8 625 }
626 }
10c73a30 627 });
95b41ca8 628
ae3d05c2 629=head1 METHOD PROXYING
630
631The automatic proxying to the underlying L<DBIx::Class::Schema> has been
632removed as of version C<0.34>, to enable this feature add C<SchemaProxy> to
633L</traits>.
634
635See L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>.
636
ad91060a 637=head1 SEE ALSO
638
7b39f3f0 639General Catalyst Stuff:
640
641L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
642L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,
643
644Stuff related to DBIC and this Model style:
645
646L<DBIx::Class>, L<DBIx::Class::Schema>,
f090a149 647L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
e203cd42 648L<CatalystX::Component::Traits>, L<MooseX::Traits::Pluggable>
ad91060a 649
c34bcab6 650Traits:
c4fee9b8 651
fb691af9 652L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>,
cbe03ea7 653L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>,
d816d7bf 654L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>,
cbe03ea7 655L<Catalyst::TraitFor::Model::DBIC::Schema::QueryLog>
c4fee9b8 656
ad91060a 657=head1 AUTHOR
658
e203cd42 659Brandon L Black C<blblack at gmail.com>
ad91060a 660
e203cd42 661=head1 CONTRIBUTORS
2ff00e2b 662
e203cd42 663caelum: Rafael Kitover C<rkitover at cpan.org>
2ff00e2b 664
4e251d1a 665dandv: Dan Dascalescu C<dandv at cpan.org>
6d9e2623 666
4e251d1a 667bluefeet: Aran Deltac C<bluefeet@cpan.org>
668
669t0m: Tomas Doran C<bobtfish@bobtfish.net>
670
671osfameron: C<osfameron@cpan.org>
49c75c04 672
cbe03ea7 673ozum: Ozum Eldogan C<ozum@ozum.net>
ce9e19dc 674
87145c6c 675Pavel I. Shaydo C<zwon@trinitum.org>
676
ad91060a 677=head1 COPYRIGHT
678
21d8159f 679Copyright (c) 2006 - 2010
4e251d1a 680the Catalyst::Model::DBIC::Schema L</AUTHOR> and L</CONTRIBUTORS>
681as listed above.
682
683=head1 LICENSE
684
6d9e2623 685This program is free software. You can redistribute it and/or modify it
ad91060a 686under the same terms as Perl itself.
687
688=cut
689
6901;
21d8159f 691# vim:sts=4 sw=4 et tw=80: