Плагин для подсчета посещаемости страниц в Ruby on Rails 7 июля 10

Я написал плагин для подсчета посещаемости избранных страниц Rails-приложения. Взять его можно на http://github.com/leonid-shevtsov/page_visit_tracker, там же посмотреть пример использования и краткую инструкцию.

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

Я решил, что посещения надо привязывать к объектам модели. Поэтому

class PostsController
  def show
    # ...
    track_page_view(@post)
  end
end

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

Кеширование страниц с динамическими элементами средствами Ruby on Rails 3 июля 10

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

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

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

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

Flash, в смысле уведомления

Наиболее очевидный динамический элемент страницы – флеш. Чтобы выводить флеш на закешированных страницах, Pivotal Labs когда-то делали плагин CacheableFlash, но он рассчитан на Prototype. Потому-то я его и портировал в плагин CacheableFlash for jQuery.

Принцип работы плагина прост: в after_filter флеш заносится в куку, а в яваскрипте забирается оттуда. Надо заметить, что это не работает с flash.now, что, по-моему, вполне логично, поскольку flash.now нужен только на динамических страницах типа форм. Выводить его придется отдельно. Вообще у меня это таким хелпером делается:

def render_flash
  messages = {:error => [], :notice => []}
  flash.each do |type, message|
    messages[type] ||= []
    messages[type] << message
  end
  messages.to_a.map do |m|
    content_tag('div', m[1].join('<br/>'), :class => ['flash',m[0]].join(' '))
  end.join
end

Он и flash.now выводит, и место под динамический флеш подготавливает.

Другие элементы

Остальные динамические элементы – у меня на CarGid это был счетчик объявлений, отобранных пользователем «в блокнот» – выводятся таким же способом. Разумеется, нужно помнить, что объем кук не резиновый и что особо ценную информацию туда лучше не писать (хотя какая ценная информация у незалогиненного пользователя?)

Большие динамические элементы можно подгружать отдельным запросом. Сам понимаешь, что такое решение гораздо хуже.

Обновление кеша в результате действий незалогиненных пользователей

У меня незалогиненные пользователи могут оставлять комментарии. Разумеется, после добавления комментария его нужно показать пользователю – поэтому страницу с комментариями нужно убрать из кеша.

Настройка кеширования в контроллерах

По умолчанию рельсы кладут кеш прямо в public. Разумеется, это нам не подходит, потому кеш нужно перенести в public/cache:

# config/environments/production.rb
config.action_controller.page_cache_directory = File.join(RAILS_ROOT, :public, :cache)

Чтобы кеширование происходило только без наличия залогиненного пользователя, замечательно подходит условно-стандартный метод Authlogic current_user:

class MainController < ActionController::Base
  caches_page :index, :unless => :current_user
end

Остается растыкать caches_page по контроллерам, чтобы Rails начали складывать в кеш правильные страницы.

Настройка nginx

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

# убираем слеш из конца URL - его любят навешивать некоторые обозреватели
rewrite ^(.+)/$ $1 permanent;
 
# запросы POST можно смело передавать прямо в Rails
if ($request_method !~ ^(GET|HEAD)$) {
  break;
}
 
# эта директива запрещает прямой доступ к каталогу cache извне
location ^~ /cache {
  internal;
}
 
# есть куки Authlogic - отключаем кеширование
if ($cookie_user_credentials) {
  break;
}
 
# наконец, проверяем наличие страницы в кеше и отдаем ее оттуда
if (-f $document_root/cache/index.html) {
  rewrite ^/$ /cache/index.html last;
}
if (-f $document_root/cache/$request_uri) {
  rewrite .* /cache/$request_uri last;
}
if (-f $document_root/cache/$request_uri.html) {
  rewrite .* /cache/$request_uri.html last;
}

Это все, что касается nginx. Наверно, надо заметить, что переместить папку с кешем из-под document_root не получится. Можно сделать симлинк, если очень хочется.

Переехали со Slicehost на Linode 8 июня 10

Что ж, смену хостинга можно считать законченной, данные пересены, сервер настроен, домены уперты.

Чем плох Slicehost?

Год и десять месяцев назад я переехал с шаред-хостинга на Slicehost. Поскольку ничего толком о впсках я не знал, выбрал самых надежных и популярных. Линод был на втором месте, кстати.

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

