Особенности настройки кеширования на production сервере

Решил я на днях заняться кешированием блога и напоролся на небольшую проблему…

Требовалось обработать JavaScript и CSS файлы (которых меня много, да и весят они немало) и получить на выходе всего лишь два файла. Для этого я использовал способ предоставляемый хелперными методами javascript_include_tag и stylesheet_link_tag, а именно: собрал все .js и .css файлы в два хелперных метода и установил у них опции :cache. Получился примерно следующий код:

<%= javascript_include_tag 'shCore.js', 'shBrushCSharp.js', 'shBrushXml.js', 'shBrushRuby.js', 'shBrushBash.js', 'shBrushPlain.js', :cache => 'sh' %>
<%= stylesheet_link_tag 'application', 'pagination', 'textilize', 'awesomebox', 'SyntaxHighlighter.css', cache => 'general_all' %>

Для того чтобы кеширование заработало в конфиге для production сервера (_config/environments/production.rb_) поле config.action_controller.perform_caching должно быть выставлено в true (на самом деле это значение по умолчанию для этого конфига)

Грабли же заключаются в том, что в этом случае в каталогах public/javascripts и public/stylesheets при первом обращении к серверу создаются файлы объединяющие все указанные вами скрипты и CSS и если у mongrel нет прав на запись в эти каталоги, по возникает исключение:


ActionView::TemplateError (Permission denied - /u/apps/blog4m/releases/20080928165941/public/javascripts/sh.js) on line #10 of layouts/main.html.erb:

10:     <%= javascript_include_tag 'shCore.js', 'shBrushCSharp.js', 'shBrushXml.js', 'shBrushRuby.js', 'shBrushBash.js', 'shBrushPlain.js', :cache => 'sh' %>
11:     <%= stylesheet_link_tag 'application', 'pagination', 'textilize', 'SyntaxHighlighter.css', :cache => 'main_all' %>

А прав у mongrel-а небыло потому что он запускался под специально созданной учётной записью (не под той под которой деплоились на сервер файлы)

Покапавшись в интернете я нашел следующий workaround: надо в deploy.rb добавить следующие задачи которые будут выполняться после создания Сapistrano проекта на сервере и после заливки новой версии на сервер:

task :after_cold, :roles => [:app, :web, :db] do
  sudo "chown -R mongrel:mongrel #{current_path}/"
end

task :after_update, :roles => [:app, :web, :db] do
  sudo "chown -R mongrel:mongrel #{current_path}/"
end

Задачи просто меняют владельца у находящихся на сервере файлов после выполнения задачь deploy:cold и deploy:update

* * *

Создание Sitemap.xml в Rails

Создание Sitemap в Rails довольно простая задача. Всё что вам нужно сделать это:

1) Создать контроллер или использовать уже готовый. Я пошел вторым путём. В этот контроллер нужно добавить действие которое будет отвечать за создание Sitemap.xml страницы.

def sitemap
  headers['Content-Type'] = "application/xml"
  @messages = Message.find :all, :limit => 50000, :order => "published_at DESC"
end

Первая строка устанавливает заголовок Content-Type в “application/xml” это означает для браузера (или поискового сервера) что контент файла находится в формате XML. Sitemap который мы создаём соответствует спецификации под номером 0.9 в которой сказано что контент должен быть именно в этом формате.

Вторая строка извлекает все сообщения в блоге и сортирует их в обратном порядке (чтобы более новые сообщения были вначале Sitemap файла). Ограничение в 50000 записей опять же установлено на основе спецификации (хотя есть ещё ограничение в 10Мб, но мы его не учитываем).

Ещё один немаловажный момент связанны с этим методом: нужно отключить текущий layout если он у вас установлен.

layout 'main', :except => [:sitemap]

Если этого не сделать, то весть XML код который мы сгенерируем на следующем шаге будет помешен в этот layout, а это в свою очередь будет означать что sitemap файл станет не корректным.

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

render :layout => false

2) Следующим шагом будет создание представления которое называется так же как и наше действие и лежит в каталоге представления ассоциированном с контроллером. Называется он у меня так: sitemap.rxml. Обратите внимание на расширение rxml это означает что в этом файле мы собираемся работать с XML а вспомогательные методы Rails помогут нам в этом.

