1package SPOPS;
2
3# $Id: SPOPS.pm,v 3.39 2004/06/02 00:48:20 lachoy Exp $
4
5use strict;
6use base  qw( Exporter ); # Class::Observable
7use Data::Dumper    qw( Dumper );
8use Log::Log4perl   qw( get_logger );
9use SPOPS::ClassFactory::DefaultBehavior;
10use SPOPS::Exception;
11use SPOPS::Tie      qw( IDX_CHANGE IDX_SAVE IDX_CHECK_FIELDS IDX_LAZY_LOADED );
12use SPOPS::Tie::StrictField;
13use SPOPS::Secure   qw( SEC_LEVEL_WRITE );
14
15my $log = get_logger();
16
17$SPOPS::AUTOLOAD  = '';
18$SPOPS::VERSION   = '0.87';
19$SPOPS::Revision  = sprintf("%d.%02d", q$Revision: 3.39 $ =~ /(\d+)\.(\d+)/);
20
21# DEPRECATED
22
23sub DEBUG                { return 1 }
24sub set_global_debug     { warn "Global debugging not supported -- use log4perl instead!\n" }
25
26my ( $USE_CACHE );
27sub USE_CACHE            { return $USE_CACHE }
28sub set_global_use_cache { $USE_CACHE = $_[1] }
29
30@SPOPS::EXPORT_OK = qw( _w _wm DEBUG );
31
32require SPOPS::Utility;
33
34########################################
35# CLASS CONFIGURATION
36########################################
37
38# These are default configuration behaviors -- all SPOPS classes have
39# the option of using them or of halting behavior before they're
40# called
41
42sub behavior_factory {
43    my ( $class ) = @_;
44
45    $log->is_info &&
46        $log->info( "Installing SPOPS default behaviors for ($class)" );
47    return { manipulate_configuration =>
48                    \&SPOPS::ClassFactory::DefaultBehavior::conf_modify_config,
49             read_code                =>
50                    \&SPOPS::ClassFactory::DefaultBehavior::conf_read_code,
51             id_method                =>
52                    \&SPOPS::ClassFactory::DefaultBehavior::conf_id_method,
53             has_a                    =>
54                    \&SPOPS::ClassFactory::DefaultBehavior::conf_relate_hasa,
55             fetch_by                 =>
56                    \&SPOPS::ClassFactory::DefaultBehavior::conf_relate_fetchby,
57             add_rule                 =>
58                    \&SPOPS::ClassFactory::DefaultBehavior::conf_add_rules, };
59}
60
61
62########################################
63# CLASS INITIALIZATION
64########################################
65
66# Subclasses should almost certainly define some behavior here by
67# overriding this method
68
69sub class_initialize {}
70
71
72########################################
73# OBJECT CREATION/DESTRUCTION
74########################################
75
76# Constructor
77
78sub new {
79    my ( $pkg, $p ) = @_;
80    my $class = ref $pkg || $pkg;
81    my $params = {};
82    my $tie_class = 'SPOPS::Tie';
83
84    my $CONFIG = $class->CONFIG;
85
86    # Setup field checking if specified
87
88    if ( $CONFIG->{strict_field} || $p->{strict_field} ) {
89        my $fields = $class->field;
90        if ( keys %{ $fields } ) {
91            $params->{field} = [ keys %{ $fields } ];
92            $tie_class = 'SPOPS::Tie::StrictField'
93        }
94    }
95
96    # Setup lazy loading if specified
97
98    if ( ref $CONFIG->{column_group} eq 'HASH' and
99         keys %{ $CONFIG->{column_group} } ) {
100        $params->{is_lazy_load}  = 1;
101        $params->{lazy_load_sub} = $class->get_lazy_load_sub;
102    }
103
104    # Setup field mapping if specified
105
106    if ( ref $CONFIG->{field_map} eq 'HASH' and
107         scalar keys %{ $CONFIG->{field_map} } ) {
108        $params->{is_field_map} = 1;
109        $params->{field_map} = \%{ $CONFIG->{field_map} };
110    }
111
112    # Setup multivalue fields if specified
113
114    my $multivalue_ref = ref $CONFIG->{multivalue};
115    if ( $multivalue_ref eq 'HASH' or $multivalue_ref eq 'ARRAY' ) {
116        my $num = ( $multivalue_ref eq 'HASH' )
117                    ? scalar keys %{ $CONFIG->{multivalue} }
118                    : scalar @{ $CONFIG->{multivalue} };
119        if ( $num > 0 ) {
120            $params->{is_multivalue} = 1;
121            $params->{multivalue} = ( $multivalue_ref eq 'HASH' )
122                                      ? \%{ $CONFIG->{multivalue} }
123                                      : \@{ $CONFIG->{multivalue} };
124        }
125    }
126
127    $params->{is_lazy_load} ||= 0;
128    $params->{is_field_map} ||= 0;
129
130    $log->is_info &&
131        $log->info( "Creating new object of class ($class) with tie class ",
132                    "($tie_class); lazy loading ($params->{is_lazy_load});",
133                    "field mapping ($params->{is_field_map})" );
134
135    my ( %data );
136    my $internal = tie %data, $tie_class, $class, $params;
137    $log->is_debug &&
138        $log->debug( "Internal tie structure of new object: ", Dumper( $internal ) );
139    my $self = bless( \%data, $class );
140
141    # Set defaults if set, unless NOT specified
142
143    my $defaults = $p->{default_values} || $CONFIG->{default_values};
144    if ( ref $defaults eq 'HASH' and ! $p->{skip_default_values} ) {
145        foreach my $field ( keys %{ $defaults } ) {
146            if ( ref $defaults->{ $field } eq 'HASH' ) {
147                my $default_class  = $defaults->{ $field }{class};
148                my $default_method = $defaults->{ $field }{method};
149                unless ( $default_class and $default_method ) {
150                    $log->warn( "Cannot set default for ($field) without a class ",
151                                "AND method being defined." );
152                    next;
153                }
154                $self->{ $field } = eval { $default_class->$default_method( $field ) };
155                if ( $@ ) {
156                    $log->warn( "Cannot set default for ($field) in ($class) using",
157                           "($default_class) ($default_method): $@" );
158                }
159            }
160            elsif ( $defaults->{ $field } eq 'NOW' ) {
161                $self->{ $field } = SPOPS::Utility->now;
162            }
163            else {
164                $self->{ $field } = $defaults->{ $field };
165            }
166        }
167    }
168
169    $self->initialize( $p );
170    $self->has_change;
171    $self->clear_save;
172    $self->initialize_custom( $p );
173    return $self;
174}
175
176
177sub DESTROY {
178    my ( $self ) = @_;
179
180    # Need to check that $log exists because sometimes it gets
181    # destroyed before our SPOPS objects do
182
183    if ( $log ) {
184        $log->is_debug &&
185            $log->debug( "Destroying SPOPS object '", ref( $self ), "' ID: " .
186                         "'", $self->id, "' at time: ", scalar localtime );
187    }
188}
189
190
191# Create a new object from an old one, allowing any passed-in
192# values to override the ones from the old object
193
194sub clone {
195    my ( $self, $p ) = @_;
196    my $class = $p->{_class} || ref $self;
197    $log->is_info &&
198        $log->info( "Cloning new object of class '$class' from old ",
199                    "object of class '", ref( $self ), "'" );
200    my %initial_data = ();
201
202    my $id_field = $class->id_field;
203    if ( $id_field ) {
204        $initial_data{ $id_field } = $p->{ $id_field } || $p->{id};
205    }
206
207    my $fields = $self->_get_definitive_fields;
208    foreach my $field ( @{ $fields } ) {
209        next if ( $id_field and $field eq $id_field );
210        $initial_data{ $field } =
211            exists $p->{ $field } ? $p->{ $field } : $self->{ $field };
212    }
213
214    return $class->new({ %initial_data, skip_default_values => 1 });
215}
216
217
218# Simple initialization: subclasses can override for
219# field validation or whatever.
220
221sub initialize {
222    my ( $self, $p ) = @_;
223    $p ||= {};
224
225    # Creating a new object, all fields are set to 'loaded' so we don't
226    # try to lazy-load a field when the object hasn't even been saved
227
228    $self->set_all_loaded();
229
230    # We allow the user to substitute id => value instead for the
231    # specific fieldname.
232
233    $self->id( $p->{id} )  if ( $p->{id} );
234    #$p->{ $self->id_field } ||= $p->{id};
235
236    # Go through the data passed in and set data for fields used by
237    # this class
238
239    my $class_fields = $self->field || {};
240    while ( my ( $field, $value ) = each %{ $p } ) {
241        next unless ( $class_fields->{ $field } );
242        $self->{ $field } = $value;
243    }
244}
245
246# subclasses can override...
247sub initialize_custom { return }
248
249########################################
250# CONFIGURATION
251########################################
252
253# If a class doesn't define a config method then something is seriously wrong
254
255sub CONFIG {
256    require Carp;
257    Carp::croak "SPOPS class not created properly, since CONFIG being called ",
258                "from SPOPS.pm rather than your object class.";
259}
260
261
262# Some default configuration methods that all SPOPS classes use
263
264sub field               { return $_[0]->CONFIG->{field} || {}              }
265sub field_list          { return $_[0]->CONFIG->{field_list} || []         }
266sub field_raw           { return $_[0]->CONFIG->{field_raw} || []          }
267sub field_all_map {
268    return { map { $_ => 1 } ( @{ $_[0]->field_list }, @{ $_[0]->field_raw } ) }
269}
270sub id_field            { return $_[0]->CONFIG->{id_field}                 }
271sub creation_security   { return $_[0]->CONFIG->{creation_security} || {}  }
272sub no_security         { return $_[0]->CONFIG->{no_security}              }
273
274# if 'field_raw' defined use that, otherwise just return 'field_list'
275
276sub _get_definitive_fields {
277    my ( $self ) = @_;
278    my $fields = $self->field_raw;
279    unless ( ref $fields eq 'ARRAY' and scalar @{ $fields } > 0 ) {
280        $fields = $self->field_list;
281    }
282    return $fields;
283}
284
285########################################
286# STORABLE SERIALIZATION
287
288sub store {
289    my ( $self, @params ) = @_;
290    die "Not an object!" unless ( ref $self and $self->isa( 'SPOPS' ) );
291    require Storable;
292    return Storable::store( $self, @params );
293}
294
295sub nstore {
296    my ( $self, @params ) = @_;
297    die "Not an object!" unless ( ref $self and $self->isa( 'SPOPS' ) );
298    require Storable;
299    return Storable::nstore( $self, @params );
300}
301
302sub retrieve {
303    my ( $class, @params ) = @_;
304    require Storable;
305    return Storable::retrieve( @params );
306}
307
308sub fd_retrieve {
309    my ( $class, @params ) = @_;
310    require Storable;
311    return Storable::fd_retrieve( @params );
312}
313
314
315########################################
316# RULESET METHODS
317########################################
318
319# So all SPOPS classes have a ruleset_add in their lineage
320
321sub ruleset_add     { return __PACKAGE__ }
322sub ruleset_factory {}
323
324# These are actions to do before/after a fetch, save and remove; note
325# that overridden methods must return a 1 on success or the
326# fetch/save/remove will fail; this allows any of a number of rules to
327# short-circuit an operation; see RULESETS in POD
328#
329# clarification: $_[0] in the following can be *either* a class or an
330# object; $_[1] is the (optional) hashref passed as the only argument
331
332sub pre_fetch_action    { return $_[0]->ruleset_process_action( 'pre_fetch_action', $_[1]   ) }
333sub post_fetch_action   { return $_[0]->ruleset_process_action( 'post_fetch_action', $_[1] ) }
334sub pre_save_action     { return $_[0]->ruleset_process_action( 'pre_save_action', $_[1] ) }
335sub post_save_action    { return $_[0]->ruleset_process_action( 'post_save_action', $_[1] ) }
336sub pre_remove_action   { return $_[0]->ruleset_process_action( 'pre_remove_action', $_[1] ) }
337sub post_remove_action  { return $_[0]->ruleset_process_action( 'post_remove_action', $_[1] ) }
338
339#sub pre_fetch_action    { return shift->notify_observers( 'pre_fetch_action', @_   ) }
340#sub post_fetch_action   { return shift->notify_observers( 'post_fetch_action', @_ ) }
341#sub pre_save_action     { return shift->notify_observers( 'pre_save_action', @_ ) }
342#sub post_save_action    { return shift->notify_observers( 'post_save_action', @_ ) }
343#sub pre_remove_action   { return shift->notify_observers( 'pre_remove_action', @_ ) }
344#sub post_remove_action  { return shift->notify_observers( 'post_remove_action', @_ ) }
345
346# Go through all of the subroutines found in a particular class
347# relating to a particular action
348
349sub ruleset_process_action {
350    my ( $item, $action, $p ) = @_;
351    #die "This method is no longer used. Please see SPOPS::Manual::ObjectRules.\n";
352
353    my $class = ref $item || $item;
354
355    $action = lc $action;
356    $log->is_info &&
357        $log->info( "Trying to process $action for a '$class' object" );
358
359    # Grab the ruleset table for this class and immediately
360    # return if the list of rules to apply for this action is empty
361
362    my $rs_table = $item->RULESET;
363    unless ( ref $rs_table->{ $action } eq 'ARRAY'
364                 and scalar @{ $rs_table->{ $action } } > 0 ) {
365        $log->is_debug &&
366            $log->debug( "No rules to process for [$action]" );
367        return 1;
368    }
369    $log->is_info &&
370        $log->info( "Ruleset exists in class." );
371
372    # Cycle through the rules -- the only return value can be true or false,
373    # and false short-circuits the entire operation
374
375    my $count_rules = 0;
376    foreach my $rule_sub ( @{ $rs_table->{ $action } } ) {
377        $count_rules++;
378        unless ( $rule_sub->( $item, $p ) ) {
379            $log->warn( "Rule $count_rules of '$action' for class '$class' failed" );
380            return undef;
381        }
382    }
383    $log->is_info &&
384        $log->info( "$action processed ($count_rules rules successful) without error" );
385    return 1;
386}
387
388
389########################################
390# SERIALIZATION
391########################################
392
393# Routines for subclases to override
394
395sub save        { die "Subclass must implement save()\n" }
396sub fetch       { die "Subclass must implement fetch()\n" }
397sub remove      { die "Subclass must implement remove()\n" }
398sub log_action  { return 1 }
399
400# Define methods for implementors to override to do something in case
401# a fetch / save / remove fails
402
403sub fail_fetch  {}
404sub fail_save   {}
405sub fail_remove {}
406
407
408########################################
409# SERIALIZATION SUPPORT
410########################################
411
412sub fetch_determine_limit { return SPOPS::Utility->determine_limit( $_[1] ) }
413
414
415########################################
416# LAZY LOADING
417########################################
418
419sub get_lazy_load_sub { return \&perform_lazy_load }
420sub perform_lazy_load { return undef }
421
422sub is_loaded         { return tied( %{ $_[0] } )->{ IDX_LAZY_LOADED() }{ lc $_[1] } }
423
424sub set_loaded        { return tied( %{ $_[0] } )->{ IDX_LAZY_LOADED() }{ lc $_[1] }++ }
425
426sub set_all_loaded {
427    my ( $self ) = @_;
428    $log->is_info &&
429        $log->info( "Setting all fields to loaded for object class", ref $self );
430    $self->set_loaded( $_ ) for ( @{ $self->field_list } );
431}
432
433sub clear_loaded { tied( %{ $_[0] } )->{ IDX_LAZY_LOADED() }{ lc $_[1] } = undef }
434
435sub clear_all_loaded {
436    $log->is_info &&
437        $log->info( "Clearing all fields to unloaded for object class", ref $_[0] );
438    tied( %{ $_[0] } )->{ IDX_LAZY_LOADED() } = {};
439}
440
441
442########################################
443# FIELD CHECKING
444########################################
445
446# Is this object doing field checking?
447
448sub is_checking_fields { return tied( %{ $_[0] } )->{ IDX_CHECK_FIELDS() }; }
449
450
451########################################
452# MODIFICATION STATE
453########################################
454
455# Track whether this object has changed (keep 'changed()' for backward
456# compatibility)
457
458sub changed      { is_changed( @_ ) }
459sub is_changed   { return $_[0]->{ IDX_CHANGE() } }
460sub has_change   { $_[0]->{ IDX_CHANGE() } = 1 }
461sub clear_change { $_[0]->{ IDX_CHANGE() } = 0 }
462
463
464########################################
465# SERIALIZATION STATE
466########################################
467
468# Track whether this object has been saved (keep 'saved()' for
469# backward compatibility)
470
471sub saved        { is_saved( @_ ) }
472sub is_saved     { return $_[0]->{ IDX_SAVE() } }
473sub has_save     { $_[0]->{ IDX_SAVE() } = 1 }
474sub clear_save   { $_[0]->{ IDX_SAVE() } = 0 }
475
476
477########################################
478# OBJECT INFORMATION
479########################################
480
481# Return the name of this object (what type it is), title of the
482# object and url (in a hashref) to be used to make a link, or whatnot.
483
484sub object_description {
485    my ( $self ) = @_;
486    my $object_type = $self->CONFIG->{object_name};
487    my $title_info  = $self->CONFIG->{name};
488    my $title = '';
489    if ( ref $title_info eq 'CODE' ) {
490        warn "NOTE: Setting a coderef for the 'name' configuration ",
491             "key in [$object_type] is deprecated. It will be phased ",
492             "out.\n";
493        $title = eval { $title_info->( $self ) };
494    }
495    elsif ( exists $self->{ $title_info } ) {
496        $title = $self->{ $title_info };
497    }
498    else {
499        $title = eval { $self->$title_info() };
500    }
501    $title ||= 'Cannot find name';
502    my $oid       = $self->id;
503    my $id_field  = $self->id_field;
504    my $link_info = $self->CONFIG->{display};
505    my ( $url, $url_edit );
506    if ( $link_info->{url} ) {
507        $url       = "$link_info->{url}?" . $id_field . '=' . $oid;
508    }
509    if ( $link_info->{url_edit} ) {
510        $url_edit  = "$link_info->{url_edit}?" . $id_field . '=' . $oid;
511    }
512    else {
513        $url_edit  = "$link_info->{url}?edit=1;" . $id_field . '=' . $oid;
514    }
515    return { class     => ref $self,
516             object_id => $oid,
517             oid       => $oid,
518             id_field  => $id_field,
519             name      => $object_type,
520             title     => $title,
521             security  => $self->{tmp_security_level},
522             url       => $url,
523             url_edit  => $url_edit };
524}
525
526
527# This is very primitive, but objects that want something more
528# fancy/complicated can implement it for themselves
529
530sub as_string {
531    my ( $self ) = @_;
532    my $msg = '';
533    my $fields = $self->CONFIG->{as_string_order} || $self->field_list;
534    my $labels = $self->CONFIG->{as_string_label} || { map { $_ => $_ } @{ $fields } };
535    foreach my $field ( @{ $fields } ) {
536        $msg .= sprintf( "%-20s: %s\n", $labels->{ $field }, $self->{ $field } );
537    }
538    return $msg;
539}
540
541
542# This is even more primitive, but again, we're just providing the
543# basics :-)
544
545sub as_html {
546    my ( $self ) = @_;
547    return "<pre>" . $self->as_string . "\n</pre>\n";
548}
549
550
551########################################
552# SECURITY
553########################################
554
555# These are the default methods that classes not using security
556# inherit. Default action is WRITE, so everything is allowed
557
558sub check_security          { return SEC_LEVEL_WRITE }
559sub check_action_security   { return SEC_LEVEL_WRITE }
560sub create_initial_security { return 1 }
561
562
563########################################
564# CACHING
565########################################
566
567# NOTE: CACHING IS NOT FUNCTIONAL AND THESE MAY RADICALLY CHANGE
568
569# All objects are by default cached; set the key 'no_cache'
570# to a true value to *not* cache this object
571
572sub no_cache            { return $_[0]->CONFIG->{no_cache} || 0 }
573
574# Your class should determine how to get to the cache -- the normal
575# way is to have all your objects inherit from a common base class
576# which deals with caching, datasource handling, etc.
577
578sub global_cache        { return undef }
579
580# Actions to do before/after retrieving/saving/removing
581# an item from the cache
582
583sub pre_cache_fetch     { return 1 }
584sub post_cache_fetch    { return 1 }
585sub pre_cache_save      { return 1 }
586sub post_cache_save     { return 1 }
587sub pre_cache_remove    { return 1 }
588sub post_cache_remove   { return 1 }
589
590
591sub get_cached_object {
592    my ( $class, $p ) = @_;
593    return undef unless ( $p->{id} );
594    return undef unless ( $class->use_cache( $p ) );
595
596    # If we can retrieve an item from the cache, then create a new object
597    # and assign the values from the cache to it.
598    my $item_data = $class->global_cache->get({ class     => $class,
599                                                object_id => $p->{id} });
600    if ( $item_data ) {
601        $log->is_info &&
602            $log->info( "Retrieving from cache..." );
603        return $class->new( $item_data );
604    }
605    $log->is_info &&
606        $log->info( "Cached data not found." );
607    return undef;
608}
609
610
611sub set_cached_object {
612    my ( $self, $p ) = @_;
613    return undef unless ( ref $self );
614    return undef unless ( $self->id );
615    return undef unless ( $self->use_cache( $p ) );
616    return $self->global_cache->set({ data => $self });
617}
618
619
620# Return 1 if we're using the cache; undef if not -- right now we
621# always return undef since caching isn't enabled
622
623sub use_cache {
624    return undef unless ( $USE_CACHE );
625    my ( $class, $p ) = @_;
626    return undef if ( $p->{skip_cache} );
627    return undef if ( $class->no_cache );
628    return undef unless ( $class->global_cache );
629    return 1;
630}
631
632
633########################################
634# ACCESSORS/MUTATORS
635########################################
636
637# We should probably deprecate these...
638
639sub get { return $_[0]->{ $_[1] } }
640sub set { return $_[0]->{ $_[1] } = $_[2] }
641
642
643# return a simple hashref of this object's data -- not tied, not as an
644# object
645
646sub as_data_only {
647    my ( $self ) = @_;
648    my $fields = $self->_get_definitive_fields;
649    return { map { $_ => $self->{ $_ } } grep ! /^(tmp|_)/, @{ $fields } };
650}
651
652# Backward compatible...
653
654sub data { return as_data_only( @_ ) }
655
656sub AUTOLOAD {
657    my ( $item, @params ) = @_;
658    my $request = $SPOPS::AUTOLOAD;
659    $request =~ s/.*://;
660
661    # First, give a nice warning and return undef if $item is just a
662    # class rather than an object
663
664    my $class = ref $item;
665    unless ( $class ) {
666        $log->warn( "Cannot fill class method '$request' from class '$item'" );
667        return undef;
668    }
669
670    $log->is_info &&
671        $log->info( "AUTOLOAD caught '$request' from '$class'" );
672
673    if ( ref $item and $item->is_checking_fields ) {
674        my $fields = $item->field_all_map || {};
675        my ( $field_name ) = $request =~ /^(\w+)_clear/;
676        if ( exists $fields->{ $request } ) {
677            $log->is_debug &&
678                $log->debug( "$class to fill param '$request'; returning data." );
679            # TODO: make these internal methods inheritable?
680            $item->_internal_create_field_methods( $class, $request );
681            return $item->$request( @params );
682        }
683        elsif ( $field_name and exists $fields->{ $field_name } ) {
684            $log->is_debug &&
685                $log->debug( "$class to fill param clear '$request'; ",
686                              "creating '$field_name' methods" );
687            $item->_internal_create_field_methods( $class, $field_name );
688            return $item->$request( @params );
689        }
690        elsif ( my $value = $item->{ $request } ) {
691            $log->is_debug &&
692                $log->debug( " $request must be a temp or something, returning value." );
693            return $value;
694        }
695        elsif ( $request =~ /^tmp_/ ) {
696            $log->is_debug &&
697                $log->debug( "$request is a temp var, but no value saved. Returning undef." );
698            return undef;
699        }
700        elsif ( $request =~ /^_internal/ ) {
701            $log->is_debug &&
702                $log->debug( "$request is an internal request, but no value",
703                              "saved. Returning undef." );
704            return undef;
705        }
706        $log->warn( "AUTOLOAD Error: Cannot access the method $request via <<$class>>",
707               "with the parameters ", join( ' ', @_ ) );
708        return undef;
709    }
710    my ( $field_name ) = $request =~ /^(\w+)_clear/;
711    if ( $field_name ) {
712        $log->is_debug &&
713            $log->debug( "$class is not checking fields, so create sub and return ",
714                         "data for '$field_name'" );
715        $item->_internal_create_field_methods( $class, $field_name );
716    }
717    else {
718        $log->is_debug &&
719            $log->debug( "$class is not checking fields, so create sub and return ",
720                         "data for '$request'" );
721        $item->_internal_create_field_methods( $class, $request );
722    }
723    return $item->$request( @params );
724}
725
726sub _internal_create_field_methods {
727    my ( $item, $class, $field_name ) = @_;
728
729    no strict 'refs';
730
731    # First do the accessor/mutator...
732    *{ $class . '::' . $field_name } = sub {
733        my ( $self, $value ) = @_;
734        if ( defined $value ) {
735            $self->{ $field_name } = $value;
736        }
737        return $self->{ $field_name };
738    };
739
740    # Now the mutator to clear the field value
741    *{ $class . '::' . $field_name . '_clear' } = sub {
742        my ( $self ) = @_;
743        delete $self->{ $field_name };
744        return undef;
745    };
746
747    return;
748}
749
750
751########################################
752# DEBUGGING
753
754# DEPRECATED! Use log4perl instead!
755
756sub _w {
757    my $lev   = shift || 0;
758    if ( $lev == 0 ) {
759        $log->warn( @_ );
760    }
761    elsif ( $lev == 1 ) {
762        $log->is_info &&
763            $log->info( @_ );
764    }
765    else {
766        $log->is_debug &&
767            $log->debug( @_ );
768    }
769}
770
771
772sub _wm {
773    my ( $lev, $check, @msg ) = @_;
774    return _w( $lev, @msg );
775}
776
7771;
778
779__END__
780
781=head1 NAME
782
783SPOPS -- Simple Perl Object Persistence with Security
784
785=head1 SYNOPSIS
786
787 # Define an object completely in a configuration file
788
789 my $spops = {
790   myobject => {
791    class   => 'MySPOPS::Object',
792    isa     => qw( SPOPS::DBI ),
793    ...
794   }
795 };
796
797 # Process the configuration and initialize the class
798
799 SPOPS::Initialize->process({ config => $spops });
800
801 # create the object
802
803 my $object = MySPOPS::Object->new;
804
805 # Set some parameters
806
807 $object->{ $param1 } = $value1;
808 $object->{ $param2 } = $value2;
809
810 # Store the object in an inherited persistence mechanism
811
812 eval { $object->save };
813 if ( $@ ) {
814   print "Error trying to save object: $@\n",
815         "Stack trace: ", $@->trace->as_string, "\n";
816 }
817
818=head1 OVERVIEW
819
820SPOPS -- or Simple Perl Object Persistence with Security -- allows you
821to easily define how an object is composed and save, retrieve or
822remove it any time thereafter. It is intended for SQL databases (using
823the DBI), but you should be able to adapt it to use any storage
824mechanism for accomplishing these tasks.  (An early version of this
825used GDBM, although it was not pretty.)
826
827The goals of this package are fairly simple:
828
829=over 4
830
831=item *
832
833Make it easy to define the parameters of an object
834
835=item *
836
837Make it easy to do common operations (fetch, save, remove)
838
839=item *
840
841Get rid of as much SQL (or other domain-specific language) as
842possible, but...
843
844=item *
845
846... do not impose a huge cumbersome framework on the developer
847
848=item *
849
850Make applications easily portable from one database to another
851
852=item *
853
854Allow people to model objects to existing data without modifying the
855data
856
857=item *
858
859Include flexibility to allow extensions
860
861=item *
862
863Let people simply issue SQL statements and work with normal datasets
864if they want
865
866=back
867
868So this is a class from which you can derive several useful
869methods. You can also abstract yourself from a datasource and easily
870create new objects.
871
872The subclass is responsible for serializing the individual objects, or
873making them persistent via on-disk storage, usually in some sort of
874database. See "Object Oriented Perl" by Conway, Chapter 14 for much
875more information.
876
877The individual objects or the classes should not care how the objects
878are being stored, they should just know that when they call C<fetch()>
879with a unique ID that the object magically appears. Similarly, all the
880object should know is that it calls C<save()> on itself and can
881reappear at any later date with the proper invocation.
882
883=head1 DESCRIPTION
884
885This module is meant to be overridden by a class that will implement
886persistence for the SPOPS objects. This persistence can come by way of
887flat text files, LDAP directories, GDBM entries, DBI database tables
888-- whatever. The API should remain the same.
889
890Please see L<SPOPS::Manual::Intro|SPOPS::Manual::Intro> and
891L<SPOPS::Manual::Object|SPOPS::Manual::Object> for more information
892and examples about how the objects work.
893
894=head1 API
895
896The following includes methods within SPOPS and those that need to be
897defined by subclasses.
898
899In the discussion below, the following holds:
900
901=over 4
902
903=item *
904
905When we say B<base class>, think B<SPOPS>
906
907=item *
908
909When we say B<subclass>, think of B<SPOPS::DBI> for example
910
911=back
912
913Also see the L<ERROR HANDLING> section below on how we use exceptions
914to indicate an error and where to get more detailed infromation.
915
916B<new( [ \%initialize_data ] )>
917
918Implemented by base class.
919
920This method creates a new SPOPS object. If you pass it key/value pairs
921the object will initialize itself with the data (see C<initialize()>
922for notes on this). You can also implement C<initialize_custom()> to
923perform your own custom processing at object initialization (see
924below).
925
926Note that you can use the key 'id' to substitute for the actual
927parameter name specifying an object ID. For instance:
928
929 my $uid = $user->id;
930 if ( eval { $user->remove } ) {
931   my $new_user = MyUser->new( { id => $uid, fname = 'BillyBob' ... } );
932   ...
933 }
934
935In this case, we do not need to know the name of the ID field used by
936the MyUser class.
937
938You can also pass in default values to use for the object in the
939'default_values' key.
940
941We use a number of parameters from your object configuration. These
942are:
943
944=over 4
945
946=item *
947
948B<strict_field> (bool) (optional)
949
950If set to true, you will use the
951L<SPOPS::Tie::StrictField|SPOPS::Tie::StrictField> tie implementation,
952which ensures you only get/set properties that exist in the field
953listing. You can also pass a true value in for C<strict_field> in the
954parameters and achieve the same result for this single object
955
956=item *
957
958B<column_group> (\%) (optional)
959
960Hashref of column aliases to arrayrefs of fieldnames. If defined
961objects of this class will use L<LAZY LOADING>, and the different
962aliases you define can typically be used in a C<fetch()>,
963C<fetch_group()> or C<fetch_iterator()> statement. (Whether they can
964be used depends on the SPOPS implementation.)
965
966=item *
967
968B<field_map> (\%) (optional)
969
970Hashref of field alias to field name. This allows you to get/set
971properties using a different name than how the properties are
972stored. For instance, you might need to retrofit SPOPS to an existing
973table that contains news stories. Retrofitting is not a problem, but
974another wrinkle of your problem is that the news stories need to fit a
975certain interface and the property names of the interface do not match
976the fieldnames in the existing table.
977
978All you need to do is create a field map, defining the interface
979property names as the keys and the database field names as the values.
980
981=item *
982
983B<default_values> (\%) (optional)
984
985Hashref of field names and default values for the fields when the
986object is initialized with C<new()>.
987
988Normally the values of the hashref are the defaults to which you want
989to set the fields. However, there are two special cases of values:
990
991=over 4
992
993=item B<'NOW'>
994
995This string will insert the current timestamp in the format
996C<yyyy-mm-dd hh:mm:ss>.
997
998=item B<\%>
999
1000A hashref with the keys 'class' and 'method' will get executed as a
1001class method and be passed the name of the field for which we want a
1002default. The method should return the default value for this field.
1003
1004=back
1005
1006One problem with setting default values in your object configuration
1007B<and> in your database is that the two may become unsynchronized,
1008resulting in many pulled hairs in debugging.
1009
1010To get around the synchronization issue, you can set this dynamically
1011using various methods with
1012L<SPOPS::ClassFactory|SPOPS::ClassFactory>. A simple implementation,
1013L<SPOPS::Tool::DBI::FindDefaults|SPOPS::Tool::DBI::FindDefaults>, is
1014shipped with SPOPS.
1015
1016=back
1017
1018As the very last step before the object is returned we call
1019C<initialize_custom( \%initialize_data )>. You can override this
1020method and perform any processing you wish. The parameters from
1021C<\%initialize_data> will already be set in the object, and the
1022'changed' flag will be cleared for all parameters and the 'saved' flag
1023cleared.
1024
1025Returns on success: a tied hashref object with any passed data already
1026assigned. The 'changed' flag is set and the and 'saved' flags is
1027cleared on the returned object.
1028
1029Returns on failure: undef.
1030
1031Examples:
1032
1033 # Simplest form...
1034 my $data = MyClass->new();
1035
1036 # ...with initialization
1037 my $data = MyClass->new({ balance => 10532,
1038                           account => '8917-918234' });
1039
1040B<clone( \%params )>
1041
1042Returns a new object from the data of the first. You can override the
1043original data with that in the C<\%params> passed in. You can also clone
1044an object into a new class by passing the new class name as the
1045'_class' parameter -- of course, the interface must either be the same
1046or there must be a 'field_map' to account for the differences.
1047
1048Note that the ID of the original object will B<not> be copied; you can
1049set it explicitly by setting 'id' or the name of the ID field in
1050C<\%params>.
1051
1052Examples:
1053
1054 # Create a new user bozo
1055
1056 my $bozo = $user_class->new;
1057 $bozo->{first_name} = 'Bozo';
1058 $bozo->{last_name}  = 'the Clown';
1059 $bozo->{login_name} = 'bozosenior';
1060 eval { $bozo->save };
1061 if ( $@ ) { ... report error .... }
1062
1063 # Clone bozo; first_name is 'Bozo' and last_name is 'the Clown',
1064 # as in the $bozo object, but login_name is 'bozojunior'
1065
1066 my $bozo_jr = $bozo->clone({ login_name => 'bozojunior' });
1067 eval { $bozo_jr->save };
1068 if ( $@ ) { ... report error ... }
1069
1070 # Copy all users from a DBI datastore into an LDAP datastore by
1071 # cloning from one and saving the clone to the other
1072
1073 my $dbi_users = DBIUser->fetch_group();
1074 foreach my $dbi_user ( @{ $dbi_users } ) {
1075     my $ldap_user = $dbi_user->clone({ _class => 'LDAPUser' });
1076     $ldap_user->save;
1077 }
1078
1079B<initialize( \%initialize_data )>
1080
1081Implemented by base class; do your own customization using
1082C<initialize_custom()>.
1083
1084Cycle through the parameters inn C<\%initialize_data> and set any
1085fields necessary in the object. This allows you to construct the
1086object with existing data. Note that the tied hash implementation
1087optionally ensures (with the 'strict_field' configuration key set to
1088true) that you cannot set infomration as a parameter unless it is in
1089the field list for your class. For instance, passing the information:
1090
1091 firt_name => 'Chris'
1092
1093should likely not set the data, since 'firt_name' is the misspelled
1094version of the defined field 'first_name'.
1095
1096Note that we also set the 'loaded' property of all fields to true, so
1097if you override this method you need to simply call:
1098
1099 $self->set_all_loaded();
1100
1101somewhere in the overridden method.
1102
1103C<initialize_custom( \%initialize_data )>
1104
1105Called as the last step of C<new()> so you can perform customization
1106as necessary. The default does nothing.
1107
1108Returns: nothing
1109
1110=head2 Accessors/Mutators
1111
1112You should use the hash interface to get and set values in your object
1113-- it is easier. However, SPOPS will also create an
1114accessor/mutator/clearing-mutator for you on demand -- just call a
1115method with the same name as one of your properties and two methods
1116('${fieldname}' and '${fieldname}_clear') will be created. Similar to
1117other libraries in Perl (e.g., L<Class::Accessor|Class::Accessor>) the
1118accessor and mutator share a method, with the mutator only being used
1119if you pass a defined value as the second argument:
1120
1121 # Accessor
1122 my $value = $object->fieldname;
1123
1124 # Mutator
1125 $object->fieldname( 'new value' );
1126
1127 # This won't do what you want (clear the field value)...
1128 $object->fieldname( undef );
1129
1130 # ... but this will
1131 $object->fieldname_clear;
1132
1133The return value of the mutator is the B<new> value of the field which
1134is the same value you passed in.
1135
1136Generic accessors (C<get()>) and mutators (C<set()>) are available but
1137deprecated, probably to be removed before 1.0:
1138
1139You can modify how the accessors/mutators get generated by overriding
1140the method:
1141
1142 sub _internal_create_field_methods {
1143     my ( $self, $class, $field_name ) = @_;
1144     ...
1145 }
1146
1147This method must create two methods in the class namespace,
1148'${fieldname}' and '${fieldname}_clear'. Since the value returned from
1149C<AUTOLOAD> depends on these methods being created, failure to create
1150them will probably result in an infinite loop.
1151
1152B<get( $fieldname )>
1153
1154Returns the currently stored information within the object for C<$fieldname>.
1155
1156 my $value = $obj->get( 'username' );
1157 print "Username is $value";
1158
1159It might be easier to use the hashref interface to the same data,
1160since you can inline it in a string:
1161
1162 print "Username is $obj->{username}";
1163
1164You may also use a shortcut of the parameter name as a method call for
1165the first instance:
1166
1167 my $value = $obj->username();
1168 print "Username is $value";
1169
1170B<set( $fieldname, $value )>
1171
1172Sets the value of C<$fieldname> to C<$value>. If value is empty,
1173C<$fieldname> is set to undef.
1174
1175 $obj->set( 'username', 'ding-dong' );
1176
1177Again, you can also use the hashref interface to do the same thing:
1178
1179 $obj->{username} = 'ding-dong';
1180
1181You can use the fieldname as a method to modify the field value here
1182as well:
1183
1184 $obj->username( 'ding-dong' );
1185
1186Note that if you want to set the field to C<undef> you will need to
1187use the hashref interface:
1188
1189 $obj->{username} = undef;
1190
1191B<id()>
1192
1193Returns the ID for this object. Checks in its config variable for the
1194ID field and looks at the data there.  If nothing is currently stored,
1195you will get nothing back.
1196
1197Note that we also create a subroutine in the namespace of the calling
1198class so that future calls take place more quickly.
1199
1200=head2 Serialization
1201
1202B<fetch( $object_id, [ \%params ] )>
1203
1204Implemented by subclass.
1205
1206This method should be called from either a class or another object
1207with a named parameter of 'id'.
1208
1209Returns on success: an SPOPS object.
1210
1211Returns on failure: undef; if the action failed (incorrect fieldname
1212in the object specification, database not online, database user cannot
1213select, etc.) a L<SPOPS::Exception|SPOPS::Exception> object (or one of
1214its subclasses) will be thrown to raise an error.
1215
1216The \%params parameter can contain a number of items -- all are optional.
1217
1218Parameters:
1219
1220=over 4
1221
1222=item *
1223
1224B<(datasource)> (obj) (optional)
1225
1226For most SPOPS implementations, you can pass the data source (a DBI
1227database handle, a GDBM tied hashref, etc.) into the routine. For DBI
1228this variable is C<db>, for LDAP it is C<ldap>, but for other
1229implementations it can be something else.
1230
1231=item *
1232
1233B<data> (\%) (optional)
1234
1235You can use fetch() not just to retrieve data, but also to do the
1236other checks it normally performs (security, caching, rulesets,
1237etc.). If you already know the data to use, just pass it in using this
1238hashref. The other checks will be done but not the actual data
1239retrieval. (See the C<fetch_group> routine in L<SPOPS::DBI|SPOPS::DBI>
1240for an example.)
1241
1242=item *
1243
1244B<skip_security> (bool) (optional)
1245
1246A true value skips security checks, false or default value keeps them.
1247
1248=item *
1249
1250B<skip_cache> (bool) (optional)
1251
1252A true value skips any use of the cache, always hitting the data
1253source.
1254
1255=back
1256
1257In addition, specific implementations may allow you to pass in other
1258parameters. (For example, you can pass in 'field_alter' to the
1259L<SPOPS::DBI|SPOPS::DBI> implementation so you can format the returned data.)
1260
1261Example:
1262
1263 my $id = 90192;
1264 my $data = eval { MyClass->fetch( $id ) };
1265
1266 # Read in a data file and retrieve all objects matching IDs
1267
1268 my @object_list = ();
1269 while ( <DATA> ) {
1270   chomp;
1271   next if ( /\D/ );
1272   my $obj = eval { ObjectClass->fetch( $_ ) };
1273   if ( $@ ) { ... report error ... }
1274   else      { push @object_list, $obj  if ( $obj ) }
1275 }
1276
1277B<fetch_determine_limit()>
1278
1279This method has been moved to L<SPOPS::Utility|SPOPS::Utility>.
1280
1281B<save( [ \%params ] )>
1282
1283Implemented by subclass.
1284
1285This method should save the object state in whatever medium the module
1286works with. Note that the method may need to distinguish whether the
1287object has been previously saved or not -- whether to do an add versus
1288an update. See the section L<TRACKING CHANGES> for how to do this. The
1289application should not care whether the object is new or pre-owned.
1290
1291Returns on success: the object itself.
1292
1293Returns on failure: undef, and a L<SPOPS::Exception|SPOPS::Exception>
1294object (or one of its subclasses) will be thrown to raise an error.
1295
1296Example:
1297
1298 eval { $obj->save };
1299 if ( $@ ) {
1300   warn "Save of ", ref $obj, " did not work properly -- $@";
1301 }
1302
1303Since the method returns the object, you can also do chained method
1304calls:
1305
1306 eval { $obj->save()->separate_object_method() };
1307
1308Parameters:
1309
1310=over 4
1311
1312=item *
1313
1314B<(datasource)> (obj) (optional)
1315
1316For most SPOPS implementations, you can pass the data source (a DBI
1317database handle, a GDBM tied hashref, etc.) into the routine.
1318
1319=item *
1320
1321B<is_add> (bool) (optional)
1322
1323A true value forces this to be treated as a new record.
1324
1325=item *
1326
1327B<skip_security> (bool) (optional)
1328
1329A true value skips the security check.
1330
1331=item *
1332
1333B<skip_cache> (bool) (optional)
1334
1335A true value skips any caching.
1336
1337=item *
1338
1339B<skip_log> (bool) (optional)
1340
1341A true value skips the call to 'log_action'
1342
1343=back
1344
1345B<remove()>
1346
1347Implemented by subclass.
1348
1349Permanently removes the object, or if called from a class removes the
1350object having an id matching the named parameter of 'id'.
1351
1352Returns: status code based on success (undef == failure).
1353
1354Parameters:
1355
1356=over 4
1357
1358=item *
1359
1360B<(datasource)> (obj) (optional)
1361
1362For most SPOPS implementations, you can pass the data source (a DBI
1363database handle, a GDBM tied hashref, etc.) into the routine.
1364
1365=item *
1366
1367B<skip_security> (bool) (optional)
1368
1369A true value skips the security check.
1370
1371=item *
1372
1373B<skip_cache> (bool) (optional)
1374
1375A true value skips any caching.
1376
1377=item *
1378
1379B<skip_log> (bool) (optional)
1380
1381A true value skips the call to 'log_action'
1382
1383=back
1384
1385Examples:
1386
1387 # First fetch then remove
1388
1389 my $obj = MyClass->fetch( $id );
1390 my $rv = $obj->remove();
1391
1392Note that once you successfully call C<remove()> on an object, the
1393object will still exist as if you had just called C<new()> and set the
1394properties of the object. For instance:
1395
1396 my $obj = MyClass->new();
1397 $obj->{first_name} = 'Mario';
1398 $obj->{last_name}  = 'Lemieux';
1399 if ( $obj->save ) {
1400     my $saved_id = $obj->{player_id};
1401     $obj->remove;
1402     print "$obj->{first_name} $obj->{last_name}\n";
1403 }
1404
1405Would print:
1406
1407 Mario Lemieux
1408
1409But trying to fetch an object with C<$saved_id> would result in an
1410undefined object, since it is no longer in the datastore.
1411
1412=head2 Object Information
1413
1414B<object_description()>
1415
1416Returns a hashref with metadata about a particular object. The keys of
1417the hashref are:
1418
1419=over 4
1420
1421=item *
1422
1423B<class> ($)
1424
1425Class of this object
1426
1427=item *
1428
1429B<object_id> ($)
1430
1431ID of this object. (Also under 'oid' for compatibility.)
1432
1433=item *
1434
1435B<id_field> ($)
1436
1437Field used for the ID.
1438
1439=item *
1440
1441B<name> ($)
1442
1443Name of this general class of object (e.g., 'News')
1444
1445=item *
1446
1447B<title> ($)
1448
1449Title of this particular object (e.g., 'Man bites dog, film at 11')
1450
1451=item *
1452
1453B<url> ($)
1454
1455URL that will display this object. Note that the URL might not
1456necessarily work due to security reasons.
1457
1458B<url_edit> ($)
1459
1460URL that will display this object in editable form. Note that the URL
1461might not necessarily work due to security reasons.
1462
1463=back
1464
1465You control what's used in the 'display' class configuration
1466variable. In it you can have the keys 'url', which should be the basis
1467for a URL to display the object and optionally 'url_edit', the basis
1468for a URL to display the object in editable form. A query string with
1469'id_field=ID' will be appended to both, and if 'url_edit' is not
1470specified we create it by adding a 'edit=1' to the 'url' query
1471string.
1472
1473So with:
1474
1475 display => {
1476   url      => '/Foo/display/',
1477   url_edit => '/Foo/display_form',
1478 }
1479
1480The defaults put together by SPOPS by reading your configuration file
1481might not be sufficiently dynamic for your object. In that case, just
1482override the method and substitute your own. For instance, the
1483following adds some sort of sales adjective to the beginning of every
1484object title:
1485
1486  package My::Object;
1487
1488  sub object_description {
1489      my ( $self ) = @_;
1490      my $info = $self->SUPER::object_description();
1491      $info->{title} = join( ' ', sales_adjective_of_the_day(),
1492                                  $info->{title} );
1493      return $info;
1494  }
1495
1496And be sure to include this class in your 'code_class' configuration
1497key. (See L<SPOPS::ClassFactory|SPOPS::ClassFactory> and
1498L<SPOPS::Manual::CodeGeneration|SPOPS::Manual::CodeGeneration> for
1499more info.)
1500
1501B<as_string>
1502
1503Represents the SPOPS object as a string fit for human consumption. The
1504SPOPS method is extremely crude -- if you want things to look nicer,
1505override it.
1506
1507B<as_html>
1508
1509Represents the SPOPS object as a string fit for HTML (browser)
1510consumption. The SPOPS method is double extremely crude, since it just
1511wraps the results of C<as_string()> (which itself is crude) in
1512'E<lt>preE<gt>' tags.
1513
1514=head2 Lazy Loading
1515
1516B<is_loaded( $fieldname )>
1517
1518Returns true if C<$fieldname> has been loaded from the datastore,
1519false if not.
1520
1521B<set_loaded( $fieldname )>
1522
1523Flags C<$fieldname> as being loaded.
1524
1525B<set_all_loaded()>
1526
1527Flags all fieldnames (as returned by C<field_list()>) as being loaded.
1528
1529=head2 Field Checking
1530
1531B<is_checking_fields()>
1532
1533Returns true if this class is doing field checking (setting
1534'strict_field' equal to a true value in the configuration), false if
1535not.
1536
1537=head2 Modification State
1538
1539B<is_changed()>
1540
1541Returns true if this object has been changed since being fetched or
1542created, false if not.
1543
1544B<has_change()>
1545
1546Set the flag telling this object it has been changed.
1547
1548B<clear_change()>
1549
1550Clear the change flag in an object, telling it that it is unmodified.
1551
1552=head2 Serialization State
1553
1554B<is_saved()>
1555
1556Return true if this object has ever been saved, false if not.
1557
1558B<has_save()>
1559
1560Set the saved flag in the object to true.
1561
1562B<clear_save()>
1563
1564Clear out the saved flag in the object.
1565
1566=head2 Configuration
1567
1568Most of this information can be accessed through the C<CONFIG>
1569hashref, but we also need to create some hooks for subclasses to
1570override if they wish. For instance, language-specific objects may
1571need to be able to modify information based on the language
1572abbreviation.
1573
1574We have simple methods here just returning the basic CONFIG
1575information.
1576
1577B<no_cache()> (bool)
1578
1579Returns a boolean based on whether this object can be cached or
1580not. This does not mean that it B<will> be cached, just whether the
1581class allows its objects to be cached.
1582
1583B<field()> (\%)
1584
1585Returns a hashref (which you can sort by the values if you wish) of
1586fieldnames used by this class.
1587
1588B<field_list()> (\@)
1589
1590Returns an arrayref of fieldnames used by this class.
1591
1592Subclasses can define their own where appropriate.
1593
1594=head2 "Global" Configuration
1595
1596These objects are tied together by just a few things:
1597
1598B<global_cache>
1599
1600A caching object. Caching in SPOPS is not tested but should work --
1601see L<Caching> below.
1602
1603=head2 Caching
1604
1605Caching in SPOPS is not tested but should work. If you would like to
1606brave the rapids, then call at the beginning of your application:
1607
1608 SPOPS->set_global_use_cache(1);
1609
1610You will also need to make a caching object accessible to all of your
1611SPOPS classes via a method C<global_cache()>. Each class can turn off
1612caching by setting a true value for the configuration variable
1613C<no_cache> or by passing in a true value for the parameter
1614'skip_cache' as passed to C<fetch>, C<save>, etc.
1615
1616The object returned by C<global_cache()> should return an object which
1617implements the methods C<get()>, C<set()>, C<clear()>, and C<purge()>.
1618
1619The method C<get()> should return the property values for a particular
1620object given a class and object ID:
1621
1622 $cache->get({ class => 'SPOPS-class', object_id => 'id' })
1623
1624The method B<set()> should saves the property values for an object
1625into the cache:
1626
1627 $cache->set({ data => $spops_object });
1628
1629The method B<clear()> should clear from the cache the data for an
1630object:
1631
1632 $cache->clear({ data => $spops_object });
1633 $cache->clear({ class => 'SPOPS-class', object_id => 'id' });
1634
1635The method B<purge()> should remove B<all> items from the cache.
1636
1637This is a fairly simple interface which leaves implementation pretty
1638much wide open.
1639
1640=head2 Timestamp Methods
1641
1642These have gone away (you were warned!)
1643
1644=head2 Debugging
1645
1646The previous (fragile, awkward) debugging system in SPOPS has been
1647replaced with L<Log::Log4perl> instead. Old calls to C<DEBUG>, C<_w>,
1648and C<_wm> will still work (for now) but they just use log4perl under
1649the covers.
1650
1651Please see L<SPOPS::Manual::Configuration> under L<LOGGING> for
1652information on how to configure it.
1653
1654=head1 NOTES
1655
1656There is an issue using these modules with
1657L<Apache::StatINC|Apache::StatINC> along with the startup methodology
1658that calls the C<class_initialize> method of each class when a httpd
1659child is first initialized. If you modify a module without stopping
1660the webserver, the configuration variable in the class will not be
1661initialized and you will inevitably get errors.
1662
1663We might be able to get around this by having most of the
1664configuration information as static class lexicals. But anything that
1665depends on any information from the CONFIG variable in request (which
1666is generally passed into the C<class_initialize> call for each SPOPS
1667implementation) will get hosed.
1668
1669=head1 TO DO
1670
1671B<Method object_description() should be more robust>
1672
1673In particular, the 'url' and 'url_edit' keys of object_description()
1674should be more robust.
1675
1676B<Objects composed of many records>
1677
1678An idea: Make this data item framework much like the one
1679Brian Jepson discusses in Web Techniques:
1680
1681 http://www.webtechniques.com/archives/2000/03/jepson/
1682
1683At least in terms of making each object unique (having an OID).
1684Each object could then be simply a collection of table name
1685plus ID name in the object table:
1686
1687 CREATE TABLE objects (
1688   oid        int not null,
1689   table_name varchar(30) not null,
1690   id         int not null,
1691   primary key( oid, table_name, id )
1692 )
1693
1694Then when you did:
1695
1696 my $oid  = 56712;
1697 my $user = User->fetch( $oid );
1698
1699It would first get the object composition information:
1700
1701 oid    table        id
1702 ===    =====        ==
1703 56712  user         1625
1704 56712  user_prefs   8172
1705 56712  user_history 9102
1706
1707And create the User object with information from all
1708three tables.
1709
1710Something to think about, anyway.
1711
1712=head1 BUGS
1713
1714None known.
1715
1716=head1 COPYRIGHT
1717
1718Copyright (c) 2001-2004 intes.net, inc; (c) 2003-2004-2004-2004 Chris
1719Winters. All rights reserved.
1720
1721This library is free software; you can redistribute it and/or modify
1722it under the same terms as Perl itself.
1723
1724=head1 SEE ALSO
1725
1726Find out more about SPOPS -- current versions, updates, rants, ideas
1727-- at:
1728
1729 http://spops.sourceforge.net/
1730
1731CVS access and mailing lists (SPOPS is currently supported by the
1732openinteract-dev list) are at:
1733
1734 http://sourceforge.net/projects/spops/
1735
1736Also see the 'Changes' file in the source distribution for comments
1737about how the module has evolved.
1738
1739L<SPOPSx::Ginsu> - Generalized Inheritance Support for SPOPS + MySQL
1740-- store inherited data in separate tables.
1741
1742=head1 AUTHORS
1743
1744Chris Winters E<lt>chris@cwinters.comE<gt>
1745
1746The following people have offered patches, advice, development funds,
1747etc. to SPOPS:
1748
1749=over 4
1750
1751=item *
1752
1753Ray Zimmerman E<lt>rz10@cornell.eduE<gt> -- has offered tons of great design
1754ideas and general help, pushing SPOPS into new domains. Too much to
1755list here.
1756
1757=item *
1758
1759Simon Ilyushchenko E<lt>simonf@cshl.eduE<gt> -- real-world usage
1760advice, work on improving the object linking semantics, lots of little
1761items.
1762
1763=item *
1764
1765Christian Lemburg E<lt>lemburg@aixonix.deE<gt> -- contributed excellent
1766documentation, too many good ideas to implement as well as design help
1767with L<SPOPS::Secure::Hierarchy|SPOPS::Secure::Hierarchy>, the
1768rationale for moving methods from the main SPOPS subclass to
1769L<SPOPS::Utility|SPOPS::Utility>
1770
1771=item *
1772
1773Raj Chandran E<lt>rc264@cornell.eduE<gt> submitted a patch to make
1774some L<SPOPS::SQLInterface|SPOPS::SQLInterface> methods work as
1775advertised.
1776
1777=item *
1778
1779Rusty Foster E<lt>rusty@kuro5hin.orgE<gt> -- was influential (and not
1780always in good ways) in the early days of this library and offered up
1781an implementation for 'limit' functionality in
1782L<SPOPS::DBI|SPOPS::DBI>
1783
1784=item *
1785
1786Rick Myers E<lt>rik@sumthin.nuE<gt> -- got rid of lots of warnings when
1787running under C<-w> and helped out with permission issues with
1788SPOPS::GDBM.
1789
1790=item *
1791
1792Harry Danilevsky E<lt>hdanilevsky@DeerfieldCapital.comE<gt> -- helped out with
1793Sybase-specific issues, including inspiring
1794L<SPOPS::Key::DBI::Identity|SPOPS::Key::DBI::Identity>.
1795
1796=item *
1797
1798Leon Brocard E<lt>acme@astray.comE<gt> -- prodded better docs of
1799L<SPOPS::Configure|SPOPS::Configure>, specifically the linking
1800semantics.
1801
1802=item *
1803
1804David Boone E<lt>dave@bis.bc.caE<gt> -- prodded the creation of
1805L<SPOPS::Initialize|SPOPS::Initialize>.
1806
1807=item *
1808
1809MSN Marketing Service Nordwest, GmbH -- funded development of LDAP
1810functionality, including L<SPOPS::LDAP|SPOPS::LDAP>,
1811L<SPOPS::LDAP::MultiDatasource|SPOPS::LDAP::MultiDatasource>, and
1812L<SPOPS::Iterator::LDAP|SPOPS::Iterator::LDAP>.
1813
1814=back
1815