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__ |