Страницы

вторник, 28 декабря 2010 г.

SSL, openssl и генерация сертификатов

Что бы не забывать
В состав дистрибутива openssl входят скрипты CA.sh и CA.pl (/usr/local/openssl/misc)
создаем корневой сертификат
./CA.sh -newca
генерируем личный ключ и сертификационный запрос сервера
./CA.sh -newreq
и подписываем его своим корневым сертификатом.
./CA.sh -sign
переписываем ключ и сертификат сервера в служебный каталог Apache
cp newreq.pem /usr/local/etc/apache/sslkey/server.key
cp newcert.pem /usr/local/etc/apache/ssl.crt/server.crt
Файл корневого сертификата ./demoCA/cacert.pem необходимо
распространить по клиентским компьютерам.

среда, 22 декабря 2010 г.

Где брать идеи

1. Идеи не появляются от просмотра телевизора.

2. Идеи иногда появляются после прослушивания лекции.

3. Идеи часто появляются во время чтения книги.

4. Хорошие идеи появляются из плохих идей, но только если последние есть в достаточном количестве.

5. Идеи ненавидят конференц-залы, особенно такие конференц-залы, где сохранился опыт критики, личных выпадов или занудства.

6. Идеи возникают, когда сталкиваются непохожие вселенные.

7. Идеи часто стремятся соответствовать ожиданиям. Если люди ожидают идею, то она появится.

8. Идеи боятся экспертов, но восхищаются умом начинающих. Немного осведомленности не помешает.

9. Идеи идут косяками, пока вы не испугаетесь. Уилли Нельсон написал три из своих главных хитов за одну неделю.

10. Идеи приходят благодаря неприятностям.

11. Идеи приходят от нашего эго, и они выходят лучше всего, когда они щедры и самозабвенны.

12. Идеи приходят из природы.

13. Иногда идеи приходят от страха (обычно это в кино), но часто – от уверенности.

14. Полезные идеи приходят, когда вы не спите и находитесь начеку, чтобы не пропустить намек.

15. Однако иногда идеи прокрадываются к нам, когда мы спим и слишком обмякли, чтобы бояться.

16. Идеи замечаешь краем глаза, а иногда – находясь в душе, когда не прилагаешь никаких усилий.

17. Посредственные идеи склонны копировать то, что, похоже, работает как раз в эту минуту.

18. Более крутые идеи перескакивают через посредственные.

19. Идеям не нужен паспорт, и они часто пересекают границы (всякого рода) безнаказанно.

20. Идея должна прийти откуда-то, потому что если она останется там, где есть, и не присоединится к нам здесь, она будет скрытой. А скрытые идеи не превращаются во что-то конкретное, не имеют влияния, не пересекаются с рынком. Они умирают в одиночестве.

Сет Годин...

четверг, 9 декабря 2010 г.

Оптимизация кода

Архитектурно правильный код - это хорошо. Он красиво выглядит, легко читается, все структурировано, а потом оказывается, что код работает долго, а все вроде бы красиво.
Так вот не всегда "правильный" с точки зрения стиля, архитектуры код самый быстрый.
Ну так получилось, так бывает, теория и практика знаете ли. А теперь идеи ускорения.

Идея 1. Назовем ее развертывание цикла.

Код :

my $ref = $dbh->selectall_arrayref(q[SELECT id FROM categories
WHERE c_status='active']);
my (@res);
my $count=10;
foreach my $tmp (@$ref){
my $ref=$dbh->selecall_hashref(qq[SELECT id,title,text,ts
FROM articles
WHERE category=$tmp
ORDER BY id DESC LIMIT $count],'id');
my @r;
push @r,$ref->{$_} foreach( key %$ref);
push @res,\@r;
}



То есть это простой пример вывода статей по 10 штук в каждой категорие. Он универсален, но его можно значительно ускорить потеряв при этом применимость нового алгоритма для некоторых частных случаев. "Развернув" внутренний цикл
мы получим ужасный код, но и более быстрый, избавившись в данном случае аж от 10 условных переходов (циклы это ничто иное как условные переходы) . Получим примерно следующее :
my $ref = $dbh->selectall_arrayref(q[SELECT id FROM categories
WHERE c_status='active']);
my @res;
my $count=10;
foreach my $tmp (@$ref){
my $ref=$dbh->selecall_arrayref(qq[SELECT id,title,text,ts
FROM articles
WHERE category=$tmp
ORDER BY id DESC LIMIT $count],'id');
my @r;
push @r,$ref->[0];
push @r,$ref->[1];
push @r,$ref->[2];
push @r,$ref->[3];
push @r,$ref->[4];
push @r,$ref->[5];
push @r,$ref->[6];
push @r,$ref->[7];
push @r,$ref->[8];
push @r,$ref->[9];
push @res,\@r;
}


Идея 2. Банальное кеширование

Не будем уходить далеко в мир высокого, а продолжим рассматривать пример выше.
Логично предположить, что новые категории добавляются не каждый день, потому от запроса на выборки списка надо избавиться путем сохранения его в памяти. Самый простой способ это Memcached, другой способ рассмотрим ниже.

sub get_cache_connection
{

require Cache::Memcached::Fast;
require Storable;
my $ref=new Cache::Memcached::Fast(
{
servers =>[{ address => '127.0.0.1:11211',weight=>2.5}],
namespace => 'sessions:',
connect_timeout => 0.2,
io_timeout => 0.5,
close_on_error => 1,
compress_threshold =>-1,
ketama_points => 150,
nowait => 1,
hash_namespace => 1,
serialize_methods => [ \&Storable::freeze, \&Storable::thaw ]
}
);

return $ref;

}

