Debian, AngularJS, Django, Python

Делюсь опытом в описанных технологиях. Блог в первую очередь выполняет роль памяток для меня самого.

Перекат на OpenSUSE с возвратом к Debian'у

Причины

Долгое время на рабочем ноуте у меня стояла Windows 7. Работала она там несколько лет и всем, в общем-то, устраивала, однако, со временем я пришёл к мысли, что так дальше продолжать нельзя и для повышения уровня следует полностью перейти на Linux, не ограничиваясь только поддержкой серверов, на которых стоит ПО, которое поддерживает контора, в которой я работаю (хотел написать "моя", но вспомнил, что учредитель не я).

Первым делом я попробовал поставить на ноутбук Ubuntu. "Раз уж она для серверов не очень, то может быть, будет работать на лэптопе?" - подумлал я, скачал установочный образ и приступил к установке.

Прямо тут и начались проблемы. Напоминаю, на ноутбуке стояла Windows 7, которая при создании разделов использует MBR. Однако, для загрузки системы в UEFI необходимо наличие раздела EFI на жёстком диске, а с MBR это сделать невозможно. Опуская подробности скажу, что даже GParted выдавал информацию о том, что диск не имеет никакой разметки.

Я перенёс все данные на съёмный накопитель, а особо ценные продублировал в облаке, и приступил к дальнейшим поискам решения. Поиски завершились получением следующего знания:

Для корректной работы Linux на машине с UEFI необходимо, чтобы таблица разделов жёсткого диска была организована через GPT, а не MBR.

Удалил все разделы, пересоздал таблицу и успешно установил систему. Дальше начались будни. Через несколько дней я почувствовал, что работа Ubuntu и её навязчивый сервис вызывает во мне отторжение. Взял ноутбук домой и успешно установил Debian 7 Wheezy. Добавил репозитории NodeJS, PostgreSQL и некоторых других проектов. Проблема пришла, откуда не ждали. Не было звука. После многочисленных опытов скачал исходные коды с сайта производителя, скомпилировал из них драйверы и добавил их в ядро. Звук в наушниках появился, в динамиках - нет. OOOOOOK.

Пришло время подключиться к AD. Помучив помощника админа, ноут и систему, я так и не смог довести конфиги до рабочего состояния, поэтому шара фирмы была для меня закрыта. Тут вдруг наступили праздники, и я решил, что можно будет не умирать от безделья, а попробовать пожить в OpenSUSE, благо один знакомый уже давно пользуется этим дистрибутивом и, теоретически, к нему можно будет обратиться за помощью.

Удобства

Что порадовало в этом дистрибутиве:

  • Звук "из коробки" - неудивительно, ядро свежее и содержит все необходимые драйверы
  • Большинство пакетов, в т.ч. всякая проприетарщина - в стандартных репозиториях
  • Почти официальный репозиторий драйверов nVidia. На рабочем ноуте я никогда не играл, но сам факт наличия таких репок порадовал.
  • "Скользящие" обновления. По-моему, это большой шаг не только для OpenSUSE, но и для Ubuntu. Кратко суть: при формировании дистрибутива последняя версия Gnome была, например, 3.11. Всё время поддержки дистрибутива версии 7.0 она не будет меняться, будут выходить лишь патчи и исправления уязвимостей. При скользящих же обновлениях в репозиторий помещается последняя стабильная версия программы, и обновить Gnome до новой версии довольно просто, нужно лишь выполнить обычное обновление системы. В итоге имеем систему в состоянии Stable, но со свежими версиями пакетов.

Что не нравится

Тем не менее, некоторые вещи мне не очень понравились (ну ещё бы)

  • Во-первых, нет жёстких зависимостей между пакетами. После удаления какой-либо программы куча пакетов, которые ставились вместе с ней, остаётся в системе. Хотя некоторые пользователи данного дистрибутива позиционируют это как огромное преимущество. Я пока не вкусил всех прелестей, но мне уже не нравится.
  • Во-вторых, проблемы с кодеками. Да, тут с этим сложнее, чем в Debian. Там было достаточно прописать в свойствах репозитория non-free, и можно было ставить кодеки mp3 и h264. Все советуют использовать репозиторий Packman. Подключил, обновил полсистемы, никакого результата. Проблему с mp3 решил, h264 играет только системный плеер, VLC мимо кассы.
  • В-третьих, ещё неизвестно, будет ли работать здесь AD. До выхода на работу ещё 4 дня, и проверить не смогу.

