Ссылки о веб-разработке за декабрь 2012

ссылки о hypermedia APIs

я как-то прозевал появившиеся за последний год подходы к встраиванию метаданных в JSON API, и вот недавно обнаружил целых две спецификации. Обе они пытаются вернуть в JSON что-то хорошее из XML. В частности это хорошее — гиперссылки, отсюда и такое название.

вкратце идею можно описать так: когда мы запрашиваем html-ресурс, мы обычно получаем и кучку ссылок на связанные объекты, а также возможные действия. Хочется иметь то же самое для JSON, ведь там обычно просто голые данные. Так давайте договоримся о стандартном способе прицеплять к ним ссылки и, возможно, сами связанные объекты. Hypertext Application Language (HAL) этим и ограничивается. Collections+JSON идёт дальше и определяет кучу других ограничений, поэтому и нравится мне меньше.

в тему метаданных хочу дать и ещё одну ссылку: про версионность API. Я пока что видел мало примеров этого, и лучшим вариантом мне казалось включение версии непосредственно в адрес: /api/v2/resource. Однако Peter Williams разумно замечает, что такой подход приводит к тому, что один и тот же ресурс с разными версиями API — это разные ресурсы. Он же предлагает и красивый альтернативный способ добавлять версию в API: Accept: application/vnd.mycompany.myapp-v2+xml. Концептуально это мне очень нравится, хотя не могу не обратить внимания на недостатки: такой интерфейс сложнее исследовать браузером, и тип всё-таки нужно регистрировать. Но красиво.

CORS 101

It is completely safe to augment any resource with Access-Control-Allow-Origin: * as long as the resource is not part of an intranet (behind a firewall). In other words, a URL you can fetch from a server on the internet using wget or curl. For your basic web site this encompasses all resources on the site. The Access-Control-Allow-Origin header (part of CORS) tells the browser the resource can be shared.

Even if the resource includes confidential information based on cookies or HTTP authentication data in the request, including the header and sharing the resource is still safe, since the browser will make the request without any cookies or HTTP authentication data. And if the browser did make the request with cookies or HTTP authentication data, it would never share the resource because that would require an additional header, Access-Control-Allow-Credentials, and a different value for the aforementioned header.

So go ahead and safely share your public data with other applications!

сохранение jabber-статусов в atom

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

к сожалению, фейсбук купил команду френдфида, и тот потихоньку начал разлагаться. Бот не работает уже давно. Впрочем, технически задача очень проста: подключиться к серверу, ждать обновлений статуса, сохранять их в поток. К сожалению, на моём сервере оказываются только LTS-версии убунты, и предыдущая версия 10.04 не имела какой-то из нужных мне библиотек на питоне. Зато недавно я сервер обновил, и вскоре после этого смог завершить и запустить своего бота. Теперь мои статусы оказываются в специальном потоке.

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

class Client(JabberClient):
    def __init__(self):
        JabberClient.__init__(self,
            JID(JID_VALUE),
            PASSWORD,
            server='talk.google.com',
            auth_methods=['plain'],
            tls_settings=TLSSettings(require=True, verify_peer=False))

client = Client()
client.connect()
client.loop(1)

настроек у клиента могло бы быть и поменьше, но Google Talk очень особенный. Зато дальше становится проще. Вот так подключается обработчик событий:

self.get_stream().set_presence_handler('available', self.presence)

а вот, собственно, и сам обработчик:

def presence(self, stanza):
    status = stanza.get_status()

    if stanza.get_show() is not None or not status or status == self.current_status:
        return True

    self.current_status = status
    self.update_feed(status)
    publish(PUSH_URL, SELF_URL)

    syslog.syslog('New status: %s' % (status.encode('utf8')))

    return True

здесь пояснения требуют get_show и publish. Первый возвращает None, когда контакт именно в онлайне, а не отошёл, недоступен или что-то ещё. Второй публикует обновление на PubSubHubbub-хабе.

кстати, мне хотелось, чтобы этот бот был полноценным сервисом на моём сервисе. Для этого пришлось разобрать основы системы upstart, которая призвана заменить предыдущего управляющего сервисами init. Оказалось, с новой системой простые вещи действительно просты. Вот такой файл я добавил в /etc/init:

description "Logger of IM statuses"
author  "Artemy Tregubenko "

start on runlevel [234]
stop on runlevel [0156]

expect daemon
respawn

chdir /var/www/shared_arty_name/im-status/
exec python /var/www/shared_arty_name/im-status/IMStatus.py

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

def start():
    client = Client()
    client.connect()
    client.loop(1)

def stop():
    client.disconnect()

