| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package DBIx::Class::Schema::SanityChecker; |
|
2
|
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
5303
|
use strict; |
|
|
5
|
|
|
|
|
12
|
|
|
|
5
|
|
|
|
|
147
|
|
|
4
|
5
|
|
|
5
|
|
29
|
use warnings; |
|
|
5
|
|
|
|
|
12
|
|
|
|
5
|
|
|
|
|
161
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
5
|
|
|
|
|
385
|
use DBIx::Class::_Util qw( |
|
7
|
|
|
|
|
|
|
dbic_internal_try refdesc uniq serialize |
|
8
|
|
|
|
|
|
|
describe_class_methods emit_loud_diag |
|
9
|
5
|
|
|
5
|
|
25
|
); |
|
|
5
|
|
|
|
|
9
|
|
|
10
|
5
|
|
|
5
|
|
35
|
use DBIx::Class (); |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
87
|
|
|
11
|
5
|
|
|
5
|
|
21
|
use Scalar::Util qw( blessed refaddr ); |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
219
|
|
|
12
|
5
|
|
|
5
|
|
26
|
use namespace::clean; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
42
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
DBIx::Class::Schema::SanityChecker - Extensible "critic" for your Schema class hierarchy |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
package MyApp::Schema; |
|
21
|
|
|
|
|
|
|
use base 'DBIx::Class::Schema'; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# this is the default setting |
|
24
|
|
|
|
|
|
|
__PACKAGE__->schema_sanity_checker('DBIx::Class::Schema::SanityChecker'); |
|
25
|
|
|
|
|
|
|
... |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
This is the default implementation of the Schema and related classes |
|
30
|
|
|
|
|
|
|
L. |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
The validator is B. See L |
|
33
|
|
|
|
|
|
|
for discussion of the runtime effects. |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Use of this class begins by invoking L |
|
36
|
|
|
|
|
|
|
(usually via L), which in turn starts |
|
37
|
|
|
|
|
|
|
invoking validators I> in the order listed in |
|
38
|
|
|
|
|
|
|
L. For each set of returned errors (if any) |
|
39
|
|
|
|
|
|
|
I> is called and the resulting strings are |
|
40
|
|
|
|
|
|
|
passed to L, where final headers are prepended and the entire |
|
41
|
|
|
|
|
|
|
thing is printed on C. |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
The class does not provide a constructor, due to the lack of state to be |
|
44
|
|
|
|
|
|
|
passed around: object orientation was chosen purely for the ease of |
|
45
|
|
|
|
|
|
|
overriding parts of the chain of events as described above. The general |
|
46
|
|
|
|
|
|
|
pattern of communicating errors between the individual methods (both |
|
47
|
|
|
|
|
|
|
before and after formatting) is an arrayref of hash references. |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=head2 WHY |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
DBIC existed for more than a decade without any such setup validation |
|
52
|
|
|
|
|
|
|
fanciness, let alone something that is enabled by default (which in turn |
|
53
|
|
|
|
|
|
|
L). The reason for this relatively |
|
54
|
|
|
|
|
|
|
drastic change is a set of revamps within the metadata handling framework, |
|
55
|
|
|
|
|
|
|
in order to resolve once and for all problems like |
|
56
|
|
|
|
|
|
|
L |
|
57
|
|
|
|
|
|
|
L |
|
58
|
|
|
|
|
|
|
DBIC internals are now way more robust than they were before, this comes at |
|
59
|
|
|
|
|
|
|
a price: some non-issues in code that has been working for a while, will |
|
60
|
|
|
|
|
|
|
now become hard to explain, or if you are unlucky: B. |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Thus, in order to protect existing codebases to the fullest extent possible, |
|
63
|
|
|
|
|
|
|
the executive decision (and substantial effort) was made to introduce this |
|
64
|
|
|
|
|
|
|
on-by-default setup validation framework. A massive amount of work has been |
|
65
|
|
|
|
|
|
|
invested ensuring that none of the builtin checks emit a false-positive: |
|
66
|
|
|
|
|
|
|
each and every complaint made by these checks B. |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 Performance considerations |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
First of all - after your connection has been established - there is B
|
|
71
|
|
|
|
|
|
|
runtime penalty> whenever the checks are enabled. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
By default the checks are triggered every time |
|
74
|
|
|
|
|
|
|
L is called. Thus there is a |
|
75
|
|
|
|
|
|
|
noticeable startup slowdown, most notably during testing (each test is |
|
76
|
|
|
|
|
|
|
effectively a standalone program connecting anew). As an example the test |
|
77
|
|
|
|
|
|
|
execution phase of the L C distribution |
|
78
|
|
|
|
|
|
|
suffers a consistent slowdown of about C<16%>. This is considered a relatively |
|
79
|
|
|
|
|
|
|
small price to pay for the benefits provided. |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Nevertheless, there are valid cases for disabling the checks during |
|
82
|
|
|
|
|
|
|
day-to-day development, and having them run only during CI builds. In fact |
|
83
|
|
|
|
|
|
|
the test suite of DBIC does exactly this as can be seen in |
|
84
|
|
|
|
|
|
|
F: |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
~/dbic_repo$ git show 39636786 | perl -ne "print if 16..61" |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Whatever you do, B: it is not |
|
89
|
|
|
|
|
|
|
worth the risk. |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head3 Perl5.8 |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
The situation with perl interpreters before C is sadly more |
|
94
|
|
|
|
|
|
|
complicated: due to lack of built-in L, the |
|
95
|
|
|
|
|
|
|
mechanism used to interrogate various classes is |
|
96
|
|
|
|
|
|
|
L<< B slower|https://github.com/dbsrgits/dbix-class/commit/296248c3 >>. |
|
97
|
|
|
|
|
|
|
As a result the very same version of L |
|
98
|
|
|
|
|
|
|
L takes a C> hit on its |
|
99
|
|
|
|
|
|
|
test execution time (these numbers are observed with the speedups of |
|
100
|
|
|
|
|
|
|
L available, without them the slowdown reaches the whopping |
|
101
|
|
|
|
|
|
|
C<350%>). |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
It is the author's B recommendation to find a way to run the |
|
104
|
|
|
|
|
|
|
checks on your codebase continuously, even if it takes much longer. Refer to |
|
105
|
|
|
|
|
|
|
the last paragraph of L above for an example how |
|
106
|
|
|
|
|
|
|
to do this during CI builds only. |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head2 Validations provided by this module |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head3 no_indirect_method_overrides |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
There are many methods within DBIC which are |
|
113
|
|
|
|
|
|
|
L<"strictly sugar"|DBIx::Class::MethodAttributes/DBIC_method_is_indirect_sugar> |
|
114
|
|
|
|
|
|
|
and should never be overridden by your application (e.g. see warnings at the |
|
115
|
|
|
|
|
|
|
end of L and L). |
|
116
|
|
|
|
|
|
|
Starting with C DBIC is much more aggressive in calling the |
|
117
|
|
|
|
|
|
|
underlying non-sugar methods directly, which in turn means that almost all |
|
118
|
|
|
|
|
|
|
user-side overrides of sugar methods are never going to be invoked. These |
|
119
|
|
|
|
|
|
|
situations are now reliably detected and reported individually (you may |
|
120
|
|
|
|
|
|
|
end up with a lot of output on C due to this). |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Note: B reported by this check B<*MUST*> be resolved |
|
123
|
|
|
|
|
|
|
before upgrading DBIC in production. Malfunctioning business logic and/or |
|
124
|
|
|
|
|
|
|
B may result otherwise. |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head3 valid_c3_composition |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Looks through everything returned by L, and |
|
129
|
|
|
|
|
|
|
for any class that B already utilize L a |
|
130
|
|
|
|
|
|
|
L is calculated and then |
|
131
|
|
|
|
|
|
|
compared to the shadowing map as if C was requested in the first place. |
|
132
|
|
|
|
|
|
|
Any discrepancies are reported in order to clearly identify L
|
|
133
|
|
|
|
|
|
|
bugs|https://blog.afoolishmanifesto.com/posts/mros-and-you> especially when |
|
134
|
|
|
|
|
|
|
encountered within complex inheritance hierarchies. |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head3 no_inheritance_crosscontamination |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Checks that every individual L, |
|
139
|
|
|
|
|
|
|
L, L, |
|
140
|
|
|
|
|
|
|
L |
|
141
|
|
|
|
|
|
|
and L class does not inherit from |
|
142
|
|
|
|
|
|
|
an unexpected DBIC base class: e.g. an error will be raised if your |
|
143
|
|
|
|
|
|
|
C inherits from both C and |
|
144
|
|
|
|
|
|
|
C. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head1 METHODS |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head2 perform_schema_sanity_checks |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=over |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item Arguments: L<$schema|DBIx::Class::Schema> |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item Return Value: unspecified (ignored by caller) |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=back |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
The entry point expected by the |
|
159
|
|
|
|
|
|
|
L. See |
|
160
|
|
|
|
|
|
|
L for details. |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=cut |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub perform_schema_sanity_checks { |
|
165
|
2
|
|
|
2
|
1
|
6
|
my ($self, $schema) = @_; |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
local $DBIx::Class::_Util::describe_class_query_cache->{'!internal!'} = {} |
|
168
|
|
|
|
|
|
|
if |
|
169
|
|
|
|
|
|
|
# does not make a measurable difference on 5.10+ |
|
170
|
|
|
|
|
|
|
DBIx::Class::_ENV_::OLD_MRO |
|
171
|
|
|
|
|
|
|
and |
|
172
|
|
|
|
|
|
|
# the callstack shouldn't really be recursive, but for completeness... |
|
173
|
2
|
|
|
|
|
3
|
! $DBIx::Class::_Util::describe_class_query_cache->{'!internal!'} |
|
174
|
|
|
|
|
|
|
; |
|
175
|
|
|
|
|
|
|
|
|
176
|
2
|
|
|
|
|
4
|
my (@errors_found, $schema_desc); |
|
177
|
2
|
|
|
|
|
3
|
for my $ch ( @{ $self->available_checks } ) { |
|
|
2
|
|
|
|
|
5
|
|
|
178
|
|
|
|
|
|
|
|
|
179
|
6
|
|
|
|
|
12
|
my $err = $self->${\"check_$ch"} ( $schema ); |
|
|
6
|
|
|
|
|
35
|
|
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
push @errors_found, map |
|
182
|
|
|
|
|
|
|
{ |
|
183
|
|
|
|
|
|
|
{ |
|
184
|
1
|
50
|
33
|
|
|
35
|
check_name => $ch, |
|
185
|
|
|
|
|
|
|
formatted_error => $_, |
|
186
|
|
|
|
|
|
|
schema_desc => ( $schema_desc ||= |
|
187
|
|
|
|
|
|
|
( length ref $schema ) |
|
188
|
|
|
|
|
|
|
? refdesc $schema |
|
189
|
|
|
|
|
|
|
: "'$schema'" |
|
190
|
|
|
|
|
|
|
), |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
@{ |
|
194
|
6
|
50
|
|
|
|
30
|
$self->${\"format_${ch}_errors"} ( $err ) |
|
|
1
|
100
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
16
|
|
|
195
|
|
|
|
|
|
|
|| |
|
196
|
|
|
|
|
|
|
[] |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
if @$err; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
|
|
201
|
2
|
100
|
|
|
|
15
|
$self->emit_errors(\@errors_found) |
|
202
|
|
|
|
|
|
|
if @errors_found; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head2 available_checks |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=over |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item Arguments: none |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=item Return Value: \@list_of_check_names |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=back |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
The list of checks L will perform on the |
|
216
|
|
|
|
|
|
|
provided L<$schema|DBIx::Class::Schema> object. For every entry returned |
|
217
|
|
|
|
|
|
|
by this method, there must be a pair of I> and |
|
218
|
|
|
|
|
|
|
I> methods available. |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Override this method to add checks to the |
|
221
|
|
|
|
|
|
|
L. |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=cut |
|
224
|
|
|
|
|
|
|
|
|
225
|
3
|
|
|
3
|
1
|
122
|
sub available_checks { [qw( |
|
226
|
|
|
|
|
|
|
valid_c3_composition |
|
227
|
|
|
|
|
|
|
no_inheritance_crosscontamination |
|
228
|
|
|
|
|
|
|
no_indirect_method_overrides |
|
229
|
|
|
|
|
|
|
)] } |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=head2 emit_errors |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=over |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=item Arguments: \@list_of_formatted_errors |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=item Return Value: unspecified (ignored by caller) |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=back |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Takes an array reference of individual errors returned by various |
|
242
|
|
|
|
|
|
|
I> formatters, and outputs them on C. |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This method is the most convenient integration point for a 3rd party logging |
|
245
|
|
|
|
|
|
|
framework. |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Each individual error is expected to be a hash reference with all values being |
|
248
|
|
|
|
|
|
|
plain strings as follows: |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
{ |
|
251
|
|
|
|
|
|
|
schema_desc => $human_readable_description_of_the_passed_in_schema |
|
252
|
|
|
|
|
|
|
check_name => $name_of_the_check_as_listed_in_available_checks() |
|
253
|
|
|
|
|
|
|
formatted_error => $error_text_as_returned_by_format_$checkname_errors() |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
If the environment variable C is set to |
|
257
|
|
|
|
|
|
|
a true value this method will throw an exception with the same text. Those who |
|
258
|
|
|
|
|
|
|
prefer to take no chances could set this variable permanently as part of their |
|
259
|
|
|
|
|
|
|
deployment scripts. |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=cut |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# *NOT* using carp_unique and the warn framework - make |
|
264
|
|
|
|
|
|
|
# it harder to accidentaly silence problems via $SIG{__WARN__} |
|
265
|
|
|
|
|
|
|
sub emit_errors { |
|
266
|
|
|
|
|
|
|
#my ($self, $errs) = @_; |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
my @final_error_texts = map { |
|
269
|
|
|
|
|
|
|
sprintf( "Schema %s failed the '%s' sanity check: %s\n", |
|
270
|
1
|
|
|
|
|
3
|
@{$_}{qw( schema_desc check_name formatted_error )} |
|
|
1
|
|
|
|
|
13
|
|
|
271
|
|
|
|
|
|
|
); |
|
272
|
1
|
|
|
1
|
1
|
2
|
} @{$_[1]}; |
|
|
1
|
|
|
|
|
2
|
|
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
emit_loud_diag( |
|
275
|
|
|
|
|
|
|
msg => $_ |
|
276
|
1
|
|
|
|
|
6
|
) for @final_error_texts; |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Do not use the constant - but instead check the env every time |
|
279
|
|
|
|
|
|
|
# This will allow people to start auditing their apps piecemeal |
|
280
|
|
|
|
|
|
|
DBIx::Class::Exception->throw( join "\n", @final_error_texts, ' ' ) |
|
281
|
1
|
50
|
|
|
|
8
|
if $ENV{DBIC_ASSERT_NO_FAILING_SANITY_CHECKS}; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head2 all_schema_related_classes |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=over |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=item Arguments: L<$schema|DBIx::Class::Schema> |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=item Return Value: @sorted_list_of_unique_class_names |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=back |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
This is a convenience method providing a list (not an arrayref) of |
|
295
|
|
|
|
|
|
|
"interesting classes" related to the supplied schema. The returned list |
|
296
|
|
|
|
|
|
|
currently contains the following class names: |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=over |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=item * The L class itself |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=item * The associated L class if any |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=item * The classes of all L if any |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=item * All L classes for all registered ResultSource instances |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=item * All L classes for all registered ResultSource instances |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=back |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=cut |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub all_schema_related_classes { |
|
315
|
4
|
|
|
4
|
1
|
12
|
my ($self, $schema) = @_; |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sort( uniq( map { |
|
318
|
14
|
100
|
|
|
|
86
|
( not defined $_ ) ? () |
|
|
|
50
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
: ( defined blessed $_ ) ? ref $_ |
|
320
|
|
|
|
|
|
|
: $_ |
|
321
|
|
|
|
|
|
|
} ( |
|
322
|
|
|
|
|
|
|
$schema, |
|
323
|
|
|
|
|
|
|
$schema->storage, |
|
324
|
|
|
|
|
|
|
( map { |
|
325
|
2
|
|
|
|
|
40
|
$_, |
|
326
|
|
|
|
|
|
|
$_->result_class, |
|
327
|
|
|
|
|
|
|
$_->resultset_class, |
|
328
|
4
|
|
|
|
|
98
|
} map { $schema->source($_) } $schema->sources ), |
|
|
2
|
|
|
|
|
36
|
|
|
329
|
|
|
|
|
|
|
))); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub format_no_indirect_method_overrides_errors { |
|
334
|
|
|
|
|
|
|
# my ($self, $errors) = @_; |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
[ map { sprintf( |
|
337
|
|
|
|
|
|
|
"Method(s) %s override the convenience shortcut %s::%s(): " |
|
338
|
|
|
|
|
|
|
. 'it is almost certain these overrides *MAY BE COMPLETELY IGNORED* at ' |
|
339
|
|
|
|
|
|
|
. 'runtime. You MUST reimplement each override to hook a method from the ' |
|
340
|
|
|
|
|
|
|
. "chain of calls within the convenience shortcut as seen when running:\n " |
|
341
|
|
|
|
|
|
|
. '~$ perl -M%2$s -MDevel::Dwarn -e "Ddie { %3$s => %2$s->can(q(%3$s)) }"', |
|
342
|
0
|
|
|
|
|
0
|
join (', ', map { "$_()" } sort @{ $_->{by} } ), |
|
|
0
|
|
|
|
|
0
|
|
|
343
|
|
|
|
|
|
|
$_->{overridden}{via_class}, |
|
344
|
|
|
|
|
|
|
$_->{overridden}{name}, |
|
345
|
0
|
|
|
0
|
0
|
0
|
)} @{ $_[1] } ] |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub check_no_indirect_method_overrides { |
|
349
|
2
|
|
|
2
|
0
|
8
|
my ($self, $schema) = @_; |
|
350
|
|
|
|
|
|
|
|
|
351
|
2
|
|
|
|
|
7
|
my( @err, $seen_shadowing_configurations ); |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
METHOD_STACK: |
|
354
|
2
|
|
|
|
|
13
|
for my $method_stack ( map { |
|
355
|
7
|
50
|
|
|
|
15
|
values %{ describe_class_methods($_)->{methods_with_supers} || {} } |
|
|
7
|
|
|
|
|
23
|
|
|
356
|
|
|
|
|
|
|
} $self->all_schema_related_classes($schema) ) { |
|
357
|
|
|
|
|
|
|
|
|
358
|
87
|
|
|
|
|
101
|
my $nonsugar_methods; |
|
359
|
|
|
|
|
|
|
|
|
360
|
87
|
|
|
|
|
119
|
for (@$method_stack) { |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
push @$nonsugar_methods, $_ and next |
|
363
|
|
|
|
|
|
|
unless( |
|
364
|
|
|
|
|
|
|
$_->{attributes}{DBIC_method_is_indirect_sugar} |
|
365
|
|
|
|
|
|
|
or |
|
366
|
|
|
|
|
|
|
$_->{attributes}{DBIC_method_is_generated_from_resultsource_metadata} |
|
367
|
176
|
50
|
50
|
|
|
572
|
); |
|
|
|
|
33
|
|
|
|
|
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
push @err, { |
|
370
|
|
|
|
|
|
|
overridden => { |
|
371
|
|
|
|
|
|
|
name => $_->{name}, |
|
372
|
|
|
|
|
|
|
via_class => ( |
|
373
|
|
|
|
|
|
|
# this way we report a much better Dwarn oneliner in the error |
|
374
|
|
|
|
|
|
|
$_->{attributes}{DBIC_method_is_bypassable_resultsource_proxy} |
|
375
|
|
|
|
|
|
|
? 'DBIx::Class::ResultSource' |
|
376
|
|
|
|
|
|
|
: $_->{via_class} |
|
377
|
|
|
|
|
|
|
), |
|
378
|
|
|
|
|
|
|
}, |
|
379
|
0
|
|
|
|
|
0
|
by => [ map { "$_->{via_class}::$_->{name}" } @$nonsugar_methods ], |
|
380
|
|
|
|
|
|
|
} if ( |
|
381
|
|
|
|
|
|
|
$nonsugar_methods |
|
382
|
|
|
|
|
|
|
and |
|
383
|
|
|
|
|
|
|
! $seen_shadowing_configurations->{ |
|
384
|
|
|
|
|
|
|
join "\0", |
|
385
|
|
|
|
|
|
|
map |
|
386
|
0
|
0
|
0
|
|
|
0
|
{ refaddr $_ } |
|
|
0
|
0
|
|
|
|
0
|
|
|
387
|
|
|
|
|
|
|
( |
|
388
|
|
|
|
|
|
|
$_, |
|
389
|
|
|
|
|
|
|
@$nonsugar_methods, |
|
390
|
|
|
|
|
|
|
) |
|
391
|
|
|
|
|
|
|
}++ |
|
392
|
|
|
|
|
|
|
) |
|
393
|
|
|
|
|
|
|
; |
|
394
|
|
|
|
|
|
|
|
|
395
|
0
|
|
|
|
|
0
|
next METHOD_STACK; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
\@err |
|
400
|
2
|
|
|
|
|
13
|
} |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub format_valid_c3_composition_errors { |
|
404
|
|
|
|
|
|
|
# my ($self, $errors) = @_; |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
[ map { sprintf( |
|
407
|
|
|
|
|
|
|
"Class '%s' %s using the '%s' MRO affecting the lookup order of the " |
|
408
|
|
|
|
|
|
|
. "following method(s): %s. You MUST add the following line to '%1\$s' " |
|
409
|
|
|
|
|
|
|
. "right after strict/warnings:\n use mro 'c3';", |
|
410
|
|
|
|
|
|
|
$_->{class}, |
|
411
|
|
|
|
|
|
|
( ($_->{initial_mro} eq $_->{current_mro}) ? 'is' : 'was originally' ), |
|
412
|
|
|
|
|
|
|
$_->{initial_mro}, |
|
413
|
1
|
50
|
|
|
|
8
|
join (', ', map { "$_()" } sort keys %{$_->{affected_methods}} ), |
|
|
3
|
|
|
|
|
21
|
|
|
|
1
|
|
|
|
|
14
|
|
|
414
|
1
|
|
|
1
|
0
|
2
|
)} @{ $_[1] } ] |
|
|
1
|
|
|
|
|
4
|
|
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
my $base_ISA = { |
|
419
|
|
|
|
|
|
|
map { $_ => 1 } @{mro::get_linear_isa("DBIx::Class")} |
|
420
|
|
|
|
|
|
|
}; |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub check_valid_c3_composition { |
|
423
|
2
|
|
|
2
|
0
|
5
|
my ($self, $schema) = @_; |
|
424
|
|
|
|
|
|
|
|
|
425
|
2
|
|
|
|
|
4
|
my @err; |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# |
|
428
|
|
|
|
|
|
|
# A *very* involved check, to absolutely minimize false positives |
|
429
|
|
|
|
|
|
|
# If this check returns an issue - it *better be* a real one |
|
430
|
|
|
|
|
|
|
# |
|
431
|
2
|
|
|
|
|
5
|
for my $class ( $self->all_schema_related_classes($schema) ) { |
|
432
|
|
|
|
|
|
|
|
|
433
|
7
|
|
|
|
|
15
|
my $desc = do { |
|
434
|
5
|
|
|
5
|
|
5883
|
no strict 'refs'; |
|
|
5
|
|
|
|
|
14
|
|
|
|
5
|
|
|
|
|
2917
|
|
|
435
|
|
|
|
|
|
|
describe_class_methods({ |
|
436
|
|
|
|
|
|
|
class => $class, |
|
437
|
7
|
|
|
|
|
68
|
( ${"${class}::__INITIAL_MRO_UPON_DBIC_LOAD__"} |
|
438
|
7
|
100
|
|
|
|
195
|
? ( use_mro => ${"${class}::__INITIAL_MRO_UPON_DBIC_LOAD__"} ) |
|
|
3
|
|
|
|
|
20
|
|
|
439
|
|
|
|
|
|
|
: () |
|
440
|
|
|
|
|
|
|
), |
|
441
|
|
|
|
|
|
|
}) |
|
442
|
|
|
|
|
|
|
}; |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# is there anything to check? |
|
445
|
|
|
|
|
|
|
next unless ( |
|
446
|
|
|
|
|
|
|
! $desc->{mro}{is_c3} |
|
447
|
|
|
|
|
|
|
and |
|
448
|
|
|
|
|
|
|
$desc->{methods_with_supers} |
|
449
|
|
|
|
|
|
|
and |
|
450
|
|
|
|
|
|
|
my @potentially_problematic_method_stacks = |
|
451
|
|
|
|
|
|
|
grep |
|
452
|
|
|
|
|
|
|
{ |
|
453
|
|
|
|
|
|
|
# at least 2 variants came via inheritance (not ours) |
|
454
|
|
|
|
|
|
|
( |
|
455
|
64
|
|
|
|
|
177
|
(grep { $_->{via_class} ne $class } @$_) |
|
456
|
|
|
|
|
|
|
> |
|
457
|
|
|
|
|
|
|
1 |
|
458
|
|
|
|
|
|
|
) |
|
459
|
|
|
|
|
|
|
and |
|
460
|
|
|
|
|
|
|
# |
|
461
|
|
|
|
|
|
|
# last ditch effort to skip examining an alternative mro |
|
462
|
|
|
|
|
|
|
# IFF the entire "foreign" stack is located in the "base isa" |
|
463
|
|
|
|
|
|
|
# |
|
464
|
|
|
|
|
|
|
# This allows for extra efficiency (as there are several |
|
465
|
|
|
|
|
|
|
# with_supers methods that would always be there), but more |
|
466
|
|
|
|
|
|
|
# importantly saves one from tripping on the nonsensical yet |
|
467
|
|
|
|
|
|
|
# begrudgingly functional (as in - no adverse effects): |
|
468
|
|
|
|
|
|
|
# |
|
469
|
|
|
|
|
|
|
# use base 'DBIx::Class'; |
|
470
|
|
|
|
|
|
|
# use base 'DBIx::Class::Schema'; |
|
471
|
|
|
|
|
|
|
# |
|
472
|
|
|
|
|
|
|
( |
|
473
|
|
|
|
|
|
|
grep { |
|
474
|
|
|
|
|
|
|
# not ours |
|
475
|
31
|
100
|
|
|
|
50
|
$_->{via_class} ne $class |
|
476
|
|
|
|
|
|
|
and |
|
477
|
|
|
|
|
|
|
# not from the base stack either |
|
478
|
|
|
|
|
|
|
! $base_ISA->{$_->{via_class}} |
|
479
|
62
|
50
|
|
|
|
233
|
} @$_ |
|
480
|
|
|
|
|
|
|
) |
|
481
|
|
|
|
|
|
|
} |
|
482
|
7
|
100
|
66
|
|
|
79
|
values %{ $desc->{methods_with_supers} } |
|
|
5
|
|
66
|
|
|
23
|
|
|
483
|
|
|
|
|
|
|
); |
|
484
|
|
|
|
|
|
|
|
|
485
|
1
|
|
|
|
|
2
|
my $affected_methods; |
|
486
|
|
|
|
|
|
|
|
|
487
|
1
|
|
|
|
|
3
|
for my $stack (@potentially_problematic_method_stacks) { |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# If we got so far - we need to see what the class would look |
|
490
|
|
|
|
|
|
|
# like under c3 and compare, sigh |
|
491
|
|
|
|
|
|
|
# |
|
492
|
|
|
|
|
|
|
# Note that if the hierarchy is *really* fucked (like the above |
|
493
|
|
|
|
|
|
|
# double-base e.g.) then recalc under 'c3' WILL FAIL, hence the |
|
494
|
|
|
|
|
|
|
# extra eval: if we fail we report things as "jumbled up" |
|
495
|
|
|
|
|
|
|
# |
|
496
|
|
|
|
|
|
|
$affected_methods->{$stack->[0]{name}} = [ |
|
497
|
7
|
|
|
|
|
38
|
map { $_->{via_class} } @$stack |
|
498
|
|
|
|
|
|
|
] unless dbic_internal_try { |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
serialize($stack) |
|
501
|
|
|
|
|
|
|
eq |
|
502
|
|
|
|
|
|
|
serialize( |
|
503
|
|
|
|
|
|
|
describe_class_methods({ class => $class, use_mro => 'c3' }) |
|
504
|
|
|
|
|
|
|
->{methods} |
|
505
|
|
|
|
|
|
|
->{$stack->[0]{name}} |
|
506
|
|
|
|
|
|
|
) |
|
507
|
10
|
100
|
|
10
|
|
71
|
}; |
|
|
10
|
|
|
|
|
24
|
|
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
push @err, { |
|
511
|
|
|
|
|
|
|
class => $class, |
|
512
|
|
|
|
|
|
|
initial_linear_isa => $desc->{linear_isa}, |
|
513
|
1
|
|
|
|
|
3
|
current_linear_isa => do { (undef, my @isa) = @{ mro::get_linear_isa($class) }; \@isa }, |
|
|
1
|
|
|
|
|
9
|
|
|
|
1
|
|
|
|
|
18
|
|
|
514
|
|
|
|
|
|
|
initial_mro => $desc->{mro}{type}, |
|
515
|
1
|
50
|
|
|
|
7
|
current_mro => mro::get_mro($class), |
|
516
|
|
|
|
|
|
|
affected_methods => $affected_methods, |
|
517
|
|
|
|
|
|
|
} if $affected_methods; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
2
|
|
|
|
|
20
|
\@err; |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
sub format_no_inheritance_crosscontamination_errors { |
|
525
|
|
|
|
|
|
|
# my ($self, $errors) = @_; |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
[ map { sprintf( |
|
528
|
|
|
|
|
|
|
"Class '%s' registered in the role of '%s' unexpectedly inherits '%s': " |
|
529
|
|
|
|
|
|
|
. 'you must resolve this by either removing an erroneous `use base` call ' |
|
530
|
|
|
|
|
|
|
. "or switching to Moo(se)-style delegation (i.e. the 'handles' keyword)", |
|
531
|
|
|
|
|
|
|
$_->{class}, |
|
532
|
|
|
|
|
|
|
$_->{type}, |
|
533
|
|
|
|
|
|
|
$_->{unexpectedly_inherits}, |
|
534
|
0
|
|
|
0
|
0
|
0
|
)} @{ $_[1] } ] |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
sub check_no_inheritance_crosscontamination { |
|
538
|
2
|
|
|
2
|
0
|
9
|
my ($self, $schema) = @_; |
|
539
|
|
|
|
|
|
|
|
|
540
|
2
|
|
|
|
|
5
|
my @err; |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
my $to_check = { |
|
543
|
|
|
|
|
|
|
Schema => [ $schema ], |
|
544
|
|
|
|
|
|
|
Storage => [ $schema->storage ], |
|
545
|
2
|
|
|
|
|
125
|
ResultSource => [ map { $schema->source($_) } $schema->sources ], |
|
|
1
|
|
|
|
|
24
|
|
|
546
|
|
|
|
|
|
|
}; |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
$to_check->{ResultSet} = [ |
|
549
|
2
|
|
|
|
|
22
|
map { $_->resultset_class } @{$to_check->{ResultSource}} |
|
|
1
|
|
|
|
|
17
|
|
|
|
2
|
|
|
|
|
8
|
|
|
550
|
|
|
|
|
|
|
]; |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
$to_check->{Core} = [ |
|
553
|
2
|
|
|
|
|
6
|
map { $_->result_class } @{$to_check->{ResultSource}} |
|
|
1
|
|
|
|
|
16
|
|
|
|
2
|
|
|
|
|
8
|
|
|
554
|
|
|
|
|
|
|
]; |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# Reduce everything to a unique sorted list of class names |
|
557
|
|
|
|
|
|
|
$_ = [ sort( uniq( map { |
|
558
|
7
|
100
|
|
|
|
41
|
( not defined $_ ) ? () |
|
|
|
50
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
: ( defined blessed $_ ) ? ref $_ |
|
560
|
|
|
|
|
|
|
: $_ |
|
561
|
2
|
|
|
|
|
10
|
} @$_ ) ) ] for values %$to_check; |
|
562
|
|
|
|
|
|
|
|
|
563
|
2
|
|
|
|
|
10
|
for my $group ( sort keys %$to_check ) { |
|
564
|
10
|
|
|
|
|
14
|
for my $class ( @{ $to_check->{$group} } ) { |
|
|
10
|
|
|
|
|
19
|
|
|
565
|
7
|
|
|
|
|
15
|
for my $foreign_base ( |
|
566
|
28
|
|
|
|
|
68
|
map { "DBIx::Class::$_" } sort grep { $_ ne $group } keys %$to_check |
|
|
35
|
|
|
|
|
66
|
|
|
567
|
|
|
|
|
|
|
) { |
|
568
|
|
|
|
|
|
|
|
|
569
|
28
|
0
|
|
|
|
170
|
push @err, { |
|
|
|
50
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
class => $class, |
|
571
|
|
|
|
|
|
|
type => ( $group eq 'Core' ? 'ResultClass' : $group ), |
|
572
|
|
|
|
|
|
|
unexpectedly_inherits => $foreign_base |
|
573
|
|
|
|
|
|
|
} if $class->isa($foreign_base); |
|
574
|
|
|
|
|
|
|
} |
|
575
|
|
|
|
|
|
|
} |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
2
|
|
|
|
|
14
|
\@err; |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
1; |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
__END__ |