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'; |
13 | |
14 | class ObjectView is 'Reaction::UI::ViewPort', which { |
15 | has object => ( |
16 | isa => 'Reaction::InterfaceModel::Object', is => 'ro', required => 1 |
17 | ); |
18 | |
7adfd53f |
19 | has _field_map => ( |
7b78a39d |
20 | isa => 'HashRef', is => 'rw', init_arg => 'fields', lazy_build => 1, |
7adfd53f |
21 | ); |
22 | |
23 | has exclude_fields => |
24 | ( is => 'rw', isa => 'ArrayRef', required => 1, default => sub{ [] } ); |
25 | |
7b78a39d |
26 | has ordered_fields => (is => 'rw', isa => 'ArrayRef', lazy_build => 1); |
27 | |
7b78a39d |
28 | implements fields => as { shift->_field_map }; |
7adfd53f |
29 | |
30 | implements BUILD => as { |
31 | my ($self, $args) = @_; |
32 | unless ($self->_has_field_map) { |
33 | my @field_map; |
34 | my $object = $self->object; |
35 | my %excluded = map{$_ => 1} @{$self->exclude_fields}; |
36 | for my $attr (grep { !$excluded{$_->name} } $object->parameter_attributes) { |
37 | push(@field_map, $self->build_fields_for($attr => $args)); |
38 | } |
39 | |
9de685fc |
40 | $self->_field_map({ @field_map }); |
7adfd53f |
41 | } |
42 | }; |
43 | |
44 | implements build_fields_for => as { |
45 | my ($self, $attr, $args) = @_; |
46 | my $attr_name = $attr->name; |
47 | my $builder = "build_fields_for_name_${attr_name}"; |
48 | my @fields; |
49 | if ($self->can($builder)) { |
50 | @fields = $self->$builder($attr, $args); # re-use coderef from can() |
51 | } elsif ($attr->has_type_constraint) { |
52 | my $constraint = $attr->type_constraint; |
53 | my $base_name = $constraint->name; |
54 | my $tried_isa = 0; |
55 | CONSTRAINT: while (defined($constraint)) { |
56 | my $name = $constraint->name; |
57 | if (eval { $name->can('meta') } && !$tried_isa++) { |
58 | foreach my $class ($name->meta->class_precedence_list) { |
59 | my $mangled_name = $class; |
60 | $mangled_name =~ s/:+/_/g; |
61 | my $builder = "build_fields_for_type_${mangled_name}"; |
62 | if ($self->can($builder)) { |
63 | @fields = $self->$builder($attr, $args); |
64 | last CONSTRAINT; |
65 | } |
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 | if ($self->can($builder)) { |
76 | @fields = $self->$builder($attr, $args); |
77 | last CONSTRAINT; |
78 | } |
79 | } |
80 | $constraint = $constraint->parent; |
81 | } |
82 | if (!defined($constraint)) { |
83 | 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"; |
84 | } |
85 | } else { |
86 | confess "Can't build field ${attr} without $builder method or type constraint"; |
87 | } |
88 | return @fields; |
89 | }; |
90 | |
7b78a39d |
91 | implements _build_field_map => as { |
7adfd53f |
92 | confess "Lazy field map building not supported by default"; |
93 | }; |
94 | |
7b78a39d |
95 | implements build_ordered_fields => as { |
96 | my $self = shift; |
9de685fc |
97 | $self->sort_by_spec($self->column_order, [keys %{$self->_field_map_}])}; |
7b78a39d |
98 | }; |
99 | |
7adfd53f |
100 | implements build_simple_field => as { |
101 | my ($self, $class, $attr, $args) = @_; |
102 | my $attr_name = $attr->name; |
103 | my %extra; |
104 | if (my $config = $args->{Field}{$attr_name}) { |
105 | %extra = %$config; |
106 | } |
107 | my $field = $class->new( |
108 | object => $self->object, |
109 | attribute => $attr, |
110 | name => $attr->name, |
111 | location => join('-', $self->location, 'field', $attr->name), |
112 | ctx => $self->ctx, |
113 | %extra |
114 | ); |
115 | return ($attr_name => $field); |
116 | }; |
117 | |
118 | implements build_fields_for_type_Num => as { |
119 | my ($self, $attr, $args) = @_; |
120 | return $self->build_simple_field(Number, $attr, $args); |
121 | }; |
122 | |
123 | implements build_fields_for_type_Int => as { |
124 | my ($self, $attr, $args) = @_; |
125 | return $self->build_simple_field(Number, $attr, $args); |
126 | }; |
127 | |
128 | implements build_fields_for_type_Bool => as { |
129 | my ($self, $attr, $args) = @_; |
130 | return $self->build_simple_field(Boolean, $attr, $args); |
131 | }; |
132 | |
133 | implements build_fields_for_type_Password => as { return }; |
134 | |
135 | implements build_fields_for_type_Str => as { |
136 | my ($self, $attr, $args) = @_; |
137 | return $self->build_simple_field(String, $attr, $args); |
138 | }; |
139 | |
140 | implements build_fields_for_type_SimpleStr => as { |
141 | my ($self, $attr, $args) = @_; |
142 | return $self->build_simple_field(String, $attr, $args); |
143 | }; |
144 | |
145 | implements build_fields_for_type_DateTime => as { |
146 | my ($self, $attr, $args) = @_; |
147 | return $self->build_simple_field(DateTime, $attr, $args); |
148 | }; |
149 | |
150 | implements build_fields_for_type_Enum => as { |
151 | my ($self, $attr, $args) = @_; |
152 | return $self->build_simple_field(String, $attr, $args); |
153 | }; |
154 | |
155 | |
156 | implements build_fields_for_type_ArrayRef => as { |
157 | my ($self, $attr, $args) = @_; |
158 | return $self->build_simple_field(List, $attr, $args) |
159 | }; |
160 | |
f670cfd0 |
161 | implements build_fields_for_type_Reaction_InterfaceModel_Collection => as { |
7adfd53f |
162 | my ($self, $attr, $args) = @_; |
163 | return $self->build_simple_field(Collection, $attr, $args) |
164 | }; |
165 | |
166 | implements build_fields_for_type_Reaction_InterfaceModel_Object => as { |
167 | my ($self, $attr, $args) = @_; |
168 | return $self->build_simple_field(RelatedObject, $attr, $args); |
169 | }; |
170 | |
171 | |
172 | no Moose; |
173 | |
174 | no strict 'refs'; |
175 | delete ${__PACKAGE__ . '::'}{inner}; |
176 | |
177 | }; |
178 | |
179 | 1; |