File Coverage

blib/lib/Fey/ORM/Table.pm
Criterion Covered Total %
statement 74 77 96.1
branch 6 6 100.0
condition n/a
subroutine 22 23 95.6
pod 9 10 90.0
total 111 116 95.6


line stmt bran cond sub pod time code
1             ## no critic (Moose::RequireMakeImmutable)
2             package Fey::ORM::Table;
3              
4 9     9   25430255 use strict;
  9         21  
  9         350  
5 9     9   48 use warnings;
  9         15  
  9         386  
6 9     9   52 use namespace::autoclean;
  9         12  
  9         96  
7              
8             our $VERSION = '0.47';
9              
10 9     9   973 use Class::Load qw( load_class );
  9         789  
  9         599  
11 9     9   3730 use Fey::Meta::Class::Table;
  9         32  
  9         531  
12 9     9   5948 use Fey::Object::Table;
  9         39  
  9         437  
13              
14 9     9   85 use Moose 1.15 ();
  9         306  
  9         285  
15 9     9   54 use MooseX::StrictConstructor 0.13 ();
  9         184  
  9         210  
16 9     9   46 use Moose::Exporter;
  9         19  
  9         61  
17 9     9   484 use Moose::Util::MetaRole;
  9         17  
  9         305  
18 9     9   54 use MooseX::Params::Validate qw( pos_validated_list );
  9         18  
  9         99  
