Несколько моих плагинов для Sublime Text 2

Почти месяц назад написал несколько плагинов для прекрасного редактора SublimeText 2, который я активно пользую каждый день, а написать о них сюда все никак не получалось. Плагины все написаны на Python и выложены на Github, там же можно найти подробную инструкцию по установке, а здесь я просто напишу пару слов о каждом.

ST2-Converter

Идею для данного плагина я позаимствовал у AnyEdit который использую в Eclipse. Плагин добавляет дополнительную функциональность в контекстное меню редактора и реализует некоторые рутинные операции со стоками. Краткий обзор некоторой функциональности:

# Преобразование символов в соответствующие HTML-сущности и обратно
<html> преобразуется в &lt;html&gt;

# Преобразование из CamelCase в under_scores и обратно
someFunctionName преобразуется в some_function_name

# Преобразование символов в Unicode обозначения
Unicode преобразуется в \u0055\u006E\u0069\u0063\u006F\u0064\u0065

# Кодирование и декодирование Base64
Test (Base64: VGVzdA==)

# Подсчет MD5 хешей
Test (MD5: 0cbc6611f5540bd0809a388dc95a615b)

# Преобразование меток времени из формата, принятого в Unix,
# в понятный человеку формат и обратно
2011-11-15 12:52 преобразуется в 1321361520.0

ST2-Brainfuck

Полнофункциональный интерпретатор эзотерического языка Brainfuck внутри Sublime Text 2. Поддерживает все команды языка включая ввод данных извне. К плагину прилагается папка с несколькими примерами кода.

ST2-WhatTheCommit

Давно хотелось использовать рандомные сообщения с сайта whatthecommit.com и вот результатом стал этот плагин. Функциональность минимально, просто делать коммиты в существующий GIT репозиторий, используя в качестве комментариев сообщения с данного сайта.

Raw GSM 6.10 Audio Stream проигрывание online

Столкнувшись с форматом Raw GSM 6.10 audio stream (.gsm) первый раз я решил пойти по пути наименьшего сопротивления и найти готовое решение для проигрывания таких файлов онлайн.

Наткнувшись на github на единственный вменяемый плеер под названием WavPlayer, поддерживающий формат Raw GSM 6.10 Audio Stream, я ужаснулся от его интерфейса да и написан он был на языке haXe. К счастью есть такой замечательный проект audio.js который представляет из себя симпатичный frontend JS плеер с backend кодеком на Flash.

Довольно быстро возникло желание совместить эти два проекта. Бегло осмотрев архитектуру плеера и api кодека, я отбросил всякое желание менять JS код плеера для совместимости с WavPlayer. Остался вариант переписать кодек, этим я и занялся.

До знакомства с WavPlayer я и понятия не имел о языке haXe на котором он написан. Посмотрев исходники кодека и почитав пару заметок на сайте языка haXe я довольно быстро освоил синтаксис который сильно напоминает ActionScript. Посидев несколько вечеров я закончил переписывание api, попутно исправив пару багов на которые автор явно забил, а может они проявляются только при проигрывании файлов gsm. Результатом всех усилий стал форкнутый проект, который можно посмотреть на github.

Сборка

Для самостоятельной сборки кодека необходимо установить haXe, скачать исходники кодека с github и скомпилировать командой:

haxe audiojs.hxml

В результате получится файл с названием audiojs.swf реализующий полный набор api методов которые необходимы для совместной работы с audio.js.

Готовый файл можно сказу скачать по ссылке.

Установка

  1. Устанавливаем скрипт audio.js
  2. Заменяем оригинальный файл audiojs.swf на новый
  3. А также в при инициализации скрипта необходимо указать в настройках useFlash: true.

Это необходимо чтобы флеш кодек использовался даже тогда когда браузер поддерживает формат audio тегов.

audiojs.events.ready(function() {
    audiojs.createAll({
      'useFlash': true // всегда использовать флеш
    });
});

Теперь для прослушивания online любой записи в формате .gsm достаточно вставить тег указывающий на файл. Например:

<audio src="test.gsm" />

Как поменять местами значения в колонках таблицы

Условия задачи: есть MySQL таблица table1. Используя только язык SQL запросов необходимо поменять местами значения из колонок value1 и value2. Структура таблицы имеет следующий вид:

