Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

A high-level light-weight web framework with many ideas from Seaside.

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 benchmark
Octocat-spinner-32 doc
Octocat-spinner-32 examples
Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 INSTALL
Octocat-spinner-32 README
Octocat-spinner-32 Rakefile
Octocat-spinner-32 TODO
Octocat-spinner-32 wee.gemspec
README
= Wee Web Framework

== Copyright and License

Copyright (c) 2004, 2005 by Michael Neumann (mneumann@ntecs.de).

Released under the same terms of license as Ruby. Some files under directory
<tt>examples/</tt> might be copyrighted by third parties and licensed under
different terms.

== Status and Bugs

Wee is not considered production ready! It should only be used for small
things. 

Known Bugs:

* Continuations leak memory!

== Introduction

Wee is a light-weight, very high-level and modern web-framework that makes
<b>W</b>eb <b>e</b>ngineering <b>e</b>asy. It mainly inherits many ideas and
features from Seaside2[http://beta4.com/seaside2], but was written from scratch
without ever looking at the Seaside (or any other) sources. All code was
developed from ideas and lots of discussions with Avi Bryant. 

== Features

=== Reusable components

Wee has _real_ components, which are like widgets in a GUI. Once written, you
can use them everywhere. They are completely independent and do not interfere
with other components. Components encapsulate state, a view and actions. Of
course you can use an external model or use templates for rendering.

=== Backtracking

See the <i>What is backtracking?</i> section below. In short, backtracking lets
the browser's back and forward-button play well together with your application.

=== Clean and concise

Wee is well thought out, is written in *and* supports clean and concise code.
Furthermore I think most parts are now very well documented.

=== Abstract core

The core of Wee is completely independent of both HTTP and HTML. That means,
with little effort, you should be able to render other formats than HTML and
use other communication protocols like SOAP, XML-RPC, Email, GUI or Console.

=== Templating-independent 

Wee does not depend on a special templating-engine. You can use a different
templating engine for each component if you want. 

=== Powerful programmatic HTML generation 

Wee ships with an easy to use and very powerful programmatic html-generation
library. For example you can create a select list easily with this piece of
code:

   # select an object from these items
   items = [1, 2, 3, 4]

   # the labels shown to the user
   labels = items.map {|i| i.to_s}

   # render it
   r.select_list(items).labels(labels).callback {|choosen| p choosen}

   # render a multi-select list, with objects 2 and 4 selected
   r.select_list(items).multi.labels(labels).selected([2,4])

The callback is called with the selected objects from the _items_ array.  Items
can be any object, e.g. whole components:

   labels = ["msg1", "msg2"]
   items = labels.collect {|m| MessageBox.new(m)}
   r.select_list(items).labels(labels).callback {|choosen| call choosen.first} 

=== Continuation based (Optional)

<b>IMPORTANT:</b> <i>The current implementation of continuations in Ruby might lead
to memory leaks, if you use continuations. Furthermore, if you use
continuations, it's no longer possible to store the session state to disk.</i>

Continuation-based frameworks are also known as _modal_ frameworks. You can
write code like the one shown below (taken from one of Avi's emails) where each
method displays a new page:

  def checkout
    billing = getAddress("Billing Address")
    if useSeparateShippingAddress()
      shipping = getAddress("Shipping Address")
    else
      shipping = billing
    end
    payment = getPaymentInfo()
    showConfirmation(billing, shipping, payment)
  end 

Try to implement the same in a non-continuation based framework and show me
your code... ;-)

=== Fully Marshallable (drops Continuation support)

If you don't need continuations, Wee can be made fully marshallable. It is not
yet fully marshallable (it was in the past), but it can be made, if required. 

== Observations and Limitations

* Using continuations (might) lead to memory leaks! 

* When using continuations for cross-component calls, it's impossible to
  store a session to disk (at least in Ruby). This problem is partly addressed
  by checkpointing provided by operating systems like
  DragonFly[www.dragonflybsd.org]. But with this approach it's still impossible
  to store sessions _selectively_ to disk. 

* Each session runs in it's own (light-weigth) thread (this is a neccessity
  when using continuations).

* Only one action callback can be invoked per request (a former version of Wee
  was able to invoke multiple callback, and answer even from multiple
  components at the same time, but this was removed, due to the unreliability of
  continuations in Ruby).  

* Components are thread-safe, as a fresh components-tree is created for each
  session and requests inside a session are serialized.

== What is backtracking?

