Fusicきばんブログ

Fusic基盤ユニットの非公式技術ブログ

cakephp/cakephp にPR 出してmerge された話

(Fusic Advent Calendar 24日目)http://qiita.com/advent-calendar/2016/fusic & (CakePHP3 AdventCalendar 24日目)http://qiita.com/JoJeongMin/items/9fbc4cc1de9268b5de56http://qiita.com/advent-calendar/2016/cakephp3です。

Fusic 新卒の濱野です。
基盤ユニットに配属されてまもなく半年。
周りの席にはその道10年の職人さんが多く座っているので、
いつもつらい気持ちで席についています。

そんな肩身の狭い新卒ですが、
最近少しだけ胸をはれることがありました!

それは、、、

http://bakery.cakephp.org/2016/12/11/cakephp_3310_released.html
f:id:adiboy:20161223210812p:plain

そう。cakephp の core に出したPR が merge されたのです!!!
これはめでたい!!!

ということで、今回は、PR がマージされるまでの模様を時系列に沿ってお伝えしようと思います。
これを機に、PRの数が増えるとうれしいです!!!

何なおしたの?

pagination のバグを修正しました。

$paginate = [
    'limit' => 30,
    'maxLimit' => 20,
];

という設定をした時に、

maxLimit => 20

が無視され、

limit => 30

が採用されていました。
つまり、 maxLimit がちゃんと動いていませんでした。

どうやって見つけたか?

プラグインを作っていたら、挙動がおかしかったのでcore を読みました。
ちなみに、core は極力読むようにしています。
そして、時々ブログなどに結果報告を書いています。

例1
例2

その癖をつけていたおかげで、割とすんなり問題箇所の特定ができました。
そして、何が原因なのかがわかりました。

issue を登録する

こんなissue を登録しました。
https://github.com/cakephp/cakephp/issues/9848

全部英語で書くのが意外と大変でした。

PR を投げる

ついでに、issue に紐付ける形でPR も出しました。

I showed an example to fix this bug in #9849 ,
please review it, and I expect for some comments.
If my PullRequest is not worth to merge, don't hesitate to close it.

って書いて。
超意訳すると、

一応このissue を解決するPR 送りますー  

っていう感じです。

https://github.com/cakephp/cakephp/pull/9849

中の人とやり取りする

PR を出すとすぐ、chinpei215さんが調整してくださいました。
そして、マージされるまで面倒を見てくださいました。
この場を借りてもう一度お礼させて頂きます。
本当に有難うございました。

結局やり取りをしたのは2人でした。
一人は前述のchinpei215さん。
もう一人は、dereuromarkさん。

お二方とやり取りし、コードを修正しました。
そして、気づいたらマージされていました。

PR を出してから Merge されるまで1日もかかりませんでした。

感想

中の人は、優しかったです。(もちろんご指摘も頂きましたが)
そして、社内の人から声をかけられる機会が増えました。
これからも何かあったらどんどんプルリク出していこうと思います。

以上、未経験から入った新人が (cakephp/cakephp)https://github.com/cakephp/cakephp/ にPR を送ってマージされた話でした!

FluentdのログフォーマットでS3に保存されたApacheのアクセスログをAmazon Athenaで集計する

突然の Amazon Web Services Advent Calendar 2016 の 15日の記事です

基盤ユニットの小山 ( @k1LoW )です。

AWS re:Invent 2016すごかったですね。

数多くのサービスが使える形でリリースされてきました。

ただ、個人的にはその後の aws-sdk-ruby が v3に上がる というエントリーを見て、awspecをどうしようか戦々恐々としています。

突然のアクセスログ集計

あるAWS上に構築しているシステムプロジェクトで、突然「月単位でのアクセス集計1年間分」という要望がどこからともなく降ってきました。

そのプロジェクトでは、アクセスログを含む様々なメトリクスをFluentd ( td-agent ) で収集して、S3のバケットに保存しています。

ただ、保存の目的がいざという時のためで、稼働している分析基盤はありませんでした。

そこで、まずはローカルのMacBook ProでDigdag+MySQLによる集計を試みました。

が、

1日立ってもログ取得が終わらず。。。

