ActiveRecord возвращает данные в ASCII-8Bit под Ruby 1.9.2-rc1 - PullRequest
5 голосов
/ 06 июля 2010

В дополнение к заголовку, при загрузке данных из ActiveRecord кодировка всегда устанавливается на ASCII-8Bit, несмотря на все мои усилия по форсированию кодирования. Я ввел здесь как можно больше подробностей, чтобы попытаться создать хороший отчет об ошибках, который кто-то может использовать, чтобы выручить меня!

В проекте используются следующие технологии:

  • Padrino Framework
  • Ruby 1.9.2-rc2 (также 1.9.1 и 1.9.2-preview3)
  • ActiveRecord
  • MySQL

(Полный список)

$ bundle show | ack '(record|padrino)'
  * activerecord (2.3.8)
  * padrino (0.9.14)
  * padrino-admin (0.9.14)
  * padrino-core (0.9.14)
  * padrino-gen (0.9.14)
  * padrino-helpers (0.9.14)
  * padrino-mailer (0.9.14)

Таблица эпизодов:

mysql> DESCRIBE `episodes`;
+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
| show_id        | int(11)      | YES  |     | NULL    |                |
| season_id      | int(11)      | YES  |     | NULL    |                |
| episode_number | int(11)      | YES  |     | NULL    |                |
| title          | varchar(255) | YES  |     | NULL    |                |
| year           | int(11)      | YES  |     | NULL    |                |
+----------------+--------------+------+-----+---------+----------------+
6 rows in set (0.02 sec)

mysql> SHOW CREATE TABLE episodes;
       Table: episodes
Create Table: CREATE TABLE `episodes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `show_id` int(11) DEFAULT NULL,
  `season_id` int(11) DEFAULT NULL,
  `episode_number` int(11) DEFAULT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `year` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=74332 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

mysql> SHOW CREATE DATABASE development;
+-------------+--------------------------------------------------------------------------------------------------------+
| Database    | Create Database                                                                                        |
+-------------+--------------------------------------------------------------------------------------------------------+
| development | CREATE DATABASE `development` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */           |
+-------------+--------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Как видите, база данных, безусловно, думает, что все должно быть UTF-8; и адаптер базы данных тоже:

ActiveRecord::Base.configurations[:development] = {
  :adapter   => 'mysql',
  :encoding  => 'utf8',
  :reconnect => false,
  :database  => "development",
  :pool      => 5,
  :username  => 'root',
  :password  => '',
  :host      => 'localhost',
}

Это отображается в консоли при проверке соединения активной записи:

ruby-1.9.2-rc1 > ActiveRecord::Base.connection
  DEBUG - [06/Jul/2010 19:24:32] "SQL (0.1ms)   SET NAMES 'utf8'"
  DEBUG - [06/Jul/2010 19:24:32] "SQL (0.1ms)   SET SQL_AUTO_IS_NULL=0"
 => #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x0000010936fa88 @logger=#<Padrino::Logger:0x00000101587198 @buffer=[], @auto_flush=true, @level=0, @log=#<IO:<STDOUT>>, @mutex=#<Mutex:0x00000101587148>, @format_datetime="%d/%b/%Y %H:%M:%S", @format_message="%s - [%s] \"%s\"">, @connection=#<Mysql:0x0000010936fad8>, @runtime=0.2608299255371094, @last_verification=0, @query_cache_enabled=false, @config={:adapter=>"mysql", :encoding=>"utf8", :reconnect=>false, :database=>"development", :pool=>5, :username=>"root", :password=>"", :host=>"localhost"}, @connection_options=["localhost", "root", "", "development", nil, nil, 131072], @quoted_table_names={}, @quoted_column_names={}> 

ruby-1.9.2-rc1> ActiveRecord :: Base.connection.encoding

Руби должен знать язык, вот мой $ locale

LANG="en_GB.UTF-8"
LC_COLLATE="en_GB.utf-8"
LC_CTYPE="en_GB.utf-8"
LC_MESSAGES="en_GB.utf-8"
LC_MONETARY="en_GB.utf-8"
LC_NUMERIC="en_GB.utf-8"
LC_TIME="en_GB.utf-8"
LC_ALL=

Хотя Ruby не настраивается Encoding.default_internal:

$ irb --simple-prompt
ruby-1.9.2-rc1 > Encoding.default_internal
 => nil 

Я добавил фрагмент в config/boot.rb моего приложения, который выглядит следующим образом:

if Kernel.const_defined?("Encoding") and Encoding.respond_to?(:find) and Encoding.respond_to?(:default_internal)
  Encoding.default_internal = Encoding.find('UTF-8')
end

Это работает именно так, как вы могли ожидать ... но это взлом, и не решает проблему.

А вот вывод проблемы на месте:

ruby-1.9.2-rc1 > e = Episode.new
  DEBUG - [06/Jul/2010 19:29:14] "SQL (0.1ms)   SET NAMES 'utf8'"
  DEBUG - [06/Jul/2010 19:29:14] "SQL (0.1ms)   SET SQL_AUTO_IS_NULL=0"
  DEBUG - [06/Jul/2010 19:29:14] "Episode Columns (0.8ms)   SHOW FIELDS FROM `episodes`"
 => #<Episode id: nil, show_id: nil, season_id: nil, episode_number: nil, title: nil, year: nil> 
ruby-1.9.2-rc1 > e.title
 => nil
ruby-1.9.2-rc1 > nt = "New Title"
 => "New Title" 
ruby-1.9.2-rc1 > nt.encoding
 => #<Encoding:UTF-8> 
ruby-1.9.2-rc1 > e.title = nt
 => "New Title" 
ruby-1.9.2-rc1 > e.title.encoding
 => #<Encoding:UTF-8> 
ruby-1.9.2-rc1 > e.save
  DEBUG - [06/Jul/2010 19:29:48] "SQL (0.1ms)   BEGIN"
  DEBUG - [06/Jul/2010 19:29:48] "Episode Create (0.2ms)   INSERT INTO `episodes` (`show_id`, `season_id`, `episode_number`, `title`, `year`) VALUES(NULL, NULL, NULL, 'New Title', NULL)"
  DEBUG - [06/Jul/2010 19:29:48] "SQL (0.4ms)   COMMIT"
 => true 
ruby-1.9.2-rc1 > Episode.find_by_title(nt).title.encoding
  DEBUG - [06/Jul/2010 19:30:04] "Episode Load (29.5ms)   SELECT * FROM `episodes` WHERE (`episodes`.`title` = 'New Title') LIMIT 1"
 => #<Encoding:ASCII-8BIT> 
ruby-1.9.2-rc1 > 

Я добился определенного успеха, переопределив методы доступа и переопределив их как:

class Episode 
  # ...
  def title
    title.encode!
  end
  # ...
end

Где encode! определено здесь, в документации API для 1.9 - чтобы указать это здесь "без параметров, возвращает копию str, перекодированную в Encoding.default_internal."

Хотя мои обходные пути успешны, я бы предпочел, чтобы UTF-8 выходил из базы данных, что, как мне кажется, показывает мой код.

Ответы [ 2 ]

7 голосов
/ 07 июля 2010

Вам, вероятно, нужен гем ruby-mysql, который поддерживает кодирование в 1.9, вместо более распространенного камня mysql, которого нет.Подробнее см. мой блог .

6 голосов
/ 12 февраля 2011

Драгоценный камень mysql2 также должен решить эту проблему, и он намного быстрее, чем гем ruby-mysql.

...