19              
20             Moose::Exporter->setup_import_methods(
21             with_meta =>
22             [qw( has_table has_policy has_one has_many transform query )],
23             as_is => [qw( inflate deflate handles )],
24             also => [ 'Moose', 'MooseX::StrictConstructor' ],
25             );
26              
27             ## no critic (Subroutines::ProhibitSubroutinePrototypes)
28              
29             sub init_meta {
30 31     31 0 145874 shift;
31 31         162 my %p = @_;
32              
33 31         202 return Moose->init_meta(
34             %p,
35             base_class => 'Fey::Object::Table',
36             metaclass => 'Fey::Meta::Class::Table',
37             );
38             }
39              
40             sub has_table {
41 26     26 1 98702 my $meta = shift;
42              
43 26         217 my ($table) = pos_validated_list( \@_, { isa => 'Fey::Table' } );
44              
45 26         6250 $meta->_associate_table(
46             $table,
47             _context(),
48             );
49             }
50              
51             sub has_policy {
52 2     2 1 65 my $meta = shift;
53 2         44 my $policy = shift;
54              
55 2 100       11 unless ( ref $policy ) {
56 1         9 load_class($policy);
57              
58 1         62 $policy = $policy->Policy();
59             }
60              
61 2         122 $meta->set_policy($policy);
62             }
63              
64             sub transform {
65 6     6 1 96 my $meta = shift;
66              
67 6         8 my @p;
68              
69 6         46 push @p, pop @_ while ref $_[-1];
70              
71 6         19 my %p = _combine_hashes(@p);
72              
73 6         15 for my $name (@_) {
74 7         23 $meta->_add_transform(
75             $name,
76             _context(),
77             %p,
78             );
79             }
80             }
81              
82             sub _combine_hashes {
83 6     6   10 return map { %{$_} } @_;
  10         10  
  10         37  
84             }
85              
86             sub inflate (&) {
87 4     4 1 90 return { inflate => $_[0] };
88             }
89              
90             sub deflate (&) {
91 5     5 1 4119 return { deflate => $_[0] };
92             }
93              
94             sub handles ($) {
95 1     1 1 6 return { handles => $_[0] };
96             }
97              
98             sub has_one {
99 12     12 1 41743 my $meta = shift;
100              
101 12         28 my %p;
102 12 100       54 if ( @_ == 1 ) {
103 4         21 ( $p{table} ) = shift;
104             }
105             else {
106 8         32 $p{name} = shift;
107              
108 8         56 %p = ( %p, @_ );
109             }
110              
111 12         95 $meta->add_has_one(%p);
112             }
113              
114             sub has_many {
115 10     10 1 44721 my $meta = shift;
116              
117 10         29 my %p;
118 10 100       47 if ( @_ == 1 ) {
119 3         17 ( $p{table} ) = shift;
120             }
121             else {
122 7         28 $p{name} = shift;
123              
124 7         51 %p = ( %p, @_ );
125              
126             }
127              
128 10         96 $meta->add_has_many(%p);
129             }
130              
131             sub query {
132 0     0 1 0 my $meta = shift;
133 0         0 my $name = shift;
134              
135 0         0 $meta->add_query_method( name => $name, @_ );
136             }
137              
138             sub _context {
139 33     33   58 my %context;
140 33         297 @context{qw(package file line)} = caller(2);
141              
142 33         274 return \%context;
143             }
144              
145             1;
146              
147             # ABSTRACT: Provides sugar for table-based classes
148              
149             __END__
150              
151             =pod
152              
153             =head1 NAME
154              
155             Fey::ORM::Table - Provides sugar for table-based classes
156              
157             =head1 VERSION
158              
159             version 0.47
160              
161             =head1 SYNOPSIS
162              
163             package MyApp::User;
164              
165             use Fey::ORM::Table;
166              
167             has_table ...;
168              
169             no Fey::ORM::Table;
170              
171             =head1 DESCRIPTION
172              
173             Use this class to associate your class with a table. It exports a
174             number of sugar functions to allow you to define things in a
175             declarative manner.
176              
177             =head1 EXPORTED FUNCTIONS
178              
179             This package exports the following functions:
180              
181             =head2 has_table($table)
182              
183             Given a L<Fey::Table> object, this method associates that table with
184             the calling class.
185              
186             Calling C<has_table()> will make your class a subclass of
187             L<Fey::Object::Table>, which provides basic CRUD operations for
188             L<Fey::ORM>. You should make sure to review the docs for
189             L<Fey::Object::Table>.
190              
191             Calling this function also generates a number of methods and
192             attributes in the calling class.
193              
194             First, it generates one attribute for each column in the associated
195             table. Of course, this assumes that your columns are named in such a
196             way as to be usable as Perl methods.
197              
198             It also generates a predicate for each attribute, where the predicate
199             is the column named prefixed with "has_". So for a column named
200             "user_id", you get a C<user_id()> attribute and a C<has_user_id()>
201             predicate.
202              
203             These column-named attributes do not have a public setter method. If
204             you want to change the value of these attributes, you need to use the
205             C<update()> method.
206              
207             =head2 has_policy($policy_class)
208              
209             =head2 has_policy($policy_object)
210              
211             This allows you to associate a policy with your class. See
212             L<Fey::ORM::Policy> for details on how policies work.
213              
214             =head2 has_one($table)
215              
216             =head2 has_one 'name' => ( table => $table, fk => $fk, cache => $bool, undef => $bool, handles => ... )
217              
218             The C<has_one()> function declares a relationship between the calling
219             class's table and another table. The method it creates returns an
220             object of the foreign table's class, or undef or none exists.
221              
222             With the single-argument form, you can simply pass a single
223             L<Fey::Table> object. This works when there is a single foreign key
224             between the calling class's table and the table passed to
225             C<has_one()>.
226              
227             With a single argument, the generated attribute will be named as C<<
228             lc $has_one_table->name() >>, and caching will be turned on.
229              
230             If you want to change any of the defaults, you can use the
231             multi-argument form. In this case, the first argument is the name of
232             the attribute or method to add. Then you can specify various
233             parameters by name. You must specify a C<table>, of course.
234              
235             The C<fk> parameter is required when there is more than one foreign
236             key between the two tables. Finally, you can turn off caching by
237             setting C<cache> to a false value.
238              
239             When caching is enabled, the object for the foreign table is only
240             fetched once, and is cached afterwards. This is independent of the
241             object caching for a particular class. If you turn off caching, then
242             the object is fetched every time the method is called.
243              
244             Also, a private clearer method is created when caching is enabled, of
245             the form C<< $object->_clear_$name() >>.
246              
247             The C<undef> parameter allows you to explicitly say whether the
248             attribute can be undefined. Normally this is calculated by looking at
249             the foreign key and seeing if any of the source columns are nullable.
250              
251             The C<handles> parameter works exactly like it does for any Moose
252             attribute, but it only works if C<cache> is true, since otherwise the
253             relationship is implemented via a simple method, not a Moose
254             attribute.
255              
256             =head2 has_one 'name' => ( table => $table, select => $select, bind_params => $sub, cache => $bool, undef => $bool, handles => ... )
257              
258             This is an alternative form of C<has_one()> that lets you declare a
259             relationship to another table via an arbitrary SELECT statement.
260              
261             In this form, you provide a query object to define the SQL used to fetch the
262             foreign row. You can provide a C<bind_params> parameter as a code reference,
263             which will be called as a method on your object. It is expected to return one
264             or more bind parameters. The C<cache> parameter works exactly the same as in
265             the first form of C<has_one()>.
266              
267             In this form the C<undef> parameter defaults to true, but you can set
268             it to a false value.
269              
270             Note that if you want to provide bind_params for the SQL you provide,
271             you need to make sure it has placeholders.
272              
273             =head2 has_many($table)
274              
275             =head2 has_many 'name' => ( table => $table, fk => $fk, cache => $bool, order_by => [ ... ] )
276              
277             The C<has_many()> function declares a relationship between the calling
278             class's table and another table, just like C<has_one()>. The method it
279             creates returns a L<Fey::Object::Iterator::FromSelect> of the foreign
280             table's objects.
281              
282             With the single-argument form, you can simply pass a single
283             L<Fey::Table> object. This works when there is a single foreign key
284             between the calling class's table and the table passed to
285             C<has_many()>.
286              
287             With a single argument, the generated attribute will be named as C<<
288             lc $has_one_table->name() >>, and caching will be turned off. There
289             will be no specific order to the results returned.
290              
291             If you want to change any of the defaults, you can use the
292             multi-argument form. In this case, the first argument is the name of
293             the attribute or method to add. Then you can specify various
294             parameters by name. You must specify a C<table>, of course.
295              
296             The C<fk> parameter is required when there is more than one foreign
297             key between the two tables. Finally, you can turn on caching by
298             setting C<cache> to a true value.
299              
300             When caching is enabled, the iterator returned is of the
301             L<Fey::Object::Iterator::FromSelect::Caching> class.
302              
303             Also, a private clearer method is created when caching is enabled, of
304             the form C<< $object->_clear_$name() >>.
305              
306             Note that you will always get an iterator object back from your
307             has_many methods and attributes, even if there are no matching rows in
308             the foreign table.
309              
310             You can also specify an C<order_by> parameter as an array
311             reference. This should be an array like you would pass to C<<
312             Fey::SQL::Select->order_by() >>.
313              
314             =head2 query $name => select => $select, bind_params => sub { ... }
315              
316             The C<query()> function declares a method based on the given query. This works
317             much like declaring an attribute with the C<FromSelect> metaclass, but the
318             value returned from the query is not stored in the object.
319              
320             =head2 transform $column1, $column2 => inflate { ... } => deflate { ... }
321              
322             The C<transform()> function declares an inflator, deflator, or both
323             for the specified column. The inflator will be used to wrap the normal
324             accessor for the column. You'd generally use this to turn a raw value
325             from the DBMS into an object, for example:
326              
327             transform 'creation_date' =>
328             inflate { DateTime::Format::Pg->parse_date( $_[1] ) };
329              
330             The inflator (and deflator) coderef you specify will be called as a
331             I<method> on the object (or class). This lets you get at other
332             attributes for the object if needed.
333              
334             When a column is inflated, a new attribute is created to allow you to
335             get at the raw data by suffixing the column name with "_raw". Given
336             the above inflator, a C<creation_date_raw()> attribute would be
337             created.
338              
339             If the column in question is nullable your inflator should be prepared
340             to handle an undef value for the column.
341              
342             Deflators are used to transform objects passed to C<update()> or
343             C<insert()> into values suitable for passing to the DBMS:
344              
345             transform 'creation_date' =>
346             deflate { defined $_[1] && ref $_[1]
347             ? DateTime::Format::Pg->format_date( $_[1] )
348             : $_[1] };
349              
350             Just as with an inflator, your deflator should be prepared to accept
351             an undef if the column is nullable.
352              
353             You can only declare one inflator and one deflator for each column.
354              
355             You can use the same inflator and deflator for more than one column at
356             once:
357              
358             transform 'creation_date', 'modification_date'
359             => inflate { ... }
360             => deflate { ... };
361              
362             =head2 inflate { .. }
363              
364             =head2 deflate { .. }
365              
366             These are sugar functions that accept a single coderef. They mostly
367             exist to prevent you from having to write this:
368              
369             # this is not valid code!
370             transform 'creation_date' =>
371             ( inflator => sub { ... },
372             deflator => sub { ... },
373             );
374              
375             =head2 handles ...
376              
377             This sugar function lets you add delegation to an inflated
378             attribute. It accepts anything that Moose accepts for an attribute's
379             C<handles> parameter.
380              
381             transform 'creation_date'
382             => inflate { ... }
383             => handles { creation_ymd => 'ymd',
384             creation_iso8601 => 'iso8601',
385             }
386             => deflate { ... };
387              
388             =head1 AUTHOR
389              
390             Dave Rolsky <autarch@urth.org>
391              
392             =head1 COPYRIGHT AND LICENSE
393              
394             This software is copyright (c) 2011 - 2015 by Dave Rolsky.
395              
396             This is free software; you can redistribute it and/or modify it under
397             the same terms as the Perl 5 programming language system itself.
398              
399             =cut