iOS5の絵文字は4バイトのUTF-8で記述されているので、それに対応したPostgreSQLやMySQL 5.5.3以降が必要。
ここではMySQL 5.5を利用する。
MySQLでutf8mb4を扱う
前述した通り、MySQLで4バイトのUTF-8を扱うにはMySQL 5.5.3以上でキャラクタセットにutf8mb4を使う必要がある。
MySQLサーバ
たぶんキャラクタセット関係にはutf8mb4を設定しておいた方がいい。
character_set_system
とcharacter_set_filesystem
以外はクライアントーサーバ間の通信と保存に使われるので、適切に設定されていないとちょん切られてしまったりすると思う。要検証。
mysql> show variables like 'char%';
+--------------------------+------------------------------------------------------+
| Variable_name | Value |
+--------------------------+------------------------------------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| character_sets_dir | /usr/local/Cellar/mysql/5.5.10/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
ActiveRecordからの接続
で、mysql2 gemの最新リリースの0.3.11ではutf8mb4に対応していないので、未だに取り込まれていないパッチを自分であてるか、Gemfileで次のようにブランチを指定する(自分はこっちをやってる)。
gem 'mysql2', :git => 'git://github.com/tmtm/mysql2.git', :branch => 'utf8mb4'
database.ymlのencoding
とcharset
にutf8mb4を指定する。
development:
adapter: mysql2
charset: utf8mb4
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: myapp_development
pool: 5
username: root
password:
socket: /tmp/mysql.sock
charset
を指定するのは、CREATE DATABASE
でDEFAULT CHARACTER SET
に使われるから(デフォルトはutf8)。なんでencoding
に統一されていないんだろう?
ユニークインデックスのバイト数制限
MySQLのユニークインデックスは767byteまでしか使えないという制限があるため、767byteわる4で191文字までしか使えない。
例えばschema_migrations.version
はvarcharでユニークインデックスがはられているが、デフォルトでは255文字が割り当てられているのでマイグレーションを実行したときにエラーが出る。
実際には14文字しか使わないので、varchar(15)
に変更する。
$ bundle open activerecord
からlib/active_record/connection_adapters/abstract/schema_statements.rb
の419行目あたりを書き換える。
schema_migrations_table.column :version, :string, :null => false, :limit => 15
ただ本番のgemを書き換えるのも嫌なので、自分はconfig/initializers/active_record_schema_migrations_version.rb
にActiveRecord::ConnectionAdapters::SchemaStatements#initialize_schema_migrations_table
をそのままコピーして、該当箇所だけ書き換えている。
またschema_migrations.version
以外にも自分でユニークインデックスをはる場合には:length
オプションで191文字以下がインデックス対象になるようにする必要がある。
intとvarcharで複合インデックスをはる際には:length
を細かく指定できるので気をつけて。
add_index :entries, [:user_id, :url],
unique: true, length: { url: (191-4) },
name: "index_on_feed_entries"
JSON
ActiveRecord::JSON::Encoding.escape
は4バイト以上の文字のエスケープに失敗するので、今はJSON gemを使ってる。
config/initializers/active_support_json_encoding.rb
にこんな感じ:
module ActiveSupport
module JSON
module Encoding
class << self
def escape_with_json_gem(string)
::JSON.generate([string])[1..-2]
end
alias_method_chain :escape, :json_gem
end
end
end
end
JSON gemを使わなくても自分でちゃんとエスケープしてあげる方法もある。
参考資料
- rails mysqlでiPhoneアプリの絵文字対応
- How do you deal with the conflict between ActiveSupport::JSON and the JSON gem?
- Emoji and Rails JSON output issue
(ちなみに)WebSocket/Node.js
WebSocketにNode.jsを使う場合、JavascriptのエンジンであるV8が4バイトのUTF-8に対応していないので、URLエンコードなどの適当な変換をかけてからデータを渡してあげる。
URLエンコードだとこんな感じ:
# Rails側
encoded = Rack::Utils.escape(mb_string)
// JS側
var mbString = decodeURIComponent(encoded).replace(/\+/g, " ");
Android, iOS4はどうするの?
- iOS4からiOS5で絵文字のキャラクタ・セットが変わったけど、iOS5ではiOS4の絵文字を表示できるので、iOS5からの絵文字をすべてiOS4のものに変換してしまうのがいいかもしれない。
- でもそれもいつまで有効なんだろうなぁ…。
- Androidは知らん
- NaverのLINEががんばってる、気がする。