
The basic idea is to do all computation on the server and treat the web-browser as a graphical dumb terminal. All javascript event handling code is moved to the server, without sacrificing usability.
ovar Int x y
x := 1 ; y := 2
input "" x
text "+"
input "" y
text "="
section "sum"
void
button "add"
section_overwrite "sum"
if x<>undefined and y<>undefined
text string:(x + y)
+ =
Unlike Apache and other webservers which forget about a request as soon as they serve it, pliant ui engine creates an instance of all transient data for use by the webpage. In this example the data consists of "x" and "y" variables. This data stays active in memory on the server until the user leaves the page.
It is instructive to look at "Net" traffic in FireBug when you click the "add" button, and at the response data. As well, notice that a request to the server is sent as soon as you change the contents of an input field.
In today's web-applications either a database or filesystem is used to simulate a job queue because the web application can not keep data in memory after it served the requset.
However, this is not the case with pliant. It is possible to run a thread as part of user interface. Here's an example:
ovar List:Int queue
ovar Sem queue_sem
ui_thread
while connection=success
queue_sem request
if queue:size = 0
queue_sem release
sleep 1 # release cpu
else
var Int seconds := queue first
queue -= queue:first
queue_sem release
# do time consuming work here
sleep seconds
sometime later in the UI,
button "add"
queue_sem request
queue += sleep_time
queue_sem release
One of the biggest difficulties with web-development is keeping state. In pliant web framework, state keeping is provided automatically per page, for all memory allocated variables. Example,
ovar Int x := 0 section "show_x" dynamic text "x = "+string:x ; eol button "add 1" x += 1 section_replay "show_x" |
x = 0 |
However, this keeps state per page. Sometimes we want to remember things like user login information accross pages. Try writing a random username in the box, and hitting the "save & reload" button. After the refresh, the field will remember your name.
gsvar Str username input "sign in: " username button "save & refresh page" execute_javascript "window.location.reload()"
sign in:
To continue the previous demo, here's a shopping cart powered by the "gsvar Set:Str cart" declaration. Open this page in several tabs and you will see that the shopping cart is remembered in all of them.
gsvar Set:Str cart
ovar Str item := "orange"
section "cart" dynamic
text "fruit shake: " ; eol
each count cart
var Str name := keyof count
text name+":"+string:count ; eol
item := "orange"
select "" item
option "orange" "orange"
option "apple" "apple"
option "banana" "banana"
button "add"
if not (exists cart:item)
cart create item
cart item := 0
cart item += 1
section_replay "cart"
|
fruit shake: |
Lets use WWW::Google::PageRank module from CPAN to find out a pagerank for any website,
perl_eval {{
use WWW::Google::PageRank;
sub get_pr {
my $url = shift;
my $google = WWW::Google::PageRank->new;
return $google->get($url) || "?";
}
}}
input "url: " url
section "pagerank"
void
button "lookup pagerank"
if url<>""
safe
pagerank := perl get_pr url
section_overwrite "pagerank"
text "PR: "+pagerank
url:
More details on pliant-perl api can be found at the Pliant-Perl SourceForge page.
We are able to generate some javascript code on the server and seamlessly send it to the browser to eval. In this example "{TIME}" is replaced on the server to local time.
ovar Str code := "alert('server time is {TIME}')"
input "js template code: " code
button "send"
var js := replace code "{TIME}" string:datetime
execute_javascript js
js template code:
Another example is playing a sound. This sample uses the "SoundManager" flash/javascript library to play mp3s:
button "play sound"
var Str js := "soundManager.play('chat_incoming')"
execute_javascript js
The template mechanism for HTML generation is a generalization of printf() function and a "switch" statement of C.
printf("... %s ... %s ...", x, y)
|
switch() in C
switch(k) {
case x:
...
break;
case y:
...
break
default:
handle(k)
}
|
template in Pliant
template "... {{x}} ... {{y}} ..."
template_section "x"
...
template_section "y"
...
template_default
text template_keyword
|
Example: A very common problem is to generate a table with alternatively colored rows, like the one on the right. This table shows angles and their sines. Below is the code to generate it.
Lets define the template,
<TABLE id="sine_table">
<TR> <th> Degree </th> <th> Sine </th> </TR>
<TEMPLATE NAME="sine_table">
<TR class="{{odd_or_even}}">
<TD> {{degree}}° </TD>
<TD> {{sin}} </TD>
</TR>
</TEMPLATE>
</TABLE>
Lets add some css to color the even and odd numbered rows differently,
<style>
.odd TD { background: #B6CFB3; }
.even TD { background: #E0FFDC; }
</style>
Finally, pliant code to drive this,
template_section "sine_table"
var Str row_design := template_value
for (var Int i) 0 12
var Int degree := i*30
template row_design
template_section "odd_or_even"
text (shunt i%2=0 "even" "odd")
template_section "degree"
text string:degree
template_section "sin"
var Float x := sin degree/180*pi
text string:((cast x * 1000 Int)/1000)
| Degree | Sine |
|---|---|
| 0° | 0 |
| 30° | 0.5 |
| 60° | 0.866 |
| 90° | 1 |
| 120° | 0.866 |
| 150° | 0.5 |
| 180° | 0 |
| 210° | -0.5 |
| 240° | -0.866 |
| 270° | -1 |
| 300° | -0.866 |
| 330° | -0.5 |
| 360° | 0 |
The template mechanism uses printing rather than string accumulation to insert content into the template. The code blocks inside "template_section"s are closures. They can contain any complex code containing UI widgets such as "text", "input", "button", nested template constructs or function invocations.
The benefit of this design is that template code can be tied into function invocation order. There is no need to prepare the template in memory by performing string substitution (HTML::Template model), or to build a hash with data that will be proccesed by the template using meta language (TemplateToolkit model), or to mix code with the template (ASP, JSP model).
Here's the implementation of "%" operator for large integers. We know from math that,
For example,
Here's the default definition for '%' operator:
function '%' a b -> r
arg Intn a b r
has_no_side_effect
var Intn q t
if addressof:r<>addressof:a and addressof:r<>addressof:b
divide a b q r
else
t := a%b
r := t
Here's the handling of the special case x^y%n:
meta '%' e
strong_definition
if e:size<>2
return
e:0 compile ?
e:1 compile ?
var Link:Instruction i
var Pointer:Function have want
i :> e:0:instructions:last map Instruction
have := i:function
want := the_function '^' Intn Intn -> Intn
if addressof:i=null
return
if addressof:have<>addressof:want
return
if (addressof e:0:result)<>(addressof i:2)
return
if not (e:1 cast Intn)
return
# found x^y%n
i size := 4
i 3 :> i 2
i 2 :> e:1:result
i function :> the_function power_modulus Intn Intn Intn -> Intn
e suckup e:1 ; e suckup e:0
e set_result e:0:result access_read
The idea behind above code is to check if previous instruction was "^". If it is found, generated instructions are rearranged to use a more optimal implementation "power_modulus". This function is:
function power_modulus a b m -> r
arg Intn a b m r
has_no_side_effect
r := 1 ; var Intn p := a
var Int n := b nbbits
for (var Int i) 0 n-1
if (b test_bit i)
r := r*p
if addressof:m<>null
r apply_modulus m
if i=n-1
return
p := p*p
if addressof:m<>null
p apply_modulus m
In general there are three stages at which modifications to compilation can be done.
You can eval a string of code by using "runtime_compile" instruction. It can also apply template substitution similar to C++ template construct <<...>>.
module "/pliant/language/compiler.pli"
function func_a
console "in a" eol
function func_b
console "in b" eol
function run_by_name name
arg Str name
runtime_compile func_name (cast "func_"+name Ident)
func_name
run_by_name "a"
run_by_name "b"
Another example of "runtime_compile" usage is implementation of a templated Array class. It is used like this:
var Array:Str array_of_strings
Its implementation:
function Array value -> t
arg Type value ; arg_R Type t
has_no_side_effect
var Address adr := 'pliant array types' query addressof:value null
if adr<>null
return (adr map Type)
runtime_compile Value value Array (cast "(Array "+value:name+")" Ident)
type Array
field Address items
field Int nb
#... implementation follows
var Address adr := 'pliant array types' query addressof:value null
check adr<>null
return (adr map Type)
view full Array:X implementation
More low-level api for runtime compilation is available through "compile_text" instruction that is used in implementing the online pliant interpreter.
Pliant is an English word that means: bending readily; flexible; supple; adaptable.
Just like with scripting languages, no Makefiles, Ant build files, or configure.sh scripts are needed. Pliant compiles from source code, and resolves all dependencies automatically.
Not always, but most of the time, theres no need to restart the pliant ui server: with a press of a button you can recompile changes in Pliant UI code.
Meta programming is the most unique and original feature in Pliant. It gives the ability to add new syntax to the language.
Just like Adobe AIR, Pliant UI Client is a program you install locally on your computer in order to use client-server application with advanced features.
Copyright © 2008 Pliant Software Solutions | All Rights Reserved
Website powered by Pliant. Contact:
Connection to the server is paused.
Please press the Continue button to resume.