From a10ed93898d88cd4414fa581b23f0397f463129e Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Fri, 23 Sep 2016 17:02:38 +0100 Subject: [PATCH] moving message return to ajax view --- .editorconfig | 6 +- aiohttp_devtools/cli.py | 4 +- aiohttp_devtools/start/main.py | 13 ++- aiohttp_devtools/start/template/app/routes.py | 3 +- .../template/app/templates/messages.jinja | 9 +- aiohttp_devtools/start/template/app/views.py | 94 ++++++++++++++----- .../start/template/requirements.txt | 20 ++-- .../start/template/static/message_display.js | 30 ++++++ 8 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 aiohttp_devtools/start/template/static/message_display.js diff --git a/.editorconfig b/.editorconfig index cc82dbab..d67a95c9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,10 +5,14 @@ root = true end_of_line = lf insert_final_newline = true +[*.txt,*.js,*.css,*.jinja] +indent_style = space +indent_size = 2 + [*.py] indent_style = space indent_size = 4 -line_length=120 +line_length = 120 [Makefile] indent_style = tab diff --git a/aiohttp_devtools/cli.py b/aiohttp_devtools/cli.py index 3b867f7d..99132969 100644 --- a/aiohttp_devtools/cli.py +++ b/aiohttp_devtools/cli.py @@ -51,8 +51,8 @@ def runserver(**config): @click.argument('path', type=_path_type, required=True) @click.argument('name', required=False) @click.option('--template-engine', type=click.Choice(Options.TEMPLATE_ENG_CHOICES), default=Options.TEMPLATE_ENG_JINJA2) -@click.option('--session', type=click.Choice(Options.SESSION_CHOICES), default=Options.SESSION_SECURE) -@click.option('--database', type=click.Choice(Options.DB_CHOICES), default=Options.DB_PG_SA) +@click.option('--session', type=click.Choice(Options.SESSION_CHOICES), default=Options.NONE) +@click.option('--database', type=click.Choice(Options.DB_CHOICES), default=Options.NONE) def start(*, path, name, template_engine, session, database): """ Create a new aiohttp app. diff --git a/aiohttp_devtools/start/main.py b/aiohttp_devtools/start/main.py index 67f8396a..cd18b802 100644 --- a/aiohttp_devtools/start/main.py +++ b/aiohttp_devtools/start/main.py @@ -47,9 +47,12 @@ def __init__(self, *, path: str, name: str, template_engine: str, session: str, self.files_created = 0 self.ctx = { 'name': name, - 'template_engine': {'is_' + o: template_engine == o for o in Options.TEMPLATE_ENG_CHOICES}, - 'session': {'is_' + o: session == o for o in Options.SESSION_CHOICES}, - 'database': {'is_' + o: database == o for o in Options.DB_CHOICES}, + 'template_engine': {'is_' + o.replace('-', '_'): template_engine == o + for o in Options.TEMPLATE_ENG_CHOICES}, + 'session': {'is_' + o.replace('-', '_'): session == o + for o in Options.SESSION_CHOICES}, + 'database': {'is_' + o.replace('-', '_'): database == o + for o in Options.DB_CHOICES}, } self.generate_directory(TEMPLATE_DIR) display_path = self.project_root.relative_to(Path('.').resolve()) @@ -74,8 +77,8 @@ def generate_file(self, p: Path): return if p.name == 'requirements.txt': - lines = set(filter(bool, text.split('\n'))) - text = '\n'.join(sorted(lines)) + packages = {p.strip() for p in text.split('\n') if p.strip()} + text = '\n'.join(sorted(packages)) elif p.suffix == '.py': # helpful when debugging: print(text.replace(' ', '·').replace('\n', '⏎\n')) for regex, repl in PY_REGEXES: diff --git a/aiohttp_devtools/start/template/app/routes.py b/aiohttp_devtools/start/template/app/routes.py index 51617656..ab1fdd40 100644 --- a/aiohttp_devtools/start/template/app/routes.py +++ b/aiohttp_devtools/start/template/app/routes.py @@ -1,6 +1,7 @@ -from .views import index, messages +from .views import index, messages, message_data def setup_routes(app): app.router.add_get('/', index, name='index') app.router.add_route('*', '/messages', messages, name='messages') + app.router.add_get('/messages/data', message_data, name='message-data') diff --git a/aiohttp_devtools/start/template/app/templates/messages.jinja b/aiohttp_devtools/start/template/app/templates/messages.jinja index 3fd6d0e8..27c7fa35 100644 --- a/aiohttp_devtools/start/template/app/templates/messages.jinja +++ b/aiohttp_devtools/start/template/app/templates/messages.jinja @@ -20,13 +20,8 @@

Messages:

- +
+ {% endblock %} {% endraw %} {% endif %} diff --git a/aiohttp_devtools/start/template/app/views.py b/aiohttp_devtools/start/template/app/views.py index b23238df..6a30894e 100644 --- a/aiohttp_devtools/start/template/app/views.py +++ b/aiohttp_devtools/start/template/app/views.py @@ -3,8 +3,9 @@ from aiohttp import web from aiohttp.hdrs import METH_POST -# {% if template_engine.is_jinja2 %} from aiohttp.web_exceptions import HTTPFound +from aiohttp.web_reqrep import json_response +# {% if template_engine.is_jinja2 %} from aiohttp_jinja2 import template # {% endif %} @@ -26,27 +27,41 @@ async def index(request): 'message': "Success! you've setup a basic aiohttp app.", } - # {% else %} +BASE_PAGE = """\ +{title} + + +
+

