diff options
author | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2007-10-02 23:33:28 +0000 |
---|---|---|
committer | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2007-10-02 23:33:28 +0000 |
commit | e716714fb1197850975fcd131149db2f53b07f64 (patch) | |
tree | bded70e1e73b0039b612eb0a999c23af59995dfd /lib | |
parent | e22de1011c40b639cc4b6e5586a9e0defe855285 (diff) | |
download | reaction-e716714fb1197850975fcd131149db2f53b07f64.tar.gz reaction-e716714fb1197850975fcd131149db2f53b07f64.zip |
gridview is so close. so close
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ComponentUI/Controller/TestModel/Bar.pm | 5 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/GridView.pm | 203 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/GridView/Row.pm | 3 | ||||
-rw-r--r-- | lib/Reaction/UI/ViewPort/ListView.pm | 4 | ||||
-rw-r--r-- | lib/Reaction/UI/Widget/GridView.pm | 57 | ||||
-rw-r--r-- | lib/Reaction/UI/Widget/GridView/Row.pm | 4 | ||||
-rw-r--r-- | lib/Reaction/UI/WidgetClass.pm | 24 |
7 files changed, 206 insertions, 94 deletions
diff --git a/lib/ComponentUI/Controller/TestModel/Bar.pm b/lib/ComponentUI/Controller/TestModel/Bar.pm index 2cf7681..644a20f 100644 --- a/lib/ComponentUI/Controller/TestModel/Bar.pm +++ b/lib/ComponentUI/Controller/TestModel/Bar.pm @@ -6,10 +6,7 @@ use Reaction::Class; __PACKAGE__->config( model_base => 'TestModel', model_name => 'Bar', - action => { base => { Chained => '/base', PathPart => 'testmodel/bar' }, - list => { ViewPort => { layout => 'bar_list' } }, - update => { ViewPort => { layout => 'bar_form' } }, - create => { ViewPort => { layout => 'bar_form' } } }, + action => { base => { Chained => '/base', PathPart => 'testmodel/bar' }}, ); 1; diff --git a/lib/Reaction/UI/ViewPort/GridView.pm b/lib/Reaction/UI/ViewPort/GridView.pm index 542af62..5691ff1 100644 --- a/lib/Reaction/UI/ViewPort/GridView.pm +++ b/lib/Reaction/UI/ViewPort/GridView.pm @@ -2,8 +2,14 @@ package Reaction::UI::ViewPort::GridView; use Reaction::Class; -use aliased 'Reaction::UI::ViewPort::GridView::Row'; -use aliased 'Reaction::InterfaceModel::Collection'; +use aliased 'Reaction::UI::ViewPort::DisplayField::Text'; +use aliased 'Reaction::UI::ViewPort::DisplayField::Number'; +use aliased 'Reaction::UI::ViewPort::DisplayField::Boolean'; +use aliased 'Reaction::UI::ViewPort::DisplayField::String'; +use aliased 'Reaction::UI::ViewPort::DisplayField::DateTime'; +use aliased 'Reaction::UI::ViewPort::DisplayField::RelatedObject'; + +use aliased 'Reaction::InterfaceModel::Collection' => 'IM_Collection'; class GridView is 'Reaction::UI::ViewPort', which { @@ -12,38 +18,23 @@ class GridView is 'Reaction::UI::ViewPort', which { has rows => ( isa => 'ArrayRef', is => 'ro', lazy_build => 1); has row_args => ( isa => 'HashRef', is => 'ro'); - has collection => (isa => Collection, is => 'ro', required => 1); - has current_collection => (isa => Collection, is => 'rw', lazy_build => 1); - - implements build_rows => as{ - my $self = shift; + has collection => (isa => IM_Collection, is => 'ro', required => 1); + has current_collection => (isa => IM_Collection, is => 'rw', lazy_build => 1); - my (@rows, $i); - for my $object ( $self->current_collection->members ){ - my $row = Row->new - ( - ctx => $self->ctx, - object => $object, - location => join('-', $self->location, 'row', ++$i), - column_order => $self->column_order, #XXX clean from ViewPort - exclude_fields => $self->exclude_columns || [], - $self->has_row_args ? %{ $self->row_args } : (), - - ); - push(@rows, $row); - } - return \@rows; - }; + has ordered_columns => (is => 'ro', isa => 'ArrayRef', lazy_build => 1); - implements build_column_names => as { + implements build_ordered_columns => as { my ($self) = @_; my %excluded = map { $_ => undef } @{ $self->has_exclude_columns ? $self->exclude_columns : [] }; #XXX this abuse of '_im_class' needs to be fixed ASAP my $object_class = $self->collection->_im_class; - my @fields = $object_class->meta->compute_all_applicable_attributes; + my @fields = $object_class->meta->parameter_attributes; + #obviously only get fields with readers. + @fields = grep { $_->get_read_method } @fields; #eliminate excluded fields & treat names that start with an underscore as private @fields = grep {$_->name !~ /^_/ && !exists $excluded{$_->name} } @fields; + #eliminate fields marked as collections, or fields that are arrayrefs @fields = grep { !($_->has_type_constraint && @@ -54,14 +45,172 @@ class GridView is 'Reaction::UI::ViewPort', which { ) } @fields; #order the columns all nice and pretty, and only get fields with readers, duh - return $self->sort_by_spec - ( $self->column_order, [ map { (($_->get_read_method) || ()) } @fields] ); + my $ordered = $self->sort_by_spec + ( $self->column_order, [ map { (($_->name) || ()) } @fields] ); + + return $ordered; }; implements build_current_collection => as { shift->collection; }; + implements build_column_names => as { + my $self = shift; + [ map{ join(' ', map{ ucfirst } split('_', $_)) } @{$self->ordered_columns} ]; + } + + implements build_rows => as { + my ($self) = @_; + my @columns = @{ $self->ordered_columns }; + + my (@rows, $i); + my $builders = {}; + for my $obj ( $self->current_collection->members ) { + $i++; + my @cells; + for my $col (@columns) { + my $attr = $obj->meta->find_attribute_by_name($col); + my $build_meth = $builders->{$col} ||= $self->build_fields_for($attr); + my $loc = join('-', $self->location, 'row', $i, 'field', $attr->name); + my $args = {Field => { $attr->name => {location => $loc} } }; + my $cell = $self->$build_meth($obj, $attr, $args); + push(@cells, $cell) if $cell; + } + push(@rows,\@cells) + } + + return \@rows; + }; + + implements build_fields_for => as { + my ($self, $attr) = @_; + my $attr_name = $attr->name; + my $builder = "build_fields_for_name_${attr_name}"; + return $builder if $self->can($builder); + if ($attr->has_type_constraint) { + my $constraint = $attr->type_constraint; + my $base_name = $constraint->name; + my $tried_isa = 0; + CONSTRAINT: while (defined($constraint)) { + my $name = $constraint->name; + if (eval { $name->can('meta') } && !$tried_isa++) { + foreach my $class ($name->meta->class_precedence_list) { + my $mangled_name = $class; + $mangled_name =~ s/:+/_/g; + my $builder = "build_fields_for_type_${mangled_name}"; + return $builder if $self->can($builder); + } + } + if (defined($name)) { + unless (defined($base_name)) { + $base_name = "(anon subtype of ${name})"; + } + my $mangled_name = $name; + $mangled_name =~ s/:+/_/g; + my $builder = "build_fields_for_type_${mangled_name}"; + return $builder if $self->can($builder); + } + $constraint = $constraint->parent; + } + if (!defined($constraint)) { + 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"; + } + } else { + confess "Can't build field ${attr} without $builder method or type constraint"; + } + }; + + + implements build_simple_field => as { + my ($self, $class, $obj, $attr, $args) = @_; + my $attr_name = $attr->name; + my %extra; + if (my $config = $args->{Field}{$attr_name}) { + %extra = %$config; + } + + return $class->new( + object => $obj, + attribute => $attr, + name => $attr->name, + ctx => $self->ctx, + %extra + ); + }; + + implements build_fields_for_type_Num => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/number' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(Number, $obj, $attr, $args); + }; + + implements build_fields_for_type_Int => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/number' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(Number, $obj, $attr, $args); + }; + + implements build_fields_for_type_Bool => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/boolean' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(Boolean, $obj, $attr, $args); + }; + + implements build_fields_for_type_Password => as { return }; + + implements build_fields_for_type_Str => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/string' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(String, $obj, $attr, $args); + }; + + implements build_fields_for_type_SimpleStr => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/string' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(String, $obj, $attr, $args); + }; + + implements build_fields_for_type_DateTime => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/date_time' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(DateTime, $obj, $attr, $args); + }; + + implements build_fields_for_type_Enum => as { + my ($self, $obj, $attr, $args) = @_; + $args->{Field}{$attr->name}{layout} = 'value/string' + unless( exists $args->{Field}{$attr->name} && + exists $args->{Field}{$attr->name}{layout} && + defined $args->{Field}{$attr->name}{layout} + ); + return $self->build_simple_field(String, $obj, $attr, $args); + }; + }; diff --git a/lib/Reaction/UI/ViewPort/GridView/Row.pm b/lib/Reaction/UI/ViewPort/GridView/Row.pm index 2edb06a..6c84967 100644 --- a/lib/Reaction/UI/ViewPort/GridView/Row.pm +++ b/lib/Reaction/UI/ViewPort/GridView/Row.pm @@ -77,6 +77,7 @@ class Row is 'Reaction::UI::ViewPort::ObjectView', which { around build_fields_for_type_ArrayRef => sub { my ($orig, $self, $attr, $args) = @_; + return; $args->{Field}{$attr->name}{layout} = 'value/list' unless( exists $args->{Field}{$attr->name} && exists $args->{Field}{$attr->name}{layout} && @@ -87,6 +88,7 @@ class Row is 'Reaction::UI::ViewPort::ObjectView', which { around build_fields_for_type_Reaction_InterfaceModel_Collection => sub { my ($orig, $self, $attr, $args) = @_; + return; $args->{Field}{$attr->name}{layout} = 'value/collection' unless( exists $args->{Field}{$attr->name} && exists $args->{Field}{$attr->name}{layout} && @@ -97,6 +99,7 @@ class Row is 'Reaction::UI::ViewPort::ObjectView', which { around build_fields_for_type_Reaction_InterfaceModel_Object => sub { my ($orig, $self, $attr, $args) = @_; + return; $args->{Field}{$attr->name}{layout} = 'value/related_object' unless( exists $args->{Field}{$attr->name} && exists $args->{Field}{$attr->name}{layout} && diff --git a/lib/Reaction/UI/ViewPort/ListView.pm b/lib/Reaction/UI/ViewPort/ListView.pm index 94e4de1..7430413 100644 --- a/lib/Reaction/UI/ViewPort/ListView.pm +++ b/lib/Reaction/UI/ViewPort/ListView.pm @@ -4,8 +4,8 @@ use Reaction::Class; class ListView is 'Reaction::UI::ViewPort::GridView', which { - does 'Reaction::UI::ViewPort::GridView::Role::Order'; - does 'Reaction::UI::ViewPort::GridView::Role::Pager'; + #does 'Reaction::UI::ViewPort::GridView::Role::Order'; + #does 'Reaction::UI::ViewPort::GridView::Role::Pager'; }; diff --git a/lib/Reaction/UI/Widget/GridView.pm b/lib/Reaction/UI/Widget/GridView.pm index bb2f526..bb7494c 100644 --- a/lib/Reaction/UI/Widget/GridView.pm +++ b/lib/Reaction/UI/Widget/GridView.pm @@ -3,7 +3,7 @@ package Reaction::UI::Widget::GridView; use Reaction::UI::WidgetClass; class GridView, which { - widget renders [ qw/header rows footer/ + widget renders [ qw/header body footer/ => { viewport => func('self', 'viewport') } ]; @@ -15,58 +15,11 @@ class GridView, which { footer_row renders [ footer_cell over func('viewport', 'column_names') ]; footer_cell renders [ string { $_ } ]; - rows renders [ viewport over func('viewport','rows') ]; + + body renders [ body_row over func('viewport','rows')]; + body_row renders [ body_cell over $_ ]; #over $_ ? heeelp + body_cell renders [ 'viewport' ]; }; 1; - - -=for layout widget -<table> - [% header %] -<tbody> - [% rows %] -</tbody> -<tfoot> - [% footer %] -</tfoot> -</table> - -=for layout header - -<thead> - [% content %] -</thead> - -=for layout header_row - -<tr> - [% content %] -</tr> - -=for layout header_cell - -<th> [% content %] </th> - -=for layout footer - -<tfoot> - [% content %] -</tfoot> - -=for layout footer_row - -<tr> [% content %] </tr> - -=for layout footer_cell - -<td> [% content %] </td> - -=for layout rows - -<tbody> - [% content %] -</tbody> - -=cut diff --git a/lib/Reaction/UI/Widget/GridView/Row.pm b/lib/Reaction/UI/Widget/GridView/Row.pm index 8ed46e0..24cb098 100644 --- a/lib/Reaction/UI/Widget/GridView/Row.pm +++ b/lib/Reaction/UI/Widget/GridView/Row.pm @@ -1,8 +1,8 @@ -package Reaction::UI::Widget::ObjectView; +package Reaction::UI::Widget::GridView::Row; use Reaction::UI::WidgetClass; -class ObjectView, which { +class Row, which { widget renders [ cells => { viewport => func('self', 'viewport') } ]; cells renders [ cell over func('viewport', 'ordered_fields') ]; cell renders [ 'viewport' ]; diff --git a/lib/Reaction/UI/WidgetClass.pm b/lib/Reaction/UI/WidgetClass.pm index 9e40936..83e3076 100644 --- a/lib/Reaction/UI/WidgetClass.pm +++ b/lib/Reaction/UI/WidgetClass.pm @@ -65,11 +65,11 @@ class WidgetClass, which { if (defined($args) && (ref($args) ne 'HASH')); $sig .= ' - where content spec is [ fragment_name over func(...), \%args? ] +where content spec is [ fragment_name over (func(...)|$_|$_{keyname}), \%args? ] or [ qw(list of fragment names), \%args ]'; # explain the mistake, yea my $inner_args = ((ref($content->[-1]) eq 'HASH') ? pop(@$content) : {}); - # [ blah over func(...), { ... } ] or [ qw(foo bar), { ... } ] + # [ blah over (func(...)|$_|$_{keyname}), { ... } ] or [ qw(foo bar), { ... } ] # predeclare since content_gen gets populated somewhere in an if # and inner_args_gen wants to be closed over by content_gen @@ -95,13 +95,23 @@ class WidgetClass, which { # - if arrayref, render fragment per entry # - if obj and can('next') call that until undef # - else scream loudly - my ($func_key, $func_meth) = @$func; + unless ((ref($func) eq 'ARRAY') || ($func =~ /^-topic:(.*)$/)) { + confess "over value wrong, should be ${sig}"; + } $content_gen = sub { my ($widget, $args) = @_; - my $topic = eval { $args->{$func_key}->$func_meth }; - confess "Error calling ${func_meth} on ${func_key} argument " - .($args->{$func_key}||'').": $@" - if $@; + my $topic; + if (ref($func) eq 'ARRAY') { + my ($func_key, $func_meth) = @$func; + $topic = eval { $args->{$func_key}->$func_meth }; + confess "Error calling ${func_meth} on ${func_key} argument " + .($args->{$func_key}||'').": $@" + if $@; + } elsif ($func =~ /^-topic:(.*)$/) { + $topic = $args->{$1}; + } else { + confess "Shouldn't get here"; + } my $iter_sub; if (ref $topic eq 'ARRAY') { my @copy = @$topic; # non-destructive on original data |