File Coverage

blib/lib/PGObject/Util/DBMethod.pm
Criterion Covered Total %
statement 61 61 100.0
branch 20 24 83.3
condition 2 3 66.6
subroutine 16 16 100.0
pod 1 1 100.0
total 100 105 95.2


line stmt bran cond sub pod time code
1             package PGObject::Util::DBMethod;
2              
3 3     3   92977 use 5.008;
  3         22  
4 3     3   16 use strict;
  3         4  
  3         60  
5 3     3   13 use warnings;
  3         5  
  3         104  
6 3     3   17 use Exporter 'import';
  3         19  
  3         131  
7 3     3   1518 use Sub::Util qw( set_subname );
  3         961  
  3         1227  
8              
9             =head1 NAME
10              
11             PGObject::Util::DBMethod - Declarative stored procedure <-> object mappings for
12             the PGObject Framework
13              
14             =head1 VERSION
15              
16             Version 1.01.000
17              
18             =cut
19              
20             our $VERSION = '1.01.000';
21              
22              
23             =head1 SYNOPSIS
24              
25             Without PGObject::Util::DBobject, you would:
26              
27             sub mymethod {
28             my ($self) = @_;
29             return $self->call_dbmethod(funcname => 'foo');
30             }
31              
32             With this you'd do this instead:
33              
34             dbmethod mymethod => (funcname => 'foo');
35              
36             =head1 EXPORT
37              
38             This exports only dbmethod, which it always exports.
39              
40             =cut
41              
42             our @EXPORT = qw(dbmethod);
43              
44             =head1 SUBROUTINES/METHODS
45              
46             =head2 dbmethod
47              
48             use as dbmethod (name => (default_arghash))
49              
50             For example:
51              
52             package MyObject;
53             use PGObject::Utils::DBMethod;
54              
55             dbmethod save => (
56             strict_args => 0,
57             funcname => 'save_user',
58             funcschema => 'public',
59             args => { admin => 0 },
60             );
61             $MyObject->save(args => {username => 'foo', password => 'bar'});
62              
63             Special arguments are:
64              
65             =over
66              
67             =item arg_lit
68              
69             It set must point to a hashref. Used to allow mapping of function arguments
70             to arg hash elements. If this is set then funcname, funcschema, etc, cannot be
71             overwritten on the call.
72              
73             =item strict_args
74              
75             If true, args override args provided by user.
76              
77             =item returns_objects
78              
79             If true, bless returned hashrefs before returning them.
80              
81             =item merge_back
82              
83             If true, merges the first record back to the $self at the end before returning,
84             and returns $self. Note this is a copy only one layer deep which is fine for
85             the use case of merging return values from the database into the current
86             object.
87              
88             =back
89              
90             =cut
91              
92             sub dbmethod {
93 10     10 1 226 my $name = shift;
94 10         35 my %defaultargs = @_;
95 10         28 my ($target) = caller;
96              
97             my $coderef = sub {
98 11     11   2435 my $self = shift @_;
        11      
        11      
        11      
        11      
        7      
        7      
99 11         20 my %args;
100 11 100       30 if ($defaultargs{arg_list}){
101 1         5 %args = ( args => _process_args($defaultargs{arg_list}, @_) );
102             } else {
103 10         25 %args = @_;
104             }
105 11         17 for my $key (keys %{$defaultargs{args}}){
  11         72  
106             $args{args}->{$key} = $defaultargs{args}->{$key}
107 6 50 66     24 unless $args{args}->{$key} or $defaultargs{strict_args};
108             $args{args}->{$key} = $defaultargs{args}->{$key}
109 6 100       18 if $defaultargs{strict_args};
110             }
111 11         37 for my $key(keys %defaultargs){
112 39 100       531 next if grep(/^$key$/, qw(strict_args args returns_objects));
113 24 50       100 $args{$key} = $defaultargs{$key} if $defaultargs{$key};
114             }
115 11         56 my @results = $self->call_dbmethod(%args);
116 11 100       126 if ($defaultargs{returns_objects}){
117 1         3 for my $ref(@results){
118 1         8 $ref = "$target"->new(%$ref);
119             }
120             }
121 11 100       45 if ($defaultargs{merge_back}){
122 5         26 _merge($self, shift @results);
123 5         43 return $self;
124             }
125 6 100       29 return shift @results unless wantarray;
126 5         30 return @results;
127 10         45 };
128 3     3   25 no strict 'refs';
  3         6  
  3         946  
129 10         64 set_subname "${target}::${name}", $coderef;
130 10         20 *{"${target}::${name}"} = $coderef;
  10         48  
131             }
132              
133             # private function _merge($dest, $src)
134             # used to merge incoming db rows to a hash ref.
135             # hash table entries in $src overwrite those in $dest.
136             # Since this is an incoming row, we can generally assume we are not having to
137             # do a deep copy.
138              
139             sub _merge {
140 5     5   11 my ($dest, $src) = @_;
141 5 100       11 if (eval {$dest->can('has') and $dest->can('extends')}){
  5 100       48  
142             # Moo or Moose. Use accessors, though better would be to just return
143             # objects in this case.
144 4         13 for my $att (keys %$src){
145 7 50       45 $dest->can($att)->($dest, $src->{$att}) if $dest->can($att);
146             }
147             } else {
148 1         7 $dest->{$_} = $src->{$_} for (keys %$src);
149             }
150             }
151              
152             # private method _process_args.
153             # first arg $arrayref of argnames
154             # after that we just pass in @_ from the function call
155             # then we return a hash with the args as specified.
156              
157             sub _process_args {
158 1     1   3 my $arglist = shift @_;
159 1         3 my @args = @_;
160              
161 1         3 my $arghref = {};
162              
163 1         3 my $maxlen = scalar @_;
164 1         2 my $it = 1;
165 1         3 for my $argname (@$arglist){
166 1 50       5 last if $it > $maxlen;
167 1         4 $arghref->{$argname} = shift @args;
168 1         3 ++$it;
169             }
170 1         5 return $arghref;
171             }
172              
173             =head1 AUTHOR
174              
175             Chris Travers, C<< >>
176              
177             =head1 BUGS
178              
179             Please report any bugs or feature requests to C, or through
180             the web interface at L. I will be notified, and then you'll
181             automatically be notified of progress on your bug as I make changes.
182              
183              
184              
185              
186             =head1 SUPPORT
187              
188             You can find documentation for this module with the perldoc command.
189              
190             perldoc PGObject::Util::DBMethod
191              
192              
193             You can also look for information at:
194              
195             =over 4
196              
197             =item * RT: CPAN's request tracker (report bugs here)
198              
199             L
200              
201             =item * AnnoCPAN: Annotated CPAN documentation
202              
203             L
204              
205             =item * CPAN Ratings
206              
207             L
208              
209             =item * Search CPAN
210              
211             L
212              
213             =back
214              
215              
216             =head1 ACKNOWLEDGEMENTS
217              
218              
219             =head1 LICENSE AND COPYRIGHT
220              
221             Copyright 2014 Chris Travers.
222              
223             This program is released under the following license: BSD
224              
225              
226             =cut
227              
228             1; # End of PGObject::Util::DBMethod