File Coverage

blib/lib/DBIx/Class/TopoSort.pm
Criterion Covered Total %
statement 11 40 27.5
branch 0 6 0.0
condition 0 5 0.0
subroutine 4 6 66.6
pod 2 2 100.0
total 17 59 28.8


line stmt bran cond sub pod time code
1             package DBIx::Class::TopoSort;
2              
3 7     7   1844094 use 5.008_004;
  7         36  
4              
5 7     7   50 use strict;
  7         20  
  7         308  
6 7     7   51 use warnings FATAL => 'all';
  7         20  
  7         453  
7              
8             our $VERSION = '0.050010';
9              
10 7     7   5400 use Graph;
  7         776159  
  7         2617  
11              
12             sub toposort_graph {
13 0     0 1   my $self = shift;
14 0           my ($schema, %opts) = @_;
15              
16 0           my $g = Graph->new;
17              
18 0           my @source_names = $schema->sources;
19              
20 0           my %table_sources;
21 0           foreach my $name ( @source_names ) {
22 0           my $table_name = $schema->source($name)->name;
23 0   0       $table_sources{$table_name} //= [];
24 0           push @{ $table_sources{$table_name} }, $name;
  0            
25             }
26              
27 0           foreach my $name ( @source_names ) {
28 0           my $source = $schema->source($name);
29 0           $g->add_vertex($name);
30              
31 0           foreach my $rel_name ( $source->relationships ) {
32 0 0         next if grep { $_ eq $rel_name } @{$opts{skip}{$name}};
  0            
  0            
33 0           my $rel_info = $source->relationship_info($rel_name);
34              
35 0 0         if ( $rel_info->{attrs}{is_foreign_key_constraint} ) {
36 0           my $sources = $table_sources{$schema->source($rel_info->{source})->name};
37 0           foreach my $source ( @$sources ) {
38 0           $g->add_edge($source, $name);
39             }
40             }
41             }
42             }
43              
44 0           return $g;
45             }
46              
47             sub toposort {
48 0     0 1   my $self = shift;
49 0           my $schema;
50 0 0 0       if (ref($self) && $self->isa('DBIx::Class::Schema')) {
51 0           $schema = $self;
52             }
53             else {
54 0           $schema = shift(@_);
55             }
56 0           return $self->toposort_graph($schema, @_)->toposort();
57             }
58              
59             1;
60             __END__
61              
62             =head1 NAME
63              
64             DBIx::Class::TopoSort - The addition of topological sorting to DBIx::Class
65              
66             =head1 SYNOPSIS
67              
68             Within your schema class:
69              
70             __PACKAGE__->load_components('TopoSort');
71              
72             Later:
73              
74             my $schema = Your::App::Schema->connect(...);
75             my @toposorted_sourcenames = $schema->toposort();
76              
77             If you have a cycle in your relationships
78              
79             my @toposorted_sourcenames = $schema->toposort(
80             skip => {
81             Artist => [qw/ first_album /],
82             },
83             );
84              
85             Alternately:
86              
87             my @toposorted_sourcenames = DBIx::Class::TopoSort->toposort($schema);
88              
89             =head1 DESCRIPTION
90              
91             This adds a method to L<DBIx::Class::Schema> which returns the full list of
92             sources (similar to L<DBIx::Class::Schema/sources>) in topological-sorted order.
93              
94             =head2 TOPOLOGICAL SORT
95              
96             A topological sort of the tables returns the list of tables such that any table
97             with a foreign key relationship appears after any table it has a foreign key
98             relationship to.
99              
100             =head1 METHODS
101              
102             This class is not instantiable nor does it provide any methods of its own. All
103             methods are added to the L<DBIx::Class::Schema> class and are callable on
104             objects instantiated of that class.
105              
106             =head2 toposort
107              
108             This is sugar for:
109              
110             $self->toposort_graph(@_)->toposort();
111              
112             Calling this method multiple times may return the list of source names in
113             different order. Each order will conform to the gurantee described in the
114             section on TOPOLOGICAL SORT.
115              
116             This method will throw an error if there are any cycles in your tables. You will
117             need to specify the skip parameter (described below) to break those cycles.
118              
119             =head2 toposort (Class method)
120              
121             Alternately, if you do not wish to use TopoSort as a component, you can call it
122             as a class method on this class. The toposort() method is smart enough to
123             distinguish.
124              
125             Note: toposort_graph() does B<not> distinguish - it assumes it will be called
126             with the C<$schema> object passed in.
127              
128             =head2 toposort_graph
129              
130             This returns a L<Graph> object with a vertex for every source and an edge for
131             every foreign key relationship.
132              
133             It takes the following parameters.
134              
135             =over 4
136              
137             =item skip
138              
139             This describes the list of relationships that should be ignored by the toposort
140             algorithm. This is generally used if you have cycles in your schema (though it
141             could possibly be useful in other ways, I guess). The value is a hashref. The
142             keys of this hashref are source names and the values are arrays of relationship
143             names.
144              
145             skip => {
146             Artist => [ qw/ first_album / ],
147             },
148              
149             =back
150              
151             =head1 SEE ALSO
152              
153             L<Graph/toposort>
154              
155             =head1 AUTHOR
156              
157             =over 4
158              
159             =item * Rob Kinyon <rob.kinyon@gmail.com>
160              
161             =back
162              
163             =head1 LICENSE
164              
165             Copyright (c) 2013 Rob Kinyon. All Rights Reserved.
166             This is free software, you may use it and distribute it under the same terms
167             as Perl itself.
168              
169             =cut