Возможно, мне этот дистрибутив придётся по душе и я на нём пока остановлюсь. Посмотрим, что нам принесёт Debian 8, который сейчас в стадии заморозки.

UPD: к домену так и не смог подключиться, также при появлении уведомлений Skype приостанавливается воспроизведение музыки. Работа с виртуальными окружениями Python значительно хуже, чем в Debian. Подумываю о переустановке винды, т.к. работать с шарой моей конторы периодически приходится, а сейчас к ней доступа нет.

Ошибка при запуске Eclipse Luna в Debian

Проблема

Я на работе окончательно переехал на Debian. После двух дней ненависти к Ubuntu понял, что лучше иметь старые проверенные пакеты, чем новые и постоянно глючащие. Как обычно, после установки системы поставил JRE:

apt-get update && apt-get install openjdk-7-jre -y

Скачал и распаковал последнюю стабильную версию Eclipse с официального сайта. Попробовал запустить:

./eclipse

Получил кучу ошибок, среди которых была такая: # C [libgdk-x11-2.0.so.0+0x5173f] gdk_display_open+0x3f.

Решение

Решение оказалось на редкость простым. Нужно импортировать в окружение переменную SWT_GTK3=0, тогда всё будет работать. Прописал в конце файла /etc/profile:

export SWT_GTK3=0

Чтобы не перезагружать систему, сделал то же самое для текущего пользователя, т.е. выполнил указанную выше команду. Всё работает!

Django и $resource

Суть проблемы

Решил использовать для реализации RESTful API своего приложения такой модуль Angular, как angular-resource. Проблем с установной и подключением, как обычно, не было. Создал свой сервис:

(function (A, U) { //U === undefined
    "use strict";
        
    A.module('DesktopApplication').factory('Category', function ($resource) {
        var instance = {
            selectedRow: U, //Используется в моём проекте для обмена данными между контроллерами,
                            //не является обязательным элементом
            resource: $resource('/category/rows/:id', {id: '@id'})
        };
        return instance;        
    });
}(this.angular));

Казалось бы, всё должно работать. И ведь правда, вот такое работает (GET-запрос на получение данных):

//Где-то в коде, использующем сервис Category
Category.resource.query(function (items){
    $scope.rows = items;
});

А вот такое уже нет:

//Чуть ниже
Category.save({'id': 4, 'name': 'Username'}, function (result) {
    $scope.success = result.success;
});

Проблема оказалась в том, что даже явно указав слэш в конце строки ресурса, я получил POST-запросы, идущие к адресам без слэша, и Django такие запросы тут же банит. Начался поиск и чтение StackOverflow.

Решение

Решение оказалось довольно простым. Нужно указать в настройках приложения, что удалять концевой слэш для URL в AJAX-запросах не надо (а по-умолчанию включено). Кроме того, для корректной работы запросов методом PUT и DELETE нужно дополнительно конфигурировать $http. Иначе декоратор @csrf_protect из комплекта Django будет резать такие запросы. С учётом вышесказанного, скрипт настройки приложения стал выглядеть так (для сокращения размера выброшены прочие модули, которые я на самом деле использую в своём проекте):

