26 декабря 2012 г.

Автоматизация работы с Google Forms

Предыстория
Сервисом Google Docs пользуюсь хорошо и давно, но только относительно недавно, до меня дошло, что его можно использовать еще лучше (спасибо habrу).
Вся прелесть Google Docs это синергия его компонентов, так:
  • Google Forms – позволяет кастомизировать формы и встраивать на свои странички
  • Google Spreadsheet – поддерживает автоматизацию, с помощью скриптов на javascript, имеется поддержка триггеров и событий.
В каком-то посте на habrahabr узнал, что формы в Google Forms можно не только создавать и открывать для публичного доступа, но и кастомизировать по своему вкусу. Что очень пригодилось — форму можно публиковать на своем сайте, и в своем профиле Google docs собирать данные. Второй момент, эти данные нужны не только мне, и поэтому их требуется как то пересылать другим людям, которые ими будут заниматься. В такой схеме работы  Google выступает как прокси, пересылает сообщения через gmail. О том как я реализовал такую схему будет посвящен этот пост.

Реализация
Так как все данные собираются в  Spreadsheet, то средства автоматизации должны работать оттуда.
И так у вас есть работающая форма Google Forms, которая связана с таблицей в сервисе Google SpreadSheet (Рис. 1). Необходимо реализовать отправку на определенные email адреса заполненных пользователями данных формы при отправке формы.
Рис. 1 Таблица, с полученными данными формы
Для создания скрипта в Spreadsheet необходимо в меню сделать переход «Инструменты» → “Редактор скриптов». В появившемся окне необходимо выбрать пункт «Создать пустой проект».
Появится вкладка с редактором (Рис. 2).
Рис. 2

function sendOnSubmit(e){
  // Тестовый вариант триггера с отправкой всех данных
  var messageString="",
      data = e.namedValues;// исходный объект с заполненными данными
/*  То как выглядят данные от формы — json с заголовками колонок и их значения:
{"Разрешает обработку данных":["желает подписаться"],"Наименование  
организации":["zanussi"],"Желаемый срок кредитования (в  
мес.)":["12"],"Регион в котором осуществляется  
деятельность":["Москва"],"Контактный номер телефона":["+7 (122)  
222-22-22"],"Электронная почта":["www333@mail.ru"],"Желаемая сумма в  
рублях":["10 000 000"],"Как к Вам обращаться":["леопольд2"],"Отметка  
времени":["26.11.2012 22:57:14"]}
Приводим полученный объект к виду удобном для восприятия: */
  
  for(var key in data){
    messageString+=key+" : "+(data[key]&&data[key][0]?data[key][0]:JSON.stringify(data[key]))+"\r\n";
  }
  
  MailApp.sendEmail("mail@yandex.ru", "Заявка", messageString);
}

В данном редакторе можно создать как обычные запускаемые функции (через меню), так и запускаемые по событиям. Такие функции могут принимать в качестве аргумента объект события.
Так функция sendOnSubmit(e) — получает объект e, в котором свойство e.namedValues содержит в JSON формате значения заполненных полей. Объект e.namedValues «обрабатывается» в текстовое сообщение (messageString), которое затем рассылается по email адресам с помощью метода MailApp.sendEmail() .

Теперь осталось созданную функцию привязать к событию отправки формы.
Во вкладке с редактором кода переходим в меню «Ресурсы» → “Триггеры текущего скрипта”.
Появится диалоговое сообщение о необходимости настройки Триггеров текущего проекта (если они не были настроены) (Рис. 3).
Рис. 3
Кликаете по гиперссылке. Должно появиться окно с настройкой триггера (Рис. 4).
 Рис. 4 
В открывшемся окне необходимо выбрать нужную функцию, «Из таблицы» и событие «Отправка формы». Нажимаете кнопку «Сохранить».
Теперь от Вас потребуются подтвердить ваши действия. А именно разрешения на отправку почты. Ведь все сделанное будет выполняться от вашего имени (учетной записи). Даже почту будет отправляться от вашего gmail профиля.
«Авторизуемся» и окончательно сохраняем триггер.

6 декабря 2012 г.

Маленькие сюрпрайзы PHP PDO.