my $tabs;
my $md=get_cache_connection();
my $ref=$md->get('categories')) ;
foreach(@$ref){
###some code there

}
Кеширование применимо для данных, которые не часто меняются, при другом подходе может возникнуть проблема, что накладные расходы на работу с кешированием превысят затраты на работу с базой данных. Не забывайте, что mysql тоже кеширует запросы, и в предыдущем примере запрос на выборку категорий по сути не имеет смысла, потому что с огромной вероятностью он будет закеширован mysql. Другое дело, если вы генерируете из категорий свой особый html код, вот его можно и за кешировать.


Идея 3. Развертывание функций.


Думаю вы уже догадались в чем фишка. Рассмотрим предыдущие примеры, только добавим внутрь цикла функцию форматирования даты - ts.
my $ref = $mem_cache->get('categories')) ;
my @res;
my $count=10; foreach my $tmp (@$ref){
my $ref=$dbh->selecall_arrayref(qq[SELECT id,title,text,ts
FROM articles
WHERE category=$tmp
ORDER BY id DESC LIMIT $count],'id');
my @r;
format_date(\$ref->[0]->[3]);
push @r,$ref->[0];
format_date(\$ref->[1]->[3]);
push @r,$ref->[1];
format_date(\$ref->[2]->[3]);
push @r,$ref->[2];
format_date(\$ref->[3]->[3]);
push @r,$ref->[3];
format_date(\$ref->[4]->[3]);
push @r,$ref->[4];
format_date(\$ref->[5]->[3]);
push @r,$ref->[5];
format_date(\$ref->[6]->[3]);
push @r,$ref->[6];
format_date(\$ref->[7]->[3]);
push @r,$ref->[7];
format_date(\$ref->[8]->[3]);
push @r,$ref->[8];
format_date(\$ref->[9]->[3]);
push @r,$ref->[9];

push @res,\@r;
}


Заметка кстати, профайлинг Perl показал, что быстрее передавать параметры в функцию по значению, а не по указателю( то есть не так как здесь ;) ). И заменяем все format_date на их код.

my $ref = $mem_cache->get('categories')) ;
my @res;
my $count=10;
foreach my $tmp (@$ref){
my $ref=$dbh->selecall_arrayref(qq[SELECT id,title,text,ts
FROM articles
WHERE category=$tmp
ORDER BY id DESC LIMIT $count],'id');
my @r;

$ref->[0]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[0]->[3]="$3.$2.$1";
push @r,$ref->[0];
$ref->[1]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[3]="$3.$2.$1";


push @r,$ref->[1];
$ref->[2]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[2]->[3]="$3.$2.$1";
push @r,$ref->[2];
$ref->[3]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[3]->[3]="$3.$2.$1";
push @r,$ref->[3];
$ref->[4]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[4]->[3]="$3.$2.$1";
push @r,$ref->[4];
$ref->[5]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[5]->[3]="$3.$2.$1";
push @r,$ref->[5];
$ref->[6]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[6]->[3]="$3.$2.$1";
push @r,$ref->[6];
$ref->[7]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[7]->[3]="$3.$2.$1";
push @r,$ref->[7];
$ref->[1]->[8]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[8]="$3.$2.$1";
push @r,$ref->[8];
$ref->[1]->[9]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[9]="$3.$2.$1";
push @r,$ref->[9];
push @res,\@r;
}

В итоге мы види ужасный неудобоворимый код, который вызывает рвотные рефлексы при одном своем виде. Дальше агрументов много
1) А что делать, если полей в таблице дофига, и все надо форматировать, это ж "каша"
2) А если надо увеличить/уменьшить количество статей, снова лезть в код!
И так далее.. И так далее...Все эти вопросы можно решить при помощи "админки". А админка есть у любого уважающего себя сайта статусом выше, персональная страничка.
На нужно при изменение нужных вам параметров сделать генерацию нужного вам кода.
Например добавить изменение количества выводимых статей с 10 на 5( вполне обычная функция для администраторского интерфейса). Помещаем функцию обработки вывода статей в отдельный модуль например "/lib/Article.pm". Даем права на работу с этим файлом пользователю от которого работает наш скрипт
( ну или обычные авось chmod 777 /lib/Artcle.pm).
ну и в функцию изменения количества статей пишем примерно следующее:
open(Fl,">document_root/lib/Article");

print FL, q{
package lib::Artcle;
use strict;
use base qw[Exporter];
use SiteDB;##connecting to the database $dbh
our @EXPORT = qw(
list
);





sub list{
my $ref = $mem_cache->get('categories')) ;
my @res;
my $count=}.$new_count_value.q{;

foreach my $tmp (@$ref){
my $ref=$dbh->selecall_arrayref(qq[SELECT id,title,text,ts
FROM articles
WHERE category=$tmp
ORDER BY id DESC LIMIT $count],'id');
my @r;

$ref->[0]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[0]->[3]="$3.$2.$1";
push @r,$ref->[0];
$ref->[1]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[3]="$3.$2.$1";


push @r,$ref->[1];
$ref->[2]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[2]->[3]="$3.$2.$1";
push @r,$ref->[2];
$ref->[3]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[3]->[3]="$3.$2.$1";
push @r,$ref->[3];
$ref->[4]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[4]->[3]="$3.$2.$1";
push @r,$ref->[4];
$ref->[5]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[5]->[3]="$3.$2.$1";
push @r,$ref->[5];
$ref->[6]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[6]->[3]="$3.$2.$1";
push @r,$ref->[6];
$ref->[7]->[3]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[7]->[3]="$3.$2.$1";
push @r,$ref->[7];
$ref->[1]->[8]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[8]="$3.$2.$1";
push @r,$ref->[8];
$ref->[1]->[9]=~/(\d{1,4})-(\d{1,2})-(\d{1,2})/;
$ref->[1]->[9]="$3.$2.$1";
push @r,$ref->[9];
push @res,\@r;
\}

return \@res;
\}

