Prevent invisible skipping of ResultSource proxy overrides
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / MethodAttributes.pm
CommitLineData
5ab72593 1package DBIx::Class::MethodAttributes;
5f48fa56 2
3use strict;
4use warnings;
5
6use DBIx::Class::_Util qw( uniq refdesc visit_namespaces );
7use Scalar::Util qw( weaken refaddr );
8
5f48fa56 9use namespace::clean;
10
5ab72593 11my ( $attr_cref_registry, $attr_cache_active );
5f48fa56 12sub DBIx::Class::__Attr_iThreads_handler__::CLONE {
13
14 # This is disgusting, but the best we can do without even more surgery
5ab72593 15 # Note the if() at the end - we do not run this crap if we can help it
5f48fa56 16 visit_namespaces( action => sub {
17 my $pkg = shift;
18
19 # skip dangerous namespaces
20 return 1 if $pkg =~ /^ (?:
21 DB | next | B | .+? ::::ISA (?: ::CACHE ) | Class::C3
22 ) $/x;
23
24 no strict 'refs';
25
26 if (
27 exists ${"${pkg}::"}{__cag___attr_cache}
28 and
29 ref( my $attr_stash = ${"${pkg}::__cag___attr_cache"} ) eq 'HASH'
30 ) {
31 $attr_stash->{ $attr_cref_registry->{$_}{weakref} } = delete $attr_stash->{$_}
32 for keys %$attr_stash;
33 }
34
35 return 1;
5ab72593 36 }) if $attr_cache_active;
5f48fa56 37
38 # renumber the cref registry itself
39 %$attr_cref_registry = map {
40 ( defined $_->{weakref} )
41 ? (
42 # because of how __attr_cache works, ugh
43 "$_->{weakref}" => $_,
44 )
45 : ()
46 } values %$attr_cref_registry;
47}
48
49sub MODIFY_CODE_ATTRIBUTES {
5ab72593 50 my $class = shift;
51 my $code = shift;
52
53 my $attrs;
54 $attrs->{
55 $_ =~ /^[a-z]+$/ ? 'builtin'
56 : $_ =~ /^DBIC_/ ? 'dbic'
57 : 'misc'
58 }{$_}++ for @_;
59
5f48fa56 60
61 # compaction step
62 defined $attr_cref_registry->{$_}{weakref} or delete $attr_cref_registry->{$_}
63 for keys %$attr_cref_registry;
64
65 # The original misc-attr API used stringification instead of refaddr - can't change that now
66 if( $attr_cref_registry->{$code} ) {
67 Carp::confess( sprintf
68 "Coderefs '%s' and '%s' stringify to the same value '%s': nothing will work",
69 refdesc($code),
70 refdesc($attr_cref_registry->{$code}{weakref}),
71 "$code"
72 ) if refaddr($attr_cref_registry->{$code}{weakref}) != refaddr($code);
73 }
74 else {
75 weaken( $attr_cref_registry->{$code}{weakref} = $code )
76 }
77
296248c3 78
79 # increment the pkg gen, this ensures the sanity checkers will re-evaluate
80 # this class when/if the time comes
81 mro::method_changed_in($class) if (
82 ! DBIx::Class::_ENV_::OLD_MRO
83 and
84 ( $attrs->{dbic} or $attrs->{misc} )
85 );
86
87
5ab72593 88 # handle legacy attrs
89 if( $attrs->{misc} ) {
90
91 # if the user never tickles this - we won't have to do a gross
92 # symtable scan in the ithread handler above, so:
93 #
94 # User - please don't tickle this
95 $attr_cache_active = 1;
96
97 $class->mk_classaccessor('__attr_cache' => {})
98 unless $class->can('__attr_cache');
99
100 $class->__attr_cache->{$code} = [ sort( uniq(
101 @{ $class->__attr_cache->{$code} || [] },
102 keys %{ $attrs->{misc} },
103 ))];
104 }
105
296248c3 106
5ab72593 107 # handle DBIC_* attrs
108 if( $attrs->{dbic} ) {
109 my $slot = $attr_cref_registry->{$code};
110
111 $slot->{attrs} = [ uniq
112 @{ $slot->{attrs} || [] },
113 grep {
114 $class->VALID_DBIC_CODE_ATTRIBUTE($_)
115 or
116 Carp::confess( "DBIC-specific attribute '$_' did not pass validation by $class->VALID_DBIC_CODE_ATTRIBUTE() as described in DBIx::Class::MethodAttributes" )
117 } keys %{$attrs->{dbic}},
118 ];
119 }
5f48fa56 120
296248c3 121
5f48fa56 122 # FIXME - DBIC essentially gobbles up any attribute it can lay its hands on:
123 # decidedly not cool
124 #
125 # There should be some sort of warning on unrecognized attributes or
126 # somesuch... OTOH people do use things in the wild hence the plan of action
127 # is anything but clear :/
128 #
129 # https://metacpan.org/source/ZIGOROU/DBIx-Class-Service-0.02/lib/DBIx/Class/Service.pm#L93-110
130 # https://metacpan.org/source/ZIGOROU/DBIx-Class-Service-0.02/t/lib/DBIC/Test/Service/User.pm#L29
131 # https://metacpan.org/source/ZIGOROU/DBIx-Class-Service-0.02/t/lib/DBIC/Test/Service/User.pm#L36
132 #
5ab72593 133 # For the time being reuse the old logic for any attribute we do not have
134 # explicit plans for (i.e. stuff that is neither reserved, nor DBIC-internal)
135 #
136 # Pass the "builtin attrs" onwards, as the DBIC internals can't possibly handle them
137 return sort keys %{ $attrs->{builtin} || {} };
138}
139
140# Address the above FIXME halfway - if something (e.g. DBIC::Helpers) wants to
141# add extra attributes - it needs to override this in its base class to allow
142# for 'return 1' on the newly defined attributes
143sub VALID_DBIC_CODE_ATTRIBUTE {
144 #my ($class, $attr) = @_;
145
1b822bd3 146###
147### !!! IMPORTANT !!!
148###
149### *DO NOT* yield to the temptation of using free-form-argument attributes.
150### The technique was proven instrumental in Catalyst a decade ago, and
151### was more recently revived in Sub::Attributes. Yet, while on the surface
152### they seem immensely useful, per-attribute argument lists are in fact an
153### architectural dead end.
154###
155### In other words: you are *very strongly urged* to ensure the regex below
156### does not allow anything beyond qr/^ DBIC_method_is_ [A-Z_a-z0-9]+ $/x
157###
158
159 $_[1] =~ /^ DBIC_method_is_ (?:
160 indirect_sugar
09d8fb4a 161 |
28ef9468 162 (?: bypassable | mandatory ) _resultsource_proxy
163 |
09d8fb4a 164 generated_from_resultsource_metadata
165 |
166 (?: inflated_ | filtered_ )? column_ (?: extra_)? accessor
167 |
168 single_relationship_accessor
169 |
170 (?: multi | filter ) _relationship_ (?: extra_ )? accessor
171 |
172 proxy_to_relationship
173 |
174 m2m_ (?: extra_)? sugar (?:_with_attrs)?
1b822bd3 175 ) $/x;
5f48fa56 176}
177
178sub FETCH_CODE_ATTRIBUTES {
5ab72593 179 #my ($class,$code) = @_;
180
181 sort(
182 @{ $_[0]->_attr_cache->{$_[1]} || [] },
183 ( defined( $attr_cref_registry->{$_[1]}{ weakref } )
184 ? @{ $attr_cref_registry->{$_[1]}{attrs} || [] }
185 : ()
186 ),
187 )
5f48fa56 188}
189
190sub _attr_cache {
191 my $self = shift;
192 +{
193 %{ $self->can('__attr_cache') ? $self->__attr_cache : {} },
194 %{ $self->maybe::next::method || {} },
195 };
196}
197
1981;
5ab72593 199
200__END__
201
202=head1 NAME
203
204DBIx::Class::MethodAttributes - DBIC-specific handling of CODE attributes
205
206=head1 SYNOPSIS
207
208 my @attrlist = attributes::get( \&My::App::Schema::Result::some_method )
209
210=head1 DESCRIPTION
211
212This class provides the L<DBIx::Class> inheritance chain with the bits
213necessary for L<attribute|attributes> support on methods.
214
215Historically DBIC has accepted any string as a C<CODE> attribute and made
216such strings available via the semi-private L</_attr_cache> method. This
217was used for e.g. the long-deprecated L<DBIx::Class::ResultSetManager>,
218but also has evidence of use on both C<CPAN> and C<DarkPAN>.
219
220Starting mid-2016 DBIC treats any method attribute starting with C<DBIC_>
221as an I<internal boolean decorator> for various DBIC-related methods.
222Unlike the general attribute naming policy, strict whitelisting is imposed
223on attribute names starting with C<DBIC_> as described in
224L</VALID_DBIC_CODE_ATTRIBUTE> below.
225
226=head2 DBIC-specific method attributes
227
228The following method attributes are currently recognized under the C<DBIC_*>
229prefix:
230
1b822bd3 231=head3 DBIC_method_is_indirect_sugar
5ab72593 232
1b822bd3 233The presence of this attribute indicates a helper "sugar" method. Overriding
234such methods in your subclasses will be of limited success at best, as DBIC
235itself and various plugins are much more likely to invoke alternative direct
236call paths, bypassing your override entirely. Good examples of this are
237L<DBIx::Class::ResultSet/create> and L<DBIx::Class::Schema/connect>.
5ab72593 238
12e7015a 239See also the check
240L<DBIx::Class::Schema::SanityChecker/no_indirect_method_overrides>.
241
28ef9468 242=head3 DBIC_method_is_mandatory_resultsource_proxy
243
244=head3 DBIC_method_is_bypassable_resultsource_proxy
245
246The presence of one of these attributes on a L<proxied ResultSource
247method|DBIx::Class::Manual::ResultClass/DBIx::Class::ResultSource> indicates
248how DBIC will behave when someone calls e.g.:
249
250 $some_result->result_source->add_columns(...)
251
252as opposed to the conventional
253
254 SomeResultClass->add_columns(...)
255
256This distinction becomes important when someone declares a sub named after
257one of the (currently 22) methods proxied from a
258L<Result|DBIx::Class::Manual::ResultClass> to
259L<ResultSource|DBIx::Class::ResultSource>. While there are obviously no
260problems when these methods are called at compile time, there is a lot of
261ambiguity whether an override of something like
262L<columns_info|DBIx::Class::ResultSource/columns_info> will be respected by
263DBIC and various plugins during runtime operations.
264
265It must be noted that there is a reason for this weird situation: during the
266original design of DBIC the "ResultSourceProxy" system was established in
267order to allow easy transition from Class::DBI. Unfortunately it was not
268well abstracted away: it is rather difficult to use a custom ResultSource
269subclass. The expansion of the DBIC project never addressed this properly
270in the years since. As a result when one wishes to override a part of the
271ResultSource functionality, the overwhelming practice is to hook a method
272in a Result class and "hope for the best".
273
274The subtle changes of various internal call-chains in C<DBIC v0.0829xx> make
275this silent uncertainty untenable. As a solution any such override will now
276issue a descriptive warning that it has been bypassed during a
277C<< $rsrc->overriden_function >> invocation. A user B<must> determine how
278each individual override must behave in this situation, and tag it with one
279of the above two attributes.
280
281Naturally any override marked with C<..._bypassable_resultsource_proxy> will
282behave like it did before: it will be silently ignored. This is the attribute
283you want to set if your code appears to work fine, and you do not wish to
284receive the warning anymore (though you are strongly encouraged to understand
285the other option).
286
287However overrides marked with C<..._mandatory_resultsource_proxy> will always
288be reinvoked by DBIC itself, so that any call of the form:
289
290 $some_result->result_source->columns_info(...)
291
292will be transformed into:
293
294 $some_result->result_source->result_class->columns_info(...)
295
296with the rest of the callchain flowing out of that (provided the override did
297invoke L<next::method|mro/next::method> where appropriate)
298
09d8fb4a 299=head3 DBIC_method_is_generated_from_resultsource_metadata
300
301This attribute is applied to all methods dynamically installed after various
302invocations of L<ResultSource metadata manipulation
303methods|DBIx::Class::Manual::ResultClass/DBIx::Class::ResultSource>. Notably
304this includes L<add_columns|DBIx::Class::ResultSource/add_columns>,
305L<add_relationship|DBIx::Class::ResultSource/add_relationship>,
306L<the proxied relationship attribute|DBIx::Class::Relationship::Base/proxy>
307and the various L<relationship
308helpers|DBIx::Class::Manual::ResultClass/DBIx::Class::Relationship>,
309B<except> the L<M2M helper|DBIx::Class::Relationship/many_to_many> (given its
310effects are never reflected as C<ResultSource metadata>).
311
312=head3 DBIC_method_is_column_accessor
313
314This attribute is applied to all methods dynamically installed as a result of
315invoking L<add_columns|DBIx::Class::ResultSource/add_columns>.
316
317=head3 DBIC_method_is_inflated_column_accessor
318
319This attribute is applied to all methods dynamically installed as a result of
320invoking L<inflate_column|DBIx::Class::InflateColumn/inflate_column>.
321
322=head3 DBIC_method_is_filtered_column_accessor
323
324This attribute is applied to all methods dynamically installed as a result of
325invoking L<filter_column|DBIx::Class::FilterColumn/filter_column>.
326
327=head3 DBIC_method_is_*column_extra_accessor
328
329For historical reasons any L<Class::Accessor::Grouped> accessor is generated
330twice as C<{name}> and C<_{name}_accessor>. The second method is marked with
331C<DBIC_method_is_*column_extra_accessor> correspondingly.
332
333=head3 DBIC_method_is_single_relationship_accessor
334
335This attribute is applied to all methods dynamically installed as a result of
336invoking L<might_have|DBIx::Class::Relationship/might_have>,
337L<has_one|DBIx::Class::Relationship/has_one> or
338L<belongs_to|DBIx::Class::Relationship/belongs_to> (though for C<belongs_to>
339see L<...filter_rel...|/DBIC_method_is_filter_relationship_accessor> below.
340
341=head3 DBIC_method_is_multi_relationship_accessor
342
343This attribute is applied to the main method dynamically installed as a result
344of invoking L<has_many|DBIx::Class::Relationship/has_many>.
345
346=head3 DBIC_method_is_multi_relationship_extra_accessor
347
348This attribute is applied to the two extra methods dynamically installed as a
349result of invoking L<has_many|DBIx::Class::Relationship/has_many>:
350C<$relname_rs> and C<add_to_$relname>.
351
352=head3 DBIC_method_is_filter_relationship_accessor
353
354This attribute is applied to (legacy) methods dynamically installed as a
355result of invoking L<belongs_to|DBIx::Class::Relationship/belongs_to> with an
356already-existing identically named column. The method is internally
357implemented as an L<inflated_column|/DBIC_method_is_inflated_column_accessor>
358and is labeled with both atributes at the same time.
359
360=head3 DBIC_method_is_filter_relationship_extra_accessor
361
362Same as L</DBIC_method_is_*column_extra_accessor>.
363
364=head3 DBIC_method_is_proxy_to_relationship
365
366This attribute is applied to methods dynamically installed as a result of
367providing L<the proxied relationship
368attribute|DBIx::Class::Relationship::Base/proxy>.
369
370=head3 DBIC_method_is_m2m_sugar
371
372=head3 DBIC_method_is_m2m_sugar_with_attrs
373
374One of the above attributes is applied to the main method dynamically
375installed as a result of invoking
376L<many_to_many|DBIx::Class::Relationship/many_to_many>. The C<_with_atrs> suffix
377serves to indicate whether the user supplied any C<\%attrs> to the
378C<many_to_many> call. There is deliberately no mechanism to retrieve the actual
379supplied values: if you really need this functionality you would need to rely on
380L<DBIx::Class::IntrospectableM2M>.
381
382=head3 DBIC_method_is_extra_m2m_sugar
383
384=head3 DBIC_method_is_extra_m2m_sugar_with_attrs
385
386One of the above attributes is applied to the extra B<four> methods dynamically
387installed as a result of invoking
388L<many_to_many|DBIx::Class::Relationship/many_to_many>: C<$m2m_rs>, C<add_to_$m2m>,
389C<remove_from_$m2m> and C<set_$m2m>.
390
5ab72593 391=head1 METHODS
392
393=head2 MODIFY_CODE_ATTRIBUTES
394
395See L<attributes/MODIFY_type_ATTRIBUTES>.
396
397=head2 FETCH_CODE_ATTRIBUTES
398
399See L<attributes/FETCH_type_ATTRIBUTES>. Always returns the combination of
400all attributes: both the free-form strings registered via the
401L<legacy system|/_attr_cache> and the DBIC-specific ones.
402
403=head2 VALID_DBIC_CODE_ATTRIBUTE
404
405=over
406
407=item Arguments: $attribute_string
408
409=item Return Value: ( true| false )
410
411=back
412
413This method is invoked when processing each DBIC-specific attribute (the ones
414starting with C<DBIC_>). An attribute is considered invalid and an exception
415is thrown unless this method returns a C<truthy> value.
416
417=head2 _attr_cache
418
419=over
420
421=item Arguments: none
422
423=item Return Value: B<purposefully undocumented>
424
425=back
426
427The legacy method of retrieving attributes declared on DBIC methods
428(L</FETCH_CODE_ATTRIBUTES> was not defined until mid-2016). This method
429B<does not return any DBIC-specific attributes>, and is kept for backwards
430compatibility only.
431
432In order to query the attributes of a particular method use
433L<attributes::get()|attributes/get> as shown in the L</SYNOPSIS>.
434
435=head1 FURTHER QUESTIONS?
436
437Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
438
439=head1 COPYRIGHT AND LICENSE
440
441This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
442by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
443redistribute it and/or modify it under the same terms as the
444L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.