do not include .git directory
[catagits/Reaction.git] / lib / Reaction / UI / LayoutSet.pm
index 1cdb0e9..1ebc646 100644 (file)
@@ -3,56 +3,105 @@ package Reaction::UI::LayoutSet;
 use Reaction::Class;
 use File::Spec;
 
-class LayoutSet which {
-
-  has 'fragments' => (is => 'ro', default => sub { {} });
-
-  has 'name' => (is => 'ro', required => 1);
-
-  has 'source_file' => (is => 'rw', lazy_fail => 1);
-
-  implements 'BUILD' => as {
-    my ($self, $args) = @_;
-    my @path = @{$args->{search_path}||[]};
-    confess "No search_path provided" unless @path;
-    my $found;
-    SEARCH: foreach my $path (@path) {
-      my $cand = $path->file($self->name);
-      print STDERR $cand,"\n";
-      if ($cand->stat) {
-        $self->_load_file($cand);
-        $found = 1;
-        last SEARCH;
+use namespace::clean -except => [ qw(meta) ];
+
+
+has 'layouts' => (is => 'ro', default => sub { {} });
+
+has 'name' => (is => 'ro', required => 1);
+
+has 'source_file' => (is => 'ro', required => 1);
+
+has 'widget_class' => (
+  is => 'rw', lazy_fail => 1, predicate => 'has_widget_class'
+);
+
+has 'widget_type' => (is => 'rw', lazy_build => 1);
+
+has 'super' => (is => 'rw', predicate => 'has_super');
+sub BUILD {
+  my ($self, $args) = @_;
+  my @path = @{$args->{search_path}||[]};
+  confess "No skin object provided" unless $args->{skin};
+  confess "No top skin object provided" unless $args->{top_skin};
+  $self->_load_file($self->source_file, $args);
+  unless ($self->has_widget_class) {
+    $self->widget_class($args->{skin}->widget_class_for($self));
+  }
+};
+sub widget_order_for {
+  my ($self, $name) = @_;
+  return (
+    ($self->has_layout($name)
+      ? ([ $self->widget_class, $self ]) #;
+      : ()),
+    ($self->has_super
+      ? ($self->super->widget_order_for($name))
+      : ()),
+  );
+};
+sub layout_names {
+  my ($self) = @_;
+  my %seen;
+  return [
+    grep { !$seen{$_}++ }
+      keys %{shift->layouts},
+      ($self->has_super
+        ? (@{$self->super->layout_names})
+        : ())
+  ];
+};
+sub has_layout { exists $_[0]->layouts->{$_[1]} };
+sub _load_file {
+  my ($self, $file, $build_args) = @_;
+  my $data = $file->slurp;
+  utf8::decode($data)
+    unless utf8::is_utf8($data);
+  my $layouts = $self->layouts;
+  # cheesy match for "=for layout name ... =something"
+  # final split group also handles last in file, (?==) is lookahead
+  # assertion for '=' so "=for layout name1 ... =for layout name2"
+  # doesn't have the match pos go past the latter = and lose name2
+  while ($data =~ m/=(.*?)\n(.*?)(?:\n(?==)|$)/sg) {
+    my ($data, $text) = ($1, $2);
+    if ($data =~ /^for layout (\S+)/) {
+      my $fname = $1;
+      $text =~ s/^(?:\s*\r?\n)+//; #remove leading empty lines
+      $text =~ s/[\s\r\n]+$//;     #remove trailing whitespace
+      $layouts->{$fname} = $text;
+    } elsif ($data =~ /^extends (\S+)/) {
+      my $super_name = $1;
+      my $skin;
+      if ($super_name eq 'NEXT') {
+        confess "No next skin and layout extends NEXT"
+          unless $build_args->{next_skin};
+        $skin = $build_args->{next_skin};
+        $super_name = $self->name;
+      } else {
+        $skin = $build_args->{top_skin};
       }
+      $self->super($skin->create_layout_set($super_name));
+    } elsif ($data =~ /^widget (\S+)/) {
+      my $widget_type = $1;
+      $self->widget_type($1);
+    } elsif ($data =~ /^cut/) {
+      # no-op
+    } else {
+      confess "Unparseable directive ${data} in ${file}";
     }
-    confess "Unable to load file for LayoutSet ".$self->name unless $found;
-  };
-
-  implements '_load_file' => as {
-    my ($self, $file) = @_;
-    my $data = $file->slurp;
-    my $fragments = $self->fragments;
-    # cheesy match for "=for layout fragmentname ... =something"
-    # final split group also handles last in file, (?==) is lookahead
-    # assertion for '=' so "=for layout fragment1 ... =for layout fragment2"
-    # doesn't have the match pos go past the latter = and lose fragment2
-    while ($data =~ m/=for layout (.*?)\n(.+?)(?:\n(?==)|$)/sg) {
-      my ($fname, $text) = ($1, $2);
-      $fragments->{$fname} = $text;
-    }
-    $self->source_file($file);
-  };
+  }
+};
+sub _build_widget_type {
+  my ($self) = @_;
+  my $widget = join('',   map { ucfirst($_) } split('_', $self->name));
+  $widget    = join('::', map { ucfirst($_) } split('/', $widget));
 
-  implements 'widget_type' => as {
-    my ($self) = @_;
-    my $widget = join('',   map { ucfirst($_) } split('_', $self->name));
-    $widget    = join('::', map { ucfirst($_) } split('/', $widget));
+  #print STDERR "--- ", $self->name, " maps to widget $widget \n";
 
-    print STDERR "--- ", $self->name, " maps to widget $widget \n";
+  return $widget;
+};
 
-    return $widget;
-  };
+__PACKAGE__->meta->make_immutable;
 
-};
 
 1;