[Dbix-class] Re: Useful patch to DBIx::Class::Schema::Loader
Ben Tilly
btilly at gmail.com
Sat May 28 00:26:50 GMT 2011
For some reason the sent patch did not include the unit test. Also,
being named ".patch", it got scrubbed.
I'm resending it with a filename to let it know that it is in fact
plain text. And this version of the patch includes the unit test
changes.
Sorry.
On Thu, May 26, 2011 at 8:35 PM, Ben Tilly <btilly at gmail.com> wrote:
> I'm looking for feedback on a patch to DBIx::Class::Schema::Loader.
> If people like it, I would like to see this added to the next release.
>
> The patch adds a relationship_name_map argument to
> DBIx::Class::Schema::Loader::Base to give people control over what
> names will be given to relationships. =A0A comment in the source
> suggests that this was supposed to be implemented, but that never
> seems to have happened.
>
> As implemented, this argument can be a coderef or a hashref. =A0If it is
> a hashref, you can do things like:
>
> =A0{
> =A0 =A0 =A0bar =3D> "baz",
> =A0 =A0 =A0Foo =3D> {
> =A0 =A0 =A0 =A0 =A0bar =3D> "blat",
> =A0 =A0 =A0},
> =A0}
>
> which will cause relationships that would have been called bar to be
> called baz, except in class Foo where it will be called blat instead.
>
> If a coderef you are given the moniker for the class being built, the
> name of the relationship accessor, and then a hashref with more
> detailed information.
>
> This patch includes documentation and unit tests.
>
-------------- next part --------------
diff -ru DBIx-Class-Schema-Loader.old/blib/lib/DBIx/Class/Schema/Loader/Bas=
e.pm DBIx-Class-Schema-Loader/blib/lib/DBIx/Class/Schema/Loader/Base.pm
--- DBIx-Class-Schema-Loader.old/blib/lib/DBIx/Class/Schema/Loader/Base.pm =
2011-05-25 06:14:57.000000000 -0700
+++ DBIx-Class-Schema-Loader/blib/lib/DBIx/Class/Schema/Loader/Base.pm 2011=
-05-25 10:32:09.000000000 -0700
@@ -87,6 +87,7 @@
preserve_case
col_collision_map
rel_collision_map
+ relationship_name_map
real_dump_directory
result_component_map
datetime_undef_if_invalid
@@ -327,6 +328,43 @@
column_info =3D> hashref of column info (data_type, is_nullable,=
etc),
}
=
+=3Dhead2 relationship_name_map
+
+Similar in idea to moniker_map, but different in the details. It can be
+a hashref or a code ref.
+
+If it is a hashref, keys can be either the default relationship name, or t=
he
+moniker. The keys that are the default relationship name should map to the
+name of the key. Keys that are monikers should map to hashes mapping
+relationship names to their translation. You can do both at once, and the
+more specific moniker version will be picked up first. So, for instance,
+you could have
+
+ {
+ bar =3D> "baz",
+ Foo =3D> {
+ bar =3D> "blat",
+ },
+ }
+
+and relationships that would have been named C<bar> will now be named C<ba=
z>
+except that in the table whose moniker is C<Foo> it will be named C<blat>.
+
+If it is a coderef, the arguments passed will be
+
+ name of the DBIC class we are building,
+ default relationship name that DBICSL would ordinarily give this colum=
n,
+ {
+ name =3D> default relationship name,
+ type =3D> the relationship type eg: C<has_many>,
+ this_moniker =3D> name of the DBIC class we are building,
+ this_columns =3D> columns in this table in the relationship,
+ that_moniker =3D> name of the DBIC class we are related to,
+ that_columns =3D> columns in the other table in the relationship,
+ }
+
+DBICSL will try to use the value returned as the relationship name.
+
=3Dhead2 inflect_plural
=
Just like L</moniker_map> above (can be hash/code-ref, falls back to defau=
lt
@@ -1703,8 +1741,7 @@
}
}
=
-# use the same logic to run moniker_map, col_accessor_map, and
-# relationship_name_map
+# use the same logic to run moniker_map and col_accessor_map
sub _run_user_map {
my ( $self, $map, $default_code, $ident, @extra ) =3D @_;
=
diff -ru DBIx-Class-Schema-Loader.old/blib/lib/DBIx/Class/Schema/Loader/Rel=
Builder.pm DBIx-Class-Schema-Loader/blib/lib/DBIx/Class/Schema/Loader/RelBu=
ilder.pm
--- DBIx-Class-Schema-Loader.old/blib/lib/DBIx/Class/Schema/Loader/RelBuild=
er.pm 2011-05-25 06:14:57.000000000 -0700
+++ DBIx-Class-Schema-Loader/blib/lib/DBIx/Class/Schema/Loader/RelBuilder.p=
m 2011-05-25 10:33:56.000000000 -0700
@@ -89,6 +89,7 @@
inflect_singular
relationship_attrs
rel_collision_map
+ relationship_name_map
_temp_classes
/);
=
@@ -105,13 +106,14 @@
# are better documented in L<DBIx::Class::Schema::Loader::Base>.
=
my $self =3D {
- base =3D> $base,
- schema =3D> $base->schema,
- inflect_plural =3D> $base->inflect_plural,
- inflect_singular =3D> $base->inflect_singular,
- relationship_attrs =3D> $base->relationship_attrs,
- rel_collision_map =3D> $base->rel_collision_map,
- _temp_classes =3D> [],
+ base =3D> $base,
+ schema =3D> $base->schema,
+ inflect_plural =3D> $base->inflect_plural,
+ inflect_singular =3D> $base->inflect_singular,
+ relationship_attrs =3D> $base->relationship_attrs,
+ rel_collision_map =3D> $base->rel_collision_map,
+ relationship_name_map =3D> $base->relationship_name_map,
+ _temp_classes =3D> [],
};
=
weaken $self->{base}; #< don't leak
@@ -460,7 +462,57 @@
}
}
=
- return ( $local_relname, $remote_relname, $remote_method );
+ # And let the user remap these names
+ my $local_info =3D {
+ name =3D> $local_relname,
+ type =3D> $remote_method,
+ this_moniker =3D> $remote_moniker,
+ that_moniker =3D> $local_moniker,
+ this_columns =3D> $rel->{remote_columns},
+ that_columns =3D> $rel->{local_columns},
+ };
+
+ my $remote_info =3D {
+ name =3D> $remote_relname,
+ type =3D> 'belongs_to',
+ this_moniker =3D> $local_moniker,
+ that_moniker =3D> $remote_moniker,
+ this_columns =3D> $rel->{local_columns},
+ that_columns =3D> $rel->{remote_columns},
+ };
+
+ my $map =3D $self->relationship_name_map;
+ if (not defined($map)) {
+ # We don't try to map the map.
+ }
+ elsif ('HASH' eq ref($map)) {
+ for my $info ($local_info, $remote_info) {
+ my $name =3D $info->{name};
+ my $type =3D $info->{type};
+ if ($map->{$type} and 'HASH' eq ref($map->{$type})
+ and $map->{$type}{$name}
+ ) {
+ $info->{name} =3D $map->{$type}{$name};
+ }
+ elsif ($map->{$name} and not 'HASH' eq ref($map->{$name})) {
+ $info->{name} =3D $map->{$name};
+ }
+ }
+ }
+ elsif ('CODE' eq ref($map)) {
+ for my $info ($local_info, $remote_info) {
+ $info->{name} =3D $map->(@$info{'this_moniker', 'name'}, $info=
);
+ }
+ }
+ else {
+ warn <<"EOF";
+Skipping relationship_name_map '$map' because it is not a hashref or coder=
ef.
+See "relationship_name_map" in perldoc DBIx::Class::Schema::Loader::Base
+for the correct usage.
+EOF
+ }
+
+ return ( $local_info->{name}, $remote_info->{name}, $remote_method );
}
=
sub cleanup {
diff -ru DBIx-Class-Schema-Loader.old/lib/DBIx/Class/Schema/Loader/Base.pm =
DBIx-Class-Schema-Loader/lib/DBIx/Class/Schema/Loader/Base.pm
--- DBIx-Class-Schema-Loader.old/lib/DBIx/Class/Schema/Loader/Base.pm 2011-=
05-25 06:14:57.000000000 -0700
+++ DBIx-Class-Schema-Loader/lib/DBIx/Class/Schema/Loader/Base.pm 2011-05-2=
5 11:16:49.000000000 -0700
@@ -87,6 +87,7 @@
preserve_case
col_collision_map
rel_collision_map
+ relationship_name_map
real_dump_directory
result_component_map
datetime_undef_if_invalid
@@ -327,6 +328,43 @@
column_info =3D> hashref of column info (data_type, is_nullable,=
etc),
}
=
+=3Dhead2 relationship_name_map
+
+Similar in idea to moniker_map, but different in the details. It can be
+a hashref or a code ref.
+
+If it is a hashref, keys can be either the default relationship name, or t=
he
+moniker. The keys that are the default relationship name should map to the
+name of the key. Keys that are monikers should map to hashes mapping
+relationship names to their translation. You can do both at once, and the
+more specific moniker version will be picked up first. So, for instance,
+you could have
+
+ {
+ bar =3D> "baz",
+ Foo =3D> {
+ bar =3D> "blat",
+ },
+ }
+
+and relationships that would have been named C<bar> will now be named C<ba=
z>
+except that in the table whose moniker is C<Foo> it will be named C<blat>.
+
+If it is a coderef, the arguments passed will be
+
+ name of the DBIC class we are building,
+ default relationship name that DBICSL would ordinarily give this colum=
n,
+ {
+ name =3D> default relationship name,
+ type =3D> the relationship type eg: C<has_many>,
+ this_moniker =3D> name of the DBIC class we are building,
+ this_columns =3D> columns in this table in the relationship,
+ that_moniker =3D> name of the DBIC class we are related to,
+ that_columns =3D> columns in the other table in the relationship,
+ }
+
+DBICSL will try to use the value returned as the relationship name.
+
=3Dhead2 inflect_plural
=
Just like L</moniker_map> above (can be hash/code-ref, falls back to defau=
lt
@@ -1703,8 +1741,7 @@
}
}
=
-# use the same logic to run moniker_map, col_accessor_map, and
-# relationship_name_map
+# use the same logic to run moniker_map and col_accessor_map
sub _run_user_map {
my ( $self, $map, $default_code, $ident, @extra ) =3D @_;
=
diff -ru DBIx-Class-Schema-Loader.old/lib/DBIx/Class/Schema/Loader/RelBuild=
er.pm DBIx-Class-Schema-Loader/lib/DBIx/Class/Schema/Loader/RelBuilder.pm
--- DBIx-Class-Schema-Loader.old/lib/DBIx/Class/Schema/Loader/RelBuilder.pm=
2011-05-25 06:14:57.000000000 -0700
+++ DBIx-Class-Schema-Loader/lib/DBIx/Class/Schema/Loader/RelBuilder.pm 201=
1-05-25 11:16:59.000000000 -0700
@@ -89,6 +89,7 @@
inflect_singular
relationship_attrs
rel_collision_map
+ relationship_name_map
_temp_classes
/);
=
@@ -105,13 +106,14 @@
# are better documented in L<DBIx::Class::Schema::Loader::Base>.
=
my $self =3D {
- base =3D> $base,
- schema =3D> $base->schema,
- inflect_plural =3D> $base->inflect_plural,
- inflect_singular =3D> $base->inflect_singular,
- relationship_attrs =3D> $base->relationship_attrs,
- rel_collision_map =3D> $base->rel_collision_map,
- _temp_classes =3D> [],
+ base =3D> $base,
+ schema =3D> $base->schema,
+ inflect_plural =3D> $base->inflect_plural,
+ inflect_singular =3D> $base->inflect_singular,
+ relationship_attrs =3D> $base->relationship_attrs,
+ rel_collision_map =3D> $base->rel_collision_map,
+ relationship_name_map =3D> $base->relationship_name_map,
+ _temp_classes =3D> [],
};
=
weaken $self->{base}; #< don't leak
@@ -460,7 +462,57 @@
}
}
=
- return ( $local_relname, $remote_relname, $remote_method );
+ # And let the user remap these names
+ my $local_info =3D {
+ name =3D> $local_relname,
+ type =3D> $remote_method,
+ this_moniker =3D> $remote_moniker,
+ that_moniker =3D> $local_moniker,
+ this_columns =3D> $rel->{remote_columns},
+ that_columns =3D> $rel->{local_columns},
+ };
+
+ my $remote_info =3D {
+ name =3D> $remote_relname,
+ type =3D> 'belongs_to',
+ this_moniker =3D> $local_moniker,
+ that_moniker =3D> $remote_moniker,
+ this_columns =3D> $rel->{local_columns},
+ that_columns =3D> $rel->{remote_columns},
+ };
+
+ my $map =3D $self->relationship_name_map;
+ if (not defined($map)) {
+ # With no map we have nothing to do.
+ }
+ elsif ('HASH' eq ref($map)) {
+ for my $info ($local_info, $remote_info) {
+ my $name =3D $info->{name};
+ my $moniker =3D $info->{this_moniker};
+ if ($map->{$moniker} and 'HASH' eq ref($map->{$moniker})
+ and $map->{$moniker}{$name}
+ ) {
+ $info->{name} =3D $map->{$moniker}{$name};
+ }
+ elsif ($map->{$name} and not 'HASH' eq ref($map->{$name})) {
+ $info->{name} =3D $map->{$name};
+ }
+ }
+ }
+ elsif ('CODE' eq ref($map)) {
+ for my $info ($local_info, $remote_info) {
+ $info->{name} =3D $map->(@$info{'this_moniker', 'name'}, $info=
);
+ }
+ }
+ else {
+ warn <<"EOF";
+Skipping relationship_name_map '$map' because it is not a hashref or coder=
ef.
+See "relationship_name_map" in perldoc DBIx::Class::Schema::Loader::Base
+for the correct usage.
+EOF
+ }
+
+ return ( $local_info->{name}, $remote_info->{name}, $remote_method );
}
=
sub cleanup {
diff -ru DBIx-Class-Schema-Loader.old/t/45relationships.t DBIx-Class-Schema=
-Loader/t/45relationships.t
--- DBIx-Class-Schema-Loader.old/t/45relationships.t 2011-05-25 06:14:57.00=
0000000 -0700
+++ DBIx-Class-Schema-Loader/t/45relationships.t 2011-05-25 11:16:24.000000=
000 -0700
@@ -1,5 +1,5 @@
use strict;
-use Test::More tests =3D> 6;
+use Test::More tests =3D> 14;
use Test::Exception;
use lib qw(t/lib);
use make_dbictest_db;
@@ -16,6 +16,79 @@
'skip_relationships blocks generation of fooref rel',
);
=
+# test hashref as relationship_name_map
+my $hash_relationship =3D schema_with(
+ relationship_name_map =3D> {
+ fooref =3D> "got_fooref",
+ bars =3D> "ignored",
+ Foo =3D> {
+ bars =3D> "got_bars",
+ fooref =3D> "ignored",
+ },
+ }
+);
+is( ref($hash_relationship->source('Foo')->relationship_info('got_bars')),
+ 'HASH',
+ 'single level hash in relationship_name_map picked up correctly'
+ );
+is( ref($hash_relationship->source('Bar')->relationship_info('got_fooref')=
),
+ 'HASH',
+ 'double level hash in relationship_name_map picked up correctly'
+ );
+
+# test coderef as relationship_name_map
+my $code_relationship =3D schema_with(
+ relationship_name_map =3D> sub {
+ my ($moniker, $name, $args) =3D @_;
+
+ if ($moniker eq 'Foo') {
+ is ($name, 'bars', 'correct second argument for Foo passed');
+ is_deeply(
+ $args,
+ {
+ name =3D> 'bars',
+ type =3D> 'has_many',
+ this_moniker =3D> 'Foo',
+ this_columns =3D> ['fooid'],
+ that_moniker =3D> 'Bar',
+ that_columns =3D> ['fooref'],
+ },
+ 'correct args for Foo passed'
+ );
+ return 'bars_caught';
+ }
+ elsif ($moniker =3D 'Bar') {
+ is ($name, 'fooref', 'correct second argument for Bar passed');
+ is_deeply(
+ $args,
+ {
+ name =3D> 'fooref',
+ type =3D> 'belongs_to',
+ this_moniker =3D> 'Bar',
+ this_columns =3D> ['fooref'],
+ that_moniker =3D> 'Foo',
+ that_columns =3D> ['fooid'],
+ },
+ 'correct args for Foo passed'
+ );
+ =
+ return 'fooref_caught';
+ }
+ else {
+ die "Bad moniker '$moniker'";
+ }
+ }
+ );
+is( ref($code_relationship->source('Foo')->relationship_info('bars_caught'=
)),
+ 'HASH',
+ 'relationship_name_map overrode local_info correctly'
+ );
+is( ref($code_relationship->source('Bar')->relationship_info('fooref_caugh=
t')),
+ 'HASH',
+ 'relationship_name_map overrode remote_info correctly'
+ );
+
+
=
# test relationship_attrs
throws_ok {
More information about the DBIx-Class
mailing list