aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbozokopic <bozo.kopic@gmail.com>2017-04-24 23:40:00 +0200
committerbozokopic <bozo.kopic@gmail.com>2017-04-24 23:40:00 +0200
commit085b236d9a169a1815a6117caab5894521e59672 (patch)
tree3d3220936a877fbe75206e2effc581c548d568ec
parented9f4f8991f4e8b31270dbbf17215732d73c9e37 (diff)
heroku init
-rw-r--r--.gitignore16
-rw-r--r--Procfile1
-rw-r--r--app.json9
-rw-r--r--bin/compile6
-rw-r--r--bin/detech8
-rw-r--r--dodo.py28
-rw-r--r--playground/server/run.sh8
-rw-r--r--runtime.txt1
-rw-r--r--src_js/opcut/common.js6
-rw-r--r--src_js/opcut/lenses.js42
-rw-r--r--src_js/opcut/renderer.js252
-rw-r--r--src_js/opcut/vt.js16
-rw-r--r--src_py/opcut/main.py160
-rw-r--r--src_py/opcut/util.py80
14 files changed, 329 insertions, 304 deletions
diff --git a/.gitignore b/.gitignore
index f0ef978..03cceb2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,8 @@
-__pycache__
-/build
-/dist
-/.doit.*
-/yarn.lock
-/node_modules
-/src_py/opcut/json_validator.py
-/src_js/opcut/validator.js
+__pycache__
+/build
+/dist
+/.doit.*
+/yarn.lock
+/node_modules
+/src_py/opcut/json_validator.py
+/src_js/opcut/validator.js
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..28fe25a
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: cd dist && python -m opcut.main --ui-addr http://0.0.0.0:$PORT
diff --git a/app.json b/app.json
new file mode 100644
index 0000000..201d708
--- /dev/null
+++ b/app.json
@@ -0,0 +1,9 @@
+{
+ "name": "opcut",
+ "website": "http://opcut.heroku.com",
+ "repository": "https://github.com/bozokopic/opcut",
+ "buildpacks": [
+ {"url": "heroku/python"},
+ {"url": "https://github.com/bozokopic/opcut"}
+ ]
+}
diff --git a/bin/compile b/bin/compile
new file mode 100644
index 0000000..6971f42
--- /dev/null
+++ b/bin/compile
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+cd $1
+pip install -r requirements.pip.txt
+doit clean_all
+doit
diff --git a/bin/detech b/bin/detech
new file mode 100644
index 0000000..6c1362c
--- /dev/null
+++ b/bin/detech
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [ -f $1/dodo.py ]; then
+ echo "opcut build"
+ exit 0
+else
+ exit 1
+fi
diff --git a/dodo.py b/dodo.py
index f1a6eb5..a465808 100644
--- a/dodo.py
+++ b/dodo.py
@@ -1,14 +1,14 @@
-import sys
-import os
-
-sys.path += ['src_py']
-
-os.environ['PYTHONPATH'] = os.pathsep.join(map(
- os.path.abspath, ['src_py']))
-
-DOIT_CONFIG = {
- 'backend': 'sqlite3',
- 'default_tasks': ['dist_build'],
- 'verbosity': 2}
-
-from opcut.doit.main import * # NOQA
+import sys
+import os
+
+sys.path += ['src_py']
+
+os.environ['PYTHONPATH'] = os.pathsep.join(map(
+ os.path.abspath, ['src_py']))
+
+DOIT_CONFIG = {
+ 'backend': 'sqlite3',
+ 'default_tasks': ['dist_build'],
+ 'verbosity': 2}
+
+from opcut.doit.main import * # NOQA
diff --git a/playground/server/run.sh b/playground/server/run.sh
index 5b7cc98..0f4984a 100644
--- a/playground/server/run.sh
+++ b/playground/server/run.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
-
-export PYTHONPATH=../../src_py
-python -m opcut.main --ui-path ../../build/jsopcut
+#!/bin/bash
+
+export PYTHONPATH=../../src_py
+python -m opcut.main --ui-path ../../build/jsopcut
diff --git a/runtime.txt b/runtime.txt
new file mode 100644
index 0000000..c91e43b
--- /dev/null
+++ b/runtime.txt
@@ -0,0 +1 @@
+python-3.6.1
diff --git a/src_js/opcut/common.js b/src_js/opcut/common.js
index adc7191..7e63d19 100644
--- a/src_js/opcut/common.js
+++ b/src_js/opcut/common.js
@@ -1,3 +1,3 @@
-
-
-export const defaultState = {};
+
+
+export const defaultState = {};
diff --git a/src_js/opcut/lenses.js b/src_js/opcut/lenses.js
index 8458dbd..39da314 100644
--- a/src_js/opcut/lenses.js
+++ b/src_js/opcut/lenses.js
@@ -1,21 +1,21 @@
-import R from 'ramda';
-
-
-export const index = R.lensIndex;
-
-export const prop = R.lensProp;
-
-export function path(...xs) {
- return R.reduce((acc, i) => R.compose(acc, pathParamToLens(i)),
- R.identity, xs);
-}
-
-function pathParamToLens(x) {
- switch (typeof(x)) {
- case 'function': return x;
- case 'number': return index(x);
- case 'string': return prop(x);
- case 'object': if (Array.isArray(x)) return R.apply(path, x);
- }
- throw 'Invalid path parameter';
-}
+import R from 'ramda';
+
+
+export const index = R.lensIndex;
+
+export const prop = R.lensProp;
+
+export function path(...xs) {
+ return R.reduce((acc, i) => R.compose(acc, pathParamToLens(i)),
+ R.identity, xs);
+}
+
+function pathParamToLens(x) {
+ switch (typeof(x)) {
+ case 'function': return x;
+ case 'number': return index(x);
+ case 'string': return prop(x);
+ case 'object': if (Array.isArray(x)) return R.apply(path, x);
+ }
+ throw 'Invalid path parameter';
+}
diff --git a/src_js/opcut/renderer.js b/src_js/opcut/renderer.js
index 22a74c6..6738141 100644
--- a/src_js/opcut/renderer.js
+++ b/src_js/opcut/renderer.js
@@ -1,126 +1,126 @@
-import Delegator from 'dom-delegator';
-import R from 'ramda';
-import bean from 'bean';
-import vh from 'virtual-dom/h';
-import diff from 'virtual-dom/diff';
-import patch from 'virtual-dom/patch';
-import createElement from 'virtual-dom/create-element';
-
-import * as l from 'opcut/lenses';
-
-
-const delegator = Delegator();
-const vhTypes = ['VirtualNode', 'Widget'];
-
-
-function vhFromArray(node) {
- if (!node)
- return [];
- if (typeof node == 'string' || vhTypes.includes(node.type))
- return node;
- if (!Array.isArray(node))
- throw 'Invalid node structure';
- if (node.length < 1)
- return [];
- if (typeof node[0] != 'string')
- return node.map(vhFromArray);
- let hasProps = (node.length > 1 &&
- typeof node[1] == 'object' &&
- !Array.isArray(node[1]) &&
- !vhTypes.includes(node[1].type));
- let children = R.flatten(node.slice(hasProps ? 2 : 1).map(vhFromArray));
- let result = hasProps ? vh(node[0], node[1], children) :
- vh(node[0], children);
-
- // disable SoftSetHook for input
- if (result.tagName == 'INPUT' &&
- result.properties &&
- result.properties.value &&
- typeof(result.properties.value) === 'object') {
- result.properties.value = result.properties.value.value;
- }
-
- return result;
-}
-
-
-class VTreeRenderer {
-
- constructor(el) {
- this._el = el;
- this._vtree = null;
- }
-
- render(vtree) {
- let vt = vhFromArray(vtree);
- if (vt.type == 'VirtualNode') {
- if (this._vtree) {
- let d = diff(this._vtree, vt);
- patch(this._el.firstChild, d);
- } else {
- while (this._el.firstChild)
- this._el.removeChild(this._el.firstChild);
- this._el.appendChild(createElement(vt));
- }
- this._vtree = vt;
- } else {
- this._vtree = null;
- while (this._el.firstChild)
- this._el.removeChild(this._el.firstChild);
- }
- }
-
-}
-
-
-export class Renderer {
-
- constructor(el, initState, vtCb) {
- this.init(el, initState, vtCb);
- }
-
- init(el, initState, vtCb) {
- this._state = null;
- this._changeCbs = [];
- this._vtCb = vtCb;
- this._r = new VTreeRenderer(el || document.querySelector('body'));
- if (initState)
- this.change(R.identity, _ => initState);
- }
-
- view(...lenses) {
- return R.view(R.apply(l.path, lenses), this._state);
- }
-
- set(lens, value) {
- if (arguments.length < 2) {
- value = lens;
- lens = R.identity;
- }
- this.change(lens, _ => value);
- }
-
- change(lens, cb) {
- if (arguments.length < 2) {
- cb = lens;
- lens = R.identity;
- }
- if (this._changeCbs.push(cb) > 1)
- return;
- let startingSubState = this.view(lens);
- while (this._changeCbs.length > 0) {
- this._state = R.over(l.path(lens), this._changeCbs[0], this._state);
- this._changeCbs.shift();
- }
- if (!this._vtCb ||
- (this._state && R.equals(startingSubState, this.view(lens))))
- return;
- this._r.render(this._vtCb(this._state));
- bean.fire(this, 'render', this._state);
- }
-
-}
-
-
-const defaultRenderer = new Renderer();
-export default defaultRenderer;
+import Delegator from 'dom-delegator';
+import R from 'ramda';
+import bean from 'bean';
+import vh from 'virtual-dom/h';
+import diff from 'virtual-dom/diff';
+import patch from 'virtual-dom/patch';
+import createElement from 'virtual-dom/create-element';
+
+import * as l from 'opcut/lenses';
+
+
+const delegator = Delegator();
+const vhTypes = ['VirtualNode', 'Widget'];
+
+
+function vhFromArray(node) {
+ if (!node)
+ return [];
+ if (typeof node == 'string' || vhTypes.includes(node.type))
+ return node;
+ if (!Array.isArray(node))
+ throw 'Invalid node structure';
+ if (node.length < 1)
+ return [];
+ if (typeof node[0] != 'string')
+ return node.map(vhFromArray);
+ let hasProps = (node.length > 1 &&
+ typeof node[1] == 'object' &&
+ !Array.isArray(node[1]) &&
+ !vhTypes.includes(node[1].type));
+ let children = R.flatten(node.slice(hasProps ? 2 : 1).map(vhFromArray));
+ let result = hasProps ? vh(node[0], node[1], children) :
+ vh(node[0], children);
+
+ // disable SoftSetHook for input
+ if (result.tagName == 'INPUT' &&
+ result.properties &&
+ result.properties.value &&
+ typeof(result.properties.value) === 'object') {
+ result.properties.value = result.properties.value.value;
+ }
+
+ return result;
+}
+
+
+class VTreeRenderer {
+
+ constructor(el) {
+ this._el = el;
+ this._vtree = null;
+ }
+
+ render(vtree) {
+ let vt = vhFromArray(vtree);
+ if (vt.type == 'VirtualNode') {
+ if (this._vtree) {
+ let d = diff(this._vtree, vt);
+ patch(this._el.firstChild, d);
+ } else {
+ while (this._el.firstChild)
+ this._el.removeChild(this._el.firstChild);
+ this._el.appendChild(createElement(vt));
+ }
+ this._vtree = vt;
+ } else {
+ this._vtree = null;
+ while (this._el.firstChild)
+ this._el.removeChild(this._el.firstChild);
+ }
+ }
+
+}
+
+
+export class Renderer {
+
+ constructor(el, initState, vtCb) {
+ this.init(el, initState, vtCb);
+ }
+
+ init(el, initState, vtCb) {
+ this._state = null;
+ this._changeCbs = [];
+ this._vtCb = vtCb;
+ this._r = new VTreeRenderer(el || document.querySelector('body'));
+ if (initState)
+ this.change(R.identity, _ => initState);
+ }
+
+ view(...lenses) {
+ return R.view(R.apply(l.path, lenses), this._state);
+ }
+
+ set(lens, value) {
+ if (arguments.length < 2) {
+ value = lens;
+ lens = R.identity;
+ }
+ this.change(lens, _ => value);
+ }
+
+ change(lens, cb) {
+ if (arguments.length < 2) {
+ cb = lens;
+ lens = R.identity;
+ }
+ if (this._changeCbs.push(cb) > 1)
+ return;
+ let startingSubState = this.view(lens);
+ while (this._changeCbs.length > 0) {
+ this._state = R.over(l.path(lens), this._changeCbs[0], this._state);
+ this._changeCbs.shift();
+ }
+ if (!this._vtCb ||
+ (this._state && R.equals(startingSubState, this.view(lens))))
+ return;
+ this._r.render(this._vtCb(this._state));
+ bean.fire(this, 'render', this._state);
+ }
+
+}
+
+
+const defaultRenderer = new Renderer();
+export default defaultRenderer;
diff --git a/src_js/opcut/vt.js b/src_js/opcut/vt.js
index e199bbe..2754877 100644
--- a/src_js/opcut/vt.js
+++ b/src_js/opcut/vt.js
@@ -1,8 +1,8 @@
-
-
-export function main() {
- return ['div',
- 'application in development - ',
- ['a', {href: 'https://github.com/bozokopic/opcut'}, 'Github page']
- ];
-}
+
+
+export function main() {
+ return ['div',
+ 'application in development - ',
+ ['a', {href: 'https://github.com/bozokopic/opcut'}, 'Github page']
+ ];
+}
diff --git a/src_py/opcut/main.py b/src_py/opcut/main.py
index d6b42a9..917cbb2 100644
--- a/src_py/opcut/main.py
+++ b/src_py/opcut/main.py
@@ -1,80 +1,80 @@
-import sys
-import argparse
-import yaml
-import logging.config
-import urllib.parse
-import aiohttp.web
-import ssl
-import asyncio
-import contextlib
-
-from opcut import util
-import opcut.json_validator
-
-
-def main():
- args = _create_parser().parse_args()
-
- if args.log_conf_path:
- with open(args.log_conf_path, encoding='utf-8') as log_conf_file:
- log_conf = yaml.safe_load(log_conf_file)
- opcut.json_validator.validate(log_conf, 'opcut://logging.yaml#')
- logging.config.dictConfig(log_conf)
-
- util.run_until_complete_without_interrupt(async_main(args))
-
-
-async def async_main(args):
-
- addr = urllib.parse.urlparse(args.ui_addr)
-
- if addr.scheme == 'https':
- ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
- ssl_ctx.load_cert_chain(args.ui_pem_path)
- else:
- ssl_ctx = None
-
- app = aiohttp.web.Application()
- app.router.add_route('GET', '/',
- lambda req: aiohttp.web.HTTPFound('/index.html'))
- app.router.add_static('/', args.ui_path)
- app_handler = app.make_handler()
-
- srv = await asyncio.get_event_loop().create_server(
- app_handler, host=addr.hostname, port=addr.port, ssl=ssl_ctx)
-
- with contextlib.suppress(asyncio.CancelledError):
- await asyncio.Future()
-
- srv.close()
- await srv.wait_closed()
- await app.shutdown()
- await app_handler.finish_connections(0)
- await app.cleanup()
-
-
-def _create_parser():
- parser = argparse.ArgumentParser(prog='opcut')
- parser.add_argument(
- '--ui-addr', default='http://0.0.0.0:8080',
- metavar='addr', dest='ui_addr',
- help="address of listening web ui socket formated as "
- "'<type>://<host>:<port>' - <type> is 'http' or 'https'; "
- "<host> is hostname; <port> is tcp port number "
- "(default http://0.0.0.0:8080)")
- parser.add_argument(
- '--ui-path', default='web',
- metavar='path', dest='ui_path',
- help="web front-end path (default web)")
- parser.add_argument(
- '--ui-pem', default=None,
- metavar='path', dest='ui_pem_path',
- help="web front-end pem file path - required for https")
- parser.add_argument(
- '--log', default=None, metavar='path', dest='log_conf_path',
- help="logging configuration")
- return parser
-
-
-if __name__ == '__main__':
- sys.exit(main())
+import sys
+import argparse
+import yaml
+import logging.config
+import urllib.parse
+import aiohttp.web
+import ssl
+import asyncio
+import contextlib
+
+from opcut import util
+import opcut.json_validator
+
+
+def main():
+ args = _create_parser().parse_args()
+
+ if args.log_conf_path:
+ with open(args.log_conf_path, encoding='utf-8') as log_conf_file:
+ log_conf = yaml.safe_load(log_conf_file)
+ opcut.json_validator.validate(log_conf, 'opcut://logging.yaml#')
+ logging.config.dictConfig(log_conf)
+
+ util.run_until_complete_without_interrupt(async_main(args))
+
+
+async def async_main(args):
+
+ addr = urllib.parse.urlparse(args.ui_addr)
+
+ if addr.scheme == 'https':
+ ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ ssl_ctx.load_cert_chain(args.ui_pem_path)
+ else:
+ ssl_ctx = None
+
+ app = aiohttp.web.Application()
+ app.router.add_route('GET', '/',
+ lambda req: aiohttp.web.HTTPFound('/index.html'))
+ app.router.add_static('/', args.ui_path)
+ app_handler = app.make_handler()
+
+ srv = await asyncio.get_event_loop().create_server(
+ app_handler, host=addr.hostname, port=addr.port, ssl=ssl_ctx)
+
+ with contextlib.suppress(asyncio.CancelledError):
+ await asyncio.Future()
+
+ srv.close()
+ await srv.wait_closed()
+ await app.shutdown()
+ await app_handler.finish_connections(0)
+ await app.cleanup()
+
+
+def _create_parser():
+ parser = argparse.ArgumentParser(prog='opcut')
+ parser.add_argument(
+ '--ui-addr', default='http://0.0.0.0:8080',
+ metavar='addr', dest='ui_addr',
+ help="address of listening web ui socket formated as "
+ "'<type>://<host>:<port>' - <type> is 'http' or 'https'; "
+ "<host> is hostname; <port> is tcp port number "
+ "(default http://0.0.0.0:8080)")
+ parser.add_argument(
+ '--ui-path', default='web',
+ metavar='path', dest='ui_path',
+ help="web front-end path (default web)")
+ parser.add_argument(
+ '--ui-pem', default=None,
+ metavar='path', dest='ui_pem_path',
+ help="web front-end pem file path - required for https")
+ parser.add_argument(
+ '--log', default=None, metavar='path', dest='log_conf_path',
+ help="logging configuration")
+ return parser
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src_py/opcut/util.py b/src_py/opcut/util.py
index 4855bc5..3ceb843 100644
--- a/src_py/opcut/util.py
+++ b/src_py/opcut/util.py
@@ -1,40 +1,40 @@
-import contextlib
-import asyncio
-import sys
-
-
-def run_until_complete_without_interrupt(future):
- """Run event loop until future or coroutine is done
-
- Args:
- future (Awaitable): future or coroutine
-
- Returns:
- Any: provided future's result
-
- KeyboardInterrupt is suppressed (while event loop is running) and is mapped
- to single cancelation of running task. If multipple KeyboardInterrupts
- occur, task is canceled only once.
-
- """
- async def ping_loop():
- with contextlib.suppress(asyncio.CancelledError):
- while True:
- await asyncio.sleep(1)
-
- task = asyncio.ensure_future(future)
- if sys.platform == 'win32':
- ping_loop_task = asyncio.ensure_future(ping_loop())
- with contextlib.suppress(KeyboardInterrupt):
- asyncio.get_event_loop().run_until_complete(task)
- asyncio.get_event_loop().call_soon(task.cancel)
- if sys.platform == 'win32':
- asyncio.get_event_loop().call_soon(ping_loop_task.cancel)
- while not task.done():
- with contextlib.suppress(KeyboardInterrupt):
- asyncio.get_event_loop().run_until_complete(task)
- if sys.platform == 'win32':
- while not ping_loop_task.done():
- with contextlib.suppress(KeyboardInterrupt):
- asyncio.get_event_loop().run_until_complete(ping_loop_task)
- return task.result()
+import contextlib
+import asyncio
+import sys
+
+
+def run_until_complete_without_interrupt(future):
+ """Run event loop until future or coroutine is done
+
+ Args:
+ future (Awaitable): future or coroutine
+
+ Returns:
+ Any: provided future's result
+
+ KeyboardInterrupt is suppressed (while event loop is running) and is mapped
+ to single cancelation of running task. If multipple KeyboardInterrupts
+ occur, task is canceled only once.
+
+ """
+ async def ping_loop():
+ with contextlib.suppress(asyncio.CancelledError):
+ while True:
+ await asyncio.sleep(1)
+
+ task = asyncio.ensure_future(future)
+ if sys.platform == 'win32':
+ ping_loop_task = asyncio.ensure_future(ping_loop())
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(task)
+ asyncio.get_event_loop().call_soon(task.cancel)
+ if sys.platform == 'win32':
+ asyncio.get_event_loop().call_soon(ping_loop_task.cancel)
+ while not task.done():
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(task)
+ if sys.platform == 'win32':
+ while not ping_loop_task.done():
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(ping_loop_task)
+ return task.result()