Links
Tags
apache
armenia
books
bsd
c
c++
chips
cinema
concurrency
cooking
database
dragonfly
erlang
filesystem
freebsd
fun
hardware
java
javascript
json
languages
linux
lyric
mac_osx
mail
math
misc
music
personal
poems
presentation
programming
python
references
ruby
rubyjs
scm
software
spiking_neural_net
study
sysadm
sysarch
technology
testing
travel
virtualization
web
wee
windows
Around 4 years have passed since the last release of Wee.
What is Wee?
Wee is a Seaside-inspired 1 web-framework for building highly-dynamic component-based web-applications.
What’s new?
- 100% Rack based
- Support for continuations
- JQuery AJAX support
- Ruby 1.9 ready
Distinctive Features
Wee is not just another web framework. It is completely different.
Continuations
You can use continuations to model the page flow of the your application, in the same way as you’d call a method in a GUI application. Continuations are optional; nothing in Wee depends on them. Example:
# you can write code like this:
if callcc YesNoMessageBox.new('Really?')
# do something
else
# something else
end
Without continuations one has to use (ugly) Continuation Passing Style (CPS):
call YesNoMessageBox.new('Really?') do |res|
if res
# do something
else
# something else
end
end
Backtracking
Backtracking means that the user can naturally use the browsers back button despite Wee’s statefulness. This is not taken for granted for stateful web frameworks. Behind the scenes, Wee keeps multiple states of the application around with only little help by the programmer.
Components
Contrary to the widely used model/view/controller (MVC) paradigm, Wee tighly couples the controller and the view within a component. Components itself are highly decoupled from the rest of the application and can be reused to construct more complex applications. The powerful programmatic HTML rendering approach further reduces wasting the programmers mind by avoiding switching files (controller/view) or languages (Ruby/HTML). Therefore in Wee everything is written in Ruby. The programmatic HTML renderer is not just a simple HTML builder, it provides very powerful methods for easily generating HTML constructs and registering callbacks. Example:
#
# Generating a <select> tag
#
# 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])
Call/Answer mechanism
From callback handlers you can call other components which in turn replace the current components view. The called component can later return (or answer) back to the calling componenent. Example:
class YesNoMessageBox < Wee::Component
def initialize(msg)
super()
@msg = msg
end
def render(r)
r.bold(@msg)
r.form do
r.submit_button.value('YES').callback { answer true }
r.space
r.submit_button.value('No').callback { answer false }
end
end
end
# Use call (or callcc) as in the Continuation section above
# to call a component:
call YesNoMessageBox.new('Really?')
The classical Hello World Example
require 'rubygems' if RUBY_VERSION < "1.9"
require 'wee'
class HelloWorld < Wee::Component
def initialize
super
add_decoration Wee::PageDecoration.new(title="Hello World")
end
def render(r)
r.h1 "Hello World from Wee!"
r.div.onclick_callback { p "clicked" }.with("click here")
end
end
Wee.run(HelloWorld) if __FILE__ == $0
# Now point your browser to http://localhost:2000/
Installation
gem install wee
More Resources
-
http://rubyforge.org/projects/wee
-
http://www.ntecs.de/projects/wee/doc/rdoc/
-
http://github.com/mneumann/wee
The last couple of days I spent refactoring my web application framework Wee, a Seaside-like framework for Ruby that I started back in 2004 with a lot of mental help from Avi Bryant (one of the main guys behind Seaside). It’s now approaching a 2.0 release. Wee is now fully Rack based (Rack is a commonly used Ruby Webserver Interface) and the code is in general even cleaner than it was before (huge parts were rewritten). Plus one cool feature: Continuations.
Continuations
Yesterday I finally thought it’s time to put back in continuations mainly because of some very interesting developments (i.e. patches) for Ruby 1.8.x, which seems to fix memory leaks that occured when using continuations.
Continuations in Wee were never used as extensively as they were used in Seaside. And it’s interesting to read that Seaside 2.8 reduced heavy usage of continuations – something that Wee did from the beginning :)
An example says more than thousands words, so here we go:
require 'wee'
class Page < Wee::Component
def initialize
add_decoration Wee::PageDecoration.new('Title')
super
end
def render(r)
r.anchor.callback {
if callcc YesNoMessageBox.new('Really delete?')
callcc InfoMessageBox.new('Deleted!')
else
callcc InfoMessageBox.new('Deleted action aborted')
end
}.with("delete?")
end
end
class InfoMessageBox < Wee::Component
def initialize(msg)
@msg = msg
super()
end
def render(r)
r.h1(@msg)
r.anchor.callback { answer }.with('OK')
end
end
class YesNoMessageBox < InfoMessageBox
def render(r)
r.h1(@msg)
r.anchor.callback { answer true }.with('YES')
r.space(1)
r.anchor.callback { answer false }.with('NO')
end
end
Wee.runcc(Page)
The interesting part of this example is the anchor tag callback handler of component Page. When clicked it will display a YES/NO message box which will return true if you clicked on YES, otherwise false. When it returns, execution resumes exactly at the point after the callcc call. In fact, callcc returns the return value of the called component. A called component returns by calling the answer method (it behaves mostly like the regular return statement). The concrete mechanism how this all works out is a bit more complicated as it involves installing and removing several decorations to delegate rendering and catch exceptions upon answer, but this is totally unrelated to continuations.
So how would it look without the use of continuations? Well, the callback handler would turn from:
if callcc YesNoMessageBox.new('Really delete?')
callcc InfoMessageBox.new('Deleted!')
else
callcc InfoMessageBox.new('Deleted action aborted')
end
into:
call YesNoMessageBox.new('Really delete?') do |res|
if res
call InfoMessageBox.new('Deleted!')
else
call InfoMessageBox.new('Deleted action aborted')
end
end
Actually in this concrete example it’s not hard to rewrite the code without using continuations. But for more complex examples it would get much worse. For example, using continations, a simple sequential flow like the following one:
callcc page1
callcc page2
callcc page3
would turn into the much less readable equivalent using CPS (“continuation passing style”):
call page1 do
call page2 do
call page3
end
end
Which one would you prefer? The good thing: In Wee you can use both, that’s why I have the two methods call and callcc, the latter not to mix up with Kernel.callcc.
Performance and Memory Usage
The reason why continuations were basically unsupported in Wee for a very long time was that they leaked memory. Since a few months there circulated patches on the ruby-core mailing list that seem to fix those leaks. Actually the reason seams to be that some parts of the stack are not overwritten when calling a function and as such old values keep referenced: a leak is born! I haven’t tried those patches, but I think they work. Instead I tried Ruby 1.9.1. And wow! It’s incredible! Memory usage stays constant at 12 MB, regardless of the number of requests. And performance scales nearly linearily as I increase the number of threads.
For 10000 requests (with one thread) Ruby 1.9.1 takes 16 seconds and requires 12 MB of memory. The same example with Ruby 1.8.7 grows to 329 MB of memory and takes 56 seconds. That’s an increase in performance of factor 3.5 and a 27-fold reduction of memory.
The example I’m using for the benchmark is the following 2-level nested callcc component call:
class Benchmark < Wee::Component
#
# calls Called2 then returns
#
class Called1 < Wee::Component
def render(r)
r.anchor.callback { callcc Called2.new; answer }.with('back')
end
end
class Called2 < Wee::Component
def render(r)
r.anchor.callback { answer }.with('back')
end
end
def render(r)
r.anchor.callback { callcc Called1.new }.with("click")
end
end
Conclusion
Continuations seem to be stable enough and not too expensive in terms of memory and performance in Ruby 1.9 so that there is nothing against using them (wisely) within Wee. This makes Wee the only web framework for Ruby to my knowledge that uses continuations. Coupled with other great features provided by Wee, for example reusable components, backtracking or the programmatic HTML generation, this undoubtly allows Wee to be called one of the most advanced web application frameworks for Ruby. Worth to note is that Wee does not focus on RESTful multi-million page-view serving, scaling applications. Instead Wee focuses on very complex applications, similar as found in traditional GUIs, and to get the job done quick and beautiful.
I wrote an implementation of the Arc Challenge in Ruby using my web-framework Wee. The sourcecode is available here. I haven’t used continuations, because I think I broke continuation support in some of the later versions of Wee (just because I haven’t used continuations in my Wee apps). Once Rubinius gets (even) more mature, and my spare time allows, I’d like to get back to some ideas of Wee and make use of continuations.
- Make session-id cookies available to both Wee::Request and Wee::PagelessRequest, controlled by an application-wide setting.
- Finish the implementation of pretty-URLs (this rulez!). Thanks to Joao Pedrosa who is working on this.
- Implement a FilterDecoration and it’s subclass AuthorizationDecoration (that’s very easy, ~20 lines of code).
- Implement a Seaside-like root_for traversal, which allows each component in the tree to add stuff inside the <head> tag. This is triggered by the root-component from it’s render method.
- Refactor the Wee::Session class and it’s subclasses.
- Implement further adaptors, most useful would be a FastCGI adaptor. This would need to factor out the WEBrick dependent parts.
- Implement ResouceHandlers, which make it possible to generate images (or other external resources) from within your components.
- Use it in your projects and write tutorials ;-)
Disclaimer: This is my understanding of how Rails work. I might be wrong.
Rails
Rails model is very simple compared to that of Wee. The controller class and the method to invoke is extracted directly from the URL. For example:
/blog/show
would invoke BlogController#show. Now imaging the following URL:
/blog/show/4
This would invoke BlogController#show with @params = {‘id’ => 4}. I don’t know how Rails knows that 4 maps to the ‘id’ key, but that’s another story.
So you have these parts of a URL:
/controller/action/arguments
In Rails, there’s also no (conceptual) distinction between performing an action and rendering, as is the case in Wee. Both are inseparable from each other (in Rails).
Another difference to Wee is, that the controller classes are stateless, and instances of it might be reused (pooling). On the other hand this implicates that you can’t store information inside a controller instance across requests.
Wee
In Wee, we differenciate between an action phase, where callbacks are invoked and as such possibly modify the state of components, and a render phase, which ideally is side-effect free and whose sole purpose is to render the component to HTML (or whatever format you like).
Imagine you look at a simple Wee application which displays an HTML anchor tag. If you click on that anchor, you’ll trigger an action phase, which eventually will find the registered callback associated with the anchor you have clicked on and invokes it. At the end of the action phase, Wee will forward you to a new (automatically generated) URL, which when requested by your browser, will trigger the render phase. This in turn renders the whole component tree. "Why redirect", you might ask. Well, this simply avoids that the same callback will be invoked again if you hit your browsers reload button. Whenever you hit on reload, this will only trigger a render phase event.
Additionally, at the end of each action phase, a snapshot of the component tree is taken, so that you can go back to older states (called back-tracking). The information about which state of the component tree we refer to, is stored as page_id inside the URL. This page_id increases whenever an action phase is performed.
So, an URL in Wee basically consists of the following parts:
session_id page_id [callback_id]
where callback_id is optional. If it is given, the URL triggers an action phase. Otherwise, a render phase.
Compared to Rails controllers, Wee’s components are composites, meaning that they may contain sub-components which itself might contain sub-components and so on. And in Wee there’s only ever one root component, whereas in Rails there are usually multiple controllers. All this makes it nearly impossible to have REST-like URLs in Wee. Also due to the reason that a sub-component cannot be rendered on it’s own, whereas a controller in Rails builds a whole page. So it does not make sense to have URLs in Wee like:
/1.2.3
# == @root_component.children[1].children[2].children[3]
Marrying Wee and Rails aproaches
The concept of multiple top-level controllers can be easily added onto Wee by using the following RootComponent class:
# NOTE: not fully functional code!
class RootComponent < Wee::Component
def initialize
super()
@controllers = {
'blog' => add_child(BlogComponent.new),
'list' => add_child(ListComponent.new)
}
end
def render
controller = # extract information from URL
r.render @controllers[controller]
end
end
This RootComponent merely acts as a dispatcher. It looks at the URL and extracts the desired controller out of it and then forwards to it.
Likewise, we could map the Rails action part (the "show" in "/blog/show"), to invoke render_xxx (e.g. render_show) of the controller-component (I use this term now, to distinguish it from a "regular" Wee component and to mark the similarity with a Rails controller):
def render
controller = # extract information from URL
action = # extract Rails action part from URL
component = @controllers[controller]
component.with_renderer {
component.send("render_" + action)
}
end
And even further, we could also extract the additional arguments of the URL for use inside the called render method. We could use a custom parse_arguments method here in each controller-component.
Now lets look at a simple example:
class BlogController < Wee::Component
def parse_arguments(str)
# for /blog/show/5, str would be "5"
@params = ... # e.g. {'id' => 5}
end
def render_show
entry = BlogEntry.get(@params['id'])
# render it
end
def render_list
BlogEntry.find_all do |entry|
# render it
end
end
end
What else would be needed is to tell which controller/action pair should be used. This could for example be specified each time when generating an anchor or form tag:
r.anchor.controller('blog/show/5').callback { .... }.with('show')
Note that this would first invoke the callback and then render ‘blog/show/5’. Of course you could ommit in this case the callback. But specifying the controller/action pair each time is tedious, as this would have to be done in each sub-component, too, which completely breaks the concept of a component. So the second approach is much better:
r.anchor.callback {
... do something
request.controller = 'blog/show/5'
}
where the request.controller setting is carried inside the URL as long as you assign a new value to it.
Note that you can still use sub-components with this approach, but they would no longer be subject of REST-like URLs.
Some days ago, I wrote an OgScaffolder component. It’s damn easy to use. Just pass an Og domain class to OgScaffolder.new, and what you get is a regular Wee component. Look at the screenshot below.
Below I’d like to show some Nemo screenshots, to show it’s potential. Note that the whole generic editor component (the first screenshot) is less than 200 lines very clean Ruby code! Thanks Kevin ;-)
leaf.dragonflybsd.org/mailarchive/users/2004-11/msg00191.html
I wrote a Ruby wrapper around the sys_checkpoint syscall. You can find the sources here: www.ntecs.de/viewcvs/viewcvs/DragonFly/checkpoint/
The next step is to put this into the Wee sources, so that you can shutdown and reload Wee applications that use continuations. Of course, this only works on DragonFlyBSD. But I don’t think this is much of a problem…
Last evening, we then build the calculator shown in the picture below. It’s a portable web application using Ruby and my new Wee framework. I spent most time on generating the diagram (it’s done using gnuplot). The rest was straightforward, once we had the underlying calculations working.
The total code size is 360 lines of code (including HTML), of which 110 are used for the calculation and for generating the diagram. Lots of lines of code are currently spent in conversion and validation routines (german number format).
You can try it out online here: ntecs.de/wee/ehf/





