Заметки Ruby программиста Всякие полезные наработки

27Июл/105

Rails 3 + Ruby 1.9 = борьба с кодировками

Посвящается Rails 3.0.0.rc

Недавно пробовал покатать Rails EDGE на Ruby 1.9.2-head и конечно же нарвался на проблему с кодировками. Однако их оказалось не так много как я предполагал.

Для начала надо убедиться что в файле config/application.rb прописано config.encoding = "utf-8". Это позволяет сэкономить нервы с кодировкой шаблонов. Но именно шаблонов, для моделей и контроллеров надо указывать специальный комментарий вначале файла:

# coding: utf-8

Первое с чем пришлось столкнуться - драйвер MySQL (gem mysql) не выставляет кодировки у строк и Ruby 1.9 воспринимает эту строку как ASCII-8BIT. Попробовал несколько альтернативных драйверов:

  1. gem sam-mysql-ruby - простой порт гема mysql под 1.9. Устанавливает кодировку строки как при чтении, так и при записи в БД.
  2. gem ruby-mysql - продвинутый драйвер с поддержкой кодировок. Устанавливает кодировки только при чтении. При записи в базу о кодировках надо думать самостоятельно.
  3. gem mysql2 - альтернативный драйвер с поддержкой кодировок строки в ruby 1.9. Устанавливает кодировки только при чтении. При записи в базу о кодировках надо думать самостоятельно.

Если возникли проблемы с компиляцией, то обычно помогает примерно следующее:

gem install sam-mysql-ruby -- --with-mysql-config=/opt/local/bin/mysql_config5

Я остановился на втором варианте. Поскольку решение следующей проблемы устранило его недостаток.

Далее возникла проблема с кодировкой данных приходящих от пользователя. При возникновении ошибки валидации данных во время сабмита формы вылетало исключение о несовместимости кодировок:

incompatible character encodings: ASCII-8BIT and UTF-8

Проблема оказалась в том, что кодировка пустой строки остается ASCII-8BIT. Пришлось найти middleware patch, который явно устанавливает кодировку данных пришедших из формы:

if RUBY_VERSION < '1.9'
  module Fx
    class UnicodeEnforcer
      def initialize(app)
        @app = app
      end
   
      def call(env)
        req = Rack::Request.new(env)
        force_encoding(req.params)
        @app.call(env)
      end
   
      def force_encoding(on)
        case on
          when String
            on.dup.force_encoding('utf-8')
          when Array
            on.map(&method(:force_encoding))
          when Hash
            on.each_pair do | k, v |
              on[k] = force_encoding(v)
            end
          else
            on
        end
      end
    end
  end
  Appname::Application.config.middleware.use Fx::UnicodeEnforcer
end

Добавляем его в config/initializers/ruby19.rb и наслаждаемся!

Пока больше проблем не замечено. Так что эта связка вполне работоспособна. Однако в продакшене пока не пробовал - лень настраивать сервер. Heroku - наше всё! 🙂

Третьими рельсами очень даволен, многие архитектурные решения сильно радуют. Стабильность работы меня пока тоже не огорчила. Так что горячо рекомендую.

P.S.
Только что проверил - middleware patch больше не нужен. Все отлично работает и без него. Ура!

Комментарии (5) Пинги (0)
  1. Mysql2 нормально пишет в базу. Нужно только базу создать с нужным дефолтным чарсетом.

  2. Привет. Начал недавно изучать Rails 3 и столкнулся с теми же проблемами. Теперь изучаю тему локализации приложений. Есть наработки по данному вопросу?

  3. Да собственно после выхода релиза никаких проблем-то и нет.
    Для локализации мне вполне хватает возможностей I18n. Что конкретно Вас интересует?

  4. # coding: utf-8 — вот за это большое спасибо. Битый час терзал google пока не наткнулся на Ваш пост.

  5. Mysql2 нормально пишет в базу. Нужно только базу создать с нужным дефолтным чарсетом. — какой именно?


Leave a comment

Нет обратных ссылок на эту запись.