gridview is so close. so close
groditi [Tue, 2 Oct 2007 23:33:28 +0000 (23:33 +0000)]
41 files changed:
componentui.conf
lib/ComponentUI/Controller/TestModel/Bar.pm
lib/Reaction/UI/ViewPort/GridView.pm
lib/Reaction/UI/ViewPort/GridView/Row.pm
lib/Reaction/UI/ViewPort/ListView.pm
lib/Reaction/UI/Widget/GridView.pm
lib/Reaction/UI/Widget/GridView/Row.pm
lib/Reaction/UI/WidgetClass.pm
share/skin/default/layout/action_form.tt [moved from share/skin/default/layout/action_form with 100% similarity]
share/skin/default/layout/display_field/boolean.tt [moved from share/skin/default/layout/display_field/boolean with 100% similarity]
share/skin/default/layout/display_field/collection.tt [moved from share/skin/default/layout/display_field/collection with 100% similarity]
share/skin/default/layout/display_field/date_time.tt [moved from share/skin/default/layout/display_field/date_time with 100% similarity]
share/skin/default/layout/display_field/list.tt [moved from share/skin/default/layout/display_field/list with 100% similarity]
share/skin/default/layout/display_field/number.tt [moved from share/skin/default/layout/display_field/number with 100% similarity]
share/skin/default/layout/display_field/related_object.tt [moved from share/skin/default/layout/display_field/related_object with 100% similarity]
share/skin/default/layout/display_field/string.tt [moved from share/skin/default/layout/display_field/string with 100% similarity]
share/skin/default/layout/display_field/text.tt [moved from share/skin/default/layout/display_field/text with 100% similarity]
share/skin/default/layout/field/boolean.tt [moved from share/skin/default/layout/field/boolean with 100% similarity]
share/skin/default/layout/field/choose_many.tt [moved from share/skin/default/layout/field/choose_many with 100% similarity]
share/skin/default/layout/field/choose_one.tt [moved from share/skin/default/layout/field/choose_one with 100% similarity]
share/skin/default/layout/field/date_time.tt [moved from share/skin/default/layout/field/date_time with 100% similarity]
share/skin/default/layout/field/file.tt [moved from share/skin/default/layout/field/file with 100% similarity]
share/skin/default/layout/field/hidden_array.tt [moved from share/skin/default/layout/field/hidden_array with 100% similarity]
share/skin/default/layout/field/number.tt [moved from share/skin/default/layout/field/number with 100% similarity]
share/skin/default/layout/field/password.tt [moved from share/skin/default/layout/field/password with 100% similarity]
share/skin/default/layout/field/string.tt [moved from share/skin/default/layout/field/string with 100% similarity]
share/skin/default/layout/field/text.tt [moved from share/skin/default/layout/field/text with 100% similarity]
share/skin/default/layout/field/time_range.tt [moved from share/skin/default/layout/field/time_range with 100% similarity]
share/skin/default/layout/grid_view.tt [moved from share/skin/default/layout/list_view with 76% similarity]
share/skin/default/layout/index.tt [moved from share/skin/default/layout/index with 100% similarity]
share/skin/default/layout/layout.tt [moved from share/skin/default/layout/layout with 100% similarity]
share/skin/default/layout/list_view.tt [moved from share/skin/default/layout/grid_view with 76% similarity]
share/skin/default/layout/object_view.tt [moved from share/skin/default/layout/object_view with 100% similarity]
share/skin/default/layout/value/boolean.tt [moved from share/skin/default/layout/value/boolean with 100% similarity]
share/skin/default/layout/value/collection.tt [moved from share/skin/default/layout/value/collection with 100% similarity]
share/skin/default/layout/value/date_time.tt [moved from share/skin/default/layout/value/date_time with 100% similarity]
share/skin/default/layout/value/list.tt [moved from share/skin/default/layout/value/list with 100% similarity]
share/skin/default/layout/value/number.tt [moved from share/skin/default/layout/value/number with 100% similarity]
share/skin/default/layout/value/related_object.tt [moved from share/skin/default/layout/value/related_object with 100% similarity]
share/skin/default/layout/value/string.tt [moved from share/skin/default/layout/value/string with 100% similarity]
share/skin/default/layout/value/text.tt [moved from share/skin/default/layout/value/text with 100% similarity]

index 9a0ed59..008ce1d 100644 (file)
@@ -1,15 +1,5 @@
 using_frontend_proxy 1
 
-<Controller Foo>
-  <action update>
-    <ViewPort>
-      <Field baz_list>
-        layout checkbox_group
-      </Field>
-    </ViewPort>
-  </Action>
-</Controller>
-
 <View Site>
   skin_name default
 </View>
\ No newline at end of file
index 2cf7681..644a20f 100644 (file)
@@ -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;
index 542af62..5691ff1 100644 (file)
@@ -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);
+  };
+
 };
 
 
index 2edb06a..6c84967 100644 (file)
@@ -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} &&
index 94e4de1..7430413 100644 (file)
@@ -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';
 
 };
 
index bb2f526..bb7494c 100644 (file)
@@ -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
index 8ed46e0..24cb098 100644 (file)
@@ -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' ];
index 9e40936..83e3076 100644 (file)
@@ -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
similarity index 76%
rename from share/skin/default/layout/list_view
rename to share/skin/default/layout/grid_view.tt
index d4826b0..3205e60 100644 (file)
@@ -2,12 +2,8 @@
 
 <table>
   [% header %]
-<tbody>
-  [% rows %]
-</tbody>
-<tfoot>
+  [% body   %]
   [% footer %]
-</tfoot>
 </table>
 
 =for layout header
 
 <td> [% content %] </td>
 
-=for layout rows
+=for layout body
 
 <tbody>
   [% content %]
 </tbody>
 
+=for layout body_row
+
+<tr> [% content %] </tr>
+
+=for layout body_cell
+
+<td> [% content %] </td>
+
 =cut
similarity index 76%
rename from share/skin/default/layout/grid_view
rename to share/skin/default/layout/list_view.tt
index d4826b0..3205e60 100644 (file)
@@ -2,12 +2,8 @@
 
 <table>
   [% header %]
-<tbody>
-  [% rows %]
-</tbody>
-<tfoot>
+  [% body   %]
   [% footer %]
-</tfoot>
 </table>
 
 =for layout header
 
 <td> [% content %] </td>
 
-=for layout rows
+=for layout body
 
 <tbody>
   [% content %]
 </tbody>
 
+=for layout body_row
+
+<tr> [% content %] </tr>
+
+=for layout body_cell
+
+<td> [% content %] </td>
+
 =cut