1;
};
close(FL);
Ну и далее можем использовать этот модуль у себя в скрипте обычным способом :



sub some_sub{
my $self=shift;
require lib::Article;
my $ref=lib::Article::list();
###some code there
####
}


Всем удачи.

p.s
При помощи генерации кода можно избавиться вообще от последнего цикла обхода категорий.

пятница, 3 декабря 2010 г.

perl, utf8,киррилица и mysql

Вы похвально решили перевести вашу базу данных в utf-8...
Везеде поставили в mysql :
set names utf8;
set charset utf8;

в таблицах проставили новую коддировку по умолчанию,
default charset utf8



Начинаете добавлять из скрипта данные, а вместо них пустые строки в базе данных....
Первое скрипту выставляем коддировку utf8 это вы и сами догадались, а второе
пишем в начале скрипта :
use utf8;
Теперь все работает...Ошибка неприятная, потому perl молчал как партизан, и просто
все менял на пустую строку

понедельник, 29 ноября 2010 г.

Построение множеств в реляционной базе данных

Человеческий мозг интересная штука, особенно интересно наблюдать, как ты работаешь, напряженно думаешь, строишь огромные схемы, чтоб решить какую-то задачу, а потом вдруг озарение и в результате 50 строк предыдущего кода заменяются на 10 нового. Данная заметка родилась примерно так. Поговорим об организации простых математических множест в реляционной базе данных.
Множество это набор элементов, если по простому. Первое, что нужно нам иметь возможность делать - это получать список элементов входящих в множество.

Потребность в решении задачи возникло при составлении списка взаимозаменяемых автозапчастей. Потому первое решение( к сожелению) было примерно таким:

create table crosses( p_key1 int(11), p_key2 int(11) )
Где p_key1 и p_key2 соответственно номера деталей, которые эквивалентны. Если "деталь1" может заменить "деталь2", а "деталь2" может заменить "деталь3", тогда логично предпложить, что "деталь1" может заменить "деталь3", это называется транзитивностью, это не обязательная характеристика элементов множеств, но в нашем примере имеет место. Собственно для эти трех деталей потребуется как минимум две записи. Чтобы охарактеризовать связь между всеми тремя, но это теория. А на практике, чтобы не путаться в запросах, потому что таких деталей может быть больше десятка, вроде бы стало удобно для каждой записи хранить ее кросы. То есть для примера из трех деталей мы получили бы следующий вид :
p_key1 p_key2
"деталь1" , "деталь2"
"деталь1" , "деталь3"
"деталь2" , "деталь1"
"деталь2" , "деталь3"
"деталь3" , "деталь2"
"деталь3" , "деталь1"
Уже настараживает...А теперь введем еще пару факторов такие как
1) Оператор ошибается, и добавляет неправильные элементы в существующие множества
2) Множества эквивалентных элементов всегда пополняется.
И теперь сразу видна неповоротливость данной схемы, что порадило априори кучу ошибок в логике работы, которые подтвердились на практике. Да и количество записей растет со скоростью n*(n-1), где n - количество деталей.
Второе решение сильно напоминающее экзоскелет. Написать переделать таблицу экивалентных деталей на манер экселя то есть :

create table crosses( p_key1 int(11), p_key2 int(11),p_key3 int(11), p_key4 int(11) .... )
Где p_key1, p_key2,p_key3 , эквивалентные детали. Размерность такой таблицы вызывает сомнения, потому что эквивалентных деталей может быть просто дикое количество, даже цифра в 100 уже вызывает сильные опасения. Приведем минусы
1) Добавление одного элемента уже в существующее множество приведет к операции сравнения по ста столбцам в худшем случае.
2) не будем же мы делать в самом деле таблицу из ста столбцов, мы напишем класс или библиотеку, которая работает с несколькими таблицами подобного типа, это нам позволит вроде бы избежать ограничения с размерностью.
3) явно видно что писать придеться снова много, ошибки неизбежны...

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


create table crosses(p_set int(11), p_key int(11) )
Где p_set уникальный идентификатор множества. Алгоритм работы прост. Мы нашли "деталь1", которая эквавалента другой "детале2". Мы начинаем поиск в таблице

SELECT p_set as p_set1 FROM crosses WHERE p_key='деталь1'

SELECT p_set as p_set2 FROM crosses WHERE p_key='деталь2'

Если первый и второй запрос дал результат, то мы на, например, заменяем p_set1 на p_set2 соответственно . То есть произошло слияние множеств.

UPDATE crosses SET p_set= $p_set1 WHERE p_set=$p_set2


Если какой из запросов дал результат, например нам стало известно $p_set2 то добавление нового свойства сводится к

