added model and controller
Robert 'phaylon' Sedlacek [Tue, 4 Aug 2009 21:49:38 +0000 (23:49 +0200)]
13 files changed:
README
lib/CatalystX/Declare.pm
lib/CatalystX/Declare/Keyword/Component.pm [new file with mode: 0644]
lib/CatalystX/Declare/Keyword/Controller.pm
lib/CatalystX/Declare/Keyword/Model.pm [new file with mode: 0644]
lib/CatalystX/Declare/Keyword/View.pm [new file with mode: 0644]
t/070_views.t [new file with mode: 0644]
t/080_models.t [new file with mode: 0644]
t/lib/TestApp/Controller/ModelTest.pm [new file with mode: 0644]
t/lib/TestApp/Controller/ViewTest.pm [new file with mode: 0644]
t/lib/TestApp/Model/TestModel.pm [new file with mode: 0644]
t/lib/TestApp/View/TestView.pm [new file with mode: 0644]
t/lib/TestApp/ViewRole/TestRole.pm [new file with mode: 0644]

diff --git a/README b/README
index 4adecc2..f2a12bf 100644 (file)
--- a/README
+++ b/README
@@ -43,7 +43,7 @@ SYNOPSIS
   Roles
         use CatalystX::Declare;
 
-        component_role MyApp::Web::ControllerRole::Bar {
+        controller_role MyApp::Web::ControllerRole::Bar {
 
             use MyApp::Types qw( Username );
 
index 60b48c3..9e584e1 100644 (file)
@@ -2,6 +2,8 @@ use MooseX::Declare;
 
 class CatalystX::Declare extends MooseX::Declare is dirty {
 
+    use aliased 'CatalystX::Declare::Keyword::Model',       'ModelKeyword';
+    use aliased 'CatalystX::Declare::Keyword::View',        'ViewKeyword';
     use aliased 'CatalystX::Declare::Keyword::Controller',  'ControllerKeyword';
     use aliased 'CatalystX::Declare::Keyword::Role',        'RoleKeyword';
     use aliased 'CatalystX::Declare::Keyword::Application', 'ApplicationKeyword';
@@ -12,9 +14,11 @@ class CatalystX::Declare extends MooseX::Declare is dirty {
 
     around keywords {
         $self->$orig,
-        ControllerKeyword->new(identifier => 'controller'),
-        RoleKeyword->new(identifier => 'controller_role'),
-        ApplicationKeyword->new(identifier => 'application'),
+        ControllerKeyword->new(     identifier => 'controller'      ),
+        RoleKeyword->new(           identifier => 'controller_role' ),
+        ApplicationKeyword->new(    identifier => 'application'     ),
+        ViewKeyword->new(           identifier => 'view'            ),
+        ModelKeyword->new(          identifier => 'model'           ),
     }
 }
 
@@ -35,6 +39,10 @@ CatalystX::Declare - EXPERIMENTAL Declarative Syntax for Catalyst Applications
         $CLASS->config(name => 'My Declarative Web Application');
     }
 
+See also: 
+L<CatalystX::Declare::Keyword::Application>, 
+L<MooseX::Declare/class>
+
 =head2 Controllers
 
     use CatalystX::Declare;
@@ -65,6 +73,12 @@ CatalystX::Declare - EXPERIMENTAL Declarative Syntax for Catalyst Applications
         }
     }
 
+See also: 
+L<CatalystX::Declare::Keyword::Controller>, 
+L<CatalystX::Declare::Keyword::Action>, 
+L<CatalystX::Declare::Keyword::Component>, 
+L<MooseX::Declare/class>
+
 =head2 Roles
 
     use CatalystX::Declare;
@@ -91,6 +105,45 @@ CatalystX::Declare - EXPERIMENTAL Declarative Syntax for Catalyst Applications
         }
     }
 
