Expand annotations to cover all generated methods
[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 |
162 generated_from_resultsource_metadata
163 |
164 (?: inflated_ | filtered_ )? column_ (?: extra_)? accessor
165 |
166 single_relationship_accessor
167 |
168 (?: multi | filter ) _relationship_ (?: extra_ )? accessor
169 |
170 proxy_to_relationship
171 |
172 m2m_ (?: extra_)? sugar (?:_with_attrs)?
1b822bd3 173 ) $/x;
5f48fa56 174}
175
176sub FETCH_CODE_ATTRIBUTES {
5ab72593 177 #my ($class,$code) = @_;
178
179 sort(
180 @{ $_[0]->_attr_cache->{$_[1]} || [] },
181 ( defined( $attr_cref_registry->{$_[1]}{ weakref } )
182 ? @{ $attr_cref_registry->{$_[1]}{attrs} || [] }
183 : ()
184 ),
185 )
5f48fa56 186}
187
188sub _attr_cache {
189 my $self = shift;
190 +{
191 %{ $self->can('__attr_cache') ? $self->__attr_cache : {} },
192 %{ $self->maybe::next::method || {} },
193 };
194}
195
1961;
5ab72593 197
198__END__
199
200=head1 NAME
201
202DBIx::Class::MethodAttributes - DBIC-specific handling of CODE attributes
203
204=head1 SYNOPSIS
205
206 my @attrlist = attributes::get( \&My::App::Schema::Result::some_method )
207
208=head1 DESCRIPTION
209
210This class provides the L<DBIx::Class> inheritance chain with the bits
211necessary for L<attribute|attributes> support on methods.
212
213Historically DBIC has accepted any string as a C<CODE> attribute and made
214such strings available via the semi-private L</_attr_cache> method. This
215was used for e.g. the long-deprecated L<DBIx::Class::ResultSetManager>,
216but also has evidence of use on both C<CPAN> and C<DarkPAN>.
217
218Starting mid-2016 DBIC treats any method attribute starting with C<DBIC_>
219as an I<internal boolean decorator> for various DBIC-related methods.
220Unlike the general attribute naming policy, strict whitelisting is imposed
221on attribute names starting with C<DBIC_> as described in
222L</VALID_DBIC_CODE_ATTRIBUTE> below.
223
224=head2 DBIC-specific method attributes
225
226The following method attributes are currently recognized under the C<DBIC_*>
227prefix:
228
1b822bd3 229=head3 DBIC_method_is_indirect_sugar
5ab72593 230
1b822bd3 231The presence of this attribute indicates a helper "sugar" method. Overriding
232such methods in your subclasses will be of limited success at best, as DBIC
233itself and various plugins are much more likely to invoke alternative direct
234call paths, bypassing your override entirely. Good examples of this are
235L<DBIx::Class::ResultSet/create> and L<DBIx::Class::Schema/connect>.
5ab72593 236
12e7015a 237See also the check
238L<DBIx::Class::Schema::SanityChecker/no_indirect_method_overrides>.
239
09d8fb4a 240=head3 DBIC_method_is_generated_from_resultsource_metadata
241
242This attribute is applied to all methods dynamically installed after various
243invocations of L<ResultSource metadata manipulation
244methods|DBIx::Class::Manual::ResultClass/DBIx::Class::ResultSource>. Notably
245this includes L<add_columns|DBIx::Class::ResultSource/add_columns>,
246L<add_relationship|DBIx::Class::ResultSource/add_relationship>,
247L<the proxied relationship attribute|DBIx::Class::Relationship::Base/proxy>
248and the various L<relationship
249helpers|DBIx::Class::Manual::ResultClass/DBIx::Class::Relationship>,
250B<except> the L<M2M helper|DBIx::Class::Relationship/many_to_many> (given its
251effects are never reflected as C<ResultSource metadata>).
252
253=head3 DBIC_method_is_column_accessor
254
255This attribute is applied to all methods dynamically installed as a result of
256invoking L<add_columns|DBIx::Class::ResultSource/add_columns>.
257
258=head3 DBIC_method_is_inflated_column_accessor
259
260This attribute is applied to all methods dynamically installed as a result of
261invoking L<inflate_column|DBIx::Class::InflateColumn/inflate_column>.
262
263=head3 DBIC_method_is_filtered_column_accessor
264
265This attribute is applied to all methods dynamically installed as a result of
266invoking L<filter_column|DBIx::Class::FilterColumn/filter_column>.
267
268=head3 DBIC_method_is_*column_extra_accessor
269
270For historical reasons any L<Class::Accessor::Grouped> accessor is generated
271twice as C<{name}> and C<_{name}_accessor>. The second method is marked with
272C<DBIC_method_is_*column_extra_accessor> correspondingly.
273
274=head3 DBIC_method_is_single_relationship_accessor
275
276This attribute is applied to all methods dynamically installed as a result of
277invoking L<might_have|DBIx::Class::Relationship/might_have>,
278L<has_one|DBIx::Class::Relationship/has_one> or
279L<belongs_to|DBIx::Class::Relationship/belongs_to> (though for C<belongs_to>
280see L<...filter_rel...|/DBIC_method_is_filter_relationship_accessor> below.
281
282=head3 DBIC_method_is_multi_relationship_accessor
283
284This attribute is applied to the main method dynamically installed as a result
285of invoking L<has_many|DBIx::Class::Relationship/has_many>.
286
287=head3 DBIC_method_is_multi_relationship_extra_accessor
288
289This attribute is applied to the two extra methods dynamically installed as a
290result of invoking L<has_many|DBIx::Class::Relationship/has_many>:
291C<$relname_rs> and C<add_to_$relname>.
292
293=head3 DBIC_method_is_filter_relationship_accessor
294
295This attribute is applied to (legacy) methods dynamically installed as a
296result of invoking L<belongs_to|DBIx::Class::Relationship/belongs_to> with an
297already-existing identically named column. The method is internally
298implemented as an L<inflated_column|/DBIC_method_is_inflated_column_accessor>
299and is labeled with both atributes at the same time.
300
301=head3 DBIC_method_is_filter_relationship_extra_accessor
302
303Same as L</DBIC_method_is_*column_extra_accessor>.
304
305=head3 DBIC_method_is_proxy_to_relationship
306
307This attribute is applied to methods dynamically installed as a result of
308providing L<the proxied relationship
309attribute|DBIx::Class::Relationship::Base/proxy>.
310
311=head3 DBIC_method_is_m2m_sugar
312
313=head3 DBIC_method_is_m2m_sugar_with_attrs
314
315One of the above attributes is applied to the main method dynamically
316installed as a result of invoking
317L<many_to_many|DBIx::Class::Relationship/many_to_many>. The C<_with_atrs> suffix
318serves to indicate whether the user supplied any C<\%attrs> to the
319C<many_to_many> call. There is deliberately no mechanism to retrieve the actual
320supplied values: if you really need this functionality you would need to rely on
321L<DBIx::Class::IntrospectableM2M>.
322
323=head3 DBIC_method_is_extra_m2m_sugar
324
325=head3 DBIC_method_is_extra_m2m_sugar_with_attrs
326
327One of the above attributes is applied to the extra B<four> methods dynamically
328installed as a result of invoking
329L<many_to_many|DBIx::Class::Relationship/many_to_many>: C<$m2m_rs>, C<add_to_$m2m>,
330C<remove_from_$m2m> and C<set_$m2m>.
331
5ab72593 332=head1 METHODS
333
334=head2 MODIFY_CODE_ATTRIBUTES
335
336See L<attributes/MODIFY_type_ATTRIBUTES>.
337
338=head2 FETCH_CODE_ATTRIBUTES
339
340See L<attributes/FETCH_type_ATTRIBUTES>. Always returns the combination of
341all attributes: both the free-form strings registered via the
342L<legacy system|/_attr_cache> and the DBIC-specific ones.
343
344=head2 VALID_DBIC_CODE_ATTRIBUTE
345
346=over
347
348=item Arguments: $attribute_string
349
350=item Return Value: ( true| false )
351
352=back
353
354This method is invoked when processing each DBIC-specific attribute (the ones
355starting with C<DBIC_>). An attribute is considered invalid and an exception
356is thrown unless this method returns a C<truthy> value.
357
358=head2 _attr_cache
359
360=over
361
362=item Arguments: none
363
364=item Return Value: B<purposefully undocumented>
365
366=back
367
368The legacy method of retrieving attributes declared on DBIC methods
369(L</FETCH_CODE_ATTRIBUTES> was not defined until mid-2016). This method
370B<does not return any DBIC-specific attributes>, and is kept for backwards
371compatibility only.
372
373In order to query the attributes of a particular method use
374L<attributes::get()|attributes/get> as shown in the L</SYNOPSIS>.
375
376=head1 FURTHER QUESTIONS?
377
378Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
379
380=head1 COPYRIGHT AND LICENSE
381
382This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
383by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
384redistribute it and/or modify it under the same terms as the
385L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.