Блог 7even

про ruby, rails, sinatra, git и всё на свете

Асинхронная работа с ВКонтакте

У широко известной соцсети ВКонтакте есть чуть менее известный API, насчитывающий немалое количество методов, а также различные способы загрузки файлов для использования в методах - и даже long-polling для получения сообщений из мессенджера ВКонтакте. В этой статье я попытаюсь рассказать о том, как работать в ruby с этим API в асинхронном режиме.

Для этого напишем собственный web-based мессенджер, умеющий отправлять сообщения, принимать и помечать сообщения прочитанными. Большая часть приложения будет оперировать на клиенте: фронт-энд будет устанавливать постоянное соединение с бэк-эндом, отправлять ему запросы и получать ответы; а бэк-энд займется непосредственной работой с API.

Самый примечательный момент в таком приложении - асинхронность: бэк-энд должен постоянно ждать от ВКонтакте новых сообщений, и параллельно с этим обрабатывать данные, присылаемые фронт-эндом, после чего вызывать нужный метод API (например, фронт-энд сообщает, что 2 входящих сообщения прочитаны - необходимо вызвать API-метод messages.markAsRead).

CLI-утилита на основе Thor

Для многих не секрет, что Ruby используется не только для web-разработки - этот язык также отлично пригодится для написания простеньких скриптов, упрощающих повседневные рутинные задачи. При этом самый естественный способ взаимодействия со скриптами - через командную строку, он же CLI (Command-Line Interface).

В стандартной библиотеке Ruby есть модуль OptionParser, здорово облегчающий работу с парсингом аргументов, пришедших из командной строки - но в этой статье речь пойдет о библиотеке thor, которую создал небезызвестный Yehuda Katz. Помимо обработки аргументов, thor предоставляет методы для более удобного взаимодействия с пользователем во время сессии работы самой утилиты.

thor заявлен как замена rake, и в этой стезе, надо сказать, он пока еще не преуспел - но зато гем здорово помогает при разработке собственных CLI-утилит, а также отлично справляется с генерацией файлов на основе шаблонов (именно на основе thor работают все генераторы в rails, начиная с версии 3.0).

Задача

Сначала я хотел написать очередную обзорную статью с небольшими примерами кода, но потом решил, что все-таки интереснее говорить о чем-то конкретном; поэтому по ходу статьи будем создавать простенькую CLI-утилитку, выводящую некоторую нужную информацию - непрочитанные письма в gmail-ящике, заголовки 5 последних новостей с ленты.ру и 5 последних коммитов в рельсы с гитхаба. А назовем ее workhorse (рабочая лошадка).

Объектная модель Ruby

Данный опус является моей попыткой собрать воедино все принципы, согласно которым работает объектная модель в языке Ruby. Цель статьи в том, чтобы читатель увидел логику там, где раньше видел “магию”.

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

Основы

Ruby является полностью объектно-ориентированным языком: числа, строки, регулярные выражения, массивы - это все объекты определенных классов. Класс определяет поведение объекта - он содержит все методы, доступные его объектам (инстансам).

1
2
3
4
5
6
7
8
class MyClass
  def my_method
    # some code
  end
end

obj = MyClass.new
obj.my_method

Традиционно в ООП используется наследование классов - методы класса-родителя доступны объектам дочерних классов (а также их дочерних классов итд). Разумеется, в ruby этот механизм тоже не забыт:

1
2
3
4
5
6
7
8
9
10
11
class Parent
  def some_method
    'Parent#some_method'
  end
end

class Child < Parent
end

obj = Child.new
obj.some_method # => "Parent#some_method"

Тонкая настройка SSH

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

Общее соединение

Часто есть необходимость подключиться к одному и тому же серверу одновременно в нескольких окнах/вкладках консоли. ssh можно настроить так, чтобы вместо создания нового соединения к серверу использовалось уже созданное. Для этого нужно добавить следующие строчки в ~/.ssh/config (возможно, этот файл придется создать):

1
2
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r

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

Этот метод также работает со всеми утилитами, использующими ssh-соединения: scp, rsync, git итд - достаточно открыть соединение, и утилита будет его использовать. При вводе пути к файлу на удаленном сервере в scp даже будет работать tab-completion.

JSON.load и компания

В работе над текущим проектом мне часто приходится иметь дело с форматом json, причем не только выдавать json ответом с сервера, но еще и читать json-параметры запросов, и даже наборы параметров, сериализованные в json и уложенные в поле в таблице БД.

В ruby, как известно, для этого существует стандартная библиотека json. Упаковка данных в json и распаковка их обратно происходит с помощью 2 методов:

1
2
3
4
JSON.dump(a: 1, b: 2)
# => '{"a":1,"b":2}'
JSON.load('{"a":1,"b":2}')
# => {"a"=>1, "b"=>2}

И все бы хорошо, да вот только в один прекрасный день моя коллега на своей ubuntu-машине запустила спеки, и JSON.load почему-то повел себя как IO.load - решил, что параметром ему подсовывают имя файла - но файла под названием {"result":true} рядом не нашлось, и случился Errno::ENOENT: No such file or directory