My patches, complete

[prev] [thread] [next] [Date index for 2004/08/08]

From: Yuval Kogman
Subject: My patches, complete
Date: 12:06 on 08 Aug 2004
--kORqDWCi7qDJ0mEj
Content-Type: multipart/mixed; boundary="PNTmBPCT7hxwcZjr"
Content-Disposition: inline


--PNTmBPCT7hxwcZjr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi Tony, List...

I have combined my two patches to Class::DBI, the one which adds
remove_from_plural and mapped_class to the HasMany relationship,
which I needed for adding Many to Many support to Maypole, and the
one that implements Class::DBI::Relationship::LinksTogether. The
patches are documented and tested, and also add tests for mapping
which, as far as I can tell, was not really tested before, as well
as slight documentation for the relationship objects inside
meta_info.

I am also introducing one new bug, in
Class::DBI::Relationship::HasMany. Passing objects is now allowed,
so you no longer need to create the data as you link two rows, but
this means that if add_to_$accessor did not create $data into the
class of the had table, then it needs to call the mutator on the
field. Without auto_update or the user calling

	$obj->add_to_foos($ladi);
	$ladi->update;

the $ladi object would not /really/ point to $obj, which is very
counter intuitive (you'd expect $obj->update to work, because that's
what the interface makes you think you edited).

Anyway, right now remove_from and add_to both have this special case
treated by checking, after the mutation if the updated field is the
only field that is yet to change, and if yes, calling update. It is
assumed that the user will take care of updating, if at all, and the
bug part is when you discard changes to $ladi, it is no longer in
$obj->foos, even after an update, and that $obj->foos will not
contain $ladi till $ladi is updated.

Initially I thought I'll refactor update to be able to work on
speific fields, but then again, the code is working and i've got to
get some work done.

Lastly, some of the columns in the Artist test module were changed -
lowercase and Propercase are used interchangebly, which was too lax
for what I was testing (like whether to update or not).

That's all,
Enjoy!

--=20
 ()  Yuval Kogman <nothingmuch@xxxxxxxx.xxx> 0xEBD27418  perl hacker &
 /\  kung foo master: /me has realultimatepower.net: neeyah!!!!!!!!!!!!


--PNTmBPCT7hxwcZjr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="cdbi.patch"
Content-Transfer-Encoding: quoted-printable

diff -Nur /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI/Relationship/HasM=
any.pm Class-DBI/lib/Class/DBI/Relationship/HasMany.pm
--- /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI/Relationship/HasMany.pm=
	Sun Apr 25 18:33:36 2004
+++ Class-DBI/lib/Class/DBI/Relationship/HasMany.pm	Sun Aug  8 14:19:10 2004
@@ -46,6 +46,26 @@
 	return ($class, $accessor, $f_class, $args);
 }
