domingo, 4 de septiembre de 2011

Jtalk tutorial. (Valid until the end of this week)





TL;DR
write this in a workspace, select, and click 'DoIt'. Instant reward.
Counter new appendToJQuery: 'body' asJQuery




You know Smalltalk, it is an object oriented programming language developed at Xerox PARC during the 70's by Alan Kay, Dan Ingalls and other brillant guys. It is known that Kay, and probably other members of the team, was born from the leg of Zeus. Jtalk is an implementation of Smalltalk that runs on top of JavaScript, developed by Nicolas Petton. It allows the use of Smalltalk for client side applications. Even more, Jtalk allows you to use the famous IDE from the Smalltalk world directly in your browser. It compiles to JavaScript, and follows Pharo, a fork of the Squeak Smalltalk implementation.

The canonical "Hello World" looks like this:

Widget subclass: #HelloWorld
instanceVariableNames: ''
category: 'Examples'
renderOn: html
"Render some html"
html div class: 'section';
id: 'headerSection';
with: [ html h1: 'Hello World' ].
view raw HelloWorld.st hosted with ❤ by GitHub

You create a subclass of Widget, and you implement the #renderOn: method. Jtalk will pass an HtmlCanvas instance that we call html in our code. This object provides some "brushes" allowing to "paint" into the html canvas by message passing. We created a div element by sending the div message to html. The div element has it's class and id attributes defined, and it contains a h1 element. Everything is performed by passing messages to the html object. Those familiar with the Seaside web framework will recognize the html canvas. After the HelloWorld class is
defined, you can see it in the browser by appending an instance to the page's body using JQuery:

HelloWorld new appendToJQuery: 'body' asJQuery


A JQuery object is created by passing the #asJQuery message to the string object 'body'. Then we pass the #append message to the JQuery object, with an instance of HelloWorld as argument. You can evaluate this expression in the Workspace and see it in action.

Another way to do the same is going to JQuery class, and send the #body message.

Where is the Counter?
If you know Seaside you'll want to look at the Counter example. I will reproduce the example from the Jtalk website, which you can find also with the IDE's class browser. It is a good example because it shows how you can write stateful applications in Samalltalk, by simply using its object system as you would do in any application:
Widget subclass: #Counter
instanceVariableNames: 'count header'
category: 'Examples'
initialize
super initialize.
counter := 0.
increase
count := count + 1.
header contents: [ :html | html with: count asString ]
decrease
count := count - 1.
header contents: [ :html | html with: count asString ]
renderOn: html
html h1 with: count asString.
html button
with: '++';
onClick: [self increase].
html button
with: '--';
onClick: [self decrease]
view raw Counter.st hosted with ❤ by GitHub

As in Seaside, the button definition is beautiful. Since the object itself holds the state of the application, you simply bind a Smalltlak code block to the onclick event, and Jtalk will take it from there.

Interacting with the DOM with JQuery and JS evaluation

As you can see, there is JQuery support provided by the JQuery class. We just used it for appending a piece of html to the body element of our page. There is support for a good part of the JQuery api, as you can see by browsing the methods implemented in the JQuery class. You can do DOM insertion, css manipulation, event handling, etc. If something you like is not implemented, you can evaluate JS code by enclosing it with the "<" and ">" characters. A wrapper for ajax is also available:

Ajax new
url: '/my_web_service/get_content';
onSuccessDo: [ :data | Transcript show: data ];
onErrorDo: [ :error | Transcript show: 'Request failed!' ];
send.
view raw simpleAjax.st hosted with ❤ by GitHub


Here we create an Ajax object and then we send some messages to it. As you can see, we use Samlltalk blocks as callbacks, this blocks are compiled to JavaScript anonymous functions for execution. There are other messages availables in an Ajax object, like #onCompleteDo. Dictionary like syntax is provided for passing additional settings to the Ajax object:

Ajax new
url: '/my/post/handler';
at: 'type' put: 'post';
at: 'data' put: 'foo';
send.


As test, I wrote a small HTTP service interfacing a CouchDb instance. I did it in Python using the small but cool Flask framework:

from Flask import Flask, abort, jsonify
from couchdb import Database
app = Flask(__name__)
database = Database("http://10.0.0.2:5984/my_database")
@app.route("/")
def index():
"""Show the Jtalk IDE"""
return render_template("ide.html")
@app.route("/get/<doc_id>")
def get_document(doc_id):
"""Lookup the document in the database and return it as JSON"""
doc = database.get(doc_id)
if doc:
response = jsonify(doc)
else:
response = abort(404)
return response
view raw simpleCouchy.py hosted with ❤ by GitHub


Please consider it almost pseudo code. You can note its very simple stuff. We serve the Jtalk DE at "/", and we provide access to the database in "/get/<doc_id>". Now, we could build a small wrapper for accessing this service:

