| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Class::Colon; | 
| 2 | 4 |  |  | 4 |  | 228596 | use strict; use warnings; | 
|  | 4 |  |  | 4 |  | 30 |  | 
|  | 4 |  |  |  |  | 97 |  | 
|  | 4 |  |  |  |  | 16 |  | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 270 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | our $VERSION = "0.04"; | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | =head1 NAME | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | Class::Colon - Makes objects out of colon delimited records and vice versa | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | =head1 VERSION | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | This document covers version 0.03 of C. | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | use Date; | 
| 17 |  |  |  |  |  |  | use Class::Colon | 
| 18 |  |  |  |  |  |  | Person  => [ qw ( first middle family date_of_birth=Date=new ) ], | 
| 19 |  |  |  |  |  |  | Address => [ qw ( street city province code country          ) ]; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | Person->DELIM(','); # change from colon to comma for delimeter | 
| 22 |  |  |  |  |  |  | my $names = Person->READ_FILE($file_name); | 
| 23 |  |  |  |  |  |  | foreach my $name (@$names) { | 
| 24 |  |  |  |  |  |  | print $name->family, ",", $name->first, $name->middle, "\n"; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | open ADDRESS_FILE, "addresses.dat" or die "...\n"; | 
| 28 |  |  |  |  |  |  | my $addresses = Address->READ_HANDLE(*ADDRESS_FILE); | 
| 29 |  |  |  |  |  |  | foreach my $address (@$addresses) { | 
| 30 |  |  |  |  |  |  | print $address->street . "\n" | 
| 31 |  |  |  |  |  |  | print $address->city . ", " . $address->province . "\n"; | 
| 32 |  |  |  |  |  |  | print $address->country, "\n" if $address->country; | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  | close ADDRESS_FILE; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | my $sample_address = Address->OBJECTIFY( | 
| 37 |  |  |  |  |  |  | "1313 Mocking Bird Ln:Adamstown:PA:12345:USA" | 
| 38 |  |  |  |  |  |  | );  # convert one string to an object | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | my $first_address = $addresses->[0]->STRINGIFY(); | 
| 41 |  |  |  |  |  |  | # puts it back in delimited form | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | Address->WRITE_FILE("output.dat", $addresses); | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | open ADDRESS_FILE, ">newaddr.dat" or die "...\n"; | 
| 46 |  |  |  |  |  |  | Address->WRITE_HANDLE(*ADDRESS_FILE, $addresses); | 
| 47 |  |  |  |  |  |  | close ADDRESS_FILE; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | To turn your colon delimited file into a list of objects, use C, | 
| 52 |  |  |  |  |  |  | giving it the name you want to use for the class and an anonymous array of | 
| 53 |  |  |  |  |  |  | column names which will become attributes of the objects in the class.  List | 
| 54 |  |  |  |  |  |  | the names in the order they appear in the input.  Missing fields will be set | 
| 55 |  |  |  |  |  |  | to "".  Extra fields will be ignored.  Use lower case names for the fields. | 
| 56 |  |  |  |  |  |  | Upper case names are reserved for use as methods of the class. | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | Most fields will be simple scalars, but if one of the fields should be an | 
| 59 |  |  |  |  |  |  | object, its entry should be of the form | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | attribute_name=package_name=constructor_name | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | as shown above for C which is of type C whose constructor | 
| 64 |  |  |  |  |  |  | is C.  In that example, I could have omitted the constructor name, since | 
| 65 |  |  |  |  |  |  | C is the default. | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | You may objectify as many different record types as you like in one use | 
| 68 |  |  |  |  |  |  | statement.  You may have multiple use statements throughout your program | 
| 69 |  |  |  |  |  |  | or module.  If you are using this package from another package, you should | 
| 70 |  |  |  |  |  |  | worry a little about namespace collision.  There is only one list of classes | 
| 71 |  |  |  |  |  |  | made by this package.  The names must be unique or Bad Things will happen. | 
| 72 |  |  |  |  |  |  | Feel free to include your module name in the names of the fabricated classes | 
| 73 |  |  |  |  |  |  | as in: | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | package YourModule; | 
| 76 |  |  |  |  |  |  | use Class::Colon YourModule::Person => [ qw( field names here ) ]; | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | You wouldn't have to use the double colon, but it makes sense to me. | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | If your delimiter is not colon, call DELIM on I class I calling | 
| 81 |  |  |  |  |  |  | C.  Pass it as a string.  It can be any length, but is taken | 
| 82 |  |  |  |  |  |  | literally. | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | Feel free to add code to the generated package(s) before or after using | 
| 85 |  |  |  |  |  |  | Class::Colon.  But, keep in mind possible name conflicts.  As pointed out | 
| 86 |  |  |  |  |  |  | below (under METHODS), all ALL_CAPS names are reserved. | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | =head1 ABSTRACT | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | This module turns colon separated data files into lists of objects. | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =head2 EXPORT | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | None, this is object oriented. | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | =head1 METHODS | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | There are currently only a few methods.  There are two class methods | 
| 99 |  |  |  |  |  |  | for reading, READ_FILE and READ_HANDLE, (these work for every class you | 
| 100 |  |  |  |  |  |  | requested in your use Class::Colon statement).  There are corresponding | 
| 101 |  |  |  |  |  |  | class methods for writing, WRITE_FILE and WRITE_HANDLE.  If you want to handle | 
| 102 |  |  |  |  |  |  | the I/O manually (or maybe you don't need I/O), there are two methods to help, | 
| 103 |  |  |  |  |  |  | OBJECTIFY (takes a string returns an object) and STRINGIFY (the opposite). | 
| 104 |  |  |  |  |  |  | There is also a set of dual use accessors, one for each field in each class. | 
| 105 |  |  |  |  |  |  | You name these yourself in the use statement.  Finally, there is a DELIM | 
| 106 |  |  |  |  |  |  | method which allows you to set the delimiter.  This can be any literal string, | 
| 107 |  |  |  |  |  |  | it applies to all fields in the file.  There is a separate delimiter for | 
| 108 |  |  |  |  |  |  | each class.  It defaults to colon. | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | You should consider every ALL_CAPS name reserved.  I reserve the right to | 
| 111 |  |  |  |  |  |  | add methods in the future, their names will be ALL_CAPS, as the current | 
| 112 |  |  |  |  |  |  | method names are.  Therefore, don't use ALL_CAPS for field names. | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | In addition to retrieving the attributes through accessor methods, you | 
| 115 |  |  |  |  |  |  | could peek directly at the data.  It is stored in a hash so the following | 
| 116 |  |  |  |  |  |  | are equivalent: | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | my $country = $address->country(); | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | and | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | my $country = $address->{country}; | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | Using this fact might make some things neater in your code (like print | 
| 125 |  |  |  |  |  |  | statements).  It also saves a tiny amount of time.  Our OO teachers | 
| 126 |  |  |  |  |  |  | will smack our hands, if they hear about this little arrangement, so keep | 
| 127 |  |  |  |  |  |  | quite about it :-).  I have no plans to change the implementation, but | 
| 128 |  |  |  |  |  |  | they tell me never to make such promises. | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | =cut | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 4 |  |  | 4 |  | 22 | use Carp; | 
|  | 4 |  |  |  |  | 17 |  | 
|  | 4 |  |  |  |  | 344 |  | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | our %simulated_classes; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | sub import { | 
| 137 | 5 |  |  | 5 |  | 394 | my $class = shift; | 
| 138 | 5 |  |  |  |  | 15 | my %fakes = @_; | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 5 |  |  |  |  | 19 | foreach my $fake (keys %fakes) { | 
| 141 | 4 |  |  | 4 |  | 22 | no strict; | 
|  | 4 |  |  |  |  | 6 |  | 
|  | 4 |  |  |  |  | 3446 |  | 
| 142 | 4 |  |  | 12 |  | 17 | *{"$fake\::NEW"}     = sub { return bless {}, shift; }; | 
|  | 4 |  |  |  |  | 25 |  | 
|  | 12 |  |  |  |  | 98 |  | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 4 |  |  |  |  | 12 | my @proxy_method_names = qw( | 
| 145 |  |  |  |  |  |  | read_file  read_handle  objectify delim | 
| 146 |  |  |  |  |  |  | write_file write_handle stringify | 
| 147 |  |  |  |  |  |  | ); | 
| 148 | 4 |  |  |  |  | 7 | foreach my $proxy_method (@proxy_method_names) { | 
| 149 | 28 |  |  |  |  | 48 | my $proxy_name   = "$fake"  . "::" . uc $proxy_method; | 
| 150 | 28 |  |  |  |  | 38 | my $real_name    = "$class" . "::" .    $proxy_method; | 
| 151 | 28 |  |  |  |  | 29 | *{"$proxy_name"} = \&{"$real_name"}; | 
|  | 28 |  |  |  |  | 93 |  | 
|  | 28 |  |  |  |  | 48 |  | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 4 |  |  |  |  | 12 | my @attributes; | 
| 155 | 4 |  |  |  |  | 4 | foreach my $col (@{$fakes{$fake}}) { | 
|  | 4 |  |  |  |  | 9 |  | 
| 156 | 15 |  |  |  |  | 34 | my ($name, $type, $constructor)  = split /=/, $col; | 
| 157 | 15 |  |  |  |  | 28 | *{"$fake\::$name"} = _make_accessor($name, $type, $constructor); | 
|  | 15 |  |  |  |  | 44 |  | 
| 158 | 15 |  |  |  |  | 30 | push @attributes, $name; | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 4 |  |  |  |  | 5150 | $simulated_classes{$fake} = {ATTRS => \@attributes, DELIM => ':'}; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | sub _make_accessor { | 
| 165 | 15 |  |  | 15 |  | 18 | my $attribute   = shift; | 
| 166 | 15 |  |  |  |  | 17 | my $type        = shift; | 
| 167 | 15 |  | 100 |  |  | 45 | my $constructor = shift || "new"; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 15 | 100 |  |  |  | 26 | if (defined $type) { # we need to call a constructor | 
| 170 |  |  |  |  |  |  | return sub { | 
| 171 | 2 |  |  | 2 |  | 3 | my $self            = shift; | 
| 172 | 2 |  |  |  |  | 3 | my $new_val         = shift; | 
| 173 | 2 | 50 |  |  |  | 34 | if (defined $new_val) { | 
| 174 | 2 |  |  |  |  | 9 | $self->{$attribute} = $type->$constructor($new_val) | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 2 |  |  |  |  | 17 | return $self->{$attribute}; | 
| 177 | 1 |  |  |  |  | 3 | }; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | else { # we can just dump the scalar into the attribute | 
| 180 |  |  |  |  |  |  | return sub { | 
| 181 | 44 |  |  | 44 |  | 528 | my $self            = shift; | 
| 182 | 44 |  |  |  |  | 53 | my $new_val         = shift; | 
| 183 | 44 | 100 |  |  |  | 102 | $self->{$attribute} = $new_val if defined $new_val; | 
| 184 | 44 |  |  |  |  | 70 | return $self->{$attribute}; | 
| 185 | 14 |  |  |  |  | 34 | }; | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head2 DELIM | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Call this through one of the names you supplied in your use statement.  Pass | 
| 192 |  |  |  |  |  |  | it a string.  For example, you could say | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | Person->DELIM(';'); | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | this would change the delimiter from colon to semi-colon for Person.  No | 
| 197 |  |  |  |  |  |  | other classes would be affected. | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | =cut | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | sub delim { | 
| 202 | 1 |  |  | 1 | 0 | 771 | my $fake_class = shift; | 
| 203 | 1 |  |  |  |  | 1 | my $string     = shift; | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 1 | 50 |  |  |  | 4 | if (defined $string) { | 
| 206 | 1 |  |  |  |  | 2 | $simulated_classes{$fake_class}{DELIM} = $string; | 
| 207 |  |  |  |  |  |  | } | 
| 208 | 1 |  |  |  |  | 3 | return $simulated_classes{$fake_class}{DELIM}; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =head2 READ_FILE and READ_HANDLE | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | Call these mehtods through one of the names you supplied in your use | 
| 214 |  |  |  |  |  |  | statement. | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | Both READ_FILE and READ_HANDLE return an array reference with one element | 
| 217 |  |  |  |  |  |  | for each line in your input file.  All lines are represented even if they | 
| 218 |  |  |  |  |  |  | are blank or start with #.  The array elements are objects of the same type | 
| 219 |  |  |  |  |  |  | as the name you used to call the method.  Think of these as super constructors, | 
| 220 |  |  |  |  |  |  | instead of making one object at a time, they make as many as they can from | 
| 221 |  |  |  |  |  |  | your input. | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | READ_FILE takes the name of a file, which it opens, reads, and closes. | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | READ_HANDLE takes an open handle ready for reading.  You must ensure that the | 
| 226 |  |  |  |  |  |  | handle is properly opened and closed. | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =cut | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | sub read_file { | 
| 231 | 3 |  |  | 3 | 0 | 772 | my $class    = shift; | 
| 232 | 3 |  |  |  |  | 7 | my $file     = shift; | 
| 233 |  |  |  |  |  |  |  | 
| 234 | 3 | 50 |  |  |  | 88 | open FILE,   "$file" or croak "Couldn't read $file: $!"; | 
| 235 | 3 |  |  |  |  | 18 | my $retval   = $class->READ_HANDLE(*FILE); | 
| 236 | 3 |  |  |  |  | 22 | close FILE; | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 3 |  |  |  |  | 11 | return $retval; | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | sub read_handle { | 
| 242 | 4 |  |  | 4 | 0 | 885 | my $class  = shift; | 
| 243 | 4 |  |  |  |  | 11 | my $handle = shift; | 
| 244 |  |  |  |  |  |  |  | 
| 245 | 4 |  |  |  |  | 6 | my @rows; | 
| 246 | 4 |  |  |  |  | 61 | while (<$handle>) { | 
| 247 | 8 |  |  |  |  | 15 | chomp; | 
| 248 | 8 |  |  |  |  | 25 | push @rows, $class->OBJECTIFY($_); | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 4 |  |  |  |  | 16 | return \@rows; | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =head2 OBJECTIFY | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | If you want to control the read loop for your data, this method is here | 
| 256 |  |  |  |  |  |  | to help you.  Call it through a class name.  Pass it one line (chomp it | 
| 257 |  |  |  |  |  |  | yourself).  Receive one object. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | =cut | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | sub objectify { | 
| 262 | 11 |  |  | 11 | 0 | 952 | my $class    = shift; | 
| 263 | 11 |  |  |  |  | 18 | my $string   = shift; | 
| 264 | 11 |  |  |  |  | 15 | my $config   = $simulated_classes{$class}; | 
| 265 | 11 |  |  |  |  | 15 | my $col_list = $config->{ATTRS}; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 11 |  |  |  |  | 27 | my $new_object = $class->NEW(); | 
| 268 | 11 |  |  |  |  | 59 | my @cols       = split /$config->{DELIM}/, $string; | 
| 269 | 11 |  |  |  |  | 34 | foreach my $i (0 .. @cols - 1) { | 
| 270 | 42 |  |  |  |  | 56 | my $method = $col_list->[$i]; | 
| 271 | 42 |  |  |  |  | 75 | $new_object->$method($cols[$i]); | 
| 272 |  |  |  |  |  |  | } | 
| 273 | 11 |  |  |  |  | 60 | return $new_object; | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | =head2 WRITE_FILE and WRITE_HANDLE | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | Call these mehtods through one of the names you supplied in your use | 
| 279 |  |  |  |  |  |  | statement. | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | Both WRITE_FILE and WRITE_HANDLE return an array reference with one element | 
| 282 |  |  |  |  |  |  | for each line in your input file.  The lines are made by joining the fields | 
| 283 |  |  |  |  |  |  | in the order they appeared in the use statement using the current DELIM. | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | WRITE_FILE takes the name of a file, which it opens, writes, and closes. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | WRITE_HANDLE takes a handle open for writing.  You must ensure that the handle | 
| 288 |  |  |  |  |  |  | is properly opened and closed. | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | =cut | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | sub write_file { | 
| 293 | 1 |  |  | 1 | 0 | 692 | my $class    = shift; | 
| 294 | 1 |  |  |  |  | 2 | my $file     = shift; | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 1 | 50 |  |  |  | 48 | open FILE,   ">$file" or croak "Couldn't write $file: $!"; | 
| 297 | 1 |  |  |  |  | 5 | my $retval   = $class->WRITE_HANDLE(*FILE, @_); | 
| 298 | 1 |  |  |  |  | 25 | close FILE; | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 1 |  |  |  |  | 4 | return $retval; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub write_handle { | 
| 304 | 2 |  |  | 2 | 0 | 109 | my $class  = shift; | 
| 305 | 2 |  |  |  |  | 6 | my $handle = shift; | 
| 306 | 2 |  |  |  |  | 3 | my $rows   = shift; | 
| 307 |  |  |  |  |  |  |  | 
| 308 | 2 |  |  |  |  | 5 | foreach my $row (@$rows) { | 
| 309 | 4 |  |  |  |  | 13 | print $handle $row->STRINGIFY() . "\n"; | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =head2 STRINGIFY | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | Call this through an object you got by using Class::Colon.  Receive | 
| 316 |  |  |  |  |  |  | a colon delimited string suitable for writing back to your file.  The | 
| 317 |  |  |  |  |  |  | string comes with no newline, unless the last field happens to have one. | 
| 318 |  |  |  |  |  |  | You may need to supply a newline, especially if you chomped. | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | =cut | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | sub stringify { | 
| 323 | 5 |  |  | 5 | 0 | 10 | my $self     = shift; | 
| 324 | 5 |  |  |  |  | 8 | my $type     = ref($self); | 
| 325 | 5 |  |  |  |  | 10 | my $config   = $simulated_classes{$type}; | 
| 326 | 5 |  |  |  |  | 6 | my $col_list = $config->{ATTRS}; | 
| 327 | 5 |  |  |  |  | 7 | my $retval; | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | my @fields; | 
| 330 | 5 |  |  |  |  | 7 | foreach my $att (@$col_list) { | 
| 331 | 15 |  |  |  |  | 25 | push @fields, $self->{$att}; | 
| 332 |  |  |  |  |  |  | } | 
| 333 | 5 |  |  |  |  | 45 | return join $config->{DELIM}, @fields; | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =head2 accessors | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | For each attribute you name in your use statement, there is a corresponding | 
| 339 |  |  |  |  |  |  | dual use accessor.  The names of the accessors are the same as the names | 
| 340 |  |  |  |  |  |  | you used (how convenient).  You can also fish directly in the hash based | 
| 341 |  |  |  |  |  |  | object using the name of attribute as the key, but don't tell your OO | 
| 342 |  |  |  |  |  |  | instructor. | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | =cut | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =head1 BUGS and OMISSIONS | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | There is no quoting.  If a colon (or the DELIM of your choice) is | 
| 349 |  |  |  |  |  |  | quoted, it still counts as a field separator. | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | Comments and blank lines are treated as regular records. | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =head1 AUTHOR | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | Phil Crow, Ephilcrow2000@yahoo.comE | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | Copyright 2003 by Phil Crow, all rights reserved. | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 362 |  |  |  |  |  |  | it under the same terms as Perl 5.8.1 itself. | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | =cut | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | 1; |