home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / Field.pm < prev    next >
Encoding:
Perl POD Document  |  2001-11-05  |  32.1 KB  |  1,011 lines

  1. ############################################################################
  2. #
  3. # Win32::ASP::Field - an abstract parent class for representing database
  4. #                     fields in the Win32-ASP-DB system
  5. #
  6. # Author: Toby Everett
  7. # Revision: 0.02
  8. # Last Change:
  9. ############################################################################
  10. # Copyright 1999, 2000 Toby Everett.  All rights reserved.
  11. #
  12. # This file is distributed under the Artistic License. See
  13. # http://www.ActiveState.com/corporate/artistic_license.htm or
  14. # the license that comes with your perl distribution.
  15. #
  16. # For comments, questions, bugs or general interest, feel free to
  17. # contact Toby Everett at teverett@alascom.att.com
  18. ############################################################################
  19.  
  20. use Class::SelfMethods;
  21. use Error qw/:try/;
  22. use Win32::ASP::Error;
  23.  
  24. package Win32::ASP::Field;
  25. @ISA = ('Class::SelfMethods');
  26.  
  27. use strict;
  28.  
  29. =head1 NAME
  30.  
  31. Win32::ASP::Field - an abstract parent class for representing database fields, used by Win32::ASP::DBRecord
  32.  
  33. =head1 SYNOPSIS
  34.  
  35.   use Win32::ASP::Field;
  36.  
  37.     %hash = (
  38.     Win32::ASP::Field->new(
  39.             name => 'RecordID',
  40.             sec  => 'ro',
  41.             type => 'int',
  42.             desc => 'Record ID',
  43.         ),
  44.  
  45.     Win32::ASP::Field->new(
  46.             name => 'SemiSecureField',
  47.             sec  => sub {
  48.                 my $self = shift;
  49.                 my($record) = @_;
  50.  
  51.                 return $record->role eq 'admin' ? 'rw' : 'ro';
  52.             },
  53.             type => 'varchar',
  54.             desc => 'Semi Secure Field',
  55.         ),
  56.  
  57.     Win32::ASP::Field->new(
  58.             name => 'Remarks',
  59.             sec  => 'rw',
  60.             type => 'text',
  61.         ),
  62.     );
  63.  
  64. =head1 DESCRIPTION
  65.  
  66. =head2 Background
  67.  
  68. Field objects are very strange Perl objects.  Perl is class based, not prototype based.
  69. Unfortunately, unless you want to create a separate class for every mildly wierd field in your
  70. database, a class based system is sub-optimal for our purposes.  To get around this I implemented
  71. a "weak" form of a prototype based language.
  72.  
  73. The major parent is C<Class::SelfMethods>.  It provides an C<AUTOLOAD> that implements the desired
  74. behavior.  In a nutshell, when asked to resolve a method it does the following:
  75.  
  76. =over 4
  77.  
  78. =item *
  79.  
  80. First it checks for whether the object has a property with that name.  If it does and the property
  81. is not a code reference, it returns the value.
  82.  
  83. =item *
  84.  
  85. If the property is a code reference, it evaluates the code reference on the object with the passed
  86. parameters.  This means that you can define "instance" (not class) methods by placing anonymous
  87. subroutines in the instance.  These override the class method. If you need to call the equivalent
  88. of C<SUPER::>, call C<_method> on the object.
  89.  
  90. =item *
  91.  
  92. If the property does not exist, it attempts to call C<_method> on the object.  Thus, calling
  93. C<read> on an instance calls the C<_read> method in the class definition if there is no matching
  94. property.  If the C<_read> method exists, C<AUTOLOAD> will not get called again.  On the other
  95. hand if it does not exist, rather than call C<__read>, the C<AUTOLOAD> subroutine will return
  96. empty-handed. This way, if the desired property is not defined for the object, C<undef> will be
  97. the default behavior.
  98.  
  99. =back
  100.  
  101. It is important to understand the above "hierarchy of behavior" if you are to make full use of the
  102. customizability of Field objects.  In a nutshell, when creating new proper classes all methods
  103. should be defined with a leading underscore, but called without the leading underscore, so that
  104. they can be over-ridden as needed.  One should B<never> directly access the hash C<$self>, but
  105. always let C<AUTOLOAD> access it by calling the method with that name.  That way instance
  106. variables can be implemented as subroutines if need be.  It also makes it easy to provide
  107. "default" behavior by implementing a method.  The only time a method should be called with a
  108. leading underscore is when an instance-defined method needs to call the method it is over-riding.
  109.  
  110.  
  111.  
  112. =head2 Methods
  113.  
  114. Except for C<new>, which is discussed here, the majority of these are farther down under
  115. L</INTERNALS>.
  116.  
  117. =head3 new
  118.  
  119. The C<new> method for C<Win32::ASP::Field> is rather strange.  It returns two values - the name along
  120. with the C<Win32::ASP::Field> object.  This makes it much easier to create hashes of C<Win32::ASP::Field>
  121. objects.  The parameters passed to the C<new> method should be the desired properties for the new
  122. object.
  123.  
  124. One oddity is that the C<type> property will be used to autoload C<Win32::ASP::Field::>I<C<type>> and the
  125. returned object will be of that class.  This makes it possible to create arbitrary C<Win32::ASP::Field>
  126. objects without an explicit list of C<use> statements at the top.
  127.  
  128. For an example of how to use C<new>, see the L</SYNOPSIS> at the top of the POD.
  129.  
  130. For a discussion of how C<new> treats passed parameters that have a name that starts with an
  131. underscore, see L<Meta properties|/Meta>.
  132.  
  133.  
  134.  
  135. =head2 Properties
  136.  
  137. =head3 Required
  138.  
  139. =over 4
  140.  
  141. =item name
  142.  
  143. This is the name of the field.  Unlike the other properties, it is not passed the C<$record> in
  144. question.
  145.  
  146. =item sec
  147.  
  148. This specified whether the field is read-write (C<'rw'>) or read-only (C<'ro'>).  Note, you can
  149. implement this as a subroutine and it gets passed C<$record>.  If it is not implemented or returns
  150. a value not equal to one of the above, it is presumed that the value is not accessible.  Note that
  151. C<$record> may not be fully formed when C<seq> is called in C<_read>.  You may wish to return
  152. C<'ro'> if in doubt.
  153.  
  154. =back
  155.  
  156.  
  157. =head3 Optional
  158.  
  159. =over 4
  160.  
  161. =item reqd
  162.  
  163. If this returns true than the field is required to be filled out when writing.
  164.  
  165. =item desc
  166.  
  167. This is the friendly description for the object.  This gets used for column headings in tables.
  168. If not specified, it defaults to C<name>.
  169.  
  170. =item help
  171.  
  172. This is the text that displays in the browser status area when the mouse is placed over the edit
  173. area in edit mode
  174.  
  175. =item size
  176.  
  177. This is used for C<TEXT> and C<TEXTAREA> form fields to define their width.
  178.  
  179. =item maxl
  180.  
  181. This is used to specify the maximum length of a varchar field.
  182.  
  183. =item textrows
  184.  
  185. This is used to specify the number of rows for a C<TEXTAREA> form field.
  186.  
  187. =item formname
  188.  
  189. This is used to deal with situations where a field in a child record has the same name as a field
  190. in a parent record.  This would, of course, complicate the resultant HTML form.  To deal with this
  191. situation, specify C<formname>.  If not specified, the default method will return C<name>.
  192.  
  193. =item writename
  194.  
  195. This is used to indicate the actual SQL field used for recording. It is frequently used in
  196. conjunction with C<as_write>.  It can sometimes be very handy to use a subroutine for
  197. C<writename>.  As a subroutine, it gets passed C<$value>.  If it needs the whole record to make
  198. its decision, you will need to intercept the C<as_write_pair> method.
  199.  
  200. Say, for instance, that you have a logging record with a C<SmallValue> field that is a 50
  201. character C<varchar> and a C<LargeValue> field that is a C<text> field.  The idea is that for
  202. short strings you won't incur much cost from the C<LargeValue> field because uninitialized C<text>
  203. fields don't create a 2K page.  If the string is longer, however, you want to write to the
  204. LargeValue field.  If the percentage of short strings is 50%, the solution would save ~49.7% on
  205. space requirements.  The penalty of the unused C<varchar> for the long strings is small contrasted
  206. with the savings by not using the C<text> field on the short ones.
  207.  
  208. In that situation, one might implement C<writename> like this:
  209.  
  210.     writename => sub {
  211.         my $self = shift;
  212.     my($value) = @_;
  213.  
  214.     return length($value) > 50 ? 'LargeValue' : 'SmallValue';
  215.     }
  216.  
  217. The discussion of C<read> includes an appropriate instance level method to round out this
  218. demonstration. No implementation of C<as_write> is required because the formatting for
  219. C<varchar> and C<text> fields is the same.
  220.  
  221. =item option_list
  222.  
  223. This should return an anonymous array of options that will be provided to the user when editing
  224. the field.  Its presence indicates to C<as_html_edit_rw> the intention to use C<as_html_options>.
  225.  
  226. =back
  227.  
  228.  
  229.  
  230. =head3 Meta
  231.  
  232. Meta properties are a funky way of executing additional code at the time of object creation.  The
  233. new method accepts a parameter list and returns two values - the name of the field and the field
  234. object itself.  The advantage of this is that it makes creating a hash of field object much
  235. easier.  On the other hand, it requires some excessively fancy notation to make method calls on
  236. the newly created object while in the hash specifier.  However, there's any easy way to indicate
  237. when you want a parameter to be a method call.  Since parameters don't start with underscores and
  238. all actual implementations in class code do, it makes sense to start meta properties with an
  239. underscore.  The new method simply scans the list of parameters for those that start with an
  240. underscore and strips them out of the parameter hash for later use.  The value of the parameter
  241. should be an anonymous array of parameters to the method.
  242.  
  243. Typical use of meta properties is to provide code for creating commonly used instance methods.
  244.  
  245. =over 4
  246.  
  247. =item _standard_option_list
  248.  
  249. This meta property sets up C<writename>, C<as_write>, and C<option_list> for use with a fairly
  250. standard option list that uses a "hidden" code field and a lookup table that has friendly
  251. descriptions.  Example usage might look like so:
  252.  
  253.     _standard_option_list => [
  254.         class     => 'MyDatabase::MyRecord',
  255.         writename => 'LookupCode',
  256.         table     => 'LookupCodes',
  257.         field     => 'LookupCode',
  258.         desc      => 'Description'
  259.     ],
  260.  
  261. Note that although the method is expecting a hash of properties, the parameter list is stored in
  262. an anonymous array when passed in during the new method.
  263.  
  264. Of note, the C<as_write> and C<option_list> methods are implemented to help minimize SQL
  265. traffic. The first call to the C<option_list> method will result in setting
  266. C<$self-E<gt>{option_list}> to a reference to the anonymous array before returning that array.
  267. Further calls will automatically return that array based on the behavior of the C<AUTOLOAD> method
  268. in C<Class::SelfMethods>.  See the entry for C<group> for a discussion of the behavior for
  269. C<as_write>.
  270.  
  271. =over 4
  272.  
  273. =item class
  274.  
  275. This specifies the C<Win32::ASP::DBRecord> subclass to which this field belongs.  This will be used later
  276. to access the C<_FIELDS> hash and the C<_DB> object.
  277.  
  278. =item writename
  279.  
  280. This specifies the field within the record object that will be written.
  281.  
  282. =item table
  283.  
  284. This specifies the name of the table that contains the list of codes and the friendly
  285. descriptions.
  286.  
  287. =item field
  288.  
  289. This specifies the name of the field within that table that contains the code.  Frequently, but
  290. not always, this will be the same as C<writename>.
  291.  
  292. =item desc
  293.  
  294. This specifies the name of the description field in the lookup table.
  295.  
  296. =item group
  297.  
  298. This specifies whether there are likely to be multiple calls to C<as_write>.  If not present
  299. or set to a false value, C<as_write> will only lookup the passed value.  If set to a true
  300. value, the first call to C<as_write> will lookup all the codes and store them in a hash for
  301. further reference.  This will reduce SQL traffic in situations where an C<Win32::ASP::DBRecord> object is
  302. used within a C<Win32::ASP::DBRecordGroup> for editing records.  Unfortunately, the code isn't smart
  303. enough to know whether it is being used in a group or on its own, so you have to hard code it.  On
  304. the other hand, if you need that level of flexibility, you can roll your own methods.
  305.  
  306. =back
  307.  
  308. =back
  309.  
  310. =cut
  311.  
  312. =head2 INTERNALS
  313.  
  314. This is where internal methods are discussed with an eye towards over-riding them if need be.
  315.  
  316. =cut
  317.  
  318. sub new {
  319.   my $class = shift;
  320.   my(%params) = @_;
  321.  
  322.   unless (exists($params{type})) {
  323.     $class eq 'Win32::ASP::Field' and
  324.       die "You should not create generic Win32::ASP::Field objects w/out a type.";
  325.     ($params{type} = $class) =~ s/^.*:://;
  326.   }
  327.  
  328.   if($class ne "Win32::ASP::Field::".$params{type}) {
  329.     $class = "Win32::ASP::Field::".$params{type};
  330.     (my $temp = "$class.pm") =~ s/::/\//g;
  331.     require $temp;
  332.     return($class->new(%params));
  333.   }
  334.  
  335.   my $self = $class->SUPER::new(%params);
  336.  
  337.   return($self->name, $self);
  338. }
  339.  
  340. sub _formname {
  341.   my $self = shift;
  342.   return $self->name;
  343. }
  344.  
  345. sub _writename {
  346.   my $self = shift;
  347.   return $self->name;
  348. }
  349.  
  350. sub _desc {
  351.   my $self = shift;
  352.   return $self->name;
  353. }
  354.  
  355. sub _size {
  356.   my $self = shift;
  357.   return 20;
  358. }
  359.  
  360. sub _maxl {
  361.   my $self = shift;
  362.   return;
  363. }
  364.  
  365. sub _help {
  366.   my $self = shift;
  367.   return;
  368. }
  369.  
  370. sub _reqd {
  371.   my $self = shift;
  372.   return;
  373. }
  374.  
  375. =head3 Checkers
  376.  
  377. These are quick little methods to provided standardized ways of checking certain boolean
  378. "properties"
  379.  
  380. =over 4
  381.  
  382. =item can_view
  383.  
  384. The C<can_view> method is used to determine if someone has view privileges on a given field. The
  385. default implementation, C<_can_view>, tests C<$self-E<gt>sec($record)> for equivalence with
  386. 'C<ro>' or 'C<rw>'.
  387.  
  388. Implementations can expect the $record as a parameter and should return 1 or 0 as appropriate.
  389.  
  390. =cut
  391.  
  392. sub _can_view {
  393.   my $self = shift;
  394.   my($record) = @_;
  395.   return $self->sec($record) =~ /^r[ow]$/ ? 1 : 0;
  396. }
  397.  
  398.  
  399.  
  400. =item can_edit
  401.  
  402. The C<can_edit> method is used to determine if someone has edit privileges on a given field. The
  403. default implementation, C<_can_edit>, tests C<$self-E<gt>sec($record)> for equivalence with
  404. 'C<rw>'.
  405.  
  406. Implementations can expect the $record as a parameter and should return 1 or 0 as appropriate.
  407.  
  408. =cut
  409.  
  410. sub _can_edit {
  411.   my $self = shift;
  412.   my($record) = @_;
  413.   return $self->sec($record) eq 'rw' ? 1 : 0;
  414. }
  415.  
  416.  
  417.  
  418. =item is_option_list
  419.  
  420. The C<is_option_list> method is used to determine if a field should be displayed using an
  421. option list.  The default implementation, C<_is_option_list>, tests for the existence of
  422. C<$self-E<gt>{option_list}>.  This is technically verboten, but it's a performance improvement
  423. over returning the full C<option_list> in order to test for it.  If you implement a subclass that
  424. implements C<option_list>, you should also implement C<_is_option_list>.
  425.  
  426. Implementations can expect $record and $data as a parameter and should return 1 or 0 as
  427. appropriate.
  428.  
  429. =cut
  430.  
  431. sub _is_option_list {
  432.   my $self = shift;
  433.   return exists $self->{option_list} ? 1 : 0;
  434. }
  435.  
  436.  
  437.  
  438. =back
  439.  
  440. =head3 Loaders
  441.  
  442. These methods are used to load a record with a given field.
  443.  
  444. =over 4
  445.  
  446. =item read
  447.  
  448. The C<read> method is used to read a specific field out of C<$results> into C<$record>.  The
  449. default implementation, C<_read>, first calls C<$self-E<gt>can_view> and then retrieves the
  450. appropriate value (if present) from the results set and places it in C<$record-E<gt>{orig}> as
  451. appropriate.
  452.  
  453. In addition to the parameters C<$record>, the C<Win32::ASP::DBRecord> that will receive the data,
  454. and C<$results>, the ADO Recordset object containing the data, the C<read> method is passed the
  455. parameter C<$columns>.  If C<$columns> contains a reference to a hash and <$self->name> doesn't
  456. return a true value, the data should not be read.  This improves performance when the
  457. C<Win32::ASP::DBRecord> object is part of a C<Win32::ASP::DBRecordGroup> that is being used to
  458. retrieve data from a query where only some of the fields will be displayed.
  459.  
  460. The properly written C<read> for the C<writename> function displayed long ago would be:
  461.  
  462.     read => sub {
  463.         my $self = shift;
  464.     my($record, $results, $columns) = @_;
  465.  
  466.         my $name = $self->name;
  467.     ref($columns) and !$columns->{$name} and return;
  468.     $self->can_view($record) or return;
  469.  
  470.         $record->{orig}->{$name} = undef;
  471.     $results->Fields->Item('SmallValue') and $record->{orig}->{$name} = $results->Fields->Item('SmallValue')->Value;
  472.         if ($record->{orig}->{$name} eq '') {
  473.       $results->Fields->Item('LargeValue') and $record->{orig}->{$name} = $results->Fields->Item('LargeValue')->Value;
  474.         }
  475.     },
  476.  
  477. =cut
  478.  
  479. sub _read {
  480.   my $self = shift;
  481.   my($record, $results, $columns) = @_;
  482.  
  483.   my $name = $self->name;
  484.   ref($columns) and !$columns->{$name} and return;
  485.   $self->can_view($record) or return;
  486.   my $temp = $results->Fields->Item($name);
  487.   if ($temp) {
  488.     $record->{orig}->{$name} = $temp->Value;
  489.     if (exists $record->{edit} and !$self->can_edit($record)) {
  490.       $record->{edit}->{$name} = $record->{orig}->{$name};
  491.     }
  492.   }
  493. }
  494.  
  495.  
  496.  
  497. =item post
  498.  
  499. The C<post> method is used to read a specific field into C<$results> from the POST data.  It also
  500. takes C<$row> as a parameter.  If C<$row> is defined, it presumes that it is dealing with a
  501. DBRecord that is a member of a DBRecordGroup and should retrieve the appropriately indexed value
  502. from the multi-valued POST data.  If it is not defined, it presumes that it is dealing with
  503. single-valued POST data.
  504.  
  505. It assigns the value into C<$record-E<gt>{edit}> as appropriate.  It also tests for whether the
  506. POST data contains any non-whitespace characters and assigns undef if it does not.
  507.  
  508. =cut
  509.  
  510. sub _post {
  511.   my $self = shift;
  512.   my($record, $row) = @_;
  513.  
  514.   my $name = $self->name;
  515.   my $formname = $self->formname;
  516.  
  517.   my $temp;
  518.   if (defined $row) {
  519.     $temp = $main::Request->Form($formname)->Item($row);
  520.   } else {
  521.     $temp = $main::Request->Form($formname)->Item;
  522.   }
  523.  
  524.   $temp =~ s/^\s+//s;
  525.   $temp =~ s/\s+$//s;
  526.  
  527.   $record->{edit}->{$name} = ($temp =~ /\S/ ? $temp : undef);
  528. }
  529.  
  530.  
  531.  
  532. =back
  533.  
  534. =head3 HTML Formatters
  535.  
  536. These methods are used to format a given value as HTML.
  537.  
  538. =over 4
  539.  
  540. =item as_html
  541.  
  542. The C<as_html> method is the accepted external interface for displaying a field in HTML.  It takes
  543. three parameters, C<$record>, C<$data>, and C<$viewtype>, and returns the appropriate HTML.
  544.  
  545. The default implementation, C<_as_html>, first checks for whether the C<$record> is viewable.  If
  546. it is not, it simply returns.  It then checks to see if C<$viewtype> is 'C<edit>'.  If it is, it
  547. calls C<$self-E<gt>can_edit($record)> to determine if the field is editable.  If it is, it calls
  548. C<as_html_edit_rw> or C<as_html_options> based on C<is_option_list>.  If it isn't editable but
  549. C<$viewtype> is 'C<edit>', it calls C<as_html_edit_ro>.  Finally, if we aren't in 'C<edit>' mode,
  550. it calls C<as_html_view>.
  551.  
  552. =cut
  553.  
  554. sub _as_html {
  555.   my $self = shift;
  556.   my($record, $data, $viewtype) = @_;
  557.  
  558.   $self->can_view($record) or return;
  559.  
  560.   if ($viewtype eq 'edit') {
  561.     if ($self->can_edit($record)) {
  562.       if ($self->is_option_list($record, $data)) {
  563.         return $self->as_html_options($record, $data);
  564.       } else {
  565.         return $self->as_html_edit_rw($record, $data);
  566.       }
  567.     } else {
  568.       return $self->as_html_edit_ro($record, $data);
  569.     }
  570.   } else {
  571.     return $self->as_html_view($record, $data);
  572.   }
  573. }
  574.  
  575.  
  576.  
  577. =item as_html_view
  578.  
  579. The C<as_html_view> method takes two parameters, C<$record> and C<$data>, and returns the
  580. appropriate HTML.
  581.  
  582. The default implementation, C<_as_html_view>, first extracts C<$value> from C<$record> using
  583. C<$data> and C<$self-E<gt>name>.  If it is defined, it returns it, otherwise it returns
  584. 'C< >'.  It runs the string through HTMLEncode to enable it to pass HTML meta-characters.
  585.  
  586. This is over-ridden in C<Win32::ASP::Field::bit> to return 'C<Yes>' or 'C<No>' and in
  587. C<Win32::ASP::Field::timestamp> to return nothing (C<timestamp> is not the same as C<datetime>).
  588.  
  589. =cut
  590.  
  591. sub _as_html_view {
  592.   my $self = shift;
  593.   my($record, $data) = @_;
  594.  
  595.   my $value = $record->{$data}->{$self->name};
  596.   return defined $value ? $main::Server->HTMLEncode($value) : ' ';
  597. }
  598.  
  599.  
  600.  
  601. =item as_html_edit_ro
  602.  
  603. The C<as_html_edit_ro> method takes two parameters, C<$record> and C<$data>, and returns the
  604. appropriate HTML.
  605.  
  606. The default implementation, C<_as_html_edit_ro>, first extracts C<$value> from C<$record> using
  607. C<$data> and C<$self-E<gt>name>.  It concatenates a C<HIDDEN> C<INPUT> field with the results of
  608. C<$self-E<gt>as_html_view($record, $data)>.
  609.  
  610. This method is over-riden in C<Win32::ASP::Field::timestamp> to encode C<$value> as hex (since
  611. C<timestamp> values are binary and thus not healthy HTML).
  612.  
  613. =cut
  614.  
  615. sub _as_html_edit_ro {
  616.   my $self = shift;
  617.   my($record, $data) = @_;
  618.  
  619.   my $formname = $self->formname;
  620.   my $value = $record->{$data}->{$self->name};
  621.  
  622.   chomp(my $retval = <<ENDHTML);
  623. <INPUT TYPE="HIDDEN" NAME="$formname" VALUE="$value">
  624. ENDHTML
  625.   $retval .= $self->as_html_view($record, $data);
  626.   return $retval;
  627. }
  628.  
  629.  
  630.  
  631. =item as_html_edit_rw
  632.  
  633. The C<as_html_edit_rw> method takes two parameters, C<$record> and C<$data>, and returns the
  634. appropriate HTML.
  635.  
  636. The default implementation, C<_as_html_edit_rw>, first extracts C<$value> from C<$record> using
  637. C<$data> and C<$self-E<gt>name>.  It then creates an appropriate C<TEXT> C<INPUT> field. Note the
  638. call to C<$self->as_html_mouseover>, which returns the appropriate parameters to implement the
  639. C<help> support.
  640.  
  641. The method is over-ridden by C<Win32::ASP::Field::bit> to display a Yes/No radio pair and by
  642. C<Win32::ASP::Field::text> to display a C<TEXTAREA>.
  643.  
  644. =cut
  645.  
  646. sub _as_html_edit_rw {
  647.   my $self = shift;
  648.   my($record, $data) = @_;
  649.  
  650.   my $formname = $self->formname;
  651.   my $value = $record->{$data}->{$self->name};
  652.   my $help = $self->as_html_mouseover($record, $data);
  653.  
  654.   my $size = $self->size;
  655.   my $maxl = $self->maxl;
  656.   chomp(my $retval = <<ENDHTML);
  657. <INPUT TYPE="TEXT" NAME="$formname" SIZE="$size" MAXLENGTH="$maxl" VALUE="$value" $help>
  658. ENDHTML
  659.   return $retval;
  660. }
  661.  
  662.  
  663.  
  664. =item as_html_options
  665.  
  666. The C<as_html_options> method takes two parameters, C<$record> and C<$data>, and returns the
  667. appropriate HTML.
  668.  
  669. The default implementation, C<_as_html_options>, first extracts C<$value> from C<$record> using
  670. C<$data> and C<$self-E<gt>name>.  It then loops over the values returned from
  671. C<$self-E<gt>option_list> and creates a C<SELECT> structure with the appropriate C<OPTION>
  672. entries.  It specified C<SELECTED> for the appropriate one based on C<$value>.
  673.  
  674. =cut
  675.  
  676. sub _as_html_options {
  677.   my $self = shift;
  678.   my($record, $data) = @_;
  679.  
  680.   my $formname = $self->formname;
  681.   my $value = $record->{$data}->{$self->name};
  682.   my $help = $self->as_html_mouseover($record, $data);
  683.  
  684.   my $retval = "<SELECT NAME=\"$formname\" $help>\n";
  685.   foreach my $option (@{$self->option_list($record, $data)}) {
  686.     my $selected = ($option eq $value ? 'SELECTED' : '');
  687.     $retval .= "<OPTION $selected>$option\n";
  688.   }
  689.   $retval .= "</SELECT>";
  690.   return $retval;
  691. }
  692.  
  693.  
  694.  
  695. =item as_html_mouseover
  696.  
  697. The C<as_html_mouseover> method takes two parameters, C<$record> and C<$data>, and returns the
  698. appropriate string with C<onMouseOver> and C<onMouseOut> method for inclusion into HTML.
  699.  
  700. The default implementation, C<_as_html_mouseover>, ignores the passed parameters and builds
  701. JavaScript for setting C<window.status> to C<$self-E<gt>help>.
  702.  
  703. =cut
  704.  
  705. sub _as_html_mouseover {
  706.   my $self = shift;
  707.  
  708.   my $help = $self->help;
  709.   $help and $help = "onMouseOver=\"window.status='$help';return true\" onMouseOut=\"window.status='';return true\"";
  710.   return $help;
  711. }
  712.  
  713.  
  714.  
  715.  
  716. =back
  717.  
  718. =head3 SQL Formatters
  719.  
  720. Values need to be formatted as legal SQL for the purposes of being included in query strings.
  721.  
  722. =over 4
  723.  
  724. =item check_value
  725.  
  726. The C<check_value> method is responsible for field level checking of C<$value>.  Note that this
  727. code does not have access to the entire record, and so record-based checking should be left to the
  728. C<check_value_write> method discussed later.  If the check fails, check_value should throw an
  729. error. Ideally, the error will either be of class C<Win32::ASP::Error::Field::bad_value> or a
  730. subclass thereof. There should be no checking for "requiredness" at this level (simply because in
  731. many situations it wouldn't be called and so putting it here lends false hope).  The default
  732. implementation in C<Win32::ASP::Field> does no checking what-so-ever and is merely provided as a
  733. prototype.
  734.  
  735. The method is over-ridden by C<Win32::ASP::Field::bit> to verify that the value is a 0 or 1 (bit
  736. fields never allow NULLs), by C<Win32::ASP::Field::datetime> to use
  737. C<Win32::ASP::Field::_clean_datetime> which use OLE to verify a datetime value, by
  738. C<Win32::ASP::Field::int> to verify that the value is an integer, and by
  739. C<Win32::ASP::Field::varchar> to verify that it doesn't exceed the maximum length.
  740.  
  741. =cut
  742.  
  743. sub _check_value {
  744.   my $self = shift;
  745.   my($value) = @_;
  746. }
  747.  
  748. =item as_sql
  749.  
  750. The C<as_sql> method is responsible for formatting of C<$value> for inclusion in SQL.  Since this
  751. code will be called during the query phase, it doesn't have access to an entire record.  The
  752. default implementation in C<Win32::ASP::Field> does nothing at all and is merely provided as a
  753. prototype.
  754.  
  755. The method is, therefore, implemented by almost every subclass of C<Win32::ASP::Field>, with the
  756. exception of C<Win32::ASP::Field::dispmeta> and C<Win32::ASP::Field::timestamp>, which are never
  757. used to query or write to the database.
  758.  
  759. =cut
  760.  
  761. sub _as_sql {
  762.   my $self = shift;
  763.   my($value) = @_;
  764. }
  765.  
  766.  
  767.  
  768.  
  769. =back
  770.  
  771. =head3 Writing Formatters
  772.  
  773. The writing formatters are responsible for preparing the output for updating or inserting records.
  774. Some of these have access to the full C<$record> object, and others only have access to the
  775. C<$value>.  In order to decentralize management of the constraint checking, it would be useful if
  776. some C<$record> object checking could be pushed out to the field objects.  At the same time, there
  777. are situations where a fully formed C<$record> object is not available for field level checking.
  778. As a result, there is a profusion of the various formatters and checkers.  Rather than discussing
  779. them in a top-down fashion, I will start from the bottom as things may make more sense that way.
  780.  
  781. =over 4
  782.  
  783. =item as_write
  784.  
  785. The C<as_write> method gets passed C<$value> and returns the value that will be paired with
  786. C<writename> for writing to the database.  Note that it does B<not> get passed the full record -
  787. otherwise it would be difficult to call as_write from an overridden as_write.
  788.  
  789. For example, to implement C<as_write> for looking up a value in a database (obviously just for
  790. demonstration purposes - normally you would use C<_standard_option_list>), one might use:
  791.  
  792.   as_write => sub {
  793.         my $self = shift;
  794.     my($value) = @_;
  795.  
  796.         my $results = MyDatabase::MyRecord->_DB->exec_sql(<<ENDSQL, error_no_records => 1);
  797.     SELECT LookupCode FROM LookupCodes WHERE Description = '$value'
  798.     ENDSQL
  799.     return MyDatabase::MyRecord->_FIELDS->{$self->writename($value)}->as_write($results->Fields->('LookupCode')->Value);
  800.     },
  801.  
  802. That last return line is rather ugly, so let me dissect it:
  803.  
  804. =over 4
  805.  
  806. =item *
  807.  
  808. C<$self-E<gt>writename> returns the fieldname to which the return value will actually get written.
  809.  
  810. =item *
  811.  
  812. C<MyDatabase::MyRecord-E<gt>_FIELDS> returns the hash of field objects for whatever class is
  813. involved.
  814.  
  815. =item *
  816.  
  817. C<MyDatabase::MyRecord-E<gt>_FIELDS-E<gt>{$self-E<gt>writename}> returns the actual field object
  818. of interest.
  819.  
  820. =item *
  821.  
  822. C<as_write> is then called on that object with the value returned by looking up the
  823. appropriate result in the database.
  824.  
  825. =back
  826.  
  827. The main reason for the last line is so that it will properly format the return value using
  828. whatever type of field the C<writename> is.  This shouldn't be an issue for common fields, but
  829. it could be for date/time values in some circumstances.
  830.  
  831. =cut
  832.  
  833. sub _as_write {
  834.   my $self = shift;
  835.   my($value) = @_;
  836.  
  837.   return $value;
  838. }
  839.  
  840. =item check_value_write
  841.  
  842. This is the first of the methods that have access to a full C<$record>.  It gets passed both
  843. C<$record> and C<$data> and as such can check a given field against other fields in the record.
  844. The default implementation calls C<check_value> on the appropriate C<$value>.  If the check fails
  845. for whatever reason, C<check_value_write> should throw an exception.
  846.  
  847. =cut
  848.  
  849. sub _check_value_write {
  850.   my $self = shift;
  851.   my($record, $data) = @_;
  852.  
  853.   $self->check_value($record->{$data}->{$self->name});
  854. }
  855.  
  856. =item as_write_pair
  857.  
  858. The method C<as_write_pair> is the accepted entry point for formatting a value for writing to
  859. the database.  It accepts C<$record> and C<$data>, so it can call C<check_value_write> to perform
  860. record-dependent field validation.  It returns a hash composed of two key/value pairs: C<field>
  861. should supply the fieldname to write to and C<value> should supply the properly formatted data for
  862. inclusion into SQL.  Note that if, for some reason, the functionality usually supplied by
  863. C<writename> requires knowledge of the entire record, that functionality should be subsumed into
  864. C<as_write_pair>.
  865.  
  866. =cut
  867.  
  868. sub _as_write_pair {
  869.   my $self = shift;
  870.   my($record, $data) = @_;
  871.  
  872.   $self->check_value_write($record, $data);
  873.   my $value = $record->{$data}->{$self->name};
  874.   return {field => $self->writename($value), value => $self->as_write($value)};
  875. }
  876.  
  877. =back
  878.  
  879. =cut
  880.  
  881.  
  882. #Here be META property implementations
  883.  
  884. sub _standard_option_list {
  885.   my $self = shift;
  886.   my(%params) = @_;
  887.  
  888.   $self->{option_list} = sub {
  889.     my $results = $params{class}->_DB->exec_sql("SELECT $params{desc} FROM $params{table} ORDER BY $params{field}", error_no_records => 1);
  890.     my $retval = [''];
  891.     while (!$results->EOF) {
  892.       push(@{$retval}, $results->Fields->Item($params{desc})->Value);
  893.       $results->MoveNext;
  894.     }
  895.     $self->{option_list} = $retval;
  896.     return($retval);
  897.   };
  898.  
  899.   $self->{writename} = $params{writename};
  900.  
  901.   if (exists $params{group}) {
  902.     my %memo;
  903.     $self->{as_write} = sub {
  904.       my $self = shift;
  905.       my($value) = @_;
  906.  
  907.       unless (scalar(keys %memo)) {
  908.         my $results = $params{class}->_DB->exec_sql("SELECT $params{desc}, $params{field} FROM $params{table}", error_no_records => 1);
  909.         while (!$results->EOF) {
  910.           $memo{$results->Fields->Item($params{desc})->Value} = $results->Fields->Item($params{field})->Value;
  911.           $results->MoveNext;
  912.         }
  913.       }
  914.  
  915.       exists $memo{$value} or throw Win32::ASP::Error::SQL::no_records (SQL => "SELECT $params{desc}, $params{field} FROM $params{table}");
  916.       return $params{class}->_FIELDS->{$self->writename($value)}->as_write($memo{$value});
  917.     }
  918.   } else {
  919.     $self->{as_write} = sub {
  920.       my $self = shift;
  921.       my($value) = @_;
  922.  
  923.       my $results = $params{class}->_DB->exec_sql("SELECT $params{field} FROM $params{table} WHERE $params{desc} = '$value'", error_no_records => 1);
  924.       return $params{class}->_FIELDS->{$self->writename($value)}->as_write($results->Fields->Item($params{field})->Value);
  925.     }
  926.   }
  927. }
  928.  
  929.  
  930.  
  931.  
  932. #Here be the various classes of Win32::ASP::Error objects that can be thrown
  933.  
  934. package Win32::ASP::Error::Field;
  935. @Win32::ASP::Error::Field::ISA = qw/Win32::ASP::Error/;
  936.  
  937.  
  938. package Win32::ASP::Error::Field::bad_value;
  939. @Win32::ASP::Error::Field::bad_value::ISA = qw/Win32::ASP::Error::Field/;
  940.  
  941. #Parameters:  field, bad_value, error
  942.  
  943. sub _as_html {
  944.   my $self = shift;
  945.  
  946.   my $bad_value = $self->bad_value;
  947.   my $name = $self->field->desc;
  948.   my $error = $self->error;
  949.   return <<ENDHTML;
  950. There was an error with the value "$bad_value" supplied for field "$name".<P>
  951. $error<P>
  952. Click the back button on your browser to return to editing the record.<P>
  953. ENDHTML
  954. }
  955.  
  956.  
  957. package Win32::ASP::Error::Field::required;
  958. @Win32::ASP::Error::Field::required::ISA = qw/Win32::ASP::Error::Field/;
  959.  
  960. #Parameters:  field
  961.  
  962. sub _as_html {
  963.   my $self = shift;
  964.  
  965.   my $name = $self->field->desc;
  966.   return <<ENDHTML;
  967. The field "$name" is required.<P>
  968. Click the back button on your browser to return to editing the record.<P>
  969. ENDHTML
  970. }
  971.  
  972.  
  973.  
  974. package Win32::ASP::Error::Field::group_wrapper;
  975. @Win32::ASP::Error::Field::group_wrapper::ISA = qw/Win32::ASP::Error::Field/;
  976.  
  977. #Parameters:  E, row_type, row_id, activity
  978.  
  979. sub _as_html {
  980.   my $self = shift;
  981.  
  982.   my $activity = $self->activity;
  983.   my $row_type = $self->row_type;
  984.   my $row_id = $self->row_id;
  985.   my $enwrapped = $self->E->as_html;
  986.   return <<ENDHTML;
  987. There was an error encountered while attempting to $activity $row_type $row_id.<P>
  988. $enwrapped
  989. ENDHTML
  990. }
  991.  
  992.  
  993.  
  994. package Win32::ASP::Error::Field::non_existent;
  995. @Win32::ASP::Error::Field::non_existent::ISA = qw/Win32::ASP::Error::Field/;
  996.  
  997. #Parameters:  fieldname, method
  998.  
  999. sub _as_html {
  1000.   my $self = shift;
  1001.  
  1002.   my $fieldname = $self->fieldname;
  1003.   my $method = $self->method;
  1004.   return <<ENDHTML;
  1005. The field $fieldname is non existent.<P>
  1006. In method $method.<P>
  1007. ENDHTML
  1008. }
  1009.  
  1010. 1;
  1011.