File Coverage

blib/lib/Rose/DB/Object/ConventionManager.pm
Criterion Covered Total %
statement 181 329 55.0
branch 68 180 37.7
condition 18 95 18.9
subroutine 33 50 66.0
pod 27 38 71.0
total 327 692 47.2


line stmt bran cond sub pod time code
1             package Rose::DB::Object::ConventionManager;
2              
3 61     61   492 use strict;
  61         143  
  61         2195  
4              
5 61     61   337 use Carp();
  61         131  
  61         827  
6 61     61   350 use Scalar::Util();
  61         129  
  61         1155  
7              
8 61     61   34290 use Rose::DB::Object::Metadata::ForeignKey;
  61         227  
  61         2601  
9              
10 61     61   549 use Rose::DB::Object::Metadata::Object;
  61         147  
  61         5180  
11             our @ISA = qw(Rose::DB::Object::Metadata::Object);
12              
13             our $VERSION = '0.795';
14              
15             our $Debug = 0;
16              
17             use Rose::Object::MakeMethods::Generic
18             (
19 61         553 'scalar --get_set_init' =>
20             [
21             'singular_to_plural_function',
22             'plural_to_singular_function',
23             ],
24              
25             boolean =>
26             [
27             tables_are_singular => { default => 0 },
28             force_lowercase => { default => 0 },
29             no_auto_sequences => { default => 0 },
30             ],
31 61     61   506 );
  61         152  
