1 package Reaction::UI::ViewPort::Object;
5 use aliased 'Reaction::UI::ViewPort::Field::Text';
6 use aliased 'Reaction::UI::ViewPort::Field::Number';
7 use aliased 'Reaction::UI::ViewPort::Field::Integer';
8 use aliased 'Reaction::UI::ViewPort::Field::Boolean';
9 use aliased 'Reaction::UI::ViewPort::Field::String';
10 use aliased 'Reaction::UI::ViewPort::Field::DateTime';
11 use aliased 'Reaction::UI::ViewPort::Field::RelatedObject';
12 use aliased 'Reaction::UI::ViewPort::Field::Array';
13 use aliased 'Reaction::UI::ViewPort::Field::Collection';
15 use aliased 'Reaction::InterfaceModel::Object' => 'IM_Object';
17 class Object is 'Reaction::UI::ViewPort', which {
19 #everything is read only right now. Later I can make somethings read-write
20 #but first I need to figure out what depends on what so we can have decent triggers
21 has model => (is => 'ro', isa => IM_Object, required => 1);
22 has fields => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
24 has field_args => (is => 'ro');
25 has field_order => (is => 'ro', isa => 'ArrayRef');
27 has builder_cache => (is => 'ro', isa => 'HashRef', lazy_build => 1);
28 has excluded_fields => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
29 has computed_field_order => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
31 implements BUILD => as {
32 my ($self, $args) = @_;
33 my $field_args = delete $args->{Field};
34 $self->field_args( $field_args ) if ref $field_args;
37 implements _build_excluded_fields => as { [] };
38 implements _build_builder_cache => as { {} };
40 implements _build_fields => as {
42 my $obj = $self->model;
43 my $args = $self->has_field_args ? $self->field_args : {};
45 for my $field_name (@{ $self->computed_field_order }) {
46 my $attr = $obj->meta->find_attribute_by_name($field_name);
47 my $meth = $self->builder_cache->{$field_name} ||= $self->get_builder_for($attr);
48 my $field = $self->$meth($attr, ($args->{$field_name} || {}));
49 push(@fields, $field) if $field;
54 implements _build_computed_field_order => as {
56 my %excluded = map { $_ => undef } @{ $self->excluded_fields };
57 #treat _$field_name as private and exclude fields with no reader
58 my @names = grep { $_ !~ /^_/ && !exists($excluded{$_})} map { $_->name }
59 grep { defined $_->get_read_method } $self->model->meta->parameter_attributes;
60 return $self->sort_by_spec($self->field_order || [], \@names);
63 override child_event_sinks => sub {
64 return ( @{shift->fields}, super());
67 #candidate for shared role!
68 implements get_builder_for => as {
69 my ($self, $attr) = @_;
70 my $attr_name = $attr->name;
71 my $builder = "_build_fields_for_name_${attr_name}";
72 return $builder if $self->can($builder);
73 if ($attr->has_type_constraint) {
74 my $constraint = $attr->type_constraint;
75 my $base_name = $constraint->name;
77 CONSTRAINT: while (defined($constraint)) {
78 my $name = $constraint->name;
79 $name = $attr->_isa_metadata if($name eq '__ANON__');
80 if (eval { $name->can('meta') } && !$tried_isa++) {
81 foreach my $class ($name->meta->class_precedence_list) {
82 my $mangled_name = $class;
83 $mangled_name =~ s/:+/_/g;
84 my $builder = "_build_fields_for_type_${mangled_name}";
85 return $builder if $self->can($builder);
89 unless (defined($base_name)) {
90 $base_name = "(anon subtype of ${name})";
92 my $mangled_name = $name;
93 $mangled_name =~ s/:+/_/g;
94 my $builder = "_build_fields_for_type_${mangled_name}";
95 return $builder if $self->can($builder);
97 $constraint = $constraint->parent;
99 if (!defined($constraint)) {
100 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";
103 confess "Can't build field ${attr} without $builder method or type constraint";
107 implements _build_simple_field => as {
108 my ($self, %args) = @_;
109 my $class = delete $args{class};
110 confess("Can not build simple field without a viewport class")
112 confess("Can not build simple field without attribute")
113 unless defined $args{attribute};
115 my $field_name = $args{attribute}->name;
118 model => $self->model,
119 location => join('-', $self->location, 'field', $field_name),
124 implements _build_fields_for_type_Num => as {
125 my ($self, $attr, $args) = @_;
126 $self->_build_simple_field(attribute => $attr, class => Number, %$args);
129 implements _build_fields_for_type_Int => as {
130 my ($self, $attr, $args) = @_;
132 $self->_build_simple_field(attribute => $attr, class => Integer, %$args);
135 implements _build_fields_for_type_Bool => as {
136 my ($self, $attr, $args) = @_;
137 $self->_build_simple_field(attribute => $attr, class => Boolean, %$args);
141 implements _build_fields_for_type_Password => as { return };
143 implements _build_fields_for_type_Str => as {
144 my ($self, $attr, $args) = @_;
146 $self->_build_simple_field(attribute => $attr, class => String, %$args);
149 implements _build_fields_for_type_SimpleStr => as {
150 my ($self, $attr, $args) = @_;
151 $self->_build_simple_field(attribute => $attr, class => String, %$args);
154 implements _build_fields_for_type_DateTime => as {
155 my ($self, $attr, $args) = @_;
156 $self->_build_simple_field(attribute => $attr, class => DateTime, %$args);
159 implements _build_fields_for_type_Enum => as {
160 my ($self, $attr, $args) = @_;
162 $self->_build_simple_field(attribute => $attr, class => String, %$args);
165 implements _build_fields_for_type_ArrayRef => as {
166 my ($self, $attr, $args) = @_;
167 $self->_build_simple_field(attribute => $attr, class => Array, %$args);
170 implements _build_fields_for_type_Reaction_InterfaceModel_Object => as {
171 my ($self, $attr, $args) = @_;
173 $self->_build_simple_field(attribute => $attr, class => RelatedObject, %$args);
176 implements _build_fields_for_type_Reaction_InterfaceModel_Collection => as {
177 my ($self, $attr, $args) = @_;
178 $self->_build_simple_field(attribute => $attr, class => Collection, %$args);
189 Reaction::UI::ViewPort::Object
205 =head2 excluded_fields
207 =head2 computed_field_order
209 =head1 INTERNAL METHODS
211 These methods, although stable, are subject to change without notice. These are meant
212 to be used only by developers. End users should refrain from using these methods to
213 avoid potential breakages.
217 =head2 get_builder_for
219 =head2 _build_simple_field
221 =head2 _build_fields_for_type_Num
223 =head2 _build_fields_for_type_Int
225 =head2 _build_fields_for_type_Bool
227 =head2 _build_fields_for_type_Password
229 =head2 _build_fields_for_type_Str
231 =head2 _build_fields_for_type_SimpleStr
233 =head2 _build_fields_for_type_DateTime
235 =head2 _build_fields_for_type_Enum
237 =head2 _build_fields_for_type_ArrayRef
239 =head2 _build_fields_for_type_Reaction_InterfaceModel_Object
241 =head2 _build_fields_for_type_Reaction_InterfaceModel_Collection
245 See L<Reaction::Class> for authors.
249 See L<Reaction::Class> for the license.