aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoredenc <edenc@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7>2008-02-11 18:38:38 +0000
committeredenc <edenc@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7>2008-02-11 18:38:38 +0000
commit88c723592b7f440af37b1e4c26c708c7998e2d6c (patch)
tree788c2e88aa56f15e09162681620b3a2037e9bc82
parentddd1dc657e6cf50128e94c103411d758538c6f99 (diff)
parent5d86015dcae72913eb60b0111961733213601873 (diff)
downloadreaction-88c723592b7f440af37b1e4c26c708c7998e2d6c.tar.gz
reaction-88c723592b7f440af37b1e4c26c708c7998e2d6c.zip
r20434@hades (orig r549): groditi | 2008-01-30 18:09:54 -0300
better error messages / dont swallow error when a widget has compile errors r20435@hades (orig r550): wreis | 2008-01-30 18:10:59 -0300 nuked CreditCard type r20442@hades (orig r553): groditi | 2008-01-30 19:20:28 -0300 ChooseMany fix r20443@hades (orig r554): matthewt | 2008-01-31 05:07:33 -0300 change dist name to Reaction r20444@hades (orig r555): matthewt | 2008-01-31 07:07:29 -0300 added widget_search_path option (ignored), defaults.conf support and moved file extension to View r20445@hades (orig r556): matthewt | 2008-01-31 07:47:10 -0300 remove vestigial override r20446@hades (orig r557): matthewt | 2008-01-31 08:06:10 -0300 removed view arg to LayoutSet, moved to using skin to resolve widget class, added temporary proxy to View from Skin r20447@hades (orig r558): matthewt | 2008-01-31 08:35:47 -0300 moved widget class search path to Skin r20448@hades (orig r559): matthewt | 2008-01-31 09:40:10 -0300 defaults.conf support works much better if I commit the damn thing r20449@hades (orig r560): matthewt | 2008-01-31 10:32:16 -0300 rework skin path handling r20451@hades (orig r562): matthewt | 2008-01-31 13:23:08 -0300 get rid of the 'title is undef' error r20452@hades (orig r563): matthewt | 2008-02-02 13:12:31 -0300 Collection field vp works again. r20453@hades (orig r564): matthewt | 2008-02-02 13:18:31 -0300 vestigial classes, nuked r20454@hades (orig r565): matthewt | 2008-02-02 13:33:15 -0300 sync_to_action not required, will trigger off value set r20455@hades (orig r566): matthewt | 2008-02-02 14:07:10 -0300 add value_is_required, add logic for sync of clearer on !required attrs r20456@hades (orig r567): wreis | 2008-02-02 14:52:14 -0300 fixed layout path for ArrayRef type r20457@hades (orig r568): groditi | 2008-02-02 15:40:58 -0300 the forwards on basic_page and basic_model_action were breaking config merging at the push viewport stage r20459@hades (orig r570): groditi | 2008-02-02 16:20:40 -0300 oops forward takes args as an arrayref and i forgot to remove that r20461@hades (orig r572): matthewt | 2008-02-03 06:54:30 -0300 generate clearer for !required action attributes r20462@hades (orig r573): matthewt | 2008-02-03 06:55:15 -0300 datetime now handles optional using a wrap on value_string r20463@hades (orig r574): matthewt | 2008-02-03 09:44:08 -0300 make delayed setup components work so implements+does with required methods in the role works r20464@hades (orig r575): matthewt | 2008-02-03 09:48:41 -0300 kill vestigial adopt_value method in Field (since 'value' doesn't get set on a non-mutable field anyway) r20465@hades (orig r576): matthewt | 2008-02-03 10:48:49 -0300 vestigial widget class removed r20466@hades (orig r577): matthewt | 2008-02-03 11:03:21 -0300 simple mutable fields now use value_string (ChooseMany unconverted) r20467@hades (orig r578): matthewt | 2008-02-03 17:27:18 -0300 clean out pre-widget stuff r20468@hades (orig r579): groditi | 2008-02-03 21:18:36 -0300 burnt out, so writing some docs to clear my brain r20523@hades (orig r586): matthewt | 2008-02-07 05:52:10 -0300 better error on failed layout set lookup r20524@hades (orig r587): matthewt | 2008-02-07 05:52:49 -0300 pass _parent to collections r20525@hades (orig r588): matthewt | 2008-02-07 12:58:53 -0300 complete MooseX::Types port r20526@hades (orig r589): wreis | 2008-02-07 18:12:22 -0300 added meta_info for VP::SiteLayout r20530@hades (orig r590): matthewt | 2008-02-08 06:42:45 -0300 refactor Reflector a bit and eliminate some silent fail possibilities r20534@hades (orig r594): matthewt | 2008-02-09 14:23:48 -0300 alter skin loading precedence (layoutset path search always starts from toip except for =extends NEXT r20535@hades (orig r595): matthewt | 2008-02-09 14:24:23 -0300 prevent immutable creating new() methods for Reaction metaclasses r20536@hades (orig r596): matthewt | 2008-02-09 14:26:50 -0300 was part of skin precedence changes but bungled the command r20537@hades (orig r597): matthewt | 2008-02-09 14:27:38 -0300 avoid passwords going into <input> elements r20539@hades (orig r599): matthewt | 2008-02-10 07:48:52 -0300 _ prefixed writer doth not mean rw r20540@hades (orig r600): matthewt | 2008-02-10 07:49:18 -0300 fix method modifiers r20541@hades (orig r601): matthewt | 2008-02-10 08:57:26 -0300 get rid of extraneous whitepsace in textaeras r20542@hades (orig r602): matthewt | 2008-02-10 08:57:43 -0300 fix vp_args passing to actions
-rw-r--r--Makefile.PL6
-rw-r--r--lab/Reaction/Class.pm82
-rw-r--r--lib/Reaction/Class.pm15
-rw-r--r--lib/Reaction/InterfaceModel/Action/DBIC/Result/Delete.pm2
-rw-r--r--lib/Reaction/InterfaceModel/Action/DBIC/Result/Update.pm2
-rw-r--r--lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/Create.pm2
-rw-r--r--lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/DeleteAll.pm2
-rw-r--r--lib/Reaction/InterfaceModel/Action/User/ChangePassword.pm4
-rw-r--r--lib/Reaction/InterfaceModel/Action/User/Login.pm5
-rw-r--r--lib/Reaction/InterfaceModel/Action/User/ResetPassword.pm4
-rw-r--r--lib/Reaction/InterfaceModel/Action/User/SetPassword.pm5
-rw-r--r--lib/Reaction/InterfaceModel/Reflector/DBIC.pm41
-rw-r--r--lib/Reaction/Meta/Class.pm2
-rw-r--r--lib/Reaction/Meta/InterfaceModel/Action/Class.pm2
-rw-r--r--lib/Reaction/Meta/InterfaceModel/Object/Class.pm2
-rw-r--r--lib/Reaction/Types/Core.pm70
-rw-r--r--lib/Reaction/Types/CreditCard.pm49
-rw-r--r--lib/Reaction/Types/DBIC.pm8
-rw-r--r--lib/Reaction/Types/DateTime.pm18
-rw-r--r--lib/Reaction/Types/Email.pm8
-rw-r--r--lib/Reaction/UI/Controller/Collection.pm68
-rw-r--r--lib/Reaction/UI/Controller/Collection/CRUD.pm117
-rw-r--r--lib/Reaction/UI/Controller/Root.pm12
-rw-r--r--lib/Reaction/UI/LayoutSet.pm6
-rw-r--r--lib/Reaction/UI/LayoutSet/TT.pm2
-rw-r--r--lib/Reaction/UI/Renderer/XHTML.pm89
-rw-r--r--lib/Reaction/UI/Skin.pm120
-rw-r--r--lib/Reaction/UI/View.pm41
-rw-r--r--lib/Reaction/UI/View/TT.pm5
-rw-r--r--lib/Reaction/UI/ViewPort.pm11
-rw-r--r--lib/Reaction/UI/ViewPort/Action.pm29
-rw-r--r--lib/Reaction/UI/ViewPort/Collection/Grid/Member.pm4
-rw-r--r--lib/Reaction/UI/ViewPort/Field.pm6
-rw-r--r--lib/Reaction/UI/ViewPort/Field/ChooseMany.pm110
-rw-r--r--lib/Reaction/UI/ViewPort/Field/ChooseOne.pm122
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Collection.pm12
-rw-r--r--lib/Reaction/UI/ViewPort/Field/DateTime.pm2
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Array.pm1
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Boolean.pm7
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/ChooseMany.pm6
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/ChooseOne.pm34
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/DateTime.pm9
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Integer.pm8
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Number.pm7
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Password.pm8
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/String.pm8
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Mutable/Text.pm8
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Password.pm4
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Role/Mutable.pm48
-rw-r--r--lib/Reaction/UI/ViewPort/Field/Role/Mutable/Simple.pm36
-rw-r--r--lib/Reaction/UI/ViewPort/Field/TimeRange.pm4
-rw-r--r--lib/Reaction/UI/ViewPort/Object.pm4
-rw-r--r--lib/Reaction/UI/ViewPort/SiteLayout.pm5
-rw-r--r--lib/Reaction/UI/Widget/Field/Mutable.pm4
-rw-r--r--lib/Reaction/UI/Widget/Field/Mutable/ChooseMany.pm1
-rw-r--r--lib/Reaction/UI/Widget/Field/Mutable/ChooseOne.pm2
-rw-r--r--lib/Reaction/UI/Widget/Field/Mutable/Password.pm1
-rw-r--r--lib/Reaction/UI/Widget/SiteLayout.pm9
-rw-r--r--lib/Reaction/UI/Widget/Value/List.pm43
-rw-r--r--root/bar_form6
-rw-r--r--root/bar_list21
-rw-r--r--root/base/actionform1
-rw-r--r--root/base/button21
-rw-r--r--root/base/cancelbtn13
-rw-r--r--root/base/checkbox22
-rw-r--r--root/base/checkbox_group19
-rw-r--r--root/base/component64
-rw-r--r--root/base/displayfield/list17
-rw-r--r--root/base/displayfield/string13
-rw-r--r--root/base/displayfield/text13
-rw-r--r--root/base/displayfield/value_string13
-rw-r--r--root/base/displayfield_base23
-rw-r--r--root/base/dt_textfield16
-rw-r--r--root/base/dual_select_group42
-rw-r--r--root/base/error_40417
-rw-r--r--root/base/field_base27
-rw-r--r--root/base/fieldset20
-rw-r--r--root/base/file16
-rw-r--r--root/base/footer12
-rw-r--r--root/base/form_base77
-rw-r--r--root/base/header11
-rw-r--r--root/base/hidden15
-rw-r--r--root/base/hiddenarray17
-rw-r--r--root/base/image11
-rw-r--r--root/base/label17
-rw-r--r--root/base/listview60
-rw-r--r--root/base/listview_base124
-rw-r--r--root/base/objectview1
-rw-r--r--root/base/pager128
-rw-r--r--root/base/password14
-rw-r--r--root/base/radio14
-rw-r--r--root/base/radio_group17
-rw-r--r--root/base/resetbtn13
-rw-r--r--root/base/search_base14
-rw-r--r--root/base/select38
-rw-r--r--root/base/select_group14
-rw-r--r--root/base/submitbtn13
-rw-r--r--root/base/textarea15
-rw-r--r--root/base/textfield17
-rw-r--r--root/base/timerange44
-rw-r--r--root/base/timerangecollection60
-rw-r--r--root/base/view_base22
-rw-r--r--root/base/xhtml29
-rw-r--r--root/favicon.icobin2551 -> 0 bytes
-rw-r--r--root/index18
-rw-r--r--root/static/images/btn_120x50_built.pngbin3826 -> 0 bytes
-rw-r--r--root/static/images/btn_120x50_built_shadow.pngbin3681 -> 0 bytes
-rw-r--r--root/static/images/btn_120x50_powered.pngbin3862 -> 0 bytes
-rw-r--r--root/static/images/btn_120x50_powered_shadow.pngbin3673 -> 0 bytes
-rw-r--r--root/static/images/btn_88x31_built.pngbin2517 -> 0 bytes
-rw-r--r--root/static/images/btn_88x31_built_shadow.pngbin2274 -> 0 bytes
-rw-r--r--root/static/images/btn_88x31_powered.pngbin2542 -> 0 bytes
-rw-r--r--root/static/images/btn_88x31_powered_shadow.pngbin2304 -> 0 bytes
-rw-r--r--root/static/images/catalyst_logo.pngbin13710 -> 0 bytes
-rw-r--r--share/skin/componentui/layout/index.tt (renamed from share/skin/default/layout/index.tt)0
-rw-r--r--share/skin/componentui/skin.conf2
-rw-r--r--share/skin/default/layout/field/mutable/text.tt6
-rw-r--r--share/skin/default/layout/site_layout.tt9
-rw-r--r--share/skin/defaults.conf1
119 files changed, 591 insertions, 1968 deletions
diff --git a/Makefile.PL b/Makefile.PL
index 5cb3833..167a9fa 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,6 +1,6 @@
use inc::Module::Install 0.64;
-name 'ComponentUI';
+name 'Reaction';
all_from 'lib/ComponentUI.pm';
requires 'Catalyst' => '5.7002';
@@ -31,10 +31,10 @@ requires 'Email::MIME::Creator';
requires 'Text::CSV_XS';
requires 'Devel::Declare' => '0.001006';
requires 'MooseX::Types' => '0.04';
-requires 'Business::CreditCard' => '0.30';
requires 'Scalar::Util';
+requires 'File::ShareDir';
-catalyst;
+install_share;
install_script glob('script/*.pl');
diff --git a/lab/Reaction/Class.pm b/lab/Reaction/Class.pm
deleted file mode 100644
index f961baf..0000000
--- a/lab/Reaction/Class.pm
+++ /dev/null
@@ -1,82 +0,0 @@
-=head1 NAME
-
-Reaction::Class - Reaction class declaration syntax
-
-=head1 SYNOPSIS
-
-In My/Person.pm:
-
-=for example My::Person setup
-
- package My::Person;
-
- use Reaction::Class;
- use Reaction::Types::Core qw/Str/;
-
- class Person which {
-
- has 'name' => Str;
-
- has 'nickname' => optional Str;
-
- implements 'preferred_name' which {
- accepts nothing;
- returns Str;
- guarantees when { $self->has_nickname } returns { $self->nickname };
- guarantees when { !$self->has_nickname } returns { $self->name };
- } with {
- return ($self->has_nickname ? $self->nickname : $self->name);
- };
-
- };
-
-=for example My::Person tests
-
-=begin tests
-
-my $meta = My::Person->meta;
-
-isa_ok($meta, 'Reaction::Meta::Class');
-
-my $attr_map = $meta->get_attribute_map;
-
-foreach my $attr_name (qw/name nickname/) {
- isa_ok($attr_map->{$attr_name}, 'Reaction::Meta::Attribute');
-}
-
-ok($attr_map->{name}->is_required, 'name is required');
-ok(!$attr_map->{nickname}->is_required, 'nickname is optional');
-
-=end tests
-
-In your code -
-
-=for example My::Person usage
-
- my $jim = My::Person->new(name => 'Jim');
-
- print $jim->name."\n"; # prints "Jim\n"
-
- print $jim->preferred_name."\n"; # prints "Jim\n"
-
- $jim->name('James'); # returns 'James'
-
- $jim->nickname('Jim'); # returns 'Jim'
-
- print $jim->preferred_name."\n"; # prints "Jim\n"
-
- $jim->preferred_name('foo'); # throws Reaction::Exception::MethodArgumentException
-
-=for example My::Person end
-
-=head1 DESCRIPTION
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/Class.pm b/lib/Reaction/Class.pm
index 7813f48..64116ce 100644
--- a/lib/Reaction/Class.pm
+++ b/lib/Reaction/Class.pm
@@ -147,15 +147,20 @@ sub setup_and_cleanup {
shift if $_[0] eq 'as';
push(@methods, [ $name, shift ]);
};
+ my $s = $setup;
foreach my $meth ($self->delayed_methods) {
$save_delayed{$meth} = $package->can($meth);
- local *{"${package}::${meth}"} =
- Sub::Name::subname "${self}::${meth}" => sub {
- push(@apply_after, [ $meth => @_ ]);
- };
+ my $s_copy = $s;
+ $s = sub {
+ local *{"${package}::${meth}"} =
+ Sub::Name::subname "${self}::${meth}" => sub {
+ push(@apply_after, [ $meth => @_ ]);
+ };
+ $s_copy->(@_);
+ };
}
# XXX - need additional fuckery to handle multi-class-per-file
- $setup->(); # populate up the crap
+ $s->(); # populate up the crap
}
my %exports = $self->exports_for_package($package);
{
diff --git a/lib/Reaction/InterfaceModel/Action/DBIC/Result/Delete.pm b/lib/Reaction/InterfaceModel/Action/DBIC/Result/Delete.pm
index 14ae6dc..4d81486 100644
--- a/lib/Reaction/InterfaceModel/Action/DBIC/Result/Delete.pm
+++ b/lib/Reaction/InterfaceModel/Action/DBIC/Result/Delete.pm
@@ -4,7 +4,7 @@ use Reaction::Types::DBIC 'Row';
use Reaction::Class;
class Delete is 'Reaction::InterfaceModel::Action', which {
- has '+target_model' => (isa => 'Row');
+ has '+target_model' => (isa => Row);
sub can_apply { 1 }
diff --git a/lib/Reaction/InterfaceModel/Action/DBIC/Result/Update.pm b/lib/Reaction/InterfaceModel/Action/DBIC/Result/Update.pm
index 34ac7f2..2a822e6 100644
--- a/lib/Reaction/InterfaceModel/Action/DBIC/Result/Update.pm
+++ b/lib/Reaction/InterfaceModel/Action/DBIC/Result/Update.pm
@@ -8,7 +8,7 @@ class Update is 'Reaction::InterfaceModel::Action', which {
does 'Reaction::InterfaceModel::Action::DBIC::Role::CheckUniques';
- has '+target_model' => (isa => 'Row');
+ has '+target_model' => (isa => Row);
implements BUILD => as {
my ($self) = @_;
diff --git a/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/Create.pm b/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/Create.pm
index 07e949b..3494f9b 100644
--- a/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/Create.pm
+++ b/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/Create.pm
@@ -9,7 +9,7 @@ class Create is 'Reaction::InterfaceModel::Action', which {
does 'Reaction::InterfaceModel::Action::DBIC::Role::CheckUniques';
- has '+target_model' => (isa => 'ResultSet');
+ has '+target_model' => (isa => ResultSet);
implements do_apply => as {
my $self = shift;
diff --git a/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/DeleteAll.pm b/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/DeleteAll.pm
index e6dfe3a..c26e287 100644
--- a/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/DeleteAll.pm
+++ b/lib/Reaction/InterfaceModel/Action/DBIC/ResultSet/DeleteAll.pm
@@ -6,7 +6,7 @@ use Reaction::InterfaceModel::Action;
class DeleteAll is 'Reaction::InterfaceModel::Action', which {
- has '+target_model' => (isa => 'ResultSet');
+ has '+target_model' => (isa => ResultSet);
sub can_apply { 1 }
diff --git a/lib/Reaction/InterfaceModel/Action/User/ChangePassword.pm b/lib/Reaction/InterfaceModel/Action/User/ChangePassword.pm
index fc8ff88..6546502 100644
--- a/lib/Reaction/InterfaceModel/Action/User/ChangePassword.pm
+++ b/lib/Reaction/InterfaceModel/Action/User/ChangePassword.pm
@@ -2,8 +2,10 @@ package Reaction::InterfaceModel::Action::User::ChangePassword;
use Reaction::Class;
+use Reaction::Types::Core qw(Password);
+
class ChangePassword is 'Reaction::InterfaceModel::Action::User::SetPassword', which {
- has old_password => (isa => 'Password', is => 'rw', lazy_fail => 1);
+ has old_password => (isa => Password, is => 'rw', lazy_fail => 1);
around error_for_attribute => sub {
my $super = shift;
diff --git a/lib/Reaction/InterfaceModel/Action/User/Login.pm b/lib/Reaction/InterfaceModel/Action/User/Login.pm
index 781ec0f..0bc3d97 100644
--- a/lib/Reaction/InterfaceModel/Action/User/Login.pm
+++ b/lib/Reaction/InterfaceModel/Action/User/Login.pm
@@ -2,11 +2,12 @@ package Reaction::InterfaceModel::Action::User::Login;
use Reaction::Class;
use aliased 'Reaction::InterfaceModel::Action';
+use Reaction::Types::Core qw(SimpleStr Password);
class Login, is Action, which {
- has 'username' => (isa => 'SimpleStr', is => 'rw', lazy_fail => 1);
- has 'password' => (isa => 'Password', is => 'rw', lazy_fail => 1);
+ has 'username' => (isa => SimpleStr, is => 'rw', lazy_fail => 1);
+ has 'password' => (isa => Password, is => 'rw', lazy_fail => 1);
around error_for_attribute => sub {
my $super = shift;
diff --git a/lib/Reaction/InterfaceModel/Action/User/ResetPassword.pm b/lib/Reaction/InterfaceModel/Action/User/ResetPassword.pm
index 3ef645d..2637dc0 100644
--- a/lib/Reaction/InterfaceModel/Action/User/ResetPassword.pm
+++ b/lib/Reaction/InterfaceModel/Action/User/ResetPassword.pm
@@ -7,12 +7,14 @@ use aliased
'Reaction::InterfaceModel::Action::User::Role::ConfirmationCodeSupport';
use aliased 'Reaction::InterfaceModel::Action::User::SetPassword';
+use Reaction::Types::Core qw(NonEmptySimpleStr);
+
class ResetPassword is SetPassword, which {
does ConfirmationCodeSupport;
has confirmation_code =>
- (isa => 'NonEmptySimpleStr', is => 'rw', lazy_fail => 1);
+ (isa => NonEmptySimpleStr, is => 'rw', lazy_fail => 1);
around error_for_attribute => sub {
my $super = shift;
diff --git a/lib/Reaction/InterfaceModel/Action/User/SetPassword.pm b/lib/Reaction/InterfaceModel/Action/User/SetPassword.pm
index fcf922a..14a561a 100644
--- a/lib/Reaction/InterfaceModel/Action/User/SetPassword.pm
+++ b/lib/Reaction/InterfaceModel/Action/User/SetPassword.pm
@@ -2,12 +2,13 @@ package Reaction::InterfaceModel::Action::User::SetPassword;
use Reaction::Class;
use Reaction::InterfaceModel::Action;
+use Reaction::Types::Core qw(Password);
class SetPassword is 'Reaction::InterfaceModel::Action', which {
- has new_password => (isa => 'Password', is => 'rw', lazy_fail => 1);
+ has new_password => (isa => Password, is => 'rw', lazy_fail => 1);
has confirm_new_password =>
- (isa => 'Password', is => 'rw', lazy_fail => 1);
+ (isa => Password, is => 'rw', lazy_fail => 1);
around error_for_attribute => sub {
my $super = shift;
diff --git a/lib/Reaction/InterfaceModel/Reflector/DBIC.pm b/lib/Reaction/InterfaceModel/Reflector/DBIC.pm
index 2d4e1a3..f4d98b7 100644
--- a/lib/Reaction/InterfaceModel/Reflector/DBIC.pm
+++ b/lib/Reaction/InterfaceModel/Reflector/DBIC.pm
@@ -161,8 +161,7 @@ class DBIC, which {
unless($model && $schema);
Class::MOP::load_class( $base );
Class::MOP::load_class( $schema );
- my $meta = eval { Class::MOP::load_class($model); } ?
- $model->meta : $base->meta->create($model, superclasses => [ $base ]);
+ my $meta = $self->_load_or_create($model, $base);
# sources => undef, #default to qr/./
# sources => [], #default to nothing
@@ -333,7 +332,11 @@ class DBIC, which {
domain_model => $dm_name,
orig_attr_name => $source,
default => sub {
- $collection->new(_source_resultset => shift->$dm_name->resultset($source));
+ my $self = $_[0];
+ return $collection->new(
+ _source_resultset => $self->$dm_name->resultset($source),
+ _parent => $self,
+ );
},
);
@@ -380,8 +383,7 @@ class DBIC, which {
Class::MOP::load_class( $base );
Class::MOP::load_class( $object );
- my $meta = eval { Class::MOP::load_class($class) } ?
- $class->meta : $base->meta->create( $class, superclasses => [ $base ]);
+ my $meta = $self->_load_or_create($class, $base);
my $make_immutable = $meta->is_immutable || $self->make_classes_immutable;;
$meta->make_mutable if $meta->is_immutable;
@@ -468,8 +470,7 @@ class DBIC, which {
Class::MOP::load_class($schema) if $schema;
Class::MOP::load_class($source_class);
- my $meta = eval { Class::MOP::load_class($class) } ?
- $class->meta : $base->meta->create($class, superclasses => [ $base ]);
+ my $meta = $self->_load_or_create($class, $base);
#create the domain model
$dm_name ||= $self->dm_name_from_source_name($source_name);
@@ -761,8 +762,7 @@ class DBIC, which {
my $attributes = $self->parse_reflect_rules($attr_rules, $attr_haystack);
#create the class
- my $meta = eval { Class::MOP::load_class($class) } ?
- $class->meta : $base->meta->create($class, superclasses => [$base]);
+ my $meta = $self->_load_or_create($class, $base);
my $make_immutable = $meta->is_immutable || $self->make_classes_immutable;
$meta->make_mutable if $meta->is_immutable;
@@ -773,7 +773,8 @@ class DBIC, which {
my $s_attr = $s_meta->find_attribute_by_name($s_attr_name);
confess("Unable to find attribute for '${s_attr_name}' via '${source}'")
unless defined $s_attr;
- next unless $s_attr->get_write_method; #only rw attributes!
+ next unless $s_attr->get_write_method
+ && $s_attr->get_write_method !~ /^_/; #only rw attributes!
my $attr_params = $self->parameters_for_source_object_action_attribute
(
@@ -809,6 +810,8 @@ class DBIC, which {
is => 'rw',
isa => $from_attr->_isa_metadata,
required => $from_attr->is_required,
+ ($from_attr->is_required
+ ? () : (clearer => "clear_$attr_name}")),
predicate => "has_${attr_name}",
);
@@ -859,6 +862,24 @@ class DBIC, which {
return \%attr_opts;
};
+ implements _load_or_create => as {
+ my ($self, $class, $base) = @_;
+ my $meta = $self->_maybe_load_class($class) ?
+ $class->meta : $base->meta->create($class, superclasses => [ $base ]);
+ return $meta;
+ };
+
+ implements _maybe_load_class => as {
+ my ($self, $class) = @_;
+ my $file = $class . '.pm';
+ $file =~ s{::}{/}g;
+ my $ret = eval { Class::MOP::load_class($class) };
+ if ($INC{$file} && $@) {
+ confess "Error loading ${class}: $@";
+ }
+ return $ret;
+ };
+
};
1;
diff --git a/lib/Reaction/Meta/Class.pm b/lib/Reaction/Meta/Class.pm
index 27c084f..b2c3b9e 100644
--- a/lib/Reaction/Meta/Class.pm
+++ b/lib/Reaction/Meta/Class.pm
@@ -5,6 +5,8 @@ use Reaction::Meta::Attribute;
extends 'Moose::Meta::Class';
+sub new { shift->SUPER::new(@_); }
+
around initialize => sub {
my $super = shift;
my $class = shift;
diff --git a/lib/Reaction/Meta/InterfaceModel/Action/Class.pm b/lib/Reaction/Meta/InterfaceModel/Action/Class.pm
index 0c83353..c09bdb6 100644
--- a/lib/Reaction/Meta/InterfaceModel/Action/Class.pm
+++ b/lib/Reaction/Meta/InterfaceModel/Action/Class.pm
@@ -5,6 +5,8 @@ use aliased 'Reaction::Meta::InterfaceModel::Action::ParameterAttribute';
class Class is 'Reaction::Meta::Class', which {
+ implements new => as { shift->SUPER::new(@_) };
+
around initialize => sub {
my $super = shift;
my $class = shift;
diff --git a/lib/Reaction/Meta/InterfaceModel/Object/Class.pm b/lib/Reaction/Meta/InterfaceModel/Object/Class.pm
index da99ffe..8fad3dc 100644
--- a/lib/Reaction/Meta/InterfaceModel/Object/Class.pm
+++ b/lib/Reaction/Meta/InterfaceModel/Object/Class.pm
@@ -7,6 +7,8 @@ use Reaction::Class;
class Class is 'Reaction::Meta::Class', which {
+ implements new => as { shift->SUPER::new(@_) };
+
around initialize => sub {
my $super = shift;
my $class = shift;
diff --git a/lib/Reaction/Types/Core.pm b/lib/Reaction/Types/Core.pm
index 62d508c..afd7454 100644
--- a/lib/Reaction/Types/Core.pm
+++ b/lib/Reaction/Types/Core.pm
@@ -6,48 +6,48 @@ use MooseX::Types
use MooseX::Types::Moose qw/Str Num Int/;
-subtype SimpleStr
- => as Str
- => where { (length($_) <= 255) && ($_ !~ m/\n/) }
- => message { "Must be a single line of no more than 255 chars" };
+subtype SimpleStr,
+ as Str,
+ where { (length($_) <= 255) && ($_ !~ m/\n/) },
+ message { "Must be a single line of no more than 255 chars" };
-subtype NonEmptySimpleStr
- => as SimpleStr
- => where { length($_) > 0 }
- => message { "Must be a non-empty single line of no more than 255 chars" };
+subtype NonEmptySimpleStr,
+ as SimpleStr,
+ where { length($_) > 0 },
+ message { "Must be a non-empty single line of no more than 255 chars" };
# XXX duplicating constraint msges since moose only uses last message
-subtype Password
- => as NonEmptySimpleStr
- => where { length($_) > 3 }
- => message { "Must be between 4 and 255 chars" };
+subtype Password,
+ as NonEmptySimpleStr,
+ where { length($_) > 3 },
+ message { "Must be between 4 and 255 chars" };
-subtype StrongPassword
- => as Password
- => where { (length($_) > 7) && (m/[^a-zA-Z]/) }
- => message {
+subtype StrongPassword,
+ as Password,
+ where { (length($_) > 7) && (m/[^a-zA-Z]/) },
+ message {
"Must be between 8 and 255 chars, and contain a non-alpha char" };
-subtype NonEmptyStr
- => as Str
- => where { length($_) > 0 }
- => message { "Must not be empty" };
-
-subtype PositiveNum
- => as Num
- => where { $_ >= 0 }
- => message { "Must be a positive number" };
-
-subtype PositiveInt
- => as Int
- => where { $_ >= 0 }
- => message { "Must be a positive integer" };
-
-subtype SingleDigit
- => as PositiveInt
- => where { $_ <= 9 }
- => message { "Must be a single digit" };
+subtype NonEmptyStr,
+ as Str,
+ where { length($_) > 0 },
+ message { "Must not be empty" };
+
+subtype PositiveNum,
+ as Num,
+ where { $_ >= 0 },
+ message { "Must be a positive number" };
+
+subtype PositiveInt,
+ as Int,
+ where { $_ >= 0 },
+ message { "Must be a positive integer" };
+
+subtype SingleDigit,
+ as PositiveInt,
+ where { $_ <= 9 },
+ message { "Must be a single digit" };
1;
diff --git a/lib/Reaction/Types/CreditCard.pm b/lib/Reaction/Types/CreditCard.pm
deleted file mode 100644
index f733355..0000000
--- a/lib/Reaction/Types/CreditCard.pm
+++ /dev/null
@@ -1,49 +0,0 @@
-package Reaction::Types::CreditCard;
-
-use MooseX::Types
- -declare => [qw/CardNumber CheckNumber/];
-
-use Reaction::Types::Core qw/NonEmptySimpleStr PositiveInt/;
-use Business::CreditCard ();
-
-subtype CardNumber
- => as NonEmptySimpleStr
- => where { Business::CreditCard::validate($_) }
- => message {"Must be a valid card number"};
-
-subtype CheckNumber
- => as PositiveInt
- => where { $_ <= 999 }
- => message { "Must be a 3 digits number" };
-
-1;
-
-=head1 NAME
-
-Reaction::Types::CreditCard
-
-=head1 DESCRIPTION
-
-=over
-
-=item * CardNumber
-
-=back
-
-=head1 SEE ALSO
-
-=over
-
-=item * L<Reaction::Types::Core>
-
-=back
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/Types/DBIC.pm b/lib/Reaction/Types/DBIC.pm
index 0dd1ff8..8f9a37d 100644
--- a/lib/Reaction/Types/DBIC.pm
+++ b/lib/Reaction/Types/DBIC.pm
@@ -11,8 +11,8 @@ subtype 'DBIx::Class::ResultSet'
=> as 'Object'
=> where { $_->isa('DBIx::Class::ResultSet') };
-subtype ResultSet
- => as 'DBIx::Class::ResultSet';
+subtype ResultSet,
+ as 'DBIx::Class::ResultSet';
use DBIx::Class::Core;
use DBIx::Class::Row;
@@ -21,8 +21,8 @@ subtype 'DBIx::Class::Row'
=> as 'Object'
=> where { $_->isa('DBIx::Class::Row') };
-subtype Row
- => as 'DBIx::Class::Row';
+subtype Row,
+ as 'DBIx::Class::Row';
1;
diff --git a/lib/Reaction/Types/DateTime.pm b/lib/Reaction/Types/DateTime.pm
index 491ebe5..d6294d2 100644
--- a/lib/Reaction/Types/DateTime.pm
+++ b/lib/Reaction/Types/DateTime.pm
@@ -6,19 +6,19 @@ use MooseX::Types
use MooseX::Types::Moose qw/Object ArrayRef/;
use DateTime;
-subtype DateTime
- => as Object
- => where { $_->isa('DateTime') }
- => message { "Must be of the form YYYY-MM-DD HH:MM:SS" };
+subtype DateTime,
+ as Object,
+ where { $_->isa('DateTime') },
+ message { "Must be of the form YYYY-MM-DD HH:MM:SS" };
use DateTime::SpanSet;
-subtype SpanSet
- => as Object
- => where { $_->isa('DateTime::SpanSet') };
+subtype SpanSet,
+ as Object,
+ where { $_->isa('DateTime::SpanSet') };
-subtype TimeRangeCollection
- => as ArrayRef;
+subtype TimeRangeCollection,
+ as ArrayRef;
1;
diff --git a/lib/Reaction/Types/Email.pm b/lib/Reaction/Types/Email.pm
index a82d16f..01660a3 100644
--- a/lib/Reaction/Types/Email.pm
+++ b/lib/Reaction/Types/Email.pm
@@ -6,10 +6,10 @@ use MooseX::Types
use Reaction::Types::Core 'NonEmptySimpleStr';
use Email::Valid;
-subtype EmailAddress
- => as NonEmptySimpleStr
- => where { Email::Valid->address($_) }
- => message { "Must be a valid e-mail address" };
+subtype EmailAddress,
+ as NonEmptySimpleStr,
+ where { Email::Valid->address($_) },
+ message { "Must be a valid e-mail address" };
1;
diff --git a/lib/Reaction/UI/Controller/Collection.pm b/lib/Reaction/UI/Controller/Collection.pm
index 77a0005..7caf276 100644
--- a/lib/Reaction/UI/Controller/Collection.pm
+++ b/lib/Reaction/UI/Controller/Collection.pm
@@ -47,17 +47,17 @@ sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
sub list :Chained('base') :PathPart('') :Args(0) {
my ($self, $c) = @_;
- $c->forward(basic_page => [{ collection => $self->get_collection($c) }]);
+ $self->basic_page($c, { collection => $self->get_collection($c) });
}
sub view :Chained('object') :Args(0) {
my ($self, $c) = @_;
- $c->forward(basic_page => [{ model => $c->stash->{object} }]);
+ $self->basic_page($c, { model => $c->stash->{object} });
}
-sub basic_page : Private {
+sub basic_page {
my ($self, $c, $vp_args) = @_;
- my $action_name = $c->stack->[-2]->name;
+ my $action_name = $c->stack->[-1]->name;
return $self->push_viewport
(
$self->action_viewport_map->{$action_name},
@@ -73,7 +73,7 @@ __END__;
=head1 NAME
-Reaction::UI::Widget::Controller
+Reaction::UI::Controller
=head1 DESCRIPTION
@@ -84,12 +84,13 @@ Inherits from L<Reaction::UI::Controller>.
=head2 model_name
-The name of the model this controller will use as it's data source. Should be a name
-that can be passed to C<$C-E<gt>model>
+The name of the model this controller will use as it's data source. Should be a
+name that can be passed to C<$C-E<gt>model>
=head2 collection_name
-The name of the collection whithin the model that this Controller will be utilizing.
+The name of the collection whithin the model that this Controller will be
+utilizing.
=head2 action_viewport_map
@@ -103,14 +104,14 @@ The name of the collection whithin the model that this Controller will be utiliz
=back
-Read-write lazy building hashref. The keys should match action names in the Controller
-and the value should be the ViewPort class that this action should use.
- See method C<basic_page> for more info.
+Read-write lazy building hashref. The keys should match action names in the
+Controller and the value should be the ViewPort class that this action should
+use. See method C<basic_page> for more info.
=head action_viewport_args
-Read-write lazy building hashref. Additional ViewPort arguments for the action named
-as the key in the controller. See method C<basic_page> for more info.
+Read-write lazy building hashref. Additional ViewPort arguments for the action
+named as the key in the controller. See method C<basic_page> for more info.
=over 4
@@ -139,6 +140,14 @@ Provided builder for C<action_viewport_map>. Returns a hash with two items:
Returns an empty hashref.
+=head2 basic_page $c, \%vp_args
+
+Accepts two arguments, context, and a hashref of viewport arguments. It will
+automatically determine the action name using the catalyst stack and call
+C<push_viewport> with the ViewPort class name contained in the
+C<action_viewport_map> with a set of options determined by merging C<$vp_args>
+and the arguments contained in C<action_viewport_args>, if any.
+
=head1 ACTIONS
=head2 base
@@ -147,34 +156,31 @@ Chain link, no-op.
=head2 list
-Chain link, chained to C<base> forwards to basic page passing one custom argument,
-C<collection> which includes an instance of the current collection.
+Chain link, chained to C<base>. C<list> fetches the collection for the model
+and calls C<basic_page> with a single argument, C<collection>.
-The default ViewPort for this action is C<Reaction::UI::ViewPort::ListView> and can be
-changed by altering the C<action_viewport_map> attribute hash.
+The default ViewPort for this action is C<Reaction::UI::ViewPort::ListView> and
+can be changed by altering the C<action_viewport_map> attribute hash.
=head2 object
-Chain link, chained to C<base>, captures one argument, 'id'. Attempts to find a single
-object by searching for a member of the current collection which has a Primary Key or
-Unique constraint matching that argument. If the object is found it is stored in the
- stash under the C<object> key.
+Chain link, chained to C<base>, captures one argument, 'id'. Attempts to find
+a single object by searching for a member of the current collection which has a
+Primary Key or Unique constraint matching that argument. If the object is found
+it is stored in the stash under the C<object> key.
=head2 view
-Chain link, chained to C<object>. Forwards to C<basic page> with one custom vp argument
- of C<object>, which is the object located in the previous chain link of the same name.
+Chain link, chained to C<object>. Calls C<basic page> with one argument,
+C<model>, which contains an instance of the object fetched by the C<object>
+action link.
-The default ViewPort for this action is C<Reaction::UI::ViewPort::Object> and can be
-changed by altering the C<action_viewport_map> attribute hash.
+The default ViewPort for this action is C<Reaction::UI::ViewPort::Object> and
+can be changed by altering the C<action_viewport_map> attribute hash.
-=head2 basic_page
+=SEE ALSO
-Private action, accepts one argument, a hashref of viewport arguments (C<$vp_args>).
- It will automatically determine the action name using the catalyst stack and call
-C<push_viewport> with the ViewPort class name contained in the C<action_viewport_map>
-and arguments of C<$vp_args> and the arguments contained in C<action_viewport_args>,
-if any.
+L<Reaction::UI::Controller>
=head1 AUTHORS
diff --git a/lib/Reaction/UI/Controller/Collection/CRUD.pm b/lib/Reaction/UI/Controller/Collection/CRUD.pm
index 877f118..04d46e9 100644
--- a/lib/Reaction/UI/Controller/Collection/CRUD.pm
+++ b/lib/Reaction/UI/Controller/Collection/CRUD.pm
@@ -56,12 +56,12 @@ sub create :Chained('base') :PathPart('create') :Args(0) {
next_action => 'list',
on_apply_callback => sub { $self->after_create_callback($c => @_); },
};
- $c->forward( basic_model_action => [$vp_args]);
+ $self->basic_model_action( $c, $vp_args);
}
sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) {
my ($self, $c) = @_;
- $c->forward(basic_model_action => [{ next_action => 'list'}]);
+ $self->basic_model_action( $c, { next_action => 'list'});
}
sub after_create_callback {
@@ -76,33 +76,120 @@ sub update :Chained('object') :Args(0) {
my @cap = @{$c->req->captures};
pop(@cap); # object id
my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
- $c->forward(basic_model_action => [$vp_args]);
+ $self->basic_model_action( $c, $vp_args);
}
sub delete :Chained('object') :Args(0) {
my ($self, $c) = @_;
#this needs a better solution. currently thinking about it
- my @cap = @{$c->req->captures};
+ my @cap = @{$c->req->captures};
pop(@cap); # object id
my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
- $c->forward(basic_model_action => [$vp_args]);
+ $self->basic_model_action( $c, $vp_args);
}
-sub basic_model_action :Private {
+sub basic_model_action {
my ($self, $c, $vp_args) = @_;
my $target = exists $c->stash->{object} ?
$c->stash->{object} : $self->get_collection($c);
- my $cat_action_name = $c->stack->[-2]->name;
- my $im_action_name = join('', (map{ ucfirst } split('_', $cat_action_name)));
- return $self->push_viewport
- (
- $self->action_viewport_map->{$cat_action_name},
- model => $self->get_model_action($c, $im_action_name, $target),
- %{ $vp_args || {} },
- %{ $self->action_viewport_args->{$cat_action_name} || {} },
- );
+ my $action_name = join('', map{ ucfirst } split('_', $c->stack->[-1]->name));
+ my $model = $self->get_model_action($c, $action_name, $target);
+ return $self->basic_page($c, { model => $model, %{$vp_args||{}} });
}
1;
+
+__END__
+
+=head1 NAME
+
+Reaction::UI::Controller::CRUD - Basic CRUD functionality for Reaction::InterfaceModel data
+
+=head1 DESCRIPTION
+
+Controller class which extends L<Reaction::UI::Controller::Collection> to
+provide basic Create / Update / Delete / DeleteAll actions.
+
+Building on the base of the Collection controller this controller allows you to
+easily create complex and highly flexible CRUD functionality for your
+InterfaceModel models by providing a simple way to render and process your
+custom InterfaceModel Actions and customize built-ins.
+
+=head1 METHODS
+
+=head2 get_model_action $c, $action_name, $target_im
+
+Get an instance of the C<$action_name>
+L<InterfaceModel::Action|Reaction::InterfaceModel::Action> for model C<$target>
+This action is suitable for passing to an
+C<Action|Reaction::UI::ViewPort::Action> viewport
+
+=head2 after_create_callback $c, $vp, $result
+
+When a <create> action is applied, move the user to the new object's,
+C<update> page.
+
+=head2 basic_model_action $c, \%vp_args
+
+Extension to C<basic_page> which automatically instantiates an
+L<InterfaceModel::Action|Reaction::InterfaceModel::Action> with the right
+data target using C<get_model_action>
+
+=head2 _build_action_viewport_map
+
+Map C<create>, C<update>, C<delete> and C<delete_all> to use the
+C<Action|Reaction::UI::ViewPort::Action> viewport by default.
+
+=head2 _build_action_viewport_args
+
+Add action_prototypes to the C<list> action so that action links render correctly in L<ListView|Rection::UI::ViewPort::Listview>.
+
+=head1 ACTIONS
+
+=head2 create
+
+Chaned to C<base>. Create a new member of the collection represented by
+this controller. By default it attaches the C<after_create_callback> to
+DWIM after apply operations.
+
+See L<Create|Reaction::InterfaceModel::Action::DBIC::ResultSet::Create>
+ for more info.
+
+=head2 delete_all
+
+Chained to B<base>, delete all the members of the B<collection>. In most cases
+this is very much like a C<TRUNCATE> operation.
+
+See L<DeleteAll|Reaction::InterfaceModel::Action::DBIC::ResultSet::DeleteAll>
+ for more info.
+
+=head2 update
+
+Chained to C<object>, update a single object.
+
+See L<Update|Reaction::InterfaceModel::Action::DBIC::Result::Update>
+ for more info.
+
+=head2 delete
+
+Chained to C<object>, deletee a single object.
+
+
+See L<Delete|Reaction::InterfaceModel::Action::DBIC::Result::Delete>
+ for more info.
+
+=head1 SEE ALSO
+
+L<Reaction::UI::Controller::Collection>, L<Reaction::UI::Controller>
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Controller/Root.pm b/lib/Reaction/UI/Controller/Root.pm
index 51b0655..43c02da 100644
--- a/lib/Reaction/UI/Controller/Root.pm
+++ b/lib/Reaction/UI/Controller/Root.pm
@@ -9,9 +9,11 @@ __PACKAGE__->config(
content_type => 'text/html',
);
-has 'view_name' => (isa => 'Str', is => 'rw');
-has 'content_type' => (isa => 'Str', is => 'rw');
-has 'window_title' => (isa => 'Str', is => 'rw');
+has 'view_name' => (isa => 'Str', is => 'rw', required => 1);
+has 'content_type' => (isa => 'Str', is => 'rw', required => 1);
+has 'window_title' => (
+ isa => 'Str', is => 'rw', predicate => 'has_window_title'
+);
sub begin :Private {
my ($self, $ctx) = @_;
@@ -20,7 +22,9 @@ sub begin :Private {
ctx => $ctx,
view_name => $self->view_name,
content_type => $self->content_type,
- title => $self->window_title,
+ ($self->has_window_title
+ ? (title => $self->window_title)
+ : ()),
)
);
$ctx->stash(focus_stack => $ctx->stash->{window}->focus_stack);
diff --git a/lib/Reaction/UI/LayoutSet.pm b/lib/Reaction/UI/LayoutSet.pm
index 4185033..b424730 100644
--- a/lib/Reaction/UI/LayoutSet.pm
+++ b/lib/Reaction/UI/LayoutSet.pm
@@ -22,11 +22,11 @@ class LayoutSet which {
implements 'BUILD' => as {
my ($self, $args) = @_;
my @path = @{$args->{search_path}||[]};
- confess "No view object provided" unless $args->{view};
confess "No skin object provided" unless $args->{skin};
+ confess "No top skin object provided" unless $args->{top_skin};
$self->_load_file($self->source_file, $args);
unless ($self->has_widget_class) {
- $self->widget_class($args->{view}->widget_class_for($self));
+ $self->widget_class($args->{skin}->widget_class_for($self));
}
};
@@ -80,7 +80,7 @@ class LayoutSet which {
$skin = $build_args->{next_skin};
$super_name = $self->name;
} else {
- $skin = $build_args->{skin};
+ $skin = $build_args->{top_skin};
}
$self->super($skin->create_layout_set($super_name));
} elsif ($data =~ /^widget (\S+)/) {
diff --git a/lib/Reaction/UI/LayoutSet/TT.pm b/lib/Reaction/UI/LayoutSet/TT.pm
index 7f9e2df..68d6749 100644
--- a/lib/Reaction/UI/LayoutSet/TT.pm
+++ b/lib/Reaction/UI/LayoutSet/TT.pm
@@ -8,8 +8,6 @@ class TT is LayoutSet, which {
has 'tt_view' => (is => 'rw', isa => View, lazy_fail => 1);
- implements file_extension => as { 'tt' };
-
implements 'BUILD' => as {
my ($self, $args) = @_;
diff --git a/lib/Reaction/UI/Renderer/XHTML.pm b/lib/Reaction/UI/Renderer/XHTML.pm
deleted file mode 100644
index af98521..0000000
--- a/lib/Reaction/UI/Renderer/XHTML.pm
+++ /dev/null
@@ -1,89 +0,0 @@
-package Reaction::UI::Renderer::XHTML;
-
-use strict;
-use base qw/Catalyst::View::TT Reaction::Object/;
-use Reaction::Class;
-
-use HTML::Entities;
-
-__PACKAGE__->config({
- CATALYST_VAR => 'ctx',
- RECURSION => 1,
-});
-
-sub render_window {
- my ($self, $window) = @_;
- my $root_vp = $window->focus_stack->vp_head;
- confess "Can't flush view for window with empty focus stack"
- unless defined($root_vp);
- $self->render_viewport($window, $root_vp);
-}
-
-sub render_viewport {
- my ($self, $window, $vp) = @_;
- my $ctx = $window->ctx;
- my %args = (
- self => $vp,
- ctx => $ctx,
- window => $window,
- type => $vp->layout
- );
- unless (length $args{type}) {
- my $type = (split('::', ref($vp)))[-1];
- $args{type} = lc($type);
- }
- return $self->render($ctx, 'component', \%args);
-}
-
-around 'render' => sub {
- my $super = shift;
- my ($self,$args) = @_[0,3];
- local $self->template->{SERVICE}{CONTEXT}{BLKSTACK};
- local $self->template->{SERVICE}{CONTEXT}{BLOCKS};
- $args->{process_attrs} = \&process_attrs;
- return $super->(@_);
-};
-
-sub process_attrs{
- my $attrs = shift;
- return $attrs unless ref $attrs eq 'HASH';
-
- my @processed_attrs;
- while( my($k,$v) = each(%$attrs) ){
- my $enc_v = $v;
- next if ($enc_v eq "");
- if ($k eq 'class' && ref $v eq 'ARRAY'){
- $enc_v = join ' ', map { encode_entities($_) } @$v;
- } elsif ($k eq 'style' && ref $v eq 'HASH'){
- $enc_v = join '; ', map{ "${_}: ".encode_entities($v->{$_}) } keys %{$v};
- }
- push(@processed_attrs, "${k}=\"${enc_v}\"");
- }
-
- return ' '.join ' ', @processed_attrs if (scalar(@processed_attrs) > 0);
- return;
-}
-
-1;
-
-=head1 NAME
-
-Reaction::UI::Renderer::XHTML
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=head2 render
-
-=head2 process_attrs
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/UI/Skin.pm b/lib/Reaction/UI/Skin.pm
index 7f3f1ca..a875366 100644
--- a/lib/Reaction/UI/Skin.pm
+++ b/lib/Reaction/UI/Skin.pm
@@ -5,14 +5,21 @@ use Reaction::Class;
# declaring dependencies
use Reaction::UI::LayoutSet;
use Reaction::UI::RenderingContext;
+use File::ShareDir;
use aliased 'Path::Class::Dir';
class Skin which {
has '_layout_set_cache' => (is => 'ro', default => sub { {} });
+ has '_widget_class_cache' => (is => 'ro', default => sub { {} });
- has 'skin_base_path' => (is => 'ro', isa => Dir, required => 1);
+ has 'name' => (is => 'ro', isa => 'Str', required => 1);
+ has 'skin_dir' => (is => 'rw', isa => Dir, lazy_fail => 1);
+
+ has 'widget_search_path' => (
+ is => 'rw', isa => 'ArrayRef', requred => 1, default => sub { [] }
+ );
has 'view' => (
is => 'ro', required => 1, weak_ref => 1,
@@ -24,51 +31,83 @@ class Skin which {
);
sub BUILD {
- my ($self) = @_;
- $self->_load_skin_config;
+ my ($self, $args) = @_;
+ $self->_find_skin_dir($args);
+ $self->_load_skin_config($args);
}
- implements '_load_skin_config' => as {
- my ($self) = @_;
- my $base = $self->skin_base_path;
+ implements '_find_skin_dir' => as {
+ my ($self, $args) = @_;
+ my $skin_name = $self->name;
+ if ($skin_name =~ s!^/(.*?)/!!) {
+ my $dist = $1;
+ $args->{skin_base_dir} =
+ Dir->new(File::ShareDir::dist_dir($dist))
+ ->subdir('skin');
+ }
+ my $base = $args->{skin_base_dir}->subdir($skin_name);
confess "No such skin base directory ${base}"
unless -d $base;
- if (-e (my $conf_file = $base->file('skin.conf'))) {
- # we get [ { $file => $conf } ]
- my ($cfg) = values %{
- Config::Any->load_files({
- files => [ $conf_file ], use_ext => 1
- })->[0]
- };
- if (my $super_name = $cfg->{extends}) {
- my $super_dir = $base->parent->subdir($super_name);
- my $super = $self->new(
- view => $self->view, skin_base_path => $super_dir
- );
- $self->super($super);
- }
+ $self->skin_dir($base);
+ };
+
+ implements '_load_skin_config' => as {
+ my ($self, $args) = @_;
+ my $base = $self->skin_dir;
+ my $lst = sub { (ref $_[0] eq 'ARRAY') ? $_[0] : [$_[0]] };
+ my @files = (
+ $args->{skin_base_dir}->file('defaults.conf'), $base->file('skin.conf')
+ );
+ # we get [ { $file => $conf }, ... ]
+ my %cfg = (map { %{(values %{$_})[0]} }
+ @{Config::Any->load_files({
+ files => [ grep { -e $_ } @files ],
+ use_ext => 1,
+ })}
+ );
+ if (my $super_name = $cfg{extends}) {
+ my $super = $self->new(
+ name => $super_name,
+ view => $self->view,
+ skin_base_dir => $args->{skin_base_dir},
+ );
+ $self->super($super);
+ }
+ if (exists $cfg{widget_search_path}) {
+ $self->widget_search_path($lst->($cfg{widget_search_path}));
+ } else {
+ confess "No widget_search_path in defaults.conf or skin.conf"
+ ." and no search path provided from super skin"
+ unless $self->full_widget_search_path;
}
}
implements 'create_layout_set' => as {
my ($self, $name) = @_;
+ $self->_create_layout_set($name, [], $self);
+ };
+
+ implements '_create_layout_set' => as {
+ my ($self, $name, $tried, $top_skin) = @_;
if (my $path = $self->layout_path_for($name)) {
return $self->layout_set_class->new(
$self->layout_set_args_for($name),
source_file => $path,
+ top_skin => $top_skin,
);
}
+ $tried = [ @{$tried}, $self->our_path_for_type('layout') ];
if ($self->has_super) {
- return $self->super->create_layout_set($name);
+ return $self->super->_create_layout_set($name, $tried, $top_skin);
}
- confess "Couldn't find layout set file for ${name}";
+ confess "Couldn't find layout set file for ${name}, tried "
+ .join(', ', @$tried);
};
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),
@@ -78,7 +117,7 @@ class Skin which {
implements 'layout_path_for' => as {
my ($self, $layout) = @_;
my $file_name = join(
- '.', $layout, $self->layout_set_class->file_extension
+ '.', $layout, $self->view->layout_set_file_extension
);
my $path = $self->our_path_for_type('layout')
->file($file_name);
@@ -98,7 +137,38 @@ class Skin which {
implements 'our_path_for_type' => as {
my ($self, $type) = @_;
- return $self->skin_base_path->subdir($type)
+ return $self->skin_dir->subdir($type)
+ };
+
+ implements 'full_widget_search_path' => as {
+ my ($self) = @_;
+ return (
+ @{$self->widget_search_path},
+ ($self->has_super ? $self->super->full_widget_search_path : ())
+ );
+ };
+
+ implements 'widget_class_for' => as {
+ my ($self, $layout_set) = @_;
+ my $base = $self->blessed;
+ my $widget_type = $layout_set->widget_type;
+ return $self->_widget_class_cache->{$widget_type} ||= do {
+
+ my @search_path = $self->full_widget_search_path;
+ my @haystack = map {join('::', $_, $widget_type)} @search_path;
+
+ foreach my $class (@haystack) {
+ #if the class is already loaded skip the call to Installed etc.
+ return $class if Class::MOP::is_class_loaded($class);
+ next unless Class::Inspector->installed($class);
+
+ my $ok = eval { Class::MOP::load_class($class) };
+ confess("Failed to load widget '${class}': $@") if $@;
+ return $class;
+ }
+ confess "Couldn't locate widget '${widget_type}' for layout "
+ ."'${\$layout_set->name}': tried: ".join(", ", @haystack);
+ };
};
};
diff --git a/lib/Reaction/UI/View.pm b/lib/Reaction/UI/View.pm
index 7a1ebc5..358fcf1 100644
--- a/lib/Reaction/UI/View.pm
+++ b/lib/Reaction/UI/View.pm
@@ -10,7 +10,6 @@ use aliased 'Path::Class::Dir';
class View which {
- has '_widget_class_cache' => (is => 'ro', default => sub { {} });
has '_widget_cache' => (is => 'ro', default => sub { {} });
has '_layout_set_cache' => (is => 'ro', default => sub { {} });
@@ -42,8 +41,8 @@ class View which {
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)),
+ # path_to returns a File, not a Dir. Thanks, Catalyst.
+ skin_base_dir => Dir->new($self->app->path_to('share', 'skin')),
);
};
@@ -77,38 +76,6 @@ class View which {
);
};
- implements 'widget_class_for' => as {
- my ($self, $layout_set) = @_;
- my $base = $self->blessed;
- my $widget_type = $layout_set->widget_type;
- my $app_name = ref $self->app || $self->app;
- 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 {
my ($self, $vp) = @_;
#print STDERR "Getting layoutset for VP ".(ref($vp) || "SC:".$vp)."\n";
@@ -126,6 +93,10 @@ class View which {
return $cache->{$lset_name} ||= $self->create_layout_set($lset_name);
};
+ implements 'layout_set_file_extension' => as {
+ confess View." is abstract, you must subclass it";
+ };
+
implements 'find_related_class' => as {
my ($self, $rel) = @_;
my $own_class = ref($self) || $self;
diff --git a/lib/Reaction/UI/View/TT.pm b/lib/Reaction/UI/View/TT.pm
index 46d3135..af282e4 100644
--- a/lib/Reaction/UI/View/TT.pm
+++ b/lib/Reaction/UI/View/TT.pm
@@ -19,10 +19,7 @@ class TT is View, which {
return (super(), tt_object => $self->_tt);
};
- overrides 'rendering_context_args_for' => sub {
- my ($self, %args) = @_;
- return ();
- };
+ implements layout_set_file_extension => as { 'tt' };
implements 'serve_static_file' => as {
my ($self, $c, $args) = @_;
diff --git a/lib/Reaction/UI/ViewPort.pm b/lib/Reaction/UI/ViewPort.pm
index 7d24efc..4f12b55 100644
--- a/lib/Reaction/UI/ViewPort.pm
+++ b/lib/Reaction/UI/ViewPort.pm
@@ -3,6 +3,8 @@ package Reaction::UI::ViewPort;
use Reaction::Class;
use Scalar::Util qw/blessed/;
+sub DEBUG_EVENTS () { $ENV{REACTION_UI_VIEWPORT_DEBUG_EVENTS} }
+
class ViewPort which {
has location => (isa => 'Str', is => 'rw', required => 1);
@@ -89,8 +91,13 @@ class ViewPort which {
my ($self, $events) = @_;
foreach my $event ($self->accept_events) {
if (exists $events->{$event}) {
- #my $name = eval{$self->name};
- #$self->ctx->log->debug("Applying Event: $event on $name with value: ". $events->{$event});
+ if (DEBUG_EVENTS) {
+ my $name = join(' at ', ref($self), $self->location);
+ $self->ctx->log->debug(
+ "Applying Event: $event on $name with value: "
+ .$events->{$event}
+ );
+ }
$self->$event($events->{$event});
}
}
diff --git a/lib/Reaction/UI/ViewPort/Action.pm b/lib/Reaction/UI/ViewPort/Action.pm
index 20ae4ea..3468fab 100644
--- a/lib/Reaction/UI/ViewPort/Action.pm
+++ b/lib/Reaction/UI/ViewPort/Action.pm
@@ -2,6 +2,10 @@ package Reaction::UI::ViewPort::Action;
use Reaction::Class;
+use aliased 'Reaction::UI::ViewPort::Object';
+
+BEGIN { *DEBUG_EVENTS = \&Reaction::UI::ViewPort::DEBUG_EVENTS; }
+
use aliased 'Reaction::UI::ViewPort::Field::Mutable::Text';
use aliased 'Reaction::UI::ViewPort::Field::Mutable::Array';
use aliased 'Reaction::UI::ViewPort::Field::Mutable::String';
@@ -16,7 +20,7 @@ use aliased 'Reaction::UI::ViewPort::Field::Mutable::ChooseMany';
use aliased 'Reaction::UI::ViewPort::Field::Mutable::File';
#use aliased 'Reaction::UI::ViewPort::Field::Mutable::TimeRange';
-class Action is 'Reaction::UI::ViewPort::Object', which {
+class Action is Object, which {
has model => (is => 'ro', isa => 'Reaction::InterfaceModel::Action', required => 1);
#has '+model' => (isa => 'Reaction::InterfaceModel::Action');
@@ -44,11 +48,26 @@ class Action is 'Reaction::UI::ViewPort::Object', which {
implements can_apply => as {
my ($self) = @_;
foreach my $field ( @{ $self->fields } ) {
- return 0 if $field->needs_sync;
+ if ($field->needs_sync) {
+ if (DEBUG_EVENTS) {
+ $self->ctx->log->debug(
+ "Failing out of can_apply on ${\ref($self)} at ${\$self->location}"
+ ." because field for ${\$field->attribute->name} needs sync"
+ );
+ }
+ }
# if e.g. a datetime field has an invalid value that can't be re-assembled
# into a datetime object, the action may be in a consistent state but
# not synchronized from the fields; in this case, we must not apply
}
+ if (DEBUG_EVENTS) {
+ my $ret = $self->model->can_apply;
+ $self->ctx->log->debug(
+ "model can_apply returned ${ret}"
+ ." on ${\ref($self)} at ${\$self->location}"
+ );
+ return $ret;
+ }
return $self->model->can_apply;
};
@@ -121,7 +140,7 @@ class Action is 'Reaction::UI::ViewPort::Object', which {
$self->_build_simple_field(attribute => $attr, class => Boolean, %$args);
};
- implements _build_fields_for_type_SimpleStr => as {
+ implements _build_fields_for_type_Reaction_Types_Core_SimpleStr => as {
my ($self, $attr, $args) = @_;
$self->_build_simple_field(attribute => $attr, class => String, %$args);
};
@@ -140,7 +159,7 @@ class Action is 'Reaction::UI::ViewPort::Object', which {
}
};
- implements _build_fields_for_type_Password => as {
+ implements _build_fields_for_type_Reaction_Types_Core_Password => as {
my ($self, $attr, $args) = @_;
$self->_build_simple_field(attribute => $attr, class => Password, %$args);
};
@@ -171,7 +190,7 @@ class Action is 'Reaction::UI::ViewPort::Object', which {
(
attribute => $attr,
class => Array,
- layout => 'interface_model/field/mutable/array/hidden',
+ layout => 'field/mutable/hidden_array',
%$args);
}
};
diff --git a/lib/Reaction/UI/ViewPort/Collection/Grid/Member.pm b/lib/Reaction/UI/ViewPort/Collection/Grid/Member.pm
index bbe1c49..ccfc02a 100644
--- a/lib/Reaction/UI/ViewPort/Collection/Grid/Member.pm
+++ b/lib/Reaction/UI/ViewPort/Collection/Grid/Member.pm
@@ -24,7 +24,7 @@ class Member is 'Reaction::UI::ViewPort::Object', which {
$_[0]->(@_[1,2], { layout => 'value/string', %{ $_[3] || {} } })
};
- around _build_fields_for_type_SimpleStr => sub {
+ around _build_fields_for_type_Reaction_Types_Core_SimpleStr => sub {
$_[0]->(@_[1,2], { layout => 'value/string', %{ $_[3] || {} } })
};
@@ -36,7 +36,7 @@ class Member is 'Reaction::UI::ViewPort::Object', which {
$_[0]->(@_[1,2], { layout => 'value/date_time', %{ $_[3] || {} } })
};
- around _build_fields_for_type_Password => sub { return };
+ around _build_fields_for_type_Reaction_Types_Core_Password => sub { return };
around _build_fields_for_type_ArrayRef => sub { return };
around _build_fields_for_type_Reaction_InterfaceModel_Collection => sub { return };
diff --git a/lib/Reaction/UI/ViewPort/Field.pm b/lib/Reaction/UI/ViewPort/Field.pm
index 09857ff..f6da895 100644
--- a/lib/Reaction/UI/ViewPort/Field.pm
+++ b/lib/Reaction/UI/ViewPort/Field.pm
@@ -14,8 +14,6 @@ class Field is 'Reaction::UI::ViewPort', which {
has model => (is => 'ro', isa => Object, required => 1);
has attribute => (is => 'ro', isa => ParameterAttribute, required => 1);
- implements adopt_value => as {};
-
implements _build_name => as { shift->attribute->name };
implements _build_label => as {
@@ -59,6 +57,10 @@ class Field is 'Reaction::UI::ViewPort', which {
implements _empty_string_value => as { '' };
+ implements value_is_required => as {
+ shift->attribute->is_required;
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/ChooseMany.pm b/lib/Reaction/UI/ViewPort/Field/ChooseMany.pm
deleted file mode 100644
index 8074fd8..0000000
--- a/lib/Reaction/UI/ViewPort/Field/ChooseMany.pm
+++ /dev/null
@@ -1,110 +0,0 @@
-package Reaction::UI::ViewPort::Field::ChooseMany;
-
-use Reaction::Class;
-
-class ChooseMany is 'Reaction::UI::ViewPort::Field::ChooseOne', which {
-
- #has '+layout' => (default => 'dual_select_group');
- has '+value' => (isa => 'ArrayRef');
-
- my $listify = sub { # quick utility function, $listify->($arg)
- return (defined($_[0])
- ? (ref($_[0]) eq 'ARRAY'
- ? $_[0] # \@arr => \@arr
- : [$_[0]]) # $scalar => [$scalar]
- : []); # undef => []
- };
-
- around value => sub {
- my $orig = shift;
- my $self = shift;
- if (@_) {
- my $value = $listify->(shift);
- if (defined $value) {
- $_ = $self->str_to_ident($_) for @$value;
- my $checked = $self->attribute->check_valid_value($self->action, $value);
- # i.e. fail if any of the values fail
- confess "Not a valid set of values"
- if (@$checked < @$value || grep { !defined($_) } @$checked);
-
- $value = $checked;
- }
- $orig->($self, $value);
- } else {
- $orig->($self);
- }
- };
-
- implements _empty_value => as { [] };
-
- implements is_current_value => as {
- my ($self, $check_value) = @_;
- my @our_values = @{$self->value||[]};
- $check_value = $self->obj_to_str($check_value) if ref($check_value);
- return grep { $self->obj_to_str($_) eq $check_value } @our_values;
- };
-
- implements current_value_choices => as {
- my $self = shift;
- my @all = grep { $self->is_current_value($_->{value}) } @{$self->value_choices};
- return [ @all ];
- };
-
- implements available_value_choices => as {
- my $self = shift;
- my @all = grep { !$self->is_current_value($_->{value}) } @{$self->value_choices};
- return [ @all ];
- };
-
- around handle_events => sub {
- my $orig = shift;
- my ($self, $events) = @_;
- my $ev_value = $listify->($events->{value});
- if (delete $events->{add_all_values}) {
- $events->{value} = [map {$self->obj_to_str($_)} @{$self->valid_values}];
- } elsif (exists $events->{add_values} && delete $events->{do_add_values}) {
- my $add = $listify->(delete $events->{add_values});
- $events->{value} = [ @{$ev_value}, @$add ];
- } elsif (delete $events->{remove_all_values}) {
- $events->{value} = [];
- }elsif (exists $events->{remove_values} && delete $events->{do_remove_values}) {
- my $remove = $listify->(delete $events->{remove_values});
- my %r = map { ($_ => 1) } @$remove;
- $events->{value} = [ grep { !$r{$_} } @{$ev_value} ];
- }
- return $orig->(@_);
- };
-
-};
-
-1;
-
-=head1 NAME
-
-Reaction::UI::ViewPort::Field::ChooseMany
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=head2 is_current_value
-
-=head2 current_values
-
-=head2 available_values
-
-=head2 available_value_names
-
-=head1 SEE ALSO
-
-=head2 L<Reaction::UI::ViewPort::Field>
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/UI/ViewPort/Field/ChooseOne.pm b/lib/Reaction/UI/ViewPort/Field/ChooseOne.pm
deleted file mode 100644
index a44314e..0000000
--- a/lib/Reaction/UI/ViewPort/Field/ChooseOne.pm
+++ /dev/null
@@ -1,122 +0,0 @@
-package Reaction::UI::ViewPort::Field::ChooseOne;
-
-use Reaction::Class;
-use URI;
-use Scalar::Util 'blessed';
-
-class ChooseOne is 'Reaction::UI::ViewPort::Field', which {
-
- #has '+layout' => (default => 'select');
-
- has valid_values => (isa => 'ArrayRef', is => 'ro', lazy_build => 1);
- has value_choices => (isa => 'ArrayRef', is => 'ro', lazy_build => 1);
-
- has value_map_method => (
- isa => 'Str', is => 'ro', required => 1, default => sub { 'display_name' },
- );
-
- around value => sub {
- my $orig = shift;
- my $self = shift;
- if (@_) {
- my $value = shift;
- if (defined $value) {
- if (!ref $value) {
- $value = $self->str_to_ident($value);
- }
- my $checked = $self->attribute->check_valid_value($self->action, $value);
- confess "${value} is not a valid value" unless defined($checked);
- $value = $checked;
- }
- $orig->($self, $value);
- } else {
- $orig->($self);
- }
- };
-
- implements _build_valid_values => as {
- my $self = shift;
- return [ $self->attribute->all_valid_values($self->action) ];
- };
-
- implements _build_value_choices => sub{
- my $self = shift;
- my @pairs = map{{value => $self->obj_to_str($_), name => $self->obj_to_name($_)}}
- @{ $self->valid_values };
- return [ sort { $a->{name} cmp $b->{name} } @pairs ];
- };
-
- implements is_current_value => as {
- my ($self, $check_value) = @_;
- my $our_value = $self->value;
- return unless ref($our_value);
- $check_value = $self->obj_to_str($check_value) if ref($check_value);
- return $self->obj_to_str($our_value) eq $check_value;
- };
-
- implements str_to_ident => as {
- my ($self, $str) = @_;
- my $u = URI->new('','http');
- $u->query($str);
- return { $u->query_form };
- };
-
- implements obj_to_str => as {
- my ($self, $obj) = @_;
- return $obj unless ref($obj);
- confess "${obj} not an object" unless blessed($obj);
- my $ident = $obj->ident_condition;
- my $u = URI->new('', 'http');
- $u->query_form(%$ident);
- return $u->query;
- };
-
- implements obj_to_name => as {
- my ($self, $obj) = @_;
- return $obj unless ref($obj);
- confess "${obj} not an object" unless blessed($obj);
- my $meth = $self->value_map_method;
- return $obj->$meth;
- };
-
-};
-
-1;
-
-=head1 NAME
-
-Reaction::UI::ViewPort::Field::ChooseOne
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=head2 is_current_value
-
-=head2 value
-
-=head2 valid_values
-
-=head2 valid_value_names
-
-=head2 value_to_name_map
-
-=head2 name_to_value_map
-
-=head2 str_to_ident
-
-=head2 obj_to_str
-
-=head1 SEE ALSO
-
-=head2 L<Reaction::UI::ViewPort::Field>
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/UI/ViewPort/Field/Collection.pm b/lib/Reaction/UI/ViewPort/Field/Collection.pm
index 8c46952..b772e02 100644
--- a/lib/Reaction/UI/ViewPort/Field/Collection.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Collection.pm
@@ -6,6 +6,18 @@ use aliased 'Reaction::UI::ViewPort::Field::Array';
class Collection is Array, which {
+ has value => (
+ is => 'rw', lazy_build => 1,
+ isa => 'Reaction::InterfaceModel::Collection'
+ );
+
+ implements _build_value_names => as {
+ my $self = shift;
+ my $meth = $self->value_map_method;
+ my @names = map { blessed($_) ? $_->$meth : $_ } $self->value->members;
+ return [ sort @names ];
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/DateTime.pm b/lib/Reaction/UI/ViewPort/Field/DateTime.pm
index e89fc47..50d26cf 100644
--- a/lib/Reaction/UI/ViewPort/Field/DateTime.pm
+++ b/lib/Reaction/UI/ViewPort/Field/DateTime.pm
@@ -6,7 +6,7 @@ use Reaction::Types::DateTime;
use aliased 'Reaction::UI::ViewPort::Field';
class DateTime is Field, which {
- has '+value' => (isa => 'DateTime');
+ has '+value' => (isa => DateTime);
has value_string_default_format => (
isa => 'Str', is => 'rw', required => 1, default => sub { "%F %H:%M:%S" }
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Array.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Array.pm
index 49e629b..0bf0104 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Array.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Array.pm
@@ -11,7 +11,6 @@ class Array is 'Reaction::UI::ViewPort::Field::Array', which {
return $orig->($self) unless @_;
my $value = defined $_[0] ? $_[0] : [];
$orig->($self, (ref $value eq 'ARRAY' ? $value : [ $value ]));
- $self->sync_to_action;
};
};
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Boolean.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Boolean.pm
index 5293b11..633f910 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Boolean.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Boolean.pm
@@ -3,7 +3,12 @@ package Reaction::UI::ViewPort::Field::Mutable::Boolean;
use Reaction::Class;
class Boolean is 'Reaction::UI::ViewPort::Field::Boolean', which{
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
implements BUILD => as {
my($self) = @_;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseMany.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseMany.pm
index f799160..b3dca44 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseMany.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseMany.pm
@@ -28,14 +28,16 @@ class ChooseMany is 'Reaction::UI::ViewPort::Field', which {
$orig->($self, $checked);
};
+
around _value_string_from_value => sub {
+ my $orig = shift;
my $self = shift;
- join ", ", (map {$self->obj_to_name($_->{value}) } @{ $self->current_value_choices })
+ join(", ", (map {$self->obj_to_name($_->{value}) } @{ $self->current_value_choices }));
};
implements is_current_value => as {
my ($self, $check_value) = @_;
- return $self->_model_has_value;
+ return unless $self->_model_has_value;
my @our_values = @{$self->value || []};
$check_value = $self->obj_to_str($check_value) if ref($check_value);
return grep { $self->obj_to_str($_) eq $check_value } @our_values;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseOne.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseOne.pm
index f16ea66..9033528 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseOne.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/ChooseOne.pm
@@ -5,28 +5,23 @@ use Scalar::Util ();
class ChooseOne is 'Reaction::UI::ViewPort::Field', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
does 'Reaction::UI::ViewPort::Field::Role::Choices';
- around value => sub {
- my $orig = shift;
- my $self = shift;
- return $orig->($self) unless @_;
- my $value = shift;
- if (defined $value) {
- $value = $self->str_to_ident($value) if (!ref $value);
- my $attribute = $self->attribute;
- my $checked = $attribute->check_valid_value($self->model, $value);
- unless (defined $checked) {
- require Data::Dumper;
- my $serialised = Data::Dumper->new([ $value ])->Indent(0)->Dump;
- $serialised =~ s/^\$VAR1 = //; $serialised =~ s/;$//;
- confess "${serialised} is not a valid value for ${\$attribute->name} on "
- ."${\$attribute->associated_class->name}";
- }
- $value = $checked;
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ my $value = $self->value_string;
+ $value = $self->str_to_ident($value) if (!ref $value);
+ my $attribute = $self->attribute;
+ my $checked = $attribute->check_valid_value($self->model, $value);
+ unless (defined $checked) {
+ require Data::Dumper;
+ my $serialised = Data::Dumper->new([ $value ])->Indent(0)->Dump;
+ $serialised =~ s/^\$VAR1 = //; $serialised =~ s/;$//;
+ confess "${serialised} is not a valid value for ${\$attribute->name} on "
+ ."${\$attribute->associated_class->name}";
}
- $orig->($self, $value);
+ $self->value($checked);
};
around _value_string_from_value => sub {
@@ -47,7 +42,6 @@ class ChooseOne is 'Reaction::UI::ViewPort::Field', which {
return $self->obj_to_str($our_value) eq $check_value;
};
-
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/DateTime.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/DateTime.pm
index 71428e5..3b38d26 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/DateTime.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/DateTime.pm
@@ -7,10 +7,7 @@ use DateTime;
class 'Reaction::UI::ViewPort::Field::Mutable::DateTime',
is 'Reaction::UI::ViewPort::Field::DateTime', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
-
- has value_string =>
- ( is => 'rw', isa => 'Str', lazy_build => 1, trigger_adopt('value_string') );
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
implements adopt_value_string => as {
my ($self) = @_;
@@ -21,13 +18,9 @@ class 'Reaction::UI::ViewPort::Field::Mutable::DateTime',
$self->value($dt);
} else {
$self->message("Could not parse date or time");
- $self->clear_value;
- $self->needs_sync(1);
}
};
- around accept_events => sub { ('value_string', shift->(@_)) };
-
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Integer.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Integer.pm
index 4882f1e..958150a 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Integer.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Integer.pm
@@ -3,7 +3,13 @@ package Reaction::UI::ViewPort::Field::Mutable::Integer;
use Reaction::Class;
class Integer is 'Reaction::UI::ViewPort::Field::Integer', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Number.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Number.pm
index 41308f3..d2be595 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Number.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Number.pm
@@ -3,7 +3,12 @@ package Reaction::UI::ViewPort::Field::Mutable::Number;
use Reaction::Class;
class Number is 'Reaction::UI::ViewPort::Field::Number', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Password.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Password.pm
index 79319f2..d009698 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Password.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Password.pm
@@ -3,7 +3,13 @@ package Reaction::UI::ViewPort::Field::Mutable::Password;
use Reaction::Class;
class Password is 'Reaction::UI::ViewPort::Field::String', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/String.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/String.pm
index 758673c..11d5d14 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/String.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/String.pm
@@ -3,7 +3,13 @@ package Reaction::UI::ViewPort::Field::Mutable::String;
use Reaction::Class;
class String is 'Reaction::UI::ViewPort::Field::String', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Mutable/Text.pm b/lib/Reaction/UI/ViewPort/Field/Mutable/Text.pm
index 31d3b04..09d2127 100644
--- a/lib/Reaction/UI/ViewPort/Field/Mutable/Text.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Mutable/Text.pm
@@ -3,7 +3,13 @@ package Reaction::UI::ViewPort::Field::Mutable::Text;
use Reaction::Class;
class Text is 'Reaction::UI::ViewPort::Field::Text', which {
- does 'Reaction::UI::ViewPort::Field::Role::Mutable';
+ does 'Reaction::UI::ViewPort::Field::Role::Mutable::Simple';
+
+ implements adopt_value_string => as {
+ my ($self) = @_;
+ $self->value($self->value_string);
+ };
+
};
1;
diff --git a/lib/Reaction/UI/ViewPort/Field/Password.pm b/lib/Reaction/UI/ViewPort/Field/Password.pm
index bc86341..a80e71a 100644
--- a/lib/Reaction/UI/ViewPort/Field/Password.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Password.pm
@@ -2,9 +2,11 @@ package Reaction::UI::ViewPort::Field::Password;
use Reaction::Class;
+use Reaction::Types::Core qw(SimpleStr);
+
class Password is 'Reaction::UI::ViewPort::Field::String', which {
- has '+value' => (isa => 'SimpleStr');
+ has '+value' => (isa => SimpleStr);
#has '+layout' => (default => 'password');
};
diff --git a/lib/Reaction/UI/ViewPort/Field/Role/Mutable.pm b/lib/Reaction/UI/ViewPort/Field/Role/Mutable.pm
index 8690603..42899c0 100644
--- a/lib/Reaction/UI/ViewPort/Field/Role/Mutable.pm
+++ b/lib/Reaction/UI/ViewPort/Field/Role/Mutable.pm
@@ -9,9 +9,16 @@ role Mutable, which {
has model => (is => 'ro', isa => Action, required => 1);
has attribute => (is => 'ro', isa => ParameterAttribute, required => 1);
- has value => (is => 'rw', lazy_build => 1, trigger_adopt('value'));
+ has value => (
+ is => 'rw', lazy_build => 1, trigger_adopt('value'),
+ clearer => 'clear_value',
+ );
has needs_sync => (is => 'rw', isa => 'Int', default => 0);
- has message => (is => 'rw', isa => 'Str');
+ has message => (is => 'rw', isa => 'Str');
+
+ after clear_value => sub {
+ shift->needs_sync(1);
+ };
implements adopt_value => as {
my ($self) = @_;
@@ -20,22 +27,33 @@ role Mutable, which {
implements sync_to_action => as {
my ($self) = @_;
- return unless $self->needs_sync && $self->has_value;
+ return unless $self->needs_sync;
my $attr = $self->attribute;
- my $writer = $attr->get_write_method;
- confess "No writer for attribute" unless defined($writer);
-
- my $value = $self->value;
- if (my $tc = $attr->type_constraint) {
- $value = $tc->coercion->coerce($value) if ($tc->has_coercion);
- #my $error = $tc->validate($self->value); # should we be checking against $value?
- my $error = $tc->validate($value);
- if (defined $error) {
- $self->message($error);
- return;
+
+ if ($self->has_value) {
+ my $value = $self->value;
+ if (my $tc = $attr->type_constraint) {
+ $value = $tc->coercion->coerce($value) if ($tc->has_coercion);
+ #my $error = $tc->validate($self->value); # should we be checking against $value?
+ my $error = $tc->validate($value);
+ if (defined $error) {
+ $self->message($error);
+ return;
+ }
+ }
+ my $writer = $attr->get_write_method;
+ confess "No writer for attribute" unless defined($writer);
+ $self->model->$writer($value);
+ } else {
+ my $predicate = $attr->predicate;
+ confess "No predicate for attribute" unless defined($predicate);
+ if ($self->model->$predicate) {
+ my $clearer = $attr->clearer;
+ confess "${predicate} returned true but no clearer for attribute"
+ unless defined($clearer);
+ $self->model->$clearer;
}
}
- $self->model->$writer($value);
$self->needs_sync(0);
};
diff --git a/lib/Reaction/UI/ViewPort/Field/Role/Mutable/Simple.pm b/lib/Reaction/UI/ViewPort/Field/Role/Mutable/Simple.pm
new file mode 100644
index 0000000..66fa464
--- /dev/null
+++ b/lib/Reaction/UI/ViewPort/Field/Role/Mutable/Simple.pm
@@ -0,0 +1,36 @@
+package Reaction::UI::ViewPort::Field::Role::Mutable::Simple;
+
+use Reaction::Role;
+
+use aliased 'Reaction::UI::ViewPort::Field::Role::Mutable';
+
+role Simple which {
+
+ does Mutable;
+
+ has value_string => (
+ is => 'rw', lazy_build => 1, trigger_adopt('value_string'),
+ clearer => 'clear_value',
+ );
+
+ around value_string => sub {
+ my $orig = shift;
+ my $self = shift;
+ if (@_ && defined($_[0]) && !ref($_[0]) && $_[0] eq ''
+ && !$self->value_is_required) {
+ $self->clear_value;
+ return undef;
+ }
+ return $self->$orig(@_);
+ };
+
+ # the user needs to implement this because, honestly, you're always going
+ # to need to do something custom and the only common thing really is
+ # "you probably set $self->value at the end"
+ requires 'adopt_value_string';
+
+ around accept_events => sub { ('value_string', shift->(@_)) };
+
+};
+
+1;
diff --git a/lib/Reaction/UI/ViewPort/Field/TimeRange.pm b/lib/Reaction/UI/ViewPort/Field/TimeRange.pm
index a63fdf4..ccf6e65 100644
--- a/lib/Reaction/UI/ViewPort/Field/TimeRange.pm
+++ b/lib/Reaction/UI/ViewPort/Field/TimeRange.pm
@@ -1,14 +1,14 @@
package Reaction::UI::ViewPort::Field::TimeRange;
use Reaction::Class;
-use Reaction::Types::DateTime;
+use Reaction::Types::DateTime qw(SpanSet);
use DateTime;
use DateTime::SpanSet;
use Time::ParseDate ();
class TimeRange is 'Reaction::UI::ViewPort::Field', which {
- has '+value' => (isa => 'SpanSet');
+ has '+value' => (isa => SpanSet);
#has '+layout' => (default => 'timerange');
diff --git a/lib/Reaction/UI/ViewPort/Object.pm b/lib/Reaction/UI/ViewPort/Object.pm
index 765b601..5f71884 100644
--- a/lib/Reaction/UI/ViewPort/Object.pm
+++ b/lib/Reaction/UI/ViewPort/Object.pm
@@ -144,7 +144,7 @@ class Object is 'Reaction::UI::ViewPort', which {
};
#XXX
- implements _build_fields_for_type_Password => as { return };
+ implements _build_fields_for_type_Reaction_Types_Core_Password => as { return };
implements _build_fields_for_type_Str => as {
my ($self, $attr, $args) = @_;
@@ -152,7 +152,7 @@ class Object is 'Reaction::UI::ViewPort', which {
$self->_build_simple_field(attribute => $attr, class => String, %$args);
};
- implements _build_fields_for_type_SimpleStr => as {
+ implements _build_fields_for_type_Reaction_Types_Core_SimpleStr => as {
my ($self, $attr, $args) = @_;
$self->_build_simple_field(attribute => $attr, class => String, %$args);
};
diff --git a/lib/Reaction/UI/ViewPort/SiteLayout.pm b/lib/Reaction/UI/ViewPort/SiteLayout.pm
index 4560dc6..a8ee133 100644
--- a/lib/Reaction/UI/ViewPort/SiteLayout.pm
+++ b/lib/Reaction/UI/ViewPort/SiteLayout.pm
@@ -9,6 +9,11 @@ class SiteLayout is ViewPort, which {
has 'static_base_uri' => (isa => 'Str', is => 'rw', lazy_fail => 1);
+ has 'meta_info' => (
+ is => 'rw', isa => 'HashRef',
+ required => '1', default => sub { {} }
+ );
+
};
1;
diff --git a/lib/Reaction/UI/Widget/Field/Mutable.pm b/lib/Reaction/UI/Widget/Field/Mutable.pm
index 5c245d8..7547799 100644
--- a/lib/Reaction/UI/Widget/Field/Mutable.pm
+++ b/lib/Reaction/UI/Widget/Field/Mutable.pm
@@ -5,8 +5,8 @@ use Reaction::UI::WidgetClass;
class Mutable is 'Reaction::UI::Widget::Field', which {
before fragment widget {
- arg 'field_id' => event_id 'value';
- arg 'field_name' => event_id 'value' unless defined $_{field_name};
+ arg 'field_id' => event_id 'value_string';
+ arg 'field_name' => event_id 'value_string' unless defined $_{field_name};
arg 'field_type' => 'text';
};
diff --git a/lib/Reaction/UI/Widget/Field/Mutable/ChooseMany.pm b/lib/Reaction/UI/Widget/Field/Mutable/ChooseMany.pm
index 2f938d6..4078bdc 100644
--- a/lib/Reaction/UI/Widget/Field/Mutable/ChooseMany.pm
+++ b/lib/Reaction/UI/Widget/Field/Mutable/ChooseMany.pm
@@ -15,6 +15,7 @@ class ChooseMany is 'Reaction::UI::Widget::Field::Mutable', which {
implements fragment current_values {
my $current_choices = $_{viewport}->current_value_choices;
if( @$current_choices ){
+ arg field_name => event_id 'value';
render hidden_value => over $current_choices;
} else {
arg field_name => event_id 'no_current_value';
diff --git a/lib/Reaction/UI/Widget/Field/Mutable/ChooseOne.pm b/lib/Reaction/UI/Widget/Field/Mutable/ChooseOne.pm
index 68255bf..b0ab7b3 100644
--- a/lib/Reaction/UI/Widget/Field/Mutable/ChooseOne.pm
+++ b/lib/Reaction/UI/Widget/Field/Mutable/ChooseOne.pm
@@ -5,7 +5,7 @@ use Reaction::UI::WidgetClass;
class ChooseOne is 'Reaction::UI::Widget::Field::Mutable', which {
implements fragment option_is_required {
- if ($_{viewport}->attribute->is_required) {
+ if ($_{viewport}->value_is_required) {
render 'option_is_required_yes';
} else {
render 'option_is_required_no';
diff --git a/lib/Reaction/UI/Widget/Field/Mutable/Password.pm b/lib/Reaction/UI/Widget/Field/Mutable/Password.pm
index c11a909..890770e 100644
--- a/lib/Reaction/UI/Widget/Field/Mutable/Password.pm
+++ b/lib/Reaction/UI/Widget/Field/Mutable/Password.pm
@@ -7,6 +7,7 @@ class Password is 'Reaction::UI::Widget::Field::Mutable', which {
around fragment widget {
call_next;
arg field_type => 'password';
+ arg field_value => ''; # no sending password to user. really.
};
};
diff --git a/lib/Reaction/UI/Widget/SiteLayout.pm b/lib/Reaction/UI/Widget/SiteLayout.pm
index 73406a9..dc9574a 100644
--- a/lib/Reaction/UI/Widget/SiteLayout.pm
+++ b/lib/Reaction/UI/Widget/SiteLayout.pm
@@ -10,6 +10,15 @@ class SiteLayout is Container, which {
arg title => $_{viewport}->title;
};
+ implements fragment meta_info {
+ render meta_member => over [keys %{$_{viewport}->meta_info}];
+ };
+
+ implements fragment meta_member {
+ arg 'meta_name' => $_;
+ arg 'meta_value' => $_{viewport}->meta_info->{$_};
+ };
+
};
1;
diff --git a/lib/Reaction/UI/Widget/Value/List.pm b/lib/Reaction/UI/Widget/Value/List.pm
deleted file mode 100644
index 89598d8..0000000
--- a/lib/Reaction/UI/Widget/Value/List.pm
+++ /dev/null
@@ -1,43 +0,0 @@
-package Reaction::UI::Widget::Value::List;
-
-use Reaction::UI::WidgetClass;
-
-class List, which {
- fragment widget [ qw/list/ ];
- fragment list [ item => over func('viewport', 'value_names') ];
- fragment item [ string {""} ], { value => $_ };
-};
-
-1;
-
-__END__;
-
-=head1 NAME
-
-Reaction::UI::Widget::Value::List
-
-=head1 DESCRIPTION
-
-=head1 FRAGMENTS
-
-=head2 widget
-
-renders C<label> passing additional variable "viewport"
-
-=head2 list
-
-renders fragment item over the viewport's C<value_names>
-
-=head2 item
-
-C<content> contains the value of the current item ($_ / $_{_})
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/root/bar_form b/root/bar_form
deleted file mode 100644
index 1eece03..0000000
--- a/root/bar_form
+++ /dev/null
@@ -1,6 +0,0 @@
-[%
-
-attrs.enctype = 'multipart/form-data';
-PROCESS form_base;
-
-%]
diff --git a/root/bar_list b/root/bar_list
deleted file mode 100644
index ce57b74..0000000
--- a/root/bar_list
+++ /dev/null
@@ -1,21 +0,0 @@
-[%
-
-PROCESS listview;
-
-table_end_block = 'bar_list_table_end';
-
-BLOCK bar_list_table_end;
-
- "\n</table>\n";
- include( 'create_link_block' );
- "\n<br />\n";
-
- enctype = attrs.enctype || 'application/x-www-form-urlencoded';
- %]<form action="[% connect_form %]" method="post" enctype="[% enctype %]"[%
- attrs.enctype = ''; process_attrs(self.attrs); '>';
- INCLUDE component type = 'search_base' attrs.value = 'xxx';
- "\n</form>";
-
-END;
-
-%]
diff --git a/root/base/actionform b/root/base/actionform
deleted file mode 100644
index ab5755b..0000000
--- a/root/base/actionform
+++ /dev/null
@@ -1 +0,0 @@
-[% PROCESS form_base %]
diff --git a/root/base/button b/root/base/button
deleted file mode 100644
index 5e09c4d..0000000
--- a/root/base/button
+++ /dev/null
@@ -1,21 +0,0 @@
-[%
-
-PROCESS field_base;
-
-main_block = 'button_control';
-
-BLOCK button_control;
-
- %]<input type="[% button_type || 'submit' %]" [%
- IF attrs.value == '';
- 'value="'; loc(self.value) | html; '" ';
- END;
- connect_control(self, self.event);
- process_attrs(attrs) %] />[%
-# IF self.img_src;
-# INCLUDE component type = 'image';
-# ELSE;
-
-END;
-
-%]
diff --git a/root/base/cancelbtn b/root/base/cancelbtn
deleted file mode 100644
index a9d8af0..0000000
--- a/root/base/cancelbtn
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS button;
-
-control_block = 'cancelbtn_control';
-
-BLOCK cancelbtn_control;
-
- INCLUDE button_control attrs.value = 'Cancel' self.event = 'close';
-
-END;
-
-%]
diff --git a/root/base/checkbox b/root/base/checkbox
deleted file mode 100644
index dd80d86..0000000
--- a/root/base/checkbox
+++ /dev/null
@@ -1,22 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'checkbox_control';
-
-BLOCK checkbox_control;
-
- %]<input type="checkbox" id="[% id_attr %]" [%
- connect_control(self, 'value');
- process_attrs(attrs);
- IF self.value;
- ' checked="checked"';
- END;
- UNLESS attrs.value;
- ' value="1"';
- END;
- %] />[%
-
-END;
-
-%]
diff --git a/root/base/checkbox_group b/root/base/checkbox_group
deleted file mode 100644
index 52660b0..0000000
--- a/root/base/checkbox_group
+++ /dev/null
@@ -1,19 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'checkbox_group_control';
-
-BLOCK checkbox_group_control;
-
- FOREACH v_name IN self.valid_value_names;
- v_val = self.name_to_value_map.$v_name;
- %]<input type="checkbox" id="[% id_attr %]" [% connect_control(self, 'value');
- ' value="'; v_val; '"';
- IF self.is_current_value(v_val); ' checked="checked"'; END;
- process_attrs(attrs); ' />'; v_name; "\n";
- END;
-
-END;
-
-%]
diff --git a/root/base/component b/root/base/component
deleted file mode 100644
index 4f455ce..0000000
--- a/root/base/component
+++ /dev/null
@@ -1,64 +0,0 @@
-[%-
-
-GLOBAL_DEBUG = ctx.debug;
-
-MACRO loc(text, args) BLOCK;
-
- ctx.localize(text, args);
-
-END;
-
-MACRO include(name, args) BLOCK;
-
- filename = ${name};
-
- IF filename;
- IF GLOBAL_DEBUG;
- '<!-- Start block '; name | html; ' calling '; filename | html; " -->\n";
- END;
- INCLUDE $filename args;
- IF GLOBAL_DEBUG;
- '<!-- End block '; name | html; " -->\n";
- END;
- ELSE;
- error = 'Chosen INCLUDE ' _ name _ ' is empty';
- THROW file error;
- END;
-
-END;
-
-MACRO connect_form(vp, event) BLOCK;
-
- '';
-
-END;
-
-MACRO connect_control(vp, event, value) BLOCK;
-
- 'name="'; vp.event_id_for(event); '"';
-
-END;
-
-MACRO connect_href(vp, events) BLOCK;
-
- FOREACH event = events.keys;
- evt_args.${vp.event_id_for(event)} = events.$event;
- END;
- 'href="'; ctx.req.uri_with(evt_args); '"';
-
-END;
-
-UNLESS type;
- errmsg = "type is empty rendering " _ self;
- THROW file errmsg;
-END;
-
-PROCESS $type;
-
-IF GLOBAL_DEBUG; '<!-- Rendering component '; type | html; " -->\n"; END;
-
-include( 'main_block' );
-
-IF GLOBAL_DEBUG; '<!-- End component '; type | html; " -->\n"; END;
-
--%]
diff --git a/root/base/displayfield/list b/root/base/displayfield/list
deleted file mode 100644
index 2dcf066..0000000
--- a/root/base/displayfield/list
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-PROCESS displayfield_base;
-
-control_block = 'list_control';
-
-BLOCK list_control;
-
- "<ul>\n";
- FOREACH v_val IN self.value_names;
- ' <li>'; v_val | html; "</li>\n";
- END;
- "</ul>\n";
-
-END;
-
-%]
diff --git a/root/base/displayfield/string b/root/base/displayfield/string
deleted file mode 100644
index 7fa3075..0000000
--- a/root/base/displayfield/string
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS displayfield_base;
-
-control_block = 'string_control';
-
-BLOCK string_control;
-
- self.value | html;
-
-END;
-
-%]
diff --git a/root/base/displayfield/text b/root/base/displayfield/text
deleted file mode 100644
index dded894..0000000
--- a/root/base/displayfield/text
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS displayfield_base;
-
-control_block = 'text_control';
-
-BLOCK text_control;
-
- self.value | html;
-
-END;
-
-%]
diff --git a/root/base/displayfield/value_string b/root/base/displayfield/value_string
deleted file mode 100644
index 1277e83..0000000
--- a/root/base/displayfield/value_string
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS displayfield_base;
-
-control_block = 'vstring_control';
-
-BLOCK vstring_control;
-
- self.value_string | html;
-
-END;
-
-%]
diff --git a/root/base/displayfield_base b/root/base/displayfield_base
deleted file mode 100644
index 3fdcfca..0000000
--- a/root/base/displayfield_base
+++ /dev/null
@@ -1,23 +0,0 @@
-[%-
-
-main_block = 'displayfield_base_field';
-
-control_block = 'displayfield_base_control';
-
-BLOCK displayfield_base_field;
-
- IF self.label;
- '<label>'; loc(self.label); '</label>: ';
- END;
-
- include( 'control_block' );
-
-END;
-
-BLOCK displayfield_base_control;
-
- "CONTROL";
-
-END;
-
--%]
diff --git a/root/base/dt_textfield b/root/base/dt_textfield
deleted file mode 100644
index 749e3ca..0000000
--- a/root/base/dt_textfield
+++ /dev/null
@@ -1,16 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'textfield_control';
-
-BLOCK textfield_control;
-
- attrs.maxlength = '255'; # SimpleStr requires <= 255
- %]<input type="text" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, 'value_string');
- ' value="'; self.value_string | html; '"'; process_attrs(attrs) %] />[%
- attrs.maxlength = '';
-
-END;
-
-%]
diff --git a/root/base/dual_select_group b/root/base/dual_select_group
deleted file mode 100644
index 1cc5243..0000000
--- a/root/base/dual_select_group
+++ /dev/null
@@ -1,42 +0,0 @@
-[%
-
-PROCESS select_group;
-
-control_block = 'dual_select_group_control';
-
-BLOCK dual_select_group_control;
-
- -%]</p><table[% process_attrs(attrs) %]>
- <tr>
- <td>
-[%- self.label = ''; self.tmp_message = self.message; self.message = '';
- values_list_type = 'available_values';
- INCLUDE component type = 'select_group' self.hide_selected = 1 attrs.size = 10 attrs.name = 'add_values' | indent(4);
- attrs.name = ''; attrs.size = ''; %]
- </td><td align="center">[%
- INCLUDE component type = 'submitbtn' attrs.value = '>>' self.event = 'add_all_values' | indent(4);
- '<br />';
- INCLUDE component type = 'submitbtn' attrs.value = '>' self.event = 'do_add_values' | indent(4);
- '<br />';
- INCLUDE component type = 'submitbtn' attrs.value = '<' self.event = 'do_remove_values' | indent(4);
- '<br />';
- INCLUDE component type = 'submitbtn' attrs.value = '<<' self.event = 'remove_all_values' | indent(4); %]
- </td><td>
-[%- attrs.value = '';
- values_list_type = 'current_values';
- INCLUDE component type = 'select_group' self.hide_selected = 1 attrs.size = 10 attrs.name = 'remove_values' | indent(4);
- attrs.name = ''; attrs.size = '';
-
- FOREACH v_val IN self.current_values;
- v_val = self.obj_to_str(v_val);
- INCLUDE component type = 'hidden' self.val = v_val attrs = '' | indent(4);
- END;
-
-# self.message = self.tmp_message; self.tmp_message = ''; %]
- </td>
- </tr>[%
- %]</table><p>[%
-
-END;
-
-%]
diff --git a/root/base/error_404 b/root/base/error_404
deleted file mode 100644
index 0177cba..0000000
--- a/root/base/error_404
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-main_block = 'error_404_main';
-
-BLOCK error_404_main;
-
- loc("404 Not Found");
-
- %] <a href="[% ctx.uri_for(ctx.action.chain.0.attributes.Chained.0) %]">[%
-
- loc("Return to root");
-
- %]</a>[%
-
-END;
-
-%]
diff --git a/root/base/field_base b/root/base/field_base
deleted file mode 100644
index 3605a8c..0000000
--- a/root/base/field_base
+++ /dev/null
@@ -1,27 +0,0 @@
-[%-
-
-main_block = 'field_base_field';
-
-control_block = 'field_base_control';
-
-BLOCK field_base_field;
-
- IF self.label;
- '<label>'; loc(self.label); '</label>: ';
- END;
-
- include( 'control_block' );
-
- IF self.message;
- "\n<span>"; loc(self.message); '</span>';
- END;
-
-END;
-
-BLOCK field_base_control;
-
- "CONTROL";
-
-END;
-
--%]
diff --git a/root/base/fieldset b/root/base/fieldset
deleted file mode 100644
index 7daa8be..0000000
--- a/root/base/fieldset
+++ /dev/null
@@ -1,20 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'fieldset_control';
-
-BLOCK fieldset_control;
-
- %]<fieldset id="[% self.field_name | html %]"[% process_attrs(attrs) %] />[%
- IF self.text;
- '<legend>'; self.text; '</legend>';
- END;
-
-# INCLUDE( 'control_block' );
-
- %]</fieldset>[%
-
-END;
-
-%]
diff --git a/root/base/file b/root/base/file
deleted file mode 100644
index c89c397..0000000
--- a/root/base/file
+++ /dev/null
@@ -1,16 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'fileselect_control';
-
-BLOCK fileselect_control;
-
- %]<input type="file" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, 'value');
- # browsers ignore this for security reasons, can be uncommented for testing.
- # ' value="'; self.value.filename | html; '"';
- process_attrs(attrs) %] />[%
-
-END;
-
-%]
diff --git a/root/base/footer b/root/base/footer
deleted file mode 100644
index aa00551..0000000
--- a/root/base/footer
+++ /dev/null
@@ -1,12 +0,0 @@
-[%-
-
-#main_block = 'footer';
-
-#BLOCK footer;
-
- %]<p>FOOTER</p>
- [%
-
-#END;
-
--%]
diff --git a/root/base/form_base b/root/base/form_base
deleted file mode 100644
index cb988ec..0000000
--- a/root/base/form_base
+++ /dev/null
@@ -1,77 +0,0 @@
-[%
-
-main_block = 'form_base_control';
-
-control_block = 'form_base_control';
-
-header_block = 'form_base_header';
-fields_block = 'form_base_fields';
-button_block = 'form_base_buttons';
-footer_block = 'form_base_footer';
-
-form_id = 0;
-
-BLOCK form_base_control;
-
- form_id = form_id + 1;
-
- enctype = attrs.enctype || 'multipart/form-data';
- %]<form action="[% attrs.action || connect_form %]" method="post" id="element_[% form_id %]" enctype="[% enctype %]"[%
- IF attrs.name != ""; ' name="'; attrs.name; attrs.name = ''; '"'; END;
- attrs.enctype = ''; attrs.action = '';
- process_attrs(self.attrs) %]>[% "\n";
-
- include( 'header_block' );
- include( 'fields_block' );
-
- id_attr = ''; '<p>';
- include( 'button_block' );
- include( 'footer_block' );
-
- "</p>\n</form>";
-
-END;
-
-BLOCK form_base_header;
-
- '';
-
-END;
-
-BLOCK form_base_fields;
-
- FOREACH f_name = self.field_names;
- field = self.fields.$f_name;
- id = form_id _ '_' _ loop.count;
- '<p>'; window.render_viewport(field); "</p>\n";
- END;
-
-END;
-
-BLOCK form_base_buttons;
-
- allowed_events = self.accept_events;
-
- IF allowed_events.grep('^ok$').size;
- INCLUDE component type = 'submitbtn' self.value = 'ok' self.event = 'ok' self.label = self.ok_label;
- END;
-
- IF (self.field_names.size != 0) && (allowed_events.grep('^apply$').size);
- INCLUDE component type = 'submitbtn' self.value = 'apply' self.event = 'apply' self.label = self.apply_label;
- END;
-
- IF allowed_events.grep('^close$').size;
- INCLUDE component type = 'cancelbtn' self.value = 'cancel' self.event = 'close' self.label = self.cancel_label;
- END;
-
-END;
-
-BLOCK form_base_footer;
-
- IF self.message;
- ' <span>'; self.message; '</span>';
- END;
-
-END;
-
-%]
diff --git a/root/base/header b/root/base/header
deleted file mode 100644
index 933457f..0000000
--- a/root/base/header
+++ /dev/null
@@ -1,11 +0,0 @@
-[%-
-
-#main_block = 'header_block';
-
-#BLOCK header_block;
-
- %]<p>HEADER</p>[%
-
-#END;
-
--%]
diff --git a/root/base/hidden b/root/base/hidden
deleted file mode 100644
index 6b5f6c4..0000000
--- a/root/base/hidden
+++ /dev/null
@@ -1,15 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'hidden_control';
-
-BLOCK hidden_control;
-
- name = attrs.name || 'value'; attrs.name = '';
- %]<input type="hidden" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, name);
- ' value="'; self.val; '"'; process_attrs(attrs) %] />[%
-
-END;
-
-%]
diff --git a/root/base/hiddenarray b/root/base/hiddenarray
deleted file mode 100644
index 8168ad3..0000000
--- a/root/base/hiddenarray
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'hiddenarray_control';
-
-BLOCK hiddenarray_control;
-
- name = attrs.name || 'value'; attrs.name = '';
- FOREACH val IN self.value;
- %]<input type="hidden" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, name);
- ' value="'; val; '"'; process_attrs(attrs) %] />[% "\n";
- END;
-
-END;
-
-%]
diff --git a/root/base/image b/root/base/image
deleted file mode 100644
index 36cf927..0000000
--- a/root/base/image
+++ /dev/null
@@ -1,11 +0,0 @@
-[%
-
-main_block = 'image_base';
-
-BLOCK image_base;
-
- %]<img src="[% self.img_src | html %]" alt="[% self.text | html %]"[% process_attrs(attrs) %] />[%
-
-END;
-
-%]
diff --git a/root/base/label b/root/base/label
deleted file mode 100644
index e380ba0..0000000
--- a/root/base/label
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'label_control';
-
-BLOCK label_control;
-
- %]<label id="[% self.field_name | html %]" [% connect_control(self, 'value') %] value="[% self.field_value | html %]" />[%
-
-# INCLUDE( 'control_block' );
-
- '</label>';
-
-END;
-
-%]
diff --git a/root/base/listview b/root/base/listview
deleted file mode 100644
index 382630d..0000000
--- a/root/base/listview
+++ /dev/null
@@ -1,60 +0,0 @@
-[%
-
-PROCESS listview_base;
-
-header_field_block = 'listview_header_field';
-
-BLOCK listview_header_field;
-
- desc = 0;
- IF (self.order_by == field_name && !self.order_by_desc);
- desc = 1;
- ELSE;
- desc = 0;
- END;
-
- "\n <th"; process_attrs(attrs); '><a '; connect_href(self, order_by => field_name, order_by_desc => desc); '>';
- loc(self.field_label(field_name)); '</a></th>';
-
-END;
-
-header_block = 'listview_header';
-
-BLOCK listview_header;
-
- INCLUDE listview_base_header;
- IF self.row_action_prototypes.size;
- %]
- <th colspan="[% self.row_action_prototypes.size %]"[%
- process_attrs(attrs); %]>[% loc('Actions'); %]</th>[%
- END;
-
-END;
-
-row_block = 'listview_row';
-
-BLOCK listview_row;
-
- INCLUDE listview_base_row;
- FOREACH action IN self.row_actions_for(row);
- %] <td[% process_attrs(attrs); %]><a href="[% action.uri %]">[%
- loc(action.label) %]</a></td>[%
- IF loop.last == 0; "\n"; END;
- END;
-
-END;
-
-row_field_block = 'listview_row_field';
-
-BLOCK listview_row_field;
-
- field_value = field_value || row.$f_name;
-
- IF field_value.isa('DateTime');
- field_value = field_value.strftime("%F %H:%M:%S");
- END;
- INCLUDE listview_base_row_field;
-
-END;
-
-%]
diff --git a/root/base/listview_base b/root/base/listview_base
deleted file mode 100644
index 9b71c30..0000000
--- a/root/base/listview_base
+++ /dev/null
@@ -1,124 +0,0 @@
-[%
-
-main_block = 'listview_base_main';
-
-table_start_block = 'listview_base_table_start';
-table_end_block = 'listview_base_table_end';
-row_block = 'listview_base_row';
-row_field_block = 'listview_base_row_field';
-header_block = 'listview_base_header';
-header_field_block = 'listview_base_header_field';
-footer_block = 'listview_base_footer';
-footer_field_block = 'listview_base_footer_field';
-create_link_block = 'listview_base_create';
-
-show_footer = 1;
-
-BLOCK listview_base_main;
-
- include( 'table_start_block' ); %]
- <thead>
- <tr>[% include( 'header_block' ) | indent(4); %]
- </tr>
- </thead>[%
-
- IF show_footer && self.footer_field_names.size != '';
- "\n <tfoot>";
- include( 'footer_block' ) | indent(4);
- "\n </tfoot>";
- END;
-
- %]
- <tbody>
- [%
-
- FOREACH row = self.current_rows;
- "<tr>\n";
- include( 'row_block' ) | indent(4);
- "\n </tr>";
- END; %]
- </tbody>[%
-
- include( 'table_end_block' );
-
-END;
-
-BLOCK listview_base_table_start;
-
- #IF self.has_per_page;
- IF self.has_per_page && self.pager.last_page > self.pager.first_page;
- INCLUDE component type = 'pager';
- END;
-
- %]<table>[%
-
-END;
-
-BLOCK listview_base_table_end;
-
- "\n</table>\n";
- include( 'create_link_block' );
-
-END;
-
-BLOCK listview_base_row;
-
- FOREACH f_name = self.field_names;
- include( 'row_field_block' );
- END;
-
-END;
-
-BLOCK listview_base_row_field;
-
- field_value = field_value || row.$f_name;
- IF field_value.can('display_name'); field_value = field_value.display_name; END;
- ' <td'; process_attrs(attrs); '>'; field_value || row.$f_name; "</td>\n";
-
-END;
-
-BLOCK listview_base_header;
-
- FOREACH field_name = self.field_names;
- include( 'header_field_block' );
- END;
-
-END;
-
-BLOCK listview_base_header_field;
-
- "\n<th>"; self.field_label(field_name); '</th>';
-
-END;
-
-BLOCK listview_base_footer;
-
- "\n<tr>";
-
- FOREACH footer_field_name = self.footer_field_names;
- include( 'footer_field_block' );
- END;
-
- '</tr>';
-
-END;
-
-BLOCK listview_base_footer_field;
-
- "\n <td>"; self.field_label(footer_field_name); '</td>';
-
-END;
-
-BLOCK listview_base_create;
-
- '<p>';
- action = ctx.controller.action_for('create');
- IF action;
- action = ctx.uri_for(action);
- '<a href="'; action; '">'; loc("Create record"); '</a>';
- END;
- '</p>';
-
-END;
-
-%]
diff --git a/root/base/objectview b/root/base/objectview
deleted file mode 100644
index 567d3c8..0000000
--- a/root/base/objectview
+++ /dev/null
@@ -1 +0,0 @@
-[% PROCESS view_base %]
diff --git a/root/base/pager b/root/base/pager
deleted file mode 100644
index cde0ce4..0000000
--- a/root/base/pager
+++ /dev/null
@@ -1,128 +0,0 @@
-[%
-
-main_block = 'pager_main';
-
-start_block = 'pager_start';
-prev_block = 'pager_prev';
-current_block = 'pager_current';
-next_block = 'pager_next';
-end_block = 'pager_end';
-list_block = 'pager_list';
-
-start_label_block = 'pager_start_label';
-prev_label_block = 'pager_prev_label';
-current_label_block = 'pager_current_label';
-next_label_block = 'pager_next_label';
-end_label_block = 'pager_end_label';
-list_label_block = 'pager_list_label';
-
-BLOCK pager_main;
-
- '<div>[ ';
- data = [];
-
- str = BLOCK; include( 'start_block' ); END;
- data.push(str) IF str;
-
- str = BLOCK; include( 'prev_block' ); END;
- data.push(str) IF str;
-
- str = BLOCK; include( 'current_block' ); END;
- data.push(str) IF str;
-
- str = BLOCK; include( 'next_block' ); END;
- data.push(str) IF str;
-
- str = BLOCK; include( 'end_block' ); END;
- data.push(str) IF str;
-
- data.join(" |\n");
- " ]</div>\n";
-
-END;
-
-BLOCK pager_start;
-
- %]<a [% connect_href(self, 'page' => self.pager.first_page); process_attrs(attrs) %]>[%
- include( 'start_label_block' ) %]</a>[%
-
-END;
-
-BLOCK pager_start_label;
-
- loc('Start'); ' ('; self.pager.first_page; ')';
-
-END;
-
-BLOCK pager_prev;
-
- IF self.pager.current_page != 1;
- %]<a [% connect_href(self, 'page' => self.pager.previous_page); process_attrs(attrs) %]>[%
- include( 'prev_label_block' ) %]</a>[%
- END;
-
-END;
-
-BLOCK pager_prev_label;
-
- loc('Previous'); ' ('; self.pager.previous_page; ')';
-
-END;
-
-BLOCK pager_current;
-
- %]<a [% connect_href(self, 'page' => self.pager.current_page); process_attrs(attrs) %]>[%
- include( 'current_label_block' ) %]</a>[%
-
-END;
-
-BLOCK pager_current_label;
-
- loc('Current'); ' ('; self.pager.current_page; ')';
-
-END;
-
-BLOCK pager_next;
-
- IF self.pager.current_page != self.pager.last_page;
- %]<a [% connect_href(self, 'page' => self.pager.next_page); process_attrs(attrs) %]>[%
- include( 'next_label_block' ) %]</a>[%
- END;
-
-END;
-
-BLOCK pager_next_label;
-
- loc('Next'); ' ('; self.pager.next_page; ')';
-
-END;
-
-BLOCK pager_end;
-
- %]<a [% connect_href(self, 'page' => self.pager.last_page); process_attrs(attrs) %]>[%
- include( 'end_label_block' ) %]</a>[%
-
-END;
-
-BLOCK pager_end_label;
-
- loc('End'); ' ('; self.pager.last_page; ')';
-
-END;
-
-BLOCK pager_list;
-
- FOREACH page IN self.pager.list;
- '<a'; connect_href(self, 'page' => page); process_attrs(attrs); '>';
- include( 'list_label_block' ); "</a>\n";
- END;
-
-END;
-
-BLOCK pager_list_label;
-
- page;
-
-END;
-
-%]
diff --git a/root/base/password b/root/base/password
deleted file mode 100644
index ba3f389..0000000
--- a/root/base/password
+++ /dev/null
@@ -1,14 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'passwordfield_control';
-
-BLOCK passwordfield_control;
-
- %]<input type="password" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, 'value');
- ' value="'; self.value | html; '"'; process_attrs(attrs) %] />[%
-
-END;
-
-%]
diff --git a/root/base/radio b/root/base/radio
deleted file mode 100644
index a4e897a..0000000
--- a/root/base/radio
+++ /dev/null
@@ -1,14 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'radio_control';
-
-BLOCK radio_control;
-
- %]<input type="radio" id="[% id_attr %]" [% connect_control(self, 'value');
- process_attrs(attrs) %] />[%
-
-END;
-
-%]
diff --git a/root/base/radio_group b/root/base/radio_group
deleted file mode 100644
index b64e5b8..0000000
--- a/root/base/radio_group
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'radiogroup_control';
-
-BLOCK radiogroup_control;
-
- FOREACH value IN self.values.keys;
- '<input type="radio" id="[% id_attr %]" [% connect_control(self, 'value');
- IF self.default == value; ' checked="checked"'; END;
- process_attrs(attrs); " />\n";
- END;
-
-END;
-
-%]
diff --git a/root/base/resetbtn b/root/base/resetbtn
deleted file mode 100644
index 859d5c8..0000000
--- a/root/base/resetbtn
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS button;
-
-control_block = 'resetbtn_control';
-
-BLOCK resetbtn_control;
-
- INCLUDE button_control button_type = 'reset' attrs.value = 'Reset';
-
-END;
-
-%]
diff --git a/root/base/search_base b/root/base/search_base
deleted file mode 100644
index 24bbfff..0000000
--- a/root/base/search_base
+++ /dev/null
@@ -1,14 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'search_base_control';
-
-BLOCK search_base_control;
-
- INCLUDE component type = 'textfield';
- INCLUDE component type = 'submitbtn' attrs.value = 'Search';
-
-END;
-
-%]
diff --git a/root/base/select b/root/base/select
deleted file mode 100644
index a387fa1..0000000
--- a/root/base/select
+++ /dev/null
@@ -1,38 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'select_control';
-
-BLOCK select_control;
-
- IF values_list_type;
- values_list = self.${values_list_type};
- ELSE;
- values_list = self.valid_values;
- END;
-
- name = attrs.name || 'value'; attrs.name = '';
- '<select ';
- IF id_attr; 'id="'; id_attr; '"'; END;
- connect_control(self, name); process_attrs(attrs); ">\n";
-
- IF attrs.nullable == 1 || !(self.attribute.required);
- attrs.nullable = '';
- " <option value=\"\">--</option>\n";
- END;
-
- FOREACH v_val IN values_list;
- v_val = self.obj_to_str(v_val);
- v_name = self.value_to_name_map.${v_val} || v_val;
- ' <option value="'; v_val | html; '"';
- IF (self.is_current_value(v_val) || self.value == v_val ) && !self.hide_selected;
- ' selected="selected"';
- END;
- '>'; v_name | html; "</option>\n";
- END;
- '</select>';
-
-END;
-
-%]
diff --git a/root/base/select_group b/root/base/select_group
deleted file mode 100644
index f740d77..0000000
--- a/root/base/select_group
+++ /dev/null
@@ -1,14 +0,0 @@
-[%
-
-PROCESS select;
-
-control_block = 'select_group_control';
-
-BLOCK select_group_control;
-
- INCLUDE select_control attrs.multiple = 'multiple';
- attrs.multiple = '';
-
-END;
-
-%]
diff --git a/root/base/submitbtn b/root/base/submitbtn
deleted file mode 100644
index 6e2246c..0000000
--- a/root/base/submitbtn
+++ /dev/null
@@ -1,13 +0,0 @@
-[%
-
-PROCESS button;
-
-control_block = 'submitbtn_control';
-
-BLOCK submitbtn_control;
-
- INCLUDE button_control button_type = 'submit' attrs.value = 'Submit' self.event = 'ok';
-
-END;
-
-%]
diff --git a/root/base/textarea b/root/base/textarea
deleted file mode 100644
index 57114f9..0000000
--- a/root/base/textarea
+++ /dev/null
@@ -1,15 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'textarea_control';
-
-BLOCK textarea_control;
-
- attrs.maxlength = '';
- %]<textarea id="[% id_attr %]" [% connect_control(self, 'value');
- process_attrs(attrs) %]>[% self.value | html; '</textarea>';
-
-END;
-
-%]
diff --git a/root/base/textfield b/root/base/textfield
deleted file mode 100644
index a43f445..0000000
--- a/root/base/textfield
+++ /dev/null
@@ -1,17 +0,0 @@
-[%
-
-PROCESS field_base;
-
-control_block = 'textfield_control';
-
-BLOCK textfield_control;
-
- attrs.maxlength = '255'; # SimpleStr requires <= 255
- name = attrs.name || 'value'; attrs.name = '';
- %]<input type="text" [% IF id_attr; 'id="'; id_attr; '"'; END; connect_control(self, name);
- ' value="'; self.value | html; '"'; process_attrs(attrs) %] />[%
- attrs.maxlength = '';
-
-END;
-
-%]
diff --git a/root/base/timerange b/root/base/timerange
deleted file mode 100644
index a987cfd..0000000
--- a/root/base/timerange
+++ /dev/null
@@ -1,44 +0,0 @@
-[%
-
-main_block = 'timerange_field';
-
-BLOCK timerange_field;
-
- include( 'control_block' );
-
- IF self.message;
- "\n<span>"; loc(self.message); '</span>';
- END;
-
-END;
-
-control_block = 'timerange_control';
-
-BLOCK timerange_control;
-
- name = attrs.name || 'value_string'; attrs.name = '';
- self.label = '';
- data = self.value_string.split(',');
- #USE dumper; dumper.dump(data);
- data.0.replace('T', ' ') | ucfirst; ' to '; data.1.replace('T', ' ');
- IF data.2 == 'none'; data.2 = ''; END;
- IF data.2 != '';
- ' every '; data.4.replace('dai', 'day').replace('ly', '');
- ' between '; data.2.replace('T', ' '); ' and '; data.3.replace('T', ' ');
- END;
- inner = {
- value => self.delete_label,
- event => 'delete',
- location => self.location,
- };
-# INCLUDE component type = 'button' button_type = 'submit' self = inner;
- '<input type="submit" value="'; self.delete_label; ;'" '; connect_control(self, 'delete'); ' />';
- "<br />\n";
- '<input type="hidden" '; connect_control(self, name); ' value="'; self.value_string; '"'; process_attrs(attrs); ' />';
- "\n";
-
-# INCLUDE component type = 'hiddenarray' self.value = ctx.stash.ranges;
-
-END;
-
-%]
diff --git a/root/base/timerangecollection b/root/base/timerangecollection
deleted file mode 100644
index 2c0bf1a..0000000
--- a/root/base/timerangecollection
+++ /dev/null
@@ -1,60 +0,0 @@
-[%
-
-PROCESS form_base;
-
-fields_block = 'timerangecollection_control';
-
-BLOCK timerangecollection_control;
-
- include( 'error_block' );
- include( 'results_block' );
- FOREACH f_name = self.field_names;
- NEXT IF f_name.match('range');
- field = self.fields.$f_name;
- '<p>'; window.render_viewport(field); "</p>\n";
- END;
-
-END;
-
-results_block = 'timerangecollection_results';
-
-BLOCK timerangecollection_results;
-
- FOREACH field = self.range_vps;
- '<p>'; window.render_viewport(field); "</p>\n";
- END;
- '<input type="hidden"'; connect_control(self, 'max_range_vps'); ' value="'; self.range_vps.size; '" />';
-# INCLUDE component type = 'hidden' self.name = 'max_range_vps' self.val = self.range_vps.size;
-
-END;
-
-error_block = 'timerangecollection_error';
-
-BLOCK timerangecollection_error;
-
- IF self.warning;
- '<p>'; self.warning; '</p>';
- END;
- IF self.error;
- '<p>'; self.error; '</p>';
- END;
-
-END;
-
-button_block = 'timerangecollection_buttons';
-
-BLOCK timerangecollection_buttons;
-
- INCLUDE component type = 'submitbtn' self.value = 'add' self.event = 'add_range_vp' self.label = '';
-
- IF self.has_on_next_callback;
- INCLUDE component type = 'submitbtn' self.value = 'next' self.event = 'next' self.label = '';
- END;
-
- IF self.is_changed; self.value = 'cancel'; ELSE; self.value = 'close'; END;
- INCLUDE component type = 'cancelbtn' self.label = '' self.event = 'close';
- '<br />';
-
-END;
-
-%]
diff --git a/root/base/view_base b/root/base/view_base
deleted file mode 100644
index e67ac8f..0000000
--- a/root/base/view_base
+++ /dev/null
@@ -1,22 +0,0 @@
-[%
-
-main_block = 'view_base_control';
-control_block = 'view_base_control';
-fields_block = 'view_base_fields';
-
-BLOCK view_base_control;
-
- include( 'fields_block' );
-
-END;
-
-BLOCK view_base_fields;
-
- FOREACH f_name = self.field_names;
- field = self.fields.$f_name;
- window.render_viewport(field); "<br />\n";
- END;
-
-END;
-
-%]
diff --git a/root/base/xhtml b/root/base/xhtml
deleted file mode 100644
index 0c0ea26..0000000
--- a/root/base/xhtml
+++ /dev/null
@@ -1,29 +0,0 @@
-[% BLOCK xhtml_main; -%]
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-
-<head>
- <title>[% window.title %]</title>
-
- [%- FOREACH stylesheet IN stylesheets; -%]
- <link rel="stylesheet" type="text/css" href="[% ctx.uri_for('/stylesheets', stylesheet) %]" />
- [%- END; -%]
- [%- FOREACH javascript IN javascripts; -%]
- <script src="[% ctx.uri_for('/javascript', javascript) %]" type="text/javascript"></script>
- [%- END; -%]
-
- <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
- <meta name="GENERATOR" content="Catalyst/TT" />
-</head>
-
-<body>
-[% INCLUDE header;
-window.render_viewport(self.inner); %]
-[% INCLUDE footer; %]
-</body>
-</html>
-[%- END;
-main_block = 'xhtml_main';
--%]
diff --git a/root/favicon.ico b/root/favicon.ico
deleted file mode 100644
index 5ad723d..0000000
--- a/root/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/root/index b/root/index
deleted file mode 100644
index 70d0472..0000000
--- a/root/index
+++ /dev/null
@@ -1,18 +0,0 @@
-[%
-
-main_block = 'index';
-
-BLOCK index;
-
-%]
-
-<h2>Using InterfaceModel, Reflector</h2>
-<p><a href="[% ctx.uri_for('/testmodel/foo') %]">foo</a></p>
-<p><a href="[% ctx.uri_for('/testmodel/bar') %]">bar</a></p>
-<p><a href="[% ctx.uri_for('/testmodel/baz') %]">baz</a></p>
-
-[%
-
-END;
-
-%]
diff --git a/root/static/images/btn_120x50_built.png b/root/static/images/btn_120x50_built.png
deleted file mode 100644
index c709fd6..0000000
--- a/root/static/images/btn_120x50_built.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_120x50_built_shadow.png b/root/static/images/btn_120x50_built_shadow.png
deleted file mode 100644
index 15142fe..0000000
--- a/root/static/images/btn_120x50_built_shadow.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_120x50_powered.png b/root/static/images/btn_120x50_powered.png
deleted file mode 100644
index 7249b47..0000000
--- a/root/static/images/btn_120x50_powered.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_120x50_powered_shadow.png b/root/static/images/btn_120x50_powered_shadow.png
deleted file mode 100644
index e6876c0..0000000
--- a/root/static/images/btn_120x50_powered_shadow.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_88x31_built.png b/root/static/images/btn_88x31_built.png
deleted file mode 100644
index 007b5db..0000000
--- a/root/static/images/btn_88x31_built.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_88x31_built_shadow.png b/root/static/images/btn_88x31_built_shadow.png
deleted file mode 100644
index ccf4624..0000000
--- a/root/static/images/btn_88x31_built_shadow.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_88x31_powered.png b/root/static/images/btn_88x31_powered.png
deleted file mode 100644
index 8f0cd9f..0000000
--- a/root/static/images/btn_88x31_powered.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/btn_88x31_powered_shadow.png b/root/static/images/btn_88x31_powered_shadow.png
deleted file mode 100644
index aa776fa..0000000
--- a/root/static/images/btn_88x31_powered_shadow.png
+++ /dev/null
Binary files differ
diff --git a/root/static/images/catalyst_logo.png b/root/static/images/catalyst_logo.png
deleted file mode 100644
index 21f1cac..0000000
--- a/root/static/images/catalyst_logo.png
+++ /dev/null
Binary files differ
diff --git a/share/skin/default/layout/index.tt b/share/skin/componentui/layout/index.tt
index 9a9bc9c..9a9bc9c 100644
--- a/share/skin/default/layout/index.tt
+++ b/share/skin/componentui/layout/index.tt
diff --git a/share/skin/componentui/skin.conf b/share/skin/componentui/skin.conf
index a9827b2..0fa450d 100644
--- a/share/skin/componentui/skin.conf
+++ b/share/skin/componentui/skin.conf
@@ -1 +1,3 @@
extends default
+
+widget_search_path ComponentUI::View::Site::Widget
diff --git a/share/skin/default/layout/field/mutable/text.tt b/share/skin/default/layout/field/mutable/text.tt
index c038280..b4038aa 100644
--- a/share/skin/default/layout/field/mutable/text.tt
+++ b/share/skin/default/layout/field/mutable/text.tt
@@ -2,8 +2,8 @@
=for layout field
-<textarea name="[% field_name %]" id="[% field_id %]">
- [% field_value %]
-</textarea>
+<textarea name="[% field_name %]" id="[% field_id %]">[%
+ field_value
+%]</textarea>
=cut
diff --git a/share/skin/default/layout/site_layout.tt b/share/skin/default/layout/site_layout.tt
index 1e7afa1..e9b70ca 100644
--- a/share/skin/default/layout/site_layout.tt
+++ b/share/skin/default/layout/site_layout.tt
@@ -24,6 +24,15 @@
=for layout head_meta
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ [% meta_info %]
+
+=for layout meta_info
+
+[% call_next %]
+
+=for layout meta_member
+
+<meta name="[% meta_name %]" content="[% meta_value %]" />[% "\n" %]
=for layout head_style
diff --git a/share/skin/defaults.conf b/share/skin/defaults.conf
new file mode 100644
index 0000000..b7d630e
--- /dev/null
+++ b/share/skin/defaults.conf
@@ -0,0 +1 @@
+widget_search_path Reaction::UI::Widget