32              
33             *meta = \&Rose::DB::Object::Metadata::Object::parent;
34              
35             sub class_to_table_singular
36             {
37 109     109 1 241 my($self, $class) = @_;
38              
39 109   33     534 $class ||= $self->meta->class;
40              
41 109         324 my $table = $self->class_suffix($class);
42 109         882 $table =~ s/([a-z]\d*|^\d+)([A-Z])/$1_$2/g;
43 109         473 return lc $table;
44             }
45              
46             sub class_suffix
47             {
48 109     109 0 260 my($self, $class) = @_;
49              
50 109         774 $class =~ /(\w+)$/;
51 109         426 return $1;
52             }
53              
54             sub class_to_table_plural
55             {
56 92     92 1 265 my($self) = shift;
57 92         285 $self->singular_to_plural($self->class_to_table_singular(@_));
58             }
59              
60             sub table_to_class_plural
61             {
62 12     12 0 31 my($self, $table, $prefix) = @_;
63 12         27 return $self->table_to_class($table, $prefix, 1);
64             }
65              
66             sub table_to_class
67             {
68 67     67 1 212 my($self, $table, $prefix, $plural) = @_;
69 67 50       194 $table = lc $table if ($self->force_lowercase);
70 67 100       568 $table = $self->plural_to_singular($table) unless($plural);
71 67         450 $table =~ s/_(.)/\U$1/g;
72 67         176 $table =~ s/[^\w:]/_/g;
73 67   100     377 return ($prefix || '') . ucfirst $table;
74             }
75              
76             sub auto_manager_base_name
77             {
78 7     7 1 19 my($self, $table, $object_class) = @_;
79              
80 7   66     32 $table ||= $self->class_to_table_plural;
81              
82 7 50       24 $table = lc $table if($self->force_lowercase);
83              
84 7 50       63 return $self->tables_are_singular ? $self->singular_to_plural($table) : $table;
85             }
86              
87 1     1 1 942 sub auto_manager_base_class { 'Rose::DB::Object::Manager' }
88              
89             sub auto_manager_class_name
90             {
91 2     2 1 11 my($self, $object_class) = @_;
92              
93 2   66     12 $object_class ||= $self->meta->class;
94              
95 2         14 return "${object_class}::Manager";
96             }
97              
98             sub auto_manager_method_name
99             {
100 1     1 1 616 my($self, $type, $base_name, $object_class) = @_;
101 1         6 return undef; # rely on hard-coded defaults in Manager
102             }
103              
104             sub class_prefix
105             {
106 51     51 1 108 my($self, $class) = @_;
107 51         297 $class =~ /^((?:\w+::)*)/;
108 51   100     283 return $1 || '';
109             }
110              
111             sub related_table_to_class
112             {
113 39     39 1 294 my($self, $table, $local_class, $plural) = @_;
114 39         135 return $self->table_to_class($table, $self->class_prefix($local_class), $plural);
115             }
116              
117             sub table_singular
118             {
119 0     0 1 0 my($self) = shift;
120              
121 0         0 my $table = $self->meta->table;
122              
123 0 0       0 if($self->tables_are_singular)
124             {
125 0         0 return $table;
126             }
127              
128 0         0 return $self->plural_to_singular($table);
129             }
130              
131             sub table_plural
132             {
133 0     0 1 0 my($self) = shift;
134              
135 0         0 my $table = $self->meta->table;
136              
137 0 0       0 if($self->tables_are_singular)
138             {
139 0         0 return $self->singular_to_plural($table);
140             }
141              
142 0         0 return $table;
143             }
144              
145             sub auto_table_name
146             {
147 89     89 1 183 my($self) = shift;
148              
149 89 100       337 if($self->tables_are_singular)
150             {
151 1         10 return $self->class_to_table_singular;
152             }
153             else
154             {
155 88         721 return $self->class_to_table_plural;
156             }
157             }
158              
159             sub auto_primary_key_column_names
160             {
161 77     77 1 151 my($self) = shift;
162              
163 77         208 my $meta = $self->meta;
164              
165             # 1. Column named "id"
166 77 100       229 return [ 'id' ] if($meta->column('id'));
167              
168             # 2. Column named <table singular>_id
169 12         54 my $column = $self->class_to_table_singular . '_id';
170 12 100       39 return [ $column ] if($meta->column($column));
171              
172             # 3. The first serial column in the column list, alphabetically
173 11         37 foreach my $column (sort { lc $a->name cmp lc $b->name } $meta->columns)
  16         188  
174             {
175 12 100       34 return [ $column->name ] if($column->type =~ /^(?:big)?serial$/);
176             }
177              
178             # 4. The first column
179 0 0       0 if(my $column = $meta->first_column)
180             {
181 0         0 return [ $column->name ];
182             }
183              
184 0         0 return;
185             }
186              
187             sub auto_column_method_name
188             {
189 184     184 1 444 my($self, $type, $column, $name, $object_class) = @_;
190 184 50       513 return lc $name if ($self->force_lowercase);
191 184         1620 return undef; # rely on hard-coded defaults in Metadata
192             }
193              
194       110 1   sub init_singular_to_plural_function { }
195       62 1   sub init_plural_to_singular_function { }
196              
197             sub singular_to_plural
198             {
199 110     110 1 269 my($self, $word) = @_;
200              
201 110 50       388 if(my $code = $self->singular_to_plural_function)
202             {
203 0         0 return $code->($word);
204             }
205              
206 110 100       512 if($word =~ /(?:x|[se]s)$/i)
207             {
208 9         62 return $word . 'es';
209             }
210             else
211             {
212 101         307 $word =~ s/y$/ies/i;
213             }
214              
215 101 100       694 return $word =~ /s$/i ? $word : ($word . 's');
216             }
217              
218             sub plural_to_singular
219             {
220 62     62 1 1322 my($self, $word) = @_;
221              
222 62 50       214 if(my $code = $self->plural_to_singular_function)
223             {
224 0         0 return $code->($word);
225             }
226              
227 62         206 $word =~ s/ies$/y/i;
228              
229 62 100       216 return $word if($word =~ s/ses$/s/i);
230 61 100       264 return $word if($word =~ /[aeiouy]ss$/i);
231              
232 60         173 $word =~ s/s$//i;
233              
234 60         161 return $word;
235             }
236              
237             sub method_name_conflicts
238             {
239 0     0 0 0 my($self, $name) = @_;
240              
241 0 0       0 return 1 if(Rose::DB::Object->can($name));
242              
243 0         0 my $meta = $self->meta;
244              
245 0         0 foreach my $column ($meta->columns)
246             {
247 0         0 foreach my $type ($column->auto_method_types)
248             {
249 0   0     0 my $method = $column->method_name($type) ||
250             $meta->method_name_from_column_name($column->name, $type) ||
251             next;
252              
253 0 0       0 return 1 if($name eq $method);
254             }
255             }
256              
257 0         0 foreach my $foreign_key ($meta->foreign_keys)
258             {
259 0         0 foreach my $type ($foreign_key->auto_method_types)
260             {
261 0   0     0 my $method =
262             $foreign_key->method_name($type) ||
263             $foreign_key->build_method_name_for_type($type) || next;
264              
265 0 0       0 return 1 if($name eq $method);
266             }
267             }
268              
269 0         0 foreach my $relationship ($meta->relationships)
270             {
271 0         0 foreach my $type ($relationship->auto_method_types)
272             {
273 0   0     0 my $method =
274             $relationship->method_name($type) ||
275             $relationship->build_method_name_for_type($type) || next;
276              
277 0 0       0 return 1 if($name eq $method);
278             }
279             }
280              
281 0         0 return 0;
282             }
283              
284             sub auto_column_sequence_name
285             {
286 0     0 0 0 my($self, $table, $column, $db) = @_;
287 0         0 my $name = join('_', $table, $column, 'seq');
288 0 0 0     0 return uc $name if($db && $db->likes_uppercase_sequence_names);
289 0 0 0     0 return lc $name if($db && $db->likes_lowercase_sequence_names);
290 0         0 return $name;
291             }
292              
293 0     0 0 0 sub auto_primary_key_column_sequence_name { shift->auto_column_sequence_name(@_) }
294              
295             sub auto_foreign_key_name
296             {
297 0     0 1 0 my($self, $f_class, $current_name, $key_columns, $used_names) = @_;
298              
299 0 0       0 if($self->force_lowercase)
300             {
301 0         0 $current_name = lc $current_name;
302 0         0 $key_columns = { map { lc } %$key_columns };
  0         0  
303             }
304              
305 0 0       0 my $f_meta = $f_class->meta or return $current_name;
306 0   0     0 my $name = $self->plural_to_singular($f_meta->table) || $current_name;
307              
308 0 0       0 if(keys %$key_columns == 1)
309             {
310 0         0 my($local_column, $foreign_column) = %$key_columns;
311              
312             # Try to lop off foreign column name. Example:
313             # my_foreign_object_id -> my_foreign_object
314 0 0       0 if($local_column =~ s/_$foreign_column$//i)
315             {
316 0         0 $name = $local_column;
317             }
318             else
319             {
320 0   0     0 $name = $self->plural_to_singular($f_meta->table) || $current_name;
321             }
322             }
323              
324             # Avoid method name conflicts
325 0 0 0     0 if($self->method_name_conflicts($name) || $used_names->{$name})
326             {
327 0         0 foreach my $s ('_obj', '_object')
328             {
329             # Try the name with a suffix appended
330 0 0 0     0 unless($self->method_name_conflicts($name . $s) ||
331             $used_names->{$name . $s})
332             {
333 0         0 return $name . $s;
334             }
335             }
336              
337 0         0 my $i = 1;
338              
339             # Give up and go with numbers...
340             $i++ while($self->method_name_conflicts($name . $i) ||
341 0   0     0 $used_names->{$name . $i});
342              
343 0         0 return $name . $i;
344             }
345              
346 0         0 return $name;
347             }
348              
349             sub auto_table_to_relationship_name_plural
350             {
351 0     0 0 0 my($self, $table) = @_;
352 0 0       0 $table = lc $table if ($self->force_lowercase);
353 0 0       0 return $self->tables_are_singular ? $self->singular_to_plural($table) : $table;
354             }
355              
356             sub auto_class_to_relationship_name_plural
357             {
358 0     0 0 0 my($self, $class) = @_;
359 0         0 return $self->class_to_table_plural($class);
360             }
361              
362             sub auto_foreign_key_to_relationship_name_plural
363             {
364 0     0 0 0 my($self, $fk) = @_;
365 0 0       0 my $name = $self->force_lowercase ? lc $fk->name : $fk->name;
366 0         0 return $self->singular_to_plural($name);
367             }
368              
369             sub auto_relationship_name_one_to_many
370             {
371 0     0 1 0 my($self, $table, $class) = @_;
372             #return $self->auto_class_to_relationship_name_plural($class);
373 0         0 my $name = $self->auto_table_to_relationship_name_plural($table);
374              
375             # Avoid method name conflicts
376 0 0       0 if($self->method_name_conflicts($name))
377             {
378 0         0 foreach my $s ('_objs', '_objects')
379             {
380             # Try the name with a suffix appended
381 0 0       0 unless($self->method_name_conflicts($name . $s))
382             {
383 0         0 return $name . $s;
384             }
385             }
386              
387 0         0 my $i = 1;
388              
389             # Give up and go with numbers...
390 0         0 $i++ while($self->method_name_conflicts($name . $i));
391              
392 0         0 return $name . $i;
393             }
394              
395 0         0 return $name;
396             }
397              
398             sub auto_relationship_name_many_to_many
399             {
400 0     0 1 0 my($self, $fk, $map_class) = @_;
401 0         0 my $name = $self->auto_foreign_key_to_relationship_name_plural($fk);
402              
403             # Avoid method name conflicts
404 0 0       0 if($self->method_name_conflicts($name))
405             {
406 0         0 foreach my $s ('_objs', '_objects')
407             {
408             # Try the name with a suffix appended
409 0 0       0 unless($self->method_name_conflicts($name . $s))
410             {
411 0         0 return $name . $s;
412             }
413             }
414              
415 0         0 my $i = 1;
416              
417             # Give up and go with numbers...
418 0         0 $i++ while($self->method_name_conflicts($name . $i));
419              
420 0         0 return $name . $i;
421             }
422              
423 0         0 return $name;
424             }
425              
426             sub auto_relationship_name_one_to_one
427             {
428 0     0 1 0 my($self, $table, $class) = @_;
429              
430 0 0       0 $table = lc $table if ($self->force_lowercase);
431              
432 0         0 my $name = $self->plural_to_singular($table);
433              
434             # Avoid method name conflicts
435 0 0       0 if($self->method_name_conflicts($name))
436             {
437 0         0 foreach my $s ('_obj', '_object')
438             {
439             # Try the name with a suffix appended
440 0 0       0 unless($self->method_name_conflicts($name . $s))
441             {
442 0         0 return $name . $s;
443             }
444             }
445              
446 0         0 my $i = 1;
447              
448             # Give up and go with numbers...
449 0         0 $i++ while($self->method_name_conflicts($name . $i));
450              
451 0         0 return $name . $i;
452             }
453              
454 0         0 return $name;
455             }
456              
457             sub is_map_class
458             {
459 0     0 1 0 my($self, $class) = @_;
460              
461 0 0       0 return 0 unless(UNIVERSAL::isa($class, 'Rose::DB::Object'));
462              
463 0         0 my $is_map_table = $self->looks_like_map_table($class->meta->table);
464 0         0 my $is_map_class = $self->looks_like_map_class($class);
465              
466 0 0 0     0 return 1 if($is_map_table && (!defined $is_map_class || $is_map_class));
      0        
467 0         0 return 0;
468             }
469              
470             sub looks_like_map_class
471             {
472 0     0 1 0 my($self, $class) = @_;
473              
474 0 0       0 unless(UNIVERSAL::isa($class, 'Rose::DB::Object'))
475             {
476 0         0 return undef;
477             }
478              
479 0         0 my $meta = $class->meta;
480 0         0 my @fks = $meta->foreign_keys;
481              
482 0 0       0 return 1 if(@fks == 2);
483 0 0 0     0 return 0 if(($meta->is_initialized || $meta->initialized_foreign_keys) &&
      0        
484             !$meta->has_deferred_foreign_keys);
485 0         0 return undef;
486             }
487              
488             sub looks_like_map_table
489             {
490 0     0 1 0 my($self, $table) = @_;
491              
492 0 0       0 if($table =~ m{^(?:
493             (?:\w+_){2,}map # foo_bar_map
494             | (?:\w+_)*\w+_(?:\w+_)*\w+s # foo_bars
495             | (?:\w+_)*\w+s_(?:\w+_)*\w+s # foos_bars
496             )$}xi)
497             {
498 0         0 return 1;
499             }
500              
501 0         0 return 0;
502             }
503              
504             sub auto_foreign_key
505             {
506 32     32 1 80 my($self, $name, $spec) = @_;
507              
508 32   100     122 $spec ||= {};
509              
510 32         84 my $meta = $self->meta;
511              
512 32 100       81 unless($spec->{'class'})
513             {
514 27         63 my $class = $meta->class;
515              
516 27         82 my $fk_class = $self->related_table_to_class($name, $class);
517              
518             LOAD:
519             {
520             # Try to load class
521 61     61   214033 no strict 'refs';
  61         185  
  61         36597  
  27         51  
522 27 50       101 unless(UNIVERSAL::isa($fk_class, 'Rose::DB::Object'))
523             {
524 0         0 local $@;
525 0         0 eval "require $fk_class";
526 0 0 0     0 return if($@ || !UNIVERSAL::isa($fk_class, 'Rose::DB::Object'));
527             }
528             }
529              
530             #return unless(UNIVERSAL::isa($fk_class, 'Rose::DB::Object'));
531              
532 27         67 $spec->{'class'} = $fk_class;
533             }
534              
535 32 100       99 unless(defined $spec->{'key_columns'})
536             {
537             my @fpk_columns = UNIVERSAL::isa($spec->{'class'}, 'Rose::DB::Object') ?
538 27 50       148 $spec->{'class'}->meta->primary_key_column_names : ();
539              
540             # Defer population of key columns until the foreign class is initialized
541 27 50       77 unless(@fpk_columns == 1)
542             {
543             # If the foreign class has more than one primary key column, give up
544 0 0       0 return if(@fpk_columns);
545              
546             # If the foreign class is initialized and the foreign key spec still
547             # has no key columns, then give up.
548 0 0 0     0 if(UNIVERSAL::isa($spec->{'class'}, 'Rose::DB::Object') &&
549             $spec->{'class'}->meta->is_initialized)
550             {
551 0         0 return;
552             }
553              
554 0         0 my %spec = %$spec;
555              
556             $meta->add_deferred_task(
557             {
558             class => $meta->class,
559             method => "foreign_key:$name",
560              
561             code => sub
562             {
563             # Generate new foreign key, then grab the key columns from it
564 0 0   0   0 my $new_fk = $self->auto_foreign_key($name, \%spec) or return;
565 0         0 my $fk = $meta->foreign_key($name);
566 0 0       0 my $key_cols = $new_fk->key_columns or return;
567              
568 0         0 $fk->key_columns($key_cols);
569             },
570              
571             check => sub
572             {
573 0 0   0   0 my $fk = $meta->foreign_key($name) or return 0;
574              
575             # If the foreign class is initialized and the foreign key still
576             # has no key columns, then we should give up.
577 0 0 0     0 if(UNIVERSAL::isa($fk->class, 'Rose::DB::Object') &&
578             $fk->class->meta->is_initialized)
579             {
580 0         0 Carp::croak "Missing key columns for foreign key named ",
581             $fk->name, " in class ", $meta->class;
582             }
583              
584 0 0       0 my $cols = $fk->key_columns or return 0;
585              
586             # Everything is okay if we have key columns
587 0 0 0     0 return (ref($cols) && keys(%$cols) > 0) ? 1 : 0;
588             }
589 0         0 });
590              
591 0         0 return Rose::DB::Object::Metadata::ForeignKey->new(name => $name, %$spec);
592             }
593              
594 27         85 my $aliases = $meta->column_aliases;
595              
596 27 50 33     78 if($meta->column($name) && $aliases->{$name} && $aliases->{$name} ne $name)
    50 0        
597             {
598 0         0 $spec->{'key_columns'} = { $name => $fpk_columns[0] };
599             }
600             elsif($meta->column("${name}_$fpk_columns[0]"))
601             {
602 27         92 $spec->{'key_columns'} = { "${name}_$fpk_columns[0]" => $fpk_columns[0] };
603             }
604 0         0 else { return }
605             }
606              
607 32         165 return Rose::DB::Object::Metadata::ForeignKey->new(name => $name, %$spec);
608             }
609              
610             sub auto_relationship
611             {
612 33     33 1 114 my($self, $name, $rel_class, $spec) = @_;
613              
614 33   50     91 $spec ||= {};
615              
616 33         106 my $meta = $self->meta;
617 33         149 my $rel_type = $rel_class->type;
618              
619 33 100       101 unless($spec->{'class'})
620             {
621 30 100       181 if($rel_type eq 'one to many')
    100          
622             {
623 3         10 my $class = $meta->class;
624              
625             # Get class suffix from relationship name
626 3         14 my $table = $self->plural_to_singular($name);
627 3         13 my $f_class = $self->related_table_to_class($table, $class);
628              
629             LOAD:
630             {
631             # Try to load class
632 61     61   574 no strict 'refs';
  61         212  
  61         10591  
  3         8  
633 3 50       20 unless(UNIVERSAL::isa($f_class, 'Rose::DB::Object'))
634             {
635 0         0 local $@;
636 0         0 eval "require $f_class";
637 0 0 0     0 return if($@ || !UNIVERSAL::isa($f_class, 'Rose::DB::Object'));
638             }
639             }
640              
641             #return unless(UNIVERSAL::isa($f_class, 'Rose::DB::Object'));
642              
643 3         10 $spec->{'class'} = $f_class;
644             }
645             elsif($rel_type =~ /^(?:one|many) to one$/)
646             {
647 6         20 my $class = $meta->class;
648              
649             # Get class suffix from relationship name
650 6         30 my $f_class = $self->related_table_to_class($name, $class);
651              
652             LOAD:
653             {
654             # Try to load class
655 61     61   521 no strict 'refs';
  61         185  
  61         54166  
  6         13  
656 6 50       28 unless(UNIVERSAL::isa($f_class, 'Rose::DB::Object'))
657             {
658 0         0 local $@;
659 0         0 eval "require $f_class";
660 0 0 0     0 return if($@ || !UNIVERSAL::isa($f_class, 'Rose::DB::Object'));
661             }
662             }
663              
664             #return unless(UNIVERSAL::isa($f_class, 'Rose::DB::Object'));
665              
666 6         17 $spec->{'class'} = $f_class;
667             }
668             }
669              
670             # Make sure this class has its @ISA set up...
671 33 100       159 unless(UNIVERSAL::isa($spec->{'class'}, 'Rose::DB::Object'))
672             {
673             # ...but allow many-to-many relationships to pass because they tend to
674             # need more time before every piece of info is available.
675 21 50       63 return unless($rel_type eq 'many to many');
676             }
677              
678 33 100       185 if($rel_type eq 'one to one')
    100          
    100          
    50          
679             {
680 4         19 return $self->auto_relationship_one_to_one($name, $rel_class, $spec);
681             }
682             elsif($rel_type eq 'many to one')
683             {
684 4         18 return $self->auto_relationship_many_to_one($name, $rel_class, $spec);
685             }
686             elsif($rel_type eq 'one to many')
687             {
688 4         19 return $self->auto_relationship_one_to_many($name, $rel_class, $spec);
689             }
690             elsif($rel_type eq 'many to many')
691             {
692 21         80 return $self->auto_relationship_many_to_many($name, $rel_class, $spec);
693             }
694              
695 0         0 return;
696             }
697              
698             sub auto_relationship_one_to_one
699             {
700 8     8 0 26 my($self, $name, $rel_class, $spec) = @_;
701              
702 8   50     20 $spec ||= {};
703              
704 8         21 my $meta = $self->meta;
705              
706 8 100       35 unless(defined $spec->{'column_map'})
707             {
708 6         25 my @fpk_columns = $spec->{'class'}->meta->primary_key_column_names;
709 6 50       17 return unless(@fpk_columns == 1);
710              
711 6         48 my $aliases = $meta->column_aliases;
712              
713 6 50 33     24 if($meta->column($name) && $aliases->{$name} && $aliases->{$name} ne $name)
    50 0        
    0          
714             {
715 0         0 $spec->{'column_map'} = { $name => $fpk_columns[0] };
716             }
717             elsif($meta->column("${name}_$fpk_columns[0]"))
718             {
719 6         30 $spec->{'column_map'} = { "${name}_$fpk_columns[0]" => $fpk_columns[0] };
720             }
721             elsif($meta->column("${name}_id"))
722             {
723 0         0 $spec->{'column_map'} = { "${name}_id" => $fpk_columns[0] };
724             }
725 0         0 else { return }
726             }
727              
728 8         62 return $rel_class->new(name => $name, %$spec);
729             }
730              
731             *auto_relationship_many_to_one = \&auto_relationship_one_to_one;
732              
733             sub auto_relationship_one_to_many
734             {
735 4     4 0 17 my($self, $name, $rel_class, $spec) = @_;
736              
737 4   50     13 $spec ||= {};
738              
739 4         12 my $meta = $self->meta;
740 4         17 my $l_col_name = $self->class_to_table_singular;
741              
742 4 100       16 unless(defined $spec->{'column_map'})
743             {
744 3         10 my @pk_columns = $meta->primary_key_column_names;
745 3 50       12 return unless(@pk_columns == 1);
746              
747 3         10 my @fpk_columns = $meta->primary_key_column_names;
748 3 50       11 return unless(@fpk_columns == 1);
749              
750 3         14 my $f_meta = $spec->{'class'}->meta;
751              
752 3         15 my $aliases = $f_meta->column_aliases;
753              
754 3 50       10 if($f_meta->column($l_col_name))
    50          
755             {
756 0         0 $spec->{'column_map'} = { $pk_columns[0] => $l_col_name };
757             }
758             elsif($f_meta->column("${l_col_name}_$pk_columns[0]"))
759             {
760 3         9 $spec->{'column_map'} = { $pk_columns[0] => "${l_col_name}_$pk_columns[0]" };
761             }
762 0         0 else { return }
763             }
764              
765 4         36 return $rel_class->new(name => $name, %$spec);
766             }
767              
768             sub auto_relationship_many_to_many
769             {
770 21     21 0 54 my($self, $name, $rel_class, $spec) = @_;
771              
772 21   50     54 $spec ||= {};
773              
774 21         65 my $meta = $self->meta;
775              
776 21 100       70 unless($spec->{'map_class'})
777             {
778 12         38 my $class = $meta->class;
779              
780             # Given:
781             # Class: My::Object
782             # Rel name: other_objects
783             # Foreign class: My::OtherObject
784             #
785             # Consider map class names:
786             # My::ObjectsOtherObjectsMap
787             # My::ObjectOtherObjectMap
788             # My::OtherObjectsObjectsMap
789             # My::OtherObjectObjectMap
790             # My::ObjectsOtherObjects
791             # My::ObjectOtherObjects
792             # My::OtherObjectsObjects
793             # My::OtherObjectObjects
794             # My::OtherObjectMap
795             # My::OtherObjectsMap
796             # My::ObjectMap
797             # My::ObjectsMap
798              
799 12         36 my $prefix = $self->class_prefix($class);
800              
801 12         27 my @consider;
802              
803 12         38 my $f_class_suffix = $self->table_to_class($name);
804 12         53 my $f_class_suffix_pl = $self->table_to_class_plural($name);
805              
806 12         55 $class =~ /(\w+)$/;
807 12         26 my $class_suffix = $1;
808 12         31 my $class_suffix_pl = $self->singular_to_plural($class_suffix);
809              
810 12         100 push(@consider, map { "${prefix}$_" }
  144         258  
811             $class_suffix_pl . $f_class_suffix_pl . 'Map',
812             $class_suffix . $f_class_suffix . 'Map',
813              
814             $f_class_suffix_pl . $class_suffix_pl . 'Map',
815             $f_class_suffix . $class_suffix . 'Map',
816              
817             $class_suffix_pl . $f_class_suffix_pl,
818             $class_suffix . $f_class_suffix_pl,
819              
820             $f_class_suffix_pl . $class_suffix_pl,
821             $f_class_suffix . $class_suffix_pl,
822              
823             $f_class_suffix . 'Map',
824             $f_class_suffix_pl . 'Map',
825              
826             $class_suffix . 'Map',
827             $class_suffix_pl . 'Map');
828              
829 12         25 my $map_class;
830              
831 12         30 CLASS: foreach my $class (@consider)
832             {
833             LOAD:
834             {
835             # Try to load class
836 61     61   574 no strict 'refs';
  61         194  
  61         12376  
  78         116  
837 78 100       345 if(UNIVERSAL::isa($class, 'Rose::DB::Object'))
838             {
839 12         20 $map_class = $class;
840 12         29 last CLASS;
841             }
842             else
843             {
844 66         96 local $@;
845 66         3368 eval "require $class";
846              
847 66 50       399 unless($@)
848             {
849 0         0 $map_class = $class;
850 0 0       0 last CLASS if(UNIVERSAL::isa($class, 'Rose::DB::Object'));
851             }
852             }
853             }
854             }
855              
856 12 50 33     77 return unless($map_class && UNIVERSAL::isa($map_class, 'Rose::DB::Object'));
857              
858 12         47 $spec->{'map_class'} = $map_class;
859             }
860              
861 21         125 return $rel_class->new(name => $name, %$spec);
862             }
863              
864             1;
865              
866             __END__
867              
868             =head1 NAME
869              
870             Rose::DB::Object::ConventionManager - Provide missing metadata by convention.
871              
872             =head1 SYNOPSIS
873              
874             package My::Product;
875              
876             use base 'Rose::DB::Object';
877              
878             __PACKAGE__->meta->setup(columns => [ ... ]);
879              
880             # No table is set above, but look at this: the
881             # convention manager provided one for us.
882             print __PACKAGE__->meta->table; # "products"
883              
884             ##
885             ## See the EXAMPLE section below for a more complete demonstration.
886             ##
887              
888             =head1 DESCRIPTION
889              
890             Each L<Rose::DB::Object>-derived object has a L<convention manager|Rose::DB::Object::Metadata/convention_manager> that it uses to fill in missing L<metadata|Rose::DB::Object/meta>. The convention manager encapsulates a set of rules (conventions) for generating various pieces of metadata in the absence of explicitly specified values: table names, column names, etc.
891              
892             Each L<Rose::DB::Object>-derived class's convention manager object is stored in the L<convention_manager|Rose::DB::Object::Metadata/convention_manager> attribute of its L<Rose::DB::Object::Metadata> (L<meta|Rose::DB::Object/meta>) object. L<Rose::DB::Object::ConventionManager> is the default convention manager class.
893              
894             The object method documentation below describes both the purpose of each convention manager method and the particular rules that L<Rose::DB::Object::ConventionManager> follows to fulfill that purpose. Subclasses must honor the purpose of each method, but are free to use any rules they choose.
895              
896             B<Note well:> When reading the descriptions of the rules used by each convention manager method below, remember that only values that are I<missing> will be set by the convention manager. Explicitly providing a value for a piece of metadata obviates the need for the convention manager to generate one.
897              
898             If insufficient information is available, or if the convention manager simply declines to fulfill a request, undef may be returned from any metadata-generating method.
899              
900             In the documentation, the adjectives "local" and "foreign" are used to distinguish between the things that belong to the convention manager's L<class|/class> and the class on "the other side" of the inter-table relationship, respectively.
901              
902             =head1 SUMMARY OF DEFAULT CONVENTIONS
903              
904             Although the object method documentation below includes all the information required to understand the default conventions, it's also quite spread out. What follows is a summary of the default conventions. Some details have necessarily been omitted or simplified for the sake of brevity, but this summary should give you a good starting point for further exploration.
905              
906             Here's a brief summary of the default conventions as implemented in L<Rose::DB::Object::ConventionManager>.
907              
908             =over 4
909              
910             =item B<Table, column, foreign key, and relationship names are lowercase, with underscores separating words.>
911              
912             Examples: C<products>, C<street_address>, C<date_created>, C<vendor_id>.
913              
914             =item B<Table names are plural.>
915              
916             Examples: C<products>, C<vendors>, C<codes>, C<customer_details>, C<employee_addresses>.
917              
918             (This convention can be overridden via the L<tables_are_singular|/tables_are_singular> method.)
919              
920             =item B<Class names are singular, title-cased, with nothing separating words.>
921              
922             Examples: C<Product>, C<Vendor>, C<Code>, C<CustomerDetail>, C<EmployeeAddress>.
923              
924             =item B<Primary key column names do not contain the table name.>
925              
926             For example, the primary key column name in the C<products> table might be C<id> or C<sku>, but should B<not> be C<product_id> or C<product_sku>.
927              
928             =item B<Foreign key column names are made from the singular version of the foreign table's name joined (with an underscore) to the foreign table's key column name.>
929              
930             Examples: C<product_sku>, C<vendor_id>, C<employee_address_id>.
931              
932             =item B<One-to-one and many-to-one relationship names are singular.>
933              
934             Examples: C<product>, C<vendor>, C<code>. These relationships may point to zero or one foreign object. The default method names generated from such relationships are based on the relationship names, so singular names make the most sense.
935              
936             =item B<One-to-many and many-to-many relationship names are plural.>
937              
938             Examples: C<colors>, C<prices>, C<customer_details>. These relationships may point to more than one foreign object. The default method names generated from such relationships are based on the relationship names, so plural names make the most sense.
939              
940             =item B<Mapping tables and their associated classes that participate in many-to-many relationships are named according a formula that combines the names of the two classes/tables that are being linked.>
941              
942             See the L<auto_relationship|/auto_relationship>, L<looks_like_map_class|/looks_like_map_class>, and L<looks_like_map_table|/looks_like_map_table> documentation for all the details.
943              
944             =back
945              
946             =head1 CONSTRUCTOR
947              
948             =over 4
949              
950             =item B<new PARAMS>
951              
952             Constructs a new object based on PARAMS, where PARAMS are
953             name/value pairs. Any object attribute is a valid parameter name.
954              
955             =back
956              
957             =head1 OBJECT METHODS
958              
959             =over 4
960              
961             =item B<auto_column_method_name TYPE, COLUMN, NAME, OBJECT_CLASS>
962              
963             Given a L<Rose::DB::Object::Metadata::Column> column L<type|Rose::DB::Object::Metadata::Column/type>, a L<Rose::DB::Object::Metadata::Column> object or column name, a default method name, and a L<Rose::DB::Object>-derived class name, return an appropriate method name. The default implementation simply returns undef, relying on the hard-coded default method-type-to-name mapping implemented in L<Rose::DB::Object::Metadata>'s L<method_name_from_column|Rose::DB::Object::Metadata/method_name_from_column> method.
964              
965             =item B<auto_foreign_key NAME [, SPEC]>
966              
967             Given a L<foreign key|Rose::DB::Object::Metadata/foreign_key> name and an optional reference to a hash SPEC of the type passed to L<Rose::DB::Object::Metadata>'s L<add_foreign_keys|Rose::DB::Object::Metadata/add_foreign_keys> method, return an appropriately constructed L<Rose::DB::Object::Metadata::ForeignKey> object.
968              
969             The foreign key's L<class name|Rose::DB::Object::Metadata::ForeignKey/class> is generated by calling L<related_table_to_class|/related_table_to_class>, passing NAME and the convention manager's L<class|/class> as arguments. An attempt is made is load the class. If this fails, the foreign key's L<class name|Rose::DB::Object::Metadata::ForeignKey/class> is not set.
970              
971             The foreign key's L<key_columns|Rose::DB::Object::Metadata::ForeignKey/key_columns> are only set if both the "local" and "foreign" tables have single-column primary keys. The foreign class's primary key column name is used as the foreign column in the L<key_columns|Rose::DB::Object::Metadata::ForeignKey/key_columns> map. If there is a local column with the same name as the foreign key name, and if that column is aliased (making way for the foreign key method to use that name), then that is used as the local column. If not, then the local column name is generated by joining the foreign key name and the foreign class's primary key column name with an underscore. If no column by that name exists, then the search is abandoned. Example:
972              
973             Given these pieces:
974              
975             Name Description Value
976             --------- -------------------------------- -------
977             NAME Foreign key name vendor
978             FCLASS Foreign class My::Vendor
979             FPK Foreign primary key column name id
980              
981             Consider column maps in this order:
982              
983             Value Formula
984             --------------------- ----------------------
985             { vendor => 'id' } { NAME => FPK }
986             { vendor_id => 'id' } { <NAME>_<FPK> => FPK }
987              
988             =item B<auto_foreign_key_name FOREIGN_CLASS, CURRENT_NAME, KEY_COLUMNS, USED_NAMES>
989              
990             Given the name of a foreign class, the current foreign key name (if any), a reference to a hash of L<key columns|Rose::DB::Object::Metadata::ForeignKey/key_columns>, and a reference to a hash whose keys are foreign key names already used in this class, return a L<name|Rose::DB::Object::Metadata::ForeignKey/name> for the foreign key.
991              
992             If there is more than one pair of columns in KEY_COLUMNS, then the name is generated by calling L<plural_to_singular|/plural_to_singular>, passing the L<table|Rose::DB::Object::Metadata/table> name of the foreign class. The CURRENT_NAME is used if the call to L<plural_to_singular|/plural_to_singular> does not return a true value.
993              
994             If there is just one pair of columns in KEY_COLUMNS, and if the name of the local column ends with an underscore and the name of the referenced column, then that part of the column name is removed and the remaining string is used as the foreign key name. For example, given the following tables:
995              
996             CREATE TABLE categories
997             (
998             id SERIAL PRIMARY KEY,
999             ...
1000             );
1001              
1002             CREATE TABLE products
1003             (
1004             category_id INT REFERENCES categories (id),
1005             ...
1006             );
1007              
1008             The foreign key name would be "category", which is the name of the referring column ("category_id") with an underscore and the name of the referenced column ("_id") removed from the end of it.
1009              
1010             If the foreign key has only one column, but it does not meet the criteria described above, then the name is generated by calling L<plural_to_singular|/plural_to_singular>, passing the L<table|Rose::DB::Object::Metadata/table> name of the foreign class. The CURRENT_NAME is used if the call to L<plural_to_singular|/plural_to_singular> does not return a true value.
1011              
1012             If the name selected using the above techniques is in the USED_NAMES hash, or is the same as that of an existing or potential method in the target class, then the suffixes "_obj" and "_object" are tried in that order. If neither of those suffixes resolves the situation, then ascending numeric suffixes starting with "1" are tried until a unique name is found.
1013              
1014             =item B<auto_manager_base_name TABLE, CLASS>
1015              
1016             Given a table name and the name of the L<Rose::DB::Object>-derived class that fronts it, return a base name suitable for use as the value of the C<base_name> parameter to L<Rose::DB::Object::Manager>'s L<make_manager_methods|Rose::DB::Object::Manager/make_manager_methods> method.
1017              
1018             If no table is specified then the table name is derived from the current class
1019             name by calling L<class_to_table_plural|/class_to_table_plural>.
1020              
1021             If L<tables_are_singular|/tables_are_singular> is true, then TABLE is passed to the L<singular_to_plural|/singular_to_plural> method and the result is returned. Otherwise, TABLE is returned as-is.
1022              
1023             =item B<auto_manager_base_class>
1024              
1025             Return the class that all manager classes will default to inheriting from. By
1026             default this will be L<Rose::DB::Object::Manager>.
1027              
1028             =item B<auto_manager_class_name CLASS>
1029              
1030             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 simply appends "::Manager" to the L<Rose::DB::Object>-derived class name.
1031              
1032             =item B<auto_manager_method_name TYPE, BASE_NAME, OBJECT_CLASS>
1033              
1034             Given the specified L<Rose::DB::Object::Manager> L<method type|Rose::DB::Object::Manager/make_manager_methods>,
1035             L<base name|Rose::DB::Object::Manager/make_manager_methods>, and L<object class|Rose::DB::Object::Manager/object_class> return an appropriate L<manager|Rose::DB::Object::Manager> method name. The default implementation simply returns undef, relying on the hard-coded default method-type-to-name mapping implemented in L<Rose::DB::Object::Manager>'s L<make_manager_methods|Rose::DB::Object::Manager/make_manager_methods> method.
1036              
1037             =item B<auto_relationship_name_many_to_many FK, MAPCLASS>
1038              
1039             Return the name of a "many to many" relationship that fetches objects from the table pointed to by the L<Rose::DB::Object::Metadata::ForeignKey> object FK by going through the class MAPCLASS.
1040              
1041             The default implementation passes the name of the table pointed to by FK through the L<singular_to_plural|/singular_to_plural> method in order to build the name.
1042              
1043             If the selected name is the name of an existing or potential method in the target class, then the suffixes "_objs" and "_objects" are tried in that order. If neither of those suffixes resolves the situation, then ascending numeric suffixes starting with "1" are tried until a unique name is found.
1044              
1045             =item B<auto_relationship_name_one_to_many TABLE, CLASS>
1046              
1047             Return the name of a "one to many" relationship that fetches objects from the specified TABLE and CLASS.
1048              
1049             If L<tables_are_singular|/tables_are_singular> is true, then TABLE is passed to the L<singular_to_plural|/singular_to_plural> method and the result is used as the name. Otherwise, TABLE is used as-is.
1050              
1051             If the selected name is the name of an existing or potential method in the target class, then the suffixes "_objs" and "_objects" are tried in that order. If neither of those suffixes resolves the situation, then ascending numeric suffixes starting with "1" are tried until a unique name is found.
1052              
1053             =item B<auto_relationship_name_one_to_one TABLE, CLASS>
1054              
1055             Return the name of a "one to one" relationship that fetches an object from the specified TABLE and CLASS. The default implementation returns a singular version of the table name.
1056              
1057             If the selected name is the name of an existing or potential method in the target class, then the suffixes "obj_" and "_object" are tried in that order. If neither of those suffixes resolves the situation, then ascending numeric suffixes starting with "1" are tried until a unique name is found.
1058              
1059             =item B<auto_primary_key_column_names>
1060              
1061             Returns a reference to an array of primary key column names.
1062              
1063             If a column named "id" exists, it is selected as the sole primary key column name. If not, the column name generated by joining the return value of L<class_to_table_singular|/class_to_table_singular> with "_id" is considered. If no column with that name exists, then the first column (sorted alphabetically) whose L<type|Rose::DB::Object::Metadata::Column/type> is "serial" is selected. If all of the above fails, then the L<first column|Rose::DB::Object::Metadata/first_column> is selected as the primary key column (assuming one exists).
1064              
1065             Examples:
1066              
1067             My::A->meta->columns(qw(a a_id id));
1068             print My::A->meta->primary_key_columns; # "id"
1069              
1070             My::B->meta->columns(qw(b b_id foo));
1071             print My::B->meta->primary_key_columns; # "a_id"
1072              
1073             My::D->meta->columns
1074             (
1075             cnt => { type => 'int' },
1076             dub => { type => 'serial' },
1077             foo => { type => 'serial'},
1078             a_id => { type => 'int' }
1079             )
1080              
1081             print My::D->meta->primary_key_columns; # "dub"
1082              
1083             My::C->meta->columns(qw(foo bar baz));
1084             print My::C->meta->primary_key_columns; # "foo"
1085              
1086             =item B<auto_relationship NAME, RELATIONSHIP_CLASS [, SPEC]>
1087              
1088             Given a L<relationship|Rose::DB::Object::Metadata/relationship> name, a L<Rose::DB::Object::Metadata::Relationship>-derived class name, and an optional reference to a hash SPEC of the type passed to L<Rose::DB::Object::Metadata>'s L<add_relationships|Rose::DB::Object::Metadata/add_relationships> method, return an appropriately constructed L<Rose::DB::Object::Metadata::Relationship>-derived object.
1089              
1090             If the relationship's L<type|Rose::DB::Object::Metadata::Relationship/type> is "one to one" or "many to one", then the relationship's L<class name|Rose::DB::Object::Metadata::Relationship/class> is generated by calling L<related_table_to_class|/related_table_to_class>, passing NAME and the convention manager's L<class|/class> as arguments. An attempt is made is load the class. If this fails, the relationship's L<class name|Rose::DB::Object::Metadata::Relationship/class> is not set.
1091              
1092             The L<column map|Rose::DB::Object::Metadata::Relationship::OneToOne/column_map> for "one to one" and "many to one" relationships is generated using the same rules used to generate L<key_columns|Rose::DB::Object::Metadata::ForeignKey/key_columns> in the L<auto_foreign_key|/auto_foreign_key> method.
1093              
1094             If the relationship's L<type|Rose::DB::Object::Metadata::Relationship/type> is "one to many" then the relationship's L<class name|Rose::DB::Object::Metadata::Relationship/class> is generated by calling L<plural_to_singular|/plural_to_singular> on NAME, then passing that value along with the convention manager's L<class|/class> to the L<related_table_to_class|/related_table_to_class> method. An attempt is made is load the class. If this fails, the relationship's L<class name|Rose::DB::Object::Metadata::Relationship/class> is not set.
1095              
1096             The L<column map|Rose::DB::Object::Metadata::Relationship::OneToMany/column_map> for a "one to many" relationship is only set if both the "local" and "foreign" tables have single-column primary keys. The following ordered list of combinations is considered.
1097              
1098             Given:
1099              
1100             Local class: My::Product
1101             Foreign class: My::Price
1102             Relationship: prices
1103              
1104             Generate these pieces:
1105              
1106             Name Description Value
1107             --------- --------------------------------- -------
1108             LTABLE_S Local class_to_table_singular() product
1109             LPK Local primary key column name id
1110             FPK Foreign primary key column name id
1111              
1112             Consider column maps in this order:
1113              
1114             Value Formula
1115             ---------------------- --------------------------
1116             { id => 'product' } { LPK => LTABLE_S }
1117             { id => 'product_id' } { LPK => <LTABLE_S>_<PK> }
1118              
1119             The first value whose foreign column actually exists in the foreign table is chosen.
1120              
1121             If the relationship's L<type|Rose::DB::Object::Metadata::Relationship/type> is "many to many" then the relationship's L<map_class|Rose::DB::Object::Metadata::Relationship/map_class> is chosen from a list of possibilities. This list is generated by constructing singular and plural versions of the local and foreign class names (sans prefixes) and then joining them in various ways, all re-prefixed by the L<class prefix|/class_prefix> of the convention manager's L<class|/class>. Example:
1122              
1123             Given:
1124              
1125             Local class: My::Product
1126             Foreign class: My::Color
1127             Relationship: colors
1128              
1129             Generate these pieces:
1130              
1131             Name Description Value
1132             --------- --------------------------------- -------
1133             PREFIX Local class prefix My::
1134             LCLASS_S Unprefixed local class, singular Product
1135             LCLASS_P Unprefixed local class, plural Products
1136             FCLASS_S Unprefixed foreign class, singular Color
1137             FCLASS_P Unprefixed foreign class, plural Colors
1138              
1139             Consider map class names in this order:
1140              
1141             Value Formula
1142             --------------- ---------------------
1143             My::ProductsColorsMap <PREFIX><LCLASS_P><FCLASS_P>Map
1144             My::ProductColorMap <PREFIX><LCLASS_S><FCLASS_S>Map
1145             My::ColorsProductsMap <PREFIX><FCLASS_P><LCLASS_P>Map
1146             My::ColorProductMap <PREFIX><FCLASS_S><LCLASS_S>Map
1147             My::ProductsColors <PREFIX><LCLASS_P><FCLASS_P>
1148             My::ProductColors <PREFIX><LCLASS_S><FCLASS_P>
1149             My::ColorsProducts <PREFIX><FCLASS_P><LCLASS_P>
1150             My::ColorProducts <PREFIX><FCLASS_S><LCLASS_P>
1151             My::ColorMap <PREFIX><FCLASS_S>Map
1152             My::ColorsMap <PREFIX><FCLASS_P>Map
1153             My::ProductMap <PREFIX><LCLASS_S>Map
1154             My::ProductsMap <PREFIX><LCLASS_P>Map
1155              
1156             The first class found that inherits from L<Rose::DB::Object> and is loaded successfully will be chosen as the relationship's L<map_class|Rose::DB::Object::Metadata::Relationship/map_class>.
1157              
1158             =item B<auto_table_name>
1159              
1160             Returns a table name for the convention manager's L<class|/class>.
1161              
1162             Class names are singular and table names are plural. To build the table name, the L<class prefix|/class_prefix> is removed from the L<class name|/class>, transitions from lowercase letters or digits to uppercase letters have underscores inserted, and the whole thing is converted to lowercase.
1163              
1164             Examples:
1165              
1166             Class Table
1167             ----------- --------
1168             Product products
1169             My::Product products
1170             My::BigBox big_boxes
1171             My5HatPig my5_hat_pig
1172              
1173             =item B<class [CLASS]>
1174              
1175             Get or set the L<Rose::DB::Object>-derived class that this convention manager belongs to.
1176              
1177             =item B<class_prefix CLASS>
1178              
1179             Given a class name, return the prefix, if any, before the last component of the namespace, including the final "::". If there is no prefix, an empty string is returned.
1180              
1181             Examples:
1182              
1183             Class Prefix
1184             ----------- --------------
1185             Product <empty string>
1186             My::Product My::
1187             A::B::C::D A::B::C::
1188              
1189             =item B<class_to_table_plural [CLASS]>
1190              
1191             Given a class name, or the convention manager's L<class|/class> if omitted, return a plural version of the corresponding table name.
1192              
1193             To do this, the output of the L<class_to_table_singular|/class_to_table_singular> method is passed to a call to the L<singular_to_plural|/singular_to_plural> method. (The CLASS argument, if any, is passed to the call to L<class_to_table_singular|/class_to_table_singular>.)
1194              
1195             Examples:
1196              
1197             Class Table
1198             ----------- --------
1199             Product products
1200             My::Product products
1201             My::Box boxes
1202              
1203             =item B<class_to_table_singular [CLASS]>
1204              
1205             Given a class name, or the convention manager's L<class|/class> if omitted, return a singular version of the corresponding table name.
1206              
1207             Examples:
1208              
1209             Class Table
1210             ----------- --------
1211             Product product
1212             My::Product product
1213             My::Box box
1214              
1215             =item B<force_lowercase [BOOL]>
1216              
1217             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 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 is false.
1218              
1219             =item B<is_map_class CLASS>
1220              
1221             Returns true if CLASS is a L<map class|Rose::DB::Object::Metadata::Relationship::ManyToMany/map_class> used as part of a L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany> relationship, false if it does not.
1222              
1223             The default implementations returns true if CLASS is derived from L<Rose::DB::Object> and its L<table|Rose::DB::Object::Metadata/table> name looks like a map table name according to the L<looks_like_map_table|/looks_like_map_table> method and the L<looks_like_map_class|/looks_like_map_class> method returns either true or undef.
1224              
1225             Override this method to control which classes are considered map classes. Note that it may be called several times on the same class at various stages of that class's construction.
1226              
1227             =item B<looks_like_map_class CLASS>
1228              
1229             Given the class name CLASS, returns true if it looks like the name of a L<map class|Rose::DB::Object::Metadata::Relationship::ManyToMany/map_class> used as part of a L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany> relationship, false (but defined) if it does not, and undef if it's unsure.
1230              
1231             The default implementation returns true if CLASS is derived from L<Rose::DB::Object> and has exactly two foreign keys. It returns false (but defined) if CLASS is derived from L<Rose::DB::Object> and has been L<initialized|Rose::DB::Object/initialize> (or if the foreign keys have been L<auto-initialized|Rose::DB::Object/auto_init_foreign_keys>) and the CLASS has no deferred foreign keys. It returns undef otherwise.
1232              
1233             =item B<looks_like_map_table TABLE>
1234              
1235             Returns true if TABLE looks like the name of a mapping table used as part of a L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany> relationship, false (but defined) if it does not, and undef if it's unsure.
1236              
1237             The default implementation returns true if TABLE is in one of these forms:
1238              
1239             Regex Examples
1240             ----------------------- -----------------------------
1241             (\w+_){2,}map pig_toe_map, pig_skin_toe_map
1242             (\w+_)*\w+_(\w+_)*\w+s pig_toes, pig_skin_toe_jams
1243             (\w+_)*\w+s_(\w+_)*\w+s pigs_toes, pig_skins_toe_jams
1244              
1245             It returns false otherwise.
1246              
1247             =item B<meta [META]>
1248              
1249             Get or set the L<Rose::DB::Object::Metadata> object associated with the class that this convention manager belongs to.
1250              
1251             =item B<plural_to_singular STRING>
1252              
1253             Returns the singular version of STRING. If a L<plural_to_singular_function|/plural_to_singular_function> is defined, then this method simply passes STRING to that function.
1254              
1255             Otherwise, the following rules are applied, case-insensitively.
1256              
1257             * If STRING ends in "ies", then the "ies" is replaced with "y".
1258              
1259             * If STRING ends in "ses" then the "ses" is replaced with "s".
1260              
1261             * If STRING matches C</[aeiouy]ss$/i>, it is returned unmodified.
1262              
1263             For all other cases, the letter "s" is removed from the end of STRING and the result is returned.
1264              
1265             =item B<plural_to_singular_function [CODEREF]>
1266              
1267             Get or set a reference to the function used to convert strings to singular. The function should take a single string as an argument and return a singular version of the string. This function is undefined by default.
1268              
1269             =item B<related_table_to_class TABLE, LOCAL_CLASS>
1270              
1271             Given a table name and a local class name, return the name of the related class that fronts the table.
1272              
1273             To do this, L<table_to_class|/table_to_class> is called with TABLE and the L<class_prefix|/class_prefix> of LOCAL_CLASS passed as arguments.
1274              
1275             Examples:
1276              
1277             Table Local Class Related Class
1278             ----------- ------------ ----------------
1279             prices My::Product My::Price
1280             big_hats A::B::FooBar A::B::BigHat
1281             a1_steaks Meat A1Steak
1282              
1283             =item B<singular_to_plural STRING>
1284              
1285             Returns the plural version of STRING. If a L<singular_to_plural_function|/singular_to_plural_function> is defined, then this method simply passes STRING to that function. Otherwise, the following rules are applied, case-insensitively, to form the plural.
1286              
1287             * If STRING ends in "x", "ss", or "es", then "es" is appended.
1288              
1289             * If STRING ends in "y" then the "y" is replaced with "ies".
1290              
1291             * If STRING ends in "s" then it is returned as-is.
1292              
1293             * Otherwise, "s" is appended.
1294              
1295             =item B<singular_to_plural_function [CODEREF]>
1296              
1297             Get or set a reference to the function used to convert strings to plural. The function should take a single string as an argument and return a plural version of the string. This function is undefined by default.
1298              
1299             =item B<table_singular>
1300              
1301             Let TABLE be the return value of the L<table|Rose::DB::Object::Metadata/table> method called on the L<meta|/meta> attribute of this object.
1302              
1303             If L<tables_are_singular|/tables_are_singular> is true, then TABLE is returned as-is. Otherwise, TABLE is passed to the L<plural_to_singular|/plural_to_singular> method and the result is returned. Otherwise, TABLE is returned as-is.
1304              
1305             =item B<table_plural>
1306              
1307             Let TABLE be the return value of the L<table|Rose::DB::Object::Metadata/table> method called on the L<meta|/meta> attribute of this object.
1308              
1309             If L<tables_are_singular|/tables_are_singular> is true, then TABLE is passed to the L<singular_to_plural|/singular_to_plural> method and the result is returned. Otherwise, TABLE is returned as-is.
1310              
1311             =item B<table_to_class TABLE [, PREFIX]>
1312              
1313             Given a table name and an optional class prefix, return the corresponding class name. The prefix will be appended to the class name, if present. The prefix should end in "::".
1314              
1315             To do this, any letter that follows an underscore ("_") in the table name is replaced with an uppercase version of itself, and the underscore is removed.
1316              
1317             Examples:
1318              
1319             Table Prefix Class
1320             ----------- ------ -----------
1321             products My:: My::Product
1322             products <none> Product
1323             big_hats My:: My::BigHat
1324             my5_hat_pig <none> My5HatPig
1325              
1326             =item B<tables_are_singular [BOOL]>
1327              
1328             Get or set a boolean value that indicates whether or not table names are expected to be singular. The default value is false, meaning that table names are expected to be plural.
1329              
1330             =back
1331              
1332             =head1 PROTECTED API
1333              
1334             These methods are not part of the public interface, but are supported for use by subclasses. Put another way, given an unknown object that "isa" L<Rose::DB::Object::Metadata::ConventionManager>, there should be no expectation that the following methods exist. But subclasses, which know the exact class from which they inherit, are free to use these methods in order to implement the public API described above.
1335              
1336             =over 4
1337              
1338             =item B<init_plural_to_singular_function>
1339              
1340             Override this method and return a reference to a function that takes a single string as an argument and returns a singular version of that string.
1341              
1342             =item B<init_singular_to_plural_function>
1343              
1344             Override this method and return a reference to a function that takes a single string as an argument and returns a plural version of that string.
1345              
1346             =back
1347              
1348             =head1 TIPS AND TRICKS
1349              
1350             Much of the richness of a convention manager relies upon the quality of the L<singular_to_plural|/singular_to_plural> and L<plural_to_singular|/plural_to_singular> methods. The default implementations are primitive at best. For example, L<singular_to_plural|/singular_to_plural> will not correctly form the plural of the word "alumnus".
1351              
1352             One easy way to improve this is by setting a custom L<singular_to_plural_function|/singular_to_plural_function>. Here's an example using the handy L<Lingua::EN::Inflect> module:
1353              
1354             package My::Product;
1355             ...
1356             use Lingua::EN::Inflect;
1357             $cm = __PACKAGE__->meta->convention_manager;
1358              
1359             $cm->singular_to_plural_function(\&Lingua::EN::Inflect::PL);
1360              
1361             print $cm->singular_to_plural('person'); # "people"
1362              
1363             But that's a bit of a pain to do in every single class. An easier way to do it for all of your classes is to make a new L<Rose::DB::Object::Metadata> subclass that overrides the L<init_convention_manager|Rose::DB::Object::Metadata/init_convention_manager> method, then make a L<Rose::DB::Object>-derived base class that uses your new metadata class. Example:
1364              
1365             package My::DB::Metadata;
1366              
1367             use Rose::DB::Object::Metadata;
1368             our @ISA = qw(Rose::DB::Object::Metadata);
1369              
1370             use Lingua::EN::Inflect;
1371              
1372             sub init_convention_manager
1373             {
1374             my $self = shift;
1375              
1376             # Let the base class make ths convention manager object
1377             my $cm = $self->SUPER::init_convention_manager(@_);
1378              
1379             # Set the new singular-to-plural function
1380             $cm->singular_to_plural_function(\&Lingua::EN::Inflect::PL);
1381              
1382             # Return the modified convention manager
1383             return $cm;
1384             }
1385              
1386             ...
1387              
1388             package My::DB::Object;
1389              
1390             use My::DB::Metadata;
1391              
1392             use Rose::DB::Object;
1393             our @ISA = qw(Rose::DB::Object);
1394              
1395             sub meta_class { 'My::DB::Metadata' }
1396              
1397             ...
1398              
1399             package My::Person;
1400              
1401             use My::DB::Object;
1402             our @ISA = qw(My::DB::Object);
1403              
1404             # The big pay-off: smart plurals!
1405             print __PACKAGE__->meta->table; # "people"
1406              
1407             You might wonder why I don't use L<Lingua::EN::Inflect> in L<Rose::DB::Object::ConventionManager> to save you this effort. The answer is that the L<Lingua::EN::Inflect> module adds almost a megabyte of memory overhead on my system. I'd rather not incur that overhead just for the sake of being more clever about naming conventions. Furthermore, as primitive as the default plural-forming is, at least it's deterministic. Guessing what L<Lingua::EN::Inflect> will return is not always easy, and the results can change depending on which version L<Lingua::EN::Inflect> you have installed.
1408              
1409             =head1 EXAMPLE
1410              
1411             Here's a complete example of nearly all of the major features of L<Rose::DB::Object::ConventionManager>. Let's start with the database schema. (This example uses PostgreSQL, but any L<supported database|Rose::DB/"DATABASE SUPPORT"> with native foreign key support will work.)
1412              
1413             CREATE TABLE vendors
1414             (
1415             id SERIAL NOT NULL PRIMARY KEY,
1416             name VARCHAR(255)
1417             );
1418              
1419             CREATE TABLE colors
1420             (
1421             code CHAR(3) NOT NULL PRIMARY KEY,
1422             name VARCHAR(255)
1423             );
1424              
1425             CREATE TABLE products
1426             (
1427             id SERIAL NOT NULL PRIMARY KEY,
1428             name VARCHAR(255),
1429             vendor_id INT NOT NULL REFERENCES vendors (id)
1430             );
1431              
1432             CREATE TABLE prices
1433             (
1434             price_id SERIAL NOT NULL PRIMARY KEY,
1435             product_id INT NOT NULL REFERENCES products (id),
1436             region CHAR(2) NOT NULL DEFAULT 'US',
1437             price DECIMAL(10,2) NOT NULL
1438             );
1439              
1440             CREATE TABLE product_colors
1441             (
1442             id SERIAL NOT NULL PRIMARY KEY,
1443             product_id INT NOT NULL REFERENCES products (id),
1444             color_code CHAR(3) NOT NULL REFERENCES colors (code)
1445             );
1446              
1447             Now the classes:
1448              
1449             # Rose::DB subclass to handle the db connection
1450             package My::DB;
1451              
1452             use base 'Rose::DB';
1453              
1454             My::DB->register_db
1455             (
1456             type => 'default',
1457             domain => 'default',
1458             driver => 'Pg',
1459             database => 'test',
1460             username => 'postgres',
1461             );
1462              
1463             ...
1464              
1465             # Common Rose::DB::Object-derived base class for the other objects
1466             package My::Object;
1467              
1468             use My::DB;
1469              
1470             use base 'Rose::DB::Object';
1471              
1472             sub init_db { My::DB->new }
1473              
1474             ...
1475              
1476             package My::Price;
1477              
1478             use base 'My::Object';
1479              
1480             __PACKAGE__->meta->setup
1481             (
1482             columns =>
1483             [
1484             price_id => { type => 'serial', not_null => 1 },
1485             product_id => { type => 'int' },
1486             region => { type => 'char', length => 2, default => 'US' },
1487             price => { type => 'decimal', precision => 10, scale => 2 },
1488             ],
1489              
1490             foreign_keys => [ 'product' ],
1491             );
1492              
1493             ...
1494              
1495             package My::Vendor;
1496              
1497             use base 'My::Object';
1498              
1499             __PACKAGE__->meta->setup
1500             (
1501             columns =>
1502             [
1503             id => { type => 'serial', not_null => 1 },
1504             name => { type => 'varchar', length => 255 },
1505             ],
1506             );
1507              
1508             ...
1509              
1510             package My::Color;
1511              
1512             use base 'My::Object';
1513              
1514             __PACKAGE__->meta->setup
1515             (
1516             columns =>
1517             [
1518             code => { type => 'char', length => 3, not_null => 1 },
1519             name => { type => 'varchar', length => 255 },
1520             ],
1521             );
1522              
1523             ...
1524              
1525             package My::Product;
1526              
1527             use base 'My::Object';
1528              
1529             __PACKAGE__->meta->setup
1530             (
1531             columns =>
1532             [
1533             id => { type => 'serial', not_null => 1 },
1534             name => { type => 'varchar', length => 255 },
1535             vendor_id => { type => 'int' },
1536             ],
1537              
1538             foreign_keys => [ 'vendor' ],
1539              
1540             relationships =>
1541             [
1542             prices => { type => 'one to many' },
1543             colors => { type => 'many to many' },
1544             ],
1545             );
1546              
1547             ...
1548              
1549             package My::ProductColors;
1550              
1551             use base 'My::Object';
1552              
1553             __PACKAGE__->meta->setup
1554             (
1555             columns => [ qw(id product_id color_code) ],
1556             foreign_keys => [ 'product', 'color' ],
1557             );
1558              
1559             Let's add some data:
1560              
1561             INSERT INTO vendors (id, name) VALUES (1, 'V1');
1562             INSERT INTO vendors (id, name) VALUES (2, 'V2');
1563              
1564             INSERT INTO products (id, name, vendor_id) VALUES (1, 'A', 1);
1565             INSERT INTO products (id, name, vendor_id) VALUES (2, 'B', 2);
1566             INSERT INTO products (id, name, vendor_id) VALUES (3, 'C', 1);
1567              
1568             INSERT INTO prices (product_id, region, price) VALUES (1, 'US', 1.23);
1569             INSERT INTO prices (product_id, region, price) VALUES (1, 'DE', 4.56);
1570             INSERT INTO prices (product_id, region, price) VALUES (2, 'US', 5.55);
1571             INSERT INTO prices (product_id, region, price) VALUES (3, 'US', 5.78);
1572             INSERT INTO prices (product_id, region, price) VALUES (3, 'US', 9.99);
1573              
1574             INSERT INTO colors (code, name) VALUES ('CC1', 'red');
1575             INSERT INTO colors (code, name) VALUES ('CC2', 'green');
1576             INSERT INTO colors (code, name) VALUES ('CC3', 'blue');
1577             INSERT INTO colors (code, name) VALUES ('CC4', 'pink');
1578              
1579             INSERT INTO product_colors (product_id, color_code) VALUES (1, 'CC1');
1580             INSERT INTO product_colors (product_id, color_code) VALUES (1, 'CC2');
1581              
1582             INSERT INTO product_colors (product_id, color_code) VALUES (2, 'CC4');
1583              
1584             INSERT INTO product_colors (product_id, color_code) VALUES (3, 'CC2');
1585             INSERT INTO product_colors (product_id, color_code) VALUES (3, 'CC3');
1586              
1587             (Be aware that not all databases are smart enough to track explicitly setting serial column values as shown in the INSERT statements above. Subsequent auto-generated serial values may conflict with the explicitly set serial column values already in the table. Values are set explicitly here to make the examples easier to follow. In "real" code, you should let the serial columns populate automatically.)
1588              
1589             Finally, the classes in action:
1590              
1591             $p = My::Product->new(id => 1)->load;
1592              
1593             print $p->vendor->name, "\n"; # "V1"
1594              
1595             # "US: 1.23, DE: 4.56"
1596             print join(', ', map { $_->region .': '. $_->price } $p->prices), "\n";
1597              
1598             # "red, green"
1599             print join(', ', map { $_->name } $p->colors), "\n";
1600              
1601             =head1 AUTO-INIT EXAMPLE
1602              
1603             Using L<Rose::DB::Object>'s L<auto-initialization|Rose::DB::Object::Metadata/"AUTO-INITIALIZATION"> feature, the Perl code can be reduced to an absurd degree. Given the same database schema and data shown in the L<example|/EXAMPLE> above, consider the following classes:
1604              
1605             package My::Auto::Color;
1606             use base 'My::Object';
1607             __PACKAGE__->meta->auto_initialize;
1608             ...
1609              
1610             package My::Auto::Price;
1611             use base 'My::Object';
1612             __PACKAGE__->meta->auto_initialize;
1613             ...
1614              
1615             package My::Auto::ProductColors;
1616             use base 'My::Object';
1617             __PACKAGE__->meta->auto_initialize;
1618             ...
1619              
1620             package My::Auto::Vendor;
1621             use base 'My::Object';
1622             __PACKAGE__->meta->auto_initialize;
1623             ...
1624              
1625             package My::Auto::Product;
1626             use base 'My::Object';
1627             __PACKAGE__->meta->auto_initialize;
1628              
1629             Not a single table, column, foreign key, or relationship is specified, yet everything still works:
1630              
1631             $p = My::Auto::Product->new(id => 1)->load;
1632              
1633             print $p->vendor->name, "\n"; # "V1"
1634              
1635             # "US: 1.23, DE: 4.56"
1636             print join(', ', map { $_->region .': '. $_->price } $p->prices), "\n";
1637              
1638             # "red, green"
1639             print join(', ', map { $_->name } $p->colors), "\n";
1640              
1641             More precisely, everything still works I<provided> that you load all the of the related modules. For example, if you load C<My::Auto::Product> but don't load C<My::Auto::Price> (either from within the C<My::Auto::Product> class or in your program itself), then the C<My::Auto::Product> will not have a C<prices()> method (since your program will have no knowledge of the C<My::Auto::Price> class). Use the L<loader|Rose::DB::Object::Loader> if you want to set up a bunch of related classes automatically without worrying about this kind of thing.
1642              
1643             Anyway, I don't recommend this kind of extreme approach, but it is an effective demonstration of the power of the convention manager.
1644              
1645             =head1 AUTHOR
1646              
1647             John C. Siracusa (siracusa@gmail.com)
1648              
1649             =head1 LICENSE
1650              
1651             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
1652             free software; you can redistribute it and/or modify it under the same terms
1653             as Perl itself.