documentation update, some api docs, overview and tutorial upto first DM and IM inclu...
phaylon [Mon, 9 Feb 2009 20:25:06 +0000 (20:25 +0000)]
lib/Reaction/Manual/Intro.pod
lib/Reaction/Manual/Overview.pod [new file with mode: 0644]
lib/Reaction/Manual/Tutorial.pod [new file with mode: 0644]
lib/Reaction/UI/Controller.pm
lib/Reaction/UI/ViewPort/Action.pm
lib/Reaction/UI/ViewPort/Action/Role/Apply.pm
lib/Reaction/UI/ViewPort/Action/Role/Close.pm
lib/Reaction/UI/ViewPort/Action/Role/OK.pm
lib/Reaction/UI/ViewPort/Object.pm
lib/Reaction/UI/ViewPort/Object/Mutable.pm

index cc868cf..cbf5bca 100644 (file)
@@ -48,7 +48,8 @@ Happily, Reaction again uses reflection to make the degenerate case easy - when
 IM has no customized functionality, it can simply delegate all work to the DM.  When 
 you need to add custom business logic, you can add or replace functionality as needed.
 
-The DM/IM split is sometimes referred to as a FacadeModel - see L<http://www.twinforces.com/tf/docs/MFCV.html>, for example.
+The DM/IM split is sometimes referred to as a FacadeModel - see 
+L<http://www.twinforces.com/tf/docs/MFCV.html>, for example.
 
 =head1 WHAT YOU'LL NEED TO KNOW
 
@@ -67,11 +68,16 @@ Thus, getting to know L<Moose> will serve you well.
 
 =head1 NEXT STEPS
 
+The L<Reaction::Manual::Overview> document tries to tie the parts of reaction
+together to form a big picture.
+
 If you'd like an example, see L<Reaction::Manual::Example>.
 
-If you're ready to dive in and start learning step by step, see L<Reaction::Manual::Tutorial>.
+If you're ready to dive in and start learning step by step, see 
+L<Reaction::Manual::Tutorial>.
 
-As you encounter unfamiliar terms, or want to see how a particular term is used in the context of the Reaction project, refer to the L<Reaction::Manual::Glossary>.
+As you encounter unfamiliar terms, or want to see how a particular term is used in the 
+context of the Reaction project, refer to the L<Reaction::Manual::Glossary>.
 
 =head1 SEE ALSO
 
