diff options
author | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2008-08-22 17:12:19 +0000 |
---|---|---|
committer | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2008-08-22 17:12:19 +0000 |
commit | 4b0ebd4e81012a5e6a5f77d7921176525dd89ace (patch) | |
tree | 1c3e64fe577f05119a726b49eea69c63e15f6c83 | |
parent | 2739c5aa63728c894634791c953f64f48a8b968e (diff) | |
parent | 599c1172394e3377d8e3e28c06557a99a1a10d1e (diff) | |
download | reaction-4b0ebd4e81012a5e6a5f77d7921176525dd89ace.tar.gz reaction-4b0ebd4e81012a5e6a5f77d7921176525dd89ace.zip |
r21703@martha (orig r863): groditi | 2008-08-20 20:38:47 -0400
container support built into fields and an example of usage in ComponentUI
-rw-r--r-- | lib/ComponentUI/Controller/TestModel/Bar.pm | 4 | ||||
-rw-r--r-- | lib/ComponentUI/Controller/TestModel/Foo.pm | 33 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/Action.pm | 59 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/Field.pm | 24 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/Object.pm | 51 | ||||
-rw-r--r-- | lib/Reaction/UI/Widget/Action.pm | 4 | ||||
-rw-r--r-- | lib/Reaction/UI/Widget/Object.pm | 21 | ||||
-rw-r--r-- | share/skin/base/layout/action.tt | 10 | ||||
-rw-r--r-- | share/skin/base/layout/object.tt | 11 | ||||
-rw-r--r-- | share/skin/default/layout/action.tt | 2 | ||||
-rw-r--r-- | share/skin/default/layout/object.tt | 2 | ||||
-rw-r--r-- | t/lib/RTest/TestDB/Bar.pm | 5 |
12 files changed, 131 insertions, 95 deletions
diff --git a/lib/ComponentUI/Controller/TestModel/Bar.pm b/lib/ComponentUI/Controller/TestModel/Bar.pm index f7859b7..0839506 100644 --- a/lib/ComponentUI/Controller/TestModel/Bar.pm +++ b/lib/ComponentUI/Controller/TestModel/Bar.pm @@ -6,7 +6,9 @@ use Reaction::Class; __PACKAGE__->config( model_name => 'TestModel', collection_name => 'Bar', - action => { base => { Chained => '/base', PathPart => 'testmodel/bar' }}, + action => { + base => { Chained => '/base', PathPart => 'testmodel/bar' }, + }, ); 1; diff --git a/lib/ComponentUI/Controller/TestModel/Foo.pm b/lib/ComponentUI/Controller/TestModel/Foo.pm index cc821c5..b5dfe5b 100644 --- a/lib/ComponentUI/Controller/TestModel/Foo.pm +++ b/lib/ComponentUI/Controller/TestModel/Foo.pm @@ -6,7 +6,38 @@ use Reaction::Class; __PACKAGE__->config( model_name => 'TestModel', collection_name => 'Foo', - action => { base => { Chained => '/base', PathPart => 'testmodel/foo' } }, + action => { + base => { Chained => '/base', PathPart => 'testmodel/foo' }, + list => { + ViewPort => { + excluded_fields => [qw/id/], + }, + }, + view => { + ViewPort => { + excluded_fields => [qw/id/], + }, + }, + }, ); +for my $action (qw/view create update/){ + __PACKAGE__->config( + action => { + $action => { + ViewPort => { + container_layouts => [ + { name => 'primary', fields => [qw/first_name last_name/]}, + { + name => 'secondary', + label => 'Optional Label', + fields => [qw/bars bazes/], + }, + ], + }, + }, + } + ); +} + 1; diff --git a/lib/Reaction/UI/ViewPort/Action.pm b/lib/Reaction/UI/ViewPort/Action.pm index 9fc808f..b198c99 100644 --- a/lib/Reaction/UI/ViewPort/Action.pm +++ b/lib/Reaction/UI/ViewPort/Action.pm @@ -21,7 +21,6 @@ use MooseX::Types::Moose qw/Int/; use Reaction::Types::Core qw/NonEmptySimpleStr/; use namespace::clean -except => [ qw(meta) ]; -extends Object; with 'Reaction::UI::ViewPort::Action::Role::OK'; has model => ( @@ -172,70 +171,40 @@ Reaction::UI::ViewPort::Object::Mutable =head1 DESCRIPTION -This subclass of L<Reaction::UI::ViewPort::Object> is used for rendering a -collection of C<Reaction::UI::ViewPort::Field::Mutable::*> objects for user editing. +This subclass of L<Reaction::UI::ViewPort::Object::Mutable> is used for +rendering a complete form supporting Apply, Close and OK. =head1 ATTRIBUTES -=head2 model +=head2 method -L<Reaction::InterfaceModel::Action> - -=head2 ok_label - -Default: 'ok' - -=head2 apply_label - -Default: 'apply' - -=head2 close_label_close - -Default: 'close' - -=head2 close_label_cancel - -This label is only shown when C<changed> is true. - -Default: 'cancel' - -=head2 fields - -=head2 can_apply - -=head2 can_close +post / get =head2 changed Returns true if a field has been edited. -=head2 next_action - -=head2 on_apply_callback - -CodeRef. - =head1 METHODS -=head2 ok +=head2 can_apply -Calls C<apply>, and then C<close> if successful. +=head2 do_apply -=head2 close +=head2 sync_action_from_fields -Pop viewport and proceed to C<next_action>. +=head1 SEE ALSO -=head2 apply +L<Reaction::UI::ViewPort> -Attempt to save changes and update C<changed> attribute if required. +L<Reaction::UI::ViewPort::Object> -=head1 SEE ALSO +L<Reaction::UI::ViewPort::Object::Mutable> -L<Reaction::UI::ViewPort::Object> +L<Reaction::InterfaceModel::Action::Role::Apply> -L<Reaction::UI::ViewPort> +L<Reaction::InterfaceModel::Action::Role::Close> -L<Reaction::InterfaceModel::Action> +L<Reaction::InterfaceModel::Action::Role::OK> =head1 AUTHORS diff --git a/lib/Reaction/UI/ViewPort/Field.pm b/lib/Reaction/UI/ViewPort/Field.pm index a4bb219..d3dd2e7 100644 --- a/lib/Reaction/UI/ViewPort/Field.pm +++ b/lib/Reaction/UI/ViewPort/Field.pm @@ -7,8 +7,6 @@ use aliased 'Reaction::Meta::InterfaceModel::Object::ParameterAttribute'; use namespace::clean -except => [ qw(meta) ]; extends 'Reaction::UI::ViewPort'; - - has value => (is => 'rw', lazy_build => 1); has name => (is => 'rw', isa => 'Str', lazy_build => 1); has label => (is => 'rw', isa => 'Str', lazy_build => 1); @@ -16,15 +14,19 @@ has value_string => (is => 'rw', isa => 'Str', lazy_build => 1); has model => (is => 'ro', isa => Object, required => 1); has attribute => (is => 'ro', isa => ParameterAttribute, required => 1); + sub _build_name { shift->attribute->name }; + sub _build_label { join(' ', map { ucfirst } split('_', shift->name)); -}; +} + sub _build_value { my ($self) = @_; my $reader = $self->attribute->get_read_method; return $self->model->$reader; -}; +} + sub _model_has_value { my ($self) = @_; my $predicate = $self->attribute->get_predicate_method; @@ -37,7 +39,8 @@ sub _model_has_value { return 1; } return 0; -}; +} + sub _build_value_string { my ($self) = @_; # XXX need the defined test because the IM lazy builds from @@ -47,14 +50,17 @@ sub _build_value_string { return ($self->_model_has_value && defined($self->_build_value) ? $self->_value_string_from_value : $self->_empty_string_value); -}; +} + sub _value_string_from_value { shift->value; -}; -sub _empty_string_value { '' }; +} + +sub _empty_string_value { '' } + sub value_is_required { shift->attribute->is_required; -}; +} __PACKAGE__->meta->make_immutable; diff --git a/lib/Reaction/UI/ViewPort/Object.pm b/lib/Reaction/UI/ViewPort/Object.pm index 35fa9c9..7fb01d5 100644 --- a/lib/Reaction/UI/ViewPort/Object.pm +++ b/lib/Reaction/UI/ViewPort/Object.pm @@ -12,6 +12,7 @@ use aliased 'Reaction::UI::ViewPort::Field::RelatedObject'; use aliased 'Reaction::UI::ViewPort::Field::Array'; use aliased 'Reaction::UI::ViewPort::Field::Collection'; use aliased 'Reaction::UI::ViewPort::Field::File'; +use aliased 'Reaction::UI::ViewPort::Field::Container'; use aliased 'Reaction::InterfaceModel::Object' => 'IM_Object'; @@ -29,6 +30,10 @@ has field_order => (is => 'ro', isa => 'ArrayRef'); has builder_cache => (is => 'ro', isa => 'HashRef', lazy_build => 1); has excluded_fields => (is => 'ro', isa => 'ArrayRef', lazy_build => 1); has computed_field_order => (is => 'ro', isa => 'ArrayRef', lazy_build => 1); + +has containers => ( is => 'ro', isa => 'ArrayRef', lazy_build => 1); +has container_layouts => ( is => 'rw', isa => 'ArrayRef' ); + sub BUILD { my ($self, $args) = @_; if( my $field_args = delete $args->{Field} ){ @@ -48,10 +53,12 @@ sub _build_fields { my $attr = $param_attrs{$field_name}; my $meth = $self->builder_cache->{$field_name} ||= $self->get_builder_for($attr); my $field = $self->$meth($attr, ($args->{$field_name} || {})); - push(@fields, $field) if $field; + next unless $field; + push(@fields, $field); } return \@fields; -}; +} + sub _build_computed_field_order { my ($self) = @_; @@ -60,7 +67,7 @@ sub _build_computed_field_order { my @names = grep { $_ !~ /^_/ && !exists($excluded{$_})} map { $_->name } grep { defined $_->get_read_method } $self->model->parameter_attributes; return $self->sort_by_spec($self->field_order || [], \@names); -}; +} override child_event_sinks => sub { return ( @{shift->fields}, super()); @@ -109,7 +116,8 @@ sub get_builder_for { } else { confess "Can't build field ${attr} without $builder method or type constraint"; } -}; +} + sub _build_simple_field { my ($self, %args) = @_; my $class = delete $args{class}; @@ -125,20 +133,23 @@ sub _build_simple_field { location => join('-', $self->location, 'field', $field_name), %args ); -}; +} + sub _build_fields_for_type_Num { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => Number, %$args); -}; +} + sub _build_fields_for_type_Int { my ($self, $attr, $args) = @_; #XXX $self->_build_simple_field(attribute => $attr, class => Integer, %$args); -}; +} + sub _build_fields_for_type_Bool { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => Boolean, %$args); -}; +} #XXX sub _build_fields_for_type_Reaction_Types_Core_Password { return }; @@ -147,41 +158,47 @@ sub _build_fields_for_type_Str { my ($self, $attr, $args) = @_; #XXX $self->_build_simple_field(attribute => $attr, class => String, %$args); -}; +} + sub _build_fields_for_type_Reaction_Types_Core_SimpleStr { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => String, %$args); -}; +} + sub _build_fields_for_type_Reaction_Types_DateTime_DateTime { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => DateTime, %$args); -}; +} + sub _build_fields_for_type_Enum { my ($self, $attr, $args) = @_; #XXX $self->_build_simple_field(attribute => $attr, class => String, %$args); -}; +} + sub _build_fields_for_type_ArrayRef { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => Array, %$args); -}; +} + sub _build_fields_for_type_Reaction_Types_File_File { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => File, %$args); -}; +} + sub _build_fields_for_type_Reaction_InterfaceModel_Object { my ($self, $attr, $args) = @_; #XXX $self->_build_simple_field(attribute => $attr, class => RelatedObject, %$args); -}; +} + sub _build_fields_for_type_Reaction_InterfaceModel_Collection { my ($self, $attr, $args) = @_; $self->_build_simple_field(attribute => $attr, class => Collection, %$args); -}; +} __PACKAGE__->meta->make_immutable; - 1; __END__; diff --git a/lib/Reaction/UI/Widget/Action.pm b/lib/Reaction/UI/Widget/Action.pm index 32ca4b7..2957317 100644 --- a/lib/Reaction/UI/Widget/Action.pm +++ b/lib/Reaction/UI/Widget/Action.pm @@ -3,9 +3,7 @@ package Reaction::UI::Widget::Action; use Reaction::UI::WidgetClass; use namespace::clean -except => [ qw(meta) ]; -extends 'Reaction::UI::Widget::Object'; - - +extends 'Reaction::UI::Widget::Object::Mutable'; after fragment widget { arg 'method' => $_{viewport}->method; diff --git a/lib/Reaction/UI/Widget/Object.pm b/lib/Reaction/UI/Widget/Object.pm index dc898cc..a44a66d 100644 --- a/lib/Reaction/UI/Widget/Object.pm +++ b/lib/Reaction/UI/Widget/Object.pm @@ -4,7 +4,15 @@ use Reaction::UI::WidgetClass; use namespace::clean -except => [ qw(meta) ]; +implements fragment container_list { + render container => over $_{viewport}->containers; +}; + +implements fragment container { + render 'viewport'; +}; +#we won't be needing these anymore implements fragment field_list { render field => over $_{viewport}->fields; }; @@ -15,7 +23,6 @@ implements fragment field { __PACKAGE__->meta->make_immutable; - 1; __END__; @@ -28,9 +35,19 @@ Reaction::UI::Widget::Object =head1 FRAGMENTS +=head2 container_list + +Sequentially renders the C<fields> of the viewport; + +=head2 container + +Renders the C<field> viewport passed by C<field_list> + +=head1 DEPRECATED FRAGMENTS + =head2 field_list -Sequentially renders the C<fields> of the viewport in the C<computed_field_order> +Sequentially renders the C<fields> of the viewport; =head2 field diff --git a/share/skin/base/layout/action.tt b/share/skin/base/layout/action.tt index 6fda5a1..5ef48cd 100644 --- a/share/skin/base/layout/action.tt +++ b/share/skin/base/layout/action.tt @@ -3,7 +3,7 @@ <div class="action_form"> <form action="" method="[% method %]" enctype="multipart/form-data"> [% header %] - [% field_list %] + [% container_list %] [% buttons %] [% footer %] </form> @@ -11,13 +11,11 @@ =for layout header -=for layout field_list +=for layout container_list -<div class="action_field_list"> - [% call_next %] -</div> +[% call_next %] -=for layout field +=for layout container [% call_next %] diff --git a/share/skin/base/layout/object.tt b/share/skin/base/layout/object.tt index 00816d3..8a5074a 100644 --- a/share/skin/base/layout/object.tt +++ b/share/skin/base/layout/object.tt @@ -1,13 +1,10 @@ =for layout widget -<div class="object_field_list"> - [% field_list %] -</div> +[% container_list %] -=for layout field +=for layout container -<span class="object_field"> - [% call_next %] -</span> +[% call_next %] =cut + diff --git a/share/skin/default/layout/action.tt b/share/skin/default/layout/action.tt index 4c32a37..a18e51d 100644 --- a/share/skin/default/layout/action.tt +++ b/share/skin/default/layout/action.tt @@ -1,6 +1,6 @@ =extends NEXT -=for layout field +=for layout container [% call_next %] <br /> diff --git a/share/skin/default/layout/object.tt b/share/skin/default/layout/object.tt index 20dee31..0f32321 100644 --- a/share/skin/default/layout/object.tt +++ b/share/skin/default/layout/object.tt @@ -1,6 +1,6 @@ =extends NEXT -=for layout field +=for layout container [% call_next %] <br> diff --git a/t/lib/RTest/TestDB/Bar.pm b/t/lib/RTest/TestDB/Bar.pm index 4359d87..36faca8 100644 --- a/t/lib/RTest/TestDB/Bar.pm +++ b/t/lib/RTest/TestDB/Bar.pm @@ -5,12 +5,13 @@ use base qw/DBIx::Class/; use metaclass 'Reaction::Meta::Class'; use Moose; +use aliased 'RTest::TestDB::Foo'; use Reaction::Types::Core qw/NonEmptySimpleStr/; use Reaction::Types::DateTime qw//; use Reaction::Types::File 'File'; has 'name' => (isa => NonEmptySimpleStr, is => 'rw', required => 1); -has 'foo' => (isa => 'RTest::TestDB::Foo', is => 'rw', required => 1); +has 'foo' => (isa => Foo, is => 'rw', required => 1); has 'published_at' => (isa => Reaction::Types::DateTime::DateTime, is => 'rw'); has 'avatar' => (isa => File, is => 'rw'); @@ -30,7 +31,7 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key('name'); __PACKAGE__->belongs_to( - 'foo' => 'RTest::TestDB::Foo', + 'foo' => Foo, { 'foreign.id' => 'self.foo_id' } ); |