На днях ковыряясь в mysql при помощи pdo сделал для себя маленькое открытие.
В методах prepere(), execute() не могут использоваться переменные (именованные не именованные  через которые в запрос подставляются название таблиц и колонок. Вот так никакого exception, ни Fatal error, Kernel demage и т.п. Все тихо пропускаем и переходим к следующим инструкциям.

Вот такое не сработает:

$preQuery = $this->pdo->prepare('SHOW ?');
$preQuery->execute(array("tables"));
К вашим услугам конкатенация строк и хранимые процедуры

12 июля 2012 г.

Какое в вашей форме поле ввода номера телефона?

Заполняя контактные данные в веб формах, вижу что то на подобие такого
или такое
На что идут веб мастера, чтобы упрастить себе работу  и навязать посетителю свое видение отформатированного номера телефона.

Лично я считаю, что чем проще форма тем лучше - просто оставить текстовое поле и попробовать сделать одним из двух вариантов:
  1. Задать шаблон в виде регулярного выражения атрибута pattern текстового поля (доступны в HTML5);
  2. Уже отправляя или получая данные, удалять из введенного пользователем значение, к примеру, все не цифровые знаки, распарсивая и приводя к нужной форме.
Начиная с HTML5 тесктовое поле input обзавелось атрибут pattern и сделало возможным проверку введенного значения регулярным выраженнием. (Если бы я не написал регулярное выражение для проверки корректности ввода номера телефона, то и начинать этот пост не было бы смысла.)
Но чтобы распарсить номер телефона надо иметь  модель "типового" номера телефона.
Поэтому приведу сразу какими могут быть номера телефона:
  • С или без указания кода страны +7, 28 или просто 8;
  • С или без указания кода оператора или кода города - 3 цифры (В Санкт-Петербурге других не встречал);
  • 7 цифр номера телефона c пробелами, через тире или все слитно (что то вроде 123-45-67, 1234567, 123 45 67).
Согласно этой модели привожу регулярное выражение которое удовлетворяет озвученным требованиям:
\s*(?:\+\d{1,2}|\d)?\s*(?:\(\s*\d{3}\s*\)\s*|\-?\d{3}(?:\-|\s)?)?\d{3}(?:\-|\s)?\d{2}(?:\-|\s)?\d{2}\s*
Описание с комментарием:
/\s*
(?:\+\d{1,2}|\d)? # +7, +38 или 8 или ничего из этого
\s* # пробелы, или ничего
(?:\(\s*\d{3}\s*\)\s*|\-?\d{3}(?:\-|\s)?)? # код города или оператора, может быть даже в скобочках
\d{3} # 3 обязательных числа
(?:\-|\s)?
\d{2} # 2 обязательных числа
(?:\-|\s)?
\d{2} # 2 обязательных числа
\s*/x
Регулярное выражение это можно использовать для выделения номеров телефона в произвольном тексте (Например если стану sms-спамером и буду писать граббер номеров телефонов с сайтов). Модифицировав выражение, можно извлекать различные части номера телефона.

Чтобы проверить работоспособность регулярного выражения мне нужна была большая выборка примеров номеров телефонов согласно созданной модели номера телефона. Для этой цели я использовал программу генератор seo текстов Seo Anchor Generator. Программа Seo Anchor Generator позволяет по заданному шаблону составить выборку всех возможных комбинаций по заданному шаблону.
Пример используемого в программе шаблона:
{+{7|38}|8|}{| }{({ |}012{ |}){ |}|{-|}012{ |-|}|}345{-| |}67{-| |}89{ |}
Таким образом я собрал 2160 примеров правильных комбинаций написания телефонов.

Javascript код для тестирования:
function isFormattedTelefon(str){
    return /^\s*(?:\+\d{1,2}|\d)?\s*(?:\(\s*\d{3}\s*\)\s*|\-?\d{3}(?:\-|\s)?)?\d{3}(?:\-|\s)?\d{2}(?:\-|\s)?\d{2}\s*$/.test(str);
}
// массив с различными комбинациями написания одного телефона
var testArray2=[ "+7( 012 ) 345-67-89","+7( 012 ) 345-67-89", ... , "345 67-89" ];
for(var i=0, len=testArray2.length;i<len;i++){
    console.log("#%s is: %s",i,isFormattedTelefon(testArray2[i]) );
}
Осталось только обкатать все это дело.

28 июня 2012 г.

Почему я больше не делаю мобильные версии сайтов


Мобильная версия будет отвлекать силы и время на ее поддержание. Смартфоны с удобным выходом в интернет сделают эту работу просто бессмысленной. Они стали доступнее, их качество выросло.
Вот, какие гейши...
Мобильные устройства быстро развиваются, в 2010 году смартфон с разрешением экрана 320 на 480 пикселей стоил около 9 тыс. руб. в 2012 около 4,5 тыс (мой телефон смартфон обесценился в 2 раза! и он все еще продается в магазинах). По собственному опыту могу сказать, что комфортное просматривание страниц начинается именно с этого разрешения экрана. Браузеры  для  смартфонов выполнены на более высоком уровне, чем для обычных мобильных телефонов, которые с малой вероятности вообще будут обновляться. Смартфоны уже  позволяют совмещать  функции некоторых уже привычных устройств, таких как навигаторы, электронные читалки, медиа плееры и будильники, органайзеры, калькуляторы и др, что позволит смартфонам занять место в карманах людей.  Стандартные браузеры для устройств на Android до 4.0 и IOs, Windows Phone, скорее всего, обновляться не будут, но будут доступны устанавливаемые альтернативные браузеры, что позволяет предположить, что качество отображение страниц в браузере будет повышаться.
К тому же, всякая попытка «пересаживать» посетителей на урезанную мобильную версию будет вызывать раздражение у них.
Что будет с мобильными телефонами
Мобильные телефоны с номинальной возможностью выхода в интернет существуют, но их доля среди владельцев будет снижаться и останется на определенном нишевом уровне. Выход в интернет с этих устройств не рассматривается их владельцами в качестве приоритетных опции телефона.
Что делать с сайтом
Теперь сайт будет необходимо тестировать для отображения на мобильных устройствах, применять техники адаптивной верстки с явным использованием viewport-а.

20 июня 2012 г.

Ruby, регулярные выражения и Windows XP


Ruby хоть и является одним из наикроссплатформейших языков общего назначения с которыми мне приходилось работать, но в нем то и дело спотыкаюсь о грабли ее реализации на разных платформах.
Так сегодня мне потребовалось написать скрипт на ruby, который должен был запускаться на Windows XP (При этом я еще хотел его запускать дома, уже на Linux).  Проблема заключалось в том что регулярные выражения, которые я применял к импортируемому тексту из стороннего файла напрачь не работали. Как заведено, в опенсорсе, раскажу о своей проблеме и как ее решал, кому нибудь пригодиться.
Что было:
  • Windows XP
  • ruby (MRI v1.9.3)
  • Большой текстовый файл в кодировке utf-8, почти все содержимое набрано кириллицей.
Соответственно требовалось написать консольный скрипт, который бы потрашил этот злополучный файл регулярными выражениями. Регулярные выражения, конечно же были с модификатором "u", ибо содержали все бе кириллические символы.
Как иследовало ожидать проблемы в Windows XP были в двух кодировках. Консоль в Windows XP в IBM866 (соответственно импортируемый текст тоже был в ней), сам файл в utf-8.

Вообщем пришел к такому варианту:
# coding: utf-8
# Попутно, в Windows, импортированный utf-8 текст (через File) будет в utf-8 (а не IBM866)
Encoding.default_internal = 'UTF-8'
Encoding.default_external = 'UTF-8'
# регулярное выражение:
FIOPATTERN=/^Имя:\s*([^\n^\r]+)/u

31 мая 2012 г.

Депозитный калькулятор


Если Вы ищете легкий инструмент с помощью, которого можно посчитать доход по вкладу.
Представляю общественному вниманию свою недавнюю работу, приложение для браузера Chrome - депозитный калькулятор Deposit Calculation Tool.
Приложение создано, чтобы помочь определить вероятный доход по вкладу. В приложение cуществует возможность расчета дохода:

  • с ежемесячной капитализации;
  • с пополнениями вкладов (с частичным снятием я делать не стал, так как не встречал людей, которые такое планировали, но пожеланию такую возможность можно добавить);
  • с различным сроком (с точностью до нескольких дней).
Внешний вид Deposit Calculation Tool 

Результаты вычиления выводятся в виде сводных данных и в виде графика.
Приложение доступно в магазине Chrome по ссылке и распространяется бесплатно.
Приятного использования.
Вывод результата вычисления


23 мая 2012 г.

Все разумно в пределах потребностей


Сегодня в обеденный перерыв мне пришло озарение, очередное. Так часто бывает. Я как обычно вышел на улицу подышать воздухом. Взглядом пробежался по округе - машины, одуваньчики, девушки. Так вот, все на месте, только  с одуваньчиками было не все в порядке. В виду того, что снег в этом году растаял на месяц раньше чем в прошлом году и одуваньчики начали во всю цвести и заполонять собой все газоны и пустыри. Не заметить аномалию с поведением одуваньчиками было не возможно.
Я даже с фотографировал.
Часть газона, которая освещена солнцем, в отличие от той что находиться в теньке, вся усеяна одуванчиками с раскрывшимися цветками. В пику им, те что в теньке, наоборот собрали свои бутончики.

Вот она растительная жизнь. Есть у них чувство хода событий. Растения может быть и не чувствуют ход времени, но меряют свой день солнечными лучами.
П.С. В конце осени видел голубя, который прыгал через водосливной проем в бардюре. Одназначно, он "на глаз" мерил расстояние и необходимые усилия своим мозгом. Хотя как бы иначе они вообще летали.

П. П. С. Вообщем занят был я последнее время. Совсем недавно запустил сайт клининговой компании "Северный блик". Люди, если кому-нибудь, потребуются чистые окна или радующий глаза, диван заходите  http://www.blik-cleaning.ru/ .

30 марта 2012 г.

Поле для форматированного ввода числа

Вступление
Вам никогда не требовалось контролировать пользователя при вводе данных в форму на сайте? На этой недели такую задачу я получил на работе. Имелось поле для ввода ссумы. Но чтобы ввод был форматированным не "100000", а "100 000". И соответстенно только цифры.

Что нам Google подскажет
Как Google(Yandex) не борется за хороший поиск, а оригинальные варианты он не отбирает. Получается список почти идентичных вариантов контроля ввода через событие keypress. Html5 тоже не помог type="number" даже не контролирует ввод только цифр. Не много отвлекусь, поиск по блогам Google или Яндекса частично помогает разрядить выдачу однотипных решений. Но все равно чтобы найти хороший метод в выдаче нужно постараться.

Что нам понадобится
В стартное html поле ввода input можно ввести данные тремя спосбами:
  1. с клавиатуры. Это можно отловить слушая события keypress, keydown, keyup;
  2. вставить из буфера обмена;
  3. автоподстановка в браузере. 
Почти все найденные мной в поисковике решения борятся с первым способом, неучитывая два других. Для борьбы с вставкой из буфера обмена я воспользовался JQuery плагином jQueryTextChange (ссылка1, ссылка2).  Для борьбы с автоподстановкой в тег input можно добавить атрибут autocomplete со значением off:
<input autocomplete="off" autofocus="" id="principal_amount" name="principal_amount" required type="text">
Как это выглядит
Представляю свое решение
if (!Array.prototype.indexOf){// Привет IE
    Array.prototype.indexOf = function(elt/*, from*/){
        var len = this.length,
            from = Number(arguments[1]) || 0;
        from = (from < 0) ? Math.ceil(from) : Math.floor(from);
        if (from < 0) from += len;
        for (; from < len; from++){
            if (from in this && this[from] === elt)
                return from;
        }
        return -1;
    };
}
function numberFormat(str){ // форматируем строку 12000 -> 12 000
    var     str_array=[],
            length=str.length;
    var undefined2string=function(val){
        return val?val:"";
    }
    for(var i=length-1; i>=0;i-=3){
        str_array.unshift(
            undefined2string(str[i-2])+undefined2string(str[i-1])+undefined2string(str[i])
        );
    }
    var resStr=str_array.join(" ");
    return resStr;
}
function createFormatControl(element,onchange_callback){
    if (!(element instanceof HTMLInputElement))
        throw("Wrong type of node, need HTMLInputElement");
        
    onchange_callback=onchange_callback||function(){};
    /*
    @e.keyCode:
        9 - Tab
        8 - Backspace
        46 - Del
        48-57 - 0-9
        13 - Enter
        37-40 - arrow: left,top,right,bottom
        35 -Home
        36 -End
    */
    $(element).bind("keydown",function(e){//контролируем ввод с клавиатуры
        var     keyCodeArray=[8,9,13,46];
        if(
            (e.keyCode>47 &&  e.keyCode<58) ||
            keyCodeArray.indexOf(e.keyCode) >= 0 ||
            (e.keyCode>34 &&  e.keyCode<41)
        ){
            return true;
        }else{
            return false;
        }
    }).bind("textchange",function(e){// Контролируем изменение текста (вставку из буфера обмена.)
    
        var     $this=$(this),
                origStr=$this.val().replace(/\D+/g,""),
                str=numberFormat(origStr);
        if(!(e.keyCode>34 &&  e.keyCode<41)){
            $this.val(str);
        }
        onchange_callback(str);
    });
}
$(document).ready(function(){
    var     in2 = document.getElementById("in2"),
            $deb3=$("#deb3");
    createFormatControl(in2,function(e){$deb3.text(e);});
});
Верстка:
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="jquery.textchange.min.js"></script>
<p>
    <label>#2: </label>
    <input type="text" id="in2" autocomplete="off"/>
</p>
<p id="deb3"></p>

Скачать архив с исходниками

19 февраля 2012 г.

Как составить график в Excel по отдельным точкам из диапазона


На все запросы Google и Yandex дружно поднимают всякий шлак на тему «Как легко вставить график».
Все оказалось достаточно просто нужно лишь перечислить необходимые точки через точку запятую («;»).
Ниже на скриншотах приведены диалоговые окна Excel 2007. Но для OpenOffice/LibreOffice Calc все делается аналогично.
Оригинальный график из которого надо оставить несколько точек
Выбираем данные.
Диалоговое окно в котором можно указать необходимые точки

Значение поля «Диапазон данных для диаграммы»:
=Позиции_по_недельно!$B$2:$AC$2;Позиции_по_недельно!$B$4:$AC$4
Заменяем на:
=Позиции_по_недельно!$B$2:$D$2;Позиции_по_недельно!$I$2:$I$2;Позиции_по_недельно!$M$2:$M$2;Позиции_по_недельно!$R$2:$R$2;Позиции_по_недельно!$W$2:$W$2;Позиции_по_недельно!$AC$2:$AC$2;Позиции_по_недельно!$B$4:$D$4;Позиции_по_недельно!$I$4:$I$4;Позиции_по_недельно!$M$4:$M$4;Позиции_по_недельно!$R$4:$R$4;Позиции_по_недельно!$W$4:$W$4;Позиции_по_недельно!$AC$4:$AC$4

В колонке B – подпись ряда, в колонке D начинаются собственно данные.

Результат



Итоги конкурса сайтов банков Санкт-Петербург

В Санкт-Петербурге, где я живу и работаю, существует организация "Северо западная ассоциация банков" (www. nwab.ru), которая ежегодно проводит конкурс банковских сайтов.  В конкурсе участвуют только банки состоящие в этой ассоциации. Так как начальство направляет меня туда в качестве члена жюри, то в моих руках оказались итоги конкурса за 2010, 2011 годы.

В следующей таблице приведены итоги конкурса за 2011 год и для сравнения итоги за 2010 год.

Занятое место в 2011 Банк Занятое место в 2010 Изменение по сравнению с 2010
1 Альфа-Банк 1 0
2 Райффайзенбанк 17 +15
ЮниКредит Банк 6 +4
3 Северо-Западный банк Сберегательного банка России 19 +16
4 Росбанк 21 +17
5 Абсолют Банк 8 +3
Открытие 9 +4
ЮНИАСТРУМ БАНК 5 0
6 Русский Стандарт 13 +7
7 Банк БФА 63 +56
Московский Банк Реконструкции и Развития 2 -5
8 ГЛОБЭКС 10 +2
КИТ Финанс 22 +14
9 НОМОС-БАНК 15 +6
10 Ханты-Мансийский банк 24 +14
11 Связь-Банк 23 +12
12 Газпромбанк 3 -9
13 ПРОМСВЯЗЬБАНК 18 +5
14 Агросоюз
15 АГРОПРОМКРЕДИТ 4 -11
16 ВТБ 24 7 -9
17 Инвестбанк 17 0
18 БАЛТИНВЕСТБАНК 39 +21
19 МДМ Банк 10 -9
20 Национальный Залоговый Банк 46 +26
Национальный Резервный Банк 38 +18
21 Мираф-Банк 57 +36
22 Банк "Санкт-Петербург" 10 -12
Стройкредит 20 -2
Энергомашбанк 26 +4
23 ГОРОД 14 -9
24 Мастер-Банк 27 +3
25 Сведбанк 32 +7
26 РОССИЯ 40 +14
27 МОСОБЛБАНК 33 +6
28 Ланта-Банк 44 +16
29 ВТБ, Северо-Западный региональный центр 12 -17
30 Витабанк 25 -5
31 Огни Москвы
32 Кредит-Москва 37 +5
33 Охотный ряд 32 -1
34 ЗЕНИТ 34 0
Констанс-Банк 56 +22
35 Александровский 53 +18
36 МБА-Москва 35 -1
37 Севзапинвестпромбанк 48 +11
38 Петербургский Городской Банк 41 +3
39 ПромСервисБанк 49 +10
Эллипс банк
40 СЕВЕРГАЗБАНК 60 +20
41 Советский 36 -5
Экономический Союз 45 +4
42 СИАБ 31 -11
43 Адмиралтейский 42 -1
Балтика
44 Таврический 51 +7
45 Объединенный капитал 65 +20
46 Международный банк Санкт-Петербурга 55 +9
47 МОРСКОЙ БАНК 47 0
Рускобанк 57 +10
48 Тетраполис 52 +4
49 Викинг 59 +10
50 Невский банк 66 +16
51 Финансовый капитал 70 +19
52 Москомприватбанк 64 +12
53 Национальный Торговый Банк 43 -10
54 Банкирский Дом 67 +13
СЭБ Банк 58 +4
55 Северо-Западный 1 Альянс Банк
56 Инвест-Экобанк 72 +16
57 Санкт-Петербургский банк инвестиций 71 +14
58 Югра 69 +11
59 ЭКСИ-Банк 75 +16
60 НЕВАСТРОЙИНВЕСТ 74 +14
Банки которые участвовали в 2010, но  не приняли участие в 2011 году:
БИНБАНК - 11, АМТ-Банк - 16, Сосьете Женераль Восток - 28, Первый Республиканский Банк - 29, Эйч-Си-Би-Си Банк - 30, Балтийский Банк - 32, Алемар - 50, СИББИЗНЕСБАНК - 61, КОММЕРЦБАНК (ЕВРАЗИЯ) - 62, БНП Париба Восток - 68, Кредит Свис - 73.

Небольшие замечания к результатам:
  • В 2011 при определение мест учитывался  средний бал по всем параметрам оценки сайта (и видимо было округление), в то время как в 2010 все балы суммировались. Возможно этим можно объяснить большое количество банков на одних и тех же местах;
  • Некоторые изменения в позициях у некоторых банков можно объяснить уменьшением количества участников.
Что здесь можно сказать:
  • Сайты студии Артемия Лебедева в фаворитах (Альфа банк и Русский Стандарт);
  • Банки которые в 2011 году вложились в свои сайты довольно существенно поднялись по таблице - Банк БФА с 63 до 7-го, Росбанк с 21 до  4 и т. д.

12 февраля 2012 г.

Регулярные выражения в Haskell

Предыстория.
Где-то на отрезке времени длиной в пол года я экспериментировал с использованием регулярных выражений в Haskell. Потому что хотел использовать для написание небольших утилит, где как раз требовалось применение регулярных выражений. В конечном итоге сделал все на Ruby.

Вступление.
И как оказалось в Haskell нет единой библиотеки для поддержки регулярных выражений. Так обилие выбора создала проблему с выбором. Да и документации по каждой из библиотеке оказалось не так и много. Что и подтолкнула меня к написанию этого поста.
Примечания:
1) Я не буду приводить примеры типа "Hello world": поиск количества совпадение, захват фрагмента. В место этого приведу готовый листинг кода с основыными замечанными особенностями.
2) Под скальпель попали:
  • regex-pcre (0.94.2)
  • pcre-light (0.4)
  • regex-posix (0.95.1)
