X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FCDBICompat%2FColumnGroups.pm;h=44a60107b7e0bf194488f06145d1fb2dc688cef0;hb=1e0233457be7f60bc0a35a4913eecd7f7b7b15e8;hp=77d6112a35fd25b6007f35ab2cb7acfb53bb28f2;hpb=7fb16f1a1bf1e7de4098b4f4ac3d061312f6bac3;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/CDBICompat/ColumnGroups.pm b/lib/DBIx/Class/CDBICompat/ColumnGroups.pm index 77d6112..44a6010 100644 --- a/lib/DBIx/Class/CDBICompat/ColumnGroups.pm +++ b/lib/DBIx/Class/CDBICompat/ColumnGroups.pm @@ -1,7 +1,10 @@ -package DBIx::Class::CDBICompat::ColumnGroups; +package # hide from PAUSE + DBIx::Class::CDBICompat::ColumnGroups; use strict; use warnings; +use Sub::Name (); +use List::Util (); use base qw/DBIx::Class::Row/; @@ -11,44 +14,143 @@ sub columns { my $proto = shift; my $class = ref $proto || $proto; my $group = shift || "All"; + $class->_init_result_source_instance(); + $class->_add_column_group($group => @_) if @_; return $class->all_columns if $group eq "All"; return $class->primary_column if $group eq "Primary"; - return keys %{$class->_column_groups->{$group}}; + + my $grp = $class->_column_groups->{$group}; + my @grp_cols = sort { $grp->{$b} <=> $grp->{$a} } (keys %$grp); + return @grp_cols; } sub _add_column_group { my ($class, $group, @cols) = @_; + $class->mk_group_accessors(column => @cols); $class->add_columns(@cols); $class->_register_column_group($group => @cols); } +sub add_columns { + my ($class, @cols) = @_; + $class->result_source_instance->add_columns(@cols); +} + sub _register_column_group { my ($class, $group, @cols) = @_; - my $groups = { %{$class->_column_groups} }; + # Must do a complete deep copy else column groups + # might accidentally be shared. + my $groups = DBIx::Class::_Util::deep_clone( $class->_column_groups ); if ($group eq 'Primary') { $class->set_primary_key(@cols); - $groups->{'Essential'}{$_} ||= {} for @cols; + delete $groups->{'Essential'}{$_} for @cols; + my $first = List::Util::max(values %{$groups->{'Essential'}}); + $groups->{'Essential'}{$_} = ++$first for reverse @cols; } if ($group eq 'All') { unless (exists $class->_column_groups->{'Primary'}) { - $groups->{'Primary'}{$cols[0]} = {}; + $groups->{'Primary'}{$cols[0]} = 1; $class->set_primary_key($cols[0]); } unless (exists $class->_column_groups->{'Essential'}) { - $groups->{'Essential'}{$cols[0]} = {}; + $groups->{'Essential'}{$cols[0]} = 1; } } - $groups->{$group}{$_} ||= {} for @cols; + delete $groups->{$group}{$_} for @cols; + my $first = List::Util::max(values %{$groups->{$group}}); + $groups->{$group}{$_} = ++$first for reverse @cols; $class->_column_groups($groups); } -sub all_columns { return shift->result_source->columns; } +# CDBI will never overwrite an accessor, but it only uses one +# accessor for all column types. DBIC uses many different +# accessor types so, for example, if you declare a column() +# and then a has_a() for that same column it must overwrite. +# +# To make this work CDBICompat has decide if an accessor +# method was put there by itself and only then overwrite. +{ + my %our_accessors; + + sub _has_custom_accessor { + my($class, $name) = @_; + + no strict 'refs'; + my $existing_accessor = *{$class .'::'. $name}{CODE}; + + return( + defined $existing_accessor + and + ! $our_accessors{$existing_accessor} + and + # under 5.8 mro the CODE slot may simply be a "cached method" + ! ( + DBIx::Class::_ENV_::OLD_MRO + and + grep { + $_ ne $class + and + ($_->can($name)||0) == $existing_accessor + } @{mro::get_linear_isa($class)} + ) + ) + } + + sub _deploy_accessor { + my($class, $name, $accessor) = @_; + + return if $class->_has_custom_accessor($name); + + { + no strict 'refs'; + no warnings 'redefine'; + my $fullname = join '::', $class, $name; + *$fullname = Sub::Name::subname $fullname, $accessor; + } + + $our_accessors{$accessor}++; + + return 1; + } +} + +sub _mk_group_accessors { + my ($class, $type, $group, @fields) = @_; + + # So we don't have to do lots of lookups inside the loop. + my $maker = $class->can($type) unless ref $type; + + # warn "$class $type $group\n"; + foreach my $field (@fields) { + if( $field eq 'DESTROY' ) { + carp("Having a data accessor named DESTROY in ". + "'$class' is unwise."); + } + + my $name = $field; + + ($name, $field) = @$field if ref $field; + + my $accessor = $class->$maker($group, $field); + my $alias = "_${name}_accessor"; + + # warn " $field $alias\n"; + { + no strict 'refs'; + + $class->_deploy_accessor($name, $accessor); + $class->_deploy_accessor($alias, $accessor); + } + } +} + +sub all_columns { return shift->result_source_instance->columns; } sub primary_column { my ($class) = @_; @@ -56,6 +158,10 @@ sub primary_column { return wantarray ? @pri : $pri[0]; } +sub _essential { + return shift->columns("Essential"); +} + sub find_column { my ($class, $col) = @_; return $col if $class->has_column($col); @@ -63,7 +169,8 @@ sub find_column { sub __grouper { my ($class) = @_; - return bless({ class => $class}, 'DBIx::Class::CDBICompat::ColumnGroups::GrouperShim'); + my $grouper = { class => $class }; + return bless($grouper, 'DBIx::Class::CDBICompat::ColumnGroups::GrouperShim'); } sub _find_columns { @@ -71,7 +178,8 @@ sub _find_columns { return map { $class->find_column($_) } @col; } -package DBIx::Class::CDBICompat::ColumnGroups::GrouperShim; +package # hide from PAUSE (should be harmless, no POD no Version) + DBIx::Class::CDBICompat::ColumnGroups::GrouperShim; sub groups_for { my ($self, @cols) = @_; @@ -83,6 +191,5 @@ sub groups_for { } return keys %groups; } - 1;