File Coverage

blib/lib/DBIx/DataModel/Meta/Utils.pm
Criterion Covered Total %
statement 86 89 96.6
branch 11 16 68.7
condition 5 12 41.6
subroutine 28 28 100.0
pod 3 4 75.0
total 133 149 89.2


line stmt bran cond sub pod time code
1             package DBIx::DataModel::Meta::Utils;
2 22     22   148 use strict;
  22         40  
  22         850  
3 22     22   106 use warnings;
  22         41  
  22         1069  
4              
5 22     22   119 use strict;
  22         38  
  22         655  
6 22     22   95 use warnings;
  22         73  
  22         1117  
7              
8 22     22   11181 use DBIx::DataModel::Carp;
  22         117  
  22         193  
9 22     22   1524 use Module::Load qw/load/;
  22         71  
  22         2639  
10 22         2427 use Params::Validate qw/validate_with SCALAR ARRAYREF CODEREF
11 22     22   15086 BOOLEAN OBJECT HASHREF/;
  22         204651  
12 22     22   13778 use List::MoreUtils qw/any/;
  22         393861  
  22         178  
13 22     22   31291 use mro qw/c3/;
  22         52  
  22         188  
14 22     22   18788 use SQL::Abstract::More 1.39;
  22         496366  
  22         158  
15              
16             # utility function 'does' imported by hand because not really meant
17             # to be publicly exportable from SQL::Abstract::More
18 22     22   289052 BEGIN {no strict 'refs'; *does = \&SQL::Abstract::More::does;}
  22     22   49  
  22         1000  
  22         616  
19              
20 22     22   118 use Exporter qw/import/;
  22         44  
  22         5918  
21             our @EXPORT = qw/define_class define_method
22             define_readonly_accessors define_abstract_methods
23             does/;
24              
25              
26              
27              
28              
29             my %seen_class_method;
30              
31             sub _check_call_as_class_method {
32 1172     1172   2148 my $first_arg = $_[0];
33              
34 1172 50 33     9938 if ($first_arg && !ref $first_arg && $first_arg->isa(__PACKAGE__) ) {
      33        
35 0         0 my $func = (caller(1))[3];
36             carp "calling $func() as class method is obsolete; import and call as a function"
37 0 0       0 unless $seen_class_method{$func}++;
38 0         0 shift @_;
39             }
40             }
41              
42              
43              
44             sub define_class {
45 110     110 1 4971 &_check_call_as_class_method;
46              
47             # check parameters
48 110         3793 my %params = validate_with(
49             params => \@_,
50             spec => {
51             name => {type => SCALAR },
52             isa => {type => ARRAYREF},
53             metadm => {isa => 'DBIx::DataModel::Meta'},
54             },
55             allow_extra => 0,
56             );
57              
58             # deactivate strict refs because we'll be playing with symbol tables
59 22     22   164 no strict 'refs';
  22         49  
  22         9062  
60              
61             # make sure that all parents are defined
62 110         793 foreach my $parent (@{$params{isa}}) {
  110         375  
63              
64             # heuristics to decide if a class is loaded (can't rely on %INC)
65 198     188   599 my $is_class_defined = any {! /::$/} keys %{$parent.'::'};
  188         617  
  198         1989  
66             # NOTE : we need to exclude symbols ending with '::' because
67             # "require Foo::Bar::Buz" will define ${Foo::Bar::}{'Buz::'} at
68             # compilation time, even if this statement is never executed.
69              
70             # try to load parent if needed
71 198 100       953 load $parent unless $is_class_defined;
72             };
73              
74             # inject parents into @ISA
75 110         496 my $class_isa = $params{name}."::ISA";
76 110 100       275 not @{$class_isa} or croak "won't overwrite \@$class_isa";
  110         1056  
77 108         202 @{$class_isa} = @{$params{isa}};
  108         2255  
  108         327  
78              
79             # use mro 'c3' in that package
80 108         796 mro::set_mro($params{name}, 'c3');
81              
82             # install an accessor to the metaclass object within the package
83             define_method(class => $params{name},
84             name => 'metadm',
85 3567     3567   25148 body => sub {return $params{metadm}},
86 108         704 check_override => 0, );
87             }
88              
89              
90             sub define_method {
91 970     970 1 2222 &_check_call_as_class_method;
92              
93             # check parameters
94 970         26436 my %params = validate_with(
95             params => \@_,
96             spec => {
97             class => {type => SCALAR },
98             name => {type => SCALAR },
99             body => {type => CODEREF },
100             check_override => {type => BOOLEAN, default => 1},
101             },
102             allow_extra => 0,
103             );
104              
105             # fully qualified name
106 970         7659 my $full_method_name = $params{class}.'::'.$params{name};
107              
108             # deactiveate strict refs because we'll be playing with symbol tables
109 22     22   191 no strict 'refs';
  22         58  
  22         10655  
110              
111             # check if method is already there
112 970 50       1399 not defined(&{$full_method_name})
  970         5845  
113             or croak "method $full_method_name is already defined";
114              
115             # check if there is a conflict with an inherited method
116             !$params{check_override} or not $params{class}->can($params{name})
117 970 50 66     7731 or carp "method $params{name} in $params{class} will be overridden";
118              
119             # install the method
120 970         1647 *{$full_method_name} = $params{body};
  970         5476  
121             }
122              
123              
124             sub define_readonly_accessors {
125 92     92 1 381 &_check_call_as_class_method;
126              
127 92         431 my ($target_class, @accessors) = @_;
128              
129 92         219 foreach my $accessor (@accessors) {
130             define_method(
131             class => $target_class,
132             name => $accessor,
133 5015     5015   6692 body => sub { my $self = shift;
        5015      
        5015      
        4886      
        2724      
134 5015         8344 my $val = $self->{$accessor};
135 5015         9781 for (ref $val) {
136 5015 100       11479 /^ARRAY$/ and return @$val;
137 4512 100       7537 /^HASH$/ and return %$val;
138 4447         16578 return $val; # otherwise
139             }
140             },
141 717         3409 );
142             }
143             }
144              
145             sub define_abstract_methods {
146 29     29 0 120 my ($target_class, @methods) = @_;
147              
148 29         93 foreach my $method (@methods) {
149             define_method(
150             class => $target_class,
151             name => $method,
152 1     1   19 body => sub { my $self = shift;
153 1   33     6 my $subclass = ref $self || $self;
154 1         12 die "$subclass should implement a $method() method, as required by $target_class";
155             },
156 49         247 );
157             }
158             }
159              
160              
161              
162             1;
163              
164             __END__