3) На производительность библиотек я не смотрел. Так как это было для меня вторичным.
То на что я обращал внимание:
- корректная обработка unicode и кириллицы (как оказалось это актуально!)
- возможность обработки строк (замена, разбитие на фрагменты)
4) Мои выводы могут быть неправильными, так как у меня не было подробной документации/примеров кода по каждой библиотеке.
Акт 1. На сцене regex-pcre
-- Пример  на тестирование Regex.PCRE
import qualified Text.Regex.PCRE as PCRE
{-
regex-pcre  v 0.94.2
"+" 
1) вроде как бы поддерживает PCRE нотацию
"-"
1) С некоторыми русскими буквам не работает ('Ф', 'Ъ' и др.)
2) Возможности для обработки строк (замена, разбитие) не нашел
-}
main = do
    putStrLn "START:"
    -- работает
    putStrLn "#0:"
    print $ testPCRE "Name: ([А-Яа-я\\s]+) Ser" "Name: ЧьетоИмя Ser" 
    -- работает
    putStrLn "#1:"
    print $ testPCRE "имя:\r\n([А-Яа-я\\s]+)\r\nфамилия" "имя:\r\nФЫВв \r\nфамилия" 
    -- работает
    putStrLn "#2:"
    print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nфамилия" "Имя:\r\nФЫВв \r\nфамилия" 
    -- работает
    putStrLn "#3:"
    print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nфАмилия" "Имя:\r\nФЫВв \r\nфАмилия" 
    -- не работает (Не любит Regex.Posix букву "Ф")
    putStrLn "#4:"
    print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nФамилия" "Имя:\r\nФЫВв \r\nФамилия" 
    -- Заменяем "Ф" на "[Ф]" и работает:
    putStrLn "#5:"
    print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\n[Ф]амилия" "Имя:\r\nФЫВв \r\nФамилия" 
    
    print $ let regex = "имя:(.*?)фамилия" ; match = "имя:\r\nПетр \r\nфамилия" PCRE.=~ regex :: [[String]] in match
    print $ let m= "xyz abc more text" PCRE.=~ "(\\w+) \\w+" ::[[String]] in m
    putStrLn "#6 Поиск email: "
    -- Тут все нормально
    print $ testPCRE "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "aaaa \r\nlesprom.spb@bk.ru\r\n bbbb" 
    -- А вот здесь текст захватвается паттерном!
    print $ testPCRE "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "регистрация"


