Commit | Line | Data |
8a293e2e |
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 { {} }); |
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 | |
151 | 1; |