(そういえば今まではdstatのような定期的に取得するメトリクス情報のログ集計で、アクセスログのようなさらに大量のデータの取得はしていなかった。。。)

さてどうしようかとなった時に、ふと思い出したのが、そう、Amazon Athenaでした。

そうだ、Amazon Athena を使ってみよう

f:id:k1LoW:20161214200312p:plain

aws.amazon.com

Amazon Athena、ざっくり紹介するとマネージドなPrestoです(ざっくりすぎる)。

AWS提供で、S3との相性がとてもよさそうなので、もしかしていけるのでは?と思って試してみたらできました。

前提

アクセスログは s3://[bucket-name]/access_logs/[ec2-hostname]/[year]/[month]/[day]/access_log.xxxxx.xxx.xxx.gz みたいに保存されています。

S3のバケット自体はap-northeast-1 (Tokyoリージョン)にあります(Amazon Athena自体はTokyoリージョンではまだ提供されていませんが、S3バケットの指定はできますよ!)。

実際のアクセスログはFluentdの一般的な(?)ログフォーマットで、

2016-12-19T04:35:27Z ip-10-xx-xxx-xxx    {"host":"xxx.xxx.xxx.xxx","user":"-","method":"GET","path":"/path/to/?q=query","code":"200","size":"1234","referer":"-","agent":"Some User-Agent"}

というような、タイムスタンプとhostnameとJSONの複合形式です。

テーブルの作成

本来ならば「Add Table」 リンクを押してデータベースの作成からするほうがいいのですが、今回は

Query Editorで、おもむろに以下のクエリを実行して集計用テーブルを作成しました。

CREATE EXTERNAL TABLE IF NOT EXISTS default.access_logs (
  date date,
  hour int,
  min int,
  second int,
  z string,
  host string ,
  ip string,
  user string,
  method string,
  path string,
  code int,
  size int,
  referer string,
  agent string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1',
  'input.regex' = '^(.+)T(\\d+):(\\d+):(\\d+)(.+)\\t([^\\s]+)\\t\\{"host":"([^"]+)","user":"([^"]+)","method":"([^"]+)","path":"([^"]+)","code":"([^"]+)","size":"([^"]+)","referer":"([^"]+)","agent":"([^"]+)"\\}'
) LOCATION 's3://[bucket-name]/access_logs/'

ポイントは 'input.regex' = '^(.+)T(\\d+):(\\d+):(\\d+)(.+)\\t([^\\s]+)\\t\\{"host":"([^"]+)","user":"([^"]+)","method":"([^"]+)","path":"([^"]+)","code":"([^"]+)","size":"([^"]+)","referer":"([^"]+)","agent":"([^"]+)"\\}' でFluentdのログフォーマットに合わせた正規表現を設定しているところです。

集計SQLの実行

ここからは簡単です。

1クエリごとに小さくない金額がかかりますが、突然の依頼なので悠長なことは言っていられません。

そこは札束で殴るように

SELECT MONTH(date) AS month, count(*), SUM(size) AS total_size
FROM rkb_web_public_access_logs
WHERE host IS NOT NULL
        AND date >= CAST('2016-01-01' AS DATE)
GROUP BY  MONTH(date) 
ORDER BY total_size DESC

などと、フルスキャンでがっつり集計などしました。

まとめ

Amazon Athenaは思った以上に気軽に使えました。

ただ、クエリごとに課金されるので、ご利用は計画的に。

突然のアクセスログ集計などには良いと思います。

よい突然ライフを。


おまけ

Dockerで開発環境・テスト環境を作る

こんにちは、2回目の登場になります。
基盤ユニットの櫻川です。

みなさんはDockerを利用してるでしょうか?

Fusicではまだまだ開発環境は Vagrantを複数立てる が主流ですが、 今はDockerの布教活動を行っている真っ最中です。

そもそもDockerを導入した目的

目的は 複数PHPのバージョンを同時に起動したい! です。

というのも、Fusicでは受託開発を行っていることもあり、プロジェクト毎に利用するPHPのバージョンやデータベースの種類・バージョンが異なること多いです。

