File Coverage

blib/lib/DBIx/Class/Candy.pm
Criterion Covered Total %
statement 167 176 94.8
branch 23 36 63.8
condition 2 5 40.0
subroutine 33 33 100.0
pod 0 15 0.0
total 225 265 84.9


line stmt bran cond sub pod time code
1             package DBIx::Class::Candy;
2             $DBIx::Class::Candy::VERSION = '0.005004';
3 5     5   1312412 use strict;
  5         11  
  5         186  
4 5     5   28 use warnings;
  5         9  
  5         333  
5              
6 5     5   1078 use namespace::clean;
  5         30989  
  5         70  
7             require DBIx::Class::Candy::Exports;
8 5     5   2349 use MRO::Compat;
  5         4033  
  5         183  
9 5     5   3548 use Sub::Exporter 'build_exporter';
  5         68451  
  5         33  
10 5     5   1261 use Carp 'croak';
  5         12  
  5         15654  
11              
12             # ABSTRACT: Sugar for your favorite ORM, DBIx::Class
13              
14             my %aliases = (
15             column => 'add_columns',
16             primary_key => 'set_primary_key',
17             unique_constraint => 'add_unique_constraint',
18             relationship => 'add_relationship',
19             );
20              
21             my @methods = qw(
22             resultset_class
23             resultset_attributes
24             remove_columns
25             remove_column
26             table
27             source_name
28              
29             inflate_column
30              
31             belongs_to
32             has_many
33             might_have
34             has_one
35             many_to_many
36              
37             sequence
38             );
39              
40 12   50 12 0 56 sub base { return $_[1] || 'DBIx::Class::Core' }
41              
42 7     7 0 16 sub perl_version { return $_[1] }
43              
44 7     7 0 31 sub autotable { $_[1] }
45              
46 10     10 0 23 sub experimental { $_[1] }
47              
48             sub _extract_part {
49 21     21   63 my ($self, $class) = @_;
50 21 100       232 if (my ( $part ) = $class =~ /(?:::Schema)?::Result::(.+)$/) {
51 20         63 return $part
52             } else {
53 1         175 croak 'unrecognized naming scheme!'
54             }
55             }
56              
57             my $decamelize = sub {
58             my $s = shift;
59             $s =~ s{([^a-zA-Z]?)([A-Z]*)([A-Z])([a-z]?)}{
60             my $fc = pos($s)==0;
61             my ($p0,$p1,$p2,$p3) = ($1,lc$2,lc$3,$4);
62             my $t = $p0 || $fc ? $p0 : '_';
63             $t .= $p3 ? $p1 ? "${p1}_$p2$p3" : "$p2$p3" : "$p1$p2";
64             $t;
65             }ge;
66             $s;
67             };
68              
69             sub gen_table {
70 21     21 0 14221 my ( $self, $class, $version ) = @_;
71 21 100       129 if ($version eq 'singular') {
    50          
72 2         6 my $part = $self->_extract_part($class);
73 2         6 $part =~ s/:://g;
74 2         6 return $decamelize->($part);
75             } elsif ($version == 1) {
76 19         99 my $part = $self->_extract_part($class);
77 18         5937 require Lingua::EN::Inflect;
78 18         115005 $part =~ s/:://g;
79 18         75 $part = $decamelize->($part);
80 18         131 return join q{_}, split /\s+/, Lingua::EN::Inflect::PL(join q{ }, split /_/, $part);
81             }
82             }
83              
84             sub import {
85 20     20   20396 my $self = shift;
86              
87 20         63 my $inheritor = caller(0);
88 20         108 my $args = $self->parse_arguments(\@_);
89 20         95 my $perl_version = $self->perl_version($args->{perl_version});
90 20         141 my $experimental = $self->experimental($args->{experimental});
91 20         84 my @rest = @{$args->{rest}};
  20         49  
92              
93 20         104 $self->set_base($inheritor, $args->{base});
94 19         59 $inheritor->load_components(@{$args->{components}});
  19         490  
95 19         315 my @custom_methods;
96             my %custom_aliases;
97             {
98 19         46 my @custom = $self->gen_custom_imports($inheritor);
  19         90  
99 19         51 @custom_methods = @{$custom[0]};
  19         45  
100 19         34 %custom_aliases = %{$custom[1]};
  19         65  
101             }
102              
103 19     19   106 my $set_table = sub {};
104 19 100       89 if (my $v = $self->autotable($args->{autotable})) {
105 15         55 my $table_name = $self->gen_table($inheritor, $v);
106 15         23130 my $ran = 0;
107 75 100   75   922 $set_table = sub { $inheritor->table($table_name) unless $ran++ }
108 15         123 }
109 19         66 @_ = ($self, @rest);
110             my $import = build_exporter({
111             exports => [
112             has_column => $self->gen_has_column($inheritor, $set_table),
113             primary_column => $self->gen_primary_column($inheritor, $set_table),
114             unique_column => $self->gen_unique_column($inheritor, $set_table),
115 247         568 (map { $_ => $self->gen_proxy($inheritor, $set_table) } @methods, @custom_methods),
116 19         129 (map { $_ => $self->gen_rename_proxy($inheritor, $set_table, %aliases, %custom_aliases) }
  76         241  
117             keys %aliases, keys %custom_aliases),
118             ],
119             groups => {
120             default => [
121             qw(has_column primary_column unique_column), @methods, @custom_methods, keys %aliases, keys %custom_aliases
122             ],
123             },
124             installer => $self->installer,
125             collectors => [
126             INIT => $self->gen_INIT($perl_version, \%custom_aliases, \@custom_methods, $inheritor, $experimental),
127             ],
128             });
129              
130 19         15743 goto $import
131             }
132              
133             sub gen_custom_imports {
134 19     19 0 53 my ($self, $inheritor) = @_;
135 19         58 my @methods;
136             my %aliases;
137 19         36 for (@{mro::get_linear_isa($inheritor)}) {
  19         107  
138 465 50       940 if (my $a = $DBIx::Class::Candy::Exports::aliases{$_}) {
139 0         0 %aliases = (%aliases, %$a)
140             }
141 465 50       1026 if (my $m = $DBIx::Class::Candy::Exports::methods{$_}) {
142 0         0 @methods = (@methods, @$m)
143             }
144             }
145 19         68 return(\@methods, \%aliases)
146             }
147              
148             sub parse_arguments {
149 20     20 0 43 my $self = shift;
150 20         45 my @args = @{shift @_};
  20         75  
151              
152 20         72 my $skipnext;
153             my $base;
154 20         0 my @rest;
155 20         41 my $perl_version = undef;
156 20         60 my $components = [];
157 20         37 my $autotable = 0;
158 20         53 my $experimental;
159              
160 20         75 for my $idx ( 0 .. $#args ) {
161 26         49 my $val = $args[$idx];
162              
163 26 50       67 next unless defined $val;
164 26 100       86 if ($skipnext) {
165 13         24 $skipnext--;
166 13         32 next;
167             }
168              
169 13 100       42 if ( $val eq '-base' ) {
    50          
    0          
    0          
    0          
170 10         24 $base = $args[$idx + 1];
171 10         23 $skipnext = 1;
172             } elsif ( $val eq '-autotable' ) {
173 3         8 $autotable = $args[$idx + 1];
174 3 50       11 $autotable = ord $autotable if length $autotable == 1;
175 3         7 $skipnext = 1;
176             } elsif ( $val eq '-perl5' ) {
177 0         0 $perl_version = ord $args[$idx + 1];
178 0         0 $skipnext = 1;
179             } elsif ( $val eq '-experimental' ) {
180 0         0 $experimental = $args[$idx + 1];
181 0         0 $skipnext = 1;
182             } elsif ( $val eq '-components' ) {
183 0         0 $components = $args[$idx + 1];
184 0         0 $skipnext = 1;
185             } else {
186 0         0 push @rest, $val;
187             }
188             }
189              
190             return {
191 20         166 autotable => $autotable,
192             base => $base,
193             perl_version => $perl_version,
194             components => $components,
195             rest => \@rest,
196             experimental => $experimental,
197             };
198             }
199              
200             sub gen_primary_column {
201 19     19 0 48 my ($self, $inheritor, $set_table) = @_;
202             sub {
203 19     19   516 my $i = $inheritor;
204             sub {
205 16         13623 my $column = shift;
206 16         34 my $info = shift;
207 16         51 $set_table->();
208 16         6098 $i->add_columns($column => $info);
209 16         5876 $i->set_primary_key($i->primary_columns, $column);
210             }
211 19         86 }
212 19         106 }
213              
214             sub gen_unique_column {
215 19     19 0 55 my ($self, $inheritor, $set_table) = @_;
216             sub {
217 19     19   482 my $i = $inheritor;
218             sub {
219 9         3786 my $column = shift;
220 9         20 my $info = shift;
221 9         41 $set_table->();
222 9         119 $i->add_columns($column => $info);
223 9         5423 $i->add_unique_constraint([ $column ]);
224             }
225 19         136 }
226 19         89 }
227              
228             sub gen_has_column {
229 19     19 0 54 my ($self, $inheritor, $set_table) = @_;
230             sub {
231 19     19   9873 my $i = $inheritor;
232             sub {
233 2         1180 my $column = shift;
234 2         8 $set_table->();
235 2         23 $i->add_columns($column => { @_ })
236             }
237 19         121 }
238 19         140 }
239              
240             sub gen_rename_proxy {
241 76     76 0 293 my ($self, $inheritor, $set_table, %aliases) = @_;
242             sub {
243 76     76   1875 my ($class, $name) = @_;
244 76         155 my $meth = $aliases{$name};
245 76         132 my $i = $inheritor;
246 41         26350 sub { $set_table->(); $i->$meth(@_) }
  41         4383  
247 76         348 }
248 76         572 }
249              
250             sub gen_proxy {
251 247     247 0 479 my ($self, $inheritor, $set_table) = @_;
252             sub {
253 247     247   6216 my ($class, $name) = @_;
254 247         364 my $i = $inheritor;
255 26         23156 sub { $set_table->(); $i->$name(@_) }
  26         2240  
256 247         1135 }
257 247         968 }
258              
259             sub installer {
260 19     19 0 47 my ($self) = @_;
261             sub {
262 19     19   240 Sub::Exporter::default_installer @_;
263 19         18633 my %subs = @{ $_[1] };
  19         208  
264 19         308 namespace::clean->import( -cleanee => $_[0]{into}, keys %subs )
265             }
266 19         140 }
267              
268             sub set_base {
269 20     20 0 56 my ($self, $inheritor, $base) = @_;
270              
271             # inlined from parent.pm
272 20         66 for ( my @useless = $self->base($base) ) {
273 20         265 s{::|'}{/}g;
274 20         1972 require "$_.pm"; # dies if the file is not found
275             }
276              
277             {
278 5     5   48 no strict 'refs';
  5         16  
  5         1760  
  20         509593  
279             # This is more efficient than push for the new MRO
280             # at least until the new MRO is fixed
281 20         37 @{"$inheritor\::ISA"} = (@{"$inheritor\::ISA"} , $self->base($base));
  20         852  
  20         163  
282             }
283             }
284              
285             sub gen_INIT {
286 19     19 0 58 my ($self, $perl_version, $custom_aliases, $custom_methods, $inheritor, $experimental) = @_;
287             sub {
288 19     19   1700 my $orig = $_[1]->{import_args};
289 19         49 $_[1]->{import_args} = [];
290 19         53 %$custom_aliases = ();
291 19         36 @$custom_methods = ();
292              
293 19         174 strict->import;
294 19         538 warnings->import;
295              
296 19 100       89 if ($perl_version) {
297 12         104 require feature;
298 12         1834 feature->import(":5.$perl_version")
299             }
300              
301 19 100       66 if ($experimental) {
302 9         1813 require experimental;
303 9 50 33     8878 die 'experimental arg must be an arrayref!'
304             unless ref $experimental && ref $experimental eq 'ARRAY';
305             # to avoid experimental referring to the method
306 9         94 experimental::->import(@$experimental)
307             }
308              
309 19         547 mro::set_mro($inheritor, 'c3');
310              
311 19         50 1;
312             }
313 19         258 }
314              
315             1;
316              
317             __END__