8a296006b688da6efad49607251ccad082078b29
[catagits/Reaction.git] / lib / Reaction / UI / Skin.pm
1 package Reaction::UI::Skin;
2
3 use Reaction::Class;
4
5 # declaring dependencies
6 use Reaction::UI::LayoutSet;
7 use Reaction::UI::RenderingContext;
8
9 use aliased 'Path::Class::Dir';
10
11 class Skin which {
12
13   has '_layout_set_cache'   => (is => 'ro', default => sub { {} });
14
15   has 'skin_base_path' => (is => 'ro', isa => Dir, required => 1);
16
17   has 'widget_search_path' => (is => 'rw', isa => 'ArrayRef', lazy_fail => 1);
18
19   has 'view' => (
20     is => 'ro', required => 1, weak_ref => 1,
21     handles => [ qw(layout_set_class widget_class_for) ],
22   );
23
24   has 'super' => (
25     is => 'rw', isa => Skin, required => 0, predicate => 'has_super',
26   );
27
28   sub BUILD {
29     my ($self) = @_;
30     $self->_load_skin_config;
31   }
32
33   implements '_load_skin_config' => as {
34     my ($self) = @_;
35     my $base = $self->skin_base_path;
36     confess "No such skin base directory ${base}"
37       unless -d $base;
38     my $lst = sub { (ref $_[0] eq 'ARRAY') ? $_[0]: [$_[0]] };
39     my @files = (
40       $base->parent->file('defaults.conf'), $base->file('skin.conf')
41     );
42     # we get [ { $file => $conf }, ... ]
43     my %cfg = (map { %{(values %{$_})[0]} }
44                 @{Config::Any->load_files({
45                   files => [ grep { -e $_ } @files ],
46                   use_ext => 1,
47                 })}
48               );
49     if (my $super_name = $cfg{extends}) {
50       my $super_dir = $base->parent->subdir($super_name);
51       my $super = $self->new(
52         view => $self->view, skin_base_path => $super_dir
53       );
54       $self->super($super);
55     }
56     if (exists $cfg{widget_search_path}) {
57       $self->widget_search_path($lst->($cfg{widget_search_path}));
58     } else {
59       confess "No widget_search_path in defaults.conf or skin.conf";
60     }
61   }
62
63   implements 'create_layout_set' => as {
64     my ($self, $name) = @_;
65     if (my $path = $self->layout_path_for($name)) {
66       return $self->layout_set_class->new(
67                $self->layout_set_args_for($name),
68                source_file => $path,
69              );
70     }
71     if ($self->has_super) {
72       return $self->super->create_layout_set($name);
73     }
74     confess "Couldn't find layout set file for ${name}";
75   };
76
77   implements 'layout_set_args_for' => as {
78     my ($self, $name) = @_;
79     return (
80       name => $name,
81       skin => $self,
82       ($self->has_super ? (next_skin => $self->super) : ()),
83       $self->view->layout_set_args_for($name),
84     );
85   };
86
87   implements 'layout_path_for' => as {
88     my ($self, $layout) = @_;
89     my $file_name = join(
90       '.', $layout, $self->view->layout_set_file_extension
91     );
92     my $path = $self->our_path_for_type('layout')
93                     ->file($file_name);
94     return (-e $path ? $path : undef);
95   };
96
97   implements 'search_path_for_type' => as {
98     my ($self, $type) = @_;
99     return [
100       $self->our_path_for_type($type),
101       ($self->has_super
102         ? @{$self->super->search_path_for_type($type)}
103         : ()
104       )
105     ];
106   };
107
108   implements 'our_path_for_type' => as {
109     my ($self, $type) = @_;
110     return $self->skin_base_path->subdir($type)
111   };
112
113 };
114
115 1;