Да вот пинг из Украины к датацентру Slicehost в Миссури составляет порядка 250 мс. Многовато.

Почему Linode?

Linode, надо сказать, не менее респектабельный хостер, чем Slicehost.

У Linode недавно появился датацентр в Лондоне. Пинг до него около 80 мс – не такой хороший, как у национальных хостеров, но вполне достаточный. Кроме этого, Линод позволяет переносить машину из одного датацентра в другой совершенно бесплатно и автоматически (чтобы сделать это на слайсхосте, нужно создавать еще одну машину в новом ДЦ и клонировать в нее старый диск).

У Linode процентов на 20 меньше цены. Кроме того, Линод позволяет докупать ресурсы по отдельности – например, только память, что существенно для наших любимых рельсов.

У Linode очень удобный менеджер DNS. Например, создание типичной доменной записи (A + A для www и mail + 5 NS + MX) происходит автоматически. Кроме того, Линод позволяет автоматически переносить DNS-записи, но для этого исходный сервер должен поддерживать AXFR, Slicehost, например, этого сделать не дает.

Кстати, GoDaddy порадовали возможностью сменить NSы всем доменам сразу.

Еще Linode самостоятельно рисует графики загрузки процессора, диска и сети. Мелочь, а приятно.

Резюмируя: если ты живешь в Украине и выбираешь VPS для своих проектов, если тебя не смущает отсутствие русскоязычной поддержки и ты не настаиваешь на расположении сервера в UA-IX, бери Linode. Будет хотя бы уверенность, что его не конфискуют вместе с соседями-порнографистами.

Почему nginx?

Я недавно настраивал другой VPS (кстати, на uh.ua, тоже пока не расстраивают) для CarGidа, вот тогда и оценил, что nginx ест намного меньше памяти, чем апач. Это ну очень критично на впске. Настолько критично, что можно отказаться от привычек и лени и посмотреть в сторону этого замечательного сервера, о котором и без меня написано много хорошего.

Если ты не в курсе, то Passenger поддерживает nginx, поэтому рельсы под ним поднимаются никак не сложнее, чем под апачем. И намного проще, чем PHP.

Time.now против Time.zone.now в Rails 4 июня 10

Функции Time.now и Time.zone.now в Ruby on Rails практически идентичны. Возвращают одни и те же значения, ведут себя одинаково. Из-за этого легко перепутать и использовать более короткую формулировку Time.now. А зря – по крайней мере, если ты используешь часовой пояс, отличный от UTC.

В чем же разница между этими функциями? А вот в чем:

>> Time.now.class
=> Time
>> Time.now.to_s :db
=> "2010-06-04 19:00:00"
>> Time.zone.now.class
=> ActiveSupport::TimeWithZone
>> Time.zone.now.to_s :db
=> "2010-06-04 16:00:11"

Дело в том, что Rails, начиная с версии 2.1, хранят время в базе в UTC, а при сохранении и чтении переводят его из и в часовой пояс, используемый в приложении.

За перевод времени в UTC отвечает, как ни странно, метод to_s(:db). Точнее странно то, что, хоть он и доступен у объекта типа Time, работает он неверно и отдает время в текущем часовом поясе. Если использовать Time.now в условиях запроса – они сместятся на определенное количество часов. Если записывать Time.now в базу – в базу запишется неправильное значение, потому что при чтении рельсы лишний раз добавят к нему смещение часового пояса.

Короче, наиболее простым и адекватным решением этой проблемы будет замена по всему проекту Time.now на Time.zone.now.

…А еще из-за это проблемы не стоит использовать в запросах функцию MySQL NOW() и аналогичные, поскольку они ничего не знают о настройках рельсов.

Перенос настроек из SSH в PuTTY 3 июня 10

С тех пор, как я вернулся с линукса на винду (а я вернулся), в SSH лазил исключительно через виртуалку. Лень было конфиг переносить в PuTTY, что довольно досадно, поскольку выходить в SSH прямо через PuTTY по ряду причин удобнее – например, можно настроить внешний вид и поведения терминала для каждого хоста, или быстро запускать сессии с помощью PuTTY-плагина к Launchy.