INSERT INTO crosses(p_key,p_set) VALUES('деталь1',$p_set2)

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

LOCK TABLE crosses WRITE;

SELECT max(p_set)+1 FROM crosses
INSERT INTO crosses(p_set,p_key) VALUES($new_set,'деталь1'), ($new_set,'деталь2')
UNLOCK TABLES


Выборка всех элементов множества, в которое входит данный элемент осуществляется
в итоге всегда в фиксированое количество опеаций - две. Например дла "деталь1"

SELECT p_set as s FROM crosses WHERE p_key='деталь1'

SELECT p_key FROM crosses WHERE p_set=$s


P.S
можно и одним запросом...Но так нагляднее

воскресенье, 28 ноября 2010 г.

пятница, 19 ноября 2010 г.

Пишем plugin для FireFox

Давно интересовала эта, но все руки не доходили, вернее не было идеи плагина. Но а потом пришлось, так сказать. Вообще написание плагина мне доставило удовольствие сравнимое с изучением фреймворка Django. Технология написания плагина для fireFox интуитивно понятный процесс. Для быстрого старта вап понадобиться зайти вот сюда :

https://addons.mozilla.org/en-US/developers/tools/builder

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

chrome chrome.manifest defaults install.rdf

./chrome:
content locale skin

./chrome/content:
about.xul adding.xul ff-overlay.js ff-overlay.xul options.xul overlay.js

./chrome/locale:
en-US

./chrome/locale/en-US:
about.dtd adding.dtd options.dtd overlay.dtd overlay.properties

./chrome/skin:
overlay.css

Содержимое папки зависит от выбранных вами опций, мне нужна была кнопка в контекстном меню и настройки. В моем примере мне нужно была отдельная кнопка в
контексном меню браузера. Для того чтоб зарегистрировать что либо объекте браузера, нужно "проверлеить" его основной объект "chrome://browser/content/browser.xul". За это отвечает файл chrome.manifest, приведу пример моего
content ext_add chrome/content/
skin ext_add classic/1.0 chrome/skin/
locale ext_add en-US chrome/locale/en-US/

overlay chrome://browser/content/browser.xul chrome://ext_add/content/ff-overlay.xul

Где ext_add/content/ff-overlay.xul содержит следующее :

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://ext_add/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://ext_add/locale/overlay.dtd">
<overlay id="ext_add-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="overlay.js"/>
<script src="ff-overlay.js"/>

<stringbundleset id="stringbundleset">
<stringbundle id="ext_add-strings" src="chrome://ext_add/locale/overlay.properties"/>
</stringbundleset>

<popup id="contentAreaContextMenu">
<menuitem id="context-ext_add" label="&ext_addContext.label;"
accesskey="&ext_addContext.accesskey;"
insertafter="context-stop"
oncommand="ext_add.onMenuItemCommand(event)"/>
</popup>


</overlay>


Добавить что-то другое можно, воспользовавшись стандартной документацие XUL.
С первых минут написания плагина возникнет вопрос, как получить доступ к текущему документу - через псевдоним текущего окна 'content' , например

var e =content.document.createElement('div');


Расскажем про диалоговые окна, не юзать же нам один promt и alert. В firefox-e в качестве шаблона используют XML-подобный язык XUL . Например пишем файлик типа adding.xul




<!DOCTYPE dialog SYSTEM "chrome://ext_add/locale/adding.dtd">
<dialog width='300' title="&about;" orient="vertical" autostretch="always" onload="sizeToContent()"
buttons="accept,cancel"
ondialogaccept="return ext_add.post2();"
ondialogcancel="return ext_add.cancel_post();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="overlay.js">

<groupbox align="center" orient="horizontal">
<vbox>
<label control="texttitle">&title.label;</label>
<textbox id="texttitle">
<separator class="thin">
<label control="cattitle">&cattitle.label;</label>
<menulist id="'cattitle'">
<menupopup>
<menuitem label="IT-сфера" value="'9'/">
<menuitem value="19" label="'Авторы'">
</menupopup>
</menulist>
<separator class="thin">
</vbox>
<groupbox align="center" orient="horizontal">
<caption label="'&desc.label;'/">
<textbox multiline="true" rows="'5'" id="'desc'">
</groupbox>

</groupbox>
</dialog>



Код вызова диалога :
var win = window.openDialog( "chrome://ext_add/content/adding.xul",
"addingWindow", "chrome,centerscreen",
{text_data: this.commit_text,
main: this,
hst:content.window.location.host,
url:content.window.location.href });







Первый параметр указывает относительный путь к файлу дескриптору adding.xul ,
второй параметр - наименование окна,
третий - позиционирование,
далее идут параметры, которые мы передаем в диалоговое окно.
К параметрам можно обратиться следующим образом :

var plugin=window.arguments[0].main;
var h=window.arguments[0].hst;
var l=window.arguments[0].url;


Стандартная документация тут

Обратите внимание в adding.xul -в теге dialog описывается наличие кнопок OK, Cancel
а так же их обработчики через атрибуты ondialogaccept, ondialogcancel,
которые находяться в файле overlay.js.
Обработчик ondialogaccept :
post2 : function(e){
var cat=document.getElementById('cattitle').value;
var t=document.getElementById('texttitle').value;
var desc=document.getElementById('desc').value;
var txt=window.arguments[0].text_data;
txt=txt.replace(" "," ");
txt=txt.replace("","");
txt=txt.replace("&","");
txt=txt.replace("&","");
txt=''+txt+'';
var plugin=window.arguments[0].main;
var h=window.arguments[0].hst;
var l=window.arguments[0].url;
var params='cat_id='+cat;
params+='&text='+txt;
params+='&title='+t;
params+='&desc='+desc;
params+='&host='+h;
params+='&url='+l;

plugin.send_http(plugin.post_url,params,plugin.debug_func);
return true; },

