File Coverage

blib/lib/DBIx/Class/CDBICompat/Relationships.pm
Criterion Covered Total %
statement 24 121 19.8
branch 0 44 0.0
condition 0 34 0.0
subroutine 8 22 36.3
pod 0 6 0.0
total 32 227 14.1


line stmt bran cond sub pod time code
1             package # hide from PAUSE
2             DBIx::Class::CDBICompat::Relationships;
3              
4 2     2   1072 use strict;
  2         5  
  2         52  
5 2     2   9 use warnings;
  2         4  
  2         42  
6 2     2   7 use base 'DBIx::Class';
  2         4  
  2         169  
7              
8 2     2   11 use Clone;
  2         4  
  2         70  
9 2     2   305 use DBIx::Class::CDBICompat::Relationship;
  2         5  
  2         64  
10 2     2   12 use Scalar::Util 'blessed';
  2         3  
  2         83  
11 2     2   10 use DBIx::Class::_Util qw(quote_sub perlstring);
  2         4  
  2         68  
12 2     2   9 use namespace::clean;
  2         3  
  2         9  
13              
14             __PACKAGE__->mk_classdata('__meta_info' => {});
15              
16              
17             =head1 NAME
18              
19             DBIx::Class::CDBICompat::Relationships - Emulate has_a(), has_many(), might_have() and meta_info()
20              
21             =head1 DESCRIPTION
22              
23             Emulate C, C, C and C.
24              
25             =cut
26              
27             sub has_a {
28 0     0 0   my($self, $col, @rest) = @_;
29              
30 0           $self->_declare_has_a($col, @rest);
31 0           $self->_mk_inflated_column_accessor($col);
32              
33 0           return 1;
34             }
35              
36              
37             sub _declare_has_a {
38 0     0     my ($self, $col, $f_class, %args) = @_;
39 0 0         $self->throw_exception( "No such column ${col}" )
40             unless $self->has_column($col);
41 0           $self->ensure_class_loaded($f_class);
42              
43 0           my $rel_info;
44              
45             # Class::DBI allows Non database has_a with implicit deflate and inflate
46             # Hopefully the following will catch Non-database tables.
47 0 0 0       if( !$f_class->isa('DBIx::Class::Row') and !$f_class->isa('Class::DBI::Row') ) {
48 0   0 0     $args{'inflate'} ||= sub { $f_class->new(shift) }; # implicit inflate by calling new
  0            
49 0   0 0     $args{'deflate'} ||= sub { shift() . '' }; # implicit deflate by stringification
  0            
50             }
51              
52 0 0 0       if ($args{'inflate'} || $args{'deflate'}) { # Non-database has_a
53 0 0         if (!ref $args{'inflate'}) {
54 0           my $meth = $args{'inflate'};
55 0     0     $args{'inflate'} = sub { $f_class->$meth(shift); };
  0            
56             }
57 0 0         if (!ref $args{'deflate'}) {
58 0           my $meth = $args{'deflate'};
59 0     0     $args{'deflate'} = sub { shift->$meth; };
  0            
60             }
61 0           $self->inflate_column($col, \%args);
62              
63 0           $rel_info = {
64             class => $f_class
65             };
66             }
67             else {
68 0           $self->belongs_to($col, $f_class);
69 0           $rel_info = $self->result_source->relationship_info($col);
70             }
71              
72 0           $rel_info->{args} = \%args;
73              
74 0           $self->_extend_meta(
75             has_a => $col,
76             $rel_info
77             );
78              
79 0           return 1;
80             }
81              
82             sub _mk_inflated_column_accessor {
83 0     0     my($class, $col) = @_;
84              
85 0           return $class->mk_group_accessors('inflated_column' => $col);
86             }
87              
88             sub has_many {
89 0     0 0   my ($class, $rel, $f_class, $f_key, $args) = @_;
90              
91 0           my @f_method;
92              
93 0 0         if (ref $f_class eq 'ARRAY') {
94 0           ($f_class, @f_method) = @$f_class;
95             }
96              
97 0 0 0       if (ref $f_key eq 'HASH' && !$args) { $args = $f_key; undef $f_key; };
  0            
  0            
98              
99 0   0       $args ||= {};
100 0   0       my $cascade = delete $args->{cascade} || '';
101 0 0 0       if (delete $args->{no_cascade_delete} || $cascade eq 'None') {
    0          
    0          
102 0           $args->{cascade_delete} = 0;
103             }
104             elsif( $cascade eq 'Delete' ) {
105 0           $args->{cascade_delete} = 1;
106             }
107             elsif( length $cascade ) {
108 0           warn "Unemulated cascade option '$cascade' in $class->has_many($rel => $f_class)";
109             }
110              
111 0 0 0       if( !$f_key and !@f_method ) {
112 0           $class->ensure_class_loaded($f_class);
113 0           my $f_source = $f_class->result_source;
114 0           ($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class }
  0            
115             $f_source->relationships;
116             }
117              
118 0           $class->next::method($rel, $f_class, $f_key, $args);
119              
120 0           my $rel_info = $class->result_source->relationship_info($rel);
121 0           $args->{mapping} = \@f_method;
122 0           $args->{foreign_key} = $f_key;
123 0           $rel_info->{args} = $args;
124              
125 0           $class->_extend_meta(
126             has_many => $rel,
127             $rel_info
128             );
129              
130 0 0         if (@f_method) {
131             my @qsub_args = (
132 0     0     { '$rf' => \sub { my $o = shift; $o = $o->$_ for @f_method; $o } },
  0            
  0            
133 0           { attributes => [ 'DBIC_method_is_generated_from_resultsource_metadata' ] },
134             );
135              
136 0           quote_sub "${class}::${rel}", sprintf( <<'EOC', perlstring $rel), @qsub_args;
137             my $rs = shift->related_resultset(%s)->search_rs( @_);
138             $rs->{attrs}{record_filter} = $rf;
139             return (wantarray ? $rs->all : $rs);
140             EOC
141              
142 0           return 1;
143             }
144             }
145              
146              
147             sub might_have {
148 0     0 0   my ($class, $rel, $f_class, @columns) = @_;
149              
150 0           my $ret;
151 0 0 0       if (ref $columns[0] || !defined $columns[0]) {
152 0           $ret = $class->next::method($rel, $f_class, @columns);
153             } else {
154 0           $ret = $class->next::method($rel, $f_class, undef,
155             { proxy => \@columns });
156             }
157              
158 0           my $rel_info = $class->result_source->relationship_info($rel);
159 0           $rel_info->{args}{import} = \@columns;
160              
161 0           $class->_extend_meta(
162             might_have => $rel,
163             $rel_info
164             );
165              
166 0           return $ret;
167             }
168              
169              
170             sub _extend_meta {
171 0     0     my ($class, $type, $rel, $val) = @_;
172              
173             ### Explicitly not using the deep cloner as Clone exhibits specific behavior
174             ### wrt CODE references - it simply passes them as-is to the new structure
175             ### (without deparse/eval cycles). There likely is code that relies on this
176             ### so we just let sleeping dogs lie.
177 0   0       my $hash = Clone::clone($class->__meta_info || {});
178              
179 0           $val->{self_class} = $class;
180 0           $val->{type} = $type;
181 0           $val->{accessor} = $rel;
182              
183 0           $hash->{$type}{$rel} = DBIx::Class::CDBICompat::Relationship->new($val);
184 0           $class->__meta_info($hash);
185             }
186              
187              
188             sub meta_info {
189 0     0 0   my ($class, $type, $rel) = @_;
190 0           my $meta = $class->__meta_info;
191 0 0         return $meta unless $type;
192              
193 0           my $type_meta = $meta->{$type};
194 0 0         return $type_meta unless $rel;
195 0           return $type_meta->{$rel};
196             }
197              
198              
199             sub search {
200 0     0 0   my $self = shift;
201 0           my $attrs = {};
202 0 0 0       if (@_ > 1 && ref $_[-1] eq 'HASH') {
203 0           $attrs = { %{ pop(@_) } };
  0            
204             }
205 0 0         my $where = (@_ ? ((@_ == 1) ? ((ref $_[0] eq "HASH") ? { %{+shift} } : shift)
  0 0          
    0          
206             : {@_})
207             : undef());
208 0 0         if (ref $where eq 'HASH') {
209 0           foreach my $key (keys %$where) { # has_a deflation hack
210             $where->{$key} = ''.$where->{$key} if (
211             defined blessed $where->{$key}
212             and
213 0 0 0       $where->{$key}->isa('DBIx::Class')
214             );
215             }
216             }
217 0           $self->next::method($where, $attrs);
218             }
219              
220             sub new_related {
221 0 0   0 0   $_[0]->throw_exception("Calling new_related() as a class method is not supported")
222             unless length ref $_[0];
223              
224 0           shift->next::method(@_);
225             }
226              
227             =head1 FURTHER QUESTIONS?
228              
229             Check the list of L.
230              
231             =head1 COPYRIGHT AND LICENSE
232              
233             This module is free software L
234             by the L. You can
235             redistribute it and/or modify it under the same terms as the
236             L.
237              
238             =cut
239              
240             1;