そのため現在ではプロジェクト毎等でVagrantを新規で立ち上げたり、phpbrewを利用してPHPバージョンを切り替えたりして開発を行っています。

基本的にVagrantとphpbrewで複数PHPを利用することは可能だったのですが以下のような不満もありました。

  • Vagrant
    • 起動に時間がかかる
    • 複数起動すると結構重くなる
  • phpbrew
    • バージョン切り替えにApacheの設定を変える必要がある
    • 同一サーバー内で同時に複数バージョンを起動できない

そこで全てを解決してくれるDockerの出番なわけですよ!

利用している環境

Windowsです。
ただ、Docker自体はVM上のCentOS7で実行しているのでMacでも問題ないと思います。

  • Windows
  • Vagrant
  • Hyper-V
  • CentOS7(ホストOS)
    • Apache(リバースプロキシ用)
  • Docker + Docker Compose
    • Apache + PHP
    • Postgres
    • MySQL
    • samba

Dockerの構成

Dockerのコンテナをポート転送を設定して、最低でも3台のコンテナを起動しています。 ※必要なPHPのバージョン分 + データベース(Postgres or MySQL) + samba

この時点で「複数のPHPバージョンを同時に触りたい」という目的は達成しています。

ただし、このままだとDockerのポート転送を使っているので、PHPにアクセスするときのURLが http://example.com:8080/hoge/fuga というようにポート指定が必要になってきます。

ポート指定 をするのは嫌なのでDockerコンテナの前にリバースプロキシを置くことでポート指定をすることなく複数のPHPにアクセスすることを実現しています。
これで完全に目的達成!

また、ファイルの修正自体はsambaを経由して行っています。

f:id:sakutarou:20161201003230p:plain

どうやって作るの?

1. Dockerインストール

DockerをホストOS(CentOS7)にインストールします。

tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF

yum install docker-engine
systemctl enable docker.service
systemctl start docker

2. sambaコンテナを立てる

samba用コンテナをCentOS6で起動します。
※dockerコンテナ内のsambaの設定は行ってください。
 yum install samba など
※ホストOS上の /var/www/html をsambaコンテナでマウントしています

docker run -itd --name samba -p 139:139 -p 445:445 -v /var/www/html:/var/www/html centos:6 /bin/bash

3. Postgresコンテナを立てる

Postgres用イメージを利用してPostgresコンテナを起動します。
MySQLでもかまいません。
{Postgresパスワード} はパスワードを設定してください

docker run --name postgres -e POSTGRES_PASSWORD={Postgresパスワード} -d -p 5432:5432 -e PGDATA=/var/lib/postgresql96/data postgres

4. Apache + PHPコンテナを起動する

Apache + PHPコンテナをCentOS6で起動します。
※dockerコンテナ内のApache + PHPの設定は行ってください。

PHPの必要なバージョン分コンテナを起動します。

docker run -itd --name PHP -p 8080:80 -p 443:443 --link postgres --volumes-from=samba centos:6 /bin/bash

5. ホストOS上でリバースプロキシを設定する。

ホストOSにApacheをインストールし、リバースプロキシを設定します。
※コンテナ上にApacheを立ててリバースプロキシを行ってもいいのですが、dockerを知らない人でも触りやすいようにホストOS上にApacheをインストールしています。

PHPを複数起動したいので、実際はVertualHostと組み合わせています。

ProxyRequests Off
ProxyPass / http://hoge.example.com:8080/
ProxyPassReverse / http://hoge.example.com:8080/

6. イメージをAmazon ECR上に保存する

実行しているDockerコンテナをイメージ化して、ECR上に保存してます。

  • ECR上にリポジトリを構築しておいてください。
  • aws cliをインストールしておいてください。
docker commit samba samba
docker commit php

aws ecr get-login --region ap-northeast-1
出力されたログインコマンドを実行

docker tag samba 発行されたECR用sambaリポジトリURL
docker tag php 発行されたECR用phpリポジトリURL

docker push 発行されたECR用sambaリポジトリURL
docker push 発行されたECR用phpリポジトリURL

最後

上記を実行することで最低限のPHP開発・テスト環境を構築することができます。
ただ、公開環境で起動する場合は公開するポート等セキュリティに注意してください。