Одним из важных элементов, которые вам понадобяться в начале написание плагина это ваши настройки...
Диалоговое окно настроек описавается тоже при помощи XUL.
Интересней, как получить сохраненные настройки :
//находим компонент настроек
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
// находим ветку относящуюся к нашему плагину в данном случае 'ext_add'
prefs = prefs.getBranch("extensions.ext_add.");
// получение настроек
login=prefs.getCharPref("loginpref");
pwd=prefs.getCharPref('pwdpref');
// проверка наличия настроек
if(!prefs.prefHasUserValue("loginpref")) {
alert("Заполните данные для авторизации");
return; }

Более детально читаем тут
Для любого приложения важна среда программирования, в данном случае удобно сделать отдельный профайл FireFox например Debug. Здесь подробно написано как это сделать под разными OS, в Linux-е я же просто запустил команду
" /usr/bin/firefox -no-remote -P dev". В новом профайле советую установить несколько плагинов для разработки.

Extension Developer's Extension
набор утилит javascript-shell, regexp evaluater, архиватор плагинов и т.п
Firebug - анализатор DOM -модели и не только
Tamper - дампер сетевых соединений, хорош, если вы активно используете Ajax.

Кстати про Ajax, из плагина можно обращаться к сторонним доменам,
если запросы выполняются из контекста вашего плагина.
Ajax работает через обычный XMLHttpRequest.


P.S Вообщем все интутивно понятно и знакомо, что не может не радовать.

среда, 17 ноября 2010 г.

Люблю python :)

Захотелось мне распарсить документ html, порылся я в мануалах наткнулся на пару
модулей и выбрал HTMLParser. Скажу сраз до меня не сразу доперло, что надо сделать класс, унаследовав его от HTMLParser сделать что-то типа этого
from HTMLParser import HTMLParser

class MyHTMLParser(HTMLParser):

def handle_starttag(self, tag, attrs):
print "Encountered the beginning of a %s tag" % tag

def handle_endtag(self, tag):
print "Encountered the end of a %s tag" % tag
А потом запустить его

p=MyHTMLParser()
p.feed('some text here')
Все хорошо, написал я свой класс, запустил, и получил что-то типа этого
HTMLParser instance has no attribute '[тут какой-то его внутренний атрибут]'

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

from HTMLParser import HTMLParser

class MyHTMLParser(HTMLParser):
def __init__(self,d,f)
self.d='ss'
self.f='dd'

def handle_starttag(self, tag, attrs):
print "Encountered the beginning of a %s tag" % tag

def handle_endtag(self, tag):
print "Encountered the end of a %s tag" % tag
Вы начнете получать ту белеберду, думаю вы уже догадались в чем проблема - все дело в методе __init__ , который, как я понял , переопределяет базовый со всеми его атрибутами. Потому там надо добавить вызов super().

пятница, 12 ноября 2010 г.

Сравнение FireFox и Microsoft IE. Или почему FireFox лучше :)

Так получилось, что понадобилось мне написать плагин для FireFox. Для сравнения решил глянуть как подобный плагин, или если быть точным будет писаться для IE. Полемику
разводить бессмысленно, главную мысль можно выразить просто приведением примера кода обоих плагинов. Например для FireFox -а он бы выглядел так :

  1. var test = {
  2. onload: function(aEvent){
  3. var doc = aEvent.originalTarget;
  4. if (doc instanceof HTMLDocument && doc.location.href != "about:blank" && !doc.defaultView.frameElement) {
  5. var s = doc.createElement('script');
  6. s.type = 'text/javascript';
  7. doc.body.appendChild(s);
  8. s.src = 'script.js';
  9. }
  10. }
  11. }
  12. window.addEventListener("load", function(){
  13. var appcontent = document.getElementById("appcontent"); if (appcontent) {
  14. appcontent.addEventListener("DOMContentLoaded", test.onload, true);
  15. }
  16. }, false);
Интуитивно понятная всем DOM -модель.. или вот код из моего плагина :


send_http : function(u,params,func){
var HttpMethod='POST';
var R=this.REQ;
if(R)
{
R.onreadystatechange=func;
R.open(HttpMethod,u,true);
R.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
R.send(params);
}else{
alert("I cant create browser");
}




},

get_http : function() {

if(window.XMLHttpRequest){

this.REQ=new XMLHttpRequest();
return this.REQ;
}else{
alert("sorry,change you browser please");
return null;
}





},
То есть всем хорошо знакомый старый JavaScript и написание диалогов при помощи XUL - подмножества XML. А вот что примерно мы увидим в IE с ее COM - моделью:

Тут происходит привязка к событию DISPID_DOCUMENTCOMPLETE, и в случае возникновения выполняем код:
  1. VARIANT vFlags = { 0 };
  2. VariantInit(&vFlags);
  3. vFlags.vt = VT_I4;
  4. vFlags.intVal |= navNoReadFromCache;
  5. BSTR testUrl = SysAllocString(L"javascript: код инъекции");
  6. mWebBrowser2->Navigate(testUrl, &vFlags, NULL, NULL, NULL);

