skin inheritnace within a single search path
[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 'view' => (
18     is => 'ro', required => 1, weak_ref => 1,
19     handles => [ qw(layout_set_class) ],
20   );
21
22   has 'super' => (
23     is => 'rw', isa => Skin, required => 0, predicate => 'has_super',
24   );
25
26   sub BUILD {
27     my ($self) = @_;
28     $self->_load_skin_config;
29   }
30
31   implements '_load_skin_config' => as {
32     my ($self) = @_;
33     my $base = $self->skin_base_path;
34     confess "No such skin base directory ${base}"
35       unless -d $base;
36     if (-e (my $conf_file = $base->file('skin.conf'))) {
37       # we get [ { $file => $conf } ]
38       my ($cfg) = values %{
39                     Config::Any->load_files({
40                       files => [ $conf_file ], use_ext => 1
41                     })->[0]
42                   };
43       if (my $super_name = $cfg->{extends}) {
44         my $super_dir = $base->parent->subdir($super_name);
45         my $super = $self->new(
46           view => $self->view, skin_base_path => $super_dir
47         );
48         $self->super($super);
49       }
50     }
51   }
52
53   implements 'create_layout_set' => as {
54     my ($self, $name) = @_;
55     if (my $path = $self->layout_path_for($name)) {
56       return $self->layout_set_class->new(
57                $self->layout_set_args_for($name),
58                source_file => $path,
59              );
60     }
61     if ($self->has_super) {
62       return $self->super->create_layout_set($name);
63     }
64     confess "Couldn't find layout set file for ${name}";
65   };
66
67   implements 'layout_set_args_for' => as {
68     my ($self, $name) = @_;
69     return (
70       name => $name,
71       view => $self->view,
72       skin => $self,
73       ($self->has_super ? (next_skin => $self->super) : ()),
74       $self->view->layout_set_args_for($name),
75     );
76   };
77
78   implements 'layout_path_for' => as {
79     my ($self, $layout) = @_;
80     my $file_name = join(
81       '.', $layout, $self->layout_set_class->file_extension
82     );
83     my $path = $self->our_path_for_type('layout')
84                     ->file($file_name);
85     return (-e $path ? $path : undef);
86   };
87
88   implements 'search_path_for_type' => as {
89     my ($self, $type) = @_;
90     return [
91       $self->our_path_for_type($type),
92       ($self->has_super
93         ? @{$self->super->search_path_for_type($type)}
94         : ()
95       )
96     ];
97   };
98
99   implements 'our_path_for_type' => as {
100     my ($self, $type) = @_;
101     return $self->skin_base_path->subdir($type)
102   };
103
104 };
105
106 1;