context = daemon.DaemonContext(
    signal_map={signal.SIGTERM: stop},
    gid=grp.getgrnam('www-data').gr_gid,
    uid=grp.getgrnam('www-data').gr_gid,
    detach_process=True,
)

with context: start()

а вот и код бота целиком. Интересно, есть ли смысл превращать его в сервис? Глядишь, кто-нибудь на пиво и пожертвует : )

Forget JavaScript, It’s Time for Browsers to Speed Up Images

So many screens, so many images (testing responsive sites with Adobe Shadow). Photo: Adobe

The average webpage is now 1.2 megabytes and around 60 percent of that rather large payload comes from images. That’s a lot of data, whether you’re handling images responsively or just trying to speed up a desktop site.

You might think, if images are the bulk of what your browser is downloading, that browsers would be working hard to speed up the image downloads, perhaps trying alternate, space-saving image formats, but you’d be wrong.

You might also think that, as Google’s Ilya Grigorik writes, “innovating on better image formats would be a top agenda item” for the web. But again you’d be wrong. The web is still using the same image formats it’s been using virtually since the first images appeared online.

Grigorik thinks it’s high time that changed and we agree.

In a recent post looking at what it would take to deploy new image formats on the web, he writes, “if we really want to make an impact on web performance, then image formats is the place to do it… there is absolutely no reason why we shouldn’t have dozens of specialized formats, each tailored for a specific case and type of image.”

Of course no web developer wants to deal with dozens of specialized image formats. Nor should they need to — that’s a job for servers. “In a world with dozens of image formats,” continues Grigorik, “the human solution does not scale — read, markup does not scale… whereas computers are fantastic at doing exactly the kind of optimization work required to solve the problem.”

Grigorik isn’t alone in calling for new image formats, nor is he the first to suggest handing these tasks off to the server. Developer and responsive images proponent Matt Wilcox has argued for a similar solution, as have others.

The basic premise of these arguments is that deciding which image to serve up to which device and browser should be a server-side problem. And in fact there’s already a way to solve this problem with HTTP headers, namely the Accepts header, which tells the server which image formats the browser supports. Based on that information the server could then “re-encode, recompress, resize, strip unnecessary metadata and deliver the optimal format.”

The problem is that web browsers (with the exception of Opera) don’t actually send useful information in the Accepts header.

Thus, the first step in creating a server-side solution for smaller images is to get other browsers to send useful Accepts headers.

The Accepts header isn’t a magic bullet by any means, but it’s a problem that’s not hard to solve provided browser makers prioritize it. But to really get server side image solutions working the web would also need new server tools (fortunately, several already exist). There are other stumbling blocks as well. Grigorik addresses half a dozen potential problems and objections that you can read through in his post.

Even if browser makers come around to the idea and do start improving Accepts headers, bringing better image formats to the web is going to be an uphill battle. But Grigorik is determined to chase the idea. “Some uphill battles are worth fighting,” he writes in a comment, “I think this a good one. Wish me luck.”

gravatar, pavatar, libravatar

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

это было намного удобнее, чем прежде, но недостаточно хорошо, потому что это была централизованная система. Всё завязано на единый сайт, и если с ним что-то случится, или он задумает сменить бизнес-модель, будет неловко. Распределённые системы в этом смысле лучше. Тут под рукой удачно оказался OpenID, который был распределённый, и у каждого пользователя была своя страничка. Что может быть очевиднее, чем дать на этой страничке ссылку на аватар? Так появился pavatar.

однако у pavatar были недостатки по сравнению с gravatar. Во-первых, пользователи без OpenID, с одним только емейлом, оставались не у дел. Во-вторых, разбор HTML-страницы, чтобы извлечь из неё ссылку на pavatar, — довольно затратный процесс, и это особенно заметно при большом числе комментаторов. Нужен какой-то другой способ совместить распределённость с простотой.

и вот Libravatar взял идею Gravatar «имя картинки — это хеш от емейла», развил её до «имя картинки — это хеш от емейла или OpenID», а распределённость сделал через SRV-запись в DNS. При этом сервера самого Libravatar служат в качестве запасного варианта. Ну и код Libravatar открытый.

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


я создал такую запись в DNS:

_avatars._tcp.arty.name. 86400 IN SRV 0 0 80 arty.name.

и скопировал 1344467.png в md5('me@arty.name') = ad4f3a81155f469603be3b8bd5cf3348 и sha256('http://arty.name') = 3876b8005186bddc104495cd6c81f160f990f7fec7e96d89cfd185668bc2886d в корневом каталоге сайта, в соответствии с API Libravatar. Когда в проверялке Libravatar очнётся кеш, она должна начать показывать мой аватар по емейлу и OpenID.

← предыдущий месяц