software simian's typewritings

как добавить pubsubhubbub в блог на django

, comments: 0

наверное, вначале стоит пояснить, что такое PubSubHubbub. Эта технология была придумана недавно как замена веб-пингам, позволяющим оповестить всех заинтересованных, что у вас в блоге появилась новая запись. Для этого при добавлении записи нужно было отправить http-запрос на один или несколько специализированных серверов, которые дальше сами рассылали уведомления всем подписавшимся. PubSubHubbub развивает эту идею дальше, добавляя в этот запрос само содержимое поста, так что заинтересованные сайты не ломятся все сразу в ваш блог, чтобы скачать фид и вынуть из него новый текст. Однако для большего нашего удобства работает и старый механизм: достаточно «пингануть» подходящий сервер, и он сам скачает фид, извлечёт из него пост, и разошлёт его подписчикам

кратко процесс можно описать так:

  1. вы добавляете запись
  2. ваш блог отправляет серверу сообщение «в таком-то фиде новое содержимое»
  3. сервер скачивает фид и находит в нём новый пост
  4. сервер отправляет всем подписанным на обновления этого фида новый пост

итак, что нужно сделать для подключения вашего блога на django к этой радости? Во-первых, выбрать PubSubHubbub-сервер. Я сам пока знаю только один: http://pubsubhubbub.appspot.com/. Пропишем его в настройках (settings.py):

PUSH_URL = u'http://pubsubhubbub.appspot.com/'

затем нужно скачать и установить питоновский модуль для публикации в PuSH. Установка после распаковки делается обычным sudo python setup.py install

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

from pubsubhubbub_publish import publish
publish(settings.PUSH_URL, u'http://your.blog/feed.url')

но это самый простой вариант, мне хочется оповещать ещё и об обновлениях в фидах тегов:

from pubsubhubbub_publish import publish
from django.core.urlresolvers import reverse as reverse
params = [settings.PUSH_URL, u'http://your.blog/feed.url']
params.extend(map(lambda tag: 'http://your.blog%s' % (reverse('tag_feed', args=[tag])), tags))
publish(*params)

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

<link href="http://pubsubhubbub.appspot.com/" rel="hub" />

но в django для фидов используется навороченный Syndication Framework, поэтому придётся унаследоваться от стандартного класса отрисовки фида Atom1Feed и добавить строчку вот таким образом:

class PubSubHubbubEnabledFeed(Atom1Feed):
    def add_root_elements(self, handler):
        super(PubSubHubbubEnabledFeed, self).add_root_elements(handler)
        handler.addQuickElement(u'link', attrs={u'rel': u'hub', u'href': settings.PUSH_URL})

после этого в определениях классов фидов нужно заменить feed_type = Atom1Feed на feed_type = PubSubHubbubEnabledFeed

всё, добавление PuSH завершено! Пройдёт некоторое время, пока заинтересованные сайты узнают, что ваши фиды транслируют обновления таким способом, и после этого новые записи будут появляться на них практически моментально

ну а если вам хочется свести код минимуму, то наверное link можно прописать в фиде вручную, и вместо вызова метода publish просто отправлять на сервер запрос с параметрами hub.mode=publish&hub.url=yourfeedurls

кстати, вот обсуждение, которое сподвигло меня на доработку блога и этот пост

оптимизация userjs: порядок событий загрузки

, comments: 1

я уже писал о том, что в опере код user javascript может быть исполнен в разное время в зависимости от расширения файла. Впрочем, многим скриптам нужно запускаться только тогда, когда весь DOM уже загружен. С привязкой к событиям load и DOMContentLoaded у меня не всегда всё было гладко (как выяснилось, из-за глупых опечаток), поэтому сейчас мне захотелось досконально разобраться, какой из вариантов привязки лучше всего использовать.

итак, я быстро набросал простенький html и два почти идентичных скрипта: test.js и test.user.js, отличающихся только расширением и строчкой идентификации внутри. В пустом профиле оперы я положил скрипты в папку userjs, и открыл сам документ. Вот что появилось после этого в консоли:

test.js: start
html: end of head
html: end of body
test.js: document.DOMContentLoaded (capture)
test.js: document.DOMContentLoaded
test.js: window.DOMContentLoaded (capture)
test.js: window.DOMContentLoaded
test.user.js: start
test.js: document.load (capture)
test.js: document.load
html: document.onload
test.user.js: document.load (capture)
test.user.js: document.load
test.js: window.load (capture)
test.js: window.load
html: body.onload
test.user.js: window.load (capture)
test.user.js: window.load

какие из этого можно сделать выводы?

  1. нет смысла использовать window.opera.addEventListener для таких событий: ни один из вариантов с ним не сработал
  2. как и было заявлено, родной опере формат .js (в отличие от «приблудного» от greasemonkey .user.js) исполняется до начала парсинга документа и, соответственно, до события DOMContentLoaded. Поэтому в скриптах лучше всего использовать вызов document.addEventListener('DOMContentLoaded', handler, true), не забывая про последний параметр, как нередко делал я : )
  3. код из файлов .user.js исполняется до события load на документе, поэтому в них можно использовать document.addEventListener('load', …), но наверняка можно обойтись и без него

подозреваю, что порядок событий load, назначенных разными способами, представляет интерес только для разработчиков браузеров, вынужденных трудиться над совместимостью. Кое-что в нём логично, что-то удивительно (например, body.onload идёт после document.load и даже window.load). Впрочем, теоретически это исследование может помочь несчастному, занимающемуся поддержкой кривого кода, но мне хочется верить, что проблем с этим ни у кого не возникнет : )

исключения в обработчиках аякс-запросов

, comments: 0

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

onException: function logException(request, exception) {
  // handle exception
}

или зарегистрировать глобальный обработчик:

Ajax.Responders.register({ onException: logException });

Однако при этом может возникнуть коллизия с другой фичей фреймворка: автоматическим исполнением пришедшего с сервера яваскрипта. По умолчанию установлено evalJS: true, и если в заголовках обнаружится что-то вроде Content-Type: text/javascript, прототайп попробует исполнить тело ответа как код, словит исключение, и передаст его в onException.

бороться с этим явлением можно двумя способами: либо добавлять в параметры каждого аякс-запроса evalJS: false, либо отдавать json с Content-Type: application/json. Первый способ просто непрактичен — некрасиво и где-нибудь наверняка забудется. Второй иногда невозможен…

хорошего решения проблемы я пока не знаю : (

sass aka syntactically awesome stylesheets

comments: 2

недавно обнаружил довольно интересную надстройку над CSS, добавляющую в синтаксис много вкусностей. Как следует из заголовка, называется она SASS, или syntactically awesome stylesheets, и представляет собой маленькую программу на руби.

за примерам сахара лучше идти на сайт, но я их коротко перечислю:

поскольку CSS — декларативный язык, такие инструменты для него подходят очень хорошо

менеджер юзерскриптов в опере

comments: 0

несколько месяцев назад я писал, как можно делать аддоны для оперы. К сожалению, тогда выводы были неутешительны. Но с тех пор появилась Opera Unite, в которой эти проблемы были решены, и теперь мы можем полноценно работать с файлами на диске!

встречайте: UJS Manager service. Он работает таким образом: вы устанавливаете в свою Opera Unite новый сервис, которому даёте доступ к папке юзерскриптов, а веб-интерфейс этого сервиса переносите на сайдбар оперы — и вуаля! У вас есть панель управления юзерскриптами прямо в интерфейсе!

сейчас сервис может ещё не очень многое, хотя одна киллер-фича у него есть: установка нового скрипта одним кликом! Окрываете скрипт в браузере, и вверху страницы появляется сообщение «UJS Manager detected script file. It can be installed as a user script.» с кнопкой «Install User Script».

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

заодно можно нечто подобное соорудить для пользовательских стилей