diff --git a/lib/Reaction/Manual/Overview.pod b/lib/Reaction/Manual/Overview.pod
new file mode 100644 (file)
index 0000000..f8748e2
--- /dev/null
@@ -0,0 +1,181 @@
+=head1 NAME
+
+Reaction::Manual::Overview - Orientation in Reaction
+
+=head1 DESCRIPTION
+
+This document aims at describing the modular parts of L<Reaction> and explain
+how they are tied together.
+
+=head1 APPLICATION
+
+A Reaction application is really a L<Catalyst> application under the hood. Reaction
+uses reflection to build more flexible and re-usable Catalyst components.
+
+The main application module (usually called C<MyApp> or C<MyApp.pm> in documentation)
+looks exactly like a typical Catalyst application module. Reaction's modular architecture
+allows it therefor to be integrated into other Catalyst applications, or to integrate
+other Catalyst extensions and components itself.
+
+=head1 CONTROLLERS
+
+Usually in Catalyst applications the controller's actions will take their arguments,
+maybe modify them or clean them up. After that they are processed by the model and then
+stashed away to be later used by the view.
+
+Reactions approach is a bit different. The cleanup and validation of values, and the
+involvement of the model are abstracted into a L<Reaction::InterfaceModel::Action>
+subclass. Examples for such actions would be C<Create>, C<Update> or C<Delete> in a 
+CRUD situation.
+
+Controllers that use Reaction have to inherit from L<Reaction::UI::Controller> or a
+subclass of it. Some other useful controller base classes are:
+
+=over
+
+=item *
+
+L<Reaction::UI::Controller::Root> should be the base for the root controller to
+every chain of Reaction actions. It will provide a C<base> action you can chain
+to which will make sure the L<window viewport|/VIEWPORTS> and 
+L<focus stack|/FOCUS STACK> are set up.
+
+=item * 
+
+L<Reaction::UI::Controller::Collection> to ease the creation of components that act
+on collections as their model (database results for example). It provides actions
+to list and view the collection items.
+
+=item *
+
+L<Reaction::UI::Controller::Collection::CRUD> is a subclass of the above and provides
+additional C<create>, C<update>, C<delete> and C<delete_all> actions.
+
+=back
+
+=head1 VIEWPORTS
+
+Viewports represent the components that render your page when combined.
+
+The C<begin> action in L<Reaction::Controller::Root> creates a new L<Reaction::UI::Window>
+object and stores it as C<window> in the stash. The L<focus stack|/FOCUSSTACKS> of that
+window object is used as the base focus stack for the request.
+
+You can add a new inner viewport to the focus stack with the C<push_viewport> method
+available on your controller:
+
+  $controller->push_viewport($viewport_class, %viewport_args);
+
+This will add a new instance of C<$viewport_class> to the current focus stack using
+C<%viewport_args> as arguments. For more information on the usage and other options
+(for example the C<next_action> option, which redirects afterwards) see
+L<Reaction::UI::FocusStack> and L<Reaction::UI::ViewPort>.
+
+You can use the L<Reaction::UI::ViewPort::Action> viewport to build viewports
+that perform typical form actions like OK, Apply and Close.
+
+=head1 FOCUSSTACKS
+
+Viewports are pushed onto the current focus stack. The C<end> action in 
+L<Reaction::Controller::Root> will C<flush> the L<Reaction::UI::Window>
+object stored as C<window> in the stash.
+
+=head1 DOMAIN MODELS
+
+The domain models should be completely decoupled from the application and it's business
+logic. Normally, you need to decide whether to put your business logic in your controller
+or in your model. Reaction solves this problem by using L<interface models|/INTERFACE MODELS>
+as a separation between the two.
+
+If you want your domain model to be reflectable (L<DBIx::Class> for example) you will have
+to use L<Moose> to add attribute metadata to those classes.
+
+=head1 INTERFACE MODELS
+
+The interface models contain your business logic. That is, the application specific logic
+representing the model your application will use.
+
+An interface model consists of action classes subclassing L<Reaction::InterfaceModel::Action>.
+These instances will have both the request context and the target model available and can do
+their work in a C<do_apply> method.
+
+To allow your own models to be tied in to reflective controllers like
+L<Reaction::Controller::Collection>, you can subclass L<Reaction::InterfaceModel::Object>.
+That will provide you with a way to let the viewports introspect the actions that your
+interface model defines for this model.
+
+An example of this would be:
+
+  - MyApp::Controller::Foo is a Reaction::Controller::Collection::CRUD 
+    for MyApp::Model::Foo
+  - The model_name config setting is 'Model::Foo'
+  - User calls action MyApp::Controller::Foo->delete_old
+  - The 'delete_old' controller action will call 
+    $self->basic_model_action($c, \%vp_args)
+  - The 'target' option in %vp_args will be asked for an action that
+    corresponds with the 'delete_old' controller action
+  - An instance of MyApp::Model::Foo::Action::DeleteOld is
+    returned
+  - This is passed as 'model' to a new instance of
+    Reaction::UI::ViewPort::Action which is then pushed
+    onto the focus stack.
+
+Form processing as provided by L<Reaction::UI::ViewPort::Action> is a very good
+example of Reaction's usefulness; Instead of creating a new dialog for every
+form using myriads of helper functions, you provide a controller baseclass
+rendering the dialog by introspecting an interface model object with fields and
+actions.
+
+Then you just need to create a new controller and interface model for your new
+dialog and it just works.
+
+If your model is a L<DBIx::Class::Schema> and contains L<Moose> metadata, you
+can let L<Reaction::InterfaceModel::Reflector::DBIC> set up your interface
+model objects and actions.
+
+=head1 SKINS, LAYOUTS AND WIDGETS
+
+When you push a viewport onto the focus stack like this:
+
+  $controller->push_viewport('Reaction::UI::ViewPort::SiteLayout');
+
+Reaction will look for a layout file named
+C<$search_path/skin/$skin_name/layout/site_layout.tt>. If it can't find it,
+it will also look in the base skin and search paths.
+
+You can also provide a specific layout:
+
+  $controller->push_viewport(
+    'Reaction::UI::ViewPort::SiteLayout',
+    layout => 'my_site_layout',
+  );
+
+A new instance of L<Reaction::UI::LayoutSet> will be created using the layout
+file. It is then used to determine the class of widget to create. The widget
+contains the Perl code counterpart of the templating part in the layout file.
+
+The widget is either determined by the C<=widget> template directive in the
+layout file or by the L<Reaction::UI::Skin> object created to represent the
+skin.
+
+The details of skins or layouts are documented in L<Reaction::Manual::Templates>.
+
+=head1 SEE ALSO
+
+=over 
+
+=item * L<Reaction::Manual>
+
+=item * L<Reaction::Manual::Intro>
+
+=back
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/Manual/Tutorial.pod b/lib/Reaction/Manual/Tutorial.pod
new file mode 100644 (file)
index 0000000..4dd23d4
--- /dev/null
@@ -0,0 +1,460 @@
+=head1 NAME
+
+Reaction::Manual::Tutorial.pod - Step by Step Tutorial
+
+=head1 DESCRIPTION
+
+This document aims at giving simple step-by-step leading to an example application
+using the common functionality provided by L<Reaction>.
+
+=head1 CREATING A NEW APPLICATION
+
+At first we have to create a new application. For this we use the C<catalyst.pl>
+script as we would for any other Catalyst application:
+
+  $ catalyst.pl MyApp
+  [lots "created ..." messages]
+
+There is nothing to change in the application class file.
+
+=head1 THE VIEW
+
+Since we are not just rendering templates with Reaction, but layouts and widgets,
+a simple TT view won't suffice. We need to create our own C<lib/MyApp/View/Site.pm>:
+
+  package MyApp::View::Site;
+  use Reaction::Class;
+  
+  use namespace::clean -except => 'meta';
+  
+  extends 'Reaction::UI::View::TT';
+  
+  __PACKAGE__->meta->make_immutable;
+  
+  1;
+
+The C<use Reaction::Class> line will import L<Moose>, L<strict> and L<warnings> into
+our file and might perform some Reaction specific setups.
+
+We make sure that we don't provide imported functions as methods at runtime by using
+L<namespace::clean>. But we need to C<-except> the C<meta> method that was exported
+by Moose.
+
+In its simplest version, our view just needs to do a C<extends 'Reaction::UI::View::TT'>
+to make a new subclass of it.
+
+We chose to call C<make_immutable> on the class' meta class instance to have it inline
+methods for runtime speed improvements.
+
+=head1 THE ROOT CONTROLLER
+
+As usual in Catalyst, our root controller (at C<lib/MyApp/Controller/Root.pm> represents
+the root namespace for our application. For this purpose, it should look like this:
+
+  package MyApp::Controller::Root;
+  use strict;
+  use warnings;
+  use parent 'Reaction::UI::Controller::Root';
+  
+  use aliased 'Reaction::UI::ViewPort';
+  use aliased 'Reaction::UI::ViewPort::SiteLayout';
+  
+  use namespace::clean -except => 'meta';
+  
+  __PACKAGE__->config(
+      view_name       => 'Site',
+      window_title    => 'MyApp Window',
+      namespace       => '',
+  );
+  
+  sub base: Chained('/') PathPart('') CaptureArgs(0) {
+      my ($self, $ctx) = @_;
+      $self->push_viewport(SiteLayout,
+          title           => 'MyApp Test Title',
+          static_base_uri => join('', $ctx->uri_for('/static')),
+      );
+  }
+  
+  sub root: Chained('base') PathPart('') Args(0) {
+      my ($self, $ctx) = @_;
+      $self->push_viewport(ViewPort, layout => 'root');
+  }
+  
+  1;
+
+The effects of L<strict>, L<warnings>, L<parent>, L<aliased> and L<namespace::clean> should
+be clear by now. Let's take a look at the configuration.
+
+The C<view_name> determines which view to use. We set it to C<Site>, which is our only view
+by now. Be careful to set C<view_name> and not C<view>, which would fail telling you it 
+expected an object.
+
+The C<window_title> is the title given to the L<Reaction::UI::Window> instance that will be
+stored in C<$ctx-E<gt>stash-E<gt>{window}> by the C<begin> action provided by
+L<Reaction::UI::Controller::Root>.
+
+The C<namespace> setting anchors the root controller at C</>.
+
+The C<base> action here acts as a general point all other actions can chain off of. It
+pushes the L<Reaction::UI::ViewPort::SiteLayout> viewport onto the 
+L<focus stack|Reaction::UI::FocusStack>. As arguments we see a C<title> that will be used
+as page title later. The C<static_base_uri> is used for static links like CSS and JavaScript
+files. Since we didn't specify a layout C<site_layout> will be used.
+
+We also defined a C<root> action serving as application index. It chains off the C<base>
+action. It is only pushing the root viewport L<Reaction::UI::ViewPort> on the focus stack,
+but this time we specified a layout named C<root>.
+
+Reaction will try to find our layout files in C<share/skin/$skin_name/layout/*>, so the next
+thing to do is to create a new skin and the layout files.
+
+=head1 A NEW SKIN
+
+If your version of Catalyst still creates a C<root> instead of a C<share> directory, you
+might want to rename it. This is regarded as a best practice and follows the conventions
+of this tutorial and other Reaction documentation.
+
+First we need to create a directory for our new skin:
+
+  $ mkdir -p share/skin/myapp/layout
+
+Next we need to configure our new skin. This is done in the C<share/skin/myapp/skin.conf>
+file. At the moment, all it should contain is
+
+  extends /Reaction/default
+
+Note that this C<extends> specification contains the distribution name of the
+library or application of which to use the templates as base. You can also give it
+a relative name like
+
+  extends foo
+
+and it would try to extend a skin named C<foo> in your own application's C<share/skin>
+directory.
+
+Now we create C<share/skin/defaults.conf> to allow settings that concern all skins of
+the application. It should contain only this:
+
+  widget_search_path MyApp::View::Site::Widget
+  widget_search_path Reaction::UI::Widget
+
+This will tell Reaction to look in C<Reaction::UI::Widget::*> and 
+C<MyApp::View::Site::Widget::*> for widget classes. That means that our layout named
+C<root> will check for C<MyApp::View::Site::Widget::Root> first and then look if
+C<Reaction::UI::Widget> exists.
+
+We want the first line to be able to create our own widgets and the second line to
+have Reaction find its own widgets.
+
+Now we need to tell Reaction what skin it should use. We do this by adding this section
+to our C<myapp.conf>:
+
+  <View Site>
+      skin_name myapp
+  </View>
+
+The value should be the name of the target directory under C<share/skin/>.
+
+=head1 LAYOUTS
+
+We will need two layout files to begin with. One controlling the site layout and one
+for the root action.
+
+The first will be created as C<share/skin/myapp/layout/site_layout.tt>:
+
+  =extends NEXT
+
+  =for layout body
+
+      <h1>Welcome to MyApp</h1>
+
+      <div id="content">
+          [% inner %]
+      </div>
+
+  =cut
+
+The C<=extends> directive specifies that this layout file is an extension of another
+layout file. The C<NEXT> value here tells Reaction that this extends the C<site_layout>
+layout in the base skin, which we have defined as C</Reaction/default>. That means, you
+can take a look at the layout we are extending at C<share/skin/default/layout/site_layout.tt>
+in the L<Reaction> distribution.
+
+The C<=for layout> directives allows us to set a layout fragment. We define a C<body> fragment
+containing the common C<body> for all pages using this site layout. The C<[% inner %]> is
+where the deeper parts of the stack will be included, in the case of our C<root> action that
+would be the C<Reaction::UI::ViewPort> with the C<root> layout.
+
+If we wanted to override a specific fragment, we could do just that. And inside that fragment
+we could call C<[% next_call %]> to include the layout fragment from the extended layout.
+
+The layout representing the root action is called C<share/skin/myapp/layout/root.tt>:
+
+  =for layout widget
+
+      <p>Hello, World!</p>
+
+  =cut
+
+This one is rather simple. The C<=for layout widget> directive is special in that the
+C<widget> fragment will always be where the rendering starts. In fact, our C<site_layout>
+layout too contains a C<widget> fragment, you just don't see it because you inherited it from
+your base skin (or your base skin's base skin, for that matter) instead of defining it yourself.
+
+=head1 A SIMPLE WIDGET
+
+If we wanted to use a different kind of widget than that assumed automatically by Reaction, we 
+could add a
+
+  =widget ClassName
+
+directive at the top of the layout file. But for now, we will instead create our own
+widget at C<lib/MyApp/View/Site/Widget/Root.pm>:
+
+  package MyApp::View::Site::Widget::Root;
+  use Reaction::UI::WidgetClass;
+  
+  use namespace::clean -except => 'meta';
+  
+  __PACKAGE__->meta->make_immutable;
+  
+  1;
+
+This adds no new functionality at the moment. It just uses C<Reaction::UI::WidgetClass> to ease
+and automate the setup of a new widget class. The widget can provide functionality and fragments
+to the layout. In a way, it can be seen as the Perl code backend to the layout file.
+
+You can now start your C<script/myapp_server.pl> and visit
+
+  http://localhost:3000/
+
+to view your "Hello, World" page.
+
+=head1 ADDING A SCHEMA
+
+The next part of the tutorial will be about adding data storage to our application. While most
+L<Catalyst> web applications today (or at least they should) abstract their database schema
+with L<DBIx::Class::Schema> into a separate module separated from the webapplication, Reaction
+takes this one step further by introducing so called interface models. The interface model
+defines the layer between your application and your domain model (in this case, the L<DBIx::Class>
+schema).
+
+The first thing we will need is a schema class:
+
+  package MyApp::Schema;
+  use strict;
+  use warnings;
+  
+  use parent 'DBIx::Class::Schema';
+  
+  __PACKAGE__->load_classes;
+  
+  1;
+
+The schema class itself is built like a typical L<DBIx::Class::Schema>. The difference in class
+definition starts at the result classes. For the example's sake, let's make a SQLite database
+called C<example.sqlite>:
+
+  $ cat > example.sqlite.sql
+  CREATE TABLE foo (
+    id          INTEGER PRIMARY KEY AUTOINCREMENT,
+    first_name  VARCHAR NOT NULL,
+    last_name   VARCHAR NOT NULL
+  );
+  <Ctrl-D>
+
+  $ sqlite3 example.sqlite < example.sqlite.sql
+  $
+
+The result class for this table combines the usual style of L<DBIx::Class> with L<Moose> meta
+data additions:
+
+  package MyApp::Schema::Foo;
+  use Moose;
+  use MooseX::Types::Moose  qw( Int );
+  use Reaction::Types::Core qw( NonEmptySimpleStr );
+  
+  use namespace::clean -except => 'meta';
+  
+  extends 'DBIx::Class';
+  
+  has id => 
+      (is => 'ro', isa => Int, required => 1);
+  
+  has first_name => 
+      (is => 'rw', isa => NonEmptySimpleStr, required => 1);
+  
+  has last_name => 
+      (is => 'rw', isa => NonEmptySimpleStr, required => 1);
+  
+  __PACKAGE__->load_components(qw( IntrospectableM2M Core ));
+  __PACKAGE__->table('foo');
+  
+  __PACKAGE__->add_columns(
+      id => { 
+          data_type         => 'integer', 
+          is_auto_increment => 1,
+      },
+      first_name => { data_type => 'varchar' },
+      last_name  => { data_type => 'varchar' },
+  );
+  
+  __PACKAGE__->set_primary_key('id');
+  
+  1;
+
+The L<MooseX::Types::Moose> and L<Reaction::Types::Core> modules export L<Moose> type
+constraints (See also L<MooseX::Types> and L<Moose::Util::TypeConstraints>). Note that
+we are using L<Moose/extends> here instead of L<base> or L<parent> to extend 
+L<DBIx::Class>.
+
+Next we see our columns in form of Moose attribute definitions. The C<is>, C<isa> and
+C<required> attribute parameters will all be used for introspection and interface
+building later. The C<required> is rather straight-forward. The C<is> will decide whether
+this attribute (or column) can be edited (C<ro> means that it can't, C<rw> means it can).
+The C<isa> attribute will be used for validation and rendering of input fields.
+
+The imported C<NonEmptySimpleStr> for example gives us a simple single-line input box,
+while a C<Str> from L<MooseX::Types::Moose> would give us a textbox.
+
+Following that, we have the usual L<DBIx::Class> result class definitions. The only thing
+different might be the new L<DBIx::Class::IntrospectableM2M> which will allow us to
+inspect many-to-many relations later on.
+
+=head1 CREATING AN INTERFACE MODEL
+
+The interface model should be separated from the application and the schema, since it
+will tie both together. In this case, we will use a reflector to set up the usual interface
+model actions for our schema (C<Create>, C<Update>, C<Delete>, C<DeleteAll>):
+
+  package MyApp::InterfaceModel::DBIC;
+  
+  # keep this on top
+  use parent 'Reaction::InterfaceModel::Object';
+  
+  use Reaction::Class;
+  use Reaction::InterfaceModel::Reflector::DBIC;
+  
+  use namespace::clean -except => 'meta';
+  
+  my $reflector = Reaction::InterfaceModel::Reflector::DBIC->new;
+  
+  $reflector->reflect_schema(
+      model_class     => __PACKAGE__,
+      schema_class    => 'MyApp::Schema',
+  );
+  
+  __PACKAGE__->meta->make_immutable;
+  
+  1;
+
+The L<parent> import must happen before the L<Reaction::Class> one in this case. Other
+than that, the only thing we do here is create a new L<Reaction::InterfaceModel::Reflector::DBIC>
+and call C<reflect_schema> to build our C<MyApp::InterfaceModel::DBIC::*> namespace out of our
+C<MyApp::Schema>.
+
+=head1 TIEING THE INTERFACE MODEL TO THE APPLICATION
+
+Next on the list is the integration of our new interface model into our application. For
+this we create a simple catalyst model in C<lib/MyApp/Model/DBIC.pm>:
+
+  package MyApp::Model::DBIC;
+  use Reaction::Class;
+  
+  use namespace::clean -except => 'meta';
+  
+  extends 'Catalyst::Model::Reaction::InterfaceModel::DBIC';
+  
+  __PACKAGE__->meta->make_immutable;
+  
+  __PACKAGE__->config(
+      im_class    => 'MyApp::InterfaceModel::DBIC',
+      db_dsn      => 'dbi:SQLite:example.sqlite',
+  );
+  
+  1;
+
+This model L<extends|Moose> the L<Catalyst::Model::Reaction::InterfaceModel::DBIC> base class
+shipped with Reaction. It's configuration must contain the C<im_class> naming our interface
+model and the C<db_dsn>. Of course, since this is Catalyst, this can also easily be specified 
+via your application config file under the C<Model::DBIC> key.
+
+=head1 BUILDING A SIMPLE CRUD CONTROLLER
+
+Now, since we have defined our interface model as well as our domain model including meta
+data, it isn't very hard (at least not for us) to build a basic (but extendable) CRUD controller:
+
+  package MyApp::Controller::Foo;
+  use strict;
+  use warnings;
+  
+  use parent 'Reaction::UI::Controller::Collection::CRUD';
+  use Reaction::Class;
+  
+  use namespace::clean -except => 'meta';
+  
+  __PACKAGE__->config(
+      model_name      => 'DBIC',
+      collection_name => 'Foo',
+      actions => {
+          base => { Chained => '/base', PathPart => 'foo' },
+      },
+  );
+  
+  1;
+
+This controller subclasses L<Reaction::UI::Controller::Collection::CRUD>, which is itself a
+subclass of L<Reaction::UI::Controller::Collection>, a class to ease the creation of controllers
+who act on collections of things.
+
+As you can see, for the simplest case we don't need any code; we simply configure our controller.
+
+The C<model_name> is the name of our interface model sans the C<MyApp::Model::> prefix. This means
+this entry points to C<MyApp::Model::DBIC> in this case. The C<collection_name> is the name of
+the collection in the specified interface model. For us, this would be C<Foo>, like the result
+class we created above and want to manage.
+
+The C<actions> part of the configuration is not Reaction, but rather Catalyst specific. This
+configures the actions inherited from L<Reaction::UI::Controller::Collection::CRUD>. For it to
+work, we only need to tell the C<base> action where to chain off from, and what C<PathPart>
+to use. We chain it to the C<base> action in our root controller. The C<foo> path part is rather
+obvious.
+
+Now you can restart your application and visit
+
+  http://localhost:3000/foo
+
+and you have a complete CRUD interface for C<Foo> with listing, viewing, creating, updating
+and deleting capabilities.
+
+=head1 SEE ALSO
+
+=over 
+
+=item * L<Reaction::Manual>
+
+=item * L<Reaction::Manual::Intro>
+
+=item * L<Reaction::Manual::Overview>
+
+=item * L<Reaction::Manual::Templates>
+
+=item * L<Reaction::Manual::RenderPage>
+
+=item * L<Reaction::UI::View::TT>
+
+=item * L<Reaction::UI::Controller::Root>
+
+=item * L<Reaction::UI::WidgetClass>
+
+=back
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
index 45a85d8..6788b40 100644 (file)
@@ -93,7 +93,26 @@ __END__;
 
 =head1 NAME
 
-Reaction::UI::Controller
+Reaction::UI::Controller - Reaction Base Controller Class
+
+=head1 SYNOPSIS
+
+  package MyApp::Controller::Foo;
+  use strict;
+  use warnings;
+  use parent 'Reaction::UI::Controller';
+
+  use aliased 'Reaction::UI::ViewPort';
+
+  sub foo: Chained('/base') Args(0) {
+    my ($self, $ctx) = @_;
+
+    $ctx->push_viewport(ViewPort,
+      layout => 'foo',
+    );
+  }
+
+  1;
 
 =head1 DESCRIPTION
 
index b2af674..f883b92 100644 (file)
@@ -68,10 +68,16 @@ __END__;
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Action
+Reaction::UI::ViewPort::Action - Provide user with a form with OK, Apply and Close.
 
 =head1 SYNOPSIS
 
+  $controller->push_viewport('Reaction::UI::ViewPort::Action',
+    model           => $interface_model_action,
+    field_order     => [qw( firstname lastname )],
+    excluded_fields => [qw( password )],
+  );
+
 =head1 DESCRIPTION
 
 This subclass of L<Reaction::UI::ViewPort::Object::Mutable> is used for 
@@ -79,6 +85,15 @@ rendering a complete form supporting Apply, Close and OK.
 
 =head1 ATTRIBUTES
 
+=head2 message
+
+=head2 model
+
+Inherited from L<Reaction::UI::ViewPort::Object::Mutable>. Must be a
+L<Reaction::InterfaceModel::Action>.
+
+Also handles C<error_message> and C<has_error_message> methods.
+
 =head2 method
 
 post / get
@@ -91,10 +106,41 @@ Returns true if a field has been edited.
 
 =head2 can_apply
 
+Returns true if no field C<needs_sync> and the L</model> C<can_apply>.
+
 =head2 do_apply
 
+Delegates to C<do_apply> on the L</model>, which is a 
+L<Reaction::InterfaceModel::Action>.
+
 =head2 sync_action_from_fields
 
+Firstly calls C<sync_to_action> on every L<Reaction::UI::ViewPort::Field::Mutable>
+in L<fields|Reaction::UI::ViewPort::Object/fields>. Then it calls C<sync_all> on
+the L<Reaction::InterfaceModel::Action> in L</model>. Next it will call
+C<sync_from_action> on every field to repopulate them from the L</model>.
+
+=head1 SUBCLASSING
+
+  package MyApp::UI::ViewPort::Action;
+  use Reaction::Class;
+  use MooseX::Types::Moose qw( Int );
+
+  use namespace::clean -except => 'meta';
+
+  extends 'Reaction::UI::ViewPort::Action';
+
+  has render_timestamp => (
+    is       => 'ro',
+    isa      => Int,
+    default  => sub { time },
+    required => 1,
+  );
+
+  has '+field_order' => (default => sub {[qw( firstname lastname )]});
+
+  1;
+
 =head1 SEE ALSO
 
 L<Reaction::UI::ViewPort>
index e65b9e7..23d5b97 100644 (file)
@@ -32,27 +32,60 @@ __END__
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Action::Role::Apply
+Reaction::UI::ViewPort::Action::Role::Apply - Integrate an Apply event into the ViewPort
+
+=head1 SYNOPSIS
+
+  package MyApp::UI::ViewPort::SomeAction;
+  use Reaction::Class;
+
+  use namespace::clean -except => 'meta';
+
+  extends 'Reaction::UI::ViewPort::Object::Mutable';
+  with    'Reaction::UI::ViewPort::Action::Role::Apply';
+
+  ...
+  1;
+
+=head1 DESCRIPTION
+
+This role integrates an C<apply> event into the consuming viewport that will call the
+required L</do_apply> role.
+
+=head1 REQUIRED METHODS
+
+=head2 do_apply
+
+Will be called when an L</apply> event comes in.
 
 =head1 ATTRIBUTES
 
 =head2 apply_label
 
-Default: 'apply'
+Defaults to 'apply', returned by L</_build_apply_label>.
 
 =head2 on_apply_callback
 
-CodeRef.
+CodeRef. Will be called after L</apply> if L</can_apply> and L</do_apply> return
+true. See L</apply> for argument details.
 
 =head1 METHODS
 
 =head2 can_apply
 
+Returns true by default. Determines if L</do_apply> can be called.
+
 =head2 apply
 
 Calls a user-supplied C<do_apply> and if it is successful runs the
 C<on_apply_callback> passing C<$self> and the result of C<do_apply> as args.
 
+=head1 INTERNAL METHODS
+
+=head2 _build_apply_label
+
+Defaults to C<apply>.
+
 =head1 SEE ALSO
 
 L<Reaction::UI::ViewPort::Action::Role::Close>
index f236a9d..fec6f91 100644 (file)
@@ -42,34 +42,71 @@ __END__
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Action::Role::Close
+Reaction::UI::ViewPort::Action::Role::Close - Integrate Close and Apply events into ViewPort
+
+=head1 SYNOPSIS
+
+  package MyApp::UI::ViewPort::SomeAction;
+  use Reaction::Class;
+
+  use namespace::clean -except => 'meta';
+
+  extends 'Reaction::UI::ViewPort::Object::Mutable';
+  with    'Reaction::UI::ViewPort::Action::Role::Close';
+
+  ...
+  1;
+
+=head1 DESCRIPTION
+
+This role integrates a C<close> event and inherits an
+L<apply|Reaction::UI::ViewPort::Action::Role::Close/apply>
+event into the consuming viewport.
 
 =head1 ATTRIBUTES
 
 =head2 close_label
 
-Default: C<close_label_close>
+Defaults to returned string value of L</_build_close_label> (C<close>).
 
 =head2 close_label_close
 
-Default: 'close'
+Defaults to returned string value of L</_build_close_label_close> (C<close>).
 
 =head2 close_label_cancel
 
-This label is only shown when C<changed> is true.
+This label is only shown when C<changed> is true. It is initialised
+with the returned string value of L</_build_close_label_cancel>.
 
 Default: 'cancel'
 
 =head2 on_close_callback
 
-CodeRef.
+CodeRef. If set will be called on L</close>.
 
 =head1 METHODS
 
 =head2 close
 
+Calls L</on_close_callback> if one is set.
+
 =head2 can_close
 
+Returns true.
+
+=head2 apply
+
+Extends L<Reaction::UI::ViewPort::Action::Role::Apply/apply> and sets
+the L</close_label> to L</close_label_cancel> if the original call to
+C<apply> was not successfull.
+
+Returns the result of the original C<apply> call.
+
+=head2 accept_events
+
+Extends L<Reaction::UI::ViewPort::Action::Role::Apply/accept_events>
+with the C<close> event if an L</on_close_callback> was provided.
+
 =head1 SEE ALSO
 
 L<Reaction::UI::ViewPort::Action::Role::Apply>
index 38001f8..06fe0c7 100644 (file)
@@ -25,13 +25,33 @@ __END__
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Action::Role::Close
+Reaction::UI::ViewPort::Action::Role::OK - Integrate OK, Apply and Close events
+
+=head1 SYNOPSIS
+
+  package MyApp::UI::ViewPort::SomeAction;
+  use Reaction::Class;
+
+  use namespace::clean -except => 'meta';
+
+  extends 'Reaction::UI::ViewPort::Object::Mutable';
+  with    'Reaction::UI::ViewPort::Action::Role::OK';
+
+  ...
+  1;
+
+=head1 DESCRIPTION
+
+This role integrates an C<ok> event and inherits a 
+L<close|Reaction::UI::ViewPort::Action::Role::Close/close>
+and an L<apply|Reaction::UI::ViewPort::Action::Role::Apply/apply>
+event into the consuming viewport.
 
 =head1 ATTRIBUTES
 
 =head2 ok_label
 
-Default: 'ok'
+Defaults to C<ok>. String is built by L</_build_ok_label>.
 
 =head1 METHODS
 
@@ -39,6 +59,18 @@ Default: 'ok'
 
 Calls C<apply>, and then C<close> if successful.
 
+=head2 accept_events
+
+Extends L<Reaction::UI::ViewPort::Action::Role::Close/accept_events> with the
+event C<ok> if an L<on_close_callback|Reaction::UI::ViewPort::Action::Role::Close/on_close_callback>
+was provided.
+
+=head1 INTERNAL METHODS
+
+=head2 _build_ok_label
+
+Returns the string representing the label for the OK action. Defaults to C<ok>.
+
 =head1 SEE ALSO
 
 L<Reaction::UI::ViewPort::Action::Role::Apply>
index ed68f20..9c96237 100644 (file)
@@ -247,26 +247,66 @@ __END__;
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Object
+Reaction::UI::ViewPort::Object - Display an InterfaceModel::Object
+
+=head1 SYNOPSIS
+
+  use aliased 'Reaction::UI::ViewPort::Object';
+
+  ...
+  $controller->push_viewport(Object,
+    model           => $person_interface_model_object,
+    fields_order    => [qw( firstname lastname )],
+    excluded_fields => [qw( password )],
+  );
 
 =head1 DESCRIPTION
 
+Takes a L<Reaction::InterfaceModel::Object> class and displays the
+configured fields.
+
 =head1 ATTRIBUTES
 
 =head2 model
 
+Required L<Reaction::InterfaceModel::Object>.
+
 =head2 fields
 
+Initialised via L</_build_fields>
+
 =head2 field_args
 
+Hash reference keyed by field names. Values are hash references containing
+arguments to the field builder method of the attribute.
+
 =head2 field_order
 
+Array reference of strings defining the order of all fields (including
+the ones that might be excluded).
+
 =head2 builder_cache
 
+Hash reference containing resolved builder method names per field. Utilised
+by L</_build_fields>
+
 =head2 excluded_fields
 
+Array reference of strings naming fields to exclude from the interface.
+
 =head2 computed_field_order
 
+Array reference of strings Initialised by the L</_computed_field_order> method.
+Contains the fields to show in the correct order.
+
+=head2 containers
+
+Array reference populated by L</_build_containers>.
+
+=head2 container_layouts
+
+Array reference containing container layout specifications.
+
 =head1 INTERNAL METHODS
 
 These methods, although stable, are subject to change without notice. These are meant
@@ -275,10 +315,61 @@ avoid potential breakages.
 
 =head2 BUILD
 
+Takes the value of the C<Field> constructor argument, if true, and sets it as
+the new L</field_args> hash reference.
+
 =head2 get_builder_for
 
+Takes an attribute object as argument and returns a string containing
+the name of the method that builds the fields for this attribute.
+
+If the viewport implements it, C<_build_fields_for_name_${attr_name}> will be used.
+
+If that is not available, it will take the C<isa> information of the type constraint
+and see if it is a loaded class implementing C<meta>. If it is, every class in its
+C<class_precedence_list> will be taken and used to try to find a 
+C<_build_fields_for_type_${mangled_class_name}> method on the viewport.
+
+"mangled" means here that every C<:*> will be replaced with C<_>. For example:
+C<Foo::Bar> would become C<Foo_Bar>.
+
+If the C<isa> information was not obtainable or no fitting method was found, it will
+try the type name in a method named C<_build_fields_for_type_${mangled_type_name}>.
+
+If could be found on this constraint, it will make the same attempts to find a
+method on its parent type constraint.
+
+This method will die if it can't locate a method to build a field for this
+attribute.
+
+=head2 _build_containers
+
+Uses L</container_layouts> to build a list of L<Reaction::UI::ViewPort::Field::Container>
+objects.
+
+=head2 _build_fields
+
+Takes the L</model>s C<parameter_attributes> to build fields via L</get_builder_for>.
+They will be ordered as specified in L</computed_field_order>.
+
+=head2 _build_computed_field_order
+
+Takes the names of the L</model>s C<parameter_attributes>' reader methods and assumes
+them as field names. Then it uses L</field_order> and L</excluded_fields> to calculate
+the order of all included fields and returns those names.
+
 =head2 _build_simple_field
 
+  $self->_build_simple_field(
+    attribute => $attribute_object,
+    class     => $field_class,
+    %field_attrs,
+  );
+
+Takes an attribute meta object, a field class (a L<Reaction::UI::ViewPort::Field> subclass)
+and an additional set of arguments to pass to the field constructor and returns the new
+field. Field classes themselves are L<Reaction::UI::ViewPort> subclasses.
+
 =head2 _build_fields_for_type_Num
 
 =head2 _build_fields_for_type_Int
@@ -301,6 +392,20 @@ avoid potential breakages.
 
 =head2 _build_fields_for_type_Reaction_InterfaceModel_Collection
 
+=head1 FIELD TYPES
+
+L<Text|Reaction::UI::ViewPort::Field::Text>,
+L<Number|Reaction::UI::ViewPort::Field::Number>,
+L<Integer|Reaction::UI::ViewPort::Field::Integer>,
+L<Boolean|Reaction::UI::ViewPort::Field::Boolean>,
+L<String|Reaction::UI::ViewPort::Field::String>,
+L<DateTime|Reaction::UI::ViewPort::Field::DateTime>,
+L<RelatedObject|Reaction::UI::ViewPort::Field::RelatedObject>,
+L<Array|Reaction::UI::ViewPort::Field::Array>,
+L<Collection|Reaction::UI::ViewPort::Field::Collection>,
+L<File|Reaction::UI::ViewPort::Field::File>,
+L<Container|Reaction::UI::ViewPort::Field::Container>
+
 =head1 AUTHORS
 
 See L<Reaction::Class> for authors.
index e58f1b4..66d6fa9 100644 (file)
@@ -111,10 +111,17 @@ __END__;
 
 =head1 NAME
 
-Reaction::UI::ViewPort::Object::Mutable
+Reaction::UI::ViewPort::Object::Mutable - Allow the user to to perform an InterfaceModel Action
 
 =head1 SYNOPSIS
 
+  use aliased 'Reaction::UI::ViewPort::Object::Mutable';
+
+  ...
+  $controller->push_viewport(Mutable,
+    model => $interface_model_action,
+  );
+
 =head1 DESCRIPTION
 
 This subclass of L<Reaction::UI::ViewPort::Object> is used for rendering a
@@ -128,6 +135,30 @@ L<Reaction::InterfaceModel::Action>
 
 =head1 METHODS
 
+=head2 is_modified
+
+Returns true if any of the L<fields|Reaction::UI::ViewPort::Object/fields> has been
+modified.
+
+=head1 INTERNAL METHODS
+
+The builder methods are resolved in the same way as described in L<Reaction::UI::ViewPort::Object>,
+but they create L<Reaction::UI::ViewPort::Field::Mutable> objects.
+
+=head2 Mutable Field Types
+
+L<Text|Reaction::UI::ViewPort::Field::Mutable::Text>,
+L<Array|Reaction::UI::ViewPort::Field::Mutable::Array>,
+L<String|Reaction::UI::ViewPort::Field::Mutable::String>,
+L<Number|Reaction::UI::ViewPort::Field::Mutable::Number>,
+L<Integer|Reaction::UI::ViewPort::Field::Mutable::Integer>,
+L<Boolean|Reaction::UI::ViewPort::Field::Mutable::Boolean>,
+L<Password|Reaction::UI::ViewPort::Field::Mutable::Password>,
+L<DateTime|Reaction::UI::ViewPort::Field::Mutable::DateTime>,
+L<ChooseOne|Reaction::UI::ViewPort::Field::Mutable::ChooseOne>,
+L<ChooseMany|Reaction::UI::ViewPort::Field::Mutable::ChooseMany>,
+L<Files|Reaction::UI::ViewPort::Field::Mutable::File>
+
 =head2  _build_fields_for_type_Num
 
 =head2  _build_fields_for_type_Int