Move code having to do with adding columns out of ColumnCase.
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / CDBICompat / ColumnGroups.pm
1 package # hide from PAUSE
2     DBIx::Class::CDBICompat::ColumnGroups;
3
4 use strict;
5 use warnings;
6
7 use Storable 'dclone';
8
9 use base qw/DBIx::Class::Row/;
10
11 __PACKAGE__->mk_classdata('_column_groups' => { });
12
13 sub columns {
14   my $proto = shift;
15   my $class = ref $proto || $proto;
16   my $group = shift || "All";
17   $class->_init_result_source_instance();
18
19   $class->_add_column_group($group => @_) if @_;
20   return $class->all_columns    if $group eq "All";
21   return $class->primary_column if $group eq "Primary";
22   return keys %{$class->_column_groups->{$group}};
23 }
24
25 sub _add_column_group {
26   my ($class, $group, @cols) = @_;
27   $class->mk_group_accessors(column => @cols);
28   $class->add_columns(@cols);
29   $class->_register_column_group($group => @cols);
30 }
31
32 sub add_columns {
33   my ($class, @cols) = @_;
34   $class->result_source_instance->add_columns(@cols);
35 }
36
37 sub _register_column_group {
38   my ($class, $group, @cols) = @_;
39
40   # Must do a complete deep copy else column groups
41   # might accidentally be shared.
42   my $groups = dclone $class->_column_groups;
43
44   if ($group eq 'Primary') {
45     $class->set_primary_key(@cols);
46     $groups->{'Essential'}{$_} ||= 1 for @cols;
47   }
48
49   if ($group eq 'All') {
50     unless (exists $class->_column_groups->{'Primary'}) {
51       $groups->{'Primary'}{$cols[0]} = 1;
52       $class->set_primary_key($cols[0]);
53     }
54     unless (exists $class->_column_groups->{'Essential'}) {
55       $groups->{'Essential'}{$cols[0]} = 1;
56     }
57   }
58
59   $groups->{$group}{$_} ||= 1 for @cols;
60
61   $class->_column_groups($groups);
62 }
63
64 # CDBI will never overwrite an accessor, but it only uses one
65 # accessor for all column types.  DBIC uses many different
66 # accessor types so, for example, if you declare a column()
67 # and then a has_a() for that same column it must overwrite.
68 #
69 # To make this work CDBICompat has decide if an accessor
70 # method was put there by itself and only then overwrite.
71 {
72   my %our_accessors;
73
74   sub _has_custom_accessor {
75     my($class, $name) = @_;
76     
77     no strict 'refs';
78     my $existing_accessor = *{$class .'::'. $name}{CODE};
79     return $existing_accessor && !$our_accessors{$existing_accessor};
80   }
81
82   sub _deploy_accessor {
83     my($class, $name, $accessor) = @_;
84
85     return if $class->_has_custom_accessor($name);
86
87     {
88       no strict 'refs';
89       no warnings 'redefine';
90       *{$class .'::'. $name} = $accessor;
91     }
92     
93     $our_accessors{$accessor}++;
94
95     return 1;
96   }
97 }
98
99 sub _mk_group_accessors {
100   my ($class, $type, $group, @fields) = @_;
101
102   # So we don't have to do lots of lookups inside the loop.
103   my $maker = $class->can($type) unless ref $type;
104
105   # warn "$class $type $group\n";
106   foreach my $field (@fields) {
107     if( $field eq 'DESTROY' ) {
108         carp("Having a data accessor named DESTROY in ".
109              "'$class' is unwise.");
110     }
111
112     my $name = $field;
113
114     ($name, $field) = @$field if ref $field;
115
116     my $accessor = $class->$maker($group, $field);
117     my $alias = "_${name}_accessor";
118
119     # warn "  $field $alias\n";
120     {
121       no strict 'refs';
122       
123       $class->_deploy_accessor($name,  $accessor);
124       $class->_deploy_accessor($alias, $accessor);
125     }
126   }
127 }
128
129 sub all_columns { return shift->result_source_instance->columns; }
130
131 sub primary_column {
132   my ($class) = @_;
133   my @pri = $class->primary_columns;
134   return wantarray ? @pri : $pri[0];
135 }
136
137 sub _essential {
138     return shift->columns("Essential");
139 }
140
141 sub find_column {
142   my ($class, $col) = @_;
143   return $col if $class->has_column($col);
144 }
145
146 sub __grouper {
147   my ($class) = @_;
148   my $grouper = { class => $class };
149   return bless($grouper, 'DBIx::Class::CDBICompat::ColumnGroups::GrouperShim');
150 }
151
152 sub _find_columns {
153   my ($class, @col) = @_;
154   return map { $class->find_column($_) } @col;
155 }
156
157 package DBIx::Class::CDBICompat::ColumnGroups::GrouperShim;
158
159 sub groups_for {
160   my ($self, @cols) = @_;
161   my %groups;
162   foreach my $col (@cols) {
163     foreach my $group (keys %{$self->{class}->_column_groups}) {
164       $groups{$group} = 1 if $self->{class}->_column_groups->{$group}->{$col};
165     }
166   }
167   return keys %groups;
168 }
169     
170
171 1;