また、このままだと起動するのが非常に手間なので、Dockerfile + Docker Composeにまとめています。

Dockerを使ったことない人は是非!

ガラケー対応の話

Fusic 基盤ユニットの濱野(@gorogoroyasu) です。
全く未経験から入社した、2016年入社の新卒です。 諸先輩方のように圧倒的な記事はかけないので、僕のスマホ遍歴でも書いていこうと思います。

僕のスマホ遍歴

iPhone 4S  
iPhone 4 (4Sがアスファルトに直撃して画面が全壊したのでヤフオクで購入。この子は最終的に裏面が全壊)  
iPhone 5S  
arrows M02 (あまりに動作が遅く、親に譲った。使用期間半年。)
iPhone SE (←今)

ずっとiPhone を使っていたからAndroid を偵察してみよう! と思って買った arrows M02 が想像以上の遅さで、
これまで以上にiPhone が好きになった。

ちなみに、最後に使ってたガラケーのことはさっぱり覚えてない。

ガラケー対応プラグインの必要性

そんな僕が、ガラケー対応のプラグインを作成した。
理由は、スマホのシェアがそこまで高くないから。
ここにシェアがまとめてある。
www.nikkei.com

その中から抜粋すると、
スマホの普及率 : 67.4%(前年度比6.8ポイント増)
スマホ以外の普及率 : 64.3%(前年度比5.5ポイント減)
という感じ。

依然としてガラケーユーザーもいる模様。
(ガラケーでWeb サイトを閲覧する人がどれだけいるのかは別の話。通話はガラケーとかいうめんどくさい選択をしている人もいるし。。。)

しかし、なぜガラケー用のプラグインなるものが必要になるのか?
それは、、、

PC、スマホとガラケーでは、色々な違いがあるから。

たくさん違いがあるが、個人的に重要だと思う違いは、
1) 文字コード
2) クッキー使用の可否
3) 外部CSS の読み込み
4) JavaScript 使用の可否
の4つ。
それぞれ簡単に説明していく。

1) 文字コード
最近のPC,スマホサイトには、文字コードは"UTF-8" が採用される。
しかし、ガラケーの文字コードは"Shift-JIS" である可能性が高い。
可能性が高いと言っているのは、
携帯キャリア、製造年月によってはUTF-8 が使用できることがあるからだ。
しかし、大事なのは、文字コードは "Shift-JIS" を使った方が安全だということだ。
ということで、全ての文字は、 UTF-8 から Shift-JIS に変換されなくてはならない。
めんどくさい。

2) クッキー使用の可否
基本的に、ガラケーではクッキーを使うことができない。
では、どうやってログイン情報等を管理するか?
URL の末尾にセッションID をつける。
それにより、ログイン情報等を管理する。
実に原始的。めんどくさい。

3) 外部CSS の読み込み ガラケーでは外部CSS が使用できない(ことが多い)。
そのため、CSS を直書きする必要がある。

<div class="hoge" style="color: green;">hoge</div>   

みたいな感じ。
めんどくさい。

4) JavaScript 使用の可否
基本的にガラケーではJS を使うことができない。
悲しい。

と、ざっくり上げただけでも4つの違いがある。
特に面倒くさいのは1) と 2) 。
ここらへん、なんとかしたい!!

作ったプラグイン

ということで、 上記 1) と 2) をいい感じで解決するプラグインを作った。

--- 追記 ---

何のプラグインを作ったかが文章だけで分からないというご指摘をいただき追記。 作成したプラグインは、CakePHP3 用のものです。

--- 追記終わり ---

本当は 3) もなかなかめんどくさいんだけど、
いい感じにテンプレートとか拾ってきて対応して下さいー。

一言対応策

1) 文字コード
本プラグインでは、文字列の変換を

public function afterLayout(Event $Event, $layoutFile)  

で行った。
詳細は Garak/src/View/Helper/EncodeHelper.php
を参照。

2) クッキー問題
redirect をいじって、クッキー情報をクエリに持たせた。
詳細は、k1low/yak を参考に実装。

プラグインはcomposer を使って ここ からインストールできます。
名前は、 "Garak" !

ぜひ使ってみて下さい。

