воскресенье, 25 октября 2009 г.

Перезапуск Планеты ДОУ

У нас на developers небольшая реорганизация.

Вместо одно большой планеты будет несколько поменьше, тематических. И называться это теперь будет "сообщества". Каждый может создавать свое собственное сообщество, пригласить туда других участников, добавлять/удалять те ленты, которые считает нужным. Выглядеть это может так:

http://www.developers.org.ua/members/c/1-Python/

Называется это "сообщества" потому что кроме собственно ленты блогов (планеты) планируется еще дополнительная функциональность. А каждое из сообществ планируется тематическим, например Python, Embedded, Стартапы и т.п.

Инструкция по переносу своей ленты блога

Мы не будем проводить автоматический импорт существующей ленты блогов в новые сообщества. Вы можете перенести свою ленту самостоятельно. Это просто.

  1. Откываете страницу создать сообщество. Ищите в списке слева подходящее сообщество среди уже имеющихся. Если ничего не выбрали - заполняете форму создаете свое.

  2. Если вы выбрали уже существующее —- странице сообщества нажимаете кнопку "Присоединиться"
  3. На странице сообщества идете по ссылке "Добавить ленту" и вписываете адрес вашей RSS/Atom ленты
  4. Ждете. Содержимое ленты появляется в планете. На странице "Управление лентами" показываются ошибки агрегации, если были. Для админа там же есть линк "Обновить СЕЙЧАС", для немедленной загрузки всех фидов.


пятница, 28 августа 2009 г.

hg rebase

Как включить новые changesets в вашу копию mercurial-репозитория? Несколько равноправных вариантов:

1. hg pull ; hg update; # затем hg merge; hg commit, если необходимо
2. hg pull -u # затем hg merge; hg commit
3. hg fetch # делает hg merge/commit сам, если необходимо
4. hg pull --rebase

До недавнего времени я использовал №3, т.к. он самый "экономный" по времени. Теперь вот перехожу на №4, т.к. он не "замусоривает" историю лишними "automatic merge" ченджсетами.

Чтобы работало rebase нужен hg 1.1 и строка "hgext.rebase =" в конфиге .hgrc.

Выглядит это так:
$ hg pull --rebase
pulling from ssh://ischenko@.../
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
saving bundle to /home/max/projects/../.hg/strip-backup/b34388790a07-temp
adding branch
adding changesets
adding manifests
adding file changes
added 3 changesets with 4 changes to 4 files
rebase completed
$ hg st

Читать больше: http://mercurial.selenic.com/wiki/RebaseProject.

понедельник, 17 августа 2009 г.

прогрессивная система прав для пользователей

Как работает система разграничения прав пользователей в традиционной CMS, такой как Wordpress или Django?

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

Что сделали разработчики stackoverflow.com?

Они автоматизировали процесс управления правами пользователей, используя понятие "кармы" (т.е. внутренний рейтинг пользователя). Чем выше карма пользователя, тем больше действий в системе ему доступно. Очень простая концепция.

Такая система имеет два существенных преимущества перед традиционной системой прав:

  1. Снижаются затраты на администрирование системы, т.к. она теперь по сути само-управляемая и не требует вмешательства в 95% случаев.
  2. Поощрение пользователей к наполнению сайта (UGC). Чем больше пользователь работает с сайтом, тем выше у него карма и автоматически - выше полномочия. Пользователи любят, когда их ценят.
Главный же недостаток - она сильно сложнее в реализации.

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

Для простых сайтов такая система - явный over-engineering. Но для сайтов посложнее я бы ее обязательно попробовал.

среда, 29 июля 2009 г.

Тупой Django ORM

...не умеет Identity Map:

>>> obj = AutoService.objects.get(id=31) # select from sto_autoservice id=31
>>> obj.carwash # select from sto_carwash where parent_id= 31
>>> obj.carwash.parent # и СНОВА select from sto_autoservice id=31

В SQLAlchemy последнего запроса не было бы, благодаря Identity Map. Да и вообще мог быть всего один запрос если сконфигурировать lazy loader.

Тупой.

суббота, 11 июля 2009 г.

работа с vendor branches в mercurial

Одна вещь, которой мне не хватало после перехода на Mercurial - это vendor branches.

На ДОУ мы используем Wordpress и несколько других сторонних библиотек. Причем не просто используем, а вынуждены вносить мелкие правки тут и там для наших нужд. С выходом каждой версии того же WordPress эти правки тоже нужно переносить.