=20
+sub mapped_class {
+	my $self =3D shift;
+	my $f_class =3D $self->foreign_class;
+
+	if (my $meta =3D $f_class->meta_info("has_a")){
+		if (my $accessor =3D $self->args->{mapping}[0]){
+			my $mapping =3D $meta->{$accessor};
+			return $mapping->foreign_class if $mapping;
+		}
+	}
+	if (my $meta =3D $f_class->meta_info("has_many")){
+		if (my $accessor =3D $self->args->{mapping}[0]){
+			my $mapping =3D $meta->{$accessor};
+			return $mapping->foreign_class if $mapping;
+		}
+	}
+
+	return $f_class;
+}
+
 sub _set_up_class_data {
 	my $self =3D shift;
 	$self->class->_extend_class_data(
@@ -67,8 +87,9 @@
 	my $self     =3D shift;
 	my $accessor =3D $self->accessor;
 	return (
-		$accessor          =3D> $self->_has_many_method,
-		"add_to_$accessor" =3D> $self->_method_add_to,
+		$accessor               =3D> $self->_has_many_method,
+		"add_to_$accessor"      =3D> $self->_method_add_to,
+		"remove_from_$accessor" =3D> $self->_method_remove_from,
 	);
 }
=20
@@ -79,15 +100,89 @@
 		my ($self, $data) =3D @_;
 		my $class =3D ref $self
 			or return $self->_croak("add_to_$accessor called as class method");
-		return $self->_croak("add_to_$accessor needs data")
-			unless ref $data eq "HASH";
+		return $self->_croak("add_to_$accessor needs either hash data or Class::=
DBI objects")
+			unless ref $data eq "HASH" or UNIVERSAL::isa($data, "Class::DBI");
+
+		my $self_many_rel =3D $class->meta_info( has_many =3D> $accessor );
+		my $self_f_class =3D $self_many_rel->foreign_class;
+		my $self_f_key =3D $self_many_rel->args->{foreign_key};
+
+
+		if ($self_f_class ne $self_many_rel->mapped_class){ # figure what kind o=
f link we're working with
+			my $lookup_mapping =3D $self_many_rel->args->{mapping}[0]; # this is th=
e mapping the foreign_class does to give us the next step towards mapped_cl=
ass
+
+			my $lookup_one_rel =3D $self_f_class->meta_info( has_a =3D> $lookup_map=
ping );
+			my $lookup_wants_class =3D $lookup_one_rel->foreign_class; # this is th=
e class $data should belong to
+			my $data_f_key =3D $lookup_one_rel->accessor; # huh? but that's what Ha=
sA::inflator uses. What the hell.
+
+			$data =3D $data->{$data_f_key} if ref $data eq 'HASH' and exists $data-=
>{$data_f_key};
+			$data =3D $lookup_wants_class->find_or_create($data) if ref $data eq 'H=
ASH';
+
+			UNIVERSAL::isa($data, $lookup_wants_class) # not mapped_class - that is=
 too deep.
+				or return $self->_croak("$accessor contains objects of class $lookup_w=
ants_class, not " . ref $data);
+
+
+			$self_f_class->find_or_create( $data_f_key =3D> $data->id, $self_f_key =
=3D> $self->id ); # create the link, unless it's already there
+		} else {
+			$data =3D $self_f_class->find_or_create({ %$data, $self_f_key =3D> $sel=
f }) and my $no_need =3D 1 if ref $data eq 'HASH';
+
+			UNIVERSAL::isa($data, $self_f_class)
+				or return $self->_croak("$accessor contains objects of class $self_f_c=
lass, not " . ref $data);
+
+			my $mut =3D $data->mutator_name($self_f_key);
+			$data->$mut($self) unless $no_need; # point to $self;
+
+			my @changed =3D $data->is_changed;
+			$data->update if (@changed =3D=3D 1 and $changed[0] eq $self_f_key); # =
FIXME - we should not be propagating the user's changes.
+			# warn "Please update $data" if $data->is_changed;
+		}
=20
-		my $meta =3D $class->meta_info(has_many =3D> $accessor);
-		my ($f_class, $f_key, $args) =3D
-			($meta->foreign_class, $meta->args->{foreign_key}, $meta->args);
-		$data->{$f_key} =3D $self->id;
-		$f_class->create($data);
+		$data;
 	};
+}
+
+sub _method_remove_from {
+	my $self     =3D shift;
+	my $accessor =3D $self->accessor;
+	return sub {
+		my ($self, $data) =3D @_;
+
+		my $class =3D ref $self
+			or return $self->_croak("remove_from_$accessor called as class method");
+		return $self->_croak("remove_from_$accessor needs either hash data or Cl=
ass::DBI objects")
+			unless ref $data eq "HASH" or UNIVERSAL::isa($data, "Class::DBI");
+
+		# find out who we're related to, and what key in them points to us
+		my $self_many_rel =3D $class->meta_info( has_many =3D> $accessor );
+		my $self_f_class =3D $self_many_rel->foreign_class;
+		my $self_f_key =3D $self_many_rel->args->{foreign_key};
+
+		if ($self_f_class ne $self_many_rel->mapped_class){ # figure what kind o=
f link we're working with
+			my $lookup_mapping =3D $self_many_rel->args->{mapping}[0]; # this is th=
e mapping the foreign_class does to give us the next step towards mapped_cl=
ass
+
+			my $lookup_one_rel =3D $self_f_class->meta_info( has_a =3D> $lookup_map=
ping );
+			my $lookup_wants_class =3D $lookup_one_rel->foreign_class; # this is th=
e class $data should belong to
+
+			UNIVERSAL::isa($data, $lookup_wants_class) # not mapped_class - that is=
 too deep.
+				or return $self->_croak("$accessor contains objects of class $lookup_w=
ants_class, not " . ref $data);
+
+			my $data_f_key =3D $lookup_one_rel->accessor; # huh? but that's what Ha=
sA::inflator uses. What the hell.
+
+			$self_f_class->retrieve( $data_f_key =3D> $data->id, $self_f_key =3D> $=
self->id )->delete; # remove the link, in manymany
+		} else {
+			ref $data eq 'HASH'
+				and return $self->_croak("remove_from_$accessor does take hash referen=
ces, like add_to_$accessor");
+			UNIVERSAL::isa($data,$self_f_class)
+				or return $self->_croak("$accessor contains objects of class $self_f_c=
lass, not " . ref $data);
+
+			my $mut =3D $data->mutator_name($self_f_key);
+			$data->$mut(undef); # break the link by no longer pointing $data to $se=
lf
+
+			my @changed =3D $data->is_changed;
+			$data->update if (@changed =3D=3D 1 and $changed[0] eq $self_f_key); # =
FIXME - we should not be propagating the user's changes.
+			# warn "Please update $data" if $data->is_changed;
+		}
+	}
 }
=20
 sub _has_many_method {
diff -Nur /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI/Relationship/Link=
sTogether.pm Class-DBI/lib/Class/DBI/Relationship/LinksTogether.pm
--- /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI/Relationship/LinksToget=
her.pm	Thu Jan  1 02:00:00 1970
+++ Class-DBI/lib/Class/DBI/Relationship/LinksTogether.pm	Sat Aug  7 18:33:=
40 2004
@@ -0,0 +1,176 @@
+package Class::DBI::Relationship::LinksTogether;
+use base 'Class::DBI::Relationship';
+
+use strict;
+use warnings;
+
+my $have_to_PL;
+BEGIN {
+	eval "use Lingua::EN::Inflect::Number"; # it's probably already loaded, s=
o forget use autouse.
+	unless ($@){
+		Lingua::EN::Inflect::Number->import(qw(to_PL));
+		$have_to_PL =3D 1;
+	}
+};
+
+sub remap_arguments {
+	my ($proto, $class) =3D (shift, shift);
+	my $args =3D pop || {} if @_ % 2;
+
+
+	my ($this,$that); # predeclared because we want them to keep the order pa=
ssed in @_
+	my %link;
+	if (@_ =3D=3D 2){ # two argument version
+		my @classes =3D map { ref $_ ? $_->[0] : $_ } @_;
+		($this,$that) =3D map { $_->moniker } @classes;
+		@link{$this,$that} =3D @classes;
+	} elsif (@_ >=3D 4){
+		%link =3D @_;
+		($this,$that) =3D @_[0,2];
+
+		if (grep { ref and ref ne 'ARRAY' } values %link){ # complain about anyt=
hing that isn't a scalar or an array reference
+			$class->_croak("The value parameter for a column may either be a scalar=
, or an array reference.");
+		}
+	} else { $class->_croak("a lookup table links between two and only two ta=
bles") }
+
+	foreach my $fkey (keys %link){ # the foreign key columns in $class
+		if (ref $link{$fkey} eq 'ARRAY'){ # if we have an array reference, then =
we have all the data we need
+			$link{$fkey} =3D {
+				class =3D> $link{$fkey}[0],
+				plural =3D> $link{$fkey}[1],
+			};
+		} else { # otherwise we need to generate our data,
+			$link{$fkey} =3D {
+				class =3D> $link{$fkey}, # we assume the only field is the class name
+			};
+
+			if ($args->{inflect}){ # and generate the plural accessor ("actors" fro=
m "Class::Actor")
+				die "Lingua::EN::Inflect::Number must be available for inflection" unl=
ess $have_to_PL;
+				$link{$fkey}{plural} =3D to_PL($fkey); # using the pluralization of th=
e foreign key, if it was asked for explicitly
+			} else {
+				$link{$fkey}{plural} =3D $link{$fkey}{class}->plural_moniker; # or the=
 plural moniker of the class
+			}
+		}
+	}
+
+	my $struct =3D {
+		$this =3D> {
+			has_a =3D> $class->has_a( $this =3D> $link{$this}{class} ),
+			has_many =3D> $link{$this}{class}->has_many( $link{$that}{plural} =3D> =
[ $class, $that ] ),
+		},
+		$that =3D> {
+			has_a =3D> $class->has_a( $that =3D> $link{$that}{class} ),
+			has_many =3D> $link{$that}{class}->has_many( $link{$this}{plural} =3D> =
[ $class, $this ] ),
+		},
+	};
+
+	return(
+		$class,
+		join("/", $link{$this}{plural}, $link{$that}{plural}),
+		join("/", $link{$this}{class}, $link{$that}{class}),
+		$struct,
+	);
+}
+
+sub has_manys {
+	my $self =3D shift;
+	map { $self->has_many($_) } $self->classes;
+}
+
+sub has_as {
+	my $self =3D shift;
+	map { $self->has_a($_) } $self->classes;
+}
+
+sub classes {
+	my $self =3D shift;
+	keys %{ $self->{args} };
+}
+
+sub has_a {
+	my $self =3D shift;
+	my $key =3D shift;
+
+	$self->class_or_accessor_or_plural($key)->{has_a};
+}
+
+sub has_many {
+	my $self =3D shift;
+	my $key =3D shift;
+
+	$self->class_or_accessor_or_plural($key, 1)->{has_many};
+}
+
+sub class_or_accessor_or_plural {
+	my $self =3D shift;
+	my $key =3D shift;
+	my $mapping =3D shift;
+
+	if (exists $self->{args}{$key}){
+		return $self->{args}{$key};
+	} else {
+		foreach my $rels (values %{ $self->{args} }){
+			return $rels if ($mapping ? $rels->{has_many}->mapped_class eq $key : $=
rels->{has_many}->{class} eq $key ) or $rels->{has_many}{accessor} eq $key;
+		}
+	}
+
+	Class::DBI->_croak("I don't know what $key is");
+}
+
+__PACKAGE__; # keep your mother happy
+
+__END__;
+
+=3Dpod
+
+=3Dhead1 NAME
+
+Class::DBI::Relationship::LinksTogether - a Class::DBI relationship for ma=
ny to
+many.
+
+=3Dhead1 SYNOPSIS
+
+	Role->defines_link( 'Actor', 'Film' );
+
+	# and then
+	my @actors =3D $film->actors;
+
+
+	# also
+	my $rel =3D Role->meta_info("links_together")->{"actors/films"};
+
+	print $rel->classes;
+
+=3Dhead1 DESCRIPTION
+
+See L<Class::DBI/links_together> and
+L<Class::DBI/"MANY TO MANY RELATIONSHIPS">.
+
+=3Dhead1 METHODS
+
+=3Dover 4
+
+=3Ditem has_manys
+
+=3Ditem has_as
+
+Return the two has_many or has_a has_a relationships this relationship set=
 up.
+
+=3Ditem has_many CLASS
+
+=3Ditem has_a CLASS
+
+Retun the has_a or has_many relationship having to do with CLASS
+
+=3Ditem classes
+
+Returns the classes that this relationship links together.
+
+=3Dback
+
+=3Dhead1 AUTHOR
+
+Yuval Kogman <nothingmuch@xxxxxxxx.xxx>
+
+=3Dcut
+
diff -Nur /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI.pm Class-DBI/lib/=
Class/DBI.pm
--- /Users/nothingmuch/Class-DBI-0.96/lib/Class/DBI.pm	Fri Apr 30 10:22:12 =
2004
+++ Class-DBI/lib/Class/DBI.pm	Sun Aug  8 14:43:51 2004
@@ -112,9 +112,10 @@
 __PACKAGE__->purge_object_index_every(1000);
=20
 __PACKAGE__->add_relationship_type(
-	has_a      =3D> "Class::DBI::Relationship::HasA",
-	has_many   =3D> "Class::DBI::Relationship::HasMany",
-	might_have =3D> "Class::DBI::Relationship::MightHave",
+	has_a          =3D> "Class::DBI::Relationship::HasA",
+	has_many       =3D> "Class::DBI::Relationship::HasMany",
+	might_have     =3D> "Class::DBI::Relationship::MightHave",
+	links_together =3D> "Class::DBI::Relationship::LinksTogether",
 );
 __PACKAGE__->mk_classdata('__meta_info');
 __PACKAGE__->__meta_info({});
@@ -315,7 +316,7 @@
=20
 	# we don't use get() here because all objects should have
 	# exisitng values for PK columns, or else loop endlessly
-	my @pk_values =3D $self->_attrs($self->primary_columns);
+	my @pk_values =3D map { UNIVERSAL::can($_, "id") ? $_->id : $_ } $self->_=
attrs($self->primary_columns);
 	return @pk_values if wantarray;
 	$self->_croak(
 		"id called in scalar context for class with multiple primary key columns=
")
@@ -2431,6 +2432,54 @@
 creating the objects, but I may make these simpler in later versions.
 (Particularly if someone asks for them!)
=20
+=3Dhead2 links_together
+
+This is a shortcut to setting up L</"MANY TO MANY RELATIONSHIPS">, and
+might, in the future, become the interface for linking two tables via
+a link table, even with complex foreign keys.
+
+	Role->links_together(qw/Film Actor/);
+
+is pretty much the same as saying
+
+	Role->has_a(film =3D> 'Film');
+	Role->has_a(actor =3D> 'Actor');
+	Film->has_many(actors =3D> [ Role =3D> 'actor' ]);
+	Actor->has_many(films =3D> [ Role =3D> 'film' ]);
+
+You can create the link in three forms, ranging from terse to explicit.=20
+
+	Role->links_together(qw/Film Actor/);
+
+will use the L<UNIVERSAL::moniker/moniker>s of C<Film> and C<Actor> to
+find the foreign key in C<Role>, and use the
+L<UNIVERSAL::moniker/plural_moniker>s of C<Film> and C<Actor> to
+generate the accessors in each of the classes' counterpart class.
+
+You can also pass the arguments as two key value pairs, in the form
+
+	Role->links_together(actor =3D> 'Actor', film =3D> 'Film');
+
+In which case the keys will be treated as the foreign key fields in Role.
+
+If the accessors you would like to generate are not the
+L<UNIVERSAL::moniker/plural_moniker> of the class in question, you can
+interchange one or both of the class names for array references, which
+contain the class name, and the plural accessor name:
+
+	Role->links_together([ Actor =3D> 'stars' ], 'Film');
+
+will create C<Film::stars> instead of C<Film::actors>.
+
+Lastly, if you prefer to not use the plural_moniker, but instead use
+L<Lingua::EN::Inflect::Number>, pass a hash reference as the third or
+fifth argument, which contains a true value for the key C<inflect>:
+
+	Role->links_together(star =3D> 'Actor', feature =3D> 'Film', {inflect =3D=
> 1});
+
+will create the plural accessors based on the foreign key fields in
+C<Role>, in this example C<stars> and C<features> respectively.
+
 =3Dhead2 Notes
=20
 has_a(), might_have() and has_many() check that the relevant class has
@@ -2439,7 +2488,8 @@
 find the module then it will assume it's not a simple require (i.e.,
 Foreign::Class isn't in Foreign/Class.pm) and that you will take care
 of it and ignore the warning. Any other error, such as a syntax error,
-triggers an exception.
+triggers an exception. links_together() uses has_many() and has_a(),
+and thus inherently performs the same magic.
=20
 NOTE: The two classes in a relationship do not have to be in the same
 database, on the same machine, or even in the same type of database! It
@@ -2448,11 +2498,36 @@
 to work across these. This should assist greatly if you need to migrate
 a database gradually.
=20
+=3Dhead1 META INFO AND RELATIONSHIP OBJECTS
+
+Inside each classes meta_info() is a hash per relationship, keyed by
+accessor, which grants you access to the relationship objects.
+
+For example, to know what class C<< Film->actors() >> is going to
+return, do
+
+	my $rel =3D Film->meta_info("has_many" =3D> "actors");
+	warn "Film->actors gives us objects of class " . $rel->mapped_class;
+
+The interfaces are not yet documented in
+L<Class::DBI::Relationship::HasA>, L<Class::DBI::Relationship::HasMany>,
+L<Class::DBI::Relationship::MightHave> and
+L<Class::DBI::Relationship::LinksTogether>.
+
+It should be noted that the links_together() relationship does not have
+an accessor in the class that defines the relationship, so the hash is
+keyed by a concatenation of the two has_a relationship accessors it
+makes in that class:
+
+	Role->meta_info("links_together" =3D> "actor/film")
+
 =3Dhead1 MANY TO MANY RELATIONSHIPS
=20
 Class::DBI does not currently support Many to Many relationships, per se.
 However, by combining the relationships that already exist it is possible
-to set these up.
+to set these up, provided the two linked tables have a single column
+primary key. The easy way of doing this is using L</"links_together">,
+and herein is explained how this is really performed.
=20
 Consider the case of Films and Actors, with a linking Role table. First
 of all we'll set up our Role class:
@@ -2497,6 +2572,34 @@
 Similarly a cascading delete will also do the right thing as it will
 only delete the relationship from the linking table.
=20
+	$film->add_to_actors($actor);
+	$film->delete;
+
+	$actor; # not deleted, Role ( actor =3D> $actor, film =3D> $film ) was
+	        # deleted instead
+
+To delete the relationship from the linking table based on the two
+link members, use
+
+	$film->remove_from_actors({ actor =3D> $actor });
+
+You can also break or create the link rows by passing just plain
+objects:
+
+	$film->add_to_actors($actor);
+	$film->remove_from_actors($actor);
+
+work just the same. Furthermore, you can pass a data hash, like to
+create():
+
+	$film->add_to_actors({ name =3D> "silly" });
+
+but that doesn't work for remove_from, since you should just create a
+new object.
+
+The L</"links_together"> relationship type is a shortcut to setting up
+a mapping between two classes, via a median one.
+
 If the Role table were to contain extra information, such as the name
 of the character played, then you would usually need to skip these
 short-cuts and set up each of the relationships, and associated helper
@@ -2511,9 +2614,10 @@
 Class::DBI through an add_relationship_type() call:
=20
 	__PACKAGE__->add_relationship_type(
-		has_a      =3D> "Class::DBI::Relationship::HasA",
-		has_many   =3D> "Class::DBI::Relationship::HasMany",
-		might_have =3D> "Class::DBI::Relationship::MightHave",
+		has_a          =3D> "Class::DBI::Relationship::HasA",
+		has_many       =3D> "Class::DBI::Relationship::HasMany",
+		might_have     =3D> "Class::DBI::Relationship::MightHave",
+		links_together =3D> "Class::DBI::Relationship::LinksTogether",
 	);
=20
 If is thus possible to add new relationship types, or modify the behaviour
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/09-has_many.t Class-DBI/t/09-=
has_many.t
--- /Users/nothingmuch/Class-DBI-0.96/t/09-has_many.t	Sun Apr 25 18:33:36 2=
004
+++ Class-DBI/t/09-has_many.t	Sun Aug  8 14:11:08 2004
@@ -3,19 +3,19 @@
=20
 BEGIN {
 	eval "use DBD::SQLite";
-	plan $@ ? (skip_all =3D> 'needs DBD::SQLite for testing') : (tests =3D> 3=
0);
+	plan $@ ? (skip_all =3D> 'needs DBD::SQLite for testing') : (tests =3D> 3=
2);
=20
 	use lib 't/testlib';
 	use Film;
 	use Actor;
 	Film->CONSTRUCT;
 	Actor->CONSTRUCT;
-	Film->has_many(actors =3D> Actor =3D> 'Film', { order_by =3D> 'name' });
-	Actor->has_a(Film =3D> 'Film');
+	Actor->has_a(film =3D> 'Film');
+	Film->has_many(actors =3D> Actor =3D> 'film', { order_by =3D> 'name' });
 	is(Actor->primary_column, 'id', "Actor primary OK");
 }
=20
-ok(Actor->can('Salary'), "Actor table set-up OK");
+ok(Actor->can('salary'), "Actor table set-up OK");
 ok(Film->can('actors'),  " and have a suitable method in Film");
=20
 ok(my $btaste =3D Film->retrieve('Bad Taste'), "We have Bad Taste");
@@ -23,27 +23,27 @@
 ok(
 	my $pvj =3D Actor->create(
 		{
-			Name   =3D> 'Peter Vere-Jones',
-			Film   =3D> undef,
-			Salary =3D> '30_000',             # For a voice!
+			name   =3D> 'Peter Vere-Jones',
+			film   =3D> undef,
+			salary =3D> '30_000',             # For a voice!
 		}
 	),
 	'create Actor'
 );
-is $pvj->Name, "Peter Vere-Jones", "PVJ name ok";
-is $pvj->Film, undef, "No film";
-ok $pvj->set_Film($btaste), "Set film";
+is $pvj->name, "Peter Vere-Jones", "PVJ name ok";
+is $pvj->film, undef, "No film";
+ok $pvj->set_film($btaste), "Set film";
 $pvj->update;
-is $pvj->Film->id, $btaste->id, "Now film";
+is $pvj->film->id, $btaste->id, "Now film";
 {
 	my @actors =3D $btaste->actors;
 	is(@actors, 1, "Bad taste has one actor");
-	is($actors[0]->Name, $pvj->Name, " - the correct one");
+	is($actors[0]->name, $pvj->name, " - the correct one");
 }
=20
 my %pj_data =3D (
-	Name   =3D> 'Peter Jackson',
-	Salary =3D> '0',               # it's a labour of love
+	name   =3D> 'Peter Jackson',
+	salary =3D> '0',               # it's a labour of love
 );
=20
 eval { my $pj =3D Film->add_to_actors(\%pj_data) };
@@ -55,34 +55,34 @@
 ok(
 	my $pj =3D $btaste->add_to_actors(
 		{
-			Name   =3D> 'Peter Jackson',
-			Salary =3D> '0',               # it's a labour of love
+			name   =3D> 'Peter Jackson',
+			salary =3D> '0',               # it's a labour of love
 		}
 	),
 	'add_to_actors'
 );
-is $pj->Name,  "Peter Jackson",    "PJ ok";
-is $pvj->Name, "Peter Vere-Jones", "PVJ still ok";
+is $pj->name,  "Peter Jackson",    "PJ ok";
+is $pvj->name, "Peter Vere-Jones", "PVJ still ok";
=20
 {
 	my @actors =3D $btaste->actors;
 	is @actors, 2, " - so now we have 2";
-	is $actors[0]->Name, $pj->Name,  "PJ first";
-	is $actors[1]->Name, $pvj->Name, "PVJ first";
+	is $actors[0]->name, $pj->name,  "PJ first";
+	is $actors[1]->name, $pvj->name, "PVJ first";
 }
=20
 eval {
-	my @actors =3D $btaste->actors(Name =3D> $pj->Name);
+	my @actors =3D $btaste->actors(name =3D> $pj->name);
 	is @actors, 1, "One actor from restricted (sorted) has_many";
-	is $actors[0]->Name, $pj->Name, "It's PJ";
+	is $actors[0]->name, $pj->name, "It's PJ";
 };
 is $@, '', "No errors";
=20
 my $as =3D Actor->create(
 	{
-		Name   =3D> 'Arnold Schwarzenegger',
-		Film   =3D> 'Terminator 2',
-		Salary =3D> '15_000_000'
+		name   =3D> 'Arnold Schwarzenegger',
+		film   =3D> 'Terminator 2',
+		salary =3D> '15_000_000'
 	}
 );
=20
@@ -90,19 +90,25 @@
 ok $@, $@;
 is($btaste->actors, 2, " - so we still only have 2 actors");
=20
-my @bta_before =3D Actor->search(Film =3D> 'Bad Taste');
+$btaste->remove_from_actors($pj);
+is_deeply([ $btaste->actors ], [ $pvj ], "remove from works");
+
+$btaste->add_to_actors($pj);
+is_deeply([ $btaste->actors ], [ $pj, $pvj ], "add_to worked on object");
+
+my @bta_before =3D Actor->search(film =3D> 'Bad Taste');
 is(@bta_before, 2, "We have 2 actors in bad taste");
 ok($btaste->delete, "Delete bad taste");
-my @bta_after =3D Actor->search(Film =3D> 'Bad Taste');
+my @bta_after =3D Actor->search(film =3D> 'Bad Taste');
 is(@bta_after, 0, " - after deleting there are no actors");
=20
 # While we're here, make sure Actors have unreadable mutators and
 # unwritable accessors
=20
-eval { $as->Name("Paul Reubens") };
+eval { $as->name("Paul Reubens") };
 ok $@, $@;
-eval { my $name =3D $as->set_Name };
+eval { my $name =3D $as->set_name };
 ok $@, $@;
=20
-is($as->Name, 'Arnold Schwarzenegger', "Arnie's still Arnie");
+is($as->name, 'Arnold Schwarzenegger', "Arnie's still Arnie");
=20
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/23-mapping.t Class-DBI/t/23-m=
apping.t
--- /Users/nothingmuch/Class-DBI-0.96/t/23-mapping.t	Thu Jan  1 02:00:00 19=
70
+++ Class-DBI/t/23-mapping.t	Sun Aug  8 13:58:45 2004
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+$| =3D 1;
+
+use strict;
+use Test::More;
+
+BEGIN {
+	eval "use DBD::SQLite";
+	plan $@ ? (skip_all =3D> 'needs DBD::SQLite for testing') : ("no_plan");
+
+	use lib 't/testlib';
+	use Artist;
+	use Album;
+	use Participation;
+	Album->CONSTRUCT;
+	Artist->CONSTRUCT;
+	Participation->CONSTRUCT;
+	FunnyParticipation->CONSTRUCT;
+
+	is(Artist->primary_column, 'id', "Artist primary OK");
+}
+
+ok(Artist->can('name'), "Artist table set-up OK");
+
+Participation->has_a( artist =3D> 'Artist' );
+Participation->has_a( album =3D> 'Album' );
+Artist->has_many( albums =3D> [ Participation =3D> 'album' ]);
+Album->has_many( artists =3D> [ Participation =3D> 'artist' ]);
+
+FunnyParticipation->has_a( starlette =3D> 'Artist' );
+FunnyParticipation->has_a( masterpiece =3D> 'Album' );
+Artist->has_many( cds =3D> [ FunnyParticipation =3D> 'masterpiece' ]);
+Album->has_many( singers =3D> [ FunnyParticipation =3D> 'starlette' ]);
+
+ok(Artist->can('albums'), "Artists have an 'albums' method, has_many set-u=
p OK");
+ok(Artist->can('cds'), "Artists have a 'cds' method, has_many set-up OK");
+
+my $britney =3D Artist->retrieve(id =3D> 1);
+my $b =3D Album->retrieve(id =3D> 1);
+
+is_deeply($b, ($britney->albums)[0], "Britney's first album id had by brit=
ney");
+is_deeply($b, ($britney->cds)[0], "And the same goes for the funny foreign=
 key names, and accessor 'cds'");
+
+my $pik =3D Artist->create({ name =3D> "Pik Sisters" });
+my $album =3D Album->create({ name =3D> "It's lousy, but you get a free br=
acelet" });
+
+$album->add_to_artists($pik);
+is_deeply([ $pik->albums ], [ $album ], "add_to works");
+
+$britney->add_to_albums({ album =3D> $album });
+is_deeply([ $album->artists ], [ $pik, $britney ], "add_to with data hash =
works too");
+
+
+my $christina =3D Artist->retrieve(id =3D> 2);
+my $christmas =3D Album->retrieve(id =3D> 3);
+
+is_deeply([ $christmas->artists ], [ $britney, $christina ], "Both christi=
na and britney are had by christmas");
+is_deeply([ $christmas->singers ], [ $britney, $christina ], "And the same=
 goes for the funny has_many 'singers'");
+
+$christmas->remove_from_artists( $christina );
+is_deeply([ $christmas->artists ], [ $britney ], "remove_from works");
+
+my $rel =3D Artist->meta_info("has_many")->{albums};
+
+is($rel->foreign_class, "Participation", "relationship's foreign class is =
known");
+is($rel->mapped_class, "Album", "relationship's mapped class is known");
+
+$rel =3D Artist->meta_info("has_many")->{cds};
+is($rel->foreign_class, "FunnyParticipation", "relationship's foreign clas=
s is known for funny");
+is($rel->mapped_class, "Album", "relationship's mapped class is known for =
funny");
+
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/24-links_together-basic.t Cla=
ss-DBI/t/24-links_together-basic.t
--- /Users/nothingmuch/Class-DBI-0.96/t/24-links_together-basic.t	Thu Jan  =
1 02:00:00 1970
+++ Class-DBI/t/24-links_together-basic.t	Sun Aug  8 15:02:41 2004
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+
+$| =3D 1;
+
+use strict;
+use Test::More;
+
+BEGIN {
+	eval "use DBD::SQLite";
+	plan $@ ? (skip_all =3D> 'needs DBD::SQLite for testing') : (tests =3D> 2=
0);
+
+	use lib 't/testlib';
+	use Artist;
+	use Album;
+	use Participation;
+	Album->CONSTRUCT;
+	Artist->CONSTRUCT;
+	Participation->CONSTRUCT;
+	FunnyParticipation->CONSTRUCT;
+
+	is(Artist->primary_column, 'id', "Artist primary OK");
+}
+
+ok(Artist->can('name'), "Artist table set-up OK");
+
+{
+Participation->links_together(artist =3D> [ Artist =3D> 'artists' ], album=
 =3D> [ Album =3D> 'albums' ]);
+ok(Artist->can('albums'), "Artists have an 'albums' method");
+
+my $britney =3D Artist->retrieve(id =3D> 1);
+my $b =3D Album->retrieve(id =3D> 1);
+
+is_deeply($b, ($britney->albums)[0], "Britney's first album ia had by brit=
ney");
+
+my $christina =3D Artist->retrieve(id =3D> 2);
+my $christmas =3D Album->retrieve(id =3D> 3);
+
+is_deeply([ $christmas->artists ], [ $britney, $christina ], "Both christi=
na and britney are had by christmas");
+
+my $link =3D Participation->meta_info("links_together")->{"artists/albums"=
};
+
+is_deeply($link->has_many("album"), Album->meta_info("has_many")->{artists=
}, "link->has_many(fkey)");
+is_deeply($link->has_many("Artist"), Album->meta_info("has_many")->{artist=
s}, "link->has_many(class)");
+is_deeply($link->has_many("artists"), Album->meta_info("has_many")->{artis=
ts}, "link->has_many(plural)");
+is_deeply($link->has_a("Artist"), Participation->meta_info("has_a")->{arti=
st}, "link->has_a(class)");
+is_deeply($link->has_a("artist"), Participation->meta_info("has_a")->{arti=
st}, "link->has_a(accessor)");
+
+eval { $link->has_a("foobar") };
+like($@, qr/I don't know what foobar is/, "link->has_a(bullsh*t) throws er=
ror");
+}
+
+{
+FunnyParticipation->links_together(starlette =3D> [ Artist =3D> 'singers' =
], masterpiece =3D> [ Album =3D> 'cds' ]);
+ok(Artist->can('cds'), "Artists have a 'cds' method");
+
+my $britney =3D Artist->retrieve(id =3D> 1);
+my $b =3D Album->retrieve(id =3D> 1);
+
+is_deeply($b, ($britney->cds)[0], "Britney's first album ia had by britney=
");
+
+my $christina =3D Artist->retrieve(id =3D> 2);
+my $christmas =3D Album->retrieve(id =3D> 3);
+
+is_deeply([ $christmas->singers ], [ $britney, $christina ], "Both christi=
na and britney are had by christmas");
+
+my $link =3D FunnyParticipation->meta_info("links_together")->{"singers/cd=
s"};
+
+is_deeply($link->has_many("masterpiece"), Album->meta_info("has_many")->{s=
ingers}, "link->has_many(fkey)");
+is_deeply($link->has_many("Artist"), Album->meta_info("has_many")->{singer=
s}, "link->has_many(class)");
+is_deeply($link->has_many("singers"), Album->meta_info("has_many")->{singe=
rs}, "link->has_many(plural)");
+is_deeply($link->has_a("Artist"), FunnyParticipation->meta_info("has_a")->=
{starlette}, "link->has_a(class)");
+is_deeply($link->has_a("starlette"), FunnyParticipation->meta_info("has_a"=
)->{starlette}, "link->has_a(accessor)");
+
+eval { $link->has_a("gorch") };
+like($@, qr/I don't know what gorch is/, "link->has_a(bullsh*t) throws err=
or");
+}
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/25-links_together-inflect.t C=
lass-DBI/t/25-links_together-inflect.t
--- /Users/nothingmuch/Class-DBI-0.96/t/25-links_together-inflect.t	Thu Jan=
  1 02:00:00 1970
+++ Class-DBI/t/25-links_together-inflect.t	Sun Aug  8 00:49:43 2004
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+$| =3D 1;
+
+use strict;
+use Test::More;
+
+BEGIN {
+	my @plan =3D (tests =3D> 11);
+	eval "use DBD::SQLite";
+	@plan =3D (skip_all =3D> 'needs DBD::SQLite for testing') if $@;
+	eval "use Lingua::EN::Inflect::Number";
+	@plan =3D (skip_all =3D> 'needs Lingua::EN::Inflect::Number for testing')=
 if $@;
+
+	plan @plan;
+
+
+	use lib 't/testlib';
+	use Artist;
+	use Album;
+	use Participation;
+	Album->CONSTRUCT;
+	Artist->CONSTRUCT;
+	FunnyParticipation->CONSTRUCT;
+
+	is(Artist->primary_column, 'id', "Artist primary OK");
+}
+
+ok(Artist->can('name'), "Artist table set-up OK");
+
+FunnyParticipation->links_together(starlette =3D> 'Artist', masterpiece =
=3D> 'Album', {inflect =3D> 1});
+ok(Artist->can('masterpieces'), "Artists have a 'masterpieces' method");
+
+my $britney =3D Artist->retrieve(id =3D> 1);
+my $b =3D Album->retrieve(id =3D> 1);
+
+is_deeply($b, ($britney->masterpieces)[0], "Britney's first album ia had b=
y britney");
+
+my $christina =3D Artist->retrieve(id =3D> 2);
+my $christmas =3D Album->retrieve(id =3D> 3);
+
+is_deeply([ $christmas->starlettes ], [ $britney, $christina ], "Both chri=
stina and britney are had by christmas");
+
+my $link =3D FunnyParticipation->meta_info("links_together")->{"starlettes=
/masterpieces"};
+
+is_deeply($link->has_many("masterpiece"), Album->meta_info("has_many")->{s=
tarlettes}, "link->has_many(fkey)");
+is_deeply($link->has_many("Artist"), Album->meta_info("has_many")->{starle=
ttes}, "link->has_many(class)");
+is_deeply($link->has_many("starlettes"), Album->meta_info("has_many")->{st=
arlettes}, "link->has_many(plural)");
+is_deeply($link->has_a("Artist"), FunnyParticipation->meta_info("has_a")->=
{starlette}, "link->has_a(class)");
+is_deeply($link->has_a("starlette"), FunnyParticipation->meta_info("has_a"=
)->{starlette}, "link->has_a(accessor)");
+
+eval { $link->has_a("foobar") };
+like($@, qr/I don't know what foobar is/, "link->has_a(bullsh*t) throws er=
ror");
+
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/26-links_together-terse.t Cla=
ss-DBI/t/26-links_together-terse.t
--- /Users/nothingmuch/Class-DBI-0.96/t/26-links_together-terse.t	Thu Jan  =
1 02:00:00 1970
+++ Class-DBI/t/26-links_together-terse.t	Sun Aug  8 15:01:43 2004
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+$| =3D 1;
+
+use strict;
+use Test::More;
+
+BEGIN {
+	my @plan =3D (tests =3D> 11);
+	eval "use DBD::SQLite";
+	@plan =3D (skip_all =3D> 'needs DBD::SQLite for testing') if $@;
+	eval "use Lingua::EN::Inflect::Number";
+	@plan =3D (skip_all =3D> 'needs Lingua::EN::Inflect::Number for testing')=
 if $@;
+
+	plan @plan;
+
+
+	use lib 't/testlib';
+	use Artist;
+	use Album;
+	use Participation;
+	Album->CONSTRUCT;
+	Artist->CONSTRUCT;
+	Participation->CONSTRUCT;
+
+	is(Artist->primary_column, 'id', "Artist primary OK");
+}
+
+ok(Artist->can('name'), "Artist table set-up OK");
+
+Participation->links_together(qw/Artist Album/);
+ok(Artist->can('albums'), "Artists have an 'albums' method");
+
+my $britney =3D Artist->retrieve(id =3D> 1);
+my $b =3D Album->retrieve(id =3D> 1);
+
+is_deeply($b, ($britney->albums)[0], "Britney's first album ia had by brit=
ney");
+
+my $christina =3D Artist->retrieve(id =3D> 2);
+my $christmas =3D Album->retrieve(id =3D> 3);
+
+is_deeply([ $christmas->artists ], [ $britney, $christina ], "Both christi=
na and britney are had by christmas");
+
+my $link =3D Participation->meta_info("links_together")->{"artists/albums"=
};
+
+is_deeply($link->has_many("album"), Album->meta_info("has_many")->{artists=
}, "link->has_many(fkey)");
+is_deeply($link->has_many("Artist"), Album->meta_info("has_many")->{artist=
s}, "link->has_many(class)");
+is_deeply($link->has_many("artists"), Album->meta_info("has_many")->{artis=
ts}, "link->has_many(plural)");
+is_deeply($link->has_a("Artist"), Participation->meta_info("has_a")->{arti=
st}, "link->has_a(class)");
+is_deeply($link->has_a("artist"), Participation->meta_info("has_a")->{arti=
st}, "link->has_a(accessor)");
+
+eval { $link->has_a("foobar") };
+like($@, qr/I don't know what foobar is/, "link->has_a(bullsh*t) throws er=
ror");
+
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/testlib/Actor.pm Class-DBI/t/=
testlib/Actor.pm
--- /Users/nothingmuch/Class-DBI-0.96/t/testlib/Actor.pm	Sun Apr 25 18:33:3=
6 2004
+++ Class-DBI/t/testlib/Actor.pm	Sun Aug  8 14:05:59 2004
@@ -8,7 +8,7 @@
 __PACKAGE__->table('Actor');
=20
 __PACKAGE__->columns(Primary =3D> 'id');
-__PACKAGE__->columns(All     =3D> qw/ Name Film Salary /);
+__PACKAGE__->columns(All     =3D> qw/ name film salary /);
 __PACKAGE__->columns(TEMP    =3D> qw/ nonpersistent /);
 __PACKAGE__->add_constructor(salary_between =3D> 'salary >=3D ? AND salary=
 <=3D ?');
=20
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/testlib/Album.pm Class-DBI/t/=
testlib/Album.pm
--- /Users/nothingmuch/Class-DBI-0.96/t/testlib/Album.pm	Thu Jan  1 02:00:0=
0 1970
+++ Class-DBI/t/testlib/Album.pm	Sun Aug  8 00:12:00 2004
@@ -0,0 +1,47 @@
+package Album;
+
+use base 'CDBase';
+use strict;
+
+__PACKAGE__->table('Albums');
+__PACKAGE__->columns('Primary',		'id');
+__PACKAGE__->columns('Essential',	'name');
+
+sub CONSTRUCT {
+	my $class =3D shift;
+	$class->create_albums_table;
+	$class->make_bad_taste;
+}
+
+sub create_albums_table {
+	my $class =3D shift;
+	$class->db_Main->do(
+		qq{
+	CREATE TABLE Albums (
+		id		INTEGER,
+		name	VARCHAR(80)
+	)
+		}
+	);
+}
+
+sub make_bad_taste {
+	my $class =3D shift;
+	$class->create($_) for (
+		{
+			id =3D> 1,
+			name =3D> "Britney's Debut"
+		},
+		{
+			id =3D> 2,
+			name =3D> "Christina Aguilera's Own Album",
+		},
+		{
+			id =3D> 3,
+			name =3D> "A Christmas Special Blah",
+		},
+	);
+}
+
+1;
+
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/testlib/Artist.pm Class-DBI/t=
/testlib/Artist.pm
--- /Users/nothingmuch/Class-DBI-0.96/t/testlib/Artist.pm	Thu Jan  1 02:00:=
00 1970
+++ Class-DBI/t/testlib/Artist.pm	Sun Aug  8 00:11:17 2004
@@ -0,0 +1,42 @@
+package Artist;
+
+use base 'CDBase';
+use strict;
+
+__PACKAGE__->table('Artists');
+__PACKAGE__->columns('Primary',		'id');
+__PACKAGE__->columns('Essential',	'name');
+
+sub CONSTRUCT {
+	my $class =3D shift;
+	$class->create_artists_table;
+	$class->make_bad_taste;
+}
+
+sub create_artists_table {
+	my $class =3D shift;
+	$class->db_Main->do(
+		qq{
+	CREATE TABLE Artists (
+		id		INTEGER,
+		name	VARCHAR(80)
+	)
+		}
+	);
+}
+
+sub make_bad_taste {
+	my $class =3D shift;
+	$class->create($_) for (
+		{
+			id =3D> 1,
+			name =3D> "Britney Spears",
+		},
+		{
+			id =3D> 2,
+			name =3D> "Christina Aguilera",
+		},
+	);
+}
+
+1;
diff -Nur /Users/nothingmuch/Class-DBI-0.96/t/testlib/Participation.pm Clas=
s-DBI/t/testlib/Participation.pm
--- /Users/nothingmuch/Class-DBI-0.96/t/testlib/Participation.pm	Thu Jan  1=
 02:00:00 1970
+++ Class-DBI/t/testlib/Participation.pm	Sun Aug  8 00:11:45 2004
@@ -0,0 +1,91 @@
+package Participation;
+
+use base 'CDBase';
+use strict;
+
+__PACKAGE__->table('Participations');
+__PACKAGE__->columns('Primary', qw( artist album ));
+
+sub CONSTRUCT {
+	my $class =3D shift;
+	$class->create_movies_table;
+	$class->make_bad_taste;
+}
+
+sub create_movies_table {
+	my $class =3D shift;
+	$class->db_Main->do(
+		qq{
+	CREATE TABLE Participations (
+		artist	INTEGER,
+		album	INTEGER
+	)
+		}
+	);
+}
+
+sub make_bad_taste {
+	my $class =3D shift;
+
+	my $britney =3D Artist->retrieve(id =3D> 1);
+	my $christina =3D Artist->retrieve(id =3D> 2);
+
+	my $b =3D Album->retrieve(id =3D> 1);
+	my $c =3D Album->retrieve(id =3D> 2);
+	my $christmas_special =3D Album->retrieve(id =3D> 3);
+
+	my @rows =3D (
+		[ $britney, $b ],
+		[ $christina, $c ],
+		[ $britney, $christmas_special ],
+		[ $christina, $christmas_special ],
+	);
+
+	$class->make_these(@rows);
+}
+
+sub make_these {
+	my $class =3D shift;
+
+	foreach my $row (@_){
+		$class->create({
+			artist =3D> $row->[0]->id,
+			album =3D> $row->[1]->id,
+		});
+	};
+}
+
+package FunnyParticipation;
+
+use base 'CDBase';
+
+__PACKAGE__->table('FunnyParticipations');
+__PACKAGE__->columns('Primary', qw( starlette masterpiece ));
+
+*CONSTRUCT =3D \&Participation::CONSTRUCT;
+*make_bad_taste =3D \&Participation::make_bad_taste;
+
+sub create_movies_table {
+	my $class =3D shift;
+	$class->db_Main->do(
+		qq{
+	CREATE TABLE FunnyParticipations (
+		starlette	INTEGER,
+		masterpiece	INTEGER
+	)
+		}
+	);
+}
+
+sub make_these {
+	my $class =3D shift;
+
+	foreach my $row (@_){
+		$class->create({
+			starlette =3D> $row->[0]->id,
+			masterpiece =3D> $row->[1]->id,
+		});
+	}
+}
+
+1;

--PNTmBPCT7hxwcZjr--

--kORqDWCi7qDJ0mEj
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFBFhdRVCwRwOvSdBgRAtZLAJ4x8aYRolYiqoz7/taVVXCI2u5vuACgo3Tb
E3GDmi3eyHDPv10rEcK+GFg=
=gXJh
-----END PGP SIGNATURE-----

--kORqDWCi7qDJ0mEj--

(message missing)

My patches, complete
Yuval Kogman 12:06 on 08 Aug 2004

Re: My patches, complete
Tony Bowden 14:38 on 08 Aug 2004

Re: My patches, complete
Yuval Kogman 15:09 on 08 Aug 2004

Re: My patches, complete
Tony Bowden 16:37 on 08 Aug 2004

Re: My patches, complete
Yuval Kogman 17:24 on 08 Aug 2004

Re: My patches, complete
Tony Bowden 17:37 on 08 Aug 2004

Re: My patches, complete
Yuval Kogman 17:52 on 08 Aug 2004

Re: My patches, complete
Tony Bowden 17:57 on 08 Aug 2004

Generated at 11:34 on 01 Dec 2004 by mariachi v0.52