File Coverage

blib/lib/DBIx/Class/MethodAttributes.pm
Criterion Covered Total %
statement 49 59 83.0
branch 29 42 69.0
condition 4 8 50.0
subroutine 10 12 83.3
pod 1 1 100.0
total 93 122 76.2


line stmt bran cond sub pod time code
1             package DBIx::Class::MethodAttributes;
2              
3 313     313   2418 use strict;
  313         796  
  313         8785  
4 313     313   1693 use warnings;
  313         722  
  313         9155  
5              
6 313     313   1846 use DBIx::Class::_Util qw( uniq refdesc visit_namespaces );
  313         741  
  313         20131  
7 313     313   2067 use Scalar::Util qw( weaken refaddr );
  313         724  
  313         16191  
8              
9 313     313   1982 use namespace::clean;
  313         726  
  313         1783  
10              
11             my ( $attr_cref_registry, $attr_cache_active );
12             sub DBIx::Class::__Attr_iThreads_handler__::CLONE {
13              
14             # This is disgusting, but the best we can do without even more surgery
15             # Note the if() at the end - we do not run this crap if we can help it
16             visit_namespaces( action => sub {
17 0     0   0 my $pkg = shift;
18              
19             # skip dangerous namespaces
20 0 0       0 return 1 if $pkg =~ /^ (?:
21             DB | next | B | .+? ::::ISA (?: ::CACHE ) | Class::C3
22             ) $/x;
23              
24 313     313   106127 no strict 'refs';
  313         958  
  313         266320  
25              
26 0 0 0     0 if (
27 0         0 exists ${"${pkg}::"}{__cag___attr_cache}
28             and
29 0         0 ref( my $attr_stash = ${"${pkg}::__cag___attr_cache"} ) eq 'HASH'
30             ) {
31             $attr_stash->{ $attr_cref_registry->{$_}{weakref} } = delete $attr_stash->{$_}
32 0         0 for keys %$attr_stash;
33             }
34              
35 0         0 return 1;
36 0 0   0   0 }) if $attr_cache_active;
37              
38             # renumber the cref registry itself
39             %$attr_cref_registry = map {
40 0         0 ( defined $_->{weakref} )
41 0 0       0 ? (
42             # because of how __attr_cache works, ugh
43             "$_->{weakref}" => $_,
44             )
45             : ()
46             } values %$attr_cref_registry;
47             }
48              
49             sub MODIFY_CODE_ATTRIBUTES {
50 169096     169096   35824719 my $class = shift;
51 169096         274853 my $code = shift;
52              
53 169096         243538 my $attrs;
54             $attrs->{
55             $_ =~ /^[a-z]+$/ ? 'builtin'
56             : $_ =~ /^DBIC_/ ? 'dbic'
57             : 'misc'
58 169096 100       1789076 }{$_}++ for @_;
    50          
59              
60              
61             # compaction step
62             defined $attr_cref_registry->{$_}{weakref} or delete $attr_cref_registry->{$_}
63 169096   100     38029772 for keys %$attr_cref_registry;
64              
65             # The original misc-attr API used stringification instead of refaddr - can't change that now
66 169096 100       2436977 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 8845 50       48265 ) if refaddr($attr_cref_registry->{$code}{weakref}) != refaddr($code);
73             }
74             else {
75 160251         950933 weaken( $attr_cref_registry->{$code}{weakref} = $code )
76             }
77              
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 169096 50 66     771351 );
86              
87              
88             # handle legacy attrs
89 169096 100       372630 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 4         8 $attr_cache_active = 1;
96              
97 4 100       232 $class->mk_classaccessor('__attr_cache' => {})
98             unless $class->can('__attr_cache');
99              
100             $class->__attr_cache->{$code} = [ sort( uniq(
101 4 100       90 @{ $class->__attr_cache->{$code} || [] },
102 4         30 keys %{ $attrs->{misc} },
  4         138  
103             ))];
104             }
105              
106              
107             # handle DBIC_* attrs
108 169096 100       351911 if( $attrs->{dbic} ) {
109 169093         340304 my $slot = $attr_cref_registry->{$code};
110              
111             $slot->{attrs} = [ uniq
112 169093 100       667871 @{ $slot->{attrs} || [] },
113             grep {
114 341460 100       1343619 $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 169093         253370 } keys %{$attrs->{dbic}},
  169093         593493  
118             ];
119             }
120              
121              
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             #
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 169095 50       363010 return sort keys %{ $attrs->{builtin} || {} };
  169095         1198620  
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
143             sub VALID_DBIC_CODE_ATTRIBUTE {
144             #my ($class, $attr) = @_;
145              
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 341454     341454 1 2116675 $_[1] =~ /^ DBIC_method_is_ (?:
160             indirect_sugar
161             |
162             (?: bypassable | mandatory ) _resultsource_proxy
163             |
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)?
175             ) $/x;
176             }
177              
178             sub FETCH_CODE_ATTRIBUTES {
179             #my ($class,$code) = @_;
180              
181             sort(
182 216393 100       479986 @{ $_[0]->_attr_cache->{$_[1]} || [] },
183             ( defined( $attr_cref_registry->{$_[1]}{ weakref } )
184 216393 100   216393   3345736 ? @{ $attr_cref_registry->{$_[1]}{attrs} || [] }
  20387 100       418919  
185             : ()
186             ),
187             )
188             }
189              
190             sub _attr_cache {
191 216397     216397   307960 my $self = shift;
192             +{
193 216397 100       894738 %{ $self->can('__attr_cache') ? $self->__attr_cache : {} },
194 216397 50       282300 %{ $self->maybe::next::method || {} },
  216397         572514  
195             };
196             }
197              
198             1;
199              
200             __END__