CREATE TABLE `table1` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `value1` VARCHAR(50) NOT NULL DEFAULT '' ,
  `value2` VARCHAR(50) NOT NULL DEFAULT '' ,
  PRIMARY KEY (`id`) )
ENGINE = MyISAM;

Решение “в лоб”

Самым простым способом является явное переименование колонок таблицы. Данное решение самое простое в реализации и самое быстрое по результатам моих тестов.

ALTER TABLE  `table1`
CHANGE `value1` `value2` VARCHAR(50) NOT NULL DEFAULT '' ,
CHANGE `value2` `value1` VARCHAR(50) NOT NULL DEFAULT '';

Решение с временной переменной

Изящное решение с использованием временной переменной для хранения промежуточного результата. По скорости оно уступает первому решению тем не менее показывает дополнительные скилзы в SQL.

UPDATE `table1` SET `value1`=(@temp:=`value1`), `value1` = `value2`, `value2` = @temp;

Решение с подзапросом

Поскольку MySQL не даст сделать простой UPDATE с выборкой из этой же таблицы, поэтому будем извращаться с ON DUPLICATE KEY UPDATE. Самое медленное из предложенных решений, но данный синтаксис предоставляет определенные преимущества в некоторых ситуациях.

INSERT INTO `table1`
SELECT * FROM `table1` `t2` ON DUPLICATE
KEY UPDATE `value1` = `t2`.`value2`, `value2` = `t2`.`value1`;

Расшифровка купона на скидку при покупке Parallels

Послушав Радио-Т номер 254, захотелось расшифровать код на 10% скидку на продукты от Parallels.

Воспользовавшись подсказкой о числе 13 можно после некоторых поисков найти прийти к выводу об использовании алгоритма ROT13 (подробнее о нем можно почитать в википедии). По сути это элементарный сдвиг на 13 позиций в алфавите. Пишем простую функцию на Python 3.0.

import string

def rot13(text):
    rot13_trans = string.maketrans( \
        "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", \
        "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")

    return string.translate(text, rot13_trans)

Первый этап пройден. Полученную в результате строку необходимо разбить на подстроки, затем сохранить в файл и пропустить через утилиту uudecode, которая есть в любом дистрибутиве linux (на mac’е тоже она есть). Но мне было лень заморачиваться с этим и я написал аналог также на питоне.

import base64, re

# s - это декодированная с помощью rot13 строка
matches = re.split('~', s)
print base64.b64decode(matches[1] + matches[2])

Описывать подробно смысла особого нет. Делим на подстроки по маске ‘~’, соединяем 2 и 3 строки и декодируем через base64. Вот и все. Финальный результат публиковать не стану, кому надо тот сам все сделает.

На Python 2.x можно написать через .decode(‘rot13’) и не морочить себе голову, как это сделал я.

import base64, re
matches = re.split('~', 'rot13 string'.decode('rot13'))
print base64.b64decode(matches[1] + matches[2])

Bitrix 11 + Basic Auth = 💩

В Битриксе разработчики или маркетологи видимо не в курсе что данные которые используются для HTTP-аутентификации не обязательно будут совпадать с теми данными которые используются для доступа в Административный раздел Битрикса. А потому навесив собственную Basic Authentication над Битриксом 11 получаем полную невозможность входа в административный раздел в случаях когда логин и пароль от HTTP-аутентификации не совпадают с теми под которыми можно войти в Административный раздел.

Файл отвечающий на эту несправедливость называется ./bitrix/modules/main/tools.php в котором есть идиотский метод ParseAuthRequest().

public static function ParseAuthRequest()
{
    $sDigest = '';

    if(isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] <> '')
    {
        // Basic Authorization PHP module
        return array("basic"=>array(
            "username"=>$_SERVER['PHP_AUTH_USER'],
            "password"=>$_SERVER['PHP_AUTH_PW'],
        ));
    }
    elseif(isset($_SERVER['PHP_AUTH_DIGEST']) && $_SERVER['PHP_AUTH_DIGEST'] <> '')
    {

    // Дальше нас не интересует

Единственный легальный способ который я сейчас вижу, это добавить в файл ./bitrix/php_interface/init.php такой хак:

if(isset($_SERVER['PHP_AUTH_USER']) && preg_match('#^/bitrix/admin/#ui', $_SERVER['REQUEST_URI'])) {
    $_SERVER['PHP_AUTH_USER'] = '';
}