testPCRE regex text = let match = text PCRE.=~ regex :: [[String]] in match


Оранжевым подсвечены ошибки.
Как оказалось самая худшая из всех тестируемых:
  • В документации были намеки на то что имеется возможность "настройки" поиска (нечувствительность регистра, точка - включает себя символ конца строки и т. п.) - но как это сделать не понятно. 
  •  Явная проблема с кириллицей. Были случаи ложного захвата в тексте на русском.
Акт 2. На сцене pcre-light
-- Пример  на тестирование Regex.PCRE.Light
import Text.Regex.PCRE.Light
import qualified Data.ByteString.UTF8 as B
{-
pcre-light v 0.4
"+":
1)шаблон регулярных выражений c PCRE нотацией
2)unicode и как следствие адекватная работа с кириллицей
"-"
1) Нет возможности работы с строками (замена, разбитие)
-}
main = do
    putStrLn $ B.toString  $ B.fromString "START:"
    print $ let r = compile (B.fromString "(\\s[фыва]+\\s)") [] in (match r (B.fromString "aaddd фыва dd") [])
    print $ testPCRELight "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "aaaa \r\nlesprom.spb@bk.ru\r\n bbbb" 
    print $ testPCRELight "\\b([\\w.\\-\\d]+@[\\w.\\-\\d]+)\\b" "регистрация"

