2017年 10月の投稿を表示しています

php-amqplib と RabbitMQ について調べた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

見慣れないライブラリを使ってるプロジェクトがあったので調査メモ。

php-amqplibとは?

php-amqplib/php-amqplib: AMQP library for PHP

RabbitMQ とよばれるメッセージングキューのためのクライアントライブラリ for PHP 。厳密には RabiitMQ という話ではなく、そこで利用されるAMQP と呼ばれるプロトコルに準拠したものを取り扱う。メッセージのやり取りについてその専用のプロトコルでやりとりしている。
利用するには mbstring と bcmath のエクステンションが必須になる。

php-amqplib は packagist で管理されているので composer からインストールすることになる。

RabbitMQ とは?

RabbitMQ - Messaging that just works

うさぎがかわいい。

AMQP というメッセージプロトコルを語れる、メッセージングキュー用のミドルウェア…、という分類でいいのかな。

Javascript の Promise みたいなイメージでいると多分認識が合う(合わない)
メッセージを送る人と受け取る人、そして運び人のウサギがいる。ウサギが様々なメッセージのやり取りや順序管理をしてくれるので、送る人は誰に届くかわからないけどこの処理できる人やって、受け取る方は誰から来たかわからないけど処理して結果をしまっておく、なんてことが出来る。
(=マイクロサービスなアーキテクチャ)

ちなみに Erlang で書かれている。
ちなみに今年で 10 年経つんだとかでトップページがお祝いムード。

 

手軽に試すなら公式イメージがあるので docker で。

library/rabbitmq - Docker Hub

 

そうでなければ公式サイトを参考にインストールしていくとよい。

RabbitMQ - Downloading and Installing RabbitMQ

CentOS の項目をみるとこんなことが書かれている。

Overview
rabbitmq-server is included in Fedora. However, the versions included are often quite old. You will probably get better results installing the .rpm from PackageCloud or Bintray. Check the Fedora package details for which version of the server is available for which versions of the distribution.

Fedora には rabbitmq-server な名前で既に登録されているものがあるが、古い可能性ので Package Cloud の RPM を使うのがオススメする、って書いてあった。

rabbitmq/rabbitmq-server - Packages - packagecloud.io | packagecloud

 

ちなみに、ちょっと昔の記事なので、バージョンが古くて今と異なる部分があるかもだが、実際に業務運用に向けていくようなところも込で、がっつりとした情報があったので、こちらも合わせて読むと実践向けっぽい。

はじめての RabbitMQ|サイバーエージェント 公式エンジニアブログ

PHPから使ってみる

例によってお手軽に試してみる程度。 docker で準備を進めていく。

// docker でキューサーバを立ち上げてみる
// 5672 ポートをバインドするのを忘れずに
$ sudo docker run -d -p 5672:5672  --name rabbit-server rabbitmq:3
...

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                                                   NAMES
92547dbde579        rabbitmq:3          "docker-entrypoint.s   5 seconds ago       Up 4 seconds        4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 25672/tcp   rabbit-server


// モジュールが必要なので入れる(mbstringはもともとあった)
$ sudo yum install php-bcmath --enablerepo=remi-php70

// モジュールを移動しないとダメだった(パス設定してないだけだと思う)
$ sudo mv /etc/php.d/20-bcmath.ini /etc/opt/remi/php70/php.d/
$ sudo mv /usr/lib64/php/modules/bcmath.so /opt/remi/php70/root/usr/lib64/php/modules/

$ php -m | grep bcmath
bcmath


// アプリを構築する
$ mkdir php-amqplib-test
$ cd php-amqplib-test

$ composer init
...

$ composer require php-amqplib/php-amqplib:2.7.*
...

ソースを書く。

<?php
// send.php

require('vendor/autoload.php');

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// デフォルトID/PW = guest/guest
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

// 送信するチャンネルを設定
$channel = $connection->channel();

// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L597
// string queue = ''
// bool passive = false
// bool durable = false
// bool exclusive = false
// bool auto_delete = true
// bool nowait = false
$channel->queue_declare('hello', false, false, false, false);


// メッセージ作成
$msg = new AMQPMessage('Hello World!');

// メッセージ送信
// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L1086
// string msg
// string exchange = ''
// string routing_key = ''
$channel->basic_publish($msg, '', 'hello');
<?php
// receive.php

require('vendor/autoload.php');

use PhpAmqpLib\Connection\AMQPStreamConnection;

// デフォルトID/PW = guest/guest
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

// チャンネルを設定
$channel = $connection->channel();

// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L597
// string queue = ''
// bool passive = false
// bool durable = false
// bool exclusive = false
// bool auto_delete = true
// bool nowait = false
$channel->queue_declare('hello', false, false, false, false);


// コールバック関数の用意
$callback = function ($msg) {
        echo $msg->body . "\n";
};

// メッセージ受信
// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L901
// string queue = ''
// string consumer_tag = ''
// bool no_local = false
// bool no_ack = false
// bool exclusive = false
// bool nowait = false
// function callback = null
$channel->basic_consume('hello', '', false, true, false, false, $callback);

// メッセージ受信待ちループ
while(count($channel->callbacks)) {
    $channel->wait();
}

なお引数に関するコメントはデフォルトの値。これが何を意味しているかはまたちょっと分からない…。ドキュメントを読むしか…。

ちなみに PHP の API ドキュメントは見当たらない。 Github のソースを読むか、インタフェースはだいたい同じなので JavaDoc のものを読むかするとよい。

 

ターミナルを2つ開いて実行してみる。

// 一方のターミナル
$ php send.php
$ php send.php
$ php send.php
$ php send.php

// もう一方のターミナル
$ php receive.php
Hello World!
Hello World!
Hello World!
Hello World!

ただし、これには問題があって、このように単純に送るだけだとキューが RabbitMQ を再起動したりマシンが落ちたりすると、中身がきれいさっぱり消える。

$ php send.php
$ sudo docker restart rabbit-server
$ php receive.php

// でてこない!

公式ドキュメントだとこのページの Message durability の項目に書いてあるが、送信時に引数を設定しないといけない。

RabbitMQ - RabbitMQ tutorial - Work Queues

<?php
require('vendor/autoload.php');

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// デフォルトID/PW = guest/guest
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

// 送信するチャンネルを設定
$channel = $connection->channel();

// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L597
// string queue = ''
// bool passive = false
// bool durable = false
// bool exclusive = false
// bool auto_delete = true
// bool nowait = false
$channel->queue_declare('hello', false, true, false, false);


// メッセージ作成
// デリバリモードの追加
$msg = new AMQPMessage('Hello World!', ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);