と、言いたいけど、
近い未来このプラグインが不要になることを願ってやみません。

あとがき

というわけで、初めてのチームブログを書かせて頂きました。
いかがでしたか? よかったら、コメントなりなんなりよろしくお願いしますー。

CakePHP3でエラーを自由自在に処理するencount

基盤ユニットリーダーの櫻川です( @kozo )。

さて今回はまたCakePHPに戻って、自分が作成しているencount プラグインについて書かせてもらいます。

encountはこちらになります。 github.com

encount 何が出来るの?

PHPが出力する NoticeWarningException といったエラーに対して、簡単にフック処理するを作ることが出来るプラグインになります。
例えば、本番環境にリリースしたサービスにencountを入れておくと、Noticeが出たタイミングでメール通知を行うといったことが可能となります。

また、 Senderクラス というencount専用のクラスを実装することで、エラー時の処理を自由にカスタマイズすることが可能です。
例えば、「Slackに通知する」や「NoticeやWarningはメールに通知するけど、FatalはSlackに通知する」といったようなことも可能となります。

※ Railsだと、 exception_notification が近い分類になると思われます。

encountのインストール

お決まりのcomposer経由にインストールになります。

composer require fusic/encount

encountの設定

ErrorHandler をCakeデフォルトのものから、encountのものへ切り替えます。
config/bootstrap.php で設定されてます。

// config/bootstrap.php
<?php

// shell
use Encount\Console\EncountConsoleErrorHandler;
(new EncountConsoleErrorHandler(Configure::read('Error')))->register();

// web
use Encount\Error\EncountErrorHandler;
(new EncountErrorHandler(Configure::read('Error')))->register();

encountはデフォルトの状態だと、Notice等何らかしらのエラーが発生したタイミングでメールを送信します。
その為、メール送信用の設定をします。

// config/app.php
<?php

return [

    // Errorを修正
    'Error' => [
        'errorLevel' => E_ALL,
        // exceptionRendererを追加
        'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
        'skipLog' => [],
        'log' => true,
        'trace' => true,
        // encount用設定を追加
        'encount' => [
            'force' => false,
            'sender' => ['Encount.Mail'],
            'mail' => [
                'prefix' => '',
                'html' => true
            ]
        ],
    ],

    // Emailにerrorを追加する
    'Email' => [
        // encount用設定を追加
        'error' => [
            'transport' => 'default',
            'from' => 'from@example.com',
            'to' => 'to@example.com'
        ]
    ],

];

encountの確認

デフォルトの設定では、 開発時はエラーを通知しないようになってます。
ですので、 debug を0に設定して、 echo $a['test'] というような存在しないキーにアクセスしてNoticeを発生後、メールが送信されれば設定OKです。

※開発時でも通知を行いたい場合は、 app.phpに先ほど設定した、 Error.encount.forcetrue に設定してください。

Senderを使って拡張する

さて、デフォルトの設定ではなく、通知を自作したい場合は、 Senderクラス を作成することになります。
例としてエラー内容を Slackで通知するSender を作成します。

1.SenderClassを作成する

src/Sender/Slack.php

<?php
namespace App\Sender;

use Encount\Sender\SenderInterface;

class Slack implements SenderInterface
{
    public function send($config, $code, $errorType, $description, $file, $line, $context)
    {
        // エラーが発生した時の処理をここに書く
        $url = '{{Incoming WebHooks URLを記載する}}';
        $data = array(
            'text' => $description,
        );
        $options = array(
            'http' => array(
            'method' => 'POST',
            'header' => 'Content-Type: application/json',
            'content' => json_encode($data),
            )
        );
        file_get_contents($url, false, stream_context_create($options));
    }
}

2. SlackSenderを使うように設定する

config/app.php

<?php
    'Error' => [
        'errorLevel' => E_ALL,
        'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
        'skipLog' => [],
        'log' => true,
        'trace' => true,
        'encount' => [
            'force' => true,
            // 作成した SlackSender に切り替える
            'sender' => ['Slack'],
            // Mail + Slack両方送信する場合は、以下のように2個並べる
            // 'sender' => ['Encount.Mail', 'Slack'],
            'mail' => [
                'prefix' => '',
                'html' => true
            ]
        ],
    ],

