LoginSignup
24
25

More than 5 years have passed since last update.

Rails 3.2でiOS5の絵文字を扱う

Last updated at Posted at 2012-12-26

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_systemcharacter_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のencodingcharsetに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 DATABASEDEFAULT 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.rbActiveRecord::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を使わなくても自分でちゃんとエスケープしてあげる方法もある。

参考資料

(ちなみに)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ががんばってる、気がする。
24
25
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
25