Работая с Subversion мы для этих целей использовали vendor branches, благо даже инструкции для wordpress + vendor branches нашлись. У Mercurial специального решения этой проблемы нет, разработчики советуют разное.

Опишу, к чему в итоге пришел я, может кому-то пригодится. Идеи по улучшению приветствуются.

Вкратце идея такова:
  1. Для Wordpress (и других библиотек) создается отдельный репозиторий, site-vendor, в который загружается "чистый" код библиотек.
  2. В рабочем репозитории хранится отдельная копия этих же библиотек, но уже с нашими локальными правками.
  3. По мере выхода новых версий, они загружаются поверх предыдущей версии в site-vendor, проставляется тег с номером новой версии.
  4. По тегу (версии) делается diff (патч) между двумя версиями библитеки, который и применяется к копии библиотеки в рабочем репозитории.
Смысл отдельного репозитория в том, что он позволяет сделать three-way merge, который является аналогом vendor branches из svn.

Mercurial умеет делать несколько бранчей в одном репозитории (named branches), но оно неудобное в использовании и вообще считается "advanced". Отдельный репозиторий проще.

Вот так примерно это выглядит в работе:

$ cd /path/to/site-vendor
$ rm -fr wordpress/
$ tar xzf latest.tar.gz # новая версия библиотеки
$ rm latest.tar.gz
$ hg addremove
$ hg status
$ hg ci -m "wordpress 2.8.0 upgrade"
$ hg tag wordpress-2.8.0
$ hg export -a wordpress-2.8.0 > /tmp/wp28.patch
$ cd to /path/to/wc
$ hg import /tmp/wp28.patch # ждем patch FAILED
$ hg st -un # просматриваем .rej файлы и патчим вручную
$ hg ci -m "updated to wordpress 2.8"

вторник, 28 апреля 2009 г.

python tip: быстрая настройка virtualenv

Во всех своих Python проектах я сейчас пользуюсь замечательным инструментом virtualenv, который позволяет создавать "приватную" копию Python специально для проекта. Таким образом решаются возможные конфликты по версиями библиотек между разными проектами.

Эта функция из моего ~/.bashrc упрощает подключение приватной копии Python для выбранного проекта. Может и вам пригодится.

function py () {
for dir in . ..; do
if [ -d $dir/py ]; then
source $dir/py/bin/activate;
return;
fi
done
echo "py dir not found, configuring virtualenv here"
which virtualenv >/dev/null && virtualenv py && source py/bin/activate
}

вторник, 21 апреля 2009 г.

использование "глобальных" переменных в Python

Маленький пример кода из документации Pylons:
from pylons import url
print url.current() # prints /foo/bar if this is request for /foo/bar
Чем является объект url? Переменной (глобальной) в pylons/__init__.py? А может это модуль url.py у которого есть функция current? С точки зрения Питона разница небольшая - и то и то есть ключ в каком-то словаре имен (Namespaces are one honking great idea).

Так почему тогда это так "напрягает" некоторых разработчиков?

Да-да, все мы читали про Singletons are evil и глобальные переменные это тоже "плохо". Но почему? Точнее не так - в питоне вы используете кучу глобальных переменных, которые "появляются" через ключевое слово import. E.g.: import urlllib; urllib.open(). Чем же import pylons; pylons.request.environ() хуже?

Псевдоглобальные объекты, типа pylons.request, удобны тем, что они доступны через простой и понятный интерфейс, который любой программист на Python хорошо знает (import). Простота - это хорошо.

Да, для создания таких объектов приходится использовать thread-locals, ну и что? Это implementation detail.

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

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

Плохо это тем, что т.к. абстракции типа request или database нужны много где, код в итоге загромождается и усложняется из-за необходисти "носить" с собой эти объекты, которые могут понадобится на 2-3 уровня ниже по стеку.

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

И что же у меня получается? Код без использования "глобальных" переменных - "правильнее", а с ними - "удобнее". Прямо как Pascal vs. C. ;)

Может, действительно pylons.url и прочие - зло, а я - ленивый программист? Хотя лень у нас недостатком вроде и не считается...

update: в комментариях очень познавательная информация про библиотеку Contextual, которая решает проблему глобально-доступных объектов, но не использует thread locals.