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';
14 use aliased 'Reaction::UI::ViewPort::Field::File';
16 use aliased 'Reaction::InterfaceModel::Object' => 'IM_Object';
18 class Object is 'Reaction::UI::ViewPort', which {
20 #everything is read only right now. Later I can make somethings read-write
21 #but first I need to figure out what depends on what so we can have decent triggers
22 has model => (is => 'ro', isa => IM_Object, required => 1);
23 has fields => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
25 has field_args => (is => 'rw');
26 has field_order => (is => 'ro', isa => 'ArrayRef');
28 has builder_cache => (is => 'ro', isa => 'HashRef', lazy_build => 1);
29 has excluded_fields => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
30 has computed_field_order => (is => 'ro', isa => 'ArrayRef', lazy_build => 1);
32 implements BUILD => as {
33 my ($self, $args) = @_;
34 if( my $field_args = delete $args->{Field} ){
35 $self->field_args( $field_args );
39 implements _build_excluded_fields => as { [] };
40 implements _build_builder_cache => as { {} };
42 implements _build_fields => as {
44 my $obj = $self->model;
45 my $args = $self->has_field_args ? $self->field_args : {};
47 for my $field_name (@{ $self->computed_field_order }) {
48 my $attr = $obj->meta->find_attribute_by_name($field_name);
49 my $meth = $self->builder_cache->{$field_name} ||= $self->get_builder_for($attr);
50 my $field = $self->$meth($attr, ($args->{$field_name} || {}));
51 push(@fields, $field) if $field;
56 implements _build_computed_field_order => as {
58 my %excluded = map { $_ => undef } @{ $self->excluded_fields };
59 #treat _$field_name as private and exclude fields with no reader
60 my @names = grep { $_ !~ /^_/ && !exists($excluded{$_})} map { $_->name }
61 grep { defined $_->get_read_method } $self->model->meta->parameter_attributes;
62 return $self->sort_by_spec($self->field_order || [], \@names);
65 override child_event_sinks => sub {
66 return ( @{shift->fields}, super());
69 #candidate for shared role!
70 implements get_builder_for => as {
71 my ($self, $attr) = @_;
72 my $attr_name = $attr->name;
73 my $builder = "_build_fields_for_name_${attr_name}";
74 return $builder if $self->can($builder);
75 if ($attr->has_type_constraint) {
76 my $constraint = $attr->type_constraint;
77 my $base_name = $constraint->name;
80 CONSTRAINT: while (defined($constraint)) {
81 my $name = $constraint->name;
82 $name = $attr->_isa_metadata if($name eq '__ANON__');
83 if (eval { $name->can('meta') } && !$tried_isa++) {
84 foreach my $class ($name->meta->class_precedence_list) {
86 my $mangled_name = $class;
87 $mangled_name =~ s/:+/_/g;
88 my $builder = "_build_fields_for_type_${mangled_name}";
89 return $builder if $self->can($builder);
94 unless (defined($base_name)) {
95 $base_name = "(anon subtype of ${name})";
97 my $mangled_name = $name;
98 $mangled_name =~ s/:+/_/g;
99 my $builder = "_build_fields_for_type_${mangled_name}";
100 return $builder if $self->can($builder);
102 $constraint = $constraint->parent;
104 if (!defined($constraint)) {
105 confess "Can't build field ${attr_name} of type ${base_name} without "
106 ."$builder method or _build_fields_for_type_<type> method "
107 ."for type or any supertype (tried ".join(', ', @tried).")";
110 confess "Can't build field ${attr} without $builder method or type constraint";
114 implements _build_simple_field => as {
115 my ($self, %args) = @_;
116 my $class = delete $args{class};
117 confess("Can not build simple field without a viewport class")
119 confess("Can not build simple field without attribute")
120 unless defined $args{attribute};
122 my $field_name = $args{attribute}->name;
125 model => $self->model,
126 location => join('-', $self->location, 'field', $field_name),
131 implements _build_fields_for_type_Num => as {
132 my ($self, $attr, $args) = @_;
133 $self->_build_simple_field(attribute => $attr, class => Number, %$args);
136 implements _build_fields_for_type_Int => as {
137 my ($self, $attr, $args) = @_;
139 $self->_build_simple_field(attribute => $attr, class => Integer, %$args);
142 implements _build_fields_for_type_Bool => as {
143 my ($self, $attr, $args) = @_;
144 $self->_build_simple_field(attribute => $attr, class => Boolean, %$args);
148 implements _build_fields_for_type_Reaction_Types_Core_Password => as { return };
150 implements _build_fields_for_type_Str => as {
151 my ($self, $attr, $args) = @_;
153 $self->_build_simple_field(attribute => $attr, class => String, %$args);
156 implements _build_fields_for_type_Reaction_Types_Core_SimpleStr => as {
157 my ($self, $attr, $args) = @_;
158 $self->_build_simple_field(attribute => $attr, class => String, %$args);
161 implements _build_fields_for_type_Reaction_Types_DateTime_DateTime => as {
162 my ($self, $attr, $args) = @_;
163 $self->_build_simple_field(attribute => $attr, class => DateTime, %$args);
166 implements _build_fields_for_type_Enum => as {
167 my ($self, $attr, $args) = @_;
169 $self->_build_simple_field(attribute => $attr, class => String, %$args);
172 implements _build_fields_for_type_ArrayRef => as {
173 my ($self, $attr, $args) = @_;
174 $self->_build_simple_field(attribute => $attr, class => Array, %$args);
177 implements _build_fields_for_type_Reaction_Types_File_File => as {
178 my ($self, $attr, $args) = @_;
179 $self->_build_simple_field(attribute => $attr, class => File, %$args);
182 implements _build_fields_for_type_Reaction_InterfaceModel_Object => as {
183 my ($self, $attr, $args) = @_;
185 $self->_build_simple_field(attribute => $attr, class => RelatedObject, %$args);
188 implements _build_fields_for_type_Reaction_InterfaceModel_Collection => as {
189 my ($self, $attr, $args) = @_;
190 $self->_build_simple_field(attribute => $attr, class => Collection, %$args);
201 Reaction::UI::ViewPort::Object
217 =head2 excluded_fields
219 =head2 computed_field_order
221 =head1 INTERNAL METHODS
223 These methods, although stable, are subject to change without notice. These are meant
224 to be used only by developers. End users should refrain from using these methods to
225 avoid potential breakages.
229 =head2 get_builder_for
231 =head2 _build_simple_field
233 =head2 _build_fields_for_type_Num
235 =head2 _build_fields_for_type_Int
237 =head2 _build_fields_for_type_Bool
239 =head2 _build_fields_for_type_Password
241 =head2 _build_fields_for_type_Str
243 =head2 _build_fields_for_type_SimpleStr
245 =head2 _build_fields_for_type_DateTime
247 =head2 _build_fields_for_type_Enum
249 =head2 _build_fields_for_type_ArrayRef
251 =head2 _build_fields_for_type_Reaction_InterfaceModel_Object
253 =head2 _build_fields_for_type_Reaction_InterfaceModel_Collection
257 See L<Reaction::Class> for authors.
261 See L<Reaction::Class> for the license.