work in progress, listview still broken
[catagits/Reaction.git] / lib / Reaction / UI / LayoutSet.pm
1 package Reaction::UI::LayoutSet;
2
3 use Reaction::Class;
4 use File::Spec;
5
6 class LayoutSet which {
7
8   has 'layouts' => (is => 'ro', default => sub { {} });
9
10   has 'name' => (is => 'ro', required => 1);
11
12   has 'source_file' => (is => 'rw', lazy_fail => 1);
13   has 'file_extension'=> (isa => 'Str', is => 'rw', lazy_build => 1);
14
15   has 'widget_class' => (
16     is => 'rw', lazy_fail => 1, predicate => 'has_widget_class'
17   );
18
19   has 'super' => (is => 'rw', predicate => 'has_super');
20
21   implements _build_file_extension => as { 'html' };
22
23   implements 'BUILD' => as {
24     my ($self, $args) = @_;
25     my @path = @{$args->{search_path}||[]};
26     confess "No search_path provided" unless @path;
27     confess "No view object provided" unless $args->{view};
28     my $found;
29     my $ext = $self->file_extension;
30     SEARCH: foreach my $path (@path) {
31       my $cand = $path->file($self->name . ".${ext}");
32       #print STDERR $cand,"\n";
33       if ($cand->stat) {
34         $self->_load_file($cand, $args);
35         $found = 1;
36         last SEARCH;
37       }
38     }
39     confess "Unable to load file for LayoutSet ".$self->name unless $found;
40     unless ($self->has_widget_class) {
41       $self->widget_class($args->{view}->widget_class_for($self));
42     }
43   };
44
45   implements 'widget_order_for' => as {
46     my ($self, $name) = @_;
47     return (
48       ($self->has_layout($name)
49         ? ([ $self->widget_class, $self ]) #;
50         : ()),
51       ($self->has_super
52         ? ($self->super->widget_order_for($name))
53         : ()),
54     );
55   };
56
57   implements 'layout_names' => as {
58     my ($self) = @_;
59     my %seen;
60     return [
61       grep { !$seen{$_}++ }
62         keys %{shift->layouts},
63         ($self->has_super
64           ? (@{$self->super->layout_names})
65           : ())
66     ];
67   };
68
69   implements 'has_layout' => as { exists $_[0]->layouts->{$_[1]} };
70
71   implements '_load_file' => as {
72     my ($self, $file, $build_args) = @_;
73     my $data = $file->slurp;
74     my $layouts = $self->layouts;
75     # cheesy match for "=for layout name ... =something"
76     # final split group also handles last in file, (?==) is lookahead
77     # assertion for '=' so "=for layout name1 ... =for layout name2"
78     # doesn't have the match pos go past the latter = and lose name2
79     while ($data =~ m/=(.*?)\n(.*?)(?:\n(?==)|$)/sg) {
80       my ($data, $text) = ($1, $2);
81       if ($data =~ /^for layout (\S+)/) {
82         my $fname = $1;
83         $layouts->{$fname} = $text;
84       } elsif ($data =~ /^extends (\S+)/) {
85         my $super_name = $1;
86         $self->super($build_args->{view}->create_layout_set($super_name))
87       } elsif ($data =~ /^cut/) {
88         # no-op
89       } else {
90         confess "Unparseable directive ${data}";
91       }
92     }
93     $self->source_file($file);
94   };
95
96   implements 'widget_type' => as {
97     my ($self) = @_;
98     my $widget = join('',   map { ucfirst($_) } split('_', $self->name));
99     $widget    = join('::', map { ucfirst($_) } split('/', $widget));
100
101     #print STDERR "--- ", $self->name, " maps to widget $widget \n";
102
103     return $widget;
104   };
105
106 };
107
108 1;