File Coverage

blib/lib/Jifty/DBI/Record.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Jifty::DBI::Record;
2              
3 8     8   1208326 use strict;
  8         15  
  8         260  
4 8     8   37 use warnings;
  8         11  
  8         269  
5              
6 8     8   3887 use Class::ReturnValue ();
  8         89019  
  8         168  
7 8     8   6085 use Lingua::EN::Inflect ();
  8         173867  
  8         405  
8 8     8   4429 use Jifty::DBI::Column ();
  0            
  0            
9             use UNIVERSAL::require ();
10              
11             use base qw/
12             Class::Data::Inheritable
13             Jifty::DBI::HasFilters
14             /;
15              
16             our $VERSION = '0.01';
17              
18             Jifty::DBI::Record->mk_classdata(qw/COLUMNS/);
19             Jifty::DBI::Record->mk_classdata(qw/TABLE_NAME/ );
20             Jifty::DBI::Record->mk_classdata(qw/_READABLE_COLS_CACHE/);
21             Jifty::DBI::Record->mk_classdata(qw/_WRITABLE_COLS_CACHE/);
22             Jifty::DBI::Record->mk_classdata(qw/_COLUMNS_CACHE/ );
23              
24             =head1 NAME
25              
26             Jifty::DBI::Record - Superclass for records loaded by Jifty::DBI::Collection
27              
28             =head1 SYNOPSIS
29              
30             package MyRecord;
31             use base qw/Jifty::DBI::Record/;
32              
33             =head1 DESCRIPTION
34              
35             Jifty::DBI::Record encapuslates records and tables as part of the L<Jifty::DBI>
36             object-relational mapper.
37              
38             =head1 METHODS
39              
40             =head2 new ARGS
41              
42             Instantiate a new, empty record object.
43              
44             ARGS is a hash used to pass parameters to the C<_init()> function.
45              
46             Unless it is overloaded, the _init() function expects one key of
47             'handle' with a value containing a reference to a Jifty::DBI::Handle
48             object.
49              
50             =cut
51              
52             sub new {
53             my $proto = shift;
54              
55             my $class = ref($proto) || $proto;
56             my $self = {};
57             bless( $self, $class );
58              
59             $self->_init_columns() unless $self->COLUMNS;
60             $self->input_filters('Jifty::DBI::Filter::Truncate');
61              
62             if ( scalar(@_) == 1 ) {
63             Carp::cluck("new(\$handle) is deprecated, use new( handle => \$handle )");
64             $self->_init( handle => shift );
65             } else {
66             $self->_init(@_);
67             }
68              
69             return $self;
70             }
71              
72             # Not yet documented here. Should almost certainly be overloaded.
73             sub _init {
74             my $self = shift;
75             my %args = (@_);
76             if ( $args{'handle'} ) {
77             $self->_handle( $args{'handle'} );
78             }
79              
80             }
81              
82             sub import {
83             my $class = shift;
84             my ($flag) = @_;
85             if ($class->isa(__PACKAGE__) and defined $flag and $flag eq '-base') {
86             my $descendant = (caller)[0];
87             no strict 'refs';
88             push @{$descendant . '::ISA'}, $class;
89             shift;
90              
91             # run the schema callback
92             my $callback = shift;
93             $callback->() if $callback;
94             }
95             $class->SUPER::import(@_);
96             }
97              
98             =head2 id
99              
100             Returns this row's primary key.
101              
102             =cut
103              
104             sub id {
105             my $pkey = $_[0]->_primary_key();
106             my $ret = $_[0]->{'values'}->{$pkey};
107             return $ret;
108             }
109              
110             =head2 primary_keys
111              
112             Return a hash of the values of our primary keys for this function.
113              
114             =cut
115              
116             sub primary_keys {
117             my $self = shift;
118             my %hash
119             = map { $_ => $self->{'values'}->{$_} } @{ $self->_primary_keys };
120             return (%hash);
121             }
122              
123              
124             =head2 _accessible COLUMN ATTRIBUTE
125              
126             Private method.
127              
128             DEPRECATED
129              
130             Returns undef unless C<COLUMN> has a true value for C<ATTRIBUTE>.
131              
132             Otherwise returns C<COLUMN>'s value for that attribute.
133              
134              
135             =cut
136              
137             sub _accessible {
138             my $self = shift;
139             my $column_name = shift;
140             my $attribute = lc( shift || '' );
141             my $col = $self->column($column_name);
142             return undef unless ( $col and $col->can($attribute) );
143             return $col->$attribute();
144              
145             }
146              
147             =head2 _primary_keys
148              
149             Return our primary keys. (Subclasses should override this, but our
150             default is that we have one primary key, named 'id'.)
151              
152             =cut
153              
154             sub _primary_keys {
155             my $self = shift;
156             return ['id'];
157             }
158              
159             sub _primary_key {
160             my $self = shift;
161             my $pkeys = $self->_primary_keys();
162             die "No primary key" unless ( ref($pkeys) eq 'ARRAY' and $pkeys->[0] );
163             die "Too many primary keys" unless ( scalar(@$pkeys) == 1 );
164             return $pkeys->[0];
165             }
166              
167             =head2 _init_columns
168              
169             Sets up the primary key columns.
170              
171             =cut
172              
173             sub _init_columns {
174             my $self = shift;
175              
176             return if defined $self->COLUMNS;
177              
178             $self->COLUMNS( {} );
179              
180             foreach my $column_name ( @{ $self->_primary_keys } ) {
181             my $column = $self->add_column($column_name);
182             $column->writable(0);
183             $column->readable(1);
184             $column->type('serial');
185             $column->mandatory(1);
186              
187             $self->_init_methods_for_column($column);
188             }
189             }
190              
191             sub _init_methods_for_column {
192             my $self = $_[0];
193             my $column = $_[1];
194             my $column_name
195             = ( $column->aliased_as ? $column->aliased_as : $column->name );
196             my $package = ref($self) || $self;
197              
198             # Make sure column has a record_class set as not all columns are added
199             # through add_column
200             $column->record_class( $package ) if not $column->record_class;
201              
202             no strict 'refs'; # We're going to be defining subs
203              
204             if ( not $self->can($column_name) ) {
205             # Accessor
206             my $subref;
207             if ( $column->readable ) {
208             if ( UNIVERSAL::isa( $column->refers_to, "Jifty::DBI::Record" ) )
209             {
210             $subref = sub {
211             $_[0]->_to_record( $column_name,
212             $_[0]->__value($column_name) );
213             };
214             } elsif (
215             UNIVERSAL::isa(
216             $column->refers_to, "Jifty::DBI::Collection"
217             )
218             )
219             {
220             $subref = sub { $_[0]->_collection_value($column_name) };
221             } else {
222             $subref = sub { return ( $_[0]->_value($column_name) ) };
223             }
224             } else {
225             $subref = sub { return '' }
226             }
227             *{ $package . "::" . $column_name } = $subref;
228              
229             }
230              
231             if ( not $self->can( "set_" . $column_name ) ) {
232             # Mutator
233             my $subref;
234             if ( $column->writable ) {
235             if ( UNIVERSAL::isa( $column->refers_to, "Jifty::DBI::Record" ) )
236             {
237             $subref = sub {
238             my $self = shift;
239             my $val = shift;
240              
241             $val = $val->id
242             if UNIVERSAL::isa( $val, 'Jifty::DBI::Record' );
243             return (
244             $self->_set( column => $column_name, value => $val )
245             );
246             };
247             } elsif (
248             UNIVERSAL::isa(
249             $column->refers_to, "Jifty::DBI::Collection"
250             )
251             )
252             { # XXX elw: collections land here, now what?
253             my $ret = Class::ReturnValue->new();
254             my $message = "Collection column '$column_name' not writable";
255             $ret->as_array( 0, $message );
256             $ret->as_error(
257             errno => 3,
258             do_backtrace => 0,
259             message => $message
260             );
261             $subref = sub { return ( $ret->return_value ); };
262             } else {
263             $subref = sub {
264             return (
265             $_[0]->_set( column => $column_name, value => $_[1] )
266             );
267             };
268             }
269             } else {
270             my $ret = Class::ReturnValue->new();
271             my $message = 'Immutable column';
272             $ret->as_array( 0, $message );
273             $ret->as_error(
274             errno => 3,
275             do_backtrace => 0,
276             message => $message
277             );
278             $subref = sub { return ( $ret->return_value ); };
279             }
280             *{ $package . "::" . "set_" . $column_name } = $subref;
281             }
282             }
283              
284              
285             =head2 _to_record COLUMN VALUE
286              
287             This B<PRIVATE> method takes a column name and a value for that column.
288              
289             It returns C<undef> unless C<COLUMN> is a valid column for this record
290             that refers to another record class.
291              
292             If it is valid, this method returns a new record object with an id
293             of C<VALUE>.
294              
295             =cut
296              
297             sub _to_record {
298             my $self = shift;
299             my $column_name = shift;
300             my $value = shift;
301              
302             my $column = $self->column($column_name);
303             my $classname = $column->refers_to();
304             my $remote_column = $column->by() || 'id';
305              
306             return unless defined $value;
307             return undef unless $classname;
308             return unless UNIVERSAL::isa( $classname, 'Jifty::DBI::Record' );
309              
310             # XXX TODO FIXME we need to figure out the right way to call new here
311             # perhaps the handle should have an initiializer for records/collections
312             my $object = $classname->new( handle => $self->_handle );
313             $object->load_by_cols( $remote_column => $value );
314             return $object;
315             }
316              
317             sub _collection_value {
318             my $self = shift;
319              
320             my $method_name = shift;
321             return unless defined $method_name;
322              
323             my $column = $self->column($method_name);
324             my $classname = $column->refers_to();
325              
326             return undef unless $classname;
327             return unless UNIVERSAL::isa( $classname, 'Jifty::DBI::Collection' );
328              
329             if ( my $prefetched_col = $self->_prefetched_collection($method_name)) {
330             return $prefetched_col;
331             }
332              
333             use Devel::SimpleTrace;
334             my $coll = $classname->new( handle => $self->_handle );
335             $coll->limit( column => $column->by(), value => $self->id );
336             return $coll;
337             }
338              
339             sub _prefetched_collection {
340             my $self =shift;
341             my $column_name = shift;
342             if (@_) {
343             $self->{'_prefetched_collections'}->{$column_name} = shift;
344             } else {
345             return $self->{'_prefetched_collections'}->{$column_name};
346             }
347              
348             }
349              
350              
351             =head2 add_column
352              
353             =cut
354              
355             sub add_column {
356             my $self = shift;
357             my $name = shift;
358             $name = lc $name;
359            
360             $self->COLUMNS->{$name} = Jifty::DBI::Column->new()
361             unless exists $self->COLUMNS->{$name};
362             $self->_READABLE_COLS_CACHE(undef);
363             $self->_WRITABLE_COLS_CACHE(undef);
364             $self->_COLUMNS_CACHE(undef );
365             $self->COLUMNS->{$name}->name($name);
366              
367             my $class = ref( $self ) || $self;
368             $self->COLUMNS->{$name}->record_class( $class );
369              
370             return $self->COLUMNS->{$name};
371             }
372              
373             =head2 column
374              
375             my $value = $self->column($column);
376              
377             Returns the $value of a $column.
378              
379             =cut
380              
381             sub column {
382             my $self = shift;
383             my $name = lc( shift || '' );
384             my $col = $self->COLUMNS;
385             return undef unless $col && exists $col->{$name};
386             return $col->{$name};
387              
388             }
389              
390             =head2 columns
391              
392             my @columns = $record->columns;
393              
394             Returns a sorted list of a $record's @columns.
395              
396             =cut
397              
398             sub columns {
399             my $self = shift;
400             return @{$self->_COLUMNS_CACHE() || $self->_COLUMNS_CACHE([
401             sort {
402             ( ( ( $b->type || '' ) eq 'serial' )
403             <=> ( ( $a->type || '' ) eq 'serial' ) )
404             or ( ($a->sort_order || 0) <=> ($b->sort_order || 0))
405             or ( $a->name cmp $b->name )
406             } values %{ $self->COLUMNS }
407              
408              
409             ])}
410              
411             }
412              
413             # sub {{{ readable_attributes
414              
415             =head2 readable_attributes
416              
417             Returns a list this table's readable columns
418              
419             =cut
420              
421             sub readable_attributes {
422             my $self = shift;
423             return @{$self->_READABLE_COLS_CACHE() || $self->_READABLE_COLS_CACHE([sort map { $_->name } grep { $_->readable } $self->columns])};
424             }
425              
426             =head2 writable_attributes
427              
428             Returns a list of this table's writable columns
429              
430              
431             =cut
432              
433             sub writable_attributes {
434             my $self = shift;
435             return @{$self->_WRITABLE_COLS_CACHE() || $self->_WRITABLE_COLS_CACHE([sort map { $_->name } grep { $_->writable } $self->columns])};
436             }
437              
438             =head2 record values
439              
440             As you've probably already noticed, C<Jifty::DBI::Record> autocreates methods for your
441             standard get/set accessors. It also provides you with some hooks to massage the values
442             being loaded or stored.
443              
444             When you fetch a record value by calling C<$my_record-E<gt>some_field>, C<Jifty::DBI::Record>
445             provides the following hook
446              
447             =over
448              
449              
450              
451             =item after_I<column_name>
452              
453             This hook is called with a reference to the value returned by
454             Jifty::DBI. Its return value is discarded.
455              
456             =back
457              
458             When you set a value, C<Jifty::DBI> provides the following hooks
459              
460             =over
461              
462             =item before_set_I<column_name> PARAMHASH
463              
464             C<Jifty::DBI::Record> passes this function a reference to a paramhash
465             composed of:
466              
467             =over
468              
469             =item column
470              
471             The name of the column we're updating.
472              
473             =item value
474              
475             The new value for I<column>.
476              
477             =item is_sql_function
478              
479             A boolean that, if true, indicates that I<value> is an SQL function,
480             not just a value.
481              
482             =back
483              
484             If before_set_I<column_name> returns false, the new value isn't set.
485              
486             =item after_set_I<column_name> PARAMHASH
487              
488             This hook will be called after a value is successfully set in the
489             database. It will be called with a reference to a paramhash that
490             contains C<column> and C<value> keys. If C<value> was a SQL function,
491             it will now contain the actual value that was set.
492              
493             This hook's return value is ignored.
494              
495             =item validate_I<column_name> VALUE
496              
497             This hook is called just before updating the database. It expects the
498             actual new value you're trying to set I<column_name> to. It returns
499             two values. The first is a boolean with truth indicating success. The
500             second is an optional message. Note that validate_I<column_name> may be
501             called outside the context of a I<set> operation to validate a potential
502             value. (The Jifty application framework uses this as part of its AJAX
503             validation system.)
504              
505             =back
506              
507              
508             =cut
509              
510             =head2 _value
511              
512             _value takes a single column name and returns that column's value for
513             this row. Subclasses can override _value to insert custom access
514             control.
515              
516             =cut
517              
518             sub _value {
519             my $self = shift;
520             my $column = shift;
521              
522             my $value = $self->__value( $column => @_ );
523             my $method = $self->can("after_$column");
524             $method->( $self, \$value ) if $method;
525             return $value;
526             }
527              
528             =head2 __value
529              
530             Takes a column name and returns that column's value. Subclasses should
531             never override __value.
532              
533             =cut
534              
535             sub __value {
536             my $self = shift;
537              
538             my $column_name = lc(shift);
539             # If the requested column is actually an alias for another, resolve it.
540             my $column = $self->column($column_name);
541             if ($column and defined $column->alias_for_column ) {
542             $column = $self->column($column->alias_for_column());
543             $column_name = $column->name;
544             }
545              
546             return unless ($column);
547              
548             # In the default case of "yeah, we have a value", return it as
549             # fast as we can.
550             return $self->{'values'}{$column_name}
551             if ( $self->{'fetched'}{$column_name}
552             && $self->{'decoded'}{$column_name} );
553              
554             if ( !$self->{'fetched'}{ $column_name } and my $id = $self->id() ) {
555             my $pkey = $self->_primary_key();
556             my $query_string = "SELECT "
557             . $column_name
558             . " FROM "
559             . $self->table
560             . " WHERE $pkey = ?";
561             my $sth = $self->_handle->simple_query( $query_string, $id );
562             my ($value) = eval { $sth->fetchrow_array() };
563             warn $@ if $@;
564              
565             $self->{'values'}{ $column_name } = $value;
566             $self->{'fetched'}{ $column_name } = 1;
567             }
568             unless ( $self->{'decoded'}{ $column_name } ) {
569             $self->_apply_output_filters(
570             column => $column,
571             value_ref => \$self->{'values'}{ $column_name },
572             ) if exists $self->{'values'}{ $column_name };
573             $self->{'decoded'}{ $column_name } = 1;
574             }
575              
576             return $self->{'values'}{ $column_name };
577             }
578              
579             =head2 as_hash
580              
581             Returns a version of this object's readable columns rendered as a hash of key => value pairs
582              
583             =cut
584              
585             sub as_hash {
586             my $self = shift;
587             my %values;
588             map {$values{$_} = $self->$_()} $self->readable_attributes ;
589             return %values;
590             }
591              
592              
593              
594             =head2 _set
595              
596             _set takes a single column name and a single unquoted value.
597             It updates both the in-memory value of this column and the in-database copy.
598             Subclasses can override _set to insert custom access control.
599              
600             =cut
601              
602             sub _set {
603             my $self = shift;
604             my %args = (
605             'column' => undef,
606             'value' => undef,
607             'is_sql_function' => undef,
608             @_
609             );
610              
611             my $method = "before_set_" . $args{column};
612             if ( $self->can($method) ) {
613             my $before_set_ret = $self->$method( \%args );
614             return $before_set_ret
615             unless ($before_set_ret);
616             }
617              
618             my $ok = $self->__set(%args);
619              
620             return $ok unless $ok;
621              
622             $method = "after_set_" . $args{column};
623             if( $self->can($method) ) {
624             # Fetch the value back to make sure we have the actual value
625             my $value = $self->_value($args{column});
626             $self->$method({column => $args{column}, value => $value});
627             }
628              
629             return $ok;
630             }
631              
632             sub __set {
633             my $self = shift;
634              
635             my %args = (
636             'column' => undef,
637             'value' => undef,
638             'is_sql_function' => undef,
639             @_
640             );
641              
642             my $ret = Class::ReturnValue->new();
643              
644             my $column = $self->column( $args{'column'} );
645             unless ($column) {
646             $ret->as_array( 0, 'No column specified' );
647             $ret->as_error(
648             errno => 5,
649             do_backtrace => 0,
650             message => "No column specified"
651             );
652             return ( $ret->return_value );
653             }
654              
655             $self->_apply_input_filters(
656             column => $column,
657             value_ref => \$args{'value'}
658             );
659              
660             # if value is not fetched or it's allready decoded
661             # then we don't check eqality
662             # we also don't call __value because it decodes value, but
663             # we need encoded value
664             if ( $self->{'fetched'}{ $column->name }
665             || !$self->{'decoded'}{ $column->name } )
666             {
667             if (( !defined $args{'value'}
668             && !defined $self->{'values'}{ $column->name }
669             )
670             || ( defined $args{'value'}
671             && defined $self->{'values'}{ $column->name }
672              
673             # XXX: This is a bloody hack to stringify DateTime
674             # and other objects for compares
675             && $args{value}
676             . "" eq ""
677             . $self->{'values'}{ $column->name }
678             )
679             )
680             {
681             $ret->as_array( 1, "That is already the current value" );
682             return ( $ret->return_value );
683             }
684             }
685              
686             if ( my $sub = $column->validator ) {
687             my ( $ok, $msg ) = $sub->( $self, $args{'value'} );
688             unless ($ok) {
689             $ret->as_array( 0, 'Illegal value for ' . $column->name );
690             $ret->as_error(
691             errno => 3,
692             do_backtrace => 0,
693             message => "Illegal value for " . $column->name
694             );
695             return ( $ret->return_value );
696             }
697             }
698            
699              
700             # Implement 'is distinct' checking
701             if ( $column->distinct ) {
702             my $ret = $self->is_distinct( $column->name, $args{'value'} );
703             return ( $ret ) if not ( $ret );
704             }
705              
706             # The blob handling will destroy $args{'value'}. But we assign
707             # that back to the object at the end. this works around that
708             my $unmunged_value = $args{'value'};
709              
710             if ( $column->type =~ /^(text|longtext|clob|blob|lob|bytea)$/i ) {
711             my $bhash = $self->_handle->blob_params( $column->name, $column->type );
712             $bhash->{'value'} = $args{'value'};
713             $args{'value'} = $bhash;
714             }
715              
716             my $val = $self->_handle->update_record_value(
717             %args,
718             table => $self->table(),
719             primary_keys => { $self->primary_keys() }
720             );
721              
722             unless ($val) {
723             my $message
724             = $column->name . " could not be set to " . $args{'value'} . ".";
725             $ret->as_array( 0, $message );
726             $ret->as_error(
727             errno => 4,
728             do_backtrace => 0,
729             message => $message
730             );
731             return ( $ret->return_value );
732             }
733              
734             # If we've performed some sort of "functional update"
735             # then we need to reload the object from the DB to know what's
736             # really going on. (ex SET Cost = Cost+5)
737             if ( $args{'is_sql_function'} ) {
738              
739             # XXX TODO primary_keys
740             $self->load_by_cols( id => $self->id );
741             } else {
742             $self->{'values'}{ $column->name } = $unmunged_value;
743             $self->{'decoded'}{ $column->name } = 0;
744             }
745             $ret->as_array( 1, "The new value has been set." );
746             return ( $ret->return_value );
747             }
748              
749             =head2 load
750              
751             C<load> can be called as a class or object method.
752              
753             Takes a single argument, $id. Calls load_by_cols to retrieve the row
754             whose primary key is $id.
755              
756             =cut
757              
758             sub load {
759             my $self = shift;
760             return unless @_ and defined $_[0];
761              
762             return $self->load_by_cols( id => shift );
763             }
764              
765             =head2 load_by_cols
766              
767             C<load_by_cols> can be called as a class or object method.
768              
769             Takes a hash of columns and values. Loads the first record that matches all
770             keys.
771              
772             The hash's keys are the columns to look at.
773              
774             The hash's values are either: scalar values to look for
775             OR hash references which contain 'operator' and 'value'
776              
777             =cut
778              
779             sub load_by_cols {
780             my $class = shift;
781             my %hash = (@_);
782             my ($self);
783             if (ref($class)) {
784             ($self,$class) = ($class,undef);
785             } else {
786             $self = $class->new( handle => (delete $hash{'_handle'} || undef));
787             }
788              
789             my ( @bind, @phrases );
790             foreach my $key ( keys %hash ) {
791             if ( defined $hash{$key} && $hash{$key} ne '' ) {
792             my $op;
793             my $value;
794             my $function = "?";
795             if ( ref $hash{$key} eq 'HASH' ) {
796             $op = $hash{$key}->{operator};
797             $value = $hash{$key}->{value};
798             $function = $hash{$key}->{function} || "?";
799             } else {
800             $op = '=';
801             $value = $hash{$key};
802             }
803              
804             if (ref $value && $value->isa('Jifty::DBI::Record') ) {
805             # XXX TODO: check for proper foriegn keyness here
806             $value = $value->id;
807             }
808              
809              
810             push @phrases, "$key $op $function";
811             push @bind, $value;
812             } else {
813             push @phrases, "($key IS NULL OR $key = ?)";
814             my $column = $self->column($key);
815              
816             if ( $column->is_numeric ) {
817             push @bind, 0;
818             } else {
819             push @bind, '';
820             }
821              
822             }
823             }
824              
825             my $query_string = "SELECT * FROM "
826             . $self->table
827             . " WHERE "
828             . join( ' AND ', @phrases );
829             if ($class) { $self->_load_from_sql( $query_string, @bind ); return $self}
830             else {return $self->_load_from_sql( $query_string, @bind );}
831              
832             }
833              
834             =head2 load_by_primary_keys
835              
836             Loads records with a given set of primary keys.
837              
838             =cut
839              
840             sub load_by_primary_keys {
841             my $self = shift;
842             my $data = ( ref $_[0] eq 'HASH' ) ? $_[0] : {@_};
843              
844             my %cols = ();
845             foreach ( @{ $self->_primary_keys } ) {
846             return ( 0, "Missing PK column: '$_'" ) unless defined $data->{$_};
847             $cols{$_} = $data->{$_};
848             }
849             return ( $self->load_by_cols(%cols) );
850             }
851              
852             =head2 load_from_hash
853              
854             Takes a hashref, such as created by Jifty::DBI and populates this record's
855             loaded values hash.
856              
857             =cut
858              
859             sub load_from_hash {
860             my $class = shift;
861             my $hashref = shift;
862             my ($self);
863              
864             if (ref($class)) {
865             ($self,$class) = ($class,undef);
866             } else {
867             $self = $class->new( handle => (delete $hashref->{'_handle'} || undef));
868             }
869            
870              
871             foreach my $f ( keys %$hashref ) {
872             $self->{'fetched'}{ lc $f } = 1;
873             }
874              
875             $self->{'values'} = $hashref;
876             $self->{'decoded'} = {};
877             return $self->id();
878             }
879              
880             =head2 _load_from_sql QUERYSTRING @BIND_VALUES
881              
882             Load a record as the result of an SQL statement
883              
884             =cut
885              
886             sub _load_from_sql {
887             my $self = shift;
888             my $query_string = shift;
889             my @bind_values = (@_);
890              
891             my $sth = $self->_handle->simple_query( $query_string, @bind_values );
892              
893             #TODO this only gets the first row. we should check if there are more.
894              
895             return ( 0, "Couldn't execute query" ) unless $sth;
896              
897             $self->{'values'} = $sth->fetchrow_hashref;
898             $self->{'fetched'} = {};
899             $self->{'decoded'} = {};
900             if ( !$self->{'values'} && $sth->err ) {
901             return ( 0, "Couldn't fetch row: " . $sth->err );
902             }
903              
904             unless ( $self->{'values'} ) {
905             return ( 0, "Couldn't find row" );
906             }
907              
908             ## I guess to be consistant with the old code, make sure the primary
909             ## keys exist.
910              
911             if ( grep { not defined } $self->primary_keys ) {
912             return ( 0, "Missing a primary key?" );
913             }
914              
915             foreach my $f ( keys %{ $self->{'values'} } ) {
916             $self->{'fetched'}{ lc $f } = 1;
917             }
918             return ( 1, "Found object" );
919              
920             }
921              
922             =head2 create PARAMHASH
923              
924             C<create> can be called as either a class or object method
925              
926             This method creates a new record with the values specified in the PARAMHASH.
927              
928             This method calls two hooks in your subclass:
929              
930             =over
931              
932             =item before_create
933              
934             This method is called before trying to create our row in the
935             database. It's handed a reference to your paramhash. (That means it
936             can modify your parameters on the fly). C<before_create> returns a
937             true or false value. If it returns false, the create is aborted.
938              
939             =item after_create
940              
941             This method is called after attempting to insert the record into the
942             database. It gets handed a reference to the return value of the
943             insert. That'll either be a true value or a L<Class::ReturnValue>
944              
945             =back
946              
947              
948             =cut
949              
950             sub create {
951             my $class = shift;
952             my %attribs = @_;
953              
954             my ($self);
955             if (ref($class)) {
956             ($self,$class) = ($class,undef);
957             } else {
958             $self = $class->new( handle => (delete $attribs{'_handle'} || undef));
959             }
960              
961              
962              
963             if ( $self->can('before_create') ) {
964             my $before_ret = $self->before_create( \%attribs );
965             return ($before_ret) unless ($before_ret);
966             }
967              
968             foreach my $column_name ( keys %attribs ) {
969             my $column = $self->column($column_name);
970             unless ($column) {
971             Carp::confess "$column_name isn't a column we know about";
972             }
973             if ( $column->readable
974             and $column->refers_to
975             and UNIVERSAL::isa( $column->refers_to, "Jifty::DBI::Record" ) )
976             {
977             $attribs{$column_name} = $attribs{$column_name}->id
978             if UNIVERSAL::isa( $attribs{$column_name},
979             'Jifty::DBI::Record' );
980             }
981              
982             $self->_apply_input_filters(
983             column => $column,
984             value_ref => \$attribs{$column_name},
985             );
986              
987             # Implement 'is distinct' checking
988             if ( $column->distinct ) {
989             my $ret = $self->is_distinct( $column_name, $attribs{$column_name} );
990             return ( $ret ) if not ( $ret );
991             }
992              
993             if ( $column->type =~ /^(text|longtext|clob|blob|lob|bytea)$/i ) {
994             my $bhash = $self->_handle->blob_params( $column_name, $column->type );
995             $bhash->{'value'} = $attribs{$column_name};
996             $attribs{$column_name} = $bhash;
997             }
998             }
999              
1000             for my $column ($self->columns) {
1001             if (not defined $attribs{$column->name} and defined $column->default and not ref $column->default) {
1002             $attribs{$column->name} = $column->default;
1003             }
1004             if (not defined $attribs{$column->name} and $column->mandatory and $column->type ne "serial" ) {
1005             # Enforce "mandatory"
1006             Carp::carp "Did not supply value for mandatory column ".$column->name;
1007             return ( 0 );
1008             }
1009             }
1010              
1011             my $ret = $self->_handle->insert( $self->table, %attribs );
1012             $self->after_create( \$ret ) if $self->can('after_create');
1013             if ($class) {
1014             $self->load_by_cols(id => $ret);
1015             return ($self);
1016             }
1017             else {
1018             return ($ret);
1019             }
1020             }
1021              
1022             =head2 delete
1023              
1024             Delete this record from the database. On failure return a
1025             Class::ReturnValue with the error. On success, return 1;
1026              
1027             This method has two hooks
1028              
1029             =over
1030              
1031             =item before_delete
1032              
1033             This method is called before the record deletion, if it exists. On
1034             failure it returns a L<Class::ReturnValue> with the error. On success
1035             it returns 1.
1036              
1037             If this method returns an error, it causes the delete to abort and return
1038             the return value from this hook.
1039              
1040             =item after_delete
1041              
1042             This method is called after deletion, with a reference to the return
1043             value from the delete operation.
1044              
1045             =back
1046              
1047             =cut
1048              
1049             sub delete {
1050             my $self = shift;
1051             if ( $self->can('before_delete') ) {
1052             my $before_ret = $self->before_delete();
1053             return $before_ret unless ($before_ret);
1054             }
1055             my $ret = $self->__delete;
1056             $self->after_delete( \$ret ) if $self->can('after_delete');
1057             return ($ret);
1058              
1059             }
1060              
1061             sub __delete {
1062             my $self = shift;
1063              
1064             #TODO Check to make sure the key's not already listed.
1065             #TODO Update internal data structure
1066              
1067             ## Constructs the where clause.
1068             my @bind = ();
1069             my %pkeys = $self->primary_keys();
1070             my $where = 'WHERE ';
1071             foreach my $key ( keys %pkeys ) {
1072             $where .= $key . "=?" . " AND ";
1073             push( @bind, $pkeys{$key} );
1074             }
1075              
1076             $where =~ s/AND\s$//;
1077             my $query_string = "DELETE FROM " . $self->table . ' ' . $where;
1078             my $return = $self->_handle->simple_query( $query_string, @bind );
1079              
1080             if ( UNIVERSAL::isa( 'Class::ReturnValue', $return ) ) {
1081             return ($return);
1082             } else {
1083             return (1);
1084             }
1085             }
1086              
1087             =head2 table
1088              
1089             This method returns this class's default table name. It uses
1090             Lingua::EN::Inflect to pluralize the class's name as we believe that
1091             class names for records should be in the singular and table names
1092             should be plural.
1093              
1094             If your class name is C<My::App::Rhino>, your table name will default
1095             to C<rhinos>. If your class name is C<My::App::RhinoOctopus>, your
1096             default table name will be C<rhino_octopuses>. Not perfect, but
1097             arguably correct.
1098              
1099             =cut
1100              
1101             sub table {
1102             my $self = shift;
1103             $self->TABLE_NAME($self->_guess_table_name) unless ($self->TABLE_NAME());
1104             return $self->TABLE_NAME();
1105             }
1106              
1107             =head2 collection_class
1108              
1109             Returns the collection class which this record belongs to; override this to
1110             subclass. If you haven't specified a collection class, this returns a best
1111             guess at the name of the collection class for this collection.
1112              
1113             It uses a simple heuristic to determine the collection class name -- It
1114             appends "Collection" to its own name. If you want to name your records
1115             and collections differently, go right ahead, but don't say we didn't
1116             warn you.
1117              
1118             =cut
1119              
1120             sub collection_class {
1121             my $self = shift;
1122             my $class = ref($self) || $self;
1123             $class . 'Collection';
1124             }
1125              
1126             =head2 _guess_table_name
1127              
1128             Guesses a table name based on the class's last part.
1129              
1130              
1131             =cut
1132              
1133             sub _guess_table_name {
1134             my $self = shift;
1135             my $class = ref($self) ? ref($self) : $self;
1136             die "Couldn't turn " . $class . " into a table name"
1137             unless ( $class =~ /(?:\:\:)?(\w+)$/ );
1138             my $table = $1;
1139             $table =~ s/(?<=[a-z])([A-Z]+)/"_" . lc($1)/eg;
1140             $table =~ tr/A-Z/a-z/;
1141             $table = Lingua::EN::Inflect::PL_N($table);
1142             return ($table);
1143              
1144             }
1145              
1146             =head2 _handle
1147              
1148             Returns or sets the current Jifty::DBI::Handle object
1149              
1150             =cut
1151              
1152             sub _handle {
1153             my $self = shift;
1154             if (@_) {
1155             $self->{'DBIxHandle'} = shift;
1156             }
1157             return ( $self->{'DBIxHandle'} );
1158             }
1159              
1160             =head2 PRIVATE refers_to
1161              
1162             used for the declarative syntax
1163              
1164              
1165             =cut
1166              
1167             sub _filters {
1168             my $self = shift;
1169             my %args = ( direction => 'input', column => undef, @_ );
1170              
1171             my @filters = ();
1172             my @objs = ( $self, $args{'column'}, $self->_handle );
1173             @objs = reverse @objs if $args{'direction'} eq 'output';
1174             my $method = $args{'direction'} . "_filters";
1175             foreach my $obj (@objs) {
1176             push @filters, $obj->$method();
1177             }
1178             return grep $_, @filters;
1179             }
1180              
1181             sub _apply_input_filters {
1182             return (shift)->_apply_filters( direction => 'input', @_ );
1183             }
1184              
1185             sub _apply_output_filters {
1186             return (shift)->_apply_filters( direction => 'output', @_ );
1187             }
1188              
1189             sub _apply_filters {
1190             my $self = shift;
1191             my %args = (
1192             direction => 'input',
1193             column => undef,
1194             value_ref => undef,
1195             @_
1196             );
1197              
1198             my @filters = $self->_filters(%args);
1199             my $action = $args{'direction'} eq 'output' ? 'decode' : 'encode';
1200             foreach my $filter_class (@filters) {
1201             local $UNIVERSAL::require::ERROR;
1202             $filter_class->require() unless
1203             $INC{ join('/', split(/::/,$filter_class)).".pm" };
1204              
1205             if ($UNIVERSAL::require::ERROR) {
1206             warn $UNIVERSAL::require::ERROR;
1207             next;
1208             }
1209             my $filter = $filter_class->new(
1210             record => $self,
1211             column => $args{'column'},
1212             value_ref => $args{'value_ref'},
1213             );
1214              
1215             # XXX TODO error proof this
1216             $filter->$action();
1217             }
1218             }
1219              
1220             =head2 is_distinct COLUMN_NAME, VALUE
1221              
1222             Checks to see if there is already a record in the database where
1223             COLUMN_NAME equals VALUE. If no such record exists then the
1224             COLUMN_NAME and VALUE pair is considered distinct and it returns 1.
1225             If a value is already present the test is considered to have failed
1226             and it returns a L<Class::ReturnValue> with the error.
1227              
1228             =cut
1229              
1230             sub is_distinct {
1231             my $self = shift;
1232             my $column = shift;
1233             my $value = shift;
1234              
1235             my $record = $self->new( handle => $self->_handle );
1236             $record->load_by_cols ( $column => $value );
1237              
1238             my $ret = Class::ReturnValue->new();
1239              
1240             if( $record->id ) {
1241             $ret->as_array( 0, "Value already exists for unique column $column");
1242             $ret->as_error(
1243             errno => 3,
1244             do_backtrace => 0,
1245             message => "Value already exists for unique column $column",
1246             );
1247             return ( $ret->return_value );
1248             } else {
1249             return (1);
1250             }
1251             }
1252              
1253             1;
1254              
1255             __END__
1256              
1257              
1258              
1259             =head1 AUTHOR
1260              
1261             Jesse Vincent <jesse@bestpractical.com>, Alex Vandiver <alexmv@bestpractical.com>, David Glasser <glasser@bestpractical.com>, Ruslan Zakirov <ruslan.zakirov@gmail.com>
1262              
1263             Based on DBIx::SearchBuilder::Record, whose credits read:
1264              
1265             Jesse Vincent, <jesse@fsck.com>
1266             Enhancements by Ivan Kohler, <ivan-rt@420.am>
1267             Docs by Matt Knopp <mhat@netlag.com>
1268              
1269             =head1 SEE ALSO
1270              
1271             L<Jifty::DBI>
1272              
1273             =cut
1274              
1275