use Reaction::Class;
use aliased 'Reaction::UI::ViewPort';
+use aliased 'Reaction::UI::ViewPort::SiteLayout';
#
# Sets the actions in this controller to be registered with no prefix
sub base :Chained('/') :PathPart('') :CaptureArgs(0) {
my ($self, $c) = @_;
- $self->push_viewport(ViewPort, layout => 'layout');
+ $self->push_viewport(SiteLayout,
+ title => 'ComponentUI test title',
+ static_base_uri => "${\$c->uri_for('static')}",
+ );
}
sub root :Chained('base') :PathPart('') :Args(0) {
has 'name' => (is => 'ro', required => 1);
- has 'source_file' => (is => 'rw', lazy_fail => 1);
-
- has 'file_extension'=> (isa => 'Str', is => 'rw', lazy_build => 1);
+ has 'source_file' => (is => 'ro', required => 1);
has 'widget_class' => (
is => 'rw', lazy_fail => 1, predicate => 'has_widget_class'
has 'super' => (is => 'rw', predicate => 'has_super');
- implements _build_file_extension => as { 'html' };
-
implements 'BUILD' => as {
my ($self, $args) = @_;
my @path = @{$args->{search_path}||[]};
- confess "No search_path provided" unless @path;
confess "No view object provided" unless $args->{view};
- my $found;
- my $ext = $self->file_extension;
- SEARCH: foreach my $path (@path) {
- my $cand = $path->file($self->name . ".${ext}");
- #print STDERR $cand,"\n";
- if ($cand->stat) {
- $self->_load_file($cand, $args);
- $found = 1;
- last SEARCH;
- }
- }
- confess "Unable to load file for LayoutSet ".$self->name unless $found;
+ $self->_load_file($self->source_file, $args);
unless ($self->has_widget_class) {
$self->widget_class($args->{view}->widget_class_for($self));
}
confess "Unparseable directive ${data}";
}
}
- $self->source_file($file);
};
implements '_build_widget_type' => as {
has 'tt_view' => (is => 'rw', isa => View, lazy_fail => 1);
- implements _build_file_extension => as { 'tt' };
+ implements file_extension => as { 'tt' };
implements 'BUILD' => as {
my ($self, $args) = @_;
class TT is RenderingContext, which {
- has 'iter_class' => (
- is => 'ro', required => 1,
- default => sub { 'Reaction::UI::Renderer::TT::Iter'; },
- );
-
our $body;
implements 'dispatch' => as {
};
};
-
-package Reaction::UI::Renderer::TT::Iter;
-
-use overload (
- q{""} => 'stringify',
- fallback => 1
-);
-
-sub pos { shift->{pos} }
-
-sub new {
- my ($class, $cr, $rctx) = @_;
- bless({ rctx => $rctx, cr => $cr, pos => 0 }, $class);
-}
-
-sub next {
- my $self = shift;
- $self->{pos}++;
- my $next = $self->{cr}->();
- return unless $next;
- return sub { $next->($self->{rctx}) };
-}
-
-sub all {
- my $self = shift;
- my @all;
- while (my $e = $self->next) {
- push(@all, $e);
- }
- \@all;
-}
-
-sub stringify {
- my $self = shift;
- my $res = '';
- foreach my $e (@{$self->all}) {
- $res .= $e->();
- }
- $res;
-}
1;
--- /dev/null
+package Reaction::UI::Skin;
+
+use Reaction::Class;
+
+# declaring dependencies
+use Reaction::UI::LayoutSet;
+use Reaction::UI::RenderingContext;
+
+use aliased 'Path::Class::Dir';
+
+class Skin which {
+
+ has '_layout_set_cache' => (is => 'ro', default => sub { {} });
+
+ has 'skin_base_path' => (is => 'ro', isa => Dir, required => 1);
+
+ has 'view' => (
+ is => 'ro', required => 1, weak_ref => 1,
+ handles => [ qw(layout_set_class) ],
+ );
+
+ has 'super' => (
+ is => 'rw', isa => Skin, required => 0, predicate => 'has_super',
+ );
+
+ sub BUILD {
+ my ($self) = @_;
+ $self->_load_skin_config;
+ }
+
+ implements '_load_skin_config' => as {
+ my ($self) = @_;
+ my $base = $self->skin_base_path;
+ confess "No such skin base directory ${base}"
+ unless -d $base;
+ }
+
+ implements 'create_layout_set' => as {
+ my ($self, $name) = @_;
+ if (my $path = $self->layout_path_for($name)) {
+ return $self->layout_set_class->new(
+ $self->layout_set_args_for($name),
+ source_file => $path,
+ );
+ }
+ if ($self->has_super) {
+ return $self->super->create_layout_set($name);
+ }
+ confess "Couldn't find layout set file for ${name}";
+ };
+
+ implements 'layout_set_args_for' => as {
+ my ($self, $name) = @_;
+ return (
+ name => $name,
+ view => $self->view,
+ skin => $self,
+ ($self->has_super ? (next_skin => $self->super) : ()),
+ $self->view->layout_set_args_for($name),
+ );
+ };
+
+ implements 'layout_path_for' => as {
+ my ($self, $layout) = @_;
+ my $file_name = join(
+ '.', $layout, $self->layout_set_class->file_extension
+ );
+ my $path = $self->our_path_for_type('layout')
+ ->file($file_name);
+ return (-e $path ? $path : undef);
+ };
+
+ implements 'search_path_for_type' => as {
+ my ($self, $type) = @_;
+ return [
+ $self->our_path_for_type($type),
+ ($self->has_super
+ ? @{$self->super->search_path_for_type($type)}
+ : ()
+ )
+ ];
+ };
+
+ implements 'our_path_for_type' => as {
+ my ($self, $type) = @_;
+ return $self->skin_base_path->subdir($type)
+ };
+
+};
+
+1;
# declaring dependencies
use Reaction::UI::LayoutSet;
use Reaction::UI::RenderingContext;
+use aliased 'Reaction::UI::Skin';
+use aliased 'Path::Class::Dir';
class View which {
- has '_layout_set_cache' => (is => 'ro', default => sub { {} });
has '_widget_class_cache' => (is => 'ro', default => sub { {} });
has '_widget_cache' => (is => 'ro', default => sub { {} });
+ has '_layout_set_cache' => (is => 'ro', default => sub { {} });
+
has 'app' => (is => 'ro', required => 1);
has 'skin_name' => (is => 'ro', required => 1);
+ has 'skin' => (
+ is => 'ro', lazy_build => 1,
+ handles => [ qw(create_layout_set search_path_for_type) ]
+ );
+
has 'layout_set_class' => (is => 'ro', lazy_build => 1);
has 'rendering_context_class' => (is => 'ro', lazy_build => 1);
return $self->find_related_class('RenderingContext');
};
+ implements '_build_skin' => as {
+ my ($self) = @_;
+ Skin->new(
+ name => $self->skin_name, view => $self,
+ skin_base_path => # returns a File, not a Dir. Thanks, Catalyst.
+ Dir->new($self->app->path_to('share', 'skin', $self->skin_name)),
+ );
+ };
+
implements 'COMPONENT' => as {
my ($class, $app, $args) = @_;
return $class->new(%{$args||{}}, app => $app);
};
- sub BUILD{
- my $self = shift;
- my $skin_name = $self->skin_name;
- #XXX i guess we will add the path to installed reaction templates here
- my $skin_path = $self->app->path_to('share','skin',$skin_name);
- confess("'${skin_path}' is not a valid path for skin '${skin_name}'")
- unless -d $skin_path;
- }
-
implements 'render_window' => as {
my ($self, $window) = @_;
my $root_vp = $window->focus_stack->vp_head;
implements 'widget_class_for' => as {
my ($self, $layout_set) = @_;
my $base = $self->blessed;
- my $tail = $layout_set->widget_type;
- my $lset_name = $layout_set->name;
- # eventually more stuff will go here i guess?
+ my $widget_type = $layout_set->widget_type;
my $app_name = ref $self->app || $self->app;
- my $cache = $self->_widget_class_cache;
- return $cache->{ $lset_name } if exists $cache->{ $lset_name };
-
- my @search_path = ($base, $app_name, 'Reaction::UI');
- my @haystack = map { join '::', $_, 'Widget', $tail } @search_path;
- for my $class (@haystack){
- #here we should throw if exits and error instead of eating the error
- #only next when !exists
- eval { Class::MOP::load_class($class) };
- #$@ ? next : return $class;
- #warn "Loaded ${class}" unless $@;
- $@ ? next : return $cache->{ $lset_name } = $class;
- }
- confess "Couldn't load widget '$tail' for layout '$lset_name': tried: " .
- join(", ", @haystack);
+ return $self->_widget_class_cache->{$widget_type} ||= do {
+
+ my @search_path = ($base, $app_name, 'Reaction::UI');
+ my @haystack = map { join('::', $_, 'Widget', $widget_type) }
+ @search_path;
+ my $found;
+ foreach my $class (@haystack) {
+ #here we should throw if exits and error instead of eating the error
+ #only next when !exists
+ eval { Class::MOP::load_class($class) };
+ #$@ ? next : return $class;
+ #warn "Loaded ${class}" unless $@;
+ #warn "Boom loading ${class}: $@" if $@;
+ unless ($@) {
+ $found = $class;
+ last;
+ }
+ }
+ unless ($found) {
+ confess "Couldn't load widget '${widget_type}'"
+ ." for layout '${\$layout_set->name}':"
+ ." tried: ".join(", ", @haystack);
+ }
+ $found;
+ };
};
implements 'layout_set_for' => as {
return $cache->{$lset_name} ||= $self->create_layout_set($lset_name);
};
- implements 'create_layout_set' => as {
- my ($self, $name) = @_;
- return $self->layout_set_class->new(
- $self->layout_set_args_for($name),
- );
- };
-
implements 'find_related_class' => as {
my ($self, $rel) = @_;
my $own_class = ref($self) || $self;
confess "Unable to find related ${rel} class for ${own_class}";
};
- implements 'layout_set_args_for' => as {
- my ($self, $name) = @_;
- return (
- name => $name,
- search_path => $self->layout_search_path,
- view => $self,
- );
- };
-
- implements 'layout_search_path' => as {
- my ($self) = @_;
- return $self->search_path_for_type('layout');
- };
-
- implements 'search_path_for_type' => as {
- my ($self, $type) = @_;
- return [ $self->app->path_to('share','skin',$self->skin_name,$type) ];
- };
-
implements 'create_rendering_context' => as {
my ($self, @args) = @_;
return $self->rendering_context_class->new(
return ();
};
+ implements 'layout_set_args_for' => as {
+ return ();
+ };
+
};
1;
--- /dev/null
+package Reaction::UI::ViewPort::SiteLayout;
+
+use Reaction::Class;
+use aliased 'Reaction::UI::ViewPort';
+
+class SiteLayout is ViewPort, which {
+
+ has 'title' => (isa => 'Str', is => 'rw', lazy_fail => 1);
+
+ has 'static_base_uri' => (isa => 'Str', is => 'rw', lazy_fail => 1);
+
+};
+
+1;
--- /dev/null
+package Reaction::UI::Widget::SiteLayout;
+
+use Reaction::UI::WidgetClass;
+use aliased 'Reaction::UI::Widget::Container';
+
+class SiteLayout is Container, which {
+
+ after fragment widget {
+ arg static_base => $_{viewport}->static_base_uri;
+ arg title => $_{viewport}->title;
+ };
+
+};
+
+1;
-=widget Container
-
=for layout widget
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">