(function(A) {
    "use strict";
    A.module('DesktopApplication', [ 'ngCookies', 'ngResource')
     .config(function ($interpolateProvider, $resourceProvider) {
         $interpolateProvider.startSymbol('{$');
         $interpolateProvider.endSymbol('$}');
         $resourceProvider.defaults.stripTrailingSlashes = false;
      })
     .run(function ($http, $cookies) {
          $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
          $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
          $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
     });
}(this.angular, this.jQuery));

Вывод дат в Django

Проблема с датами

Часто приходится выводить даты в JSON или в шаблон. Начал искать решения, позволяющие обойти вечные ошибки при преобразовании, в итоге сделал для себя небольшую памятку:

item.pub_date = child.date_of_birth.strftime('%d.%m.%Y')

В таком виде данные без проблем выводятся в шаблонах и при отправке через HttpResponse.

Как, например, вывожу данные о новости:

for item in news:
    rows.append({
        id: item.id,
        "pub_date": '%s в %s' % (item.pub_date.strftime('%d.%m.%Y'), item.pub_date.strftime('%H:%M'))       
    })
return render_to_response('detail.html', RequestContext(request, {'rows': rows}))

AngularJS, Django и POST

Введение

Начал писать приложение на Angular. Стало сразу две проблемы.

  • Как передавать CSRF-Token Django при каждом запросе?
  • Почему данные, отправляемые через POST, не видны в объекте request Django?
  • CSRF-Token

    Для CSRF-Token нашлось очень простое решение. Достаточно подключить модуль angular-cookies и при старте приложения задать параметры сервиса $http. С учётом того, что скобки для Angular у меня заменены с {{ и }} на {$ и $}, заготовка главного модуля приложения у меня выглядит так:

    (function(A){
        "use strict";
        A.module('DesktopApplication', ['ngCookies']).config(function($interpolateProvider){
            $interpolateProvider.startSymbol('{$');
            $interpolateProvider.endSymbol('$}');
    
        }).run(['$http', '$cookies', function ($http, $cookies){
            $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
            $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
            }]);
    }(this.angular));

    Естественно, на сервере при отдаче шаблонов нужно, чтобы соответствующие Cookies были установлены. Делается очень просто:

    return render_to_response('template.html', RequestContext(request))

    Angular и POST

    Отдельной болью стала отправка данных на сервер через POST. Было потрачено несколько часов на чтение Тостера, официальной документации по Angular и Django, пока наконец я не наткнулся на решение одного из пользователей StackOverflow. Суть решения такова: Angular при отправке данных не превращает их в ожидаемый на бекэнде формат, а передаёт их как строку.

    Допустим, у нас имеется такой объект:

    userdata = {
        login: 'userlogin',
        password: '123456'
    };

    Попробуем отправить эти данные:

    $http.post('/login/', userdata).success(function(data){
        //Что-то делаем с полученными в ответ данными
    });

    В консоли Firebug будет выведена строка запроса:

    "{"login": "userlogin", "password": "123456"}"

    Попробуем получить эти данные:

    def login_view(request):
        if request.method == 'POST' and request.is_ajax():
            username = request.POST.get('login', '')
            password = request.POST.get('password', '')

    Мы ожидаем, что в третьей и четвёртой строках будет примерно следующее:

    username = 'username'
    password = '123456'

    На самом деле этого не происходит, т.к. данные должны быть переданы в таком виде:

    login=userlogin&password=123456

    Проблема в том, что в текущей версии Angular (1.3.4 на момент написания статьи) нет функции для приведения объекта в такой вид. Поэтому автор посоветовал использовать старый добрый jQuery, точнее его функцию param:

    $http.post('/login/', $.param(userdata)).success(function(data){
        //Обработка ответа сервера
    });

    Вот такой код будет работать. Так же пользователи предлагали самописные функции, выполняющие ту же работу, но я советую использовать проверенные решения. Кроме этого обязательно присутствие в заголовке пост параметра Content-Type, равного application/x-www-form-urlencoded, соответствующий код приведён выше.

    Debian, OpenSSL и ГОСТ

    Введение

    В статье рассказывается, как в Debian 7 включить поддержку шифрования по ГОСТ для OpenSSL. Все операции, как обычно, от имени root. Статью написал в связи с тем, что в интернете всюду перепечатаны две инструкции - первая предлагает поставить Gentoo и собирать OpenSSL из исходников, а вторая - сломать OpenSSL так, чтобы он поддерживал только ГОСТ.

    Активные действия

    Единственный пакет, который нужно поставить - openssl, но он уже и так должен быть в системе. Не мешает также обновить его, если он устарел, т.к. OpenSSL - один из самых забагованных и дырявых пакетов в *nix-системах вообще.

    apt-get update && apt-get dist-upgrade -y
    apt-get install openssl

    Переходим в каталог /etc/ssl/ и открываем на запись файл openssl.conf. Мне нравится использовать vim, новичкам же советую открывать файлы с помощью nano.

    cd /etc/ssl/
    vim openssl.conf

    В самое начало файла, перед первой секцией, пишем следующее:

    openssl_conf = openssl_def

    Получится так:

    #
    # OpenSSL example configuration file.
    # This is mostly being used for generation of certificate requests.
    #
    
    # This definition stops the following lines choking if HOME isn't
    # defined.
    
    HOME            = .
    RANDFILE        = $ENV::HOME/.rnd
    
    # Extra OBJECT IDENTIFIER info:
    #oid_file       = $ENV::HOME/.oid
    oid_section     = new_oids
    
    # To use this configuration file with the "-extfile" option of the
    # "openssl x509" utility, name here the section containing the
    # X.509v3 extensions to use:
    # extensions        =
    # (Alternatively, use a configuration file that has only
    # X.509v3 extensions in its main [= default] section.)
    
    openssl_conf = openssl_def
    
    [ new_oids ]
    
    # We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
    # Add a simple OID like this:
    # testoid1=1.2.3.4

    Теперь в конец файла дописываем:

    [openssl_def]
    engines = engine_section
    
    [engine_section]
    gost = gost_section
    
    [gost_section]
    soft_load=1
    default_algorithms = ALL
    CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet

    После сохранения проверяем доступность алгоритмов ГОСТ в системе:

    Раз:

    openssl list-message-digest-algorithms | grep gost
    gost-mac
    md_gost94
    gost-mac
    md_gost94

    Два:

    openssl list-cipher-algorithms | grep gost
    gost89
    gost89
    gost89-cnt

    Написано на основе багрепорта, но, в отличие от инструкции на сайте одного известного отечественного производителя средств шифрования, всё работает.

    Grunt

    Введение

    Надоело мне как-то перепроверять все CSS-файлы при изменении дизайна. Поискав немного, я остановился на LESS. Вместо того, чтобы управлять одним огромным файлом styles.css, LESS позволяет создать множество мелких файлов, каждый из которых отвечает за свою часть общей таблицы стилей. Однако, собирать всё вручную мне было лень, поэтому я решил для своего проекта использовать Grunt. Для него есть множество модулей, которые способны сильно облегчить жизнь Web-разработчика, в частности, компилировать множество LESS файлов автоматически, собирать и сжимать JavaScript'ы и т.д.

    В данной статье пойдёт рассказ о том, зачем нужен Grunt, показаны примеры его использования и базовой настройки, а также мимоходом захвачено использование LESS для разработки каскадных таблиц стилей.

    Для проекта, над которым я работаю, было принято решение создать в корневом каталоге папку /less, куда поместить следующие файлы:

    • bootstrap.less - объединяет в себе все остальные файлы. Изменяя содержимое этого файла легко управлять результирующей таблицей стилей.
    • colors.less - содержит переменные, описывающие цвета, используемые на страницах, например, @red: #FF0000;.
    • fonts.less - описывает используемые на страницах шрифты. В своём проекте я решил использовать символьный шрифт FontAwesome вместо того, чтобы рисовать иконки.

    а также по одному файлу на каждый отдельный блок, например, визуальный компонент или целую страницу.

    Установка

    Целевой системой сегодня будет не Debian, а Windows 8. Установка всех средств в Debian, кроме NodeJS, производится аналогично - командами в консоли. Если хотите NodeJS в Debian Wheezy - прочитайте вот эту статью.

    Скачиваем установщик с сайта проекта NodeJS и запускаем, при установке не забываем указать, что нам также понадобится менеджер пакетов (Node Package Manager - NPM).

    Настройка проекта для использования Grunt

    Существует два способа установки Grunt'а - глобально и локально, в проект. Я рекомендую сам Grunt установить глобально, а необходимые модули - в папку проекта. Её всегда можно добавить в исключения вашей СКВ.

    Запускаем командную строку от имени администратора и вводим следующие команды:

    npm install grunt -g
    npm install grunt-cli -g

    Первый пакет - это собственно Grunt, второй - утилиты командной строки, позволяющие работать с ним. Ключ -g означает, что установленные пакеты должны быть доступны глобально.

    Перейдём в каталог нашего проекта (вместо %project_root_folder% подставьте нужное) и инициализируем его для использования Grunt'а:

    cd %project_root_folder%
    grunt init

    Будет задано несколько вопросов, касающихся проекта, в частности, имя автора, версия, лицензия и т.д. На основе ответов будет составлен файл package.json, который при необходимости всегда можно изменить. например, в моём проекте он выглядит вот так:

    {
        "name": "tester",
        "version": "0.0.0",
        "description": "Система тестирования",
        "main": "index.html",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": "Dunaevsky Maxim",
        "license": "MIT",
        "devDependencies": {
        "grunt": "^0.4.5",
        "grunt-contrib-concat": "^0.5.0",
        "grunt-contrib-less": "^0.11.4",
        "grunt-contrib-uglify": "^0.5.1"
      }
    }

    Теперь нам нужно будет установить необходимые модули, я ограничусь всего тремя:

    • concat - собирает несколько указанных файлов в один.
    • less - компилирует LESS-файлы в каскадные таблицы стилей CSS.
    • uglify - оптимизирует и сжимает JavaScript-код.

    Установка производится очень просто:

    npm install grunt-contrib-concat --save-dev
    npm install grunt-contrib-less --save-dev
    npm install grunt-contrib-uglify --save-dev

    Ключ --save-dev означает, что пакеты должны быть также прописаны в файле package.json.

    Пакеты установлены, самое время дать Grunt'у задание - скомпилировать LESS-файлы, склеить вместе все JavaScript'ы из каталога /src/ и сжать итоговый файл. Для этого нам понадобится файл Gruntfile.js. Обратите внимание, первая буква в названии файла должна быть большой. Создать его следует в корневом каталоге проекта.

    Рассмотрим Gruntfile.js, которым пользуюсь я сам:

    "use strict";
    module.exports = function(grunt) {
        grunt.initConfig({
            pkg: grunt.file.readJSON('package.json'),
            less: {
                dev: {
                    files: {
                        'css/styles.css': 'less/bootstrap.less'
                    }
                },
                min: {
                    options: {
                        ieCompat: true,
                        compress: 2,
                        optimization: true
                    },
                    files: {
                        'css/styles.min.css': 'less/bootstrap.less'
                    }
                }
            },
            concat: {
                js: {
                    src: [
                        'src/*.js'
                    ],
                    dest: 'js/ui.js'
                }
            },
            uglify: {
                build: {
                    src: 'js/ui.js',
                    dest: 'js/ui.min.js'
                }
            }
        });
    
        grunt.loadNpmTasks('grunt-contrib-less');
        grunt.loadNpmTasks('grunt-contrib-concat');
        grunt.loadNpmTasks('grunt-contrib-uglify');
    
        grunt.registerTask('default', ['less:dev', 'concat:js', 'uglify']);
    };

    Первым делом определяются настройки для задач, а также указывается, откуда брать информацию о проекте (файл package.json).

    Задание для сборки CSS из LESS-файлов содержит две секции - настройки для разработки (dev) и для релиза (min), когда важен маленький размер файла и оптимизация кода. В обоих заданиях указано, что нужно на основе файла bootstrap.less из каталога less собрать файл styles.css в каталоге css.

    Задание для concat'а небольшое - ему нужно всего навсего взять все js-файлы в каталоге src и склеить в один - ui.js, положив его в каталог js.

    Самое последнее задание - оптимизация и сжатие получившегося при склеивании файла с помощью утилиты uglify.

    В конце файла производится загрузка необходимых модулей и выполнение их в порядке перечисления аргументов функции grunt.registerTask. Изменяя порядок следования аргументов можно управлять процессом сборки.

    Закончив редактирование файла Gruntfile.js, запустите командую строку в текущем каталоге и выполните команду grunt. В случае ошибки в конфигурации или одном из файлов проекта будет выдано соответствующее сообщение. В противном случае вывод на экран будет выглядеть примерно так:

    Running "less:dev" (less) task
    File css/styles.css created: 0 B → 251 B
    
    Running "concat:js" (concat) task
    File js/ui.js created.
    
    Running "uglify:build" (uglify) task
    
    Done, without errors.