File Coverage

blib/lib/DBIx/DataModel/Meta/Utils.pm
Criterion Covered Total %
statement 89 92 96.7
branch 11 16 68.7
condition 5 12 41.6
subroutine 25 25 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 18     18   110 use strict;
  18         32  
  18         446  
3 18     18   78 use warnings;
  18         28  
  18         391  
4              
5 18     18   82 use strict;
  18         28  
  18         269  
6 18     18   75 use warnings;
  18         36  
  18         465  
7              
8 18     18   96 use Carp;
  18         28  
  18         1149  
9 18     18   7494 use Module::Load qw/load/;
  18         16955  
  18         91  
10 18         1253 use Params::Validate qw/validate_with SCALAR ARRAYREF CODEREF
11 18     18   7407 BOOLEAN OBJECT HASHREF/;
  18         109820  
12 18     18   8662 use List::MoreUtils qw/any/;
  18         206736  
  18         102  
13 18     18   16176 use mro qw/c3/;
  18         39  
  18         122  
14 18     18   10021 use SQL::Abstract::More 1.39;
  18         268026  
  18         115  
15 18     18   168288 use Carp::Clan qw[^(DBIx::DataModel::|SQL::Abstract)];
  18         46515  
  18         128  
16              
17             # utility function 'does' imported by hand because not really meant
18             # to be publicly exportable from SQL::Abstract::More
19 18     18   1847 BEGIN {no strict 'refs'; *does = \&SQL::Abstract::More::does;}
  18     18   44  
  18         478  
  18         359  
20              
21 18     18   92 use Exporter qw/import/;
  18         30  
  18         2998  
22             our @EXPORT = qw/define_class define_method
23             define_readonly_accessors define_abstract_methods
24             does/;
25              
26              
27              
28              
29              
30             my %seen_class_method;
31              
32             sub _check_call_as_class_method {
33 968     968   1360 my $first_arg = $_[0];
34              
35 968 50 33     6078 if ($first_arg && !ref $first_arg && $first_arg->isa(__PACKAGE__) ) {
      33        
36 0         0 my $func = (caller(1))[3];
37             carp "calling $func() as class method is obsolete; import and call as a function"
38 0 0       0 unless $seen_class_method{$func}++;
39 0         0 shift @_;
40             }
41             }
42              
43              
44              
45             sub define_class {
46 98     98 1 2803 &_check_call_as_class_method;
47              
48             # check parameters
49 98         2399 my %params = validate_with(
50             params => \@_,
51             spec => {
52             name => {type => SCALAR },
53             isa => {type => ARRAYREF},
54             metadm => {isa => 'DBIx::DataModel::Meta'},
55             },
56             allow_extra => 0,
57             );
58              
59             # deactivate strict refs because we'll be playing with symbol tables
60 18     18   126 no strict 'refs';
  18         34  
  18         5005  
61              
62             # make sure that all parents are defined
63 98         494 foreach my $parent (@{$params{isa}}) {
  98         289  
64              
65             # heuristics to decide if a class is loaded (can't rely on %INC)
66 178     169   479 my $is_class_defined = any {! /::$/} keys %{$parent.'::'};
  169         385  
  178         1078  
67             # NOTE : we need to exclude symbols ending with '::' because
68             # "require Foo::Bar::Buz" will define ${Foo::Bar::}{'Buz::'} at
69             # compilation time, even if this statement is never executed.
70              
71             # try to load parent if needed
72 178 100       683 load $parent unless $is_class_defined;
73             };
74              
75             # inject parents into @ISA
76 98         388 my $class_isa = $params{name}."::ISA";
77 98 100       123 not @{$class_isa} or croak "won't overwrite \@$class_isa";
  98         546  
78 96         173 @{$class_isa} = @{$params{isa}};
  96         1717  
  96         190  
79              
80             # use mro 'c3' in that package
81 96         632 mro::set_mro($params{name}, 'c3');
82              
83             # install an accessor to the metaclass object within the package
84             define_method(class => $params{name},
85             name => 'metadm',
86 3026     3026   17159 body => sub {return $params{metadm}},
87 96         472 check_override => 0, );
88             }
89              
90              
91             sub define_method {
92 790     790 1 1544 &_check_call_as_class_method;
93              
94             # check parameters
95 790         16447 my %params = validate_with(
96             params => \@_,
97             spec => {
98             class => {type => SCALAR },
99             name => {type => SCALAR },
100             body => {type => CODEREF },
101             check_override => {type => BOOLEAN, default => 1},
102             },
103             allow_extra => 0,
104             );
105              
106             # fully qualified name
107 790         4447 my $full_method_name = $params{class}.'::'.$params{name};
108              
109             # deactiveate strict refs because we'll be playing with symbol tables
110 18     18   147 no strict 'refs';
  18         30  
  18         5811  
111              
112             # check if method is already there
113 790 50       1017 not defined(&{$full_method_name})
  790         3272  
114             or croak "method $full_method_name is already defined";
115              
116             # check if there is a conflict with an inherited method
117             !$params{check_override} or not $params{class}->can($params{name})
118 790 50 66     4596 or carp "method $params{name} in $params{class} will be overridden";
119              
120             # install the method
121 790         1246 *{$full_method_name} = $params{body};
  790         3198  
122             }
123              
124              
125             sub define_readonly_accessors {
126 80     80 1 243 &_check_call_as_class_method;
127              
128 80         306 my ($target_class, @accessors) = @_;
129              
130 80         166 foreach my $accessor (@accessors) {
131             define_method(
132             class => $target_class,
133             name => $accessor,
134 4442     4442   5365 body => sub { my $self = shift;
135 4442         6154 my $val = $self->{$accessor};
136 4442         7029 for (ref $val) {
137 4442 100       8624 /^ARRAY$/ and return @$val;
138 3975 100       5793 /^HASH$/ and return %$val;
139 3916         10414 return $val; # otherwise
140             }
141             },
142 566         1980 );
143             }
144             }
145              
146             sub define_abstract_methods {
147 24     24 0 101 my ($target_class, @methods) = @_;
148              
149 24         70 foreach my $method (@methods) {
150             define_method(
151             class => $target_class,
152             name => $method,
153 1     1   16 body => sub { my $self = shift;
154 1   33     4 my $subclass = ref $self || $self;
155 1         10 die "$subclass should implement a $method() method, as required by $target_class";
156             },
157 41         202 );
158             }
159             }
160              
161              
162              
163             1;
164              
165             __END__