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