testPCRELight :: String -> String -> Maybe [B.ByteString]
testPCRELight pattern str = let r = compile (B.fromString pattern) [] in (match r (B.fromString str) [])

-- Могут пригодится:
func1 :: Maybe [String] -> String
func1 (Just x) = last x
func1 Nothing = " - "

func2 :: Maybe [B.ByteString] -> B.ByteString
func2 (Just x) = last x
func2 Nothing = B.fromString " - "

Хорошая PCRE библиотека:
  1. PCRE и кириллица в норме.
  2. Нет возможности работы с строками (замена, разбитие). 
Акт 3. На сцене Нет возможности работы с строками (замена, разбитие)
-- Пример  на тестирование Regex.posix
import Text.Regex.Posix
import Text.Regex --Regular expression matching. Uses the POSIX regular expression interface in Text.Regex.Posix.
-- <) http://hackage.haskell.org/packages/archive/regex-compat/0.95.1/doc/html/Text-Regex.html
{-
regex-posix v. 0.95.1
Особенности:
1) Это Posix а не Perl нотация регулярных выражений,  по этому:
- нет никаких \w, \d, \s символьных классов
- нет ленивых квантификаторов (.*?), только жадные
2) Есть возможность замены в строковых переменных по posix регулярным выражениям
-}
main = do
    putStrLn $ "START:"
    putStrLn $ "exp #0:"
    print $ testCountMatches "12, 34, 78" 
    print $ testReplace "12 moon"
    putStrLn $ "exp #1:"
    print $ removeByPattern " /*Hel\r\nl122o*/ 13 friday " "(/\\*.*\\*/)"
    putStrLn $ "exp #2:" -- Posix видимо использует только жадные квантификаторы
    print $ removeByPattern " /*Hel\r\nl122o*/ 13 friday /* ddd*/ dddd" "/\\*.*?\\*/"
    