В общем, терпеть надоело, и я написал скрипт, создающий, на основании конфига в формате ssh-config, reg-файл, который вгружается в реестр винды и добавляет в PuTTY все недостающие сессии.

Процедура такая:

  1. В линуксе нужны ruby и puttygen (apt-get install putty-tools).
  2. По умолчанию скрипт скидывает ключи для PuTTY в ~/putty_keys – такой каталог должен существовать. Кроме этого в скрипт нужно вписать правильный путь к каталогу винды, куда ты собираешься положить ключи для PuTTY.
  3. Запускаем скрипт: ./ssh_to_putty.rb >putty.reg. Вводим пароли к ключам, если будет спрашивать.
  4. Получаем пачку (или не пачку) ключей для PuTTY и файл putty.reg. Первые копируем в соответствующую папку в винде, второй вгружаем в реестр.

Кстати, перед тем, как будешь вгружать конфиг в реестр, убедись, что в Default Settings в PuTTY сохранены подходящие настройки внешнего вида – цвета, шрифт, все такое. Иначе придется потом для каждой сессии настраивать отдельно.
читать дальше →

Отладка скриптов в Internet Explorer 27 мая 10

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

Во-первых, где взять нужную версию Internet Explorer? Есть два пути – использовать мутантский набор движков типа IETester или IECollection, или развернуть несколько виртуальных машин под каждую нужную версию Internet Explorer. Первый путь мне никогда не нравился, хотя бы из-за кучи проблем с совместимостью и потенциально неверного отображения сайтов в разных версиях.

Потому будем настраивать виртуалки. К счастью, достаточно настроить только первую – остальные можно наклонировать.

Создаем виртуальные машины

  1. Скачиваем и устанавливаем VirtualBox.
  2. Создаем виртуальную машину IE6. Ей нужен будет диск размером 2G и порядка 512 мегабайт памяти. Еще ей нужны будут два сетевых адаптера – NAT и Host-Only – первый послужит для выхода в интернет, второй – для связи с хостом (и, если сервер на другой виртуальной машине, с сервером).
  3. Находим образ Windows XP Home SP3. Замечу, что SP3 не обновляет IE6 до IE7 самостоятельно.
  4. Убираем все лишнее из установки. Если места на диске не жалко, можно обойтись и без этого шага.
    1. Распаковываем образ каким-нибудь 7-zip.
    2. Скачиваем nLite -инструмент для пересборки образов XP.
    3. Скачиваем мой конфиг к nLite – он вырежет из образа все, кроме необходимого.
    4. Прогоняем распакованный образ через nLite, получаем ISO размером порядка 200 мегабайт.
  5. Подключаем образ к виртуалке, устанавливаем Windows – тут ничего особенного.
  6. Подключаем к виртуалке образ VirtualBox Guest Additions, чтоб получить разделенный с хостом буфер обмена, курсор, возможность растягивать окно-экран виртуалки и т.п. удобства.
  7. Ставим Adobe Flash Player, Java Runtime, Notepad++ для просмотра исходников.
  8. По желанию ставим на виртуалку какой-нибудь CCleaner, чтоб поубирать мусор.
  9. Базовая виртуалка готова. Можно ее выключить, а образ диска запаковать в архив и отложить – может, пригодится когда-нибудь.
  10. Клонируем образ (обычным копированием тут не обойтись):
    "C:\Program Files\Sun\VirtualBox\VboxManage.exe" clonevdi ie6.vdi ie7.vdi
  11. Создаем новую виртуалку, IE7, с теми же настройками, подключаем к ней склонированный образ. Рекомендую указать статический IP для Host-only соединения (в виртуалке это LAN Connection 2) – естественно, на каждой виртуалке разный. Помимо этого я в c:\windows\system32\drivers\etc\hosts указываю домен для сервера.
  12. На виртуалку для IE7 скачиваем и устанавливаем IE7.
  13. Готово!

Инструментарий

Для адекватной отладки яваскрипта нужно установить Script Debugger (ну и пусть говорят, что он устарел – альтернатив нет), а потом CompanionJS. И еще отключаем Disable JS Debugging на вкладке Advanced настроек IE (хитрая такая опция). Получаем некое подобие Firebug.

Можно поставить и другие инструменты, хотя бы IE Developer Toolbar для отладки верстки.

