Commit | Line | Data |
7adfd53f |
1 | package Reaction::UI::ViewPort::ObjectView; |
2 | |
3 | use Reaction::Class; |
4 | |
5 | use aliased 'Reaction::UI::ViewPort::DisplayField::Text'; |
6 | use aliased 'Reaction::UI::ViewPort::DisplayField::Number'; |
7 | use aliased 'Reaction::UI::ViewPort::DisplayField::Boolean'; |
8 | use aliased 'Reaction::UI::ViewPort::DisplayField::String'; |
9 | use aliased 'Reaction::UI::ViewPort::DisplayField::DateTime'; |
10 | use aliased 'Reaction::UI::ViewPort::DisplayField::RelatedObject'; |
11 | use aliased 'Reaction::UI::ViewPort::DisplayField::List'; |
12 | use aliased 'Reaction::UI::ViewPort::DisplayField::Collection'; |
e22de101 |
13 | use aliased 'Reaction::InterfaceModel::Object'; |
14 | |
7adfd53f |
15 | |
16 | class ObjectView is 'Reaction::UI::ViewPort', which { |
e22de101 |
17 | has object => (isa => Object, is => 'ro', required => 1); |
18 | has ordered_fields => (is => 'rw', isa => 'ArrayRef', lazy_build => 1); |
7adfd53f |
19 | |
7adfd53f |
20 | has _field_map => ( |
7b78a39d |
21 | isa => 'HashRef', is => 'rw', init_arg => 'fields', lazy_build => 1, |
7adfd53f |
22 | ); |
23 | |
24 | has exclude_fields => |
25 | ( is => 'rw', isa => 'ArrayRef', required => 1, default => sub{ [] } ); |
26 | |
e22de101 |
27 | |
7b78a39d |
28 | |
7b78a39d |
29 | implements fields => as { shift->_field_map }; |
7adfd53f |
30 | |
31 | implements BUILD => as { |
32 | my ($self, $args) = @_; |
33 | unless ($self->_has_field_map) { |
34 | my @field_map; |
35 | my $object = $self->object; |
36 | my %excluded = map{$_ => 1} @{$self->exclude_fields}; |
37 | for my $attr (grep { !$excluded{$_->name} } $object->parameter_attributes) { |
89939ff9 |
38 | push(@field_map, $self->_build_fields_for($attr => $args)); |
7adfd53f |
39 | } |
40 | |
6ab43711 |
41 | my %field_map = @field_map; |
42 | $self->_field_map( \%field_map ); |
7adfd53f |
43 | } |
44 | }; |
45 | |
89939ff9 |
46 | implements _build_fields_for => as { |
7adfd53f |
47 | my ($self, $attr, $args) = @_; |
48 | my $attr_name = $attr->name; |
89939ff9 |
49 | my $builder = "_build_fields_for_name_${attr_name}"; |
7adfd53f |
50 | my @fields; |
51 | if ($self->can($builder)) { |
52 | @fields = $self->$builder($attr, $args); # re-use coderef from can() |
53 | } elsif ($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; |
de48f4e6 |
59 | $name = $attr->_isa_metadata if($name eq '__ANON__'); |
7adfd53f |
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; |
89939ff9 |
64 | my $builder = "_build_fields_for_type_${mangled_name}"; |
7adfd53f |
65 | if ($self->can($builder)) { |
66 | @fields = $self->$builder($attr, $args); |
67 | last CONSTRAINT; |
68 | } |
69 | } |
70 | } |
71 | if (defined($name)) { |
72 | unless (defined($base_name)) { |
73 | $base_name = "(anon subtype of ${name})"; |
74 | } |
75 | my $mangled_name = $name; |
76 | $mangled_name =~ s/:+/_/g; |
89939ff9 |
77 | my $builder = "_build_fields_for_type_${mangled_name}"; |
7adfd53f |
78 | if ($self->can($builder)) { |
79 | @fields = $self->$builder($attr, $args); |
80 | last CONSTRAINT; |
81 | } |
82 | } |
83 | $constraint = $constraint->parent; |
84 | } |
85 | if (!defined($constraint)) { |
89939ff9 |
86 | 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"; |
7adfd53f |
87 | } |
88 | } else { |
89 | confess "Can't build field ${attr} without $builder method or type constraint"; |
90 | } |
91 | return @fields; |
92 | }; |
93 | |
7b78a39d |
94 | implements _build_field_map => as { |
7adfd53f |
95 | confess "Lazy field map building not supported by default"; |
96 | }; |
97 | |
89939ff9 |
98 | implements _build_ordered_fields => as { |
7b78a39d |
99 | my $self = shift; |
6ab43711 |
100 | my $ordered = $self->sort_by_spec($self->column_order, [keys %{$self->_field_map}]); |
101 | return [@{$self->_field_map}{@$ordered}]; |
7b78a39d |
102 | }; |
103 | |
89939ff9 |
104 | implements _build_simple_field => as { |
7adfd53f |
105 | my ($self, $class, $attr, $args) = @_; |
106 | my $attr_name = $attr->name; |
107 | my %extra; |
108 | if (my $config = $args->{Field}{$attr_name}) { |
109 | %extra = %$config; |
110 | } |
111 | my $field = $class->new( |
112 | object => $self->object, |
113 | attribute => $attr, |
114 | name => $attr->name, |
115 | location => join('-', $self->location, 'field', $attr->name), |
116 | ctx => $self->ctx, |
117 | %extra |
118 | ); |
119 | return ($attr_name => $field); |
120 | }; |
121 | |
89939ff9 |
122 | implements _build_fields_for_type_Num => as { |
7adfd53f |
123 | my ($self, $attr, $args) = @_; |
89939ff9 |
124 | return $self->_build_simple_field(Number, $attr, $args); |
7adfd53f |
125 | }; |
126 | |
89939ff9 |
127 | implements _build_fields_for_type_Int => as { |
7adfd53f |
128 | my ($self, $attr, $args) = @_; |
89939ff9 |
129 | return $self->_build_simple_field(Number, $attr, $args); |
7adfd53f |
130 | }; |
131 | |
89939ff9 |
132 | implements _build_fields_for_type_Bool => as { |
7adfd53f |
133 | my ($self, $attr, $args) = @_; |
89939ff9 |
134 | return $self->_build_simple_field(Boolean, $attr, $args); |
7adfd53f |
135 | }; |
136 | |
89939ff9 |
137 | implements _build_fields_for_type_Password => as { return }; |
7adfd53f |
138 | |
89939ff9 |
139 | implements _build_fields_for_type_Str => as { |
7adfd53f |
140 | my ($self, $attr, $args) = @_; |
89939ff9 |
141 | return $self->_build_simple_field(String, $attr, $args); |
7adfd53f |
142 | }; |
143 | |
89939ff9 |
144 | implements _build_fields_for_type_SimpleStr => as { |
7adfd53f |
145 | my ($self, $attr, $args) = @_; |
89939ff9 |
146 | return $self->_build_simple_field(String, $attr, $args); |
7adfd53f |
147 | }; |
148 | |
89939ff9 |
149 | implements _build_fields_for_type_DateTime => as { |
7adfd53f |
150 | my ($self, $attr, $args) = @_; |
89939ff9 |
151 | return $self->_build_simple_field(DateTime, $attr, $args); |
7adfd53f |
152 | }; |
153 | |
89939ff9 |
154 | implements _build_fields_for_type_Enum => as { |
7adfd53f |
155 | my ($self, $attr, $args) = @_; |
89939ff9 |
156 | return $self->_build_simple_field(String, $attr, $args); |
7adfd53f |
157 | }; |
158 | |
89939ff9 |
159 | implements _build_fields_for_type_ArrayRef => as { |
7adfd53f |
160 | my ($self, $attr, $args) = @_; |
89939ff9 |
161 | return $self->_build_simple_field(List, $attr, $args) |
7adfd53f |
162 | }; |
163 | |
89939ff9 |
164 | implements _build_fields_for_type_Reaction_InterfaceModel_Collection => as { |
7adfd53f |
165 | my ($self, $attr, $args) = @_; |
89939ff9 |
166 | return $self->_build_simple_field(Collection, $attr, $args) |
7adfd53f |
167 | }; |
168 | |
89939ff9 |
169 | implements _build_fields_for_type_Reaction_InterfaceModel_Object => as { |
7adfd53f |
170 | my ($self, $attr, $args) = @_; |
89939ff9 |
171 | return $self->_build_simple_field(RelatedObject, $attr, $args); |
7adfd53f |
172 | }; |
173 | |
7adfd53f |
174 | no Moose; |
175 | |
176 | no strict 'refs'; |
177 | delete ${__PACKAGE__ . '::'}{inner}; |
178 | |
179 | }; |
180 | |
181 | 1; |