testCountMatches :: String -> Int
testCountMatches inPut = inPut =~ "([0-9]+)" :: Int

testReplace :: String -> String
testReplace input = subRegex (mkRegex "([0-9]+)") input "White"

-- mkRegexWithOpts() parameters:
-- False -> meen that '.' include also "\n" character!
-- True -> meen that Case sensitive!
removeByPattern :: String -> String -> String
removeByPattern input pattern = subRegex (mkRegexWithOpts pattern False True) input ""

Все хорошо только PCRE все же не хватает.
Имеется возможность работы замены в строке, разбитие строк.

Заключение. 
Все течет. Все меняется. Надеюсь, с библиотеками так же.

12 января 2012 г.

Битрикс головоломка


В предыдущем посте посвященном ЧПУ Битрикс, был задан вопрос такого содержания:
Скажем есть инфоблок КАТАЛОГ, в нем раздел ТЕЛЕФОНЫ, в нем подраздел НОКИЯ, вот как в обработке адресов правильно написать условие, если мы не используем стандартные битриксовые убогие айди-чпу для комплексного компонента?

Единственное, что мне пока пришло в голову - это использовать уникальный символьный код каждого отдельного товара.

Надеюсь Вы поймете мой ход мыслей и если что подправите.
Допустим у нас такая иерархия:
КАТАЛОГ (тип инфоблока: katalog)
\_раздел ТЕЛЕФОНЫ ( инфоблок: phone)
| \_ подраздел НОКИЯ (символьный код раздела: nokia_phone)
| | \_ телефон1 (символьный код элемента: code1)
| | |_ телефон2 (code2)
| |- подраздел САМСУНГ (символьный код раздела: samsyng_phone)
| | \_ телефон3 (code3)
| | |_ телефон4 (code4)
| |...