If you want, you can make the back-button of your browser work correctly
together with your web-application. Imagine you have a simple counter
application, which shows the current count and two links _inc_ and _dec_ with
which you can increase or decrease the current count. Starting with an inital
count of 0, you increase the counter up to 8, then click three times the back
button of your browser (now displays 5) and finally decrease by one, then the
counter really shows the expected 4, instead of 7 (as clicking the back button
does usually not send a HTTP request, and the last state of your application
was 8).

Only individual objects are backtracked, those of which you explicitly take a
snapshot, not the whole component. That's the easiest (from an application
programmers perspective) and most flexible way. And its fast and uses less
memory.

You can decide yourself whether you want infinite backtracking or only
backtracking up to n pages, with whatever replacement strategy you want, least
recently used (LRU), least frequently used (LFU) etc. 

== Decorations

Decorations are used to modify the look and behaviour of a component, without
modifying the components tree. A component can have more than one decoration.
This is implemented as a linked list of decorations (Wee::Decoration#owner
points to the next decoration). Wee::Component#decoration points to the first
decoration in the chain or to the component itself, if no decorations were
specified. We actually use a Wee::ValueHolder for the <tt>@decoration</tt>
instance variable of class Wee::Component to be able to easily backtrack it
(required if you want to "undo" component calls).

== The anatomy of a request/response cycle

The request/response cycle in Wee is actually split into two separate phases
or steps. Depending on the point-of-view (given that a page is rendered and
the user clicks on a link or button), the first phase is to invoke an action
(a "callback"). Then in the second phase, a new page is rendered and sent
back to the user. So the two steps are:

1. invoke callbacks (action phase)

2. render new page and display (render phase)

These two phases repeat permanently. Which tasks are performed in each of them, is briefly listed below:

<b>Action:</b>

1. restore snapshot (if not up-to-date)

2. invoke actions   

3. backtrack state

4. update url -> redirect to render phase (not yet)

<b>Render:</b>

1. restore snapshot (if not up-to-date)

2. render

For each session there is at most one request handled at the same time. That
means, that there is either one action request or one render request handled.
Why? Because we have only one components tree, which we update on action
requests. As Wee allows to go back in time, we have to restore this components
tree to a certain point in time before we can handle an action or render
request. This disallows to handle e.g. two render requests simultaneous.

=== Action Phase (Invoking Callbacks)

Possible sources for callbacks are links (anchors) and all kinds of
form-elements like submit buttons, input-fields etc. There are two different
kinds of callbacks:

* Input callbacks (input-fields)

* Action callbacks (anchor, submit-button)

The distinction between input and action callbacks is important, as action
callbacks might depend on values of input-fields being assigned to instance
variables of the controlling component. Hence, Wee first invokes all input
callbacks before any action callback is triggered.

There are two methods related to callback processing:

* Wee::Component#process_callbacks_chain

* Wee::Presenter#process_callbacks

Note that each Wee::Component is also a Wee::Presenter, whereas a
Wee::Decoration is not a Component (but a Presenter)! 

Method <i>process_callbacks_chain</i> invokes <i>process_callbacks</i> for
it's first decoration, or if the component has no decorations, the method is
called for the component itself.  As such, <i>process_callbacks_chain</i>
is important to avoid entering an infinite loop (a method calling itself). What
decorations are, is discussed elsewhere.

Method <i>process_callbacks</i> of class Component first invokes all input
callbacks specified for this component, then calls
<i>process_callbacks_chain</i> for all of it's child components. This ensures,
that all input callbacks are triggered before the first action callback is
run. Finally, it invokes all of it's action callbacks.

=== Rendering Phase

The rendering phase is assumed to be side-effect free! So, you as a
programmer should take care to meet this assumption.

Similar as in the callback-step, there are two methods related to rendering a
page:

* Wee::Component#do_render_chain

* Wee::Presenter#do_render

Method <i>Component#do_render_chain</i> starts rendering the decoration chain by
calling <i>Presenter#do_render</i> for the first decoration of the component or
for the component itself if no decorations were specified.  Method
<i>Presenter#do_render</i> then generates the desired output which gets sent to
the user. Note that method <i>do_render</i> might call other components'
<i>do_render_chain</i> methods to display those components "inside" itself
(usually a component does this for it's child components, but this has to be
implemented by the programmer). 

=== Further Reads

In this order:

* Wee::Presenter
* Wee::Component
* Wee::Decoration
* Wee::Delegate
* Wee::AnswerDecoration
Something went wrong with that request. Please try again.