Тут мы видим что-то абсолютно новое, непонятное и далекое от нас если мы незнакомы с документацией COM. Итог "без комментариев"

среда, 10 ноября 2010 г.

Разбиение чисел на разряды в php

Зашел я статистику блога, и увидел там, что на блог зашло
аж ...секунду 12 пользователей по запросу :
"php regexp разбиения числа на разряды"

Вообщем дорогие php-шники, помогу, чем могу...люблю помогать людям
есть такая прекрасная функция number_format в php, которой нет к сожелению в perl - сказывается разница в возрасте...

$number= 1234.56;

// английский формат (по умолчанию)
$english_format_number = number_format($number);
// 1,234

// французский формат
$nombre_format_francais = number_format($number, 2, ',', ' ');
// 1 234,56

$number = 1234.5678;

// английский формат без разделителей групп
$format_number = number_format($number, 2, '.', ' ');
// 1 234.57

?>

скопипастено отсюда - http://www.php.ru/manual/function.number-format.html

P.S
Кликнете на рекламу, не будь злюкой...я кофе хочу

понедельник, 8 ноября 2010 г.

Инновации, что учить начинающему специалисту

Согласно новости , мы наблюдаем новый виток развития индустрии. Так что дорогие студенты, в ближайшее время надо срочно начинать учить Android и сопутствующие сервисы.
Очень хорошие статья для так называемого "квик старта "
http://habrahabr.ru/blogs/android/99323/

http://habrahabr.ru/blogs/android/98704/

Удачи всем.
p.s
Новость радует, потому что политика гугла, имхо полиберальней, чем гламурного яблока.

Google и mod_pagespeed

Оно само не ничего не сжимает

http://code.google.com/intl/en/speed/page-speed/docs/using_m...

Могут перестать работать сложные js скрипты, например CKEditor.

Из личного опыта простейшая страница поиска с ajax стала дико тормозить, чего раньше не наблюдалось.

Итого незачет однозначно...

p.s

И вообще почему не использовать nginx в режиме проксирования.


пятница, 22 октября 2010 г.

mysql, max_conntection и too many connections


mysql > set global max_connections = 200;

И да будет нам счастье

среда, 6 октября 2010 г.

Работа с идеями, их так много а я один.

Думаю многие сталкивались с некой, что ли проблемой. Вот в моей голове появилась идея. И после недолгих раздумий она показалась вам гениальной!
Дальше следует запись примерного названия идеи и...поставление ее в список задачь на будущее. А дальше следовала реализации наиболее понравившейся идеи( выбрал из десятка и начал), а потом... Два варианта события
1) в процессе реализации все так затянулось под напором объема работы или нового потока идей, что проект заглох.
2) в процессе реализации идея, как оказалось, нефига не гениальная.
3) идея оказалось гениальной, вы ее реализовали и почеваете на лаврах.

Итак забудем про пункт три. Ответим на пункты 1 и 2. В первом случае вам поможет, так называемые, циклы разработки, о них много написано. А вот наболевший для меня второй пункт решается до ужаса просто. Надо всего лишь изложить идею на бумаге, не просто название, а подробное описание.
Как правило, составив описание, вы довольно адекватно сможете оценить ценность идеи. Для себя я это объяснил тем, что человек не может хранить в голове больше семи объектов, а записав он видит полную картину.
Всем удачи

суббота, 18 сентября 2010 г.

Про сервисы Агава, хостинг,colocation etc

Сидел и думал, каким образом у веб-мастера происходит процесс выбора новой площадки для своего сайта, будь то хостинг или колокейшен. Действительно, а что тут думать?! Набираем в адресной строке google.com, yandex.ru ну и дальше по пристрастием.. В строке поиска пишем "хостинг" или "colocation" и дальше по ссылкам. Выбрали хостинг, цена подходит, имя у компании громкое..Заключаем договор?! Ан нет, брат не поленись, и набери в поисковой строке "отзывы хостинг Агава", и тогда ты попадешь на эту страничку( может быть:) )....
И здесь ты узнаешь, что colocation( подозреваю что и хостинг) у агава галимый, плохой, очень плохой, х...вый и так далее. Мы пользуемся их сервисом около(страшно подумать) больше трех лет, проблемы случаются регулярно с печальным постоянством :(, переехать не получается - ибо находимся мы не в москве, да и не корпорация мы, чтоб командировку себе устраивать туда, и возится с ним несколько дней...

P.S
Как оказалось самое интересное будет в комментариях

пятница, 17 сентября 2010 г.

Пишем понятный код

Давно хотелось написать что-то подобное, но все не знал как начать. И похоже начну с собственных боков, в принципе есть что в выражение: "на чужих ошибках люди учатся,а на чужих делают каръеру ". Вот обнаружил у себя с одной стороны безобидный вроде бы код :

REQ=getHttp();
var login=document.getElementById('login').value;
var email=document.getElementById('email').value;

var name=document.getElementById('name').value;
var paswd1=document.getElementById('paswd1').value;
var paswd2=document.getElementById('paswd2').value;

var str='login='+login+'&email='+email+'&name='+name+'paswd1='+paswd1+'&paswd2='+paswd2;
SendRequest2(REQ,'regis.php',str,'POST',ajax_regis);


Мне понадобилось добавить сюда три дополнительных поля город, телефон и примечания в итоге, следуя принципу психологической инерции я чуть было не оставил в работе вот этот вариант :

REQ=getHttp();
var login=document.getElementById('login').value;
var email=document.getElementById('email').value;

var name=document.getElementById('name').value;
var paswd1=document.getElementById('paswd1').value;
var paswd2=document.getElementById('paswd2').value;
var phone=document.getElementById('phone').value;
var comments=document.getElementById('comments').value;
var city=document.getElementById('city').value;
var str='login='+login+'&email='+email+'&name='+name+'&paswd1='+paswd1+'&paswd2='+paswd2+'&phone='+phone+'&comments='+comments+'&city='+city;
SendRequest2(REQ,'regis.php',str,'POST',ajax_regis);

Так бы его и оставил, если б не начав его проверять я не понял, что
а) приходится постоянно переключать внимание со строки str на операции типа document.getElementById
б) строка str тяжело компоновалась в мысленное представление