Object subclass: #Data
instanceVariableNames: ''
category: 'Example'
get: docId onSuccess: aBlock
|smalltalk|
smalltalk := Smalltalk new.
Ajax new
url: '/get/', docId;
onSuccessDo: [ :data | aBlock value: (smalltalk readJson: data) ];
onErrorDo: [ :error | Transcript show: 'Request Failed!' ];
send.
view raw Data.st hosted with ❤ by GitHub


We provide a single method called get:onSuccess: allowing the client code to request a document and executing a callback block on the resulting data. Smalltalk blocks are constructed using brackets, and they are like anonymous functions. Temporary named arguments are declared with the :argName, and after the bar you can write any Smalltalk statement. Our onSuccessDo callback takes the data received form the Ajax request as argument. This data comes in JSON format, so we
use an instance of the Smalltalk object to translate this to a Jtalk object, and then we pass that to the block provided by the client code, which we called "aBlock".

An experiment using Rasta.js for data persistence

Rasta.js
is a very simple key/value storage service. It provides a REST API and a client library:

<script src="http://rastajs.errorjs.com/rasta.min.js" type="text/javascript">

We can use this for looking at how can we delegate execution to a JavaScript object and also for providing some data persistence to a static website.

Object subclass: #Rasta
instanceVariableNames: 'rasta'
Category: 'Example'
initialize
super initialize.
rasta := <Rasta;>.
get: aKey onSuccessDo: aBlock
rasta get: aKey value: aBlock
set: aKey with: aValue onSuccessDo: aBlock
rasta set: aKey value: aValue value: aBlock.
view raw Rasta.st hosted with ❤ by GitHub


That was easy. The Rasta class has one instance variable, called "rasta". Instance attributes are private in Smalltalk, you have to implement methods if you want to access them from the outside. But in this case, our rasta attribute is for internal use, we don't need the stinky accessors. The Rasta.js API provides two simple JS functions:

Rasta.get('age', function(val){
/* val == '100' */
})

Rasta.set('age','100', function(){
/* */
})
Since they use an upper cased name, and in Smalltalk those are reserved for classes, we have to use a special trick. Jtalk will get confused if you try to evaluate "Rasta" directly. This not happens with lower case JS names, which are available directly in the Jtalk IDE (you can try
console log: 'fooBar' for an example in the Workspace.) So, we will use our "rasta" instance attribute for storing the JS object, we use the <jsobject> notation for getting a sort of proxy object which will delegate messages in the JS one, and we can access it under the "rasta"
name in our messages. Now we can use the Rasta.js KISS service for our data storage:
database := Rasta new.
database get: 'aKey' onSuccess: [ :data | 'div#container' asJQuery append: data ].

Ok, usualy you will want to format the data. We can wrap this into a Widget subclass and implement a #renderOn: method. At this point we can put all these features together in our mind: you get a nice programming environment which promotes good code practices like layers and concerns separation, clean and readable syntax, testing facilities, and who knows
what, with an IDE accessible directly from the browser.

On code persistence

I will just say it. If you press "F5" (or "r" if you are a hacker) you will lose your code. The IDE features a "Commit category" button, which sends a PUT request with the compiled ST in the body. But as Jtalk is focused in client side coding, it is your responsability to handle the
request and save the code into the "js/" folder, next to the Jtalk library. I will show you the "almost pseudo code" thing again, with Flask and Python:

from werkzeug import secure_filename
@app.route("/<filetype>/<filename>", methods=["PUT"])
def commit_changes(filetype, filename):
"""Commit changes in the Jtalk image to disk"""
filename = secure_filename(filename)
data = request.form.keys()[0]
with open(os.path.join(UPLOAD_FOLDER, filetype, filename), "w") as fileobject:
fileobject.write(data)
return "201"
view raw commit.py hosted with ❤ by GitHub


Note that I used the secure_filename from werkzeug to sanitize the data, since I use it to build the path. You don't want to allow write to your .zshrc or similar.

Concluding

Jtalk can help you to write heavy client side applications by taking advantage of the ancient wisdom from the Smalltalk world. It still has some sharp edges but it is there for you to try it. Allows you to write stateful client code easily, and to port known and tested patterns to
the web browser, for great justice. And there is a plus. Jtalk is part of a legendary story, the Smalltalk one. After all, I have told you, these guys came from the leg of a god.

A moving target

In the couple of days that Rodrigo Bistolfi and me have been experimenting with Jtalk and writing this article (In fact he wrote most of the article), a bunch of things changed already, or are going to change shortly. For example, Capitalized Javascript objects are parsed now without problems in Jtalk. On the JQuery side, Nicolas Petton recently said that JQuery binding is going to disappear from jtalk bundle. It seems we'll be able to fetch DOM nodes directly with jtalk. So better be up to date with the jtalk git repo, because jtalk is moving fast.

Btw, there's a lot more related to Jtalk. Goran Krampe wrote jtalkc, that compiles jtalk code to js able to run in node.js. I haven't digged on that yet, but seems it also opens a shitload of possibilities... :)

Cya!

1 comentario:

Anónimo dijo...

Should it be in the example:

header := html h1 with: count asString ; yourself.

?