// メッセージ送信
// https://github.com/php-amqplib/php-amqplib/blob/master/PhpAmqpLib/Channel/AMQPChannel.php#L1086
// string msg
// string exchange = ''
// string routing_key = ''
$channel->basic_publish($msg, '', 'hello');
// 一旦再起動しておく
$ sudo docker restart rabbit-server

// 送信, 再起動, 受信
$ php send.php
$ sudo docker restart rabbit-server
$ php receive.php
Hello World!

なるほどね~。

ちなみにの話

ちなみに Laravel のキュードライバとして RabbitMQ を使うライブラリも既にあり、これはもう入れるだけでいい。先人の知恵すごい。

vyuldashev/laravel-queue-rabbitmq: RabbitMQ driver for Laravel Queue

書いてある通りです、以上の説明は出来なくて。要するに QUEUE_DRIVER を変更し、 RabbitMQ 用の設定を幾つか書けば勝手にやってくれるのだそうだ。

ちなみに上記の durable 、メッセージの永続化はデフォルトで有効になっているようだ。

 

ちなみにちなみに、メッセージキューの実装を標準化しよう!という話があるっぽい。知らなかった。動きとしては将来的に PSR になったらいいな、くらいの強制していないけどぼちぼち運動してます!なもの。

queue-interop/queue-interop: Promoting the interoperability of MQs objects. Based on Java JMS

書き方、インタフェースが大きく分かれそうだし、複数対応を謳うようなライブラリもあるし、標準化されるといろいろ嬉しい予感がする。

残念ながら ISUCON 7 は予選敗退で幕を閉じた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

ISUCON 5, ISUCON 6, と引き続き ISUCON 7 に参加しました。

ISUCON についてはこのあたりを見ると良いです。
ISUCON7 まとめ : ISUCON公式Blog

今回もぼっちーむを回避して、会社の人と組んでいきました。前回の ISUCON 6 と同じメンバーです。

過去の参戦レポはこのあたり参照するとよいです。

今回は、ぼくの参戦時間が実質 3 時間程度、少ない時間しか当てられませんでした。
(ぼくからみた義親、つまりは妻の両親が家庭訪問という重大なイベントが発生して、椅子コンよりも優先度高めだった)

当日までにやっておいたこと

メンバーで集まって、何度か方針や準備するものについて話あっていました。準備するのは当日にあたふたしないように、ある程度は機械的に出来るように、というところからです。
具体的にはこんなものが挙がりました。

  • さくらクラウドのアカウント準備
    • クーポン投入
    • お金をケチらない気持ち。必要なら大規模マシンで殴って戻すことも視野に。
  • 個々人の環境
    • ISUCONイメージがさくらクラウドから提供される?情報まち。
    • されないならubuntu使って空っぽマシン用意
      • 中身をがばっともってくる
  • 簡単に色々なログ仕込める君
    • スクリプトとかansible的なやつでやりたいよね
    • mysql-slowとかアクセスログとか。
    • CPUとかメモリ、ネットワークの様子もみたいよね
    • プロファイリングツールの準備
      • alp
      • new relicをいれたい
        • 準備だけしておきたい
        • PHPの計測もできるが細かに計測する記述をしないといけない
      • Kibana
        • mysql も
        • td-agent でいろいろ飛ばせば全部見れる
  • ssh公開鍵の準備
    • 鍵をcurlするだけでつなげるような状態にしたい
  • gitリポジトリ作る
    • 鍵とssh-configとを準備する
  • つけるべきHTTPヘッダの整理
    • 304 not modified, expire + etag
    • 200 ok
    • 301 / 302 転送
  • 画像圧縮
    • ツールや構築の下準備
  • ご飯@ランチの用意
    • 当日朝各自で買ってこよう
    • 大人なのでお菓子とアルコールはご自由に!
  • FWになれる
  • 実装の読み解き
    • 頭の中でやるとつらい
    • ホワイトボードに書こう
    • 付箋を使おう
  • 当日の流れを作っておく

ISUCON のたびに毎回考えるのはアレなので、そろそろメンテしながら使いまわしをしていきたい所存。

それと、練習しよっ♡、なんて言ってたけど結局一回もやってない。いや、環境構築までは一回やった。 vagrant-isucon という素晴らしいものがあって、これを使わせてもらった。当時行われた時の得点まで伸びはしないだろうが、何をしたらどう伸びるのか、という部分は見ていける、便利ちゃん。

matsuu/vagrant-isucon: ISUCON過去問を構築するためのVagrantfile集

ちなみに結局当日になっても、このリストの大半は実施あるいは準備されませんでした。。熱量の違いだったり、そういうアレ。

当日にやったこと

合流したのは18時過ぎ。それまでに以下のことをチームメンバーが進めていました。

  • phpへの切り替え
  • リポジトリの準備
  • /messages/ の N+1 の解消
  • インデックス設定
  • 画像ファイルを DB から取り出し DB に保存しないようにする
  • nginx, mysql, php-fpm の微調整

合流したときにはスコア的には 1 万くらい?
( っ˘ω˘c).。o○( なんでスコア伸びてないんだ…? )

そこからはこんなことを進めていきました。

  • 画像圧縮するしないで揉めてたっぽいのでやるなって言った
  • それするより画像に 304 つけろって言った
  • LINE 通話をつなげた
  • ハッピーターンでお腹を満たす
  • /fetch の N+1 改善
  • nginx の設定サポート
  • /message や /login あたりの微妙な改善

時間も少なく、現場まで合流するのは厳しかったのでリモートで作業せざるを得ませんでした。チャットだけでも良かったんですが、声が繋がったほうが何かと便利なので LINE 通話を PC で繋ぎっぱなしにして、あーだこーだ言いながらやってました。

最終スコアは 37000 くらい、だったかな?(結果一覧に出ている点数と微妙に違うっぽい?)

ISUCON 5 とくらべて、アプリ上の実装の問題はほとんど消化できたと思っていて、そのあたりは前回からレベルアップしたと思う(チームとして)

画像の304については理解が浅くて結局出来ていなかった。周りの攻略記事を見ると、ここが出来る出来ないでスコアが大きく変わっていたように思える。最後までベンチマーク実行結果に icons のレスポンスが遅くて~、とあって、これが改善できないともうスコア伸びないってのは分かっていたけど、いろいろなものを信用しすぎて何も動けなかった。

結局よくわかんなくて合流してからずっと右往左往したところがあって、ベンチマーク時に 2 つチェックいれたらどうなんねん、というところ。リバースプロキシしてバランシングしたり、外したり、そもそも設定うまく行かなくて、だいぶ時間がかかった。結局 2 つチェックいれたらベンチが倍速になってスコア上がるんかな?どこかに説明あったっけ(見落としたかも?)

サーバ構成的には AP + AP + DB という、多分他と同じ構成。

次に向けて

