File Coverage

blib/lib/DBIx/Class/Candy.pm
Criterion Covered Total %
statement 168 178 94.3
branch 23 36 63.8
condition 2 5 40.0
subroutine 33 33 100.0
pod 0 15 0.0
total 226 267 84.6


line stmt bran cond sub pod time code
1             package DBIx::Class::Candy;
2             $DBIx::Class::Candy::VERSION = '0.005002';
3 5     5   311395 use strict;
  5         7  
  5         172  
4 5     5   18 use warnings;
  5         5  
  5         103  
5              
6 5     5   803 use namespace::clean;
  5         19993  
  5         25  
7             require DBIx::Class::Candy::Exports;
8 5     5   1490 use MRO::Compat;
  5         2338  
  5         113  
9 5     5   2124 use Sub::Exporter 'build_exporter';
  5         35007  
  5         23  
10 5     5   739 use Carp 'croak';
  5         8  
  5         6320  
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 38 sub base { return $_[1] || 'DBIx::Class::Core' }
41              
42 7     7 0 8 sub perl_version { return $_[1] }
43              
44 7     7 0 19 sub autotable { $_[1] }
45              
46 10     10 0 12 sub experimental { $_[1] }
47              
48             sub _extract_part {
49 21     21   23 my ($self, $class) = @_;
50 21 100       131 if (my ( $part ) = $class =~ /(?:::Schema)?::Result::(.+)$/) {
51 20         38 return $part
52             } else {
53 1         155 croak 'unrecognized naming scheme!'
54             }
55             }
56              
57             sub gen_table {
58 21     21 0 7831 my ( $self, $class, $version ) = @_;
59 21 100       79 if ($version eq 'singular') {
    50          
60 2         4 my $part = $self->_extract_part($class);
61 2         9 require String::CamelCase;
62 2         4 $part =~ s/:://g;
63 2         5 return String::CamelCase::decamelize($part);
64             } elsif ($version == 1) {
65 19         45 my $part = $self->_extract_part($class);
66 18         2707 require Lingua::EN::Inflect;
67 18         69886 require String::CamelCase;
68 18         1495 $part =~ s/:://g;
69 18         43 $part = String::CamelCase::decamelize($part);
70 18         396 return join q{_}, split /\s+/, Lingua::EN::Inflect::PL(join q{ }, split /_/, $part);
71             }
72             }
73              
74             sub import {
75 20     20   9578 my $self = shift;
76              
77 20         35 my $inheritor = caller(0);
78 20         82 my $args = $self->parse_arguments(\@_);
79 20         54 my $perl_version = $self->perl_version($args->{perl_version});
80 20         91 my $experimental = $self->experimental($args->{experimental});
81 20         38 my @rest = @{$args->{rest}};
  20         29  
82              
83 20         49 $self->set_base($inheritor, $args->{base});
84 19         26 $inheritor->load_components(@{$args->{components}});
  19         257  
85 19         164 my @custom_methods;
86             my %custom_aliases;
87             {
88 19         20 my @custom = $self->gen_custom_imports($inheritor);
  19         60  
89 19         41 @custom_methods = @{$custom[0]};
  19         25  
90 19         18 %custom_aliases = %{$custom[1]};
  19         55  
91             }
92              
93 19     19   66 my $set_table = sub {};
94 19 100       59 if (my $v = $self->autotable($args->{autotable})) {
95 15         50 my $table_name = $self->gen_table($inheritor, $v);
96 15         10898 my $ran = 0;
97 75 100   75   582 $set_table = sub { $inheritor->table($table_name) unless $ran++ }
98 15         64 }
99 19         61 @_ = ($self, @rest);
100             my $import = build_exporter({
101             exports => [
102             has_column => $self->gen_has_column($inheritor, $set_table),
103             primary_column => $self->gen_primary_column($inheritor, $set_table),
104             unique_column => $self->gen_unique_column($inheritor, $set_table),
105 247         258 (map { $_ => $self->gen_proxy($inheritor, $set_table) } @methods, @custom_methods),
106 19         88 (map { $_ => $self->gen_rename_proxy($inheritor, $set_table, %aliases, %custom_aliases) }
  76         150  
107             keys %aliases, keys %custom_aliases),
108             ],
109             groups => {
110             default => [
111             qw(has_column primary_column unique_column), @methods, @custom_methods, keys %aliases, keys %custom_aliases
112             ],
113             },
114             installer => $self->installer,
115             collectors => [
116             INIT => $self->gen_INIT($perl_version, \%custom_aliases, \@custom_methods, $inheritor, $experimental),
117             ],
118             });
119              
120 19         7271 goto $import
121             }
122              
123             sub gen_custom_imports {
124 19     19 0 24 my ($self, $inheritor) = @_;
125 19         16 my @methods;
126             my %aliases;
127 19         18 for (@{mro::get_linear_isa($inheritor)}) {
  19         58  
128 465 50       527 if (my $a = $DBIx::Class::Candy::Exports::aliases{$_}) {
129 0         0 %aliases = (%aliases, %$a)
130             }
131 465 50       575 if (my $m = $DBIx::Class::Candy::Exports::methods{$_}) {
132 0         0 @methods = (@methods, @$m)
133             }
134             }
135 19         43 return(\@methods, \%aliases)
136             }
137              
138             sub parse_arguments {
139 20     20 0 22 my $self = shift;
140 20         21 my @args = @{shift @_};
  20         43  
141              
142 20         23 my $skipnext;
143             my $base;
144 0         0 my @rest;
145 20         20 my $perl_version = undef;
146 20         23 my $components = [];
147 20         20 my $autotable = 0;
148 20         15 my $experimental;
149              
150 20         45 for my $idx ( 0 .. $#args ) {
151 26         49 my $val = $args[$idx];
152              
153 26 50       44 next unless defined $val;
154 26 100       41 if ($skipnext) {
155 13         24 $skipnext--;
156 13         18 next;
157             }
158              
159 13 100       33 if ( $val eq '-base' ) {
    50          
    0          
    0          
    0          
160 10         14 $base = $args[$idx + 1];
161 10         16 $skipnext = 1;
162             } elsif ( $val eq '-autotable' ) {
163 3         6 $autotable = $args[$idx + 1];
164 3 50       14 $autotable = ord $autotable if length $autotable == 1;
165 3         4 $skipnext = 1;
166             } elsif ( $val eq '-perl5' ) {
167 0         0 $perl_version = ord $args[$idx + 1];
168 0         0 $skipnext = 1;
169             } elsif ( $val eq '-experimental' ) {
170 0         0 $experimental = $args[$idx + 1];
171 0         0 $skipnext = 1;
172             } elsif ( $val eq '-components' ) {
173 0         0 $components = $args[$idx + 1];
174 0         0 $skipnext = 1;
175             } else {
176 0         0 push @rest, $val;
177             }
178             }
179              
180             return {
181 20         93 autotable => $autotable,
182             base => $base,
183             perl_version => $perl_version,
184             components => $components,
185             rest => \@rest,
186             experimental => $experimental,
187             };
188             }
189              
190             sub gen_primary_column {
191 19     19 0 21 my ($self, $inheritor, $set_table) = @_;
192             sub {
193 19     19   227 my $i = $inheritor;
194             sub {
195 16         6315 my $column = shift;
196 16         16 my $info = shift;
197 16         25 $set_table->();
198 16         3199 $i->add_columns($column => $info);
199 16         3326 $i->set_primary_key($i->primary_columns, $column);
200             }
201 19         59 }
202 19         67 }
203              
204             sub gen_unique_column {
205 19     19 0 19 my ($self, $inheritor, $set_table) = @_;
206             sub {
207 19     19   204 my $i = $inheritor;
208             sub {
209 9         1757 my $column = shift;
210 9         10 my $info = shift;
211 9         15 $set_table->();
212 9         52 $i->add_columns($column => $info);
213 9         3169 $i->add_unique_constraint([ $column ]);
214             }
215 19         59 }
216 19         53 }
217              
218             sub gen_has_column {
219 19     19 0 28 my ($self, $inheritor, $set_table) = @_;
220             sub {
221 19     19   4041 my $i = $inheritor;
222             sub {
223 2         557 my $column = shift;
224 2         3 $set_table->();
225 2         12 $i->add_columns($column => { @_ })
226             }
227 19         71 }
228 19         78 }
229              
230             sub gen_rename_proxy {
231 76     76 0 143 my ($self, $inheritor, $set_table, %aliases) = @_;
232             sub {
233 76     76   857 my ($class, $name) = @_;
234 76         72 my $meth = $aliases{$name};
235 76         48 my $i = $inheritor;
236 41         14412 sub { $set_table->(); $i->$meth(@_) }
  41         2492  
237 76         201 }
238 76         330 }
239              
240             sub gen_proxy {
241 247     247 0 184 my ($self, $inheritor, $set_table) = @_;
242             sub {
243 247     247   2569 my ($class, $name) = @_;
244 247         168 my $i = $inheritor;
245 26         11638 sub { $set_table->(); $i->$name(@_) }
  26         1792  
246 247         649 }
247 247         507 }
248              
249             sub installer {
250 19     19 0 19 my ($self) = @_;
251             sub {
252 19     19   129 Sub::Exporter::default_installer @_;
253 19         9620 my %subs = @{ $_[1] };
  19         135  
254 19         175 namespace::clean->import( -cleanee => $_[0]{into}, keys %subs )
255             }
256 19         80 }
257              
258             sub set_base {
259 20     20 0 26 my ($self, $inheritor, $base) = @_;
260              
261             # inlined from parent.pm
262 20         41 for ( my @useless = $self->base($base) ) {
263 20         178 s{::|'}{/}g;
264 20         1270 require "$_.pm"; # dies if the file is not found
265             }
266              
267             {
268 5     5   25 no strict 'refs';
  5         10  
  5         1094  
  20         272817  
269             # This is more efficient than push for the new MRO
270             # at least until the new MRO is fixed
271 20         23 @{"$inheritor\::ISA"} = (@{"$inheritor\::ISA"} , $self->base($base));
  20         463  
  20         97  
272             }
273             }
274              
275             sub gen_INIT {
276 19     19 0 24 my ($self, $perl_version, $custom_aliases, $custom_methods, $inheritor, $experimental) = @_;
277             sub {
278 19     19   874 my $orig = $_[1]->{import_args};
279 19         27 $_[1]->{import_args} = [];
280 19         31 %$custom_aliases = ();
281 19         16 @$custom_methods = ();
282              
283 19         81 strict->import;
284 19         124 warnings->import;
285              
286 19 100       34 if ($perl_version) {
287 12         48 require feature;
288 12         640 feature->import(":5.$perl_version")
289             }
290              
291 19 100       41 if ($experimental) {
292 9         1182 require experimental;
293 9 50 33     7568 die 'experimental arg must be an arrayref!'
294             unless ref $experimental && ref $experimental eq 'ARRAY';
295             # to avoid experimental referring to the method
296 9         37 experimental::->import(@$experimental)
297             }
298              
299 19         323 mro::set_mro($inheritor, 'c3');
300              
301 19         26 1;
302             }
303 19         120 }
304              
305             1;
306              
307             __END__
308              
309             =pod
310              
311             =head1 NAME
312              
313             DBIx::Class::Candy - Sugar for your favorite ORM, DBIx::Class
314              
315             =head1 SYNOPSIS
316              
317             package MyApp::Schema::Result::Artist;
318              
319             use DBIx::Class::Candy -autotable => v1;
320              
321             primary_column id => {
322             data_type => 'int',
323             is_auto_increment => 1,
324             };
325              
326             column name => {
327             data_type => 'varchar',
328             size => 25,
329             is_nullable => 1,
330             };
331              
332             has_many albums => 'A::Schema::Result::Album', 'artist_id';
333              
334             1;
335              
336             =head1 DESCRIPTION
337              
338             C<DBIx::Class::Candy> is a simple sugar layer for definition of
339             L<DBIx::Class> results. Note that it may later be expanded to add sugar
340             for more C<DBIx::Class> related things. By default C<DBIx::Class::Candy>:
341              
342             =over
343              
344             =item *
345              
346             turns on strict and warnings
347              
348             =item *
349              
350             sets your parent class
351              
352             =item *
353              
354             exports a bunch of the package methods that you normally use to define your
355             L<DBIx::Class> results
356              
357             =item *
358              
359             makes a few aliases to make some of the original method names shorter or
360             more clear
361              
362             =item *
363              
364             defines very few new subroutines that transform the arguments passed to them
365              
366             =back
367              
368             It assumes a L<DBIx::Class::Core>-like API, but you can tailor it to suit
369             your needs.
370              
371             =head1 IMPORT OPTIONS
372              
373             See L</SETTING DEFAULT IMPORT OPTIONS> for information on setting these schema wide.
374              
375             =head2 -base
376              
377             use DBIx::Class::Candy -base => 'MyApp::Schema::Result';
378              
379             The first thing you can do to customize your usage of C<DBIx::Class::Candy>
380             is change the parent class. Do that by using the C<-base> import option.
381              
382             =head2 -autotable
383              
384             use DBIx::Class::Candy -autotable => v1;
385              
386             Don't waste your precious keystrokes typing C<< table 'buildings' >>, let
387             C<DBIx::Class::Candy> do that for you! See L<AUTOTABLE VERSIONS> for what the
388             existing versions will generate for you.
389              
390             =head2 -components
391              
392             use DBIx::Class::Candy -components => ['FilterColumn'];
393              
394             C<DBIx::Class::Candy> allows you to set which components you are using at
395             import time so that the components can define their own sugar to export as
396             well. See L<DBIx::Class::Candy::Exports> for details on how that works.
397              
398             =head2 -perl5
399              
400             use DBIx::Class::Candy -perl5 => v10;
401              
402             I love the new features in Perl 5.10 and 5.12, so I felt that it would be
403             nice to remove the boiler plate of doing C<< use feature ':5.10' >> and
404             add it to my sugar importer. Feel free not to use this.
405              
406             =head2 -experimental
407              
408             use DBIx::Class::Candy -experimental => ['signatures'];
409              
410             I would like to use signatures and postfix dereferencing in all of my
411             C<DBIx::Class> classes. This makes that goal trivial.
412              
413             =head1 IMPORTED SUBROUTINES
414              
415             Most of the imported subroutines are the same as what you get when you use
416             the normal interface for result definition: they have the same names and take
417             the same arguments. In general write the code the way you normally would,
418             leaving out the C<< __PACKAGE__-> >> part. The following are methods that
419             are exported with the same name and arguments:
420              
421             belongs_to
422             has_many
423             has_one
424             inflate_column
425             many_to_many
426             might_have
427             remove_column
428             remove_columns
429             resultset_attributes
430             resultset_class
431             sequence
432             source_name
433             table
434              
435             There are some exceptions though, which brings us to:
436              
437             =head1 IMPORTED ALIASES
438              
439             These are merely renamed versions of the functions you know and love. The idea is
440             to make your result classes a tiny bit prettier by aliasing some methods.
441             If you know your C<DBIx::Class> API you noticed that in the L</SYNOPSIS> I used C<column>
442             instead of C<add_columns> and C<primary_key> instead of C<set_primary_key>. The old
443             versions work, this is just nicer. A list of aliases are as follows:
444              
445             column => 'add_columns',
446             primary_key => 'set_primary_key',
447             unique_constraint => 'add_unique_constraint',
448             relationship => 'add_relationship',
449              
450             =head1 SETTING DEFAULT IMPORT OPTIONS
451              
452             Eventually you will get tired of writing the following in every single one of
453             your results:
454              
455             use DBIx::Class::Candy
456             -base => 'MyApp::Schema::Result',
457             -perl5 => v12,
458             -autotable => v1,
459             -experimental => ['signatures'];
460              
461             You can set all of these for your whole schema if you define your own C<Candy>
462             subclass as follows:
463              
464             package MyApp::Schema::Candy;
465              
466             use base 'DBIx::Class::Candy';
467              
468             sub base { $_[1] || 'MyApp::Schema::Result' }
469             sub perl_version { 12 }
470             sub autotable { 1 }
471             sub experimental { ['signatures'] }
472              
473             Note the C<< $_[1] || >> in C<base>. All of these methods are passed the
474             values passed in from the arguments to the subclass, so you can either throw
475             them away, honor them, die on usage, or whatever. To be clear, if you define
476             your subclass, and someone uses it as follows:
477              
478             use MyApp::Schema::Candy
479             -base => 'MyApp::Schema::Result',
480             -perl5 => v18,
481             -autotable => v1,
482             -experimental => ['postderef'];
483              
484             Your C<base> method will get C<MyApp::Schema::Result>, your C<perl_version> will
485             get C<18>, your C<experimental> will get C<['postderef']>, and your C<autotable>
486             will get C<1>.
487              
488             =head1 SECONDARY API
489              
490             =head2 has_column
491              
492             There is currently a single "transformer" for C<add_columns>, so that
493             people used to the L<Moose> api will feel more at home. Note that this B<may>
494             go into a "Candy Component" at some point.
495              
496             Example usage:
497              
498             has_column foo => (
499             data_type => 'varchar',
500             size => 25,
501             is_nullable => 1,
502             );
503              
504             =head2 primary_column
505              
506             Another handy little feature that allows you to define a column and set it as
507             the primary key in a single call:
508              
509             primary_column id => {
510             data_type => 'int',
511             is_auto_increment => 1,
512             };
513              
514             If your table has multiple columns in its primary key, merely call this method
515             for each column:
516              
517             primary_column person_id => { data_type => 'int' };
518             primary_column friend_id => { data_type => 'int' };
519              
520             =head2 unique_column
521              
522             This allows you to define a column and set it as unique in a single call:
523              
524             unique_column name => {
525             data_type => 'varchar',
526             size => 30,
527             };
528              
529             =head1 AUTOTABLE VERSIONS
530              
531             Currently there are two versions:
532              
533             =head2 C<v1>
534              
535             It looks at your class name, grabs everything after C<::Schema::Result::> (or
536             C<::Result::>), removes the C<::>'s, converts it to underscores instead of
537             camel-case, and pluralizes it. Here are some examples if that's not clear:
538              
539             MyApp::Schema::Result::Cat -> cats
540             MyApp::Schema::Result::Software::Building -> software_buildings
541             MyApp::Schema::Result::LonelyPerson -> lonely_people
542             MyApp::DB::Result::FriendlyPerson -> friendly_people
543             MyApp::DB::Result::Dog -> dogs
544              
545             =head2 C<'singular'>
546              
547             It looks at your class name, grabs everything after C<::Schema::Result::> (or
548             C<::Result::>), removes the C<::>'s and converts it to underscores instead of
549             camel-case. Here are some examples if that's not clear:
550              
551             MyApp::Schema::Result::Cat -> cat
552             MyApp::Schema::Result::Software::Building -> software_building
553             MyApp::Schema::Result::LonelyPerson -> lonely_person
554             MyApp::DB::Result::FriendlyPerson -> friendly_person
555             MyApp::DB::Result::Dog -> dog
556              
557             Also, if you just want to be different, you can easily set up your own naming
558             scheme. Just add a C<gen_table> method to your candy subclass. The method
559             gets passed the class name and the autotable version, which of course you may
560             ignore. For example, one might just do the following:
561              
562             sub gen_table {
563             my ($self, $class) = @_;
564              
565             $class =~ s/::/_/g;
566             lc $class;
567             }
568              
569             Which would transform C<MyApp::Schema::Result::Foo> into
570             C<myapp_schema_result_foo>.
571              
572             Or maybe instead of using the standard C<MyApp::Schema::Result> namespace you
573             decided to be different and do C<MyApp::DB::Table> or something silly like that.
574             You could pre-process your class name so that the default C<gen_table> will
575             still work:
576              
577             sub gen_table {
578             my $self = shift;
579             my $class = $_[0];
580              
581             $class =~ s/::DB::Table::/::Schema::Result::/;
582             return $self->next::method(@_);
583             }
584              
585             =head1 AUTHOR
586              
587             Arthur Axel "fREW" Schmidt <frioux+cpan@gmail.com>
588              
589             =head1 COPYRIGHT AND LICENSE
590              
591             This software is copyright (c) 2016 by Arthur Axel "fREW" Schmidt.
592              
593             This is free software; you can redistribute it and/or modify it under
594             the same terms as the Perl 5 programming language system itself.
595              
596             =cut