Вот и все – имеем работающий IE, независимый от базовой ОС, с возможностью удобно отлаживать скрипты.

Как загрузить карты украинских городов в Ovi Maps 3 24 мая 10

Нокия тут в Украине развернула масштабную кампанию по продвижению навигационных телефонов – на биллбордах, по телевизору, все такое. Захотелось посмотреть, что ж там за карты.

(Я последнее время на своей Nokia 5800 пользуюсь Мобильными Яндекс.Картами, поскольку их кеш можно скачать через компьютер и экономить потом трафик. Увы, МЯК не умеют прокладывать маршруты, да и к тому же являются растровыми.)

Итак. Ovi Maps предустанавливаются на все свежие смартфоны от Nokia. Свежую версию можно скачать с сайта Nokia. Свежие карты можно скачать с помощью Ovi Suite.

…Прискорбно, но на картах Украины, которые скачиваются из Ovi Suite, есть только крупные дороги, даже Киев практически не детализирован.

Немного погуглив, я выяснил, что можно совершенно безвозмездно загрузить и другую версию карт – с городами. С помощью Nokia Map Loader. Вот только ссылки на этот самый Map Loader с сайта Нокии нет.
Нашелся Nokia Map Loader на CNET – очевидно, вполне себе официальная версия.

Map Loader загружает 70 мегабайт карт вместо тех 7, что загружает Ovi Suite – на них оказалась, помимо всего прочего, детальная карта Днепропетровска с возможностью прокладывать маршруты, как автомобильные, так и пешие.

Список украинских городов для сайта 23 апреля 10

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

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

Вот он – список городов Украины. Тут города, разбитые по областям, и их координаты, в легко читаемом XML. Никаких дубликатов, никакого мусора.

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

[UPD 2010-05-28] в списке почему-то не было Киева. Добавил.

Как Hoptoad помогает на тестовом сервере 26 марта 10

Итак, Hoptoad – это такая продвинутая штука для записывания исключений в Rails-приложениях. Его использование выливается в то, что каждое новое исключение логируется, а также при желании отправляется тебе на почту. Удобно.

Но речь не об этом, а о том, что Hoptoad можно использовать на отладочном сервере (назовем его staging) для упрощения работы тестера. Для этого нужно настроить его так, чтобы он ошибку и показывал, и отправлял в Hoptoad.

По умолчанию Hoptoad логирует ошибки только тогда, когда не показывает (consider_all_requests_local = false). Самый простой способ это изменить – переопределить метод в ApplicationController:

#consider_all_requests_local должен быть true
class ApplicationController < ActionController::Base
  def rescue_action_locally(exception)
    if Rails.env == "staging" # or something else
      unless hoptoad_ignore_user_agent?
        HoptoadNotifier.notify_or_ignore(exception, hoptoad_request_data)
      end
      rescue_action_locally_with_hoptoad(exception)
    end
    super(exception)
  end
end

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

Чего не хватает? Ссылки на Hoptoad прямо со страницы ошибки, конечно!

UPD: Чтоб появились ссылки, нужно стянуть с гитхаба патченный файл vendor/plugins/hoptoad_notifier/lib/templates/rescue.erb и подложить в проект.

Теперь каждое сообщение об ошибке на staging будет сопровождаться вот такой вот красной ссылкой прямо на ошибку:

 title=

Делаем комментарии Хабрахабра похожими на Reddit 5 марта 10

Я последнее время практически не бываю на Хабре, зато часто захожу на Reddit. По-моему на последнем гораздо удобнее читать комментарии, в первую очередь потому, что их можно отсортировать по рейтингу и читать только
наиболее «ценные».

Так вот. Сел и написал юзерскрипт, который делает просмотр комментариев на Хабре более удобным.

  • Комментарии сортируются по рейтингу (в пределах одного уровня).
  • Ветки комментариев можно сворачивать и разворачивать нажатием на ссылку [-] в заголовке коммента.
  • Ник автора подсвечивается синим и отмечается буквой [S].
  • Юзерпики убираются насовсем, чтоб не мельтешили.

Скачать Habrahabr ‘comments like reddit’ mod с UserScripts.
Проверено в Firefox 3.6 + Greasemonkey и в Chrome 5.