Пользователь
16,0
рейтинг
13 августа 2014 в 12:46

Разработка → Окружение для разработки на aiorest (asyncio) + angular.js tutorial

В этой статье мы соберём вместе aiorest + jinja2 + angular.js + gulp.js + bower.js + nginx. В результате мы получим:
  • авто-перезапуск сервера при изменении python-кода и jinja-шаблонов
  • сборка, минификация и автоматическая пересборка при изменении клиентского js-кода
Начнём с главного — как пользоваться, а затем я подробнее опишу некоторые моменты реализации. Если вам легче читать код — вот ссылка на репу.

Установка


$ git clone git@github.com:imbolc/aiorest-angular-template.git 
$ cd aiorest-angular-template 
$ sudo npm install -g gulp bower 
$ npm install 
$ bower install 
$ ./bin/buildenv.py 
$ sudo ./bin/configure_nginx.py 

Добавляем в /etc/hosts:

127.0.1.1   aio-angular.l.com 

И запускаем сервер разработчика:

$ gulp 

Готово, теперь можно открывать в браузере: http://aio-angular.l.com/

Использование


Просто добавляйте обработчики в handlers.py, урлы — в urls.py, а клиентский код раскладывайте в папке client в удобной для вас структуре.

Конфигурация


aiorest не умеет ничего, кроме как отдавать json, поэтому, для отдачи статики мы будем использовать nginx, шаблон его конфига как и другие настройки находится в папке cfg. Там же находится файлик с питон-зависимостями pipreq.txt и конфиг для питон-части приложения __init__.py + local.py (последний в джанга-стайл перезаписывает специфичную для текущего сервера конфигурацию первого).

Хранить конфигурацию лучше в одном месте, поэтому клиентский конфиг мы так же генерируем на сервере, а потом подсовываем ангуляру в виде value (templates/client_config.js):

angular.module('app').value('cfg', << config|tojson|safe >>); 

Сборка js


Мы будем пользоваться мудростью этой статьи, которую я всячески рекомендую к прочтению: Real-World Best Practices for Building Angular.js Apps without Browserify or Require.js. Вкратце: require.js для ангуляра не подходит, browserify — подходит, но нам не нужна большая часть его функционала потому, что в ангуляре уже есть модули.

Итак, будем использовать gulp.js — современную замену grunt.js, он не использует промежуточных файлов, имеет более приятный синтаксис описания задач и уже аналогичную кучу готовых плагинов. Смотрим в gulpfile.js:

gulp.task('js', function () { 
    gulp.src(['client/app.js', 'client/**/*.js']) 
    .pipe(plumber()) 
    .pipe(sourcemaps.init()) 
    .pipe(concat('app.js')) 
    .pipe(ngAnnotate()) 
    .pipe(uglify()) 
    .pipe(sourcemaps.write()) 
    .pipe(gulp.dest('./static/build')); 
}); 

  • sourcemaps.init + sourcemaps.write добавляют в сборку source map который позволяет в консоли браузера видеть ошибки и лог-сообщения с нумерацией строк исходных файлов
  • concat — копирует все файлы в один
  • ngAnnotate — нельзя просто так взять и минифицировать ангуляр-код: Dependency Annotation
  • uglify — минификация
  • plumber — перехватывает ошибки в ходе всего этого, чем не даёт падать авто-персборке


Авто-перезапуск сервера


gulp.task('dev_server', shell.task([ 
    ('nodemon ./app.py --exec "var/env/bin/python"' + 
    ' --ext "py html" --ignore "static"') 
])); 

Дада, nodemon умеет перезапускать не только node.js :)

Серверный код


Приложение начинается в app.py:

import asyncio 
import aiorest 
 
import cfg 
import lib.logging 
import prepare_static 
import urls 
 
 
lib.logging.setup(cfg.LOG_FILE) 
prepare_static.client_config(urls=urls.CLIENT_URLS) 
prepare_static.render_html('index.html') 
 
 
server = aiorest.RESTServer(hostname='127.0.0.1') 
for url in urls.SERVER_URLS: 
    server.add_url(*url) 
 
 
loop = asyncio.get_event_loop() 
loop.run_until_complete(loop.create_server( 
    server.make_handler, '127.0.0.1', cfg.PORT)) 
try: 
    loop.run_forever() 
except KeyboardInterrupt: 
    pass 

Здесь мы настраиваем логирование, подготавливаем клиентский конфиг описанный выше. Затем рендерим пока единственный шаблон т.к. в нём есть некоторая логика:

<% if cfg.DEBUG %> 
    <script src="/static/bower/angular/angular.js"></script> 
    <script src="/static/bower/angular-resource/angular-resource.js"></script> 
<% else %> 
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.14/angular.min.js"></script> 
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.14/angular-resource.min.js"></script> 
<% endif %> 

И да, мы переопределили обозначения управляющих конструкций в jinja2 ({{ }} => << >>, {% %} => <% %>), т.к. ангуляр использует такие же.

Далее мы создаём aiorest-сервер, добавляем урлы из файлика urls.py:

import handlers 
 
 
SERVER_URLS = [ 
    ('GET', '/hello',        handlers.hello), 
    ('GET', '/hello/{name}', handlers.hello), 
] 
 
CLIENT_URLS = { 
    'hello': '/api/hello/:name', 
} 

CLIENT_URLS — это аякс урлы которые мы будем использовать в браузерном кодe. А хендлеры для серверных урлов находятся в файлике handlers.py:

def hello(request):
    name = request.matchdict.get('name', 'world')
    message = 'Hello, {}!'.format(name)
    return {'message': message}

Angular-код


Описывать hello world на ангуляре пожалуй не буду :) Единственное, обращу внимание на использование клиентского конфига, который мы готовили выше (client/hello/hello.svc.js):

angular.module('app') 
    .service('HelloSvc', function ($resource, cfg) { 
        this.fetchGreeting = function () { 
            return $resource(cfg.urls.hello).get.apply(this, arguments); 
        }; 
    }); 

@Imbolc
карма
24,0
рейтинг 16,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (6)

  • 0
    Очень жду mainstream web-фрэймворка на asyncio
    • 0
      Ну по словам инсайдера svetlov в aiorest как раз и нарабатывается то, что пойдёт потом в официальный aiohttp.
  • +1
    Если что, aiorest 0.2.0 сломал обратную совместимость: github.com/aio-libs/aiorest/pull/18
    Так было надо.
    • 0
      Верными шагами движетесь к class-based handlers, быстрее бы уже :)
  • 0
    gulp.js — современную замену bower.js

    Я не силён в мире js, но, наверное, всё же `grunt.js` должен быть вместо `bower.js`?
    • 0
      Верно, спасибо.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.