This is Info file pm.info, produced by Makeinfo version 1.68 from the input file bigpm.texi.  File: pm.info, Node: SPOPS/Configure, Next: SPOPS/Configure/DBI, Prev: SPOPS, Up: Module List read in configuration information for collections and create/configure ********************************************************************** NAME ==== SPOPS::Configure - read in configuration information for collections and create/configure SYNOPSIS ======== use Ad::This; use Ad::That; use SPOPS::Configure; my $classes = []; $classes = SPOPS::Configure->parse_config( $conf ); DESCRIPTION =========== This class only has one method: parse_config. It takes a hashref of collection information and configures existing classes with any information necessary (including the @ISA for each) and also whips up collection classes on the fly based on the data found in the hashref passed in. Note that this class is *not required* to use SPOPS. You can happily just define the configuration information right in your class and never have a need to METHODS ======= *process_config( \%params )* Parameters: config (\%) A hashref of configuration information for one or more SPOPS objects. The key is the SPOPS object alias, the value is a hashref of configuration information corresponding to that alias. In the absence of an 'alias_list', each of the SPOPS objects specified in the configuration will be processed. alias_list (\@) (optional) List of aliases to process from the 'config'. Use this if for some reason you do not want to process all the aliases. meta (\%) (optional) Can also be in the 'config' hashref -- see information under 'parse_config()' below. *create_spops_class( \%config, \%spops_config )* Takes configuration information for a single SPOPS class, creates it on-the-fly and reads in any external code when specified. Note that you can pass a true value for the key 'require_isa' in the \%config parameter. The routine will then try to 'require' every class in the 'isa' field of your SPOPS class configuration. Returns: the class name if successful. *parse_config( \%config, \%spops_config )* Takes a hashref of configuration information and returns the classes created/configured properly. One of the keys in the configuration is special. You can pass it in directly as a parameter (using the 'meta' key) or put it in the configuration (using the '_meta' key). If you do neither, the system will use a default (currently consisting of parsing the fields 'field', 'no_insert', 'no_update' and 'skip_undef' from an arrayref into a hashref.) This 'meta' information allows you to manipulate the information in the configuration. Generally this is only for easier input: who wants to type out an entire hashref when all you want is to list some fields that get used later as lookups? The 'meta' information is a hashref. Currently the only supported key is 'parse_into_hash', which takes a listref and makes it into a hashref to facilitate individual lookups. (No 'grep in a void context' for us!) So we read in the \%spops_config information, massage it if necessary and assign the information to the SPOPS class configuration. After this class is run you should be able to call: my \%class_config = $object_class->CONFIG; And get back a hashref of configuration information for the class. Returns: the class name if successful. *create_relationship* Currently this creates the 'has_a' relationship and installs the ruleset information in each class that asks for it. See the writeup below under `Relationship Fields' in this node. Returns: the class name if successful. *_read_code_class* Used internally to emulate some of what 'require' does to find a file with code and then reads the subroutines into another package. Returns: arrayref of filenames that whose subroutines were read into the SPOPS object class. CONFIGURATION FIELDS EXPLAINED ============================== The configuration for a SPOPS class can be elaborate or minimal, depending on your needs. Required Fields --------------- class ($) Name the class you want to create. field (\@) (parsed into \%) List the properties of the object. If you try to assign to a property that is not in the list, *Note SPOPS/Tie: SPOPS/Tie, will warn you and the assignment will be discarded. For instance: # configuration class => 'My::HipHop', field => [ qw/ hip hop hooray / ], ... # code my $obj = My::HipHop->new; $obj->{'boo-yah'} = "and he said"; will result in a warning. isa (\@) List the classes that your class inherits from. This can be a combination of SPOPS and other classes - for instance, you might use *Note SPOPS/DBI: SPOPS/DBI, for serialization functinality but another class (`My::FullTextIndex') to implement a ruleset for full-text indexing. (See *Note SPOPS: SPOPS, for more information on rulesets.) *id_field* ($) Name the field that will be used to uniquely identify an object. The type of field does not matter, but the fact that each value is unique does. You also cannot use an empty string (or undef, or 'NULL') as an identifier. SPOPS does not currently deal with objects that use multiple fields to identify a record. (In database parlance, this is a "multiple field primary key".) To get around the restriction, you can simply add another field to the record and use it as a primary key. Instead of using the 'fetch' method to retrieve records, you can create a simple 'fetch_by_blah' that takes two fields instead of one. (Note: on the TO DO list for SPOPS is the ability to create a 'fetch_by_blah' method on-the-fly from configuration information. Optional Fields --------------- *code_class* ($ or \@) Note: This is not optional if you wish to draw code from a class separate from the one you are creating. When this class finds a 'code_class' value, it tries to find the class somewhere in @ISA. If it can find the class, it reads the file in and puts the subroutines into the class you are creating. For instance: ... class => 'My::Tofu', code_class => 'Food::Tofu', ... Will read the routines from 'Food::Tofu' and put them into the 'My::Tofu' namespace. (It will also currently put any lexical variables from the code class into your class, so be careful.) You can also bring in routines from multiple files: ... class => 'My::Tofu', code_class => [ 'Food::Tofu', 'Food::Soybean', 'Food::Vegan' ], ... However, you should be careful with this. Possibilities abound for different classes defining the same subroutine and similar actions which are quite difficult to debug. *no_security* ($) Set this to a true value if you do not want this class to use security. This overrides all other values - even if you have *Note SPOPS/Secure: SPOPS/Secure, in the 'isa' of your class, security will not be checked if this value is true. Be careful! *no_insert* (\@) (parsed into (\%) Specify fields that should not be included when we first create a record. *no_update* (\@) (parsed into (\%) Specify fields whose values should never be changed when we update a record. skip_undef (\@) (parsed into (\%) Specify fields that are not included on either a create or update if they are not defined. (Note that 'undef' is a bit of a misnomer - we do a simple perl 'truth' test to see if the field exists or not.) alias (\@) What other aliases do you want this class to be known under? SPOPS does not currently do anything with this, but implementations can. display (\%) How should this object be displayed? Currently, the hashref it points to must have at least one key 'url', to which SPOPS appends a query string to identify this object. The query string it appends is very simple, something like: url . ? . $class->CONFIG->{id_field} . = . $object->id() name (\&) How can we find the name of an individual object? For instance, in a contact database, the name of a person object is the full name of the person. Here we expect either a code reference or a scalar. Most often you will use a scalar, which just names the field within an object to use, such as: name => 'title' The code reference can do anything complicated you like, but more often than not it is just something like: name => sub { return join( ', ', $_[0]->{field1}, $_[0]->{field2} ) } *object_name* ($) What is the generic name for an object? For instance, 'Document', 'Link', 'Page', 'Food'. *as_string_order* (\@) *as_string_label* (\%) Every SPOPS object has a method 'as_string' as defined in *Note SPOPS: SPOPS,. However, this is a very blunt instrument as it basically just dumps out the properties of the object into a string without any nice labelling or control over order. The 'as_string_order' field allows you to list the fields you want included in the 'as_string' output along with their order, and 'as_string_label' allows you to assign a label to these fields. *creation_security* (\%) (used by SPOPS::Secure) See *Note SPOPS/Secure: SPOPS/Secure, for more information how this is used. *base_table* ($) (used by SPOPS::DBI) Table name for data to be stored. *sql_defaults* (\@) (used by SPOPS::DBI) List of fields that have defaults defined in the SQL table. For instance: active CHAR(3) NOT NULL DEFAULT 'yes', After *Note SPOPS/DBI: SPOPS/DBI, fetches a record, it then checks to see if there are any defaults for the record and if so it refetches the object to ensure that the data in the object and the data in the database are synced. *field_alter* (\%) (used by SPOPS::DBI) Allows you to define different formatting behaviors for retrieving fields. For instance, if you want dates formatted in a certain manner in MySQL, you can do something like: field_alter => { posted_on => q/DATE_FORMAT( posted_on, '%M %e, %Y (%h:%i %p)' )/ } Which instead of the default time format: 2000-09-26 10:29:00 will return something like: September 26, 2000 (10:29 AM) These are typically database-specific. Relationship Fields ------------------- *has_a* (\%) Define a one-to-one relationship from one object to another. Currently, this means that an object has one or more fields that contain the ID value of another object. The 'has_a' field of a configuration tells this class what these relationships are, and this class automatically builds the subroutines to make this happen. Here is a simple example that many people are familiar with in which a user can belong to a single group. my $spops = { user => { field => [ 'user_id', 'group_id', 'email' ], id_field => 'user_id', class => 'My::User', has_a => { 'My::Group' => [ 'group_id' ] }, ... }, group => { field => [ 'group_id', 'name' ], id_field => 'group_id', class => 'My::Group', ... }, }; Here are the steps this class goes through to create a subroutine you can call from one object to retrieve its associated object. (Using the above example, to retrieve a `My::Group' object given a `My::User' object.) 1. Find the SPOPS configuration information matching the SPOPS class given as the key. ('My::Group' under {user}->{has_a} in the example above.) We will call this the SPOPS-Has below. 2. Compare the 'id_field' in the SPOPS-Has information ('group_id' under {group}->{id_field} in the example) to the field given as the key in the original link ('group_id' under {user}->{has_a}->{'My::Group'} in the example). 3. If the 'id_field' and the linking field are the same, create a subroutine of the same name as the SPOPS-Has tag. This is true in the above example, so we can do: # Retrieve a My::User object my $user = My::User->fetch( 13 ); # Retrieve the My::Group object related to this My::User object my $group = $user->group(); since the field specified in the user 'has_a' clause matches the id_field specified in the class it is linking to. 4. If the 'id_field' and the linking field are not the same we either create a subroutine automatically or allow the configuration to specify one for us. We will deal with both possibilities below. First, the automatic creation. For this example, replace the above definition for 'user' with: user => { field => [ 'user_id', 'group_id', 'subgroup_id', 'email' ], id_field => 'user_id', class => 'My::User', has_a => { 'My::Group' => [ 'group_id', 'subgroup_id' ] }, ... }, Now we have two relationships: the user belongs to both a group and a subgroup. Both the group and subgroup are instances of the same class, so we cannot refer to both of them using the 'group' alias as we did in the previous example. Once the above configuration is processed, we can do: # Retrieve a My::User object my $user = My::User->fetch( 13 ); # Retrieve the My::Group object related to this My::User object by # the 'group_id' field my $group = $user->group(); # Retrieve the My::Group object related to this My::User object by # the 'subgroup_id' field my $subgroup = $user->subgroup_id_group(); So we create a subroutine with the name: my $subroutine_name = join( '_', $id_field, $link_alias ); In this case, $id_field is 'subgroup_id' and $alias is 'group'. You can sometimes use this to your advantage but it makes for some awkward naming schemes. However, you can use another means of naming. The custom means allows you to do something like this: user => { field => [ 'user_id', 'group_id', 'subgroup_id', 'email' ], id_field => 'user_id', class => 'My::User', has_a => { 'My::Group' => [ 'group_id', { subgroup_id => 'subgroup' } ] }, ... }, Now you can do the following: # Retrieve a My::User object my $user = My::User->fetch( 13 ); # Retrieve the My::Group object related to this My::User object by # the 'group_id' field my $group = $user->group(); # Retrieve the My::Group object related to this My::User object by # the 'subgroup_id' field my $subgroup = $user->subgroup(); Here, instead of relying on SPOPS to name the subroutine for us that maps to the $id_field 'subgroup_id', we named it ouselves. The only warning here is to ensure that you do not create a subroutine of the same name in a 'code_class', otherwise the 'code_class' routine will get overwritten. *fetch_by* (\@) Create a 'fetch_by_{fieldname}' routine that simply returns an arrayref of objects that match the value of a particular field. Example: my $spops = { user => { field => [ 'user_id', 'group_id', 'subgroup_id', 'email' ], id_field => 'user_id', class => 'My::User', fetch_by => [ 'email' ], ... }, }; Allows us to do: my $user_list = My::User->fetch_by_email( 'allyour@base.ours.com' ); foreach my $user ( @{ $user_list } ) { send_email( $user->{email}, "This is an invalid address" ); } *links_to* (\@) (See *Note SPOPS/Configure/DBI: SPOPS/Configure/DBI, for information.) TO DO ===== *Creating a file of code* Instead of always reading the code into memory we might want to create a file with the new package if it is not found the first time or if it is modified in the process here. This would allow offline tools to modify a SPOPS configuration and generate a class with subroutines from more than one class... *Make 'code_class' more flexible* Instead of making 'code_class' read in just packages, maybe we want to have a file of just subroutines that gets included to the class. BUGS ==== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/Configure/DBI, Next: SPOPS/Configure/Ruleset, Prev: SPOPS/Configure, Up: Module List Define additional configuration methods *************************************** NAME ==== SPOPS::Configure::DBI - Define additional configuration methods SYNOPSIS ======== my $init_classes = SPOPS::Configure::DBI->parse_config( { config => $CONFIG->{SPOPS} } ); DESCRIPTION =========== We override the method *additional_work()* from *Note SPOPS/Configure: SPOPS/Configure, so we can create subroutines in the classes that want to *link_to* other objects, among other things. It is possible - and perhaps desirable for the sake of clarity - to create a method within *SPOPS::DBI* that does all the work for us, then we would only need to create a subroutine that calls that subroutine. However, creating routines with the values embedded directly in them should be quicker and more efficient. So we will try it this way. METHODS ======= *create_relationship( $spops_config )* Get the config and plow through the SPOPS classes, seeing if any of them have the *links_to* key defined. If so, we create three subroutines with the proper info. The first is named '${links_to}' and simply returns an arrayref of objects that the main object links to. For instance: Example: # $links_to = 'group' # Retrieve all groups that this user is a member of my $group_list = eval { $user->group }; The second is named '${links_to}_add' and links the object to any number of other objects. The return value is the number of successful links. The third is named '${links_to}_remove' and removes links from the object to any number of other objects. The return value is the number of successful removals. Examples: # $links_to = 'group' # First retrieve all groups my $group_list = eval { $user->group }; print "Group list: ", join( ' // ', map { $_->{group_id} } @{ $group_list } ); >> 2 // 3 // 5 # Now add some more, making the user a member of these new groups my $added = eval { $user->group_add( [ 7, 9, 21, 23 ] ) }; print "Group list: ", join( ' // ', map { $_->{group_id} } @{ $group_list } ); >> 2 // 3 // 5 // 7 // 9 // 21 // 23 # Now remove two of them my $removed = eval { $user->group_remove( [ 2, 21 ] ) }; print "Group list: ", join( ' // ', map { $_->{group_id} } @{ $group_list } ); >> 3 // 5 // 7 // 9 // 23 CONFIGURATION FIELDS EXPLAINED ============================== *links_to* (\%) The 'links_to' field allows you to specify a SPOPS alias and specify which table is used to link the objects: { 'SPOPS-tag' => 'table_name', } Note that this relationship assumes a link table that joins two separate tables. When you sever a link between two objects, you are only deleting the link rather than deleting an object. See `TO DO' in this node for another proposal. We are also considering making 'SPOPS-tag' into 'SPOPS-class' to make this more versatile. See *Note SPOPS/Configure: SPOPS/Configure, for more info. TO DO ===== *Make 'links_to' more flexible* We need to account for different types of linking; this may require an additional field beyond 'links_to' that has a similar effect but works differently. For instance, Table-B might have a 'has_a' relationship with Table-A, but Table-A might have a 'links_to' relationship with Table-B. (Themes in OpenInteract work like this.) We need to be able to specify that when Table-A severs its relationship with one or more objects from Table-B, the actual object is removed rather than just a link between them. BUGS ==== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/Configure/Ruleset, Next: SPOPS/DBI, Prev: SPOPS/Configure/DBI, Up: Module List Install variables, subroutines and process inherited rulesets per class *********************************************************************** NAME ==== SPOPS::Configure::Ruleset - Install variables, subroutines and process inherited rulesets per class SYNOPSIS ======== # Note that this is almost (entirely?) exclusively done # from SPOPS::Configure SPOPS::Configure::Ruleset->create_relationship( $spops_config ); DESCRIPTION =========== This class only has one method: create_relationship. It is almost always called from the *Note SPOPS/Configure: SPOPS/Configure, class after doing intitial processing of classes. The method takes configuration information for a SPOPS class. For this class, we install a package variable \%RULESET and a method RULESET which returns that hashref. The variable \%RULESET holds all of the applicable rules for that particular class, and after creating the variable and subroutine we find all of the rules inherited by the class and install them in the class. This is a performance win, since we do not need to dynamically search them out everytime the rules for a particular action need to be executed. See *Note SPOPS: SPOPS, for more information about what a ruleset is and how it is executed. METHODS ======= *create_relationship( \%spops_config )* Install the package variable \%RULESET and the method RULESET to access it. Also find all the rules that apply to a particular class (inherited from parents) and install to the class. TO DO ===== BUGS ==== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/DBI, Next: SPOPS/DBI/Keypool, Prev: SPOPS/Configure/Ruleset, Up: Module List Implement SPOPS class, serializing into a DBI database ****************************************************** NAME ==== SPOPS::DBI - Implement SPOPS class, serializing into a DBI database SYNOPSIS ======== use SPOPS::DBI; @ISA = qw( SPOPS::DBI ); ... DESCRIPTION =========== This SPOPS class is not meant to be used directly. Instead, you inherit certain methods from it while implementing your own. Your module should implement: * (optional) Methods to sort member objects or perform operations on groups of them at once. * (optional) Methods to relate an object of this class to objects of other classes - for instance, to find all users within a group. * (optional) The initialization method (*_class_initialize()*), which should create a config() object stored in the package variable and initialize it with configuration information relevant to the class. * (optional) Methods to accomplish actions before/after many of the actions implemented here: fetch/save/remove. * (optional) Methods to accomplish actions before/after saving or removing an object from the cache. Of course, these methods can also do anything else you like. :) As you can see, all the methods are optional. Along with *Note SPOPS/Configure: SPOPS/Configure, and *Note SPOPS/Configure/DBI: SPOPS/Configure/DBI,, you can create an entirely virtual class consisting only of configuration information. So you can actually create the implementation for a new object in two steps: 1. Create the configuration file (or add to the existing one) 2. Create the database table the class depends on. Complete! DATA ACCESS METHODS =================== The following methods access configuration information about the class but are specific to the DBI subclass. * base_table ($): Just the table name, no owners or db names prepended. * table_name ($): Fully-qualified table name * field (\%): Hashref of fields/properties (field is key, value is true) * field_list (\@): Arrayref of fields/propreties * no_insert (\%): Hashref of fields not to insert (field is key, value is true) * no_update (\%): Hashref of fields not to update (field is key, value is true) * skip_undef (\%): Hashref of fields to skip update/insert if they are undefined (field is key, value is true) * field_alter (\%): Hashref of data-formatting instructions (field is key, instruction is value) OBJECT METHODS ============== *id_clause( [ $id, [ $opt, \%params ] )* Returns a snippet of SQL suitable for identifying this object in the database. This can be called either from the class or from a particular object. If called from a class, the $id value must be passed in. Examples: my $id = 35; my $sql = qq/ DELETE FROM $table WHERE @{[ $class->id_clause( $id ) ]} /; print "SQL: $sql"; >> SQL: DELETE FROM this_table WHERE this_table.this_id = 35 $class->db_select( ... where => $class->id_clause( 15 ), ... ) If the system cannot determine the data type of the id field, it makes a best guess based on the package variable GUESS_ID_FIELD_TYPE. It defaults to SQL_INTEGER (as set by DBI), but you can set it dynamically as well: $SPOPS::DBI::GUESS_ID_FIELD_TYPE = SQL_VARCHAR; Note that the default behavior is to create a fully-qualified ID field. If you do not wish to do this (for instance, if you need to use the ID field for a lookup into a link table), just pass 'noqualify' as the second argument. To use the example from above: my $id = 35; my $sql = qq/ DELETE FROM $table WHERE @{[ $class->id_clause( $id, 'noqualify' ) ]} /; print "SQL: $sql"; >> SQL: DELETE FROM this_table WHERE this_id = 35 *fetch( $id, \%params )* Fetches the information for an object of type class from the data store, creates an object with the data and returns the object. Any failures trigger a die with pertinent information as described in `ERROR HANDLING' in this node. If you have security turned on for the object class, the system will first check if the currently-configured user is allowed to fetch the object. If the user has less that SEC_LEVEL_READ access, the fetch is denied and a die() triggered. Note that if the fetch is successful we store the access level of this object within the object itself. Check the temporary property {tmp_security_level} of any object and you will find it. Parameters: field_alter - (\%) fields are keys, values are database-dependent formatting strings. You can accomplish different types of date-formatting or other manipulation tricks. You can also pass a DEBUG value to get debugging information for that particular statement dumped into the error log: my $obj = eval { $class->fetch( $id, { DEBUG => 1 } ); }; *fetch_group( \%params )* Returns an arrayref of objects that meet the criteria you specify. Parameters: where - a WHERE clause; leave this blank and you will get all entries order - an ORDER BY clause; leave this blank and the order is arbitrary other parameters get passed onto the fetch() statement when the records are being retrieved. This is actually fairly powerful. Examples: # Get all the user objects and put them in a hash # indexed by the id my %uid = map { $_->id => $_ } @{ $R->user->fetch_group( { order => 'last_name' } ) }; # Get only those user objects for people who have # logged in today my $users = $R->user->fetch_group( { where => 'datediff( dd, last_login, get_date() ) = 0', order => 'last_name' } ); foreach my $user ( @{ $users } ) { print "User: $user->{login_name} logged in today.\n"; } Note that you can also return objects that match the results of a join query: my $list = eval { $class->fetch_group( { order => 'item.this, item.that', from => [ 'item', 'modifier' ], where => 'modifier.property = ? AND ' . 'item.item_id = modifier.item_id', value => [ 'property value' ], } ); }; *save( [ \%params ] )* Object method that saves this object to the data store. Returns the new ID of the object if it is an add; returns the object ID if it is an update. As with other methods, any failures trigger a die(). Example: my $obj = $class->new; $obj->{param1} = $value1; $obj->{param2} = $value2; my $new_id = eval { $obj->save }; if ( $@ ) { print "Error inserting object: $@->{error}\n"; } else { print "New object created with ID: $new_id\n"; } The object can generally tell whether it should be created in the data store or whether it should update the data store values. Currently it determines this by the presence of an ID value. If an ID value exists, this is probably an update; if it does not exist, it is probably a save. You can give SPOPS hints otherwise. If you are controlling ID values yourself and an ID value exists in your object, you can do: $obj->save({ is_add => 1 }); to tell SPOPS to treat the request as a creation rather than an update. One other thing to note if you are using *Note SPOPS/Secure: SPOPS/Secure, for security: SPOPS assumes that your application determines whether a user can create an object. That is, all requests to create an object are automatically approved. Once the object is created, the initial security logic kicks in and any further actions (fetch/save/remove) are controlled by `SPOPS::Secure'. Note that if your database schema includes something like: CREATE TABLE my_table ( my_id int, created_on datetime default today(), table_legs tinyint default 4 ) and your object had the following values: my_id => 10, created_on => undef, table_legs => undef The only thing your object would reflect after inserting is the ID, since the other values are filled in by the database. The save() method tries to take this into account and syncs the object up with what is in the database once the record has been successfully inserted. If you want to skip this step, either pass a positive value for the 'no_sync' key or set 'no_save_sync' to a positive value in the CONFIG of the implementing class. *remove( [ \%params ] )* Note that you can only remove a saved object (duh). Also tries to remove the object from the cache. The object will not actually be destroyed until it goes out of scope, so do not count on its DESTROY method being called exactly when this happens. Returns 1 on success, die() with hashref on failure. Example: eval { $obj->remove }; if ( $@ ) { print "Object not removed. Error: $@->{error}"; } else { print "Object removed properly."; } *log_action( $action, $id )* Implemented by subclass. This method is passed the action performed upon an object ('create', 'update', 'remove') and the ID. SPOPS::DBI comes with an empty method, but you can subclass it and do what you wish ERROR HANDLING ============== Like all SPOPS classes, any errors encountered will be tossed up to the application using a die() and a simple string as a message. We also set more detailed information in a number of *Note SPOPS/Error: SPOPS/Error, package variables; see that module for more details. TO DO ===== BUGS ==== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters The following people added to this module with patches and/or advice: * Christian Lemburg  File: pm.info, Node: SPOPS/DBI/Keypool, Next: SPOPS/DBI/MySQL, Prev: SPOPS/DBI, Up: Module List Retrieves ID field information from a pool ****************************************** NAME ==== SPOPS::DBI::Keypool - Retrieves ID field information from a pool SYNOPSIS ======== package MySPOPS; @MySPOPS::ISA = qw( SPOPS::DBI::Keypool SPOPS::DBI ); DESCRIPTION =========== This module retrieves a value from a pool of key values matched up to tables. It is not as fast as IDENTITY fields, auto_incrementing values or sequences, but it is portable among databases and, most importantly, works in a replicated environment. It also has the benefit of being fairly simple to understand. Currently, the key fetching procedure is implemented via a stored procedure for portability among tools in different languages, but it does not have to remain this way. It is perfectly feasible to program the entire procedure in perl. BUGS ==== *Put this class before others in ISA* Not really a bug, but you must put this class before any database-specific ones in your @ISA, otherwise you will not see the results of this class and likely get very confused. TO DO ===== *Make option for perl implementation* Allow authors to use a perl implementation of a key pool rather than relying on a stored procedure (particularly for those databases without stored procedures...). COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/DBI/MySQL, Next: SPOPS/DBI/RandomCode, Prev: SPOPS/DBI/Keypool, Up: Module List MySQL-specific code for DBI collections *************************************** NAME ==== SPOPS::DBI::MySQL - MySQL-specific code for DBI collections SYNOPSIS ======== package MySPOPS; @MySPOPS::ISA = qw( SPOPS::DBI::MySQL SPOPS::DBI ); DESCRIPTION =========== This just implements some MySQL-specific routines so we can abstract them out. One of these items is to return the just-inserted ID. Only works for tables that have at least one auto-increment field: CREATE TABLE my_table ( id int not null auto_increment, ... ) BUGS ==== TO DO ===== SEE ALSO ======== *Note DBD/mysql: DBD/mysql,, `DBI' in this node COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/DBI/RandomCode, Next: SPOPS/DBI/Sybase, Prev: SPOPS/DBI/MySQL, Up: Module List Creates a random code for the ID field ************************************** NAME ==== SPOPS::DBI::RandomCode - Creates a random code for the ID field SYNOPSIS ======== package MySPOPS; @MySPOPS::ISA = qw( SPOPS::DBI::RandomCode SPOPS::DBI ); DESCRIPTION =========== Very, very simple. We just use the *generate_random_code()* method in all SPOPS classes to generate an n character code. The width of the code is determined by the field {id_width} in the CONFIG of the SPOPS implementation. BUGS ==== *Getting a 'random' value* If you are using this under mod_perl, you might have the problem of colliding ID fields. This seems to happen because the httpd children all have the same random seed, since they are all forked off from the same parent. The solution is to put a 'srand()' in the PerlChildInitHandler, although mod_perl versions from 1.25 on might take care of this for you. TO DO ===== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/DBI/Sybase, Next: SPOPS/Error, Prev: SPOPS/DBI/RandomCode, Up: Module List Sybase-specific routines for the SPOPS::DBI ******************************************* NAME ==== SPOPS::DBI::Sybase - Sybase-specific routines for the SPOPS::DBI SYNOPSIS ======== # In your configuration: 'myspops' => { 'isa' => [ qw/ SPOPS::DBI::Sybase SPOPS::DBI / ], # If you have an IDENTITY field, set syb_identity to true 'syb_identity' => 1, ... }, DESCRIPTION =========== This just implements some Sybase-specific routines so we can abstract them out. One of them optionally returns the IDENTITY value returned by the last insert. Of course, this only works if you have an IDENTITY field in your table: CREATE TABLE my_table ( id numeric( 8, 0 ) IDENTITY not null, ... ) NOTE: You also need to let this module know if you are using this IDENTITY option by setting in your class configuration the key 'syb_identity' to a true value. METHODS ======= *sql_quote* `DBD::Sybase' depends on the type of a field if you are quoting values to put into a statement, so we override the default 'sql_quote' from `SPOPS::SQLInterface' to ensure the type of the field is used in the DBI->quote call. BUGS ==== TO DO ===== SEE ALSO ======== *Note SPOPS/Key/DBI/Identity: SPOPS/Key/DBI/Identity,, `DBD::Sybase' in this node, `DBI' in this node COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/Error, Next: SPOPS/GDBM, Prev: SPOPS/DBI/Sybase, Up: Module List Centralized error messages from all SPOPS objects. ************************************************** NAME ==== SPOPS::Error - Centralized error messages from all SPOPS objects. SYNOPSIS ======== # Using SPOPS in your application my $obj_list = eval { $obj->fetch_group( { where => 'this = that' } ) }; if ( $@ ) { warn "Error found! Error: $@\n", "Error type: $SPOPS::Error::type\n", "More specific: $SPOPS::Error::system_msg\n", "Extra stuff:\n", "--$SPOPS::Error::extra->{sql}\n", "--$SPOPS::Error::extra->{valuesb}\n"; } DESCRIPTION =========== This class provides a central location for error messages from all SPOPS modules. The error information collected in these variables is guaranteed to result from the most recent error generated by SPOPS. VARIABLES ========= All of these variables are package variables, so you refer to them like this: $SPOPS::Error:: $SPOPS::Error::system_msg See the `NOTES' in this node section below for hints on making the error variables shorter. *user_msg* ($) A generic message that is suitable for showing a user. When telling a user something went wrong, you do not want to tell them: execute called with 2 bind variables when 1 are needed instead, you want to tell them: Database query failed to execute This variable is identical to the value thrown by the *die()* command, so you do not normally need to refer to it. *system_msg* ($) Even though you do not want to show your users details of the error, you still need to know them! The variable *system_msg* gives you details regarding the error. type ($) SPOPS knows about a few types of errors. Some depend on your SPOPS implementation (e.g., DBI, dbm, LDAP, etc.). Others can be: -security: There is a security violation and the action could not be completed package ($) Set to the package from where the error was thrown. method ($) Set to the method from where the error was thrown. filename ($) Set to the filename from where the error was thrown. line ($) Set to the line number from where the error was thrown. *extra* (\%) Different SPOPS classes have different information related to the current request. For instance, DBI errors will typically fill the 'sql' and 'values' keys. Other SPOPS implementations may use different keys; see their documentation for details. METHODS ======= clear Clears the current error saved in the class. Classes outside the *SPOPS::* hierarchy should never need to call this. No return value. get() Returns a hashref with all the currently set error values. *set( \% )* First clears the variables then sets them all in one fell swoop. The variables that are set are passed in the first argument, a hashref. Also sets both the package and method variables for you, although you can override by setting manually. No return value; NOTES ===== Some people might find it easier to alias a local package variable to a SPOPS error variable. For instance, you can do: *err_user_msg = \$SPOPS::Error::user_msg; *err_system_msg = \$SPOPS::Error::system_msg; *err_type = \$SPOPS::Error::type; *err_extra = \%SPOPS::Error::extra; And then refer to the alias in your local package: my $obj_list = eval { $obj->fetch_group( { where => 'this = that' } ) }; if ( $@ ) { warn "Error found! Error: $@\n", "Error type: $err_type\n", "More specific: $err_system_msg\n", "Extra stuff:\n", "--$err_extra{sql}\n", "--$err_extra{values}\n"; } Whatever floats your boat. TO DO ===== BUGS ==== COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/GDBM, Next: SPOPS/HashFile, Prev: SPOPS/Error, Up: Module List Store SPOPS objects in a GDBM database ************************************** NAME ==== SPOPS::GDBM - Store SPOPS objects in a GDBM database SYNOPSIS ======== my $obj = Object::Class->new; $obj->{parameter1} = 'this'; $obj->{parameter2} = 'that'; my $id = $obj->save; DESCRIPTION =========== Implements SPOPS persistence in a GDBM database. Currently the interface is not as robust or powerful as the `SPOPS::DBI' implementation, but if you want more robust data storage, retrieval and searching needs you should probably be using a SQL database anyway. This is also a little different than the `SPOPS::DBI' module in that you have a little more flexibility as to how you refer to the actual GDBM file required. Instead of defining one database throughout the operation, you can change in midstream. (To be fair, you can also do this with the `SPOPS::DBI' module, it is just a little more difficult.) For example: # Read objects from one database, save to another my @objects = Object::Class->fetch_group( { filename => '/tmp/object_old.gdbm' } ); foreach my $obj ( @objects ) { $obj->save( { is_add => 1, gdbm_filename => '/tmp/object_new.gdbm' } ); } METHODS ======= *id_field* If you want to define an ID field for your class, override this. Can be a class or object method. *class_initialize* Much the same as in DBI. (Nothing interesting.) *initialize( \%params )* Much the same as in DBI, although you are able to initialize an object to use a particular filename by passing a value for the 'GDBM_FILENAME' key in the hashref for parameters when you create a new object: my $obj = Object::Class->new( { GDBM_FILENAME = '/tmp/mydata.gdbm' } ); *global_gdbm_tie( \%params )* Returns a tied hashref if successful. There are many different ways of creating a filename used for GDBM. You can define a default filename in your package configuration; you can pass it in with every request (using the parameter 'filename'); you can define a file fragment (non-specific directory name plus a filename, like 'conf/package.gdbm') and then pass a directory to anchor the filename with every request. Parameters: perm ($ (default 'read') Defines the permissions to open the GDBM file. GDBM recognizes three permissions: 'GDBM_READER', 'GDBM_WRITER', 'GDBM_WRCREAT' (for creating and having write access to the file). You only need to pass 'read', 'write', or 'create' instead of these constants. If you pass nothing, C will assume 'read'. Also note that on some GDBM implementations, specifying 'write' permission to a file that has not yet been created still creates it, so 'create' might be redundant on your system. filename ($) (optional) Filename to use. If it is not passed, we look into the 'tmp_gdbm_filename' field of the object, and then the 'filename' key of the 'gdbm_info' key of the class config, and then the 'filename' key of the 'gdbm_info' key of the global configuration. directory ($) (optional) Used if you have defined 'file_fragment' within your package configuration; we join the directory and filename with a '/' to create the gdbm filename. id If you have defined a routine that returns the 'id_field' of an object, it returns the value of that for a particular object. Otherwise it executes the coderef found in the 'create_id' key of the class configuration for the object. Usually this is something quite simple: ... 'create_id' => sub { return join( '--', $_[0]->{name}, $_[0]->{version} ) } ... In the config file just joins the 'name' and 'version' parameters of an object and returns the result. *object_key* Creates a key to store the object in GDBM. The default is to prepend the class to the value returned by id() to prevent ID collisions between objects in different classes. But you can make it anything you want. *fetch( $id, \%params * Retrieve a object from a GDBM database. Note that $id corresponds not to the object key, or the value used to store the data. Instead it is a unique identifier for objects within this class. You can pass normal db parameters. *fetch_group( \%params )* Retrieve all objects from a GDBM database from a particular class. If you modify the 'object_key' method, you will probably want to modify this as well. You can pass normal db parameters. *save( \%params )* Save (either insert or update) an object in a GDBM database. You can pass normal db parameters. *remove( \%params )* Remove an object from a GDBM database. You can pass normal db parameters. Private Methods --------------- *_return_structure_for_key( \%params )* Returns the data structure in the GDBM database corresponding to a particular key. This data structure is not blessed yet, it is likely just a hashref of data (depending on how you implement your objects, although the default method for SPOPS objects is a tied hashref). This is an internal method, so do not use it. You can pass normal db parameters. TO DO ===== BUGS ==== SEE ALSO ======== GDBM software: http://www.fsf.org/gnulist/production/gdbm.html GDBM on Perl/Win32: http://www.roth.net/perl/GDBM/ COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters  File: pm.info, Node: SPOPS/HashFile, Next: SPOPS/Impl/SecurityObj, Prev: SPOPS/GDBM, Up: Module List Implement as objects files containing perl hashrefs dumped to text ****************************************************************** NAME ==== SPOPS::HashFile - Implement as objects files containing perl hashrefs dumped to text SYNOPSIS ======== my $config = SPOPS::HashFile->new( { filename => '/home/httpd/myapp/server.perl', perm => 'read' } ); print "My SMTP host is $config->{smtp_host}"; # Setting a different value is ok... $config->{smtp_host} = 'smtp.microsoft.com'; # ...but this will 'die' with an error, since you set the permission # to read-only 'read' in the 'new' call $config->save; DESCRIPTION =========== Implement a simple interface that allows you to use a perl data structure dumped to disk as an object. This is often used for configuration files, since the key/value, and the flexibility of the 'value' part of the equation, maps well to varied configuration directives. METHODS ======= *new( { filename => $, [ perm => $ ] } )* Create a new `SPOPS::HashFile' object that uses the given filename and, optionally, the given permission. The permission can be one of three values: 'read', 'write' or 'new'. If you try to create a new object without passing the 'new' permission, the action will die because it cannot find a filename to open. Any value passed in that is not 'read', 'write' or 'new' will get changed to 'read', and if no value is passed in it will also be 'read'. Note that the 'new' permission does not mean that a new file will overwrite an existing file automatically. It simply means that a new file will be created if one does not already exist; if one does exist, it will be used. The 'read' permission only forbids you from saving the object or removing it entirely. You can still make modifications to the data in the object. This overrides the new() method from SPOPS. *fetch( $filename, [ { perm => $ } ] )* Retrieve an existing config object (just a perl hashref data structure) from a file. The action will result in a 'die' if you do not pass a filename, if the file does not exist or for any reason you cannot open it. save Saves the object to the file you read it from. remove Deletes the file you read the object from, and blanks out all data in the object. *clone( { filename => $, [ perm => $ ] } )* Create a new object from the old, but you can change the filename and permission at the same time. Example: my $config = SPOPS::HashFile->new( { filename => '~/myapp/spops.perl' } ); my $new_config = $config->clone( { filename => '~/otherapp/spops.perl', perm => 'write' } ); $new_config->{base_dir} = '~/otherapp/spops.perl'; $new_config->save; This overrides the clone() method from SPOPS. NOTES ===== *No use of SPOPS::Tie* TO DO ===== *Use SPOPS::Tie* This is one of the few SPOPS implementations that will never use the `SPOPS::Tie' class to implement its data holding. We still use a tied hash, but it is much simpler - no field checking, no ensuring that the keys match in case, etc. This just stores some information about the object (filename, permission, and data) and lets you go on your merry way. However, since we recently changed *Note SPOPS/Tie: SPOPS/Tie, to make field-checking optional we might be able to use it. BUGS ==== SEE ALSO ======== *Note SPOPS: SPOPS,, *Note Data/Dumper: Data/Dumper, COPYRIGHT ========= Copyright (c) 2001 intes.net, inc.. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHORS ======= Chris Winters