Чтоб понять что не так, просто взглянем наш внутренний( тобишь тот, что у нас в голове) алгоритм проверки правильности строки
str.
Пошагово
1) наше внимание фокусируется на элменте "
login="
2) фокусируется на переменной
login
3) ищет в коде
var login=document.getElementById('login').value;
4) проверяет соответствие переменной login и значение аргумента функции
getElementById
5) возвращается к строке str и ищет следующий элемент типа "login="
6) находит, и заносит элемент "
login=" во внутреннюю допустим память, как обработанный
.....
повторяет шаг 1-6 для этого элемента добавляется дополнительный шаг поиска обрабатываемого элемента в списке уже обработанных.
......
Итак, сколько там у нас элементов, восемь?! А теперь приведу справку, при всей гениальности человека, среднестатистический индивид способен держать в оперативной памяти до 7 объектов ( наверно ученые имели ввиду оперировать ими ), да данный тезис наталкивает на дискуссию , но в приведенном примере алгоритма объектов явно больше. Да наш мозг работает быстро, и нам кажется, что мы даже не так обработаем данную строку, наверняка что-то оптимизируется, но это думаю потом, а первый раз по логике он сработает именно так, иначе появится вероятность ошибки.
После этих умозаключений предыдущий код переписал следующим образом:

REQ=getHttp();
var str='';
var login=document.getElementById('login').value;
str+='login='+login;
var email=document.getElementById('email').value;
str+='&email='+email;
var name=document.getElementById('name').value;
str+='&name='+name;
var paswd1=document.getElementById('paswd1').value;
str+='&paswd1='+paswd1;

var paswd2=document.getElementById('paswd2').value;
str+='&paswd2='+paswd2;


var phone=document.getElementById('phone').value;
str+='&phone='+phone;

var comments=document.getElementById('comments').value;
str+='&comments='+comments;

var city=document.getElementById('city').value;
str+='&city='+city;

SendRequest2(REQ,'regis.php',str,'POST',ajax_regis);

Вроде бы алгоритм проверки тот же, но длинных переводов фокуса внимание нет. Как по мне намног удобнее, кстати раньше в IBM был негласный( а может добровольно принудительный) программисткий стандарт - код любой функции не должен привышать в длинну больше 25 строк. Почему именно 25 строк, а потому что именно столько вмещалось в текстовом редакторе DOS.

вторник, 3 августа 2010 г.

Собеседование


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

а) определить что мне от человека нужно

б) составить задания для их проверки

ответы :

а) для меня оказалось не важно ,какой язык знает кандидат, главное чтоб он
согласился писать на нужном мне(Perl в данном случае) и соображал головой..
б) Потому б свелось к списку простых тестов на соображалку

Собственно тесты

1.Объясните что такое База Данных в трёх предложениях, так как это сделал бы ваш 8-летний племянник


2.Вы были уменьшены до размеров 5-центовой монеты…


… и ваша масса была пропорционально уменьшена соответственно вашей плотности. Теперь вас бросили в пустой стакан блендера. Ножи начнут движение через 60 секунд. Что делать?


3.У вас есть 8 шаров одинакового размера…


...7 из них одинакового веса, а один весит чуть больше остальных. найти мяч, который тяжелее остальных, используя баланс и только два взвешивания?


4.Вы — капитан пиратского судна…


… и ваша команда собирается голосовать, как разделить награбленное золото. Если с вами будет согласно меньше чем половина пиратов, вы умрёте. Как вы поделите золото, так, чтобы получить хорошую часть добычи, но всё же остаться в живых?

5.Вам надо проверить, правильно ли записан ваш телефон у Боба…


… но вы не можете его спросить об этом прямо. Вам надо написать вопрос на бумажке и отдать Еве, которая отнесёт её Бобу и принесёт обратно ответ от него. Что вы должны написать на бумажке, кроме прямого вопроса, так, чтобы Боб смог понять сообщение, а Ева не смогла узнать ваш номер телефона?


6.Разработайте алгоритм работы лифта. (Домашнее задание)

Оцените его эффективность.









понедельник, 26 июля 2010 г.

MySql и leftmost

Если индекс составной (a, b), то операция сравнения идёт слева направо. Тогда индекс будет справделив для вариантов
WHERE a =?
WHERE a =? AND b =?
Но индекс не будет использоваться в случае
WHERE b =?

вторник, 18 мая 2010 г.

Инь и янь , и с чем его едят...или о мужчинах и женщинах