3. 動作確認

先ほどと同じく、echo $a['test'] というような存在しないキーにアクセスしてNoticeを発生してSlackが送信されればOKです。

最後に

気になった方はぜひ!encount使ってみてください!
そして、encountへの プルリク、Issue、Watch、Starよろしくお願い致します。

CentOS5/RHEL5系でSHA-2/TLS1.2のアウトバウンドのhttps通信を行う方法を検討してみる

基盤ユニットの貞方(@sadapon2008)です。

今回は刺さる人には刺さるCentOS5/RHEL5系でSHA-2/TLS1.2のアウトバウンドのhttps通信の実現方法を検討してみたいと思います。

背景

CentOS5/RHEL5系はすでにサポートが切れていますが、諸事情でまだ稼働しているシステムがあったりします。 CentOS5/RHEL5に含まれるOpenSSLは0.9.8eのため、curlなどOpenSSLをベースにアウトバウンドのhttps通信を行う場合、SHA-2やTLS1.2には未対応です(SHA-2はOpenSSL 0.9.8o以降、TLS1.2はOpenSSL 1.0.1以降が必要)。 しかし、世の中の流れでhttps通信で使うサーバ証明書の署名アルゴリズムはSHA-1からSHA-2への移行が進んでいて、 TLSのバージョンもAPIサービスによっては1.2でしか受け付けないものも出てきています。 そこで、CentOS5/RHEL5系で外部とSHA-2/TLS1.2のhttps通信を行う方法を検討してみました。

案1. 最新のOpenSSLをソースコードからコンパイルしてインストールする

最初に思いつくのは、最新のOpenSSLをソースコードからコンパイルしてインストールすることです。 ただし、ディストリビューションの標準パッケージのOpenSSLを上書きするようなことをするとOSがぶっ壊れる可能性が高いため、 標準パッケージと競合しないようにインストールしないと危険です。 その場合、コンパイルしてインストールしたOpenSSLでhttps通信するためには、curlなどOpenSSLを利用するプログラムも ソースコードからコンパイルしてインストールする必要があります。

また、PHPなどサーバサイドのアプリケーションなどでそれらを利用するには、アプリケーションのソースコードの大幅な変更も必要になります。

案2. 別途リバースプロキシサーバを用意する

CentOS5/RHEL5より新しいOpenSSLを利用できるCentOS7/RHEL7などでリバースプロキシサーバを用意して、そちら経由でhttps通信を行うことも考えられます。 例えば、「https://api.hoge.com」のURLでアクセスしていたものが、「http://(リーバスプロキシサーバのIPアドレス)」のURLでアクセスするように構成することが可能です。

この方法の場合、PHPなどサーバサイドのアプリケーションはアクセスするURLの修正だけで済むため、案1.に比べたら変更量を抑えられます。

ただし、別途サーバを準備する必要があります。

案3. CentOS5/RHEL5上でJava 1.7のリバースプロキシサーバを動作させる

CentOS5/RHEL5のディストリビューンの標準パッケージにはJava 1.7が含まれています。 Java 1.7自体もすでにサポートは切れていますが、実はJava 1.7はSHA-2/TLS1.2に対応していますので、Javaで動くリバースプロキシサーバを動かせば、別途サーバを用意する必要がありません。

Javaはずぶの素人なのですが、下記のGithubのコードを参考にJettyの組み込みプロキシを使うプログラムを作成してみました。

github.com

作成したものがこちらになります。

github.com

gradlewを使っていますので、以下のようすればビルドできると思います。

$ sudo yum install java-1.7.0-openjdk-devel
$ chmod a+x gradlew
$ ./gradlew fatJar

ビルドに成功したら、以下のようにコマンドを実行すると「http://127.0.0.1:8080」で待ち受けするリバースプロキシとして動作します。

$ java -jar ./build/libs/simple_reverse_proxy.jar https://api.hoge.com 8080

この状態で下記のようにするとSHA-2/TLS1.2のサーバに対してhttps通信を行うことができます。

$ curl http://127.0.0.1:8080/