{title}

+ {content} +
+""" + async def index(request): """ This is the view handler for the "/" url. + **Note: returning html without a template engine like jinja2 is ugly, no way round that.** + :param request: the request object see http://aiohttp.readthedocs.io/en/stable/web_reference.html#request :return: aiohttp.web.Response object """ - content = """\ - - - {title} - - - -

{title}

-

{message}

-""" - return web.Response(text='hello', content_type='text/html') + # Note: in the name of brevity we return stripped down html, + # this works fine on chrome but shouldn't be used in production, + # the tag is required to activate aiohttp-debugtoolbar + ctx = dict( + title=request.app['name'], + styles_css_url=request.app['static_url'] + '/styles.css', + content="""\ +

Success! you've setup a basic aiohttp app.

+

To demonstrate a little of the functionality of aiohttp this app implements a very simple message board.

+ + View and add messages + """.format(message_url=request.app.router['messages'].url()) + ) + return web.Response(text=BASE_PAGE.format(**ctx), content_type='text/html') # {% endif %} @@ -66,7 +81,7 @@ async def process_form(request): new_message['username'] = new_message['username'].replace('|', '') with MESSAGE_FILE.open('a') as f: now = datetime.now().isoformat() - f.write('{username}|{timestamp:%Y-%m-%d %H:%M}|{message}'.format(timestamp=now, **new_message)) + f.write('{username}|{timestamp}|{message}'.format(timestamp=now, **new_message)) raise HTTPFound(request.app.router['messages'].url()) @@ -80,6 +95,47 @@ async def messages(request): else: form_errors = None + # {% if template_engine.is_jinja2 %} + return { + 'title': 'Message board', + 'form_errors': form_errors, + 'messages': messages, + } + # {% else %} + ctx = dict( + title=request.app['name'], + styles_css_url=request.app['static_url'] + '/styles.css', + content="""\ +

Add a new message:

+
+ {form_errors} +

+ + + + +

+ +
+ +

Messages:

+
+ """.format( + message_url=request.app.router['messages'].url(), + message_data_url=request.app.router['message-data'].url(), + message_display_js_url=request.app['static_url'] + '/message_display.js', + form_errors=form_errors and '
{}
'.format(form_errors) + ) + ) + return web.Response(text=BASE_PAGE.format(**ctx), content_type='text/html') + # {% endif %} + + +async def message_data(request): + """ + As an example of aiohttp providing a non-html response, we load the actual messages for the "message" above + via ajax using this endpoint to get data. see static/message_display.js for details of rendering. + """ messages = [] if MESSAGE_FILE.exists(): lines = MESSAGE_FILE.read_text().split('\n') @@ -89,12 +145,4 @@ async def messages(request): username, ts, message = line.split('|', 2) ts = '{:%Y-%m-%d %H:%M:%S}'.format(datetime.strptime(ts, '%Y-%m-%dT%H:%M:%S.%f')) messages.append({'username': username, 'timestamp': ts, 'message': message}) - # {% if template_engine.is_jinja2 %} - return { - 'title': 'Message board', - 'form_errors': form_errors, - 'messages': messages, - } - # {% else %} - raise NotImplementedError() - # {% endif %} + return json_response(messages) diff --git a/aiohttp_devtools/start/template/requirements.txt b/aiohttp_devtools/start/template/requirements.txt index 2f406a18..0f624510 100644 --- a/aiohttp_devtools/start/template/requirements.txt +++ b/aiohttp_devtools/start/template/requirements.txt @@ -1,14 +1,22 @@ -{# This file is special: lines are made unique and sorted before the new requirements.tzt file is created #} +{# This file is special: lines are made unique, stripped and sorted before the new requirements.txt file is created #} + +aiohttp==1.0.2 + {% if template_engine.is_jinja2 %} -aiohttp-jinja2==0.8.0 + aiohttp-jinja2==0.8.0 {% endif %} {% if session.is_vanilla %} -aiohttp-session==0.5.0 + aiohttp-session==0.5.0 {% elif session.is_secure %} -aiohttp-session[secure]==0.5.0 + aiohttp-session[secure]==0.5.0 {% elif session.is_redis %} -aiohttp-session[aioredis]==0.5.0 + aiohttp-session[aioredis]==0.5.0 {% endif %} -{# TODO: others #} +{% if database.is_postgres_sqlalchemy or database.is_postgres_raw %} + aiopg==0.11.0 + {% if database.is_postgres_sqlalchemy %} + SQLAlchemy==1.0.15 + {% endif %} +{% endif %} diff --git a/aiohttp_devtools/start/template/static/message_display.js b/aiohttp_devtools/start/template/static/message_display.js new file mode 100644 index 00000000..129268d3 --- /dev/null +++ b/aiohttp_devtools/start/template/static/message_display.js @@ -0,0 +1,30 @@ +// some very basic javascript to populate the message board +// for simplicity this has not external requirements + +var messages = document.getElementById('messages'); + +function request_state_changed() { + if (this.readyState != 4) { + return; + } + if (this.status != 200) { + console.warn('error getting messages:', r); + alert('error getting messages, response:' + this.status); + return; + } + var data = JSON.parse(this.responseText); + if (data.length == 0) { + messages.innerHTML = 'No messages available.' + } else { + messages.innerHTML = ''; + } +} + +var r = new XMLHttpRequest(); +r.open('GET', messages.getAttribute('data-url'), true); +r.onreadystatechange = request_state_changed; +r.send('');