Подразделы в разделах Инфоблоках похоже можно использовать только в декоративных целях.
Для вывода элементов подраздела в таких компонентах как компонент "Список новостей", ну или только  использовать его символьный код ЧПУ.

Каждый элемент инфоблока для использования его #CODE# в ЧПУ должен содержать уникальный символьный код. Как настроить всю вязанку компонентов Битрикс  для учета и символьного кода раздела я еще не представляю.. 

Каталог Мобильных телефонов.
В параметрах компонентах, в поле URL страницы детального просмотра можно указать ЧПУ адрес вида:
/mobile/#SECTION_CODE#/#CODE#/
где #SECTION_CODE# - поле подстановки "символьный код раздела"
#CODE# поле подстановки "символьный код элемента"

В обработку адресов можно добавить следующую запись.
Условие:
#^/mobile/([\w\d\_]+)/([\w\d\_]+)/#
Файл: 
указать url адрес на страницу с компонентом детального просмотра
Правило:
SECTION_CODE=$1&CODE=$2

Итого:
Имеется url адрес /mobile/nokia_phone/code2/, где смысловую нагрузку несет только code2

10 января 2012 г.

Клонирование объектов Java-script


Парадоксальная ситуация Javascript объектно ориентированный язык программирование, но в нем нет встроенных механизмов для клонирования объектов. Чехарда с передачей ссылок на объекты "деревенским" присваиванием всеже не всегда нужна.
Когда мне понадобился рабочий код для копирования объектов, то я довольно быстро нашел по блогам готовые решения.
Но в дальнейшем, под действием ряда факторов его пришлось модифицировать.
Поэтому представляю свой метод-функцию копирования объектов java-script:

// notUseRecursion - опция копирования свойств объекта без использования рекурсии (сохраняются ссылки).
// Полное копирование объекта события в IE это его смерть от выхода за пределы памяти.
// Поэтому есть смысл его отключить
function clone(o,notUseRecursion) {// Out of the memory in IE8
    if(!o || 'object' !== typeof o)  return o;
    var     c = 'function' === typeof o.pop ? [] : {},
            p, v;
    for(p in o) {
        //if(o.hasOwnProperty(p)) {// Dont supported in IE8
        if (Object.prototype.hasOwnProperty.call(o,p)){
            v = o[p];
            c[p] = (v && 'object' === typeof v && !notUseRecursion)?clone(v):v;
        }
    } 
    return c;
}

Основные особенности, которые пришлось учесть в коде функции клонирования - это перлы Internet Explorer 6-8:
1) Свойство объекта hasOwnProperty() не поддерживается, в место него происходит применение одноименной функции прототипа объекта Object (см. код).
2) Рекурсивное копирование объекта события (window.event к примеру) - приводило к остановке выполнения скрипта от "out of the memory". Объект события содержит методы "нативного кода" (Native code) который лучше не копировать, а сохранять ссылки на них. Поэтому есть смысл скопировать объект без использования рекурсии (notUseRecursion=yes).
Если найду еще какие-нибудь препятствия на пути свободного копирования объектов, обязательно опубликую их в блоге...

9 января 2012 г.

Snap - Haskell web framework

Snap прежде всего первый веб фреймворк на Haskell с которым я познакомился и в общем, этот пост основан на том опыте который я получил тестируя этот фреймворк.
C помощью Snap я создал небольшой демонстрационный проект, который я представляю по ходу текста. Местами в тексте будет встречаться дословный перевод документации.
Официальный сайт
Погружение в Snap

Установка snap через cabal

#: sudo cabal update
#: sudo cabal install snap

Настройки параметров системы Ubuntu

Snap хранит свои исполняемые бинарные файлы в папке ~/.cabal/bin (snap к примеру), по этому для удобства их использования в файл ~/.bashrc следует добавить строчку:
PATH=$HOME/.cabal/bin:$PATH

Создание каркаса типового Snap проекта

#: mkdir myproject
#: cd myproject
#: snap init

команда snap init создает шаблон типового проекта в текущей директории.
Сборка проекта:
$: sudo cabal install
Запуск:
#: myproject -p 8000
Теперь по адресу  http://localhost:8000/ должен откликаться созданый сайт.