正直Jettyの中身はまだよくわかっていないところもあるのですが、superviosrなどと組み合わせればデーモン化したりもできそうです。

jQuery.tooltipの「出力エリアをずれてしまう」の原因と解決

基盤ユニットのイです。
結構軽い記事になりますが、よろしくお願いします。 ( _ _ )

概要

皆さんは、tooltipをよく利用していますか?
tooltipとは、マウスを当該イメージ画面や特定の対象に持っておけば、当該対象に関する詳細説明が表示される機能でございます。

const IMG_MESSAGE = 'はじめまして!イと申します。';
$('.ldh-img').tooltip({
    position: { my: "left+15 center", at: "right center" },
    content: IMG_MESSAGE,
    track: false
});

f:id:ldhdba:20160923083402p:plain

通常、🔗jQuery.tooltip、もしくは🔗Bootstrapで、機能を導入しています。
しかし、その後、テストや運用から以下のような「出力エリアをずれてしまう」の結果のため、困るかもしれません。

const TEST_CASE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$('.ldh-img').tooltip({
    position: { my: "left+15 center", at: "right center" },
    content: TEST_CASE,
    track: false
});

f:id:ldhdba:20160923080948p:plain

ということで、こいう結果の原因と解決方法を話しさせて頂きます。

原因

まず、いくつかのケースの結果をご覧して、どのケースで出力エリアをずれてしまうのか、確認しましょう。

const TEST_CASE_1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const TEST_CASE_2 = '12345678901234567890123456789012345678901234567890';
const TEST_CASE_3 = 'ひらがなカタカナひらがなカタカナひらがなカタカナひらがなカタカナ';
const TEST_CASE_4 = '漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字漢字';
const TEST_CASE_5 = '한글한글한글한글한글한글한글한글한글한글한글한글한글한글한글한글한글한글';
const TEST_CASE_6 = '@#()*$(*%()!#&%)(&!#*()&*)(!#&*(&!#(*&*()!$&*(!#&%*(!#&*!#$';
const TEST_CASE_7 = '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!';
const TEST_CASE_8 = '12345678901234567890123456789012345678 9012345 67890';
const TEST_CASE_9 = '12345678901234567890123456789012345678한901234567890';

$('.ldh-img').tooltip({
    position: { my: "left+15 center", at: "right center" },
    content: TEST_CASE_9,
    track: false
});

ケース①、連続英字
f:id:ldhdba:20160923083948p:plain

ケース②、連続数字
f:id:ldhdba:20160923084033p:plain

ケース③、連続ひらがな・カタカナ
f:id:ldhdba:20160923084059p:plain

ケース④、連続漢字
f:id:ldhdba:20160923084140p:plain

ケース⑤、連続ハングル
f:id:ldhdba:20160923084154p:plain

ケース⑥、連続特殊文字
f:id:ldhdba:20160923084242p:plain

ケース⑦、連続ビックリ文字
f:id:ldhdba:20160923084334p:plain

ケース⑧、スペース含め連続数字
f:id:ldhdba:20160923084507p:plain

ケース⑨、ハングル含め連続数字
f:id:ldhdba:20160928134400p:plain

テストケースが多かった感じがありますが、テストの結果をご覧すると、原因をすぐ理解しましたと思っております。
原因は、🌟スペース以外の1バイト文字がスペースなしで連続に並んでいると、それを一つの単語と判断してエリアを過ぎてしまう
という原因でした。

解決

解決できる方法としては、いろいろあると思いますが、自分が解決した方法を説明させていただきたいと思っています。

それは、CSSのword-breakで改行処理する方法でございます。
word-breakとは、単語意味の通り、単語を切るというものです。
基本的に、次の行に変わるときは、単語単位で変わることになっています。
word-breakは、エリアの範囲を過ぎたら改行するCSS設定でございます。

.ui-tooltip {
    word-break:break-all;
}

f:id:ldhdba:20160923095835p:plain

まとめ

jqueryから提供されている便利なオブジェクトがいろいろありますが、利用ケースによって予想以外の結果を出している場合があります。
その原因が起こる可能性が少ないだと無視するとまずいと思っております。
しっかり、利用する機能は色々なケースを確認しましょう!
以上です。