ISUCON が来年も実施されるかわからないけれども(ものすごく楽しいイベントなので実施されてほしい)、自分のスキルアップはもちのろん、チーム側の意識・熱量・レベル感が合ってないどうすっかなあ、というところをやっていかないと勝てないだろうなって気持ち。例えばあった話だと、とりあえず画像圧縮してプロキシキャッシュすれば勝てるやろ~~、とか真面目に雑な話をしている人とか。19時くらいになってわかんないからスロークエリ入れるか~~~とか言ってたり、先やれよ。とか。うごかないんだけど見てくれ~って、別にこっちも分からないから都度調べてるんだけどなあ…。とかなんとか、こんな人と仕事していたのかとか云々。余談が過ぎた。ようは 3 人で役割を分けてそれぞれ最高のパフォーマンスをしていくべきで、それは 2 人になっても変わらなくて、ぼくが居ないまたは手が出せない状況だろうと頑張ってくれって気持ち。なのでそれが足りないっぽいのでチームビルディングというか、そういう類の云々がだめだめだったんだろうなあ~~。

もし次があったとして、次も同じチームででるかっていうと怪しく、楽しく全力で戦えるチームで出来るといいな。どんどんやっていこう。
結果として ISUCON 7 は予選敗退だったけれども、俺の、俺たちの、 ISUCON 7 はまだ終わってない!(練習しよ

そんなポエムで〆


ISUCON 運営の人たちメチャ忙しいと思いますけど、こうやって今年も楽しいイベントを開催してくれてありがとうございました?

PCウィッチャー3のメインストーリークリア報告、あるいはプレイレポート

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

PC 版ウィッチャー3 クリアしたよって報告。ネタバレを回避するぎりぎりを行く。

DLC なし。攻略サイトも特に見ていない。

総プレイ時間は 105 時間。一日 2-3 時間くらいでちまちまやったり、途中でモンハンやったり、マインクラフトやったり、いろいろしてて、プレイ期間半年くらいかかった。

 

難易度はストーリー+バトル。
たまに事故死したけれども水や肉をモリモリ食べて、クエンを張って。蘇生のスキルもあったのでそれで後半はほぼ死亡事故なし。なお転落死は何度もしている模様。意外と行けるだろって高さで体力がモリっと持っていかれるのよね・・・。

 

エンディング分岐はハッピーエンド・・・なのか・・・?一番大きなエンディングスタイル以外にも、そのあとに出てきたお話はこうなった、みたいな絵巻を見るとこっちの結末も選択肢次第では大きく変わったものになるんだろうな。ゲラルト本人は放浪の旅 END 二兎を追う者は一兎をも得ず、とはよく言ったものだ。

(前作を知らないけれど、事情聴取の様子から)そもそもウィッチャーの世界でハッピーエンドという概念はない、だろう。誰かから見たらそれはハッピーなのかもしれないけど、もっと広い目で、あるいは別の視点でとらえるとそれは別にハッピーではないのでは・・・?

ストーリー中でも幾度となく、そういった選択肢が提示されていたように感じる。一方を生かすと後で怒られたり、報酬が出なかったり。良い方、良い方に行こうとしても、それが本当に良い結果に繋がるのかまったくわからなかった。もうちょっと具体例でいくと、人助けたわ~~~って思って後日その村に行く、ストーリーが進んでからいくと、崩壊したり敵対してたり、とかそういう。

 

サブクエストや収集品はほぼ回収した、と思う。掲示板やストーリーの過程で発停するサブクエストはすべて完了した。

序盤であった盗賊を馬で追いかけるクエストは失敗した、馬の操作になれていなかった。あとはクエストの過程で魔術師と道を違える結末になってしまった。ラブシーンも過ごした、敵対するべきではないキーキャラだったと思うのだが、上記のような、当たり障りのない正義感を貫こうとして失敗してしまった。

一部見つからない、地図が購入できてない猫流派の最高級は未回収。収集でいけばスケリッジの ごみ拾い 密輸品拾いが一番面倒だった・・・。船は事故るわドラウナーとセイレーンに囲まれるわ、もうやりたくない。

 

最終的な装備品はグリフィン流派の最高級一式と、猫流派の石弓。
武器にはゾリアのルーン大を全枠埋めて、凍結させながらガンガン押す。防具側はイグニの刻印大で、ファイアーー!燃やすか凍らすかどっちかにしろ感ある。

最終的な馬装備はゼリカニア一式で、ずっとグリフィン討伐の証をつけていた。凍結+流血+切断+炎上で、戦闘を楽にしていく気持ちの表れ。馬装備でいくと、早めの段階・・・、レベルにして15くらい?のときにゼリカニアの鞍袋を入手したので、重量にはほとんど困らなかった。

探索しまくって途中の商人や鍛冶屋で武器防具道具ほぼ全て売却していた。レリックの装備だけサブ用として持ち歩いてたが、資金が安定してきて修理道具が安定して持ち歩けるようになってからは不要だった。それでも一応倉庫に貯めたけど、飾る場所もないしなあ・・・。

道具や素材に関して、クリアしてからのことを話すと、商人や鍛冶屋、薬草医から購入することができ、集めることは不要だった。チェストなど、拾った素材に関して、あまりにも高いもの(宝石の類)やモンスターのドロップ素材以外は全売却でいいんじゃなかろーか。

 

クリア時のレベルは 35 でステ振りはこんな感じ。

  • 戦技
    • 小攻撃をフル強化
    • 反撃 3
    • 反射神経 3
    • 冷血 2 (使ってない)
    • 不屈の精神 5
    • 蘇生 5 (めっちゃ助けられた)
    • 精神集中 5 (蘇生とのコラボ狙い)
    • 防御力消失 5
    • 火流 3(使ってない)
    • 幻惑 3
    • 操り人形 3(装備したけどほぼ使ってない)
  • 錬金術
    • なし
  • 全般
    • 憤怒 1 (序盤だけ使用)

赤枠が3つ、青枠が1つで、印力+40% 攻撃力+120% になった。力の場もすべて回収した、はず。

 

戦闘に関しては、霊薬を飲まない+爆薬もほぼ使わない、圧倒的パワースタイル(でも装備は熊流派ではない)というスーパーゴリ押しスタイル。パワースタイルならもっと戦技を洗練して選び、装備も熊流派にするべきだった。

 

グウェントは途中から本気出してカード集めしたらもりもり勝てるようになった。北方領土デッキにヒーロー集め、おとりと医師で諜報員を並べ、気候と笛、ダンディリオンのバフて差をつける、みたいな戦術だった。途中でカードの収集具合によって、スケリッジ、ニルフガード、ワイルドハントも使った。

スケリッジは集合すると 5x3 くらいあつまって、一気に戦力差を作れる。そこに笛を吹けば優秀。天候が先に置かれていてもこちらが後出しなら近接と間接が切り替えられるので有利に事が運べる。

ニルフガード、引き分けて勝利できるので泥試合に強かった。意外と攻城 10 とか強カードがいる、諜報員多めってところで、火力と焦土作戦のコントロールが勝利のポイントだった。

ワイルドハントについては圧倒的集合力。カードが揃うまであまり強いと思わなかったが、吸血鬼、アラキス、妖婆が一気に揃うと 4x3 + 4x3 + 6x3 (取れていないカードもあるのでもうちょっと伸びるかも?)が並ぶ。さらに指導者で笛が吹けるので、一瞬にして 100 が見える。敵にやられるとクソゲー化も辞さない。近接ばかりなので冬将軍をされると厳しいが、数で押す。または意外と間接・攻城にも 6-8 が鎮座するので戦える。場にランダムな1体が残るのも非常に強力で、1-2ラウンドの出し方を調整すれば優位に事が運べる。

最後に北欧。刺青隊を3人並べるとそれだけで 12x3 という強力なユニット。ヒーローがいないと攻城がメイン火力になりがちだったので、天候操作のタイミングが結構重要だった。特にいうことないや。

 

まとめとして。

プレイ前は 60 時間くらいで終わるかなあ、と思っていたのが、とても良い意味で覆された。思っていた以上に豊富なサブクエスト、宝探し、討伐によって、終始飽きることのない冒険物語を楽しむことができた。

これは完全にロールプレイの問題なのかもしれないが、多様な見た目の多くの装備品があるのに、それを強制されず、中盤くらいからずっとウィッチャー装備・グリフィン流派一色で、ドロップ品がもったいなかった。もっと武器防具の耐久力の減りが高く、なんなら使い捨て生活でもよかったんじゃないかなあという気持ち。

システム上の面倒ポイントでいくと、圧倒的に小舟とオイル。特にスケリッジのゴミ収集の成果、小舟はもう乗りたくない。もうちょっと快適に収集できればなあ・・・。オイルもかなり面倒だった。敵ごとに分けて塗るのはとても面白いが、大体敵の種類を調べて、塗っていく。後半は幽鬼や飛竜はさほど問題ではなく、エレメンタルなどの精霊種が固くて面倒だったので、結果オイルは精霊ばっかり。

オイルについてはロールプレイの問題で、俺が、俺たちがウィッチャーだぞ!!!!って言ってプレイするべきポイントなんだろう。クエストでは自動的に目標として本を読めよ!とか、モンスターの情報を見ておけよって促されたり、足跡を見ていい感じに判断しているけど、本来のプレイする人がウィッチャーであればそれもプレイヤーが判断するべきだし、敵の種別も知っているべき。だからそれに応じてオイルも塗分けるべきなんだろうな。

エンディングがどうであれ、最初から最後まで、俺のウィッチャー像を貫き通すべき。完全にロールプレイして楽しむゲームだろうって思った。助けるなら助ける、倒すなら倒す。そこにとやかくといった話はなく、一貫した考え方や愛のありどころをもって、住民をはじめ、ゲラルトの仲間たち、各組織に属している人たちと会って、話をしていくべきなんだと思う。

僕の場合は、そこそこできていたが、それは中盤~後半にかけて。序盤から中盤はぶれてしまい、ゲラルトはいったいなんなんだ・・・?みたいな気持ちになっていた。どのタイミングかはわからないが、「人助けをなるべくする、無理ならしない、それによる別の被害が起きても気にしない、シリは過保護にせず彼女のやりたいように、気持ちを上げていく」というような気持ちで全体プレイをするようになってからはゲラルトが定まった。それによって女たらしみたいなゲラルトが出来上がったわけだが、それはまあ、それ。

そんなこんなでウィッチャー3はオープンワールドのロールプレイングゲームとして激推しする。その手のゲームが嫌でなければぜひ一度遊んでみたほうが良い Fallout やエルダースクロール、GTA、セインツロウあたりがハマるような人であればウィッチャー3もハマること間違いなし、サンプル数は1、僕がそうだったから。

ちなみにウィッチャー2など前作プレイ経験はまったくなかったがほとんど問題なかった。一部だけ、昔話が出てきて????となったが、ストーリー上大きな問題ではないだろう。前作プレイ経験があると、それ以上にもうちょっとだけ楽しめるのだと思う。

Windows でも ANSI エスケープした

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

Windows ネイティブに動く php.exe を使って phpunit を動かしていたわけですが、どうにも --color をした時にカラーコードが反映されない。

いやそもそも Windows やんけ、という話は置いておき。

カラーコードが出来ないことで何か問題あるかってーとこれまた特にあるわけでもなく。とはいえ、あたまのいい人たちが何か考えてるでしょと思いながら調べたら解決した。

そもそもとして色が出るのは ANSI エスケープシーケンスに割り当たってるところに色定義があるからっぽい。 ANSI カラーコードとか、 xterm16 とか、そう呼ばれているっぽい。
背景的なところはわからなんだ。

ANSIエスケープコード - コンソール制御 - 碧色工房
ターミナルのANSIカラーの分布 - Folioscope
ターミナル環境のTrue Colorとは?意味やMac、Vimなどの対応状況まとめ | Simplie Post

で、コマンドプロンプトでどうすんねん、についてはこれをつかうと出来る。

adoxa/ansicon: Process ANSI escape sequences for Windows console programs.

ANSICON provides ANSI escape sequences for Windows console programs. It provides much the same functionality as ANSI.SYS does for MS-DOS.

といった、説明のとおり Windows において ANSI エスケープシーケンスを実現するためのツール。
使い方はこんな感じ。

上に書いたリポジトリをダウンロード。適当な場所において、 32 ビット OS なら x86 を。 64 ビット OS なら x64 のフォルダをエクスプローラーで開く。そのフォルダのパスをコピーしておく。

コマンドプロンプトの起動。レジストリ登録をするそうなので管理者権限がよいようにみえるけど…。

フォルダへ移動

ansicon -i の実行

コマンドプロンプトを再起動する。これでカラーコードが使えるようになる。例えばこんな風に。

余談だけど cmder や Cygwin といったターミナルを使っていた場合は、内部でよろしくやっているので、特に何もしなくても出る。

cmder、タブが使えたり Linux のそれっぽく振る舞ってくれたりして便利ちゃんでおすすめちゃん。

cmder | Console Emulator

Cygwin のそれみたいなもっさり感…?はない、と思うけど、Cygwin も昔さわった時にもっさりしてた気がするだけで、今は改善されているのかな…?どちらにしても結局のところ Windows なのはかわりないので、そういうところ気がきかないのね~~~みたいのがたまにあって、そういうときに辛みを感じる。

Bitbucket Pipelines で MySQL コンテナを使う時に早すぎるとエラーになる

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

Bitbucket Pipelines を使ってて、全く設定を変えていないのに MySQL に繋がったり繋がらなくなったりしていたので、どういうこっちゃ、というメモ。

結論から行くと 3 〜 5 秒くらい待ってあげればよい。

MySQL に繋がらない

エラーとしてはこんな感じ。ちなみにこの問題と直接関係はないが CakePHP 3.5 である。

+ bin/cake migrations migrate
using migration paths 
 - /opt/atlassian/pipelines/agent/build/config/Migrations
using seed paths 
 - /opt/atlassian/pipelines/agent/build/config/Seeds
Exception: There was a problem connecting to the database: SQLSTATE[HY000] [2002] Connection refused in [/opt/atlassian/pipelines/agent/build/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php, line 115]
2017-10-16 10:57:41 Error: [InvalidArgumentException] There was a problem connecting to the database: SQLSTATE[HY000] [2002] Connection refused in /opt/atlassian/pipelines/agent/build/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php on line 115
Stack Trace:
#0 /opt/atlassian/pipelines/agent/build/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php(238): Phinx\Db\Adapter\MysqlAdapter->connect()
#1 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/CakeAdapter.php(57): Phinx\Db\Adapter\PdoAdapter->getConnection()
#2 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/Command/CommandTrait.php(78): Migrations\CakeAdapter->__construct(Object(Phinx\Db\Adapter\MysqlAdapter), Object(Cake\Database\Connection))
#3 /opt/atlassian/pipelines/agent/build/vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php(72): Migrations\Command\Migrate->bootstrap(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/Command/CommandTrait.php(35): Phinx\Console\Command\Migrate->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/Command/Migrate.php(65): Migrations\Command\Migrate->parentExecute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 /opt/atlassian/pipelines/agent/build/vendor/symfony/console/Command/Command.php(262): Migrations\Command\Migrate->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 /opt/atlassian/pipelines/agent/build/vendor/symfony/console/Application.php(888): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /opt/atlassian/pipelines/agent/build/vendor/symfony/console/Application.php(224): Symfony\Component\Console\Application->doRunCommand(Object(Migrations\Command\Migrate), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /opt/atlassian/pipelines/agent/build/vendor/symfony/console/Application.php(125): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/Shell/MigrationsShell.php(101): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /opt/atlassian/pipelines/agent/build/vendor/cakephp/cakephp/src/Console/Shell.php(508): Migrations\Shell\MigrationsShell->main('migrations', 'migrate')
#12 /opt/atlassian/pipelines/agent/build/vendor/cakephp/migrations/src/Shell/MigrationsShell.php(156): Cake\Console\Shell->runCommand(Array, true, Array)
#13 /opt/atlassian/pipelines/agent/build/vendor/cakephp/cakephp/src/Console/CommandRunner.php(141): Migrations\Shell\MigrationsShell->runCommand(Array, true)
#14 /opt/atlassian/pipelines/agent/build/bin/cake.php(12): Cake\Console\CommandRunner->run(Array)
#15 {main}
script:
	- composer install --no-interaction
	- chmod +x bin/cake
	- chmod -R 777 tmp/ logs/
	- bin/cake migrations migrate
	- composer test

bitbucket-pipelines.yml に記載した実行スクリプトに関してはこれだけなのだが、マイグレーションのところで、トピックブランチでは通って、マスターブランチでは通らず、なんてことがぼちぼち起きた。

Bitbucket Pipelines 上で MySQL のログが見えるので、これを確認すると、通らなかったときには、通ったときのログが途中できれているようなものだった。

Initializing database
...(中略)...

2017-10-16T01:57:41.831583Z 0 [Note] End of list of non-natively partitioned tables

成功した時はこんな感じの最終行になる。

2017-10-16T01:51:55.506134Z 0 [Note] InnoDB: Starting shutdown...
2017-10-16T01:51:55.606342Z 0 [Note] InnoDB: Dumping buffer pool(s) to /var/lib/mysql/ib_buffer_pool
2017-10-16T01:51:55.606688Z 0 [Note] InnoDB: Buffer pool(s) dump completed at 171016  1:51:55

つまり MySQL コンテナが何かしらの初期化処理をしていて、にも関わらず繋ごうとしたから接続できないエラー?そうして Bitbucket Pipelines がエラーを認識して停止。ふむ、納得出来る気がする。

アプリケーション側で接続時のタイムアウト設定

とりあえず思いつくスマートな方法はこれ。

このプロジェクトは CakePHP3 を使っているが CakePHP3 には接続時のタイムアウト設定な項目はないらしい。かなしい。とはいえ何かあるだろうとソースを読み進めると 最終的に PDO を使っているらしいことがわかる。ので PDO の設定方法を調べてみると PDO::ATTR_TIMEOUT というオプションを設定するとよいらしい。

php - Setting a connect timeout with PDO - Stack Overflow

このオプションを CakePHP3 で設定するには、コンフィグ内の flags にいれると出来そうなソースをしている。

cakephp/PDODriverTrait.php at master · cakephp/cakephp

$connection = new PDO(
	$dsn,
	$config['username'],
	$config['password'],
	$config['flags']
);

しかし入れても状況は改善されなかったので、違うらしい。。これはまたそのうち調べよう。。。

bitbucket-pipelines.yml に sleep を入れる

というわけでこっちの方法。yml (の抜粋)はこんな感じ。

script:
	- composer install --no-interaction
	- chmod +x bin/cake
	- chmod -R 777 tmp/ logs/
	- sleep 5
	- bin/cake migrations migrate
	- composer test

これで安定的に動いてくれているので、このままでいいか。

MySQL コンテナの初期化処理

そもそもとして MySQL コンテナの初期化処理って何をしているんだろうか。Dockerfile はここ。どうやら docker-entrypoint.sh が何かしていそうだ。

mysql/Dockerfile at master · docker-library/mysql
mysql/docker-entrypoint.sh at master · docker-library/mysql

すごいざっくり読むとこんなことをしているっぽい。

  • Docker イメージが提供するのは mysqld の準備~起動、 docker-entrypoint.sh の起動まで
  • docker-entrypoint.sh によって初回起動によるデータファイル構築。設定したデータベース、ユーザの準備。ちなみに途中で mysqld の再起動をしている。

なるほど理解。
やっぱりコンテナが立ち上がったと同時に初期化処理が走って、それが終わるまでは接続が出来ないようだ。

ものすごいバッドプラクティスを感じている sleep 5 なのでなんとかしたいものの、どうこうするのがいいのかイマイチわからないので、知見あるひと教えてほしス

WordPress にプラグインを使わずに JavaScript だけで目次機能をつけてみた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

目次機能が出来るプラグインとかってあるけど、別に自分で作ってもいいよねって思ったので作った。そのうちモリモリっとテーマやプラグインの整備に力を入れていくので、そのあたりに介入されると困るので~~ってくらいの理由で作った次第。

これこれ!この目次! h2 タグを使うんだよ

2階層までなら対応するよ! h3 タグだよ

ここは h3 タグ

ここは h2 タグなので 1 階層目になる

ここは 2 階層。 h3 タグだからね

デモおわり

ここまで見出しだらけで見にくいのは仕方ない。

仕組み

  1. h2 タグと h3 タグを投稿箇所から探す
  2. それぞれのタグに ID をつける
  3. それぞれへのリンクを生成する
  4. 目次用の HTML を作る
  5. 一番最初の見出しの直前に HTML を挿入する

タイトルこそ WordPress って言ってるけど JavaScript が差し込めるブログサービス的なものなら、投稿箇所を探す部分を調整する必要はあるけど、どれでもできるんじゃないかな。
jQuery を使わなくていいように作ったので jQuery ないんだけど、みたいな環境でもいいと思う。いやそもそもそういう環境はまた違う問題っぽいけど…。

JavaScript

(function() {
	setTimeout(function() {
		var headingIndex = 1;
		Array.prototype.forEach.call(document.querySelectorAll('.post_content'), function(postContent) {
			if (postContent.innerText.length < 100) {
				return;
			}

			var headings = postContent.querySelectorAll('h2,h3');
			if (headings.length <= 0) {
				return;
			}

			var indexHtml = '
    '; var lastElement = 'H2'; Array.prototype.forEach.call(headings, function(e) { var id = 'heading-' + headingIndex; e.id = id; if (lastElement != e.tagName) { if (lastElement == 'H2') { indexHtml += '
      '; } else { indexHtml += '
    '; } lastElement = e.tagName; } indexHtml += '
  • ' + e.innerHTML + '
  • '; headingIndex++; }); indexHtml += '
'; var wrapper = document.createElement('div'); wrapper.classList.add('content-index'); wrapper.innerHTML = indexHtml; headings[0].parentNode.insertBefore(wrapper, headings[0]); }); if (location.hash.length > 1) { setTimeout(function() { var element = document.querySelector(location.hash); if (element != null) { window.scroll(0, element.offsetTop); } }, 10); } }, 10); })();

CSS

.post_content .content-index {
	border: 2px solid #eee;
	border-radius: 20px;
	padding: 20px;
	background-color: #fefefe;
}

.post_content .content-index:before {
	content: '目次';
	display: block;
	font-size: 24.5px;
	margin-bottom: 15px;
}

他のスタイルとのバッティング回避した部分を除くとこれだけ。シンプルに線を引くことと色味の調整くらい。

SEO的な観点とか

目次足すと SEO に効果あるのかわからんけど、ふつうに見ていて、長めの投稿だったら合ったほうが便利だよね、とは感じる。

一応 Fetch as Google をして Google Bot にこの目次が認識されていることは確認したので、まあいいんじゃないかな。

composer create-project は何が起こるのか

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

CakePHP 3 のクイックスタートを改めて見てたら composer create-project ... ってでてきて何だこいつは…ってなった話。

Quick Start Guide - 3.x

php composer.phar create-project --prefer-dist cakephp/app bookmarker

composer create-project がやること

要はドキュメントにあるので読めって話になる。

Command-line interface / Commands - Composer

You can use Composer to create new projects from an existing package. This is the equivalent of doing a git clone/svn checkout followed by a "composer install" of the vendors.

There are several applications for this:

1. You can deploy application packages.
2. You can check out any package and start developing on patches for example.
3. Projects with multiple developers can use this feature to bootstrap the initial application for development.

To create a new project using Composer you can use the "create-project" command. Pass it a package name, and the directory to create the project in. You can also provide a version as third argument, otherwise the latest version is used.

ざっくりと翻訳してみると、こんなことが書かれているようだ。

既にあるパッケージから新しいプロジェクトを作ります。それは git clone または svn checkout をして composer install を行うのと同等です。

1. アプリケーションパッケージをデプロイできる
2. パッケージをチェックアウトして開発をしていける
3. 新しい開発のため、ブートストラップな初期化を提供ができる

"create-project" コマンドによってそれは実行できる。パッケージ名、プロジェクトのディレクトリ名、そしてパッケージバージョンを指定できる。

CakePHP のドキュメントにあったコマンドを読み解く

php composer.phar create-project --prefer-dist cakephp/app bookmarker

まず指定されているパッケージを見てみる。

cakephp/app - Packagist

A skeleton for creating applications with CakePHP 3.x.

なるほど。つまり、空っぽの CakePHP アプリケーションがパッケージ登録されているので、それをもってくることで新規に空っぽの CakePHP アプリケーション作れるやん!ということだそうだ。

ところで --prefer-dist はなんだろう。 composer のドキュメントをもう一度見る。

--prefer-dist: Install packages from dist when available.

んーむ。 dist があればインストールするよって言ってるけどなんのことやら…? → install の項目にガッツリと書いてある。

--prefer-dist: Reverse of --prefer-source, Composer will install from dist if possible. This can speed up installs substantially on build servers and other use cases where you typically do not run updates of the vendors. It is also a way to circumvent problems with git if you do not have a proper setup.

--prefer-source の逆で、 dist からのインストールを優先する。これによってビルドサーバやアップデートをしないような環境では高速に動作する。あるいは git の設定をしてないときに問題を解決出来る。

--prefer-source の説明も合わせて読んだところ、どうやら composer は dist って呼ばれるところを基本的には利用するらしい。 source はバージョン管理リポジトリで、直接 git clone するようなものになっているので、設定をしてないと問題が起きたりするそう。 Github を直接見に行ってエラー、とかそういう感じなのかな?まあ、デフォルトでは dist なので --prefer-dist はあまり気にしなくても良さそう。

で。

3 つめの引数に書いてある bookmarker が作られるディレクトリだ。
カレントディレクトリに bookmarker というディレクトリが作られ、その中に cakephp/app パッケージの内容が展開されているはず。加えて composer install も行われているので bookmarker/vendor ディレクトリも作られている。

まとめ

要は git clone しまっせ、みたいなものだと思っておけばだいたい良さそうだ。

Dockerfile を Github で書いて Docker Hub へ自動で連携する

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

出来ると思うんだけど~~って言いながら、なかなかやってなかったのでいい加減やった。

Docker Hub にアカウント登録

Docker Hub

右上からサインアップ出来る。適当な ID とメールアドレス、パスワードを入力すると良い。メールで確認がやってくるので、リンクぽちー。

以上で終わり。

Github でリポジトリを作る

Github にリポジトリを作っていきます。

Github 上からファイルの追加が出来るので、手元の Dockerfile をアップする。当然ながらパブリックなリポジトリで扱うのであれば、社外秘です!みたいな内容を含まないように気をつけて。パスワードとか、脆弱性ある古いバージョン使ってるじゃんとか、鍵が載ってるよとか、うっかり事故気をつけようね。

というわけで出来ました。

sters/docker-php7-cakephp3-base: docker base image for php7 x cakephp3

Docker Hub で Automated Build の設定をする

Docker Hub 側で自動ビルドが出来ます(便利!)URL が提供されるので、正しいトークンを送るとビルドキューへ追加され、自動でビルド、イメージの提供がされ始めます。
というわけで連携を進めていきます。

この URL および トークンを Github の Webhook に追加します。

ちなみに Docker Hub に Webhook もありますが、これはビルドが終わったときにそのイベントを指定した URL に通知できるものです。でっかいイメージを扱いたい時とか、ビルドしたイメージがちゃんと使えるかテストしたいとか、そういうときに使えるんじゃないですかね。

Github で Webhook の設定

Docker Hub で取得できたトリガーの URL を Github の Webhook に設定します。

とりあえずは Push された時、でいいと思います。タグが付いたとき、にすると Docker Hub 側でタグごとのビルドがされていきます。バージョニングしていく上ではそうするほうがいいでしょうね~。

Docker Hub で確認

ビルドが動き始めました。しばらく待つと完了します。

何事もなければこれでイメージが利用できるようになります。

 $ docker run -it --rm sters/php7-cakephp3-base "/bin/sh"

// ここからゲスト側

/ # composer
Do not run Composer as root/super user! See https://getcomposer.org/root for details
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.5.2 2017-09-11 16:59:25

Usage:
  command [options] [arguments]

Options:
  -h, --help                     Display this help message
  -q, --quiet                    Do not output any message
  -V, --version                  Display this application version
      --ansi                     Force ANSI output
      --no-ansi                  Disable ANSI output
  -n, --no-interaction           Do not ask any interactive question
      --profile                  Display timing and memory usage information
      --no-plugins               Whether to disable plugins.
  -d, --working-dir=WORKING-DIR  If specified, use the given directory as working directory.
  -v|vv|vvv, --verbose           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  about           Shows the short information about Composer.
  archive         Creates an archive of this composer package.
  browse          Opens the package's repository URL or homepage in your browser.
  clear-cache     Clears composer's internal package cache.
  clearcache      Clears composer's internal package cache.
  config          Sets config options.
  create-project  Creates new project from a package into given directory.
  depends         Shows which packages cause the given package to be installed.
  diagnose        Diagnoses the system to identify common errors.
  dump-autoload   Dumps the autoloader.
  dumpautoload    Dumps the autoloader.
  exec            Executes a vendored binary/script.
  global          Allows running commands in the global composer dir ($COMPOSER_HOME).
  help            Displays help for a command
  home            Opens the package's repository URL or homepage in your browser.
  info            Shows information about packages.
  init            Creates a basic composer.json file in current directory.
  install         Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
  licenses        Shows information about licenses of dependencies.
  list            Lists commands
  outdated        Shows a list of installed packages that have updates available, including their latest version.
  prohibits       Shows which packages prevent the given package from being installed.
  remove          Removes a package from the require or require-dev.
  require         Adds required packages to your composer.json and installs them.
  run-script      Runs the scripts defined in composer.json.
  search          Searches for packages.
  self-update     Updates composer.phar to the latest version.
  selfupdate      Updates composer.phar to the latest version.
  show            Shows information about packages.
  status          Shows a list of locally modified packages.
  suggests        Shows package suggestions.
  update          Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
  upgrade         Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
  validate        Validates a composer.json and composer.lock.
  why             Shows which packages cause the given package to be installed.
  why-not         Shows which packages prevent the given package from being installed.

/ # php -v
PHP 7.1.10 (cli) (built: Sep 30 2017 01:01:44) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

/ # exit

おわり

基本的に Official なイメージを使うとおおよそ大丈夫だと思うけど、再ビルド必要です!とか拡張機能必要です!とか、そういう状況になったら都度やるのもアレなので、オレオレイメージを作ると便利ちゃん。

そういえば Docker Hub って Dcokerfile によって記述された Docker イメージを管理共有してくれているけど、 docker-compose.yml で記述されるコンテナ管理セットを共有する場ってあるのかな。ちょっと調べたけどクエリが悪いのか見つからず…。

Office365 の Excel で Ctrl + H を押しても「検索」開く(解決済み)

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

会社の PC で Office365 の Excel を使っているがここ最近で、置換のショートカットキーが効かなくなったような気がしている。

Ctrl + H を押しても置換ではなく、検索が開くのだ。まあ、置換タブを押せばいいだけなのだが。

で気になって調べた。

もしやショートカットキーが変わった?ということもあるので、公式のガイドを見る。

Windows 用 Excel キーボード ショートカットとファンクション キー - Excel

CTRL + H
[検索と置換] ダイアログ ボックスの [置換] タブを表示します。

間違いなく Ctrl + H だ。
特にアプリ追加したなどないし、再起動しても変わらない。はてさて。

日本語の情報が全く見つからなかったが、英語で同じような状況を検索すると同じ状況の困った!がヒット。

Excel Shortcut Keys ctrl+f and ctrl+h now gives the same function? - Microsoft Tech Community

I just want to know why would microsoft change the function of excel 2016 in pressing ctrl+h (Find and replace) and have it the same as pressing ctrl+f (Find fucntion)?? The recent update makes the shortcut keys the same. I mean it is really useful when you can a quickly press keys to find and replace. Instead they made it the same as find function only which really is really annoying and time consuming for excel advanced users.

公式コミュニティだ。そして全く同じ問題が起きている。
雑に訳すと「Excel 2016 を使ってるんだけど Ctrl + H の動きが Ctrl + F と一緒じゃね?」って言ってる。

ついている回答を挙げてみるとこんな感じ。

I updated couple of minutes ago on 1709.8528.2084, Ctrl+H works now in that build.
And I'm on Inseders Slow (now Monthly Channel if i remember correctly)

これまた雑に訳すと「アップデートしたら動くようになったよ!Insider の Slow チャンネルでは直っているからそのうちになおるかも?」みたいなことを言っている。ぼくは大きなダメージもないのでまったり待ち。

 

 

と思ったけど 10/06 の朝に更新をしてみたら治った。
更新は ファイル → アカウント を押して「 Office 更新プログラム」かアップデート確認・実行ができる。

ぼくの場合はこのタイミングで 1708.8431.2094 というビルド番号になり Ctrl + H が直ってた。

CSS 設計の教科書 を読み終わった

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

「 CSS 設計の強化を読みました」って言ってたけどめっちゃ途中で止まっていたので完走しました。

前の記事
CSS設計の教科書を読んだ@途中まで | ごみばこいん Blog

読んだ本
Web制作者のためのCSS設計の教科書 モダンWeb開発に欠かせない「修正しやすいCSS」の設計手法 | 谷 拓樹 |本 | 通販 | Amazon

コンポーネント設計の実践

コンポーネントをどのように作るか。

コンポーネントを作るタイミングの問題。始めからあらゆる事態を想定してコンポーネントを作っていくことは困難であり、そんなところにパワーを使いすぎてはいけない。早すぎる最適化は悪の根源。 "YAGNI" (You ain't gonna need it.) = いらないものをつくらない。

"Rule of Three" はそれを解決するための 1 つの案。たとえば「3回繰り返すものがでてきたらコンポーネント化する」あるいは「別の3プロジェクトでも使うならコンポーネント化する」といった意識を持つのがよい。

粒度感の問題もある。ここについては、プログラミングにおける原理原則がCSSにおいても適用できることに着目し、「単一責任の原則」「開放・閉鎖の原則」などを意識するとよい。そもそも OOCSS ではオブジェクト指向プログラミングの考え方を CSS に取り入れた。

この章にはこの後、本の中では「コンポーネントの設計・実装パターン」としてボタンや見出しなど、筆者の経験上こうすることが多い、が書かれている。実際に1から作るとき、これを試してみるとよさげたん。

CSS プリプロセッサを用いた設計と管理

SMACSS や MCSS にはカテゴリやレイヤーといったルールが設けられていることを既に学んだ。これらを CSS の書き方の上でキレイに整えて書いていくことはなかなか大変。
そこで Sass を使うのがよい。Sass によってルールの構造化、使い回しなどが容易にできる。

Sass: Syntactically Awesome Style Sheets

コンポーネントの運用に必要なツール

コンポーネントを作る上で・・・。そもそも CSS を書く上でコメントを書くことは非常に大事。どんなところに提供されるルールなのか、なぜそういう書き方をしているのか、どのようなコンポーネント単位なのか。

CSS 設計をしたり、何かしらのルールを設けたのであれば、そのルールを文章化していくことが必要。全体の設計のやり方、細かいルール、ガイドラインといったことを記載するのがよい。なにせその CSS は他の人が受け継いでいくのだから。

どのようなコンポーネントがあるか、どんなクラスをつけるとどんな見た目になるか、といったスタイルガイド、パターンライブラリがあると、開発時に非常に便利になる。ある一定の決まりにそってコメントを記述すると自動で生成できるツールもあるので、活用するべし。
紹介されていたのはこれ。

KSS · Knyle Style Sheets
StyleDocco
thomasdavis/kaleistyleguide web サイトが終了しているようです。。

そのほか CSS 開発の効率を上げるために次のようなツールが紹介されていた。

  • csslint // 正しく書かれているか
  • stylestats // CSSの指標について数値化
  • autoprefixer // -webkit- などの自動付与
  • csscomb // 記述の並び替え、フォーマット
  • csso // 構造の最適化
  • Grunt, gulp // タスクランナー

Web Components の可能性

ここまで CSSの設計、ルール化によって壊れにくい、戦いやすいCSSが出来るようになった。しかし、それでも壊れる可能性は高い。それをこれからの未来に解決するための案が W3C で進められている Web Components という仕様だ。

Web Components は 4 つの技術から成り立つ。

  • Templates
    • マークアップと CSS を内包できる
  • Custom Elements
    • 独自の要素を定義できる
  • Shadow DOM
    • 隠蔽された DOM を作成できる
    • Templates と Custom Elements を組み合わせることで影響を受けない出さないコンポーネントを作成できる
  • HTML Imports
    • 外部の HTML ファイルを読み込める

まだ仕様策定中であり、利用できるブラウザも多くはないが Polymer というポリフィルがあるので、これを使うことでどのブラウザでも試すことは可能。これからのコンポーネントがやってくる未来に備え、 Web Components に触れておくとよいだろう。

読み終わってみて。

2014 年に初版が出たのもあって、 2017 年のフロントエンド界隈の話と比較するといくつか話が古そうなところもあります。

たとえばコンポーネントについては React を筆頭に Vue や Angular といった仮装 DOM を使ってコンポーネントを実現していくライブラリが活躍しています。 Web Components の実装も進み、 Chrome や Safari ではおよそ利用することが可能に、 Firefox でも実装が進んでいるようです。

React - A JavaScript library for building user interfaces
Vue.js
Angular

Web Components の実装状況
Can I use... Support tables for HTML5, CSS3, etc

CSS についても cssnext と呼ばれる、未来の CSS 仕様に向けた先行実装が今の流行り…?なのかなあ。
Sass で使っていたようなネストや変数、 Mix-in といったものは利用できますし、実行環境が cssnext というツールではなく PostCSS を利用するところに人気があるのかもしれません。

cssnext - Use tomorrow’s CSS syntax, today.
PostCSS - a tool for transforming CSS with JavaScript

PostCSS は Sass などが行っていたようなプリプロセッサというよりは babel のような、 Pluggable な CSS を処理する君で、プラグインを足して独自記法やブラウザが未実装な機能を書けるようにするものです。

このプラグインの一覧サイトがあるのですが、眺めているだけでも非常に様々なものがあります。
きれいなグラデーションを自動生成したり、 @component なんてすると BEM の命名を自動でつけてくれたり、画像や SVG を CSS のインラインに展開してくれたり…。以前までは、それぞれ別のプラグインとして作られ、 Grunt や gulp を使ってバケツリレー変換をしていたものが、 PostCSS というプラットフォームに乗っかることで、 CSS に対する前処理を容易にできるようになったようです。

PostCSS.parts | A searchable catalog of PostCSS plugins

ちなみに Sass っぽく使えるよ!というものもあるので、 Sass がどうしても!の場合にはこういう選択もありだと思います。

jonathantneal/precss: Use Sass-like markup in your CSS

というわけで、現代の先端?流行?と比べると、本で言われている技術ポイントと乖離がありそうですが、ぼくがこの本を読んでみたかった理由はそこではなく。CSS 設計のやり方、考え方、大切さ、コンポーネント分け方のイメージをつけることが目的でした。あとはそもそも OOCSS って? SMACSS って?みたいな状態だったので、これを解消したかったというところですね。

ここについては十分に解消され、実践できる武器になっているかわかりませんが、イメージはなんとなくついたので、あとは実践するのみになりましたとさ。おわり。