Институт отношений на самом деле это всего лишь какая-то подсознательная модель , которая появляется у человека в процессе становления его, как личности .....Кстати Вопрос откуда появилась вообще потребность брака тоже открыт(хотя на самом деле закрыт ,но сейчас не об этом) ))) А становление этой модели строится на смеси детских сказок,мелодрамм,романов, а потом бытовых реалий - нехилая такая смесь представьте, порно со сказками))) В условиях современного общества ,когда сила перестала быть определяющим фактором - выбор стал предоставлен женщине(раньше было не так,хотя я тогда не жил не знаю) , поэтому она и является инициатором начало этих самих отношений(спорьте сколько влезет ,но исключения подтверждают правило) поэтому и мужчина воспринимается как субъект этого проекта..Единение душ вы мне скажете - да это тоже придумано, а мы просто следуя нашей мечте(ведь человек по сути своей эгоист) не прочь себе нарисовать замок из песка для двоих,и строить его вдвоем.Вопросы возникнут когда каснется деталей замка . Вернемся к общему ,а про частное пока забудем. Что из этого следует ,да ничего...Раньше было наоборот ,но сути не меняет ....Мы оказываемся в стране с двумя сторонами , которые по сути противодействуют друг другу,только одна в них ведущий,а другой типа второй номер ,но противостояние налицо ...
что дальше,дальше все легко . один требует понимания,ласки,материальных ценностей, внесения посильной лепты в проект совместного жилья, причем , как любая власть требует полной отдачи....Другой сопротивляется , и наученный пропагандой , что его полу не свойственны скандалы уступает, думая что поступает правильно. Только это "правильно" , как правило рассказано нашими матерями ,кто не догадался они тоже женщины)) Почему ими ,потому что почему-то в обществе нашего сраного гумманизма считается что детей должна воспитывать женщина...Что дальше ?скажете я маразматик))а я посмеюсь и отвечу , что Австралийские ученые проанализировав днк там и еще какие-то там физиологические признаки пришли к выводу , что через пару сотен лет мужчин, как вида,не останется )))вы думаете это правильно ,может и правильно ,раз мужики становяться так зависимы от девушек , что не могут их поставить на место ,чтоб была хоть сраная демократия,а не сплошная истерика

Выбор машины

mysterio (01:03:29 30/01/2010)
но краааасивая
iRBIS (01:04:00 30/01/2010)
да, красивая
iRBIS (01:07:21 30/01/2010)
У меня за спиной стоит очеь красивый Epson C63. Я когда его покупал почитал обзоры, этож Эпсон качество картинки, бла-бла-бла. А на самом деле: маленькие картриджи, которые до конца нельзя выработать (потому что чипованные. и принтер сам решает когда всё), дофига чернил уходит для чистки дорогущих головок в папмперс (головки не сменные), головки засираются, бошки мажут, памперс менять только в сервисе. Короче я до сих пор когда смотрю на этот принтер чувтсвую себя долбоёбом.

пятница, 26 марта 2010 г.

Перенос django на боевой lighttpd

Почитали мы маны да и запустили наше djangoapp, как fastcgi приложение....
А lighttpd настроили подобным образом


server.document-root = "/home/user/projects/domain_com"

fastcgi.server = (
"/mysite.fcgi" => (
"main" => (
"host" => "127.0.0.1",
"port" => 2605,
"check-local" => "disable",
)
),
)



alias.url = (
"/media/" => "/usr/lib/python2.5/site-packages/django/contrib/admin/media/",
)

url.rewrite-once = (
"^(/media.*)$" => "$1",
"^(/site_media.*)$" => "$1",
"^/mysite.fcgi(/.*)$" => "mysite.fcgi$1",
"^(/.*)$" =>"/mysite.fcgi$1",
)

Сама django запускается стандартной командой из руководства:

./manage.py runfcgi method=threaded host=127.0.0.1 port=2605 pidfile=mysite.pid

Вроде все работало, но тут решил заглянуть в админку ,которая отказалась работать...Налицо была ошибка в неправильном формировании путей ссылок..Упорно меня кидало куда-то к mysite.fcgi. После гугления нашолся ответ - надо в settings.py переменную FORCE_SCRIPT_NAME установить пустой строкой - и все заработает.
Жирным шрифтом выделил так же подключение css и js файлов админки.Сами по себе как на тестовом они подключаться отказались.

четверг, 28 января 2010 г.

Что может быть страшного ubuntu,msyqld,datadir

Вообщем ничего вроде страшного в замене datadir в mysql...
Я тоже так думал..но поменяв datadir в /etc/my.cnf меня ждал сюрприз..
Куча страшных слов в syslog и неработающий mysql...
Вообщем официальный сайт mysql приказал курить траву и ловить дзен...
Магические chmod -Rf 777 * не помогали,и когда я решил ,что буду ночевать в офисе, неожидано пришел ответ в виде демона apparmor...Вообщем весь трабл в нем...находим его настройки в папке /etc/apparmor.d , а там обнаруживаем
файлик-профайлик для mysql...Хм...Я так понял этот демон следит за тем,чтобы определенные приложения могли обращаться только к определенным папкам и судя по логу ошибок в syslog следит за этим на уровне ядра ... Меняем профайл для mysql перегружаем обоих и ....все работает....

p.s
Вкурив как он работает ,можно ж туда апач запихнуть, на случай если криворукие пхп-исты провтычат какую-то дырку у себя