+See also: 
+L<CatalystX::Declare::Keyword::Role>, 
+L<CatalystX::Declare::Keyword::Action>, 
+L<MooseX::Declare/class>
+
+=head2 Views
+
+    use CatalystX::Declare;
+
+    view MyApp::Web::View::TT
+        extends Catalyst::View::TT {
+
+        $CLASS->config(
+            TEMPLATE_EXTENSION => '.html',
+        );
+    }
+
+See also: 
+L<CatalystX::Declare::Keyword::View>, 
+L<CatalystX::Declare::Keyword::Component>, 
+L<MooseX::Declare/class>
+
+=head2 Models
+
+    use CatalystX::Declare;
+
+    model MyApp::Web::Model::DBIC::Schema
+        extends Catalyst::Model::DBIC::Schema {
+
+        $CLASS->config(
+            schema_class => 'MyApp::Schema',
+        );
+    }
+
+See also: 
+L<CatalystX::Declare::Keyword::Model>, 
+L<CatalystX::Declare::Keyword::Component>, 
+L<MooseX::Declare/class>
+
 =head1 DESCRIPTION
 
 B<This module is EXPERIMENTAL>
@@ -118,12 +171,16 @@ syntax extensions:
 
 =item L<CatalystX::Declare::Keyword::Application>
 
-=item L<CatalystX::Declare::Keyword::Controller>
-
 =item L<CatalystX::Declare::Keyword::Action>
 
+=item L<CatalystX::Declare::Keyword::Controller>
+
 =item L<CatalystX::Declare::Keyword::Role>
 
+=item L<CatalystX::Declare::Keyword::View>
+
+=item L<CatalystX::Declare::Keyword::Model>
+
 =back
 
 Things like models, views, roles for request or response objects, can be built
diff --git a/lib/CatalystX/Declare/Keyword/Component.pm b/lib/CatalystX/Declare/Keyword/Component.pm
new file mode 100644 (file)
index 0000000..25efa82
--- /dev/null
@@ -0,0 +1,94 @@
+use MooseX::Declare;
+
+class CatalystX::Declare::Keyword::Component
+    extends MooseX::Declare::Syntax::Keyword::Class
+    with    CatalystX::Declare::DefaultSuperclassing {
+
+
+    before add_namespace_customizations (Object $ctx, Str $package) {
+
+        $ctx->add_preamble_code_parts(
+            'use CLASS',
+        );
+    }
+
+    method default_superclasses { 'Catalyst::Component' }
+}
+
+__END__
+
+=head1 NAME
+
+CatalystX::Declare::Keyword::Component - Declare Catalyst Components
+
+=head1 DESCRIPTION
+
+This handler provides common functionality for all component handlers.
+Please refer to the respective keyword handler documentation for more
+information:
+
+=over
+
+=item L<CatalystX::Declare::Keyword::Model>
+
+=item L<CatalystX::Declare::Keyword::View>
+
+=item L<CatalystX::Declare::Keyword::Controller>
+
+=back
+
+=head1 SUPERCLASSES
+
+=over
+
+=item L<MooseX::Declare::Syntax::Keyword::Class>
+
+=back
+
+=head1 ROLES
+
+=head1 METHODS
+
+These methods are implementation details. Unless you are extending or 
+developing L<CatalystX::Declare>, you should not be concerned with them.
+
+=head2 add_namespace_customizations
+
+    Object->add_namespace_customizations (Object $ctx, Str $package)
+
+This will simply add L<CLASS> to the imported modules, to make C<CLASS>
+and C<$CLASS> available in the component.
+
+=head2 default_superclasses
+
+    List[Str] Object->default_superclasses ()
+
+Returns L<Catalyst::Component> as default superclass for components. The
+subclasses for other component keywords will usually want to override this.
+
+=head1 SEE ALSO
+
+=over
+
+=item L<CatalystX::Declare>
+
+=item L<CatalystX::Declare::Keyword::Model>
+
+=item L<CatalystX::Declare::Keyword::View>
+
+=item L<CatalystX::Declare::Keyword::Controller>
+
+=item L<MooseX::Declare/class>
+
+=back
+
+=head1 AUTHOR
+
+See L<CatalystX::Declare/AUTHOR> for author information.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under 
+the same terms as perl itself.
+
+=cut
index 6d1e9a5..84fd8f7 100644 (file)
@@ -1,9 +1,7 @@
 use MooseX::Declare;
 