xml.instruct! :xml, :version=>"1.0"
xml.urlset(:xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9") {
  xml.url {
    xml.loc(url_for :controller => 'blog', :only_path => false)
    lastmod = @messages[0].published_at.strftime("%Y-%m-%d") if !@messages.nil?
    lastmod = lastmod || Time.now.strftime("%Y-%m-%d")
    xml.lastmod(lastmod)
    xml.changefreq("daily")
    xml.priority(1)
  }
  for message in @messages
    xml.url {
      xml.loc(url_for :controller => 'blog', :action => 'message', :id => message.id, :only_path => false)
      xml.lastmod(message.published_at.strftime("%Y-%m-%d"))
      xml.changefreq("weekly")
      xml.priority(0.8)
    }
  end
}

В первой строке мы задаём сигнатуру XML файла. А затем, во второй строке, создаём корневой XML тэг под названием urlset с атрибутом xmlns который указывает на то какую версию Sitemap спецификации мы используем при генерации файла.

Затем обратите внимание на два места в коде где встречается строка xml.url в первом случае этот тег будет описывать главную страницу блога, а во втором каждое конкретное сообщение блога (т.к. этот тег находится внутри цикла который перебирает сообщения). Подобным же способом можно добавить ссылки на другие страницы вашего сайта (например на страницу с описанием сайта и т.п.)

Тэг xml.loc содержит полный URL путь в странице (чтобы получить полный путь, а не относительный, у метода urlfor_ устанавливаем опцию onlypath_ равной false)

Тэг xml.lastmod содержит дату (время указывать необязательно) последнего изменения страницы. Для главной страницы блога я вычисляю его на основании первой последней записи в созданной блоге.

Тэг xml.changefreq указывает с какой частотон надо проводить сканирование каждой конкретной страницы (учтите что вы всего лишь предлагаете поисковому боту сканировать с такой частотой, а он сам затем решает следовать вашим указаниям или нет). В данном случае я хочу чтобы главная страница сканировалась ежедневно (daily), а страницы с сообщениями раз в неделю (weekly)

Тэг xml.priority указывает в каком порядке бот должен сканировать страницы (допустимы знчения от 0.0 до 1.0) После этих 2-х шагов по адресу http://localhost:3000/<контроллер содержащий действие sitemap>/sitemap вы должны получить контент sitemap файла.

3) Последний завершающий штрих состоит в том что мы заставим Rails отдавать sitemap файл по более короткому URL. Для этого в файл config/routes.rb добавим следующую строку:

map.sitemap "/sitemap.xml", :controller => "<ваш контроллер>", :action => "sitemap"

Теперь обратившись по адресу http://localhost:3000/sitemap.xml вы получите sitemap.xml файл.

На этом создание sitemap.xml файла закончено.

P.S. Есть ещё более правильный способ создания sitemap файла, это использование специальных плагинов, но этот способ я постараюсь рассмотреть позже.

* * *

Книги по Ruby и Rails на русском языке

Ruby Book

Тема с русскими книгами по Ruby и Rails довольно избитая и озвучена во многих блогах, но я не могу не добавить свои “пять копеек” :)

* * *

Скорая помощь

В этом посте я расскажу какие справочные системы по Ruby и Rails (и не только) доступны разработчикам в Интернете.

* * *

Новая книга о Ruby on Rails!!!

Ура товарищи!!! Не прошло и года, а в продаже уже появилась новая книга на русском языке о Ruby on Rails 2.0 от издательства “Символ-Плюс”.

Путь Rails. Подробное руководство по созданию приложений в среде Ruby on Rails
Путь Rails. Подробное руководство по созданию приложений в среде Ruby on Rails

Называется она ”Путь Rails. Подробное руководство по созданию приложений в среде Ruby on Rails” от Оби Фернандеса. Надеюсь она станет неплохим дополнением к моей небольшой коллекции книг по Ruby и Rails.

* * *

Rails + Application Template = ext-rails-template

main-page.png

Недавно у меня возникло несколько идей, связанных с разработкой web-приложений на Rails и, как и следовало ожидать, оказалось что все они будут состоять из схожих компонентов. Например будет присутсвовать авторизация и аутентификация (в виде диалог авторизации, ограничении доступа к приватным ресурсам приложения и т.п.), админка со списком пользователей и настройками приложения, сайдбар (sidebar) с действиями и .т.п. Посути при создании каждого нового приложения прошлось бы создавать и настраивать (или копипастить) эти компоненты. Так как я лентяй и не люблю заниматься копипастом я решил вынести эти компоненты в Rails Application Template.

* * *

Vital Ideas

Идеи приходят и уходят...

В одной из предыдущих статей я обещал привести пример приложения созданного на базе шаблона ext-rails-template. Этот момент настал :), но сначала небольшая предыстория…

* * *