Файловая структура созданного проекта

dist/
log/
resources/
static/
templates/
src/
Site.hs
Main.hs
Application.hs
Попробую объяснить назначение файлов в папке src
Файл main.hs - это скелет проекта. В файле Application.hs определяется модуль Application, который связан с обработкой ответов сервера и который используется в файле Site.hs. Application.hs используется для расширения стандартного Snap обработчика HTTP запросов. Site.hs - это основной исходный код проекта, роутинг, контроллеры и т.п.
Обработка url адресов в файле Site.hs задается следующим образом:
site :: Application ()
site = route [ ("/",            index)
             , ("/echo/:stuff", echo)
             ]
       <|> serveDirectory "resources/static"

Url адресс "/" относится к функции-обработчику index, "/echo/:stuff" к функции-обработчику echo. Обе эти функции выполняют роли контроллеров.
Фрагмент url адреса отмеченный двоеточием (":") - захватываемое значение параметра stuff. Значение этого параметра можно получить в дальнейшем с помощью rqParams или getParam (как и параметры передаваемые через POST и GET методы).
Вторая половина отделенная от основной части "<|>" будет переправлять на содержимое папки resources/static/.
Дословно оператор "<|>" в монаде Snap означает "try a, and if it fails, try b".
К примеру файл style.css будет искаться в resources/static/. Если он там не найдется метод serveDirectory вынудит Snap выдать ответ "404 Not Found".
Функция/метод/обработчик echo выглядит следующим образом
echo :: Handler App App ()
echo = do
    message <- decodedParam "stuff"
    heistLocal (bindString "message" (T.decodeUtf8 message)) $ render "echo"
  where
    decodedParam p = fromMaybe "" <$> getParam p

Здесь функция decodedParam выступает в качестве обёртки над методом  getParam. В качестве аргумента передается ей имя параметра заданного в роутинге ("stuff"), но можно и передать и имя параметра post при необходимости.
С помощью метода heistLocal используем  полученное значение для построения страницы с помощью шаблона "echo" (render "echo").
А при помощи метода bindString подменяем параметр в шаблоне <message/> на значение (T.decodeUtf8 message).

Snap использует систему шаблонизации Heist. Довольно гибкая и на оф. сайте представлено  достаточно документации.
В предыдущем примере указанный шаблон echo располагается в файле resources/templates/echo.tpl, который представляет из себя html файл с расставленными полями подстановки/параметрами.
Поля подстановки имеют вид ${key} или <key/>
Вот так выглядит шаблон echo:
<html>
<body>
  <div id="content">
    <h1>Is there an echo in here?</h1>
  </div>
  <p>You wanted me to say this?</p>
  <p>"<message/>"</p>
  <p><a href="/">Return</a></p>
</body>
</html>
Усложним типовой проект.
Добавим на главную страницу ("/") форму, которая при отправке данных будет:
  1. сохранять данные в файле -> Вызов монады IO в монаде Snap 
  2. выдавать страницу ответа -> Передача на страницу данных методом POST 
Добавим в файл Site.hs:
import System.IO
import qualified Data.ByteString as BS
...
-- пример на обработку post параметров ($_POST[]) и вызова монады IO из монады Snap (Понадобится чтобы сохранить данные в файл)
command3post :: Handler App App ()
command3post = do
    input_name <- decodedPost "firstname" -- ByteString
    second_name <- decodedPost "secondname"
    liftIO $ appendFile "output.txt" (((T.unpack . T.decodeUtf8. BS.concat ) [input_name, " : " ,second_name])++"\n")
-- Пояснение справа на лево:
-- BS.concat - слияние списка элементов ByteString
-- T.unpack . T.decodeUtf8 - преобразуем ByteString в String
-- appendFile - добавляет строку в файл
-- liftIO - преобразует монаду IO в монаду Snap
    heistLocal (bindString "firstname" (T.decodeUtf8 input_name)) $ render "formrezult"
    where
        decodedPost p = fromMaybe "" <$> getParam p 
А список маршрутов приведем к следующему виду:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/",            index)
         , ("formaction", command3post)
         , ("/echo/:stuff", echo)
         , ("", with heist heistServe)
         , ("", serveDirectory "resources/static")
         ] 
В файл resources/templates/index.tpl добавить
<form method="post" action="formaction">
    <p>First name: <input type="text" name="firstname"/></p>
    <p>Second name: <input type="text" name="secondname"/></p>
    <input type="submit" value="send"/>
</form> 
Создать файл resources/templates/formrezult.tpl
<!DOCTYPE HTML>
<html>
  <head>
    <title>Form request Page</title>
  </head>
  <body>
    <div id="content">
      <h1>Form Rezult</h1>
    </div>
    <p>Thank you <firstname/></p>
    <p><a href="/">Return on main page</a></p>
  </body>
</html>
Вот и все. Проект можно собирать и запускать.
Скачать проект целеком.

Вместо вывода

Snap фреймворк очень напоминает работу с RoR или ASP.Net MVC. Хотя я и не вижу явных преимуществ перед другими фреймворками как на Haskell так и на других ЯП, Snap может пригодиться хаскелоидам. В общем дело чисто вкусовых предпочтений аудитории.