App-MFILE-WWW

 view release on metacpan or  search on metacpan

lib/App/MFILE/WWW.pm  view on Meta::CPAN

various widgets are used.



=head1 QUICK START (DEMO)

L<App::MFILE::WWW> can be run as a standalone "front-end web application" written
in JavaScript with an embedded HTTP server.

Assuming L<App::MFILE::WWW> has been installed properly, this mode of operation
can be started by running C<mfile-www>, as a normal user (even 'nobody'), with
no arguments or options:

    $ mfile-www

The start-up script will write information about its state to the standard
output. This includes the location of its log file, the port where the HTTP
server is listening (default is 5001), etc. For a detailed description of what
happens when the start-up script is run, see the POD of C<mfile-www> itself
- e.g. "man mfile-www".



=head1 DERIVED WWW CLIENTS

When you write your own web frontend using this distro, from
L<App::MFILE::WWW>'s perspective it is a "derived client" and will be referred
to as such in this document.


=head2 Derived client operation

In a derived-client scenario, L<App::MFILE::WWW> serves as the foundation
upon which the "real" application is built.

The derived-client handling is triggered by providing the C<--ddist>
command-line option, i.e.

    $ mfile-www --ddist=App-Dochazka-WWW

where 'App-Dochazka-WWW' refers to the Perl module L<App::Dochazka::WWW>,
which is assumed to contain the derived client source code.

So, in the first place it is necessary to create such a Perl module. It should
have a sharedir configured and present. One such derived client,
L<App::Dochazka::WWW>, is available on CPAN.



=head1 PERL AND JAVASCRIPT

The L<App::MFILE::WWW> codebase has two parts, or "sides": the "Perl side"
and the "JavaScript side". The Perl side implements the embedded web server
and the JavaScript side implements the front-end application served to
browsers by the Perl side.

Control passes from the Perl side to the JavaScript side

=over

=item * B<synchronously> whenever the user (re)loads the page

=item * B<asynchronously> whenever the user triggers an AJAX call

=back

=head2 Perl side

The HTTP request-response cycle implemented by the Perl side is designed to
work approximately like this:

=over

=item * B<nginx> listens for incoming connections on port 80/443 of the server

=item * When a connection comes in, B<nginx> decrypts it and forwards it to a
high-numbered port where a PSGI-compatible HTTP server (such as L<Starman>) is
listening

=item * The embedded HTTP server takes the connection and passes it to the
Plack middleware.  The key middleware component is
L<Plack::Middleware::Session>, which assigns an ID to the session, stores
whatever data the server-side code needs to associate with the session, links
the session to the user's browser via a cookie, and provides the application a
hook (in the Plack environment stored in the HTTP request) to access the
session data

=item * if the connection is asking for static content (defined as anything in
C<images/>, C<css/>, or C<js/>), that content is served immediately and the
request doesn't even make it into our Perl code

=item * any other path is considered dynamic content and is passed to
L<Web::Machine> for processing -- L<Web::Machine> implements the HTTP standard
as a state machine

=item * the L<Web::Machine> state machine takes the incoming request and runs
it through several functions that are overlayed in L<App::MFILE::WWW::Resource>
- an appropriate HTTP error code is returned if the request doesn't make it
through the state machine. Along the way, log messages are written to the log.

=item * as part of the state machine, all incoming requests are subject to
"authorization" (in the HTTP sense, which actually means authentication).
First, the session data is examined to determine if the request belongs to an
existing authorized session. If it doesn't, the request is treated as a
login/logout attempt -- the session is cleared and control passes to the 
JavaScript side, which, lacking a currentUser object, displays the login
dialog.

=item * once an authorized session is established, there are two types of
requests: GET and POST

=item * incoming GET requests happen whenever the page is reloaded -
in an authorized session, this causes the main menu to be displayed, but all
static content (CSS and JavaScript modules) are reloaded for a "clean slate",
as if the user had just logged in.

=item * Note that L<App::MFILE::WWW> pays no attention to the URI - if the user
enters a path (e.g. http://mfile.site/some/bogus/path), this will be treated
like any other page (re)load and the path is simply ignored.

=item * if the session is expired or invalid, any incoming GET request will
cause the login dialog to be displayed.

=item * well-formed POST requests are directed to the C<process_post> routine
in L<App::MFILE::WWW::Dispatch>. In derived-distro mode, the derived distro
must provide its own dispatch module.

=item * under ordinary operation, the user will spend 99% of her time
interacting with the JavaScript code running in her browser, which will
communicate asynchronously as needed with the back-end (which must be
implemented separately) via AJAX calls.

=back


=head2 JavaScript side

The JavaScript side provides a toolkit for building web applications that

=over

=item do not require the use of a mouse; and 

=item look and feel very much like text terminal applications from the 1980s

=back

Developing a front-end application with L<App::MFILE::WWW> currently assumes
that you, the developer, will want to use RequireJS, jQuery, and QUnit.

The JavaScript code is modular. Each code module has its own file and
modules are loaded asynchronously by L<RequireJS|http://requirejs.org/>.
Also, jQuery and QUnit L<http://qunitjs.com/> are loaded automatically.

In addition to the above, L<App::MFILE::WWW> provides a number of primitives,
also referred to as "targets", that can be used to quickly slap together a web
application. The next chapter explains what these widgets are and how to use
them.



=head1 FRONT-END PRIMITIVES

The JavaScript side implements a set of primitives, or widgets, from which the
front-end application is built up. These include a menu primitive, a form
primitive for entering data, table and "browser" primitives for viewing
datasets, and a "rowselect" primitive for selecting among 

=head2 daction

The C<daction> primitive is a generalized widget that can do anything. 


=head2 dbrowser

The C<dbrowser> primitive is like C<dform>, except that it displays a set
of data objects and enables the user to "browse" the dataset using arrow keys.
Like C<dform>, the primitive includes "miniMenu" functionality through which
the user can potentially trigger actions that take the current object as input.


=head2 dcallback

The C<dcallback> primitive is useful for cases when none of the other primitives
are appropriate for displaying a given type of object, and no interactivity is
needed beyond that provided by miniMenu. The C<dcallback> primitive writes the
target title and miniMenu to the screen, along with a "dcallback" div in
between, which it populates by calling the callback function. Since the callback
function part of the target definition, it can be app-specific.


=head2 dform

The C<dform> primitive is used to implement forms consisting of read-only
fields (for viewing data), read-write fields (for entering data), or a
combination of both. A "miniMenu" can be defined, allowing the user to trigger
actions that take the current object as input.


=head2 dmenu

The C<dmenu> primitive is used to implement menus.


=head2 dnotice

The C<dnotice> primitive takes an HTML string and displays it. The same
functionality can be accomplished with a C<daction>, of course, but using the
C<dnotice> primitive ensures that the notice will have the same "look and feel"
as the other widgets.




( run in 1.619 second using v1.01-cache-2.11-cpan-39bf76dae61 )