FullStack Single File Example
Fullstack development includes: webserver, database, object relational mapping, javascript and html with css. This example includes all of these in a single file, in less than 300 lines of code.
Dataset
Dataset is a database abstraction layer that makes using SQL dead simple.
https://dataset.readthedocs.org/en/latest/
git clone git://github.com/pudo/dataset.git
cd dataset/
sudo python setup.py install
Tornado Server
the tornado webserver saves objects into the SQL database using Dataset.
./rusthon.py ./examples/hello_fullstack.md --run=myserver.py
@myserver.py
#!/usr/bin/python
import dataset
import tornado
import tornado.ioloop
import tornado.web
import tornado.websocket
import os, sys, subprocess, datetime, json, time, mimetypes
PORT = int(os.environ.get("PORT", 8000))
## connect to database ##
##db = dataset.connect('sqlite:///mydatabase.db')
db = dataset.connect('sqlite:///:memory:')
# dataset will get or create `mytable`
table = db['mytable']
class MainHandler( tornado.web.RequestHandler ):
def get(self, path=None):
print('path', path)
guess = path
if not path or path == '/': guess = 'index.html'
mime_type, encoding = mimetypes.guess_type(guess)
if mime_type: self.set_header("Content-Type", mime_type)
if path == 'favicon.ico' or path.endswith('.map'):
self.write('')
elif path and '.' in path:
self.write( open(path).read() )
else:
self.write( open('index.html').read() )
Clients = []
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print( 'websocket open' )
print( self.request.connection )
Clients.append( self )
def on_message(self, msg):
ob = json.loads( msg )
## if a simple test string ##
if isinstance(ob, str):
print ob
elif isinstance(ob, list):
print 'doing database search'
results = []
for search in ob:
assert isinstance(search, dict)
## `**search` unpacks to something like `name="foo"`
r = table.find_one( **search )
if r: results.append(r)
if results:
print results
self.write_message(json.dumps(results))
else:
self.write_message(json.dumps("nothing found in databases"))
elif isinstance(ob, dict):
print 'saving object into database'
print ob
if len( list(table.find(id=ob['id'])) ):
table.update( ob, ['id'] )
else:
table.insert( ob )
self.write_message( json.dumps('updated database:'+str(ob)) )
for other in Clients:
if other is self: continue
print 'updating other client with new data'
other.write_message( msg )
def on_close(self):
print('websocket closed')
if self in Clients:
Clients.remove( self )
if self.ws_connection:
self.close()
## Tornado Handlers ##
Handlers = [
(r'/websocket', WebSocketHandler),
(r'/(.*)', MainHandler), ## order is important, this comes last.
]
print('<starting tornado server>')
app = tornado.web.Application(
Handlers,
#cookie_secret = 'some random text',
#login_url = '/login',
#xsrf_cookies = False,
)
app.listen( PORT )
tornado.ioloop.IOLoop.instance().start()
Client Side
https://github.com/rusthon/Rusthon/wiki/JavaScript-DOM-Syntax
@myapp
#backend:javascript
from runtime import *
ws = None
def on_open_ws():
print 'websocket open'
ws.send(JSON.stringify('hello server'))
def on_close_ws():
print 'websocket close'
def on_message_ws(event):
print 'on message', event
msg = None
if instanceof(event.data, ArrayBuffer):
print 'got binary bytes', event.data.byteLength
arr = new(Uint8Array(event.data))
txt = String.fromCharCode.apply(None, arr)
msg = JSON.parse(txt)
else:
msg = JSON.parse(event.data)
pre = document->('#RESULTS')
if isinstance(msg, list):
for res in msg:
s = JSON.stringify(res)
pre->(s+'\n')
elif isinstance(msg, string):
pre->(msg+'\n')
else:
proxy = man.instances[ msg.id ]
#a = { msg['key'] : msg['value'] } ## TODO support this syntax
a = {}
a[ msg['key'] ] = msg['value']
proxy.restore( a )
def connect_ws():
global ws
addr = 'ws://' + location.host + '/websocket'
print 'websocket test connecting to:', addr
ws = new( WebSocket(addr) )
ws.binaryType = 'arraybuffer'
ws.onmessage = on_message_ws
ws.onopen = on_open_ws
ws.onclose = on_close_ws
class Proxy:
ids = 0
def __init__(self):
self.__id__ = Proxy.ids
Proxy.ids += 1
with 𝕚𝕟𝕡𝕦𝕥 as "%s=_=document.createElement('input');_.setAttribute('type','text');%s=0;":
𝕚𝕟𝕡𝕦𝕥( self._xe, self._x )
𝕚𝕟𝕡𝕦𝕥( self._ye, self._y )
@bind(self._xe.onkeyup, self)
def update_xe(e):
print 'update xe:' + self._xe.value
self.x = self._xe.value
@bind(self._ye.onkeyup, self)
def update_ye(e):
print 'update ye:' + self._ye.value
self.y = self._ye.value
def restore(self, restore):
for key in restore.keys():
self[ '_' + key ] = restore[key]
self[ '_' + key + 'e' ].value = restore[key]
def getwidget(self):
div = document.createElement('div')
with 𝓕 as "div.appendChild(document.createTextNode(%s));div.appendChild(%s);div.appendChild(document.createElement('br'))":
𝓕('some field X:', self._xe )
𝓕('some field Y:', self._ye )
return div
@getter
def x(self):
return self._x
@setter
def x(self, v):
if v != self._x:
msg = {id:self.__id__, key:'x', value:v}
ws.send( JSON.stringify(msg) )
self._x = v
self._xe.value = v ## updates widget
@getter
def y(self):
return self._y
@setter
def y(self, v):
if v != self._y:
msg = {id:self.__id__, key:'y', value:v}
ws.send( JSON.stringify(msg) )
self._y = v
self._ye.value = v ## updates widget
class Manager:
def __init__(self):
self.instances = {}
def makeproxy(self):
p = new Proxy()
self.instances[p.__id__] = p
return p
@debugger
def main():
global man, p
## connect websocket
connect_ws()
man = Manager()
p = man.makeproxy()
document->('#FORM')->(
document->('h3')->('update database:'),
p.getwidget()
)
HTML
@index.html
<html>
<head>
<link href='~/bootstrap-3.3.5-dist/css/bootstrap.css' rel='stylesheet' zip="https://github.com/twbs/bootstrap/releases/download/v3.3.5/bootstrap-3.3.5-dist.zip"/>
<script src="~/rusthon_cache/jquery-2.1.4.min.js" source="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="~/bootstrap-3.3.5-dist/js/bootstrap.min.js"></script>
<@myapp>
</head>
<body onload="main()">
<div id="FORM" class="well"></div>
<pre id="RESULTS" class="well">
</pre>
</body>
</html>