-class CatalystX::Declare::Keyword::Controller 
-    extends MooseX::Declare::Syntax::Keyword::Class
-    with    CatalystX::Declare::DefaultSuperclassing {
-
+class CatalystX::Declare::Keyword::Controller
+    extends CatalystX::Declare::Keyword::Component {
 
     use MooseX::MethodAttributes ();
     use aliased 'CatalystX::Declare::Keyword::Action', 'ActionKeyword';
@@ -17,8 +15,8 @@ class CatalystX::Declare::Keyword::Controller
     before add_namespace_customizations (Object $ctx, Str $package) {
 
         MooseX::MethodAttributes->init_meta(for_class => $package);
+
         $ctx->add_preamble_code_parts(
-            'use CLASS',
             ['BEGIN',
                 sprintf('Class::MOP::load_class(q(%s))', TypeConstraintMapping),
                 sprintf('%s->meta->apply(%s->meta->meta)', TypeConstraintMapping, $package),
@@ -34,6 +32,18 @@ class CatalystX::Declare::Keyword::Controller
 
     method auto_make_immutable { 0 }
 
+    around default_inner () {
+
+        my @modifiers = qw( ); 
+
+        return [
+            ( grep { my $id = $_->identifier; not grep { $id eq $_ } @modifiers } @{ $self->$orig() || [] } ),
+            ActionKeyword->new(identifier => 'action'),
+            ActionKeyword->new(identifier => 'under'),
+            ActionKeyword->new(identifier => 'final'),
+        ];
+    }
+
     method add_with_option_customizations (Object $ctx, $package, ArrayRef $roles, HashRef $options) {
 
         $ctx->add_cleanup_code_parts(
@@ -47,18 +57,6 @@ class CatalystX::Declare::Keyword::Controller
             sprintf '%s->meta->make_immutable', $package
         ) unless $options->{is}{mutable};
     }
-
-    around default_inner () {
-
-        my @modifiers = qw( ); 
-
-        return [
-            ( grep { my $id = $_->identifier; not grep { $id eq $_ } @modifiers } @{ $self->$orig() || [] } ),
-            ActionKeyword->new(identifier => 'action'),
-            ActionKeyword->new(identifier => 'under'),
-            ActionKeyword->new(identifier => 'final'),
-        ];
-    }
 }
 
 __END__
@@ -92,7 +90,9 @@ CatalystX::Declare::Keyword::Controller - Declare Catalyst Controllers
 =head1 DESCRIPTION
 
 This handler module allows the declaration of Catalyst controllers. The
-C<controller> keyword is an extension of L<MooseX::Declare/class> with all the
+C<controller> keyword is an extension of the 
+L<CatalystX::Declare::Keyword::Component>, which in turn is an extension 
+of L<MooseX::Declare/class> with all the
 bells and whistles, including C<extends>, C<with>, C<method> and modifier
 declarations.
 
@@ -108,15 +108,7 @@ usual.
 
 =over
 
-=item L<MooseX::Declare::Syntax::Keyword::Class>
-
-=back
-
-=head1 ROLES
-
-=over
-
-=item L<CatalystX::Declare::DefaultSuperclassing>
+=item L<CatalystX::Declare::Keyword::Component>
 
 =back
 
@@ -130,7 +122,7 @@ developing L<CatalystX::Declare>, you should not be concerned with them.
     Object->add_namespace_customizations (Object $ctx, Str $package)
 
 This method modifier will initialise the controller with 
-L<MooseX::MethodAttributes>, import L<CLASS> and add the 
+L<MooseX::MethodAttributes> and add the 
 L<CatalystX::Declare::Controller::RegisterActionRoles> and
 L<CatalystX::Declare::Controller::DetermineActionClass> controller roles
 before calling the original.
@@ -142,14 +134,6 @@ before calling the original.
 Returns L<Catalyst::Controller> as the default superclass for all declared
 controllers.
 
-=head2 auto_make_immutable
-
-    Bool Object->auto_make_immutable ()
-
-Returns C<0>, indicating that L<MooseX::Declare> should not make this class
-immutable by itself. We will do that in the L</add_with_option_customizations>
-method ourselves.
-
 =head2 add_with_option_customizations
 
     Object->add_with_option_customizations (
@@ -167,6 +151,14 @@ together.
 This method will also add a callback to make the controller immutable to the
 cleanup code parts unless C<is mutable> was specified.
 
+=head2 auto_make_immutable
+
+    Bool Object->auto_make_immutable ()
+
+Returns C<0>, indicating that L<MooseX::Declare> should not make this class
+immutable by itself. We will do that in the L</add_with_option_customizations>
+method ourselves.
+
 =head2 default_inner
 
     ArrayRef[Object] Object->default_inner ()
@@ -184,6 +176,8 @@ C<under> and C<final> identifiers.
 
 =item L<CatalystX::Declare::Keyword::Action>
 
+=item L<CatalystX::Declare::Keyword::Component>
+
 =item L<MooseX::Declare/class>
 
 =back
diff --git a/lib/CatalystX/Declare/Keyword/Model.pm b/lib/CatalystX/Declare/Keyword/Model.pm
new file mode 100644 (file)
index 0000000..16a6465
--- /dev/null
@@ -0,0 +1,71 @@
+use MooseX::Declare;
+
+class CatalystX::Declare::Keyword::Model
+    extends CatalystX::Declare::Keyword::Component {
+
+    method default_superclasses { 'Catalyst::Model' }
+}
+
+__END__
+
+=head1 NAME
+
+CatalystX::Declare::Keyword::Model - Declare Catalyst Models
+
+=head1 SYNOPSIS
+
+    use CatalystX::Declare;
+
+    model MyApp::Web::Model::Random {
+
+        method upto (Int $n) { int(rand $n) }
+    }
+
+=head1 DESCRIPTION
+
+This handler is a simple extension of L<CatalystX::Declare::Keyword::Component>
+and defaults to L<Catalyst::Model> as superclass. Additionally, L<CLASS> will
+be imported. See L<MooseX::Declare/class> for more information on usage, since
+this keyword is subclass extending its parent's features.
+
+=head1 SUPERCLASSES
+
+=over
+
+=item L<CatalystX::Declare::Keyword::Component>
+
+=back
+
+=head1 METHODS
+
+These methods are implementation details. Unless you are extending or 
+developing L<CatalystX::Declare>, you should not be concerned with them.
+
+=head2 default_superclasses
+
+    List[Str] Object->default_superclasses ()
+
+Defaults to L<Catalyst::View>.
+
+=head1 SEE ALSO
+
+=over
+
+=item L<CatalystX::Declare>
+
+=item L<CatalystX::Declare::Keyword::Component>
+
+=item L<MooseX::Declare/class>
+
+=back
+
+=head1 AUTHOR
+
+See L<CatalystX::Declare/AUTHOR> for author information.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under 
+the same terms as perl itself.
+
+=cut
diff --git a/lib/CatalystX/Declare/Keyword/View.pm b/lib/CatalystX/Declare/Keyword/View.pm
new file mode 100644 (file)
index 0000000..1efd1f2
--- /dev/null
@@ -0,0 +1,82 @@
+use MooseX::Declare;
+
+class CatalystX::Declare::Keyword::View
+    extends CatalystX::Declare::Keyword::Component {
+
+    method default_superclasses { 'Catalyst::View' }
+}
+
+__END__
+
+=head1 NAME
+
+CatalystX::Declare::Keyword::View - Declare Catalyst Views
+
+=head1 SYNOPSIS
+
+    use CatalystX::Declare;
+
+    view MyApp::Web::View::Example
+        extends Catalyst::View::TT 
+        with    MyApp::Web::ViewRole::Caching {
+
+        after process (Object $ctx) {
+            $ctx->log->debug('done processing at ' . time)
+                if $ctx->debug;
+        }
+    }
+
+=head1 DESCRIPTION
+
+This handler is a direct extension of L<CatalystX::Declare::Keyword::Component>
+and provides a C<view> keyword to declare a catalyst view. Currently, the only
+things this declaration does is setting up a L<Moose> class like 
+L<MooseX::Declare> does, provide L<CLASS> to its scope and default the 
+superclass to L<Catalyst::View>.
+
+See L<MooseX::Declare/class> for more information on this kind of keyword. 
+Since views do not take actions, no special handling of roles is required, 
+other than with controller roles. So if you want to write roles for views, 
+simply use the L<MooseX::Declare/role> syntax.
+
+=head1 SUPERCLASSES
+
+=over
+
+=item L<CatalystX::Declare::Keyword::Component>
+
+=back
+
+=head1 METHODS
+
+These methods are implementation details. Unless you are extending or 
+developing L<CatalystX::Declare>, you should not be concerned with them.
+
+=head2 default_superclasses
+
+    List[Str] Object->default_superclasses ()
+
+Defaults to L<Catalyst::View>.
+
+=head1 SEE ALSO
+
+=over
+
+=item L<CatalystX::Declare>
+
+=item L<CatalystX::Declare::Keyword::Component>
+
+=item L<MooseX::Declare/class>
+
+=back
+
+=head1 AUTHOR
+
+See L<CatalystX::Declare/AUTHOR> for author information.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under 
+the same terms as perl itself.
+
+=cut
diff --git a/t/070_views.t b/t/070_views.t
new file mode 100644 (file)
index 0000000..c87d7b0
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use Test::More; 
+use Catalyst::Test 'TestApp';
+
+is get('/view_test/normal'),    'view rendered', 'normal view rendering';
+is get('/view_test/role_test'), 'view rendered modified', 'view role changed rendering';
+
+
+done_testing;
diff --git a/t/080_models.t b/t/080_models.t
new file mode 100644 (file)
index 0000000..a29f694
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use Test::More; 
+use Catalyst::Test 'TestApp';
+
+my $counter = 1;
+
+is get('/model_test/next'), $counter++, 'modifying model'
+    for 1 .. 3;
+
+
+done_testing;
diff --git a/t/lib/TestApp/Controller/ModelTest.pm b/t/lib/TestApp/Controller/ModelTest.pm
new file mode 100644 (file)
index 0000000..b64afd0
--- /dev/null
@@ -0,0 +1,22 @@
+use CatalystX::Declare;
+
+controller TestApp::Controller::ModelTest {
+
+    method find_model (Object $ctx) {
+        return $ctx->component('Model::TestModel');
+    }
+
+
+    action base as 'model_test' under '/';
+
+    under base {
+
+        final action next {
+            $ctx->response->body( $self->find_model($ctx)->next );
+        }
+
+        final action reset {
+            $ctx->response->body( $self->find_model($ctx)->reset );
+        }
+    }
+}
diff --git a/t/lib/TestApp/Controller/ViewTest.pm b/t/lib/TestApp/Controller/ViewTest.pm
new file mode 100644 (file)
index 0000000..f82d679
--- /dev/null
@@ -0,0 +1,19 @@
+use CatalystX::Declare;
+
+controller TestApp::Controller::ViewTest {
+
+    action base as 'view_test' under '/';
+
+    under base {
+
+        final action normal;
+
+        final action role_test {
+            $ctx->stash(role_tag => 'modified');
+        }
+    }
+
+    action end { 
+        $ctx->forward( 'View::TestView' );
+    }
+}
diff --git a/t/lib/TestApp/Model/TestModel.pm b/t/lib/TestApp/Model/TestModel.pm
new file mode 100644 (file)
index 0000000..53aea6d
--- /dev/null
@@ -0,0 +1,23 @@
+use CatalystX::Declare;
+
+model TestApp::Model::TestModel {
+
+    use MooseX::Types::Moose qw( Int );
+
+    has counter => (
+        is          => 'rw',
+        isa         => Int,
+        required    => 1,
+        default     => 0,
+    );
+
+    method next {
+        $self->counter( $self->counter + 1 );
+        return $self->counter;
+    }
+
+    method reset {
+        $self->counter(0);
+        return 'reset';
+    }
+}
diff --git a/t/lib/TestApp/View/TestView.pm b/t/lib/TestApp/View/TestView.pm
new file mode 100644 (file)
index 0000000..28f422e
--- /dev/null
@@ -0,0 +1,13 @@
+use CatalystX::Declare;
+
+view TestApp::View::TestView
+    with TestApp::ViewRole::TestRole {
+
+    method render (Object $ctx) {
+        return 'view rendered';
+    }
+
+    method process (Object $ctx) {
+        $ctx->response->body( $self->render($ctx) );
+    }
+}
diff --git a/t/lib/TestApp/ViewRole/TestRole.pm b/t/lib/TestApp/ViewRole/TestRole.pm
new file mode 100644 (file)
index 0000000..33ec31b
--- /dev/null
@@ -0,0 +1,13 @@
+use MooseX::Declare;
+
+role TestApp::ViewRole::TestRole {
+
+    around render (Object $ctx) {
+
+        if (my $tag = $ctx->stash->{role_tag}) {
+            return join ' ' => $self->$orig($ctx), $tag;
+        }
+
+        return $self->$orig($ctx);
+    }
+}