changes so far for new moose / mop
[catagits/Reaction.git] / lib / Reaction / UI / ViewPort / GridView / Entity.pm
1 package Reaction::UI::ViewPort::GridView::Entity;
2
3 use Reaction::Class;
4 use Catalyst::Utils;
5 use aliased 'Reaction::InterfaceModel::Object';
6 use aliased 'Reaction::UI::ViewPort::DisplayField::Text';
7 use aliased 'Reaction::UI::ViewPort::DisplayField::Number';
8 use aliased 'Reaction::UI::ViewPort::DisplayField::Boolean';
9 use aliased 'Reaction::UI::ViewPort::DisplayField::String';
10 use aliased 'Reaction::UI::ViewPort::DisplayField::DateTime';
11 use aliased 'Reaction::UI::ViewPort::DisplayField::RelatedObject';
12
13
14 class Entity is 'Reaction::UI::ViewPort', which {
15
16   has object        => (isa => Object,     is => 'ro', required => 1);
17   has field_order   => (isa => 'ArrayRef', is => 'ro', required => 1);
18
19   has fields        => (isa => 'ArrayRef', is => 'rw', lazy_build => 1);
20   has builder_cache => (isa => 'HashRef',  is => 'ro');
21   has field_args   => (isa => 'rw');
22
23   implements BUILD => as {
24     my ($self, $args) = @_;
25     my $field_args = delete $args->{Field};
26     $self->field_args( {Field => $field_args} ) if ref $field_args;
27   };
28
29   implements _build_fields => as {
30     my ($self) = @_;
31     my $obj      = $self->object;
32     my $args     = $self->has_field_args    ? $self->field_args    : {};
33     my $builders = $self->has_builder_cache ? $self->builder_cache : {};
34     my @cells;
35     for my $field (@{ $self->field_order }) {
36       my $attr = $obj->meta->find_attribute_by_name($field);
37       my $build_meth = $builders->{$field} ||= $self->get_builder_for($attr);
38       my $loc = join('-', $self->location, 'field', $attr->name);
39       my $vp_args = {Field => { $attr->name => {location => $loc} } };
40       my $merged  = Catalyst::Utils::merge_hashes($args, $vp_args);
41       my $cell = $self->$build_meth($obj, $attr, $merged);
42       #XXX add a blank VP if !$cell here to mantain grid integrity
43       push(@cells, $cell) if $cell;
44     }
45     return \@cells;
46   };
47
48   implements get_builder_for => as {
49     my ($self, $attr) = @_;
50     my $attr_name = $attr->name;
51     my $builder = "_build_fields_for_name_${attr_name}";
52     return $builder if $self->can($builder);
53     if ($attr->has_type_constraint) {
54       my $constraint = $attr->type_constraint;
55       my $base_name = $constraint->name;
56       my $tried_isa = 0;
57     CONSTRAINT: while (defined($constraint)) {
58         my $name = $constraint->name;
59         $name = $attr->_isa_metadata if($name eq '__ANON__');
60         if (eval { $name->can('meta') } && !$tried_isa++) {
61           foreach my $class ($name->meta->class_precedence_list) {
62             my $mangled_name = $class;
63             $mangled_name =~ s/:+/_/g;
64             my $builder = "_build_fields_for_type_${mangled_name}";
65             return $builder if $self->can($builder);
66           }
67         }
68         if (defined($name)) {
69           unless (defined($base_name)) {
70             $base_name = "(anon subtype of ${name})";
71           }
72           my $mangled_name = $name;
73           $mangled_name =~ s/:+/_/g;
74           my $builder = "_build_fields_for_type_${mangled_name}";
75           return $builder if $self->can($builder);
76         }
77         $constraint = $constraint->parent;
78       }
79       if (!defined($constraint)) {
80         confess "Can't build field ${attr_name} of type ${base_name} without $builder method or _build_fields_for_type_<type> method for type or any supertype";
81       }
82     } else {
83       confess "Can't build field ${attr} without $builder method or type constraint";
84     }
85   };
86
87
88   implements _build_simple_field => as {
89     my ($self, $class, $obj, $attr, $args) = @_;
90     my $attr_name = $attr->name;
91     my %extra;
92     if (my $config = $args->{Field}{$attr_name}) {
93       %extra = %$config;
94     }
95
96     return $class->new(
97                        object => $obj,
98                        attribute => $attr,
99                        name => $attr->name,
100                        ctx => $self->ctx,
101                        %extra
102                       );
103   };
104
105   implements _build_fields_for_type_Num => as {
106     my ($self, $obj, $attr, $args) = @_;
107     $args->{Field}{$attr->name}{layout} = 'value/number'
108       unless( exists  $args->{Field}{$attr->name}         &&
109               exists  $args->{Field}{$attr->name}{layout} &&
110               defined $args->{Field}{$attr->name}{layout}
111             );
112     return $self->_build_simple_field(Number, $obj, $attr, $args);
113   };
114
115   implements _build_fields_for_type_Int => as {
116     my ($self, $obj, $attr, $args) = @_;
117     $args->{Field}{$attr->name}{layout} = 'value/number'
118       unless( exists  $args->{Field}{$attr->name}         &&
119               exists  $args->{Field}{$attr->name}{layout} &&
120               defined $args->{Field}{$attr->name}{layout}
121             );
122     return $self->_build_simple_field(Number, $obj, $attr, $args);
123   };
124
125   implements _build_fields_for_type_Bool => as {
126     my ($self, $obj, $attr, $args) = @_;
127     $args->{Field}{$attr->name}{layout} = 'value/boolean'
128       unless( exists  $args->{Field}{$attr->name}         &&
129               exists  $args->{Field}{$attr->name}{layout} &&
130               defined $args->{Field}{$attr->name}{layout}
131             );
132     return $self->_build_simple_field(Boolean, $obj, $attr, $args);
133   };
134
135   implements _build_fields_for_type_Password => as { return };
136
137   implements _build_fields_for_type_Str => as {
138     my ($self, $obj, $attr, $args) = @_;
139     $args->{Field}{$attr->name}{layout} = 'value/string'
140       unless( exists  $args->{Field}{$attr->name}         &&
141               exists  $args->{Field}{$attr->name}{layout} &&
142               defined $args->{Field}{$attr->name}{layout}
143             );
144     return $self->_build_simple_field(String, $obj, $attr, $args);
145   };
146
147   implements _build_fields_for_type_SimpleStr => as {
148     my ($self, $obj, $attr, $args) = @_;
149     $args->{Field}{$attr->name}{layout} = 'value/string'
150       unless( exists  $args->{Field}{$attr->name}         &&
151               exists  $args->{Field}{$attr->name}{layout} &&
152               defined $args->{Field}{$attr->name}{layout}
153             );
154     return $self->_build_simple_field(String, $obj, $attr, $args);
155   };
156
157   implements _build_fields_for_type_DateTime => as {
158     my ($self, $obj, $attr, $args) = @_;
159     $args->{Field}{$attr->name}{layout} = 'value/date_time'
160       unless( exists  $args->{Field}{$attr->name}         &&
161               exists  $args->{Field}{$attr->name}{layout} &&
162               defined $args->{Field}{$attr->name}{layout}
163             );
164     return $self->_build_simple_field(DateTime, $obj, $attr, $args);
165   };
166
167   implements _build_fields_for_type_Enum => as {
168     my ($self, $obj, $attr, $args) = @_;
169     $args->{Field}{$attr->name}{layout} = 'value/string'
170       unless( exists  $args->{Field}{$attr->name}         &&
171               exists  $args->{Field}{$attr->name}{layout} &&
172               defined $args->{Field}{$attr->name}{layout}
173             );
174     return $self->_build_simple_field(String, $obj, $attr, $args);
175   };
176 };
177
178 1;