File Coverage

blib/lib/DBIx/ORM/Declarative/Schema.pm
Criterion Covered Total %
statement 246 404 60.8
branch 34 116 29.3
condition 12 67 17.9
subroutine 21 49 42.8
pod 2 3 66.6
total 315 639 49.3


line stmt bran cond sub pod time code
1             package DBIx::ORM::Declarative::Schema;
2              
3 1     1   11 use strict;
  1         2  
  1         32  
4 1     1   5 use Carp;
  1         1  
  1         75  
5              
6 1     1   5 use vars qw(@ISA);
  1         2  
  1         1081  
7             @ISA = qw(DBIx::ORM::Declarative);
8              
9             # Check a create action against constraints, return a data structure suitable
10             # for use in constructing the required INSERT statement
11             sub __check_constraints
12             {
13 0     0   0 my ($self, $tab_obj, %params) = @_;
14 0 0 0     0 carp "This method requires an object" and return unless ref $tab_obj;
15 0         0 my %rv = ();
16              
17             # Create a map of constraints by name
18 0         0 my %cons = map { @{$_}{qw(name constraint)} } $tab_obj->_columns;
  0         0  
  0         0  
19              
20             # Primary keys
21 0         0 my @pk = $tab_obj->_primary_key;
22              
23 0         0 my @keys = ();
24 0         0 my @vals = ();
25 0         0 my @binds = ();
26 0         0 my $npk = 0;
27 0 0       0 if(@pk)
28             {
29 0         0 for my $k (@pk)
30             {
31 0         0 my $v = delete $params{$k};
32 0 0       0 if(defined $v)
33             {
34 0 0       0 if(not $self->apply_method($cons{$k}, 0, $v))
35             {
36 0         0 carp "column $k constraint failed";
37 0         0 return;
38             }
39 0         0 push @binds, $v;
40 0         0 push @vals, '?';
41 0         0 push @keys, $k;
42             }
43             else
44             {
45 0         0 $npk = 1;
46 0         0 my $fnp = $tab_obj->_for_null_primary;
47 0 0       0 if($fnp)
48             {
49 0         0 push @vals, $fnp;
50 0         0 push @keys, $k;
51             }
52             }
53             }
54             }
55            
56             # Non-primary keys
57 0         0 for my $k (map { $_->{name} } $tab_obj->_columns)
  0         0  
58             {
59 0 0       0 next if grep { $_ eq $k } @pk;
  0         0  
60 0         0 my $v = $params{$k};
61 0 0       0 if(not $self->apply_method($cons{$k}, 0, $v))
62             {
63 0         0 carp "column $k constraint failed";
64 0         0 return;
65             }
66              
67             # We only need to save the key if it was presented in the parameters
68 0 0       0 if(exists $params{$k})
69             {
70 0         0 push @keys, $k;
71 0 0       0 if(defined $v)
72             {
73 0         0 push @binds, $v;
74 0         0 push @vals, '?';
75             }
76             else
77             {
78 0         0 push @vals, 'NULL';
79             }
80             }
81             }
82              
83             # Generate the columns list
84 0         0 my %n2s = $tab_obj->_column_map;
85 0         0 my $kstr = '' . join(',', map { $n2s{$_} } @keys);
  0         0  
86              
87             # The values string
88 0         0 my $vstr = '' . join(',', @vals);
89              
90             # We return a 1 first, because it's conceivable that everything else
91             # is empty
92 0         0 return (1, $kstr, $vstr, $npk, @binds);
93             }
94              
95             # Make a SQL-safe alias from a table's name or alias
96             sub __make_sql_safe
97             {
98 2     2   3 my ($self, $str) = @_;
99 2         9 $str =~ s/\W/_/g;
100 2 50       9 if($str =~ /^[^a-zA-Z_]/)
101             {
102 0         0 $str = "_$str";
103             }
104 2         7 $str;
105             }
106              
107             # Create an alias for a table
108             sub alias
109             {
110 0     0 0 0 my ($self, @args) = @_;
111 0 0       0 if(@args<2)
112             {
113 0 0       0 if(@args==1)
114             {
115 0         0 return $self->table(@args);
116             }
117 0         0 my $alias;
118 0         0 eval { $alias = $self->_alias; };
  0         0  
119 0         0 return $alias;
120             }
121              
122             # Create/install a new alias
123 0         0 my ($alias, $table) = @args;
124 0   0     0 my $schema_class = ref $self || $self;
125 0         0 my $alias_class = $schema_class . "::$alias";
126 0         0 my $table_class = $schema_class . "::$table";
127              
128             # Set it up
129 1     1   6 no strict 'refs';
  1         2  
  1         823  
130 0 0       0 if(not @{$alias_class . '::ISA'})
  0         0  
131             {
132 0         0 @{$alias_class . '::ISA'} = ($table_class);
  0         0  
133 0     0   0 *{$alias_class . '::_class'} = sub { $alias_class; };
  0         0  
  0         0  
134 0     0   0 *{$alias_class . '::_table'} = sub { $alias; };
  0         0  
  0         0  
135 0     0   0 *{$alias_class . '::_alias'} = sub { $table; };
  0         0  
  0         0  
136 0         0 my $cons = *{$alias_class} = sub
137             {
138 0     0   0 my ($self) = @_;
139 0         0 my $rv = $self->new;
140 0 0       0 bless $rv, $alias_class unless $rv->isa($alias_class);
141 0         0 return $rv;
142 0         0 } ;
143              
144             # Make sure row objects promote to alias objects, NOT table objects
145 0         0 my $row_class = $alias_class . '::Rows';
146 0         0 @{$row_class . '::ISA'} = ($self->ROW_CLASS, $alias_class);
  0         0  
147 0     0   0 *{$alias_class . '::_row_class'} = sub { $row_class; };
  0         0  
  0         0  
148              
149             # Install into the table methods hash
150 0         0 $self->table_method($alias, $cons);
151             }
152             }
153              
154             # Get the current table name, or switch to a new table, or create a new
155             # table class
156             sub table
157             {
158 5     5 1 31 my ($self, @args) = @_;
159 5 100       16 if(@args<2)
160             {
161 2 50       7 if(@args==1)
162             {
163 2         4 my $table = shift @args;
164 2 50       8 return $self unless $table;
165 2         7 my $meth = $self->table_method($table);
166 2 50       7 return unless $meth;
167 2         5 return $meth->($self);
168             }
169 0         0 my $table;
170 0         0 eval { $table = $self->_table; };
  0         0  
171 0         0 return $table;
172             }
173              
174             # Get the table's name
175 3         13 my %args = @args;
176 3         9 my $table = delete $args{table};
177 3 50 0     9 carp "missing table argument" and return unless $table;
178 3   33     15 my $name = delete $args{alias} || $table;
179              
180             # Column definitions
181 3         7 my $primary = delete $args{primary};
182 3         5 my $unique = delete $args{unique};
183 3         7 my $columns = delete $args{columns};
184 3 50 0     17 carp "missing column definitions" and return unless
      66        
      66        
185             $primary or $unique or $columns;
186              
187             # Other miscellany
188 3         5 my $onpnull = delete $args{for_null_primary};
189 3         8 my $selonpnull = delete $args{select_null_primary};
190 3         4 my $join = delete $args{join_clause};
191 3         4 my $group_by = delete $args{group_by};
192              
193             # class and schema names
194 3         22 my $super = $self->_schema_class;
195 3         8 my $table_class = $super . "::$name";
196 3         7 my $row_class = $table_class . "::Rows";
197 3         90 my $schema = $self->_schema;
198            
199             # Set up the class heirarchy
200 1     1   7 no strict 'refs';
  1         3  
  1         4200  
201 3         24 @{$table_class . '::ISA'} = ($super, $self->TABLE_CLASS);
  3         106  
202 3         20 @{$row_class . '::ISA'} = ($self->ROW_CLASS, $table_class);
  3         251  
203              
204             # Information methods
205 3     2   15 *{$table_class . '::_class'} = sub { $table_class; };
  3         70  
  2         34  
206 3     0   2213 *{$table_class . '::_row_class'} = sub { $row_class; };
  3         40  
  0         0  
207 3     2   33 *{$table_class . '::_table'} = sub { $name; };
  3         23  
  2         7  
208 3     2   11 *{$table_class . '::_sql_name'} = sub { $table; };
  3         31  
  2         9  
209 3     0   14 *{$table_class . '::_for_null_primary'} = sub { $onpnull; };
  3         24  
  0         0  
210 3     0   12 *{$table_class . '::_select_null_primary'} = sub { $selonpnull; };
  3         26  
  0         0  
211 3     0   11 *{$table_class . '::_join_clause'} = sub { $join; };
  3         23  
  0         0  
212              
213             # handle GROUP BY
214 3 50       10 if($group_by)
215             {
216 0         0 my @p = @$group_by;
217 0     0   0 *{$table_class . '::_group_by' } = sub { @p; };
  0         0  
  0         0  
218             }
219             else
220             {
221 3     2   10 *{$table_class . '::_group_by' } = sub { };
  3     2   33  
  2     2   4  
222             }
223              
224             # The table object constructor
225             my $cons = sub
226             {
227 7     7   25 my ($self) = @_;
228 7         41 my $rv = $self->new;
229 7 50       142 bless $rv, $table_class unless $rv->isa($table_class);
230 7         251 return $rv;
231 3         17 } ;
232              
233 3         5 *{$table_class} = $cons;
  3         53  
234              
235 3         30 $self->table_method($name, $cons);
236              
237             # Handle column information
238 3         4 my %seen_keys;
239             my @newcolumns;
240 0         0 my @p;
241              
242             # The primary keys
243 3 100       11 @p = @$primary if $primary;
244 3     2   164 *{$table_class . '::_primary_key'} = sub { @p; };
  3         28  
  2         15  
245              
246             # Just in case the primary keys aren't formally defined elsewhere...
247             $seen_keys{$_} =
248 3         24 { sql_name => $_, name => $_, constraint => 'isstring' } foreach @p;
249 3         8 my %pk = map {($_,1);} @p;
  2         9  
250 3         7 @newcolumns = @p;
251              
252             # Process unique keys
253 3         5 my @uniqs;
254 3 100       13 push @uniqs, [@p] if @p;
255             # This is not strictly needed, since the loop will autovivify
256             # $unique to contain an empty array ref if it's undefined at this
257             # point. The loop provides the lvalue context to make this work.
258 3   100     21 $unique ||= [ ];
259 3         9 for my $un (@$unique)
260             {
261             # Check to see if they've duplicated the primary key
262 1         75 my %kv = map {($_,1)} @$un;
  2         143  
263 1         5 delete @kv{@p};
264 1 50 33     11 next if not %kv and scalar(@p) == scalar(@$un);
265              
266             # Create a copy so they can't change things out from under us
267 1         4 push @uniqs, [ @$un ];
268              
269             # Add the keys to the %seen_keys hash
270 1         3 for my $k (@$un)
271             {
272 2 50       6 next if $seen_keys{$k};
273 2         8 $seen_keys{$k} = { sql_name => $k, name => $k,
274             constraint => 'isnullablestring' };
275 2         8 push @newcolumns, $k;
276             }
277             }
278              
279             # Get the unique keys data
280             # For stability, this _should_ be a Readonly variable
281             # Unfortunately, Readonly is really slow on older Perls
282 3     0   20 *{$table_class . '::_unique_keys' } = sub { @uniqs; };
  3         27  
  0         0  
283              
284             # The rest of the column definitions
285 3         7 my @coldefs;
286             my %colmap;
287 3         7 for my $col (@$columns)
288             {
289             # Copy the column definition, hack-n-slash at will...
290 11         199 my %cdef = %$col;
291              
292             # Column names
293 11         23 my $sql_name = delete $cdef{name};
294 11   33     321 my $name = delete $cdef{alias} || $sql_name;
295 11         23 $colmap{$name} = $sql_name;
296 11         21 delete $seen_keys{$sql_name};
297              
298             # Handle constraints and type matching
299 11         16 my $constraint = delete $cdef{constraint};
300 11         12 my $match = delete $cdef{matches};
301 11         13 my $type = delete $cdef{type};
302 11 50       31 if (not $constraint)
303             {
304             # The default constraint is "match EVERYTHING"
305 11         13 $constraint = 'isnullablestring';
306              
307             # If we have a regular expression, use it
308 11 50       32 if($match)
    50          
309             {
310             $constraint = sub
311             {
312 0     0   0 my ($self, $value) = @_;
313 0         0 $value =~ /$match/;
314 0         0 };
315             }
316              
317             # Or if we have a type, use that
318             elsif($type)
319             {
320             # We check for every type except nullablestring,
321             # because we already set that as the default
322 0 0       0 if($type eq 'number') { $constraint = 'isnumber'; }
  0 0       0  
    0          
323 0         0 elsif($type eq 'string') { $constraint = 'isstring'; }
324             elsif($type eq 'nullablenumber')
325             {
326 0         0 $constraint = 'isnullablenumber';
327             }
328             }
329             }
330              
331             # Save the column definition
332 11         166 push @coldefs,
333             {
334             sql_name => $sql_name,
335             name => $name,
336             constraint => $constraint,
337             column_name => $sql_name,
338             };
339              
340             # Create the column method
341 11         58 *{$row_class . "::$name"} = $self->__create_column_accessor(
  11         94  
342             $sql_name, $pk{$sql_name});
343             }
344              
345             # Add columns for missing primary/unique key components
346 3         8 for my $col (@newcolumns)
347             {
348 4         6 my $def = delete $seen_keys{$col};
349 4 50       13 next unless $def;
350 0         0 push @coldefs, $def;
351 0         0 $colmap{$col} = $col;
352 0         0 *{$row_class . "::$col"} = $self->__create_column_accessor(
  0         0  
353             $col, $pk{$col});
354             }
355              
356             # Save the column and mapping information
357 3     4   12 *{$table_class . '::_columns' } = sub { @coldefs; } ;
  3         25  
  4         13  
358 3     0   13 *{$table_class . '::_column_map' } = sub { %colmap; } ;
  3         23  
  0         0  
359 3         20 my @sql_cols = sort values %colmap;
360 3     0   11 *{$table_class . '::_column_sql_names' } = sub { @sql_cols; };
  3         34  
  0         0  
361              
362 3         4 return &{$table_class}($self);
  3         16  
363             }
364              
365             # Create a new join, or return the name of this join object
366             sub join
367             {
368 1     1 1 5 my ($self, @args) = @_;
369 1 50       12 if(@args<2)
370             {
371             # Turn this into a join object, if requested
372 0 0       0 if(@args==1)
373             {
374 0         0 my $join = shift @args;
375 0 0       0 return $self unless $join;
376 0         0 my $meth = $self->join_method($join);
377 0 0       0 return unless $meth;
378 0         0 return $meth->($self);
379             }
380              
381 0         0 my $join;
382 0         0 eval { $join = $self->_join; };
  0         0  
383 0         0 return $join;
384             }
385              
386             # If we get to here, we're adding a new join declaration.
387 1         5 my %args = @args;
388 1         4 my $name = delete $args{name};
389 1 50 0     18 carp "duplicate table/join declaration" and return if $self->can($name);
390              
391             # Class family names
392 1         5 my $super = $self->_schema_class;
393 1         4 my $join_class = $super . "::$name";
394 1         3 my $row_class = $join_class . '::Rows';
395 1         6 my $schema = $self->_schema;
396              
397 1         3 my $ptab = delete $args{primary};
398 1 50 0     4 carp "missing join name" and return unless $name;
399              
400 1         4 my $tables = delete $args{tables};
401 1 50 0     24 carp "missing table(s) to join" and return unless $ptab and $tables;
      33        
402              
403             # Look for the table class(es) we need
404 1         6 my @req_tabs = ($ptab, map { $_->{table} } @$tables);
  1         5  
405              
406 2         24 carp "missing required tables" and return
407 1 50 0     3 if grep { not $self->can($_); } @req_tabs;
408              
409             # Create a primary table object
410 1         4 my $ptab_obj = $self->table($ptab);
411 1 50 0     16 carp "No such table '$ptab'" and return unless $ptab_obj;
412              
413             # Info to create the join
414 1         4 my $ptab_name = $ptab_obj->_table;
415 1         10 my $ptab_alias = $self->__make_sql_safe($ptab_name);
416              
417             # Primary table's columns
418 1         4 my @ptab_cols = map { $_->{name} } $ptab_obj->_columns;
  5         447  
419              
420             # Will be turned into _sql_name
421 1         13 my @join_table_info = ($ptab_obj->_sql_name . " $ptab_alias");
422              
423             # Will be turned into _columns
424 5         54 my @column_info =
425             map
426             {
427 1         4 (
428             {
429             sql_name => "$ptab_alias." . $_->{sql_name},
430             name => $_->{name},
431             constraint => $_->{constraint},
432             table => $ptab_name,
433             table_alias => $ptab_alias,
434             column_name => $_->{sql_name},
435             },
436             {
437             sql_name => "$ptab_alias." . $_->{sql_name},
438             name => $ptab_name . '_' . $_->{name},
439             constraint => $_->{constraint},
440             table => $ptab_name,
441             table_alias => $ptab_alias,
442             column_name => $_->{sql_name},
443             },
444             )
445             }
446             $ptab_obj->_columns;
447              
448             # Will be turned into _column_map
449 10         30 my %column_map =
450 1         3 map { @{$_}{qw(name sql_name)} } @column_info;
  10         11  
451              
452             # The "where" clause info
453 1         3 my @wherefrags;
454              
455             # The "group by" clause info
456 1         4 my @group_by = map { $column_map{$_} } $ptab_obj->_group_by;
  0         0  
457              
458             # Primary table's primary keys
459 1         5 my %pkeys = map {($_ => 1, $ptab_name . "_$_" => 1)}
  1         6  
460             $ptab_obj->_primary_key;
461              
462 1         3 my @tables_seen;
463              
464             # Need to clone table info so it doesn't get changed out from under us
465             my @tab_info;
466 1         12 for my $tab (@$tables)
467             {
468 1         9 my $tab_name = $tab->{table};
469 1         4 my $tab_obj = $self->table($tab_name);
470              
471             # No sense doing all the work if the table doesn't exist...
472 1 50 0     16 carp "No such table '$tab_name'" and return unless $tab_obj;
473 1         4 my $info_ref = { table => $tab_name };
474              
475             # Support secondary table joins
476 1         4 my @use_cols = @ptab_cols;
477 1         2 my $usetab_name = $ptab_name;
478 1         2 my $usetab_alias = $ptab_alias;
479 1         3 my $secondary = $tab->{on_secondary};
480 1 50       4 if($secondary)
481             {
482 0         0 carp "Secondary table '$secondary' unknown" and return
483 0 0 0     0 unless grep { $secondary eq $_ } @tables_seen;
484 0         0 my $secondary_obj = $self->table($secondary);
485 0 0 0     0 carp "No such table '$secondary'" and return unless $secondary_obj;
486 0         0 $info_ref->{on_secondary} = $secondary;
487 0         0 @use_cols = map { $_->{name} } $secondary_obj->_columns;
  0         0  
488 0         0 $usetab_name = $secondary;
489 0         0 $usetab_alias = $self->__make_sql_safe($usetab_name);
490             }
491              
492 1         3 push @tables_seen, $tab_name;
493              
494 1         20 my $tab_alias = $self->__make_sql_safe($tab_obj->_table);
495              
496 1         3 my %join_info = %{$tab->{columns}};
  1         5  
497 1         4 my @tab_cols = $tab_obj->_columns;
498 1         4 for my $k (keys %join_info)
499             {
500 5         14 carp "No such key '$k' on primary table '$usetab_name'" and return
501 1 50 0     2 unless grep { $k eq $_ } @use_cols;
502 5         15 carp "No such key '$k' on secondary table '$tab_name'" and return
503 1 50 0     2 unless grep { $join_info{$k} eq $_->{name} } @tab_cols;
504              
505 1         4 $info_ref->{columns}{$k} = $join_info{$k};
506              
507             # We set the join keys as primary so they don't get changed on us
508 1         3 $pkeys{$k} = $pkeys{$join_info{$k}} = 1;
509              
510             # Save the "where" clause info
511 1         6 push @wherefrags, "$usetab_alias.$k = $tab_alias.$join_info{$k}";
512             }
513             # Save the copy
514 1         3 push @tab_info, $info_ref;
515              
516             # Save table join information
517 1         8 push @join_table_info, $tab_obj->_sql_name . " $tab_alias";
518              
519             # Save group by information
520 1         4 my %tab_group_by = map { ($_,1) } $tab_obj->_group_by;
  0         0  
521              
522             # Housekeeping information
523 1         3 for my $col ($tab_obj->_columns)
524             {
525 5         14 my $column_ref = "$tab_alias." . $col->{sql_name};
526             # Save the column information
527 5         61 push @column_info,
528             {
529             sql_name => $column_ref,
530             name => $col->{name},
531             constraint => $col->{constraint},
532             table => $tab_name,
533             table_alias => $tab_alias,
534             column_name => $_->{sql_name},
535             },
536             {
537             sql_name => $column_ref,
538             name => $tab_name . '_' .$col->{name},
539             constraint => $col->{constraint},
540             table => $tab_name,
541             table_alias => $tab_alias,
542             column_name => $_->{sql_name},
543             };
544              
545             # Save column mapping info
546 5   33     25 $column_map{$col->{name}} ||= $column_ref;
547 5   33     34 $column_map{$tab_name . '_' . $col->{name}} ||= $column_ref;
548              
549 5 50       17 push @group_by, $column_ref if $tab_group_by{$col->{name}};
550             }
551              
552             # Keep track of housekeeping information
553             $pkeys{$tab_name . "_$_"} = $pkeys{$_} = 1
554 1         5 foreach $tab_obj->_primary_key;
555             }
556              
557             # We're constructing refs, so turn off strictness
558 1     1   11 no strict 'refs';
  1         3  
  1         1505  
559            
560             # Class heirarchy
561 1         178 @{$join_class . '::ISA'} = ($super, $self->JOIN_CLASS);
  1         363  
562 1         10 @{$row_class . '::ISA'} = ($self->JROW_CLASS, $join_class);
  1         38  
563              
564             # Information methods
565 1     1   6 *{$join_class . '::_class'} = sub { $join_class; };
  1         9  
  1         5  
566 1     0   4 *{$join_class . '::_row_class'} = sub { $row_class; };
  1         8  
  0         0  
567 1     0   10 *{$join_class . '::_join'} = sub { $name; };
  1         9  
  0         0  
568 1     0   11 *{$join_class . '::_primary'} = sub { $ptab; };
  1         9  
  0         0  
569              
570             # The stuff to make searching for joins work...
571 1         4 my $join_tabs = join(', ', @join_table_info);
572 1     0   4 *{$join_class . '::_sql_name'} = sub { $join_tabs; };
  1         8  
  0         0  
573              
574 1         3 my $where_prefix = join(' AND ', @wherefrags);
575 1     0   4 *{$join_class . '::_where_prefix'} = sub { $where_prefix; };
  1         9  
  0         0  
576 1     0   5 *{$join_class . '::_columns'} = sub { @column_info; };
  1         9  
  0         0  
577 1     0   13 *{$join_class . '::_column_map'} = sub { %column_map; };
  1         9  
  0         0  
578 1     0   4 *{$join_class . '::_group_by'} = sub { @group_by; };
  1         8  
  0         0  
579            
580             # We need to get the list of columns...
581 1         29 my %h = reverse %column_map;
582 1         10 my @sql_cols = sort keys %h;
583 1     0   6 *{$join_class . '::_column_sql_names' } = sub { @sql_cols; };
  1         9  
  0         0  
584              
585             my $cons = sub
586             {
587 3     3   6 my ($self) = @_;
588 3         19 my $rv = $self->new;
589 3 50       117 bless $rv, $join_class unless $rv->isa($join_class);
590 3         41 return $rv;
591 1         5 } ;
592              
593 1         2 *{$join_class} = $cons;
  1         13  
594              
595 1         11 $self->join_method($name, $cons);
596            
597 1     0   11 *{$join_class . '::_join_info'} = sub { @tab_info; };
  1         10  
  0         0  
598              
599             # Create the accessors
600 20         330 *{$row_class . "::$_" } =
601             $self->__create_column_accessor($column_map{$_}, $pkeys{$_})
602 1         10 foreach keys %column_map;
603              
604             # Return success
605 1         3 return &{$join_class}($self);
  1         6  
606             }
607              
608             # Create a method to access column data
609             sub __create_column_accessor
610             {
611 31     31   65 my ($self, $name, $pk_flag) = @_;
612 31 100       64 if($pk_flag)
613             {
614             # This is a primary key, so it's read-only
615             return sub
616             {
617 0     0   0 my $self = shift;
618 0 0 0     0 carp "$name is not a class method" and return unless ref $self;
619 0 0 0     0 carp "$name is part of the primary key" and return $self if @_;
620 0         0 return $self->{__data}{$name};
621 8         275 };
622             }
623              
624             # If we get to here, it's not a primary key, so we can beat on it
625             return sub
626             {
627 0     0     my $self = shift;
628 0 0 0       carp "$name is not a class method" and return unless ref $self;
629 0           my $val = $self->{__data}{$name};
630 0 0         if(@_)
631             {
632 0           my $nval = $_[0];
633             # Changing undef to undef is not a change...
634 0 0 0       return $self if not defined $val and not defined $nval;
635 0 0 0       if(not defined $val or $nval ne $val)
636             {
637 0           delete $self->{__data}{$name};
638 0 0         $self->{__data}{$name} = $nval if defined $nval;
639 0           $self->{__dirty} = 1;
640             }
641 0           return $self;
642             }
643 0           return $val;
644 23         93 };
645             }
646              
647             1;