defaults.conf support works much better if I commit the damn thing
[catagits/Reaction.git] / lib / Reaction / UI / Skin.pm
CommitLineData
8a293e2e 1package Reaction::UI::Skin;
2
3use Reaction::Class;
4
5# declaring dependencies
6use Reaction::UI::LayoutSet;
7use Reaction::UI::RenderingContext;
8
9use aliased 'Path::Class::Dir';
10
11class Skin which {
12
13 has '_layout_set_cache' => (is => 'ro', default => sub { {} });
a2a5872b 14 has '_widget_class_cache' => (is => 'ro', default => sub { {} });
8a293e2e 15
16 has 'skin_base_path' => (is => 'ro', isa => Dir, required => 1);
17
a2a5872b 18 has 'widget_search_path' => (
19 is => 'rw', isa => 'ArrayRef', requred => 1, default => sub { [] }
20 );
349a57a4 21
8a293e2e 22 has 'view' => (
23 is => 'ro', required => 1, weak_ref => 1,
a2a5872b 24 handles => [ qw(layout_set_class) ],
8a293e2e 25 );
26
27 has 'super' => (
28 is => 'rw', isa => Skin, required => 0, predicate => 'has_super',
29 );
30
31 sub BUILD {
32 my ($self) = @_;
33 $self->_load_skin_config;
34 }
35
36 implements '_load_skin_config' => as {
37 my ($self) = @_;
38 my $base = $self->skin_base_path;
39 confess "No such skin base directory ${base}"
40 unless -d $base;
a2a5872b 41 my $lst = sub { (ref $_[0] eq 'ARRAY') ? $_[0] : [$_[0]] };
349a57a4 42 my @files = (
43 $base->parent->file('defaults.conf'), $base->file('skin.conf')
44 );
45 # we get [ { $file => $conf }, ... ]
46 my %cfg = (map { %{(values %{$_})[0]} }
47 @{Config::Any->load_files({
48 files => [ grep { -e $_ } @files ],
49 use_ext => 1,
50 })}
51 );
52 if (my $super_name = $cfg{extends}) {
53 my $super_dir = $base->parent->subdir($super_name);
54 my $super = $self->new(
55 view => $self->view, skin_base_path => $super_dir
56 );
57 $self->super($super);
58 }
59 if (exists $cfg{widget_search_path}) {
60 $self->widget_search_path($lst->($cfg{widget_search_path}));
61 } else {
a2a5872b 62 confess "No widget_search_path in defaults.conf or skin.conf"
63 ." and no search path provided from super skin"
64 unless $self->full_widget_search_path;
7c2bcb55 65 }
8a293e2e 66 }
67
68 implements 'create_layout_set' => as {
69 my ($self, $name) = @_;
70 if (my $path = $self->layout_path_for($name)) {
71 return $self->layout_set_class->new(
72 $self->layout_set_args_for($name),
73 source_file => $path,
74 );
75 }
76 if ($self->has_super) {
77 return $self->super->create_layout_set($name);
78 }
79 confess "Couldn't find layout set file for ${name}";
80 };
81
82 implements 'layout_set_args_for' => as {
83 my ($self, $name) = @_;
84 return (
85 name => $name,
8a293e2e 86 skin => $self,
87 ($self->has_super ? (next_skin => $self->super) : ()),
88 $self->view->layout_set_args_for($name),
89 );
90 };
91
92 implements 'layout_path_for' => as {
93 my ($self, $layout) = @_;
94 my $file_name = join(
349a57a4 95 '.', $layout, $self->view->layout_set_file_extension
8a293e2e 96 );
97 my $path = $self->our_path_for_type('layout')
98 ->file($file_name);
99 return (-e $path ? $path : undef);
100 };
101
102 implements 'search_path_for_type' => as {
103 my ($self, $type) = @_;
104 return [
105 $self->our_path_for_type($type),
106 ($self->has_super
107 ? @{$self->super->search_path_for_type($type)}
108 : ()
109 )
110 ];
111 };
112
113 implements 'our_path_for_type' => as {
114 my ($self, $type) = @_;
115 return $self->skin_base_path->subdir($type)
116 };
117
a2a5872b 118 implements 'full_widget_search_path' => as {
119 my ($self) = @_;
120 return (
121 @{$self->widget_search_path},
122 ($self->has_super ? $self->super->full_widget_search_path : ())
123 );
124 };
125
126 implements 'widget_class_for' => as {
127 my ($self, $layout_set) = @_;
128 my $base = $self->blessed;
129 my $widget_type = $layout_set->widget_type;
130 return $self->_widget_class_cache->{$widget_type} ||= do {
131
132 my @search_path = $self->full_widget_search_path;
133 my @haystack = map {join('::', $_, $widget_type)} @search_path;
134
135 foreach my $class (@haystack) {
136 #if the class is already loaded skip the call to Installed etc.
137 return $class if Class::MOP::is_class_loaded($class);
138 next unless Class::Inspector->installed($class);
139
140 my $ok = eval { Class::MOP::load_class($class) };
141 confess("Failed to load widget '${class}': $@") if $@;
142 return $class;
143 }
144 confess "Couldn't locate widget '${widget_type}' for layout "
145 ."'${\$layout_set->name}': tried: ".join(", ", @haystack);
146 };
147 };
148
8a293e2e 149};
150
1511;