/usr/share/perl5/Rose/DB/Object/Loader.pm is in librose-db-object-perl 1:0.815-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 | package Rose::DB::Object::Loader;
use strict;
use Cwd;
use File::Path;
use File::Spec;
use DBI;
use Carp;
use Rose::DB;
use Rose::DB::Object;
use Rose::DB::Object::ConventionManager;
use Rose::DB::Object::Metadata::Util qw(perl_hashref);
use Rose::DB::Object::Metadata::Auto;
use Rose::Object;
our @ISA = qw(Rose::Object);
our $VERSION = '0.799';
our $Debug = 0;
use Rose::Object::MakeMethods::Generic
(
scalar =>
[
'db_catalog',
'db_database',
'db_schema',
'db_username',
'db_password',
'db_options',
'include_tables',
'exclude_tables',
'filter_tables',
'pre_init_hook',
'post_init_hook',
'module_dir',
'module_preamble',
'module_postamble',
],
'scalar --get_set_init' =>
[
'with_relationships',
],
boolean =>
[
'using_default_base_class',
'require_primary_key' => { default => 1 },
'include_views' => { default => 0 },
'with_managers' => { default => 1 },
'with_foreign_keys' => { default => 1 },
'with_unique_keys' => { default => 1 },
'convention_manager_was_set' => { default => 0 },
'include_predicated_unique_indexes' => { default => 0 },
'warn_on_missing_primary_key',
'no_auto_sequences' => { default => 0 },
],
);
sub warn_on_missing_pk { shift->warn_on_missing_primary_key(@_) }
CHOOSE_CLONE:
{
local $@;
# Get the best available clone method
eval
{
require Scalar::Util::Clone;
*clone = \&Scalar::Util::Clone::clone;
};
if($@)
{
require Clone;
*clone = \&Clone::clone;
}
}
sub init_with_relationships { 1 }
my $Base_Class_Counter = 1;
sub generate_object_base_class_name
{
my($self) = shift;
return ($self->class_prefix ?
($self->class_prefix . 'DB::Object::AutoBase') :
'Rose::DB::Object::LoaderGenerated::AutoBase') .
$Base_Class_Counter++;
}
sub generate_db_base_class_name
{
my($self) = shift;
return ($self->class_prefix ?
($self->class_prefix . 'DB::AutoBase') :
'Rose::DB::LoaderGenerated::AutoBase') .
$Base_Class_Counter++;
}
sub generate_manager_class_name
{
my($self, $object_class, $cm) = @_;
return $cm ? $cm->auto_manager_class_name($object_class) :
$self->convention_manager->auto_manager_class_name($object_class);
}
sub base_classes
{
my($self) = shift;
unless(@_)
{
if(my $bc = $self->{'base_classes'})
{
return wantarray ? @$bc : $bc;
}
# Make new base class
my $bc = $self->{'base_classes'} = [ $self->generate_object_base_class_name ];
$self->using_default_base_class(1);
no strict 'refs';
@{"$bc->[0]::ISA"} = qw(Rose::DB::Object);
return wantarray ? @$bc : $bc;
}
my $bc = shift;
unless(ref $bc)
{
$bc = [ $bc ];
}
#my $found_rdbo = 0;
foreach my $class (@$bc)
{
unless($class =~ /^(?:\w+::)*\w+$/)
{
croak "Illegal base class name: $class";
}
#$found_rdbo = 1 if(UNIVERSAL::isa($class, 'Rose::DB::Object'));
}
#unless($found_rdbo)
#{
# croak "None of the base classes inherit from Rose::DB::Object";
#}
$self->using_default_base_class(0);
$self->{'base_classes'} = $bc;
return wantarray ? @$bc : $bc;
}
sub base_class { shift->base_classes(@_) }
use constant DEFAULT_MANAGER_BASE_CLASS => 'Rose::DB::Object::Manager';
sub manager_base_classes
{
my($self) = shift;
if(my $bc = shift)
{
unless(ref $bc)
{
$bc = [ $bc ];
}
#my $found_base = 0;
foreach my $class (@$bc)
{
unless($class =~ /^(?:\w+::)*\w+$/)
{
croak "Illegal manager base class name: $class";
}
#$found_base = 1 if(UNIVERSAL::isa($class, 'Rose::DB::Object::Manager'));
}
#unless($found_base)
#{
# croak "None of the manager base classes inherit from ",
# "Rose::DB::Object::Manager";
#}
$self->{'manager_base_classes'} = $bc;
}
my $bc = $self->{'manager_base_classes'};
unless(defined $bc && @$bc)
{
return wantarray ? DEFAULT_MANAGER_BASE_CLASS : [ DEFAULT_MANAGER_BASE_CLASS ];
}
return wantarray ? @{$self->{'manager_base_classes'}} :
$self->{'manager_base_classes'}
}
sub manager_base_class { shift->manager_base_classes(@_) }
sub convention_manager
{
my($self) = shift;
if(@_)
{
my $cm = shift;
if(ref $cm)
{
unless(UNIVERSAL::isa($cm, 'Rose::DB::Object::ConventionManager'))
{
croak "Not a Rose::DB::Object::ConventionManager-derived object: $cm";
}
$self->{'convention_manager'} = $cm;
}
else
{
unless(UNIVERSAL::isa($cm, 'Rose::DB::Object::ConventionManager'))
{
croak "Not a Rose::DB::Object::ConventionManager-derived class: $cm";
}
$self->{'convention_manager'} = $cm->new;
}
$self->convention_manager_was_set(1);
}
return $self->{'convention_manager'} ||= Rose::DB::Object::ConventionManager->new;
}
sub class_prefix
{
my($self) = shift;
return $self->{'class_prefix'} unless(@_);
my $class_prefix = shift;
if(length $class_prefix)
{
unless($class_prefix =~ /^(?:\w+::)*\w+(?:::)?$/)
{
croak "Illegal class prefix: $class_prefix";
}
$class_prefix .= '::' unless($class_prefix =~ /::$/);
}
return $self->{'class_prefix'} = $class_prefix;
}
sub force_lowercase
{
my($self) = shift;
if(@_)
{
my $value = shift;
# Important: allow this attribute to be set to undef, unlike
# most "normal" boolean attributes.
return $self->{'force_lowercase'} = undef unless(defined $value);
return $self->{'force_lowercase'} = $value ? 1 : 0;
}
return $self->{'force_lowercase'};
}
sub db
{
my($self) = shift;
return $self->{'db'} unless(@_);
my $db = shift;
unless(UNIVERSAL::isa($db, 'Rose::DB'))
{
croak "Not a Rose::DB-derived object: $db";
}
if(defined $db)
{
$self->{'db_class'} = $db->class;
$self->{'db_dsn'} = undef;
}
return $self->{'db'} = $db;
}
sub db_dsn
{
my($self) = shift;
return $self->{'db_dsn'} unless(@_);
my $db_dsn = shift;
if(my $db = $self->db)
{
$db->db_dsn($db_dsn);
}
return $self->{'db_dsn'} = $db_dsn;
}
sub dsn { shift->db_dsn(@_) }
sub dbi_dsn { shift->db_dsn(@_) }
sub db_class
{
my($self) = shift;
return $self->{'db_class'} unless(@_);
my $db_class = shift;
unless($db_class =~ /^(?:\w+::)*\w+$/)
{
croak "Illegal class name: $db_class";
}
unless(UNIVERSAL::isa($db_class, 'Rose::DB'))
{
my $error;
TRY:
{
local $@;
eval "require $db_class";
$error = $@;
}
no strict 'refs';
if(!$error && @{"${db_class}::ISA"} && !UNIVERSAL::isa($db_class, 'Rose::DB'))
{
croak "Not a Rose::DB-derived class: $db_class";
}
}
if(my $db = $self->db)
{
$self->db(undef) unless($db->class eq $db_class);
}
return $self->{'db_class'} = $db_class;
}
sub make_modules
{
my($self, %args) = @_;
my $module_dir = exists $args{'module_dir'} ?
delete $args{'module_dir'} : $self->module_dir;
$module_dir = cwd() unless(defined $module_dir);
unless(-d $module_dir)
{
croak "Module directory '$module_dir' does not exist";
}
my(@extra_classes, %extra_info);
$args{'extra_classes'} = \@extra_classes;
$args{'extra_info'} = \%extra_info;
my @classes = $self->make_classes(%args);
foreach my $class (@classes, @extra_classes)
{
my @path = split('::', $class);
$path[-1] .= '.pm';
unshift(@path, $module_dir);
my $dir = File::Spec->catfile(@path[0 .. ($#path - 1)]);
mkpath($dir) unless(-e $dir);
unless(-d $dir)
{
if(-f $dir)
{
croak "Could not create module directory '$module_dir' - a file ",
"with the same name already exists";
}
croak "Could not create module directory '$module_dir' - $!";
}
my $file = File::Spec->catfile(@path);
open(my $pm, '>', $file) or croak "Could not create $file - $!";
my $preamble = exists $args{'module_preamble'} ?
$args{'module_preamble'} : $self->module_preamble;
my $postamble = exists $args{'module_postamble'} ?
$args{'module_postamble'} : $self->module_postamble;
if($class->isa('Rose::DB::Object'))
{
if($preamble)
{
my $this_preamble = ref $preamble eq 'CODE' ?
$preamble->($class->meta) : $preamble;
print {$pm} $this_preamble;
}
if($extra_info{'base_classes'}{$class})
{
print {$pm} _perl_base_class($class, \%extra_info, \%args);
}
else
{
print {$pm} _perl_class($class, \%extra_info, \%args);
}
if($postamble)
{
my $this_postamble = ref $postamble eq 'CODE' ?
$postamble->($class->meta) : $postamble;
print {$pm} $this_postamble;
}
}
elsif($class->isa('Rose::DB::Object::Manager'))
{
if($preamble)
{
my $this_preamble = ref $preamble eq 'CODE' ?
$preamble->($class->object_class->meta, $class) : $preamble;
print {$pm} $this_preamble;
}
print {$pm} $class->perl_class_definition(%args), "\n";
if($postamble)
{
my $this_postamble = ref $postamble eq 'CODE' ?
$postamble->($class->object_class->meta, $class) : $postamble;
print {$pm} $this_postamble;
}
}
elsif($class->isa('Rose::DB'))
{
print {$pm} _perl_db_class($class, \%extra_info, \%args);
}
else { croak "Unknown class: $class" }
close($pm) or croak "Could not write $file - $!";
}
return wantarray ? @classes : \@classes;
}
sub _perl_class
{
my($class, $info, $args) = @_;
my $auto_load = $args->{'auto_load_related_classes'};
my $old_auto_load = $class->meta->auto_load_related_classes;
if(defined $auto_load)
{
$class->meta->auto_load_related_classes($auto_load);
}
my $code = $class->meta->perl_class_definition(%$args);
if(defined $auto_load)
{
$class->meta->auto_load_related_classes($old_auto_load);
}
if(!$info->{'init_db_in_base_class'} && $info->{'perl_init_db'})
{
my $init_db = $info->{'perl_init_db'};
$code =~ s/1;/$init_db\n\n1;/;
}
return $code . "\n";
}
sub _perl_base_class
{
my($class, $info, $args) = @_;
my $init_db = '';
if($info->{'init_db_in_base_class'} && $info->{'perl_init_db'})
{
$init_db = "\n" . $info->{'perl_init_db'} . "\n";
}
return<<"EOF";
package $class;
use base 'Rose::DB::Object';
$init_db
1;
EOF
}
sub _perl_db_class
{
my($class, $info, $args) = @_;
my $max = 0;
foreach my $key (keys %{$info->{'db_entry'}})
{
$max = length($key) if(length($key) > $max);
}
my $hash = perl_hashref(hash => $info->{'db_entry'},
inline => 0,
no_curlies => 1,
key_padding => $max,
indent => $args->{'indent'} || 2);
return<<"EOF";
package $class;
use strict;
use base 'Rose::DB';
__PACKAGE__->use_private_registry;
__PACKAGE__->register_db
(
$hash
);
1;
EOF
}
sub default_pre_init_hook { }
sub default_post_init_hook { }
sub make_classes
{
my($self, %args) = @_;
my $extra_classes = delete $args{'extra_classes'};
my $extra_info = delete $args{'extra_info'};
my $db = delete $args{'db'};
$args{'stay_connected'} = 1;
$args{'passive'} = 1 unless(exists $args{'passive'});
my $require_primary_key = exists $args{'require_primary_key'} ?
delete $args{'require_primary_key'} : $self->require_primary_key;
# Check for parameter alias conflicts
if(exists $args{'warn_on_missing_pk'})
{
if(exists $args{'warn_on_missing_primary_key'} &&
(($args{'warn_on_missing_pk'} ? 1 : 0) != ($args{'warn_on_missing_primary_key'} ? 1 : 0)))
{
croak "The warn_on_missing_primary_key and warn_on_missing_pk parameters ",
"were both passed, and they conflict. Since these two parameters are ",
"aliases for each other, try passing just one.";
}
$args{'warn_on_missing_primary_key'} = delete $args{'warn_on_missing_pk'};
}
my $warn_on_missing_primary_key;
# If not requiring PKs and no explicit decision, either in args or
# in the object, has been made about whether to warn on missing PKs,
# then don't warn (because not requiring PKs is a strong indication
# that their absence is not worth a warning)
if(!$require_primary_key &&
((!exists $args{'warn_on_missing_primary_key'} && !defined $self->warn_on_missing_primary_key) ||
exists $args{'warn_on_missing_primary_key'} && !defined $args{'warn_on_missing_primary_key'}))
{
$warn_on_missing_primary_key = 0;
}
else
{
$warn_on_missing_primary_key = exists $args{'warn_on_missing_primary_key'} ?
delete $args{'warn_on_missing_primary_key'} : $self->warn_on_missing_primary_key;
}
my $include_views = exists $args{'include_views'} ?
delete $args{'include_views'} : $self->include_views;
my $with_managers = exists $args{'with_managers'} ?
delete $args{'with_managers'} : $self->with_managers;
$args{'with_relationships'} = $self->with_relationships
unless(exists $args{'with_relationships'});
$args{'with_foreign_keys'} = $self->with_foreign_keys
unless(exists $args{'with_foreign_keys'});
$args{'with_unique_keys'} = $self->with_unique_keys
unless(exists $args{'with_unique_keys'});
my $no_auto_sequences = exists $args{'no_auto_sequences'} ?
delete $args{'no_auto_sequences'} : $self->no_auto_sequences;
my $pre_init_hook = exists $args{'pre_init_hook'} ?
delete $args{'pre_init_hook'} : $self->pre_init_hook;
my $post_init_hook = exists $args{'post_init_hook'} ?
delete $args{'post_init_hook'} : $self->post_init_hook;
my $include = exists $args{'include_tables'} ?
delete $args{'include_tables'} : $self->include_tables;
my $exclude = exists $args{'exclude_tables'} ?
delete $args{'exclude_tables'} : $self->exclude_tables;
my $filter = exists $args{'filter_tables'} ? delete $args{'filter_tables'} :
(!defined $include && !defined $exclude) ? $self->filter_tables : undef;
if($include || $exclude)
{
if($filter)
{
croak "The filter_tables parameter and/or object attribute cannot ",
"be used with the include_tables or exclude_tables parameters ",
"or object attributes";
}
if(defined $include)
{
if(ref $include eq 'ARRAY')
{
$include = '(?i)\A(?:' . join('|', map { quotemeta } @$include) . ')\z'
}
$include = qr((?i)$include);
}
if(defined $exclude)
{
if(ref $exclude eq 'ARRAY')
{
$exclude = '(?i)\A(?:' . join('|', map { quotemeta } @$exclude) . ')\z'
}
$exclude = qr((?i)$exclude);
}
$filter = sub
{
no warnings 'uninitialized';
return 0 if((defined $include && !/$include/) ||
(defined $exclude && /$exclude/));
return 1;
};
}
my $class_prefix = exists $args{'class_prefix'} ?
delete $args{'class_prefix'} : $self->class_prefix || '';
if(length $class_prefix)
{
unless($class_prefix =~ /^(?:\w+::)*\w+(?:::)?$/)
{
croak "Illegal class prefix: $class_prefix";
}
$class_prefix .= '::' unless($class_prefix =~ /::$/);
}
# Evil masking of object attribute
local $self->{'class_prefix'} = $class_prefix;
# When setting explicit values for attributes that cascade to
# affect other attributes, save off the old values are restore
# them at the end.
my %save;
if(exists $args{'db_class'})
{
my $db_class = delete $args{'db_class'};
if($db && $db_class && $db_class ne $db->class)
{
Carp::croak "The db and db_class parameters conflict: ", $db->class,
" vs. $db_class";
}
if(defined(my $db_class = $self->db_class))
{
$save{'db_class'} = $db_class;
}
if(defined(my $db = $self->db))
{
$save{'db'} = $db;
}
$self->db_class($db_class);
}
#
# Get or create the db object
#
$db ||= $self->db;
my $db_class = $db ? $db->class : undef;
my $made_new_db_class = 0;
unless($db)
{
$db_class = $self->db_class;
if($db_class)
{
unless(UNIVERSAL::isa($db_class, 'Rose::DB'))
{
my $error;
TRY:
{
local $@;
eval "require $db_class";
$error = $@;
}
if($error)
{
# Failed to load existing module
unless($error =~ /^Can't locate $db_class\.pm/)
{
croak "Could not load db class '$db_class' - $error";
}
# Make the class
no strict 'refs';
@{"${db_class}::ISA"} = qw(Rose::DB);
$db_class->registry(clone(Rose::DB->registry));
}
}
}
else
{
$db_class = $self->generate_db_base_class_name;
# Make a class
no strict 'refs';
@{"${db_class}::ISA"} = qw(Rose::DB);
$db_class->registry(clone(Rose::DB->registry));
push(@$extra_classes, $db_class) if($extra_classes);
$made_new_db_class = 1;
}
}
# Create the init_db subroutine that will be used with the objects
my %db_args;
if($db)
{
%db_args =
(
type => $db->type,
domain => $db->domain,
);
delete $db_args{'type'} if($db_args{'type'} eq $db->default_type);
delete $db_args{'domain'} if($db_args{'domain'} eq $db->default_domain);
}
foreach my $attr (qw(db_dsn db_catalog db_schema db_username db_password db_database))
{
(my $db_attr = $attr) =~ s/^db_//;
no strict 'refs';
$db_args{$db_attr} = $self->$attr() if(defined $self->$attr());
}
$db_args{'connect_options'} = $self->db_options if(defined $self->db_options);
# Set up the object base class
my @base_classes = $self->base_classes;
foreach my $class (@base_classes)
{
no strict 'refs';
unless(UNIVERSAL::isa($class, 'Rose::DB::Object') || @{"${class}::ISA"})
{
my $error;
TRY:
{
local $@;
eval "require $class";
$error = $@;
}
croak $error if($error);
}
}
my ($init_db, $need_new_init_db);
# Check if the base class already has its own init_db
my $can_rdbo = Rose::DB::Object->can('init_db');
my $can_base = $base_classes[0]->can('init_db');
unless($can_rdbo && $can_base && $can_rdbo ne $can_base)
{
$need_new_init_db = 1;
}
if($made_new_db_class)
{
if($db_args{'dsn'} && !$db_args{'driver'})
{
if(DBI->can('parse_dsn'))
{
$db_args{'driver'} = lc((DBI->parse_dsn($db_args{'dsn'}))[1]);
}
unless($db_args{'driver'})
{
$db_args{'dsn'} =~ /^dbi:(\w+)/i or
Carp::croak "Could not extract driver name from DSN: $db_args{'dsn'}";
$db_args{'driver'} = lc $1;
}
}
$db_class->register_db(domain => $db_class->default_domain,
type => $db_class->default_type,
%db_args);
my $entry =
$db_class->registry->entry(domain => $db_class->default_domain,
type => $db_class->default_type);
$extra_info->{'db_entry'} = { %db_args };
unless($entry->database)
{
# Need appropriate db just for parsing
my $tmp_db = $db_class->new;
my $database = $tmp_db->database_from_dsn($entry->dsn) or
Carp::croak "Could not extract database name from DSN: ", $entry->dsn;
$entry->database($database);
$extra_info->{'database'} = $database;
}
$init_db = sub { $db_class->new };
$extra_info->{'perl_init_db'} =
"use $db_class;\n\n" .
"sub init_db { $db_class->new }";
}
else
{
$init_db = $need_new_init_db ? sub { $db_class->new(%db_args) } : $can_base;
my $hash = perl_hashref(hash => \%db_args,
inline => 1,
no_curlies => 1,
indent => 0);
if($need_new_init_db)
{
$extra_info->{'perl_init_db'} =
"use $db_class;\n\n" .
"sub init_db { $db_class->new($hash) }";
}
}
# Refresh the db
$db = $init_db->();
$extra_info->{'init_db_in_base_class'} = 0;
# Install the init_db routine in the base class, but only if
# using the default base class.
if($self->using_default_base_class)
{
no strict 'refs';
*{"$base_classes[0]::init_db"} = $init_db;
$extra_info->{'init_db_in_base_class'} = 1;
$extra_info->{'base_classes'}{$base_classes[0]}++;
push(@$extra_classes, $base_classes[0]);
}
else
{
if($made_new_db_class || $db_class ne 'Rose::DB')
{
if($need_new_init_db)
{
no strict 'refs';
no warnings;
*{"$base_classes[0]::init_db"} = $init_db;
}
$extra_info->{'init_db_in_base_class'} = 1;
$extra_info->{'base_classes'}{$base_classes[0]}++;
}
else
{
foreach my $base_class (@base_classes)
{
if($base_class->can('init_db'))
{
$extra_info->{'init_db_in_base_class'} = 1;
last;
}
}
}
}
my $force_lowercase;
if(exists $args{'force_lowercase'})
{
$force_lowercase = delete $args{'force_lowercase'};
}
else
{
unless(defined($force_lowercase = $self->force_lowercase))
{
$force_lowercase = $db->driver eq 'oracle' ? 1 : 0;
}
}
my $cm;
# XXX: Lame way to check if the convention_manager attribute has
# XXX: been set or fetched.
if($self->{'convention_manager'})
{
$cm = $self->convention_manager;
}
else
{
$cm = $base_classes[0]->meta->convention_manager;
$self->convention_manager($cm);
}
die "Missing convention manager" unless($cm);
# Propagate CM-relevant attributes
$cm->force_lowercase($force_lowercase);
$cm->no_auto_sequences($no_auto_sequences);
$self->convention_manager($cm);
my @classes;
my %list_args;
$list_args{'include_views'} = 1 if($include_views);
# XXX: Horrible hack. Replce eventually...
local $Rose::DB::Object::Metadata::Auto::Missing_PK_OK =
$require_primary_key ? 0 : 1;
my %created;
# Iterate over tables, creating RDBO classes for each
foreach my $table ($db->list_tables(%list_args))
{
local $_ = $table;
next unless(!$filter || $filter->($table));
unless($db->has_primary_key($table))
{
if($warn_on_missing_primary_key)
{
# Warn about tables with no primary keys
warn "Warning: table '$table' has no primary key defined.",
($require_primary_key ? " Skipping.\n" : "\n");
}
# Skip table if primary keys are required
next if($require_primary_key);
}
my $obj_class = $class_prefix . $cm->table_to_class($table);
$Debug && warn "Loader loading table: $table - $obj_class\n";
no strict 'refs';
# Skip classes that have already been created
#if($obj_class->isa('Rose::DB::Object') && $obj_class->meta->is_initialized)
if($created{$obj_class})
{
$Debug && warn "Skipping: $obj_class already initialized\n";
next;
}
# Set up the class
@{"${obj_class}::ISA"} = @base_classes;
unless($extra_info->{'init_db_in_base_class'})
{
*{"${obj_class}::init_db"} = $init_db;
}
my $meta = $obj_class->meta;
$meta->db($db);
if($pre_init_hook)
{
if(ref $pre_init_hook eq 'CODE')
{
$pre_init_hook = [ $pre_init_hook ];
}
elsif(ref $pre_init_hook ne 'ARRAY')
{
Carp::croak "Invalid pre_init_hook: $pre_init_hook";
}
}
unshift(@$pre_init_hook, sub { $self->default_pre_init_hook(@_) });
$meta->pre_init_hook($pre_init_hook);
if($post_init_hook)
{
if(ref $post_init_hook eq 'CODE')
{
$post_init_hook = [ $post_init_hook ];
}
elsif(ref $post_init_hook ne 'ARRAY')
{
Carp::croak "Invalid post_init_hook: $post_init_hook";
}
}
unshift(@$post_init_hook, sub { $self->default_post_init_hook(@_) });
$meta->post_init_hook($post_init_hook);
$meta->table($table);
$meta->convention_manager($cm->clone);
$meta->db($db);
my $include_predicated_unique_indexes =
exists $args{'include_predicated_unique_indexes'}
? delete $args{'include_predicated_unique_indexes'}
: $self->include_predicated_unique_indexes;
$meta->include_predicated_unique_indexes($include_predicated_unique_indexes);
$meta->auto_initialize(%args);
push(@classes, $obj_class);
$created{$obj_class}++;
# Make the manager class
if($with_managers)
{
my $mgr_class = $self->generate_manager_class_name($obj_class, $cm);
$meta->make_manager_class(
class => $mgr_class,
base_name => $cm->auto_manager_base_name($table, $obj_class),
isa => scalar $self->manager_base_classes);
push(@classes, $mgr_class);
}
}
if(@classes)
{
my $meta = $classes[0]->meta;
# Retry deferred stuff: two passes
for(1 .. 2)
{
$meta->retry_deferred_tasks;
$meta->retry_deferred_foreign_keys;
$meta->retry_deferred_relationships;
}
$classes[0]->meta_class->clear_all_dbs;
}
if(%save)
{
while(my($method, $value) = each(%save))
{
$self->$method($value);
}
}
return wantarray ? @classes : \@classes;
}
1;
__END__
=head1 NAME
Rose::DB::Object::Loader - Automatically create Rose::DB::Object subclasses based on database table definitions.
=head1 SYNOPSIS
Sample database schema:
CREATE TABLE vendors
(
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
UNIQUE(name)
);
CREATE TABLE products
(
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
vendor_id INT REFERENCES vendors (id),
status VARCHAR(128) NOT NULL DEFAULT 'inactive'
CHECK(status IN ('inactive', 'active', 'defunct')),
date_created TIMESTAMP NOT NULL DEFAULT NOW(),
release_date TIMESTAMP,
UNIQUE(name)
);
CREATE TABLE prices
(
id SERIAL NOT NULL PRIMARY KEY,
product_id INT NOT NULL REFERENCES products (id),
region CHAR(2) NOT NULL DEFAULT 'US',
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
UNIQUE(product_id, region)
);
CREATE TABLE colors
(
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
UNIQUE(name)
);
CREATE TABLE product_color_map
(
product_id INT NOT NULL REFERENCES products (id),
color_id INT NOT NULL REFERENCES colors (id),
PRIMARY KEY(product_id, color_id)
);
To start, make a L<Rose::DB::Object::Loader> object, specifying the database connection information and an optional class name prefix.
$loader =
Rose::DB::Object::Loader->new(
db_dsn => 'dbi:Pg:dbname=mydb;host=localhost',
db_username => 'someuser',
db_password => 'mysecret',
db_options => { AutoCommit => 1, ChopBlanks => 1 },
class_prefix => 'My::Corp');
It's even easier to specify the database information if you've set up L<Rose::DB> (say, by following the instructions in L<Rose::DB::Tutorial>). Just pass a L<Rose::DB>-derived object pointing to the database you're interested in.
$loader =
Rose::DB::Object::Loader->new(
db => My::Corp::DB->new('main'),
class_prefix => 'My::Corp');
Finally, automatically create L<Rose::DB::Object> subclasses for all the tables in the database. All it takes is one method call.
$loader->make_classes;
Here's what you get for your effort.
$p = My::Corp::Product->new(name => 'Sled');
$p->vendor(name => 'Acme');
$p->prices({ price => 1.23, region => 'US' },
{ price => 4.56, region => 'UK' });
$p->colors({ name => 'red' },
{ name => 'green' });
$p->save;
$products =
My::Corp::Product::Manager->get_products_iterator(
query => [ name => { like => '%le%' } ],
with_objects => [ 'prices' ],
require_objects => [ 'vendor' ],
sort_by => 'vendor.name');
$p = $products->next;
print $p->vendor->name; # Acme
# US: 1.23, UK: 4.56
print join(', ', map { $_->region . ': ' . $_->price } $p->prices);
See the L<Rose::DB::Object> and L<Rose::DB::Object::Manager> documentation for learn more about the features these classes provide.
The contents of the database now look like this.
mydb=# select * from products;
id | name | price | vendor_id | status | date_created
----+--------+-------+-----------+----------+-------------------------
1 | Sled 3 | 0.00 | 1 | inactive | 2005-11-19 22:09:20.7988
mydb=# select * from vendors;
id | name
----+--------
1 | Acme 3
mydb=# select * from prices;
id | product_id | region | price
----+------------+--------+-------
1 | 1 | US | 1.23
2 | 1 | UK | 4.56
mydb=# select * from colors;
id | name
----+-------
1 | red
2 | green
mydb=# select * from product_color_map;
product_id | color_id
------------+----------
1 | 1
1 | 2
=head1 DESCRIPTION
L<Rose::DB::Object::Loader> will automatically create L<Rose::DB::Object> subclasses for all the tables in a database. It will configure column data types, default values, primary keys, unique keys, and foreign keys. It can also discover and set up inter-table L<relationships|Rose::DB::Object::Metadata/relationship_type_classes>. It uses L<Rose::DB::Object>'s L<auto-initialization|Rose::DB::Object::Metadata/"AUTO-INITIALIZATION"> capabilities to do all of this.
To do its work, the loader needs to know how to connect to the database. This information can be provided in several ways. The recommended practice is to set up L<Rose::DB> according to the instructions in the L<Rose::DB::Tutorial>, and then pass a L<Rose::DB>-derived object or class name to the loader. The loader will also accept traditional L<DBI>-style connection information: DSN, username, password, etc.
Once the loader object is configured, the L<make_classes|/make_classes> method does all the work. It takes a few options specifying which tables to make classes for, whether or not to make L<manager|Rose::DB::Object::Manager> classes for each table, and a few other L<options|/make_classes>. The L<convention manager|/convention_manager> is used to convert table names to class names, generate foreign key and relationship method names, and so on. The result of this process is a suite of L<Rose::DB::Object> (and L<Rose::DB::Object::Manager>) subclasses ready for use.
L<Rose::DB::Object::Loader> inherits from, and follows the conventions of, L<Rose::Object>. See the L<Rose::Object> documentation for more information.
=head1 GOTCHAS
Database schema information is extracted using L<DBI>'s schema interrogation methods, which dutifully report exactly how the database describes itself. In some cases, what the database reports about a particular table may not exactly match what you specified in your table definition.
The most egregious offender is (surprise!) MySQL, which, to give just one example, tends to offer up empty string default values for non-null character columns. That is, if you write a table definition like this:
CREATE TABLE widgets
(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL
);
and then interrogate it using L<DBI>, you will find that the "name" column has a default value (as reflected in the C<COLUMN_DEF> column returned by L<DBI>'s L<column_info()|DBI/column_info> method) of '' (i.e., an empty string). In other words, it's as if your table definition was this instead:
CREATE TABLE widgets
(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL DEFAULT ''
);
MySQL is full of such surprises, and it's not the only database to do such things. Consult the documentation for your database (or do a Google search for "E<lt>mydbnameE<gt> gotchas") for the gory details.
To work around these kinds of problems, try the L<pre_init_hook|/pre_init_hook> feature. For example, in your L<pre_init_hook|/pre_init_hook> subroutine you could walk over the list of L<columns|Rose::DB::Object::Metadata/columns> for each class, eliminating all the empty string default values (i.e., changing them to undef instead).
=head1 CONSTRUCTOR
=over 4
=item B<new PARAMS>
Returns a new L<Rose::DB::Object::Loader> constructed according to PARAMS, where PARAMS are name/value pairs. Any object method is a valid parameter name.
=back
=head1 OBJECT METHODS
=over 4
=item B<base_class CLASS>
This is an alias for the L<base_classes|/base_classes> method.
=item B<base_classes [ CLASS | ARRAYREF ]>
Get or set the list of base classes to use for the L<Rose::DB::Object> subclasses created by the L<make_classes|/make_classes> method. The argument may be a class name or a reference to an array of class names. At least one of the classes should inherit from L<Rose::DB::Object>.
Returns a list (in list context) or reference to an array (in scalar context) of base class names. Defaults to a dynamically-generated L<Rose::DB::Object> subclass name.
=item B<class_prefix [PREFIX]>
Get or set the prefix affixed to all class names created by the L<make_classes|/make_classes> method. If PREFIX doesn't end in "::", it will be added automatically.
=item B<convention_manager [ CLASS | MANAGER ]>
Get or set the L<Rose::DB::Object::ConventionManager>-derived class name or object to be used during the L<auto-initialization|Rose::DB::Object::Metadata/"AUTO-INITIALIZATION"> process for each class created by the L<make_classes|/make_classes> method. Returns a L<Rose::DB::Object::ConventionManager>-derived object, which defaults to a new L<Rose::DB::Object::ConventionManager> object.
Unless this attribute is explicitly set or fetched before the call to the L<make_classes|/make_classes> method, the convention manager object used by L<make_classes|/make_classes> will be produced by calling the L<convention_manager|Rose::DB::Object::Metadata/convention_manager> method of the metadata object of the first (left-most) L<base class|/base_classes>.
=item B<db [DB]>
Get or set the L<Rose::DB>-derived object used to connect to the database. This object will be used by the L<make_classes|/make_classes> method when extracting information from the database. It will also be used as the prototype for the L<db|Rose::DB::Object/db> object used by each L<Rose::DB::Object> subclass to connect to the database.
Setting this attribute also sets the L<db_class|/db_class> attributes, overwriting any previously existing value, and sets the L<db_dsn|/db_dsn> value to undef.
=item B<db_catalog [CATALOG]>
Get or set the L<catalog|Rose::DB/catalog> for the database connection.
=item B<db_class [CLASS]>
Get or set the name of the L<Rose::DB>-derived class used by the L<make_classes|/make_classes> method to construct a L<db|/db> object if one has not been set via the method of the same name.
Setting this attribute sets the L<db|/db> attribute to undef unless its class is the same as CLASS.
=item B<db_dsn [DSN]>
Get or set the L<DBI>-style Data Source Name (DSN) used to connect to the database. This will be used by the L<make_classes|/make_classes> method when extracting information from the database. The L<Rose::DB>-derived objects used by each L<Rose::DB::Object> subclass to connect to the database will be initialized with this DSN.
Setting this attribute immediately sets the L<dsn|Rose::DB/dsn> of the L<db|/db> attribute, if it is defined.
=item B<db_options [HASHREF]>
Get or set the L<options|Rose::DB/connect_options> used to connect to the database.
=item B<db_password [PASSWORD]>
Get or set the L<password|Rose::DB/password> used to connect to the database.
=item B<db_schema [SCHEMA]>
Get or set the L<schema|Rose::DB/schema> for the database connection.
=item B<db_username [USERNAME]>
Get or set the L<username|Rose::DB/username> used to connect to the database.
=item B<exclude_tables [ REGEX | ARRAYREF ]>
Get or set a regular expression or reference to an array of table names to exclude. Table names that match REGEX or are contained in ARRAYREF will be skipped by default during calls to the L<make_classes|/make_classes> method. Tables without primary keys are automatically (and always) skipped.
Table names are compared to REGEX and the names in ARRAYREF in a case-insensitive manner. To override this in the case of the REGEX, add C<(?-i)> to the front of the REGEX. Otherwise, use the L<filter_tables|/filter_tables> method instead.
=item B<filter_tables CODEREF>
Get or set a reference to a subroutine that takes a single table name argument and returns true if the table should be processed by default during calls to the L<make_classes|/make_classes> method, false if the table should be skipped. The C<$_> variable will also be set to the table name before the call to CODEREF.
This attribute should not be combined with the L<exclude_tables|/exclude_tables> or L<include_tables|/include_tables> attributes.
=item B<force_lowercase [BOOL]>
Get or set a boolean value that indicates whether or not L<metadata|Rose::DB::Object::Metadata> entity names should be forced to lowercase even when the related entity (e.g., table or column name) is uppercase or mixed case. ("Metadata entities" are thing like L<columns|Rose::DB::Object::Metadata/columns>, L<relationships|Rose::DB::Object::Metadata/relationships>, and L<foreign keys|Rose::DB::Object::Metadata/foreign_keys>.) The default value undef.
=item B<generate_manager_class_name CLASS [, CM]>
Given the name of a L<Rose::DB::Object>-derived class, returns a class name for a L<Rose::DB::Object::Manager>-derived class to manage such objects. The default implementation calls the L<auto_manager_class_name|Rose::DB::Object::ConventionManager/auto_manager_class_name> method on the convention manager object passed as the optional CM argument, or returned from the L<convention_manager|/convention_manager> method if a CM argument is not passed.
=item B<include_tables [ REGEX | ARRAYREF ]>
Get or set a regular expression or reference to an array of table names to include. Table names that do not match REGEX or are not contained in ARRAYREF will be skipped by default during calls to the L<make_classes|/make_classes> method. Tables without primary keys are automatically (and always) skipped.
Table names are compared to REGEX and the names in ARRAYREF in a case-insensitive manner. To override this in the case of the REGEX, add C<(?-i)> to the front of the REGEX. Otherwise, use the L<filter_tables|/filter_tables> method instead.
=item B<include_predicated_unique_indexes BOOL>
Get or set a boolean value that will be assigned to the L<include_predicated_unique_indexes|Rose::DB::Object::Metadata/include_predicated_unique_indexes> attribute of the L<Rose::DB::Object::Metadata> object for each class created by the L<make_classes|/make_classes> method. The default value is false.
=item B<include_views BOOL>
If true, database views will also be processed by default during calls to the L<make_classes|/make_classes> method. Defaults to false.
=item B<make_classes [PARAMS]>
Automatically create L<Rose::DB::Object> and (optionally) L<Rose::DB::Object::Manager> subclasses for some or all of the tables in a database. The class creation process is controlled by the loader object's attributes. Optional name/value pairs passed to this method may override some of those values. Valid PARAMS are:
=over 4
=item B<db [DB]>
The L<Rose::DB>-derived object used to connect to the database. This object will also be used as the prototype for the L<db|Rose::DB::Object/db> object used by each L<Rose::DB::Object> subclass created by this call to L<make_classes|/make_classes>. Defaults to the value of the loader object's L<db|/db> attribute.
=item B<db_class [CLASS]>
The name of the L<Rose::DB>-derived class used to construct a L<db|/db> object if one has not been set via the parameter or object attribute of the same name. Defaults to the value of the loader object's L<db_class|/db_class> attribute.
=item B<include_tables [ REGEX | ARRAYREF ]>
Table names that do not match REGEX or are not contained in ARRAYREF will be skipped. Defaults to the value of the loader object's L<include_tables|/include_tables> attribute. Tables without primary keys are automatically (and always) skipped.
Table names are compared to REGEX and the names in ARRAYREF in a case-insensitive manner. To override this in the case of the REGEX, add C<(?-i)> to the front of the REGEX. Otherwise, use the C<filter_tables> parameter instead.
=item B<exclude_tables [ REGEX | ARRAYREF ]>
Table names that match REGEX or are contained in ARRAYREF will be skipped. Defaults to the value of the loader object's L<exclude_tables|/exclude_tables> attribute. Tables without primary keys are automatically (and always) skipped.
Table names are compared to REGEX and the names in ARRAYREF in a case-insensitive manner. To override this in the case of the REGEX, add C<(?-i)> to the front of the REGEX. Otherwise, use the C<filter_tables> parameter instead.
=item B<filter_tables CODEREF>
A reference to a subroutine that takes a single table name argument and returns true if the table should be processed, false if it should be skipped. The C<$_> variable will also be set to the table name before the call. This parameter cannot be combined with the C<exclude_tables> or C<include_tables> options.
Defaults to the value of the loader object's L<filter_tables|/filter_tables> attribute, provided that both the C<exclude_tables> and C<include_tables> values are undefined. Tables without primary keys are automatically skipped.
=item B<force_lowercase BOOL>
A boolean value that indicates whether or not L<metadata|Rose::DB::Object::Metadata> entity names should be forced to lowercase even when the related entity is uppercase or mixed case. ("Metadata entities" are thing like L<columns|Rose::DB::Object::Metadata/columns>, L<relationships|Rose::DB::Object::Metadata/relationships>, and L<foreign keys|Rose::DB::Object::Metadata/foreign_keys>.)
If this parameter is omitted and if the loader object's L<force_lowercase|/force_lowercase> attribute is not defined, then the value is chosen based on the database currently being examined. If the database is Oracle, then it defaults to true. Otherwise, it defaults to false.
The final value is propagated to the L<convention manager|/convention_manager> L<attribute of the same name|Rose::DB::Object::ConventionManager/force_lowercase>.
=item B<include_predicated_unique_indexes BOOL>
This value will be assigned to the L<include_predicated_unique_indexes|Rose::DB::Object::Metadata/include_predicated_unique_indexes> attribute of the L<Rose::DB::Object::Metadata> object for each class created by this method. Defaults to the value of the loader object's L<include_predicated_unique_indexes|/include_predicated_unique_indexes> attribute.
=item B<include_views BOOL>
If true, database views will also be processed. Defaults to the value of the loader object's L<include_views|/include_views> attribute.
=item B<post_init_hook [ CODEREF | ARRAYREF ]>
A reference to a subroutine or a reference to an array of code references that will be called just after each L<Rose::DB::Object>-derived class is L<initialize|Rose::DB::Object::Metadata/initialize>d. Each referenced subroutine will be passed the class's L<metadata|Rose::DB::Object::Metadata> object plus any arguments to the L<initialize|Rose::DB::Object::Metadata/initialize> method. Defaults to the value of the loader object's L<post_init_hook|/post_init_hook> attribute.
=item B<pre_init_hook [ CODEREF | ARRAYREF ]>
A reference to a subroutine or a reference to an array of code references that will be called just before each L<Rose::DB::Object>-derived class is L<initialize|Rose::DB::Object::Metadata/initialize>d. Each referenced subroutine will be passed the class's L<metadata|Rose::DB::Object::Metadata> object plus any arguments to the L<initialize|Rose::DB::Object::Metadata/initialize> method. Defaults to the value of the loader object's L<pre_init_hook|/pre_init_hook> attribute.
=item B<require_primary_key BOOL>
If true, then any table that does not have a primary key will be skipped. Defaults to the value of the loader object's L<require_primary_key|/require_primary_key> attribute. Note that a L<Rose::DB::Object>-derived class based on a table with no primary key will not function correctly in all circumstances. Use this feature at your own risk.
=item B<warn_on_missing_pk BOOL>
This is an alias for the C<warn_on_missing_primary_key> parameter.
=item B<warn_on_missing_primary_key BOOL>
If true, then any table that does not have a primary key will trigger a warning.
If C<require_primary_key> is false and the loader object's L<warn_on_missing_primary_key|/warn_on_missing_primary_key> attribute is undefined, or if the C<warn_on_missing_primary_key> parameter is set to an undefined value or is not passed to the L<make_classes|/make_classes> call at all, then C<warn_on_missing_primary_key> is set to false. Otherwise, it defaults to the value of the loader object's L<warn_on_missing_primary_key|/warn_on_missing_primary_key> attribute. Note that a L<Rose::DB::Object>-derived class based on a table with no primary key will not function correctly in all circumstances.
These complicated defaults are intended to honor the intentions of the C<require_primary_key> attribute/parameter. If not requiring primary keys and no explicit decision has been made about whether to warn about missing primary keys, either in the parameters to the L<make_classes|/make_classes> call or in the loader object itself, then we don't warn about missing primary keys. The idea is that not requiring primary keys is a strong indication that their absence is not worth a warning.
=item B<with_foreign_keys BOOL>
If true, set up foreign key metadata for each L<Rose::DB::Object>-derived. Defaults to the value of the loader object's L<with_foreign_keys|/with_foreign_keys> attribute.
=item B<with_managers BOOL>
If true, create L<Rose::DB::Object::Manager|Rose::DB::Object::Manager>-derived manager classes for each L<Rose::DB::Object> subclass. Defaults to the value of the loader object's L<with_managers|/with_managers> attribute.
The manager class name is determined by passing the L<Rose::DB::Object>-derived class name to the L<generate_manager_class_name|/generate_manager_class_name> method.
The L<Rose::DB::Object> subclass's L<metadata object|Rose::DB::Object::Metadata>'s L<make_manager_class|Rose::DB::Object::Metadata/make_manager_class> method will be used to create the manager class. It will be passed the return value of the convention manager's L<auto_manager_base_name|Rose::DB::Object::ConventionManager/auto_manager_base_name> method as an argument.
=item B<with_relationships [ BOOL | ARRAYREF ]>
A boolean value or a reference to an array of relationship L<type|Rose::DB::Object::Metadata::Relationship/type> names. If set to a simple boolean value, then all types of relationships will be considered when making classes. If set to a list of relationship type names, then only relationships of those types will be considered. Defaults to the value of the loader object's L<with_relationships|/with_relationships> attribute.
=item B<with_unique_keys BOOL>
If true, set up unique key metadata for each L<Rose::DB::Object>-derived. Defaults to the value of the loader object's L<with_unique_keys|/with_unique_keys> attribute.
=back
Any remaining name/value parameters will be passed on to the call to L<auto_initialize|Rose::DB::Object::Metadata/auto_initialize> used to set up each class. For example, to ask the loader not to create any L<relationships|Rose::DB::Object::Metadata/relationships>, pass the C<with_relationships> parameter with a false value.
$loader->make_classes(with_relationships => 0);
This parameter will be passed on to the L<auto_initialize|Rose::DB::Object::Metadata/auto_initialize> method, which, in turn, will pass the parameter on to its own call to the L<auto_init_relationships|Rose::DB::Object::Metadata/auto_init_relationships> method. See the L<Rose::DB::Object::Metadata> documentation for more information on these methods.
Each L<Rose::DB::Object> subclass will be created according to the "best practices" described in the L<Rose::DB::Object::Tutorial>. If a L<base class|/base_classes> is not provided, one (with a dynamically generated name) will be created automatically. The same goes for the L<db|/db> object. If one is not set, then a new (again, dynamically named) subclass of L<Rose::DB>, with its own L<private data source registry|Rose::DB/use_private_registry>, will be created automatically.
This method returns a list (in list context) or a reference to an array (in scalar context) of the names of all the classes that were created. (This list will include L<manager|Rose::DB::Object::Manager> class names as well, if any were created.)
=item B<make_modules [PARAMS]>
Automatically create L<Rose::DB::Object> and (optionally) L<Rose::DB::Object::Manager> subclasses for some or all of the tables in a database, then create Perl module (*.pm) files for each class.
This method calls L<make_classes|/make_classes> to make the actual classes.
B<Note:> If you are trying to regenerate a set of module files that already exist in the target C<module_dir>, please make sure that this C<module_dir> is I<not> in your C<@INC> path. (That is, make sure it is not in the set of paths that perl will search when looking for module files in response to a C<use> or C<require> statement.) More generally, you must make sure that existing versions of the modules you are attempting to generate are not in your C<@INC> path.
(If you do not do this, when L<make_classes|/make_classes> makes a class and looks for a related class, it will find and load the previously generated C<.pm> file, which will then cause L<make_classes|/make_classes> to skip that class later when it sees that it already exists in memory. And if L<make_classes|/make_classes> skips it, L<make_modules|/make_modules> will never see it and therefore will never regenerate the C<.pm> file.)
This method takes all of the same parameters as L<make_classes|/make_classes>, with several additions:
=over 4
=item B<module_dir DIR>
The path to the directory where the Perl module files will be created. For example, given a DIR of "/home/john/lib", the Perl module file for the class C<My::DB::Object> would be located at "/home/john/lib/My/DB/Object.pm".
Defaults to the value of the loader object's L<module_dir|/module_dir> attribute. If the L<module_dir|/module_dir> attribute is also undefined, then the current working directory (as determined by a call to L<cwd()|Cwd/cwd>) is used instead.
=item B<module_preamble [ SCALAR | CODE ]>
If defined as a scalar, inserts the contents of the variable into the auto-generated file before any of the auto-generated class information. If provided as a code ref, calls the indicated function, passing the L<metadata object|Rose::DB::Object::Metadata> as a parameter. (The metadata object that belongs to the C<object_class> and the L<Rose::DB::Object::Manager>-derived class name are passed if the module is a L<Rose::DB::Object::Manager>-derived class.) The returned value of the function is inserted as the preamble text.
Defaults to the value of the loader object's L<module_preamble|/module_preamble> attribute.
=item B<module_postamble [ SCALAR | CODE ]>
If defined as a scalar, inserts the contents of the variable into the auto-generated file after any of the auto-generated class information. If provided as a code ref, calls the indicated function, passing the L<metadata object|Rose::DB::Object::Metadata> as a parameter. (The metadata object that belongs to the C<object_class> and the L<Rose::DB::Object::Manager>-derived class name are passed if the module is a L<Rose::DB::Object::Manager>-derived class.) The returned value of the function is inserted as the postamble text.
Defaults to the value of the loader object's L<module_postamble|/module_postamble> attribute.
=back
=item B<module_dir [DIR]>
Get or set the path to the directory where L<make_modules|/make_modules> will create its Perl modules files. For example, given a DIR of "/home/john/lib", L<make_modules|/make_modules> would create the file "/home/john/lib/My/DB/Object.pm" for the class C<My::DB::Object>.
=item B<module_preamble [ SCALAR | CODE ]>
If defined as a scalar, inserts the contents of the variable into the auto-generated file before any of the auto-generated class information. If provided as a code ref, calls the indicated function, passing the L<metadata object|Rose::DB::Object::Metadata> as a parameter. (The metadata object that belongs to the C<object_class> and the L<Rose::DB::Object::Manager>-derived class name are passed if the module is a L<Rose::DB::Object::Manager>-derived class.) The returned value of the function is inserted as the preamble text.
=item B<module_postamble [ SCALAR | CODE ]>
If defined as a scalar, inserts the contents of the variable into the auto-generated file after any of the auto-generated class information. If provided as a code ref, calls the indicated function, passing the L<metadata object|Rose::DB::Object::Metadata> as a parameter. (The metadata object that belongs to the C<object_class> and the L<Rose::DB::Object::Manager>-derived class name are passed if the module is a L<Rose::DB::Object::Manager>-derived class.) The returned value of the function is inserted as the postamble text.
=item B<pre_init_hook [CODE]>
Get or set a reference to a subroutine to be called just before each L<Rose::DB::Object>-derived class is L<initialize|Rose::DB::Object::Metadata/initialize>ed within the L<make_classes|/make_classes> method. The subroutine will be passed the class's L<metdata|Rose::DB::Object::Metadata> object as an argument.
=item B<require_primary_key BOOL>
Get or set a boolean value that determines whether or not the L<make_classes|/make_classes> method will skip any table that does not have a primary key will be skipped. Defaults to true.
Note that a L<Rose::DB::Object>-derived class based on a table with no primary key will not function correctly in all circumstances. Use this feature at your own risk.
=item B<warn_on_missing_pk BOOL>
This is an alias for the L<warn_on_missing_primary_key|/warn_on_missing_primary_key> method.
=item B<warn_on_missing_primary_key BOOL>
Get or set a boolean value that determines whether or not the L<make_classes|/make_classes> method will emit a warning when it encounters a table that does not have a primary key. Defaults to undefined.
=item B<with_foreign_keys BOOL>
If true, the L<make_classes|/make_classes> method will set up foreign key metadata for each L<Rose::DB::Object>-derived class it creates. Defaults to true.
=item B<with_managers [BOOL]>
If true, the L<make_classes|/make_classes> method will create L<Rose::DB::Object::Manager|Rose::DB::Object::Manager>-derived manager classes for each L<Rose::DB::Object> subclass by default. Defaults to true.
The manager class name is determined by passing the L<Rose::DB::Object>-derived class name to the L<generate_manager_class_name|/generate_manager_class_name> method.
The L<Rose::DB::Object> subclass's L<metadata object|Rose::DB::Object::Metadata>'s L<make_manager_class|Rose::DB::Object::Metadata/make_manager_class> method will be used to create the manager class. It will be passed the return value of the convention manager's L<auto_manager_base_name|Rose::DB::Object::ConventionManager/auto_manager_base_name> method as an argument.
=item B<with_relationships [ BOOL | ARRAYREF ]>
A boolean value or a reference to an array of relationship L<type|Rose::DB::Object::Metadata::Relationship/type> names. If set to a simple boolean value, then the L<make_classes|/make_classes> method will consider all types of relationships when making classes. If set to a list of relationship type names, then only relationships of those types will be considered by L<make_classes|/make_classes>. Defaults to true.
=item B<with_unique_keys BOOL>
If true, the L<make_classes|/make_classes> method will set up unique key metadata for each L<Rose::DB::Object>-derived class it creates. Defaults to true.
=item B<manager_base_class CLASS>
This is an alias for the L<manager_base_classes|/manager_base_classes> method.
=item B<manager_base_classes [ CLASS | ARRAYREF ]>
Get or set the list of base classes to use for the L<Rose::DB::Object::Manager> subclasses created by the L<make_classes|/make_classes> method. The argument may be a class name or a reference to an array of class names. At least one of the classes should inherit from L<Rose::DB::Object::Manager>.
Returns a list (in list context) or reference to an array (in scalar context) of base class names. Defaults to L<Rose::DB::Object::Manager>.
=back
=head1 AUTHOR
John C. Siracusa (siracusa@gmail.com)
=head1 LICENSE
Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
free software; you can redistribute it and/or modify it under the same terms
as Perl itself.
|