叫ぶうさぎの悪ふざけ

うさぎが目印のWebエンジニアが、得たことや思ったことを言の葉に乗せて叫ぶ場所です。

【技術書典6】誰かに背中を押された話【執筆に向けて】

これは 技術同人誌 その2 Advent Calendar 2018 Advent Calendar 2018 の12月8日のエントリーです。

技術書典、参加するだけでも最高に楽しくて刺激的で、持ち帰るものも膨大なら、成長も膨大。

個人的に 技術書典は成長の種を得るところ って思っています。

で、参加するうちに 書いてみたい と思うようになったんですよね。ポエムでもいいし、僕が活動理念にしている 初学者に寄り添い、背中を押す ことができる場所だなって、いろんな人の書籍を読むたびに思ってたんです。

そんな折、僕が主催に属している エンジニアの登壇を応援する会Slack で共同執筆の話が持ち上がりました。 これはやるしかない!と思い、編集長の ariaki さんと話しながら、ざっくりまとめた内容を綴ってみたいと思います。

というわけで、3つのテーマのうちの最初 誰かに背中を押された話 をします。

はじめに

人は誰もが背中を押されて生きている と言えるかもしれません。 しかし押されるタイミングが合わなければ、羽ばたけない人もいる。 僕もそんな1人でした。

エンジニアとして、何をするにもとっかかりが掴めない。 ずっと暗がりでもがいている。 諦めた方がいいんだろうか…

そんな漠然とした悩みを打ち明けた人がいました。

そこで本章は、僕が人生で初めて ガッツリ背中を押されるまで を解説していきます。

背中を押されるまで

不惑をとうに越え、人生そのものに焦りを迎えていたある日、同僚の何気ない言葉 から僕の背中は押され続けてきました。

きっかけ

43歳になっていました。何もかもが焦ります。 これだけは誰にも負けないと自信を持てるものが何もない。

そんな折、同僚のチーフディレクターとランチに行った時のことです。

  • D:「最近なんか悩みある?」
  • M:「自分の技術、どこに特化させるか未だに答えが出ません…」
  • D:「興味のある分野とかないの?」
  • M:「この先20年役立つ、自分がやりたいの両方で、インフラですね」
  • D:「なんか気になる書籍とかないの?」
  • M:「あり過ぎて積みまくってます」
  • D:「じゃあそれをお互いに毎日読んでLTし合おう。ちょうど俺も読みたいのあるし」

こうして、僕の毎日のインプット生活がスタートしたのでした。

やりかた

時間、互いの労力、計画性、全てに一定以上のものが求められました。では具体的にどうやったのか解説します。 個人的には ここまで1対1で他人が時間を割いてくれるものなのか と驚愕と歓喜が同居した複雑な気分と、モチベーションが常に補充される不思議な体験でもありました。

前準備

  1. 課題図書を決める
  2. それぞれが1日30分(目安)読む
  3. Markdownで得たこと、調べたことを記録する

LT

  1. 互いに10分間、記録したことを相手にプレゼンする
  2. 理解が及ばないところは互いに質疑応答する
  3. 10分経過で強制終了
  4. 続きは次回

ゼロ秒思考の実施

  1. 大きめ(できればA4)の紙を用意
  2. 書きやすいサインペンを用意
  3. 最初の1分でお題を決める
  4. 残りの時間、ゼロ秒で思考したことをとにかく書き出す
  5. 終わったらトピックを互いに話す(3分程度)

実施日

  1. 月曜日〜木曜日の18:30-19:00
  2. 金曜日はMTGが多いのでお休み
  3. 読書などのインプットは毎日続ける
  4. 書籍以外でも、技術メモ、他人のブログについてなどテーマは無差別

毎月の振り返り

  1. KPTを実施
  2. 翌月にすべきことを取捨選択
  3. より効果的な時間を作り上げていく
  4. 業務の改善ポイントも振り返る(ゼロ秒思考の振り返り)

結果

当エントリー執筆段階(12月8日)で、9ヶ月目に入っています。それまでどんな理由があっても1日も欠かしていません。 今はその会社を退職していますので、お互いのLTは実施していませんが、以下の結果が得られました。

  • 継続的なインプット・アウトプットが習慣化
  • 非エンジニアに説明して理解を得られる状態に落とし込む
    • エンジニア以外でも理解できるよう言葉の解説や正確性を重視した
  • 学習時間の確保のための生活時間の見直し
    • 寝る前のどの時間にやるのか決めた結果、1日のサイクルが変わった
  • 1つの技術の深掘りによる理解
    • 解説することによるさらなる自分へのインプット
  • わかる!楽しい!
    • 学習が楽しくなり、現在でも仕事が楽しい瞬間が増えた
  • 解説したい!話したい!
    • 特に自分のような悩みを抱えているかもしれない人の背中を押したい

出会いは必ず来る

こういう人には一生出会えないと思っていましたし、想像もしていませんでした。 全て自分で理解して進む必要があるんだ…そう思い込んでいたんですよね。 自分がいい歳だっていうのも拍車をかけたと思います。

しかし、そんな思いを杞憂にして吹き飛ばしてくれた同僚がいました。 そして背中を押してもらいました。

彼とは今でもたまに話し、これからも悩めば相談するでしょう。

  • M:「俺、この恩をどうやって返せばいいかわからんっす」
  • D:「それはお互い様でしょう。おかげで自分もDNSを理解したしw」
  • M:「俺も組織論やリーダーシップについて目から鱗なところたくさんあったな」
  • D:「重要なのは継続すること。それが達成できたならそれでいいんですよ」

こんなこと話したな。なんやねん最高かよ。って思ったし、この恩は自分のエンジニアの師匠の言葉 受けた恩は後続に送っていけ。それが未来だ という 恩送りの精神 で誰かに送り続けていこうと思います。

こうして、僕はインプット・アウトプットの輪廻の中に身を置き、今でも悩みながらぐるぐるぐるぐる回っていたりします。

押されたからには…押していく!

そう、自分も押さなくては、という気持ちになるわけです。 同じように困っている人が絶対にいるはずだ。

MySQLDNSで登壇した際、自分が困ったこと、わからなかったこと、技術書やブログ、RFCで理解できなかったことを調べ、その行間に含まれている暗黙知見える化してきました。

最初は 高度な話でもないのに必要とされるかな? という不安がありました。 しかしインプット・アウトプットを続け、登壇し、様々な人から感想をいただいて、不安が確信に変わりました。

人はわからないと不安になります。不安が続けば嫌になります。嫌になれば諦めて辞めちゃいます。

その不安を取り除こう。自分にできることでやっていこう。それをエンジニア人生の目標にしよう。

そう、思い定めたのでした。

もう少し続くんじゃよ

今現在、エンジニアとしても幸せだなって思える瞬間がすごく増えてきました。 MySQLDNS、だけでなく、OOPドメイン設計もわかるようになってきて、実際のコードを書くのがより楽しくなってます。 (OOPは「ぺちオブ」という勉強会があり、僕より遥かに濃密で深く強い意志で活動されている素晴らしい方々がいらっしゃいます。)

もちろんインフラを操作する時も前よりずっと安心感がありますし、少なくとも、しんどいな、手に汗握るな、というネガティブな瞬間が明らかに減りました。

稀有な人に背中を押されたな。彼には 今後も友達でよろしくね と言ってもらえて、こんなナイスガイがおるんやな、って思ってますが、じゃあ俺もナイスガイになってやろう。

そして誰かの背中を押していこう。 そう思っていた矢先、主催のariakiさんに誘っていただき、エンジニアの登壇を応援する会に入ることになったのでした。

というわけで、 誰かの背中を押していく という話に続け、執筆します。

やっていくぞ!

学習記録:12月7日(金):Webフレームワークに依存しない、PHP製のシンプルなSQL マイグレーションツール「Mig」を使ってみた

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の7日目のエントリーです。

こちらのブログ、Webフレームワークに依存しない、PHP製のシンプルなSQL マイグレーションツール「Mig」を作った。 を拝見し、宣言通り使ってみました。

元ブログでやってなかったこと

使い方は製作者さまのブログの通りで、それをなぞっただけなので、特に新しいことはしていませんが、あえてブログにないことをしたと言えば、以下のポイントかなと思います。

  • インストールまでの道筋を全部書いた
    • 趣味で行間を埋めただけですが…
  • 個人の趣味で作ったテーブルの根拠を書いた
    • COLLATE=utf8mb4_bin とか
    • 金額カラムには DECIMAL 使うといいよと公式で言ってたとか
  • データベース接続設定ファイルの記述にデータベース名を入れ忘れた
    • 個人的にミスったのでメモした
  • CREATE TABLE, DROP TABLEのスペルミスをしていた
    • 注意書き程度にコンテンツに追加
    • 12/07追記:製作者さんにブログを修正していただきました

前提

もろもろちょっと古いですが、MySQLがサクッと試せる環境が今んところこれしかなかったのでご容赦を。

あと、vagrant使ってますので、コマンドは全て vagrant ssh した後になります。

$ cat /etc/system-release
CentOS Linux release 7.3.1611 (Core) 
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.19, for Linux (x86_64) using  EditLine wrapper
$ php -v
PHP 7.1.8 (cli) (built: Aug  2 2017 12:13:05) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

インストール

単純にMySQL5.7の検証用に作ったものだったんでcomposerから。

composer

$ curl -sS https://getcomposer.org/installer | php
All settings correct for using Composer
Downloading...

Composer (version 1.8.0) successfully installed to: /home/vagrant/composer.phar
Use it: php composer.phar
$ sudo mv composer.phar /usr/local/bin/composer
$ composer
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.8.0 2018-12-03 10:31:16
(以下略)

Mig

そして本体をインストール。ここからはブログにもある通りです。

$ composer global require arakaki-yuji/mig
Changed current directory to /home/vagrant/.config/composer
Using version ^0.3.3 for arakaki-yuji/mig
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 4 installs, 0 updates, 0 removals
  - Installing symfony/polyfill-mbstring (v1.10.0): Downloading (100%)         
  - Installing symfony/contracts (v1.0.2): Downloading (100%)         
  - Installing symfony/console (v4.2.0): Downloading (100%)         
  - Installing arakaki-yuji/mig (0.3.3): Downloading (100%)         
symfony/contracts suggests installing psr/cache (When using the Cache contracts)
symfony/contracts suggests installing psr/container (When using the Service contracts)
symfony/contracts suggests installing symfony/cache-contracts-implementation
symfony/contracts suggests installing symfony/service-contracts-implementation
symfony/contracts suggests installing symfony/translation-contracts-implementation
symfony/console suggests installing psr/log-implementation (For using the console logger)
symfony/console suggests installing symfony/event-dispatcher
symfony/console suggests installing symfony/lock
symfony/console suggests installing symfony/process
Writing lock file
Generating autoload files

パスを通す

みなさんの環境に合わせてくださいね。 適当に作ってしまった環境なので僕は以下のパスを通しました。

$ export PATH=$PATH:$HOME/.config/composer/vendor/bin

設定ファイル作成

ここは作者さまの通りに。

$ pwd
/home/vagrant
$ vim mig.config.php 

設定ファイルの中身はこんな感じ。 注意点としては、 db_dsn に設定する内容は db_name まで設定しないとPDOでコケます。

詳しく言うと、接続先のデータベース名を指定しないと invalid data source name になりますので、以下のようにhostとdb_nameを設定してください。

<?php
return [
    'db_dsn' => 'mysql:host=localhost;dbname=mamy1326',
    'db_username' => 'root',
    'db_passwd' => 'mSbXeWZyGz4R',
    'migration_filepath' => 'migrations',  // migrationファイルを保存するディレクトリ名
];

次にmigrationファイルを保存するディレクトリを作っておきます。

$ mkdir migrations
$ ls -la migrations
合計 12
drwxrwxr-x  2 vagrant vagrant   88 127 02:18 .
drwx------. 9 vagrant vagrant 4096 127 02:18 ..

これで準備は完了です。

初期化を実行

コマンドを実行し、migration用のテーブルができていることを確認します。

$ mig-cli init
Initialize for manage migration.
=================

Already created migrations table.

次に実際のテーブルを確認します。

$ mysql -u root -p mamy1326
Enter password: 
(中略)
mysql> show tables where Tables_in_mamy1326='migrations';
+--------------------+
| Tables_in_mamy1326 |
+--------------------+
| migrations         |
+--------------------+
1 row in set (0.00 sec)

無事、migrationテーブルが作成されていることがわかります。 一応、create tableも確認します。 中身も空っぽですね。

mysql> show create table migrations\G
*************************** 1. row ***************************
       Table: migrations
Create Table: CREATE TABLE `migrations` (
  `id` bigint(20) NOT NULL,
  `applied_at` int(11) DEFAULT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

mysql> select * from migrations;
Empty set (0.00 sec)

migrationファイル作成

実際のテーブル名をつけて、migrationファイル作成コマンドを実行します。 今回はmigrationテーブルで差分を確認したいので、2つテーブルを作成します。

# migrationファイル作成(1テーブル目)
$ mig-cli create items
Create a new migration file.
=================

create migrations/20181206174917_items.up.sql
create migrations/20181206174917_items.down.sql

# migrationファイル作成(2テーブル目)
$ mig-cli create item_details
Create a new migration file.
=================

create migrations/20181206180443_item_details.up.sql
create migrations/20181206180443_item_details.down.sql

# 作成を確認
$ ls -la migrations
合計 6
drwxrwxr-x  2 vagrant vagrant 4096 127 02:49 .
drwx------. 9 vagrant vagrant 4096 127 02:18 ..
-rw-rw-r--  1 vagrant vagrant    0 127 02:49 20181206174917_items.down.sql
-rw-rw-r--  1 vagrant vagrant    0 127 02:49 20181206174917_items.up.sql
-rw-rw-r--  1 vagrant vagrant    0 127 03:15 20181206180443_item_details.down.sql
-rw-rw-r--  1 vagrant vagrant    0 127 03:15 20181206180443_item_details.up.sql

SQLをファイルに書き込む

CREATE TABLE, DROP TABLEをそれぞれファイルに書き込みます。 ブログの内容ですと、 IF EXIST になっていますが、正しくは IF EXISTS です。

12/07追記:製作者さんにブログを修正していただきました!

テーブル作成

itemsテーブル

$ vim migrations/20181206174917_items.up.sql

若干僕の趣味で、PKは bigint(20) unsigned だったり、絵文字を識別したいので COLLATE=utf8mb4_bin だったりします。

CREATE TABLE IF NOT EXISTS `items` (
  `item_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `item_name` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

item_detailsテーブル

$ vim migrations/20181206180443_item_details.up.sql

こちら金額が入ってるだけですが、金額を扱う場合は DECIMAL または NUMERIC を推奨、とMySQL公式でも記述があります。

CREATE TABLE IF NOT EXISTS `item_details` (
  `item_detail_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `price` decimal(10,3) NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`item_detail_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

テーブル削除

itemsテーブル

$ vim migrations/20181206174917_items.down.sql
DROP TABLE IF EXISTS `items`;

item_detailsテーブル

$ vim migrations/20181206180443_item_details.down.sql
DROP TABLE IF EXISTS `item_details`;

migration実行

実際に実行し、テーブルを作成します。

テーブルが存在しないことを確認

$ mysql -u root -p mamy1326
Enter password: 
(中略)
mysql> show tables where Tables_in_mamy1326 like 'item%';
Empty set (0.00 sec)

migration実行

$ mig-cli migrate
Start migration.
=================

Migrate migrations/20181206174917_items.up.sql
Migrate migrations/20181206180443_item_details.up.sql

テーブル作成を確認

$ mysql -u root -p mamy1326
Enter password: 
(中略)

# テーブル作成確認
mysql> show tables where Tables_in_mamy1326 like 'item%';
+--------------------+
| Tables_in_mamy1326 |
+--------------------+
| item_details       |
| items              |
+--------------------+
2 rows in set (0.00 sec)

# テーブル定義確認
mysql> show create table items\G
*************************** 1. row ***************************
       Table: items
Create Table: CREATE TABLE `items` (
  `item_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `item_name` varchar(255) COLLATE utf8mb4_bin NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)

mysql> show create table item_details\G
*************************** 1. row ***************************
       Table: item_details
Create Table: CREATE TABLE `item_details` (
  `item_detail_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `price` decimal(10,3) NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`item_detail_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)

無事に作成されました。

migrationテーブル確認

ここまでで2つのテーブルを作成したので、管理しているmigrationテーブルをみてみます。

$ mysql -u root -p mamy1326
Enter password: 
(中略)

mysql> select * from migrations\G
*************************** 1. row ***************************
        id: 20181206174917
applied_at: 1544120647
*************************** 2. row ***************************
        id: 20181206180443
applied_at: 1544120647
2 rows in set (0.00 sec)

idが migrationファイル名の年月日時分秒 になっていることが確認できます。

rollback実行

2つのテーブルを作りましたが、rollbackするとファイル単位で実行されているようですね。

$ mig-cli rollback
Rollback a migration.
======================

Rollback migrations/20181206180443_item_details.down.sql

migrationテーブル確認

Rollback migrations/20181206180443_item_details.down.sql とあるように、該当する年月日時分秒のレコードが削除されているのがわかります。

mysql> mysql> select * from migrations\G
*************************** 1. row ***************************
        id: 20181206174917
applied_at: 1544120647
1 row in set (0.00 sec)

実際にテーブルも削除されていますね。

mysql> show tables where Tables_in_mamy1326 like 'item%';
+--------------------+
| Tables_in_mamy1326 |
+--------------------+
| items              |
+--------------------+
1 row in set (0.00 sec)

再度migration実行

さっきDROP TABLEされたmigrationファイルが実行されています。

$ mig-cli migrate
Start migration.
=================

Migrate migrations/20181206180443_item_details.up.sql

migrationテーブル、実際のテーブルも作成されていますね。

mysql> melect * from migrations\G
*************************** 1. row ***************************
        id: 20181206174917
applied_at: 1544120647
*************************** 2. row ***************************
        id: 20181206180443
applied_at: 1544121133
2 rows in set (0.00 sec)

mysql> show tables where Tables_in_mamy1326 like 'item%';
+--------------------+
| Tables_in_mamy1326 |
+--------------------+
| item_details       |
| items              |
+--------------------+
2 rows in set (0.00 sec)

連続でrollback

1つずつ、年月日時分秒を遡ってrollbackが実行されるようです。

$ mig-cli rollback
Rollback a migration.
======================

Rollback migrations/20181206180443_item_details.down.sql
$ mig-cli rollback
Rollback a migration.
======================

Rollback migrations/20181206174917_items.down.sql

テーブル、レコードも消えていますね。

mysql> select * from migrations\G
Empty set (0.00 sec)

mysql> show tables where Tables_in_mamy1326 like 'item%';
Empty set (0.00 sec)

使ってみて

非常にお手軽に導入できて、直接SQLを記述できて素晴らしいと思いました。これなら何にも依存せずに使うことができそうです。

PostgreSQLにも対応してくれたら個人的には嬉しいです!!

migrationは便利だけど、フレームワークに依存するよなーSQL直接書きたいよなーって思っていた僕にはとてもいいものに思えました。

現在のところはフレームワークの流儀にのっとって、フレームワークのmigrationを使っていますが、もしレガシーな運用をしているプロダクトに出会ったら、便利に使えそうだなって思ったりしてました。

また、ここに掲載しなかったいろんなパターン(CREATE TABLEの後にALTER TABLEなど)を試してみて、migrationってシンプルに運用できるよな、でもなんども実行するもんじゃないし、最初に設計って重要だよな、って改めて思ったりしてました。

おわりに

OSS活動されているかたって本当に尊いなって思います。 以前、吉祥寺.pm で fujiwara さんがされていたLTを思い出しました。

業務などで使う便利なツールを、業務の活動の範囲でOSSにしてしまう。

こんな活動に通じるものがあるよなあって思ったし、自分が何かOSSを提供できるかどうかはわかりませんが、作ったもので便利そうなものがあれば、積極的にGitHubで公開などできたらなって思わせていただきました。

いやー楽しかった!

学習記録:12月6日(木):ぺちオブ参加日誌2 -オブジェクト指向のいろはとモブプロ-

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の6日目のエントリーです。

昨日のエントリーである 学習記録:12月5日(水):ぺちオブ参加日誌 -これほどわかるOOPの解説は今までなかった- に引き続き、本日 【ぺちオブ】第二回オブジェクト指向のいろはを輪読とか写経する会 に参加してきました。

今回初めての

  • Qiitaの記事を著者さん監修でモブプロする

の体験記を、日が変わらぬ熱いうちに綴ってみたいと思います。

はじめに

先日参加した 【ぺちオブ】初心者向け!第四回オブジェクト指向を1から勉強してみよう会、 前々から興味を持っていた 第二回ボトムアップドメイン駆動設計 に引き続きの参加でした。

なんとこの時点で僕は存じ上げていなかったのですが、Qiitaの記事と 第二回ボトムアップドメイン駆動設計 の講義をしてくださったのが @nrs さんで、同一人物だったってことです。

昨日、 hiro@miraito さんと会話していて知りました。100人以上の前でDDDのお話を体系立ててわかりやすく話してくださった人がいる場でのQiitaの写経とモブプロ。

非常に刺激的かつ、あたたかみに溢れる場所で、部屋が少々暑かったのはエアコンのせいだけじゃないなって思いました。

また、モブプロというのも初めてで、これは今回初参加グループのファシリテーターをしてくださった おーひら モブプロ本売ってまっせ! さんの記事 モブプロで技術書を攻略する! に詳しいです。

参加してみて

最高でした。(今月2回目)

ちょっと遅れての参加だったんですが、ほんとなんでしょうね。ぺちオブのみなさんは本当に丁寧かつポジティブというか、もうなんども言いますが あったかい んですよ。

参加すれば歓迎され、コメントすれば褒められ、コードが書ければ盛り上がり、動いたら踊りだしかねない。 そりゃあポジティブに楽しめるってものです。

生きるってことの本質ってこういうことだよな、って思わせてくれます。

勉強会の進みかた

Qiitaの記事の写経を、モブプロで実施していきます。まさに モブプロで技術書を攻略する! の中に書かれていることを実践してくださっていました。

やったことを列挙してみます。

予習なし

  • 記事を読んでないこと前提

1行ずつ輪読する

  • グループ7人(だったと思う)が1行ずつ読む
  • これが抽象化なのだとあとで知ることになります
  • 多くの文章を読ませない

わからないをそのままにしない

モブプロでコードをPHPに変換して動かしながら進める

  • Qiitaの記事はC#で書かれている
  • その場でPHPに変換して、動かしながらやる
  • みんなで小さな章に区切ってコードを書いていく

会の終わりに振り返りをして締める

  • みんなが何を持ち帰れたかみんながわかる
  • みんな笑顔
  • 最高じゃん?

僕が得られたこと

濃密な時間の中で得られたことはたくさんあるんですが、箇条書きにしてみます。 これも全部 モブプロで技術書を攻略する! に書いてありそうなので、せっかくなので引用しながら。

ポイント1. すべてを声に出して読む

これ本当に重要で、自分の声に出すだけじゃなく、他人の声で聞く。見る、聞く、交代する。僕が一番強く思ったのは、これぞ輪読の抽象化なのだと思いますが、 自分の読んでいる部分にのみ集中できる ということです。

つまり、OOPの目的である 抽象化による本質への集中(でいいのかな) をやっているってことです。

ポイント2. とにかく表明する

すでに書いちゃったんですけど、質問、わからないこと、思ったことだけでなく、他人がどう思っているのか。その場でとにかく表明してしまうんですよね。

わからないので知りたい。僕はこう思った。あなたはどう思いますか。

感想だけでもいいし、良いこと言ったら 最高! とか言い合うわけです。決して宗教じゃないんですよね。いろんな人の考え方から、多角的に1つずつ自分の中にインプットされていくのが重要なんだなって思いました。

感情の共有も含め、非常に大事なことなんだってことを体験させてもらいました。

ポイント3. 予習をしない

僕は記事を1行も読んでいませんでした。またそういったかたも多くいらっしゃいました。

人間は予習をしない

とは おーひら モブプロ本売ってまっせ! さんの言葉でもありましたが、まさに予習をしない(必要としない)前提で会が進んでいきます。

なぜかというと、予習しないことでまっさらな自分で参加できる。 そこには自分の偏った認識ではなく、ファシリテーター、執筆者含め、体系立てた情報が多角的にインプットされる。

自分が綺麗にフォーマットされていくんだなってことを気づかせてくれました。 シンプルな輪読会なら、事前に予習したほうが捗るかもしれません。でもこの会の目的は違う。なんの敷居もない。そこにあるのはただ シンプルかつ莫大なインプットがある ってことでした。

予習しないというのは今まで考えたこともなかったことなので、目から鱗でした。

他の特徴

とにかく時間はかかります。今日は オブジェクト指向のいろは のclassの途中 まで進んで終了でしたし、ここまで2時間半かかっています。

が、これはその場にいる13人(だったかな)全員の体験なんですよね。13人13様が、みんなで共有できる。13人のことが13人でわかるんです。

これがもしチームでやれてたりしたらすごい力になると思うし、きっと元々はそれが目的だったんだろうなってことがわかったりもしました。

モブプロってすげぇな、をこんな短時間で体感できて、多くのことを持ち帰ることができた。 いやこれってすごいことだと本当に思います。

聴講しながら書いたコード

モブプロにも参加しましたが、話を聞きながら僕も手を動かしたかった。 なので聴講しつつ話をしつつ、大急ぎで書いて手元で動かしたコードがこちらです。

<?php

class Writer {
    const WRITE_TYPE_CONSOLE = "console";
    const WRITE_TYPE_FILE = "file";
    private $writeType;
    private $data;

    public function __construct(string $writeType, array $data)
    {
        $this->writeType = $writeType;
        $this->data = $data;
    }

    public function write(): void 
    {
        if($this->writeType == self::WRITE_TYPE_CONSOLE) {
            $this->writeConsole();
        }
        else if($this->writeType == self::WRITE_TYPE_FILE){
            file_put_contents('write.txt', $this->data);
        }
        else {
            throw new Exception("writeType:" . $this->writeType . PHP_EOL);
        }
    }

    private function writeConsole():void
    {
       echo("Data:".PHP_EOL);
       foreach($this->data as $element) {
           echo("- {$element}".PHP_EOL);
       }
    }

    private function writeFile():void
    {
        file_put_contents('write.txt', $this->data);
    }
}
$writer = new Writer($argv[1], ['hoge', 'fuga']);
$writer->write();

おわりに

最後は会を締め、会議室の現状復帰をし、後片付けをして撤収。 軽くご飯組と帰る組で分かれたんですが、なぜかハイタッチの嵐をいただきまして。

ぺちぱーだからぺちぺち

和むじゃないですか。なんか自分が人気者になったような錯覚に陥って妙に照れ臭かったです。 なんだよもうそんなんあるんだったら手を洗っておくんだったわ、って思いました(汚いわけじゃないですw)

執筆者の @nrs さんも ついつい解説したくなる とのことで本当にありがたいなって思うとともに、こういう オブジェクト指向に特化 した会を 連続して実行する っていうのは少なくとも僕の行動範囲では全くありませんでした。

が、昨日も言ったかもしれないんですが、ここまでの時間と知識と経験を使って惜しげも無くインプットをいただける。そんな場を作って楽しくやっていく。

なんの気後れも必要ない。なんならなんの用意も必要ない。欲しいと思う人に、欲しいだけの全てを分けてくれる。

あー。エンジニア諦めなくてよかったなー。こんな人たちがいるんだなー。受けた恩は後ろに続いてくれ人に送っていいかなきゃなー。

改めて、僕の目標や信念を改めさせてもいただきました。

あと、最後に紹介したいことタイムを作ってくれたので、僕が主催にいる エンジニアの登壇を応援する会 の告知をさせていただきました。

また、直近のイベントである 読書の技術を勉強する会 #1 や、年末12月27日の エンジニアの登壇を応援する忘年LT大会 の紹介をさせていただきました。

僕はぺちオブとはまた違った角度で、誰かの背中を押し続けていきたいし、いずれ理解や経験が積み重なったら、その積み重なりをぺちオブに返還したいなあって思ってもいます。

今後も参加していくし、まだのかたはぜひ参加してみてください。最高だぞ!

というわけで、本日の諸々の手配をしてくださったみなさま、ありがとうございました!

学習記録:12月5日(水):ぺちオブ参加日誌 -これほどわかるOOPの解説は今までなかった-

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の5日目のエントリーです。

ところで去る12月1日(土)、10時〜16時くらいまで、【ぺちオブ】初心者向け!第四回オブジェクト指向を1から勉強してみよう会 に参加してきました。

というわけで、参加ポエムを綴ります。

最初に言うと、ポエムが綴れるようになるくらい最高の方々と内容と時間だったってことです。

はじめに

オブジェクト指向プログラミング (Object-oriented programming) と言われ続けて個人的に25年。

今の今まで、自分の中にすんなり入ってくるタイミングが全くありませんでした。

それは僕自身の理解力や技術力、知識、経験など、いろんな要素が不足していたとともに、階段を上がるタイミングを得られなかったんだなって思います。

積み上がった知識はある。しかし…

僕としても最近はLaravelを使った設計思想だったり、DDDだったり、いろんな意味で オブジェクト指向についての知識が自分の中に蓄積 されているのを感じていました。

しかし実務で感じられるレベルではまだない。何か丁寧かつ体系的にインプットできる場所はないものか…。そう思っていた矢先に見つけたのが、この 【ぺちオブ】初心者向け!第四回オブジェクト指向を1から勉強してみよう会 でした。

僕のOOPへの階段はここから上がったんだと思います。

参加してみて

最高でした。

詳しくはぺちオブの主催のみなさんが用意し、更新し続けてくださった これからオブジェクト指向を学ぶ方へ送りたい旅のしおり (後日まとめて公開される予定とのことです) を読めば一目瞭然。これだけで本ができるし、絶対売れると思います。

具体的な振り返り

僕自身の言葉で振り返り、噛み砕くエントリーは、また別で作りたいと思います。おそらくですが、準備してくださり、回を重ねるごとに情報が蓄積・追加されていく過程を僕の理解でなぞることになると思います。

当然、今まで積み上げてくださった時間以上に、僕自身時間をかけなくてはアウトプットできない情報量でした。

ぺちオブで得たインプット・アウトプットの輪廻の中に自分の身を置かねば、と強く思っている次第です。

勉強会の進みかた

SOLID原則を1つずつ1日かけて紐解いていくスタイルだったのですが、

  • 毎回初参加の人向けと振り返りのために
  • 前回までやったことを丁寧に解説

してくれるんですね。僕は第四回ですので、第三回までの「O、D、S」までは参加していません。 が、参加したのと同等かそれ以上の時間と情報でスッと理解できる内容でした。

何と言っても、振り返りに3時間近くかけている。これがどれだけありがたいことで、かつどれだけすごいことか…。

hiro@miraito さん曰く 勉強会が終わった後に追加される内容の方が多い とのこと。 なんなんだよ最高だよ。これぞ集合知

今までにない体験をさせていただきましたし、こういうスタイルで多くの人に何かしら情報を発信できる乗って本当に最高だな(語彙力)ってずっと思ってました。

自分が8ヶ月続けてきた学習スタイルも、巡り巡って 知ったことを丁寧に積み上げていく 形に落ち着いたので、個人としてもすごく入りやすかったです。

改めて最高だったところ

なので、僕が改めて語ることは本当に感想くらいしかないのですが、それでも何が最高だったかを列挙したいのでしてみます。

  • まず言葉を解説。言葉がわからないと進めないよね
    • 何より解説や内容以上に、みんなあったかい
  • 概念理解から入り、丁寧に解説してくれた
    • 具体性を紐解いていく 徹底したトップダウンスタイルで説明してくれた
  • 解説のドキュメントが充実している
    • 1回ずつの積み重ねや前提、ターゲットなど非常に丁寧
  • SOLID原則を1つずつ、5種類を5日かけて丁寧に
    • 講義を受ける側が5日かかるということは
    • 準備してくれる皆さんはその何倍もの時間をかけてくれている
    • それを自発的にやってくれている
    • お金を払ってもやってくれそうにないくらいの濃密さ
  • オブジェクト指向の学習という旅
    • 学習とは知らない土地にいく旅
    • しおりがあった方が快適だし楽しめる
    • そのしおりですよ、と
  • 特にぺちオブを体現していると思われる言葉たちを引用します

このしおりを持って実際にオブジェクト指向の学習という旅出るとこのしおりだけでは伝えきれなかった事も含めて様々な実体験をするでしょう。そして、その旅を終えた頃にはようやく、オブジェクト指向設計やプログラミングが体感できるようになる事かと思います。

そしてその暁に、またそれぞれの新たなしおりが作成されていると非常に嬉しいです。

雰囲気

これまた最高でした。

プレゼンター

hiro@miraito さんでした。また話が丁寧、かつ言葉を紐解いていってくれるのでわかりやすいことこの上なかったです。

これまでのOOPの説明のなかで(書籍も含めて)最高でした。 また声も優しくて素敵。

hiroさんの言葉、僕が言うのもなんなのでご本人の言葉を紹介させていただきます。

特別講師

れおりん 🦀 🐘YYPHP さん。どんな質問もmsec単位ではっきり返してくれました。 しかも根拠も解説してくれるし、講義中にライブコーディングでサンプルコードを提示してくれる。

メインプレゼンターがhiroさんだったからなのだと思いますが、ほとんど言葉を挟まずに静かにその場にいらっしゃいました。が、質問が出ると即座に反応してくださる。

hiroさんのトークとれおりんさんの解説、相乗効果が半端ではなかったです。

質疑応答

話の途中でも頻繁に質問が飛び交い、回答がどんどん得られる。(ライブ配信も行っていました)

僕も何度か質問させていただきましたし、質問だけじゃなく、確認という意味で「自分で理解したことはこういうことだけど、あっていますか?」という問いかけにも丁寧に答えていただきました。

おわりに

完全に感想文になってますが、それくらい最高だったってことでした。 他にも会場を手配して準備してくださったかた、当日案内してくれたかた、などなど、その場を形成してくださった全てのかたに感謝しています。

また、自分の中に蓄積されていた知識や経験が少しずつJOINされていく感覚をはっきり味わうことのできた時間でした。 その証拠に、次に参加した 第二回ボトムアップドメイン駆動設計 の内容が理解できるんですよね。

まだまだ実践には至ってないですが、言っていることがわかる。これもまた初めての体験でした。

本当は振り返りとして、学んだことを丁寧に書き留めてアウトプットしたいのですが、まだ自分の中の情報が整理しきれていません。

振り返りは次の機会にしたいと思います。

明日の 【ぺちオブ】第二回オブジェクト指向のいろはを輪読とか写経する会、土曜日の 【ぺちオブ】初心者向け!最終回オブジェクト指向を1から勉強してみよう会 も参加するのでよろしくお願いします!

学習記録:12月4日(火):[sftp] ユーザーの作り方

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の4日目のエントリーです。

以前、sftpユーザーを作って提供する機会があったのですが、意外と情報が少ないことに気づきました。

ファイル転送プロトコルであるsftpですが、Webサーバーへ様々なファイルをアップロードする際など、まだまだ利用されているのが実情だと思います。

また、sftpだけでなく、ftpを利用するシーンもあることですし、この情報は有用なのでは?と思い、手取り足取りsftpユーザーの作り方を解説してみます。

前提

  • ubuntu16で実行
  • Webサーバーへのコンテンツアップ目的
  • アップロードできるディレクトリに制限を設ける
  • sftpユーザー作成にまつわる作業は全てコンソール上から実施

設定の流れ

  • sftp用ユーザー作成
  • ユーザーのグループ設定
  • ssh鍵作成
  • sftpできるディレクトリ制限
  • sftp先を制限する(DocumentRoot配下のみ許可)
  • sftp確認

対象者

  • 提供したサーバーに対し、直接ファイルアップロードのニーズがある方
  • 古いシステムをメンテナンスしている方
  • 鍵認証についてざっくり知りたい方
  • コンソール怖いという方(コワクナイヨ

sftp設定

新規ユーザー作成

  • sample-sftp というユーザーを作ります
$ sudo useradd sample-sftp
$ sudo passwd sample-sftp
新しい UNIX パスワードを入力してください: 
新しい UNIX パスワードを再入力してください: 
passwd: password updated successfully

# ユーザー用のディレクトリを作成
$ sudo mkdir /home/sample-sftp

sftpユーザーのグループ指定

次にsftpユーザーの所属グループを設定します。 今回はnginxで動いているDocumentRootに対し、コンテンツをアップすることが目的なので、条件を以下とします。

  • sftp接続後のディレクトリは変更しない
    • /home/sample-sftp のまま
    • 理由は後述の openssl(chroot)によるディレクトリ制限 のため
# DocumentRootの持ち主であるnginxユーザーをみる
$ id nginx
uid=112(nginx) gid=116(nginx) groups=116(nginx)

# sftpユーザーをnginxのグループに追加する
$ sudo usermod -g 116 sample-sftp

# グループIDを確認する
$ id sample-sftp
uid=1001(sample-sftp) gid=116(nginx) groups=116(nginx)

無事にグループがnginxになりました。

SSH鍵作成

次にsftpで接続するための鍵を作ります。 その前に鍵を保存するディレクトリを作成します。

$ cd /home/sample-sftp
$ pwd
/home/sample-sftp
$ sudo mkdir .ssh
$ sudo chown sample-sftp:sample-sftp .ssh
ubuntu@ip-172-31-47-139:/home/sample-sftp$ ls -la 
合計 12
drwxr-xr-x 3 root        root        4096 124 22:37 .
drwxr-xr-x 4 root        root        4096 124 22:35 ..
drwxr-xr-x 2 sample-sftp sample-sftp 4096 124 22:37 .ssh

次に実際にsftpするユーザーにチェンジし、鍵を作成します。 鍵のファイル名はデフォルト、パスワードは設定しません。

$ su sample-sftp
パスワード:
$ cd .ssh
$ ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/home/sample-sftp/.ssh/id_rsa): [press enter]
Enter passphrase (empty for no passphrase): [press enter]
Enter same passphrase again: [press enter]
Your identification has been saved in /home/sample-sftp/.ssh/id_rsa.
Your public key has been saved in /home/sample-sftp/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:3YlPDtl7NZ0wX6DbGu5B5Z4+C39amQfQ3GcBg9XjGwk sample-sftp@ip-172-31-47-139
The key's randomart image is:
+---[RSA 4096]----+
|            o++. |
|           . E.=.|
|            oo* B|
|         . = B+==|
|        S = O ++=|
|           B = =+|
|            O +oo|
|           . *..o|
|            . == |
+----[SHA256]-----+

生成された鍵ファイルを確認します。

$ ls -la
合計 20
drwxr-xr-x 2 sample-sftp sample-sftp 4096 124 22:43 .
drwxr-xr-x 3 root        root        4096 124 22:37 ..
-rw------- 1 sample-sftp nginx       3247 124 22:40 id_rsa
-rw-r--r-- 1 sample-sftp nginx        754 124 22:40 id_rsa.pub

次に、公開鍵(ドアノブの鍵穴みたいなもの)を authorized_keys というファイルに転記します。 なお、適切な権限に設定しないと認証に失敗しますので、合わせて設定します。

$ chmod 600 authorized_keys
$ chmod 700 ../.ssh
$ pwd
/home/sample-sftp/.ssh
$ ls -la
合計 20
drwx------ 2 sample-sftp sample-sftp 4096 124 22:43 .
drwxr-xr-x 3 root        root        4096 124 22:37 ..
-rw------- 1 sample-sftp nginx        754 124 22:43 authorized_keys
-rw------- 1 sample-sftp nginx       3247 124 22:40 id_rsa
-rw-r--r-- 1 sample-sftp nginx        754 124 22:40 id_rsa.pub

# suでチェンジしたユーザーからexit
$ exit

sftpできるディレクトリを制限

sftpアカウントで接続した際、ルートからなんでも見れて更新できては何かと困ります。 ゆえに制限をかけていきます。

  • sshd_configはバックアップをとっておき、何かあったらすぐ戻せるように
  • sshd_configに制限を追加
  • 再起動

まずバックアップを作成します。

$ pwd
/etc/ssh
$ sudo cp -rp sshd_config sshd_org_config

ユーザーの制限事項を追加します。

# /etc/ssh/sshd_configを開く
$ sudo vim sshd_config

/etc/ssh/sshd_configにユーザー制限を追加します。 ファイルの末尾に追加しました。

Match User sample-sftp
    ChrootDirectory /home/sample-sftp
    ForceCommand internal-sftp

次にsshを再起動して設定を反映します。

$ sudo /etc/init.d/ssh restart
[ ok ] Restarting ssh (via systemctl): ssh.service.

あと、僕は失敗した時のために、必ずすぐ戻すためのコマンドを先に用意してたりします。

mv sshd_config sshd_config_failed
mv sshd_org_config sshd_config
sudo /etc/init.d/ssh restart

DocumentRootの調整

DocumentRootで直接ファイルを操作されると何かと困(ry 以下の手順で sftpユーザーのhome配下のみ更新できる ように設定していきます。

  • /home/sample-sftp/htdocs の配下にアップロードしてもらう
  • /srv/www/www.mamy1326.jp/htdocs を上記のシンボリックリンクにする
  • これをやることで、 sshd_configに設定したChrootDirectoryの制限を回避できる

/home/sample-sftp/htdocsを作成し、sftp接続で書き込み可能にします。

$ pwd
/home/sample-sftp
$ sudo mkdir htdocs
$ sudo chown sample-sftp:sample-sftp htdocs
$ sudo chmod 755 htdocs
$ ls -la
合計 16
drwxr-xr-x 4 root        root        4096 124 22:57 .
drwxr-xr-x 4 root        root        4096 124 22:35 ..
drwx------ 2 sample-sftp sample-sftp 4096 124 22:43 .ssh
drwxr-xr-x 2 sample-sftp sample-sftp 4096 124 22:57 htdocs

次に、実際のDocumentRootにシンボリックリンクを作成します。

$ pwd
/srv/www/www.mamy1326.jp
$ sudo ln -s /home/sample-sftp/htdocs public
$ ls -la
合計 12
drwxr-xr-x 3 root root 4096 124 23:01 .
drwxr-xr-x 4 root root 4096  97 02:37 ..
lrwxrwxrwx 1 root root   24 124 23:01 public -> /home/sample-sftp/htdocs

sftp実行

これで準備は整いましたので、実際に接続し、意図通りにファイル操作ができるかを確認します。手順は以下です。

  • ローカルから接続
  • 書き込みできることを確認
  • 他のディレクトリに移動できないことを確認
  • sftpのみ(sshできない)ことを確認
  • コンテンツをアップして、DocumentRootにアップされていることを確認する

まずは先ほど作った秘密鍵をサーバーから取得します。 ファイルの設置や、適切な権限設定を実行しておいてください。

$ scp -i mamy1326.pem ubuntu@18.179.206.65://home/ubuntu/id_rsa id_rsa_sample-sftp
id_rsa                                                                                                                              100% 3247    54.4KB/s   00:00 
$ sudo cp id_rsa_sample-sftp ~/.ssh
$ sudo chmod 600 ~/.ssh/id_rsa_sample-sftp
$ ls -la ~/.ssh/id_rsa_sample-sftp
-rw-------   1 mamy1326  staff  3247 12  4 23:06 id_rsa_sample-sftp

準備は整ったので、実際に接続してみます。

$ sftp -i id_rsa_sample-sftp sample-sftp@18.179.206.65
Connected to sample-sftp@18.179.206.65.
sftp> 

無事に接続できたので、色々コマンドを実行してみましょう。

# /home/sample-sftp直下にはアップロードできない(root:rootなので)

# 一覧をみる
sftp> ls -la
drwxr-xr-x    4 0        0            4096 Dec  4 13:57 .
drwxr-xr-x    4 0        0            4096 Dec  4 13:57 ..
drwx------    2 1001     1001         4096 Dec  4 13:43 .ssh
drwxr-xr-x    2 1001     1001         4096 Dec  4 13:57 htdocs

# 場所を確認する。sftpユーザーからみてルートであることがわかる
sftp> pwd
Remote working directory: /

# 権限が与えられていない場所にputすると権限がなくて失敗する
sftp> put sample.pdf                    
Uploading sample.pdf to /sample.pdf
remote open("/sample.pdf"): Permission denied
sftp> 

次に、権限が与えられているディレクトリに移動し、ファイルをputしてみます。

# htdocs配下にはアップできる

sftp> cd htdocs 
sftp> put sample.pdf
Uploading sample.pdf to /htdocs/sample.pdf
sample.pdf                                                                                                                          100% 4007    54.2KB/s   00:00    
sftp> ls -la
drwxr-xr-x    2 1001     1001         4096 Dec  4 14:17 .
drwxr-xr-x    4 0        0            4096 Dec  4 13:57 ..
-rw-r--r--    1 1001     116        614935 Dec  4 14:17 sample.pdf
sftp> pwd
Remote working directory: /htdocs
sftp> 

最後に、実際にDocumentRootにファイルが設置されているか確認します。

$ ls -la /srv/www/www.mamy1326.jp/public/
合計 12
drwxr-xr-x 2 sample-sftp sample-sftp 4096 124 23:17 .
drwxr-xr-x 4 root        root        4096 124 22:57 ..
-rw-r--r-- 1 sample-sftp nginx     614935 124 23:17 sample.pdf

無事にアップされていますね。 これでめでたく、外部の人に制限されたアクセス状態で、DocumentRootを更新 できるようになりました。

Webブラウザから確認

DocumentRootにアップしたので、Webブラウザから見れることを確認しておきます。 そのうちインスタンスを停止しますが、現在こんな感じですね。

f:id:Mamy1326:20181205010630p:plain
さんぷるのPDF

連絡報告

あとはクライアントに、接続情報、鍵ファイル、制限事項などを連絡報告および共有します。 その結果として、クライアントが無事に接続でき、目的を達成できたことを確認して、クライアント向けの作業が完了、となるかと思います。

課題

ブラウザから確認

  • せっかくWebサーバー立てて、ドメイン名をとったので、ブラウザから確認できるようにエントリーに追記します。
    • やりました!

CyberDuck確認

おそらくクライアントは何かしらツールを使うと思うので、設定手順があると便利よね。

ドキュメント整備

実際の業務だと、作って終わりというわけではないと思います。 各種情報や鍵ファイルなどは適切な権限と場所に管理しておきましょう。

  • GoogleDriveにsftp用の鍵を置く
  • 同じくアカウント情報を置く
    • 情報(sftp)
      • IPアドレス
      • ユーザー名
      • 鍵のファイル名
      • 接続方法
      • コンテンツアップ先(フルパス)

やってみて

以上、実際にEC2インスタンスを立て、Webサーバー(nginx)を起動し、実行した結果です。

実際の業務では、単一の作業だけではなく、お客さんへのリリースや連絡、社内でのドキュメント整備など、付随する作業があると思います。

また、なぜそう設定するのか。 理由も含めて手順として残しておけたらいいですよね。

というわけで、僕の個人メモをもう一度再現し、実際に動いた結果といて公開してみました。

もう一度やれと言われたら暗記してる自信は皆無だし、未来の自分が今の自分に必ず感謝すると思うので、積極的に残していきたいですね。

学習記録:12月3日(月):【git stash】作業ブランチを作らず作業してしまった時の対処法

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の3日目のエントリーです。

今日は MyNA(日本MySQLユーザ会) 望年LT大会2018@赤坂 に参加して多少酔っています。いやー楽しかった!次は僕もLTできるネタを持って行こう。

ところでみなさん、gitでうっかり 作業ブランチを作らず作業してしまった なんてこと1度はありませんか?僕はあります。

とはいえ他に同じようなエントリーは溢れていると思うので、備忘録とこれからの人にすんなり解説できるためにも、自分のブログに書いてみます。

前提条件

  • gitはコマンドから実行します
  • 仮に以下のプロジェクトがあったとします
$ pwd;find . | sort | sed '1d;s/^\.//;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/|  /g'
/Users/mamy1326/dev/mamy1326/git_stash_test
|--app
|  |--controller
|  |  |--Fuga.php
|  |  |--Hoge.php
  • このプロジェクト、うっかりmasterに変更しちゃったよ
  • それをstashを使って作業ブランチへ移動し、merge
  • 無事に作業ブランチからadd, commit, pushして、PullRequestを出す

というところまで実施します。

正しい(と僕は思っている)方法

僕が実際にやってみて、他でも調べて、これが正解と思う内容を紹介します。

やっちまった

さあ修正が終わったぞ、と思って、 add, commit, push してさっさとPullRequestしちまおう!と思うじゃないですか。

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

OKOK、さてブランチは…っと。

$ git branch
* master

( ゚д゚)ハッ! 作業ブランチ切ってないやないかーい!

そしてこの時、僕はまだstashを知らないわけです。

まず深呼吸して落ち着く

困った時に最初にやること。いや大事です。まず深呼吸しましょう。そのあと、できれば何に困っててどうしたいか紙に書きましょう。 絶対に解決できる方法がある。先人の知恵ってすごいんです。

まずは落ち着いて未来を信じましょう。

変更を保存する: git stashgit stash list

落ち着いたら以下のコマンドを実行します

$ git stash
Saved working directory and index state WIP on master: 2b38798 first commit.
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.

これにより、masterで実行した変更が一時的な保存リスト stash@{0} に保存されたことがわかります。

変更を確認する: git stash show

一応、保存された内容を git stash show で確認してみましょう。

$ git stash show stash@{0}
 app/controller/Fuga.php | 2 +-
 app/controller/Hoge.php | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

どうでしょう。変更した内容をちゃんと確認できるかと思います。 こうなれば、別のブランチに移動できます。

つまり、作業ブランチを作成することができるということです。

作業ブランチを作って移動: git checkout -b

Issueに基づいて作業することを前提としているので、Issue番号で作業ブランチを作成します。

$ git checkout -b feature/1 origin/master
Branch 'feature/1' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'feature/1'
$ git branch
* feature/1
  master

無事に作業ブランチを作成し、移動できたことがわかります。

一時保存した変更内容をmergeする: git stash apply

まずは一応、一時保存リストを確認します。 先程はmasterで確認しましたが、新しい作業ブランチでも見れることを確認しないと不安で胸が張ちきれそうです。

特に僕はブログなどの「わかる人にはわかる行間」がわからなくてハマることが多いので、1つずつ確認していきます。

$ git branch
* feature/1
  master
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.

先ほど保存した内容が、作業ブランチでも確認できます。

一応の一応、変更内容そのものも確認しておきましょう。

$ git diff stash@{0}
diff --git a/app/controller/Fuga.php b/app/controller/Fuga.php
index 173f17f..4f6b682 100644
--- a/app/controller/Fuga.php
+++ b/app/controller/Fuga.php
@@ -1,2 +1,2 @@
 <?php
-echo("fugafuga!!");
+echo("fuga!!");
diff --git a/app/controller/Hoge.php b/app/controller/Hoge.php
index 079d8f7..4a1f61a 100644
--- a/app/controller/Hoge.php
+++ b/app/controller/Hoge.php
@@ -1,3 +1,2 @@
 <?php
-echo("hogehoge!!");
-
+echo("hoge!!");

いやーつまんない修正例ですみませんね!でもちゃんと修正の差分も見れることが確認できました。

では落ち着いて、mergeしちゃいましょう。

$ git stash apply stash@{0}
On branch feature/1
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

git stash で一時保存された内容がmergeされ、そのファイルのリストが表示されます。 これは git status を実行した時と同じです。

念の為 git status も実行します。

$ git status
On branch feature/1
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

おんなじですね。

やっといつもの: git add, git commit, git push

ここまでくればいつものやつ。 落ち着いて git add, git commit, git push すればOKです。

まずは git add で追加します。

$ git add .

次に git commit します。コメントはご愛嬌。

余談ですが、コメントは英文にこだわっていた時期がありました。が、英語圏の人と仕事をしないなら、頑張って英文にするより、一目見てわかる日本語のコメントの方が良いと個人的に思っています。

ちなみに、commitコメントに Issue番号 を入れる ( #1 ) と、Issueに記録されていくので便利です。

$ git commit -m "#1 メッセージが寂しいので単語を多くする(ひどい)"
[feature/1 c094ec8] #1 メッセージが寂しいので単語を多くする(ひどい)
 Committer: まみー <mamy1326@gmail.com>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:

    git config --global --edit

After doing this, you may fix the identity used for this commit with:

    git commit --amend --reset-author

 2 files changed, 3 insertions(+), 2 deletions(-)

そしてリモートブランチに git push します。

$ git push origin feature/1
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 502 bytes | 251.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: 
remote: Create a pull request for 'feature/1' on GitHub by visiting:
remote:      https://github.com/mamy1326/git_stash_test/pull/new/feature/1
remote: 
To github.com:mamy1326/git_stash_test.git
 * [new branch]      feature/1 -> feature/1

gitを日常で使っている方にはおなじみのコマンドを実行してリモートブランチにpushし、PullRequestを出すだけです。

本当にお疲れさまでした。焦るとロクなことないですよね!

一時保存を消す: git stash drop

push、PullRequestしてmergeされたら stash で一時保存した内容は不要なので消しておきましょう。

ローカルもリモートも作業ブランチは削除する(mergeされたら不要だし残しておくべきではない)ので、masterブランチへ移動してから実施します。

# masterブランチへ移動
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
# 現在のブランチを確認
$ git branch
  feature/1
* master
# 一時保存リストを見る
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.
# 一時保存を消す
$ git stash drop stash@{0}
Dropped stash@{0} (ed610ef4a14ee816875b883ff558afac02303ddb)
# dropしたので消えている
$ git stash list

ついでにmasterも最新にしておけば、次の作業もスムーズですね。

$ git pull origin master
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From github.com:mamy1326/git_stash_test
 * branch            master     -> FETCH_HEAD
   2b38798..9ad9e31  master     -> origin/master
Updating 2b38798..9ad9e31
Fast-forward
 app/controller/Fuga.php | 2 +-
 app/controller/Hoge.php | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

エントリーにしようと思った動機

そもそもなぜ記録を残して解説しようと思ったのか。

うっかりmasterブランチのまま、修正作業をしてました。で、stashコマンドを使い慣れていればいいんですけど、僕の場合は今回(数週間前)が初めてだったんですよね。

しかも20ファイル以上に修正入れてた。

で、深夜に作業しててひとりでめっちゃ焦って「どうすればええんや…」という脱力感と絶望感に襲われたので、同じような思いをこれからする人もいるかなーと思ったのと、一緒に働くメンバーにgitコマンドを教える機会がありそうなので、出来るだけ丁寧な形で残そうと思ったのでした。

人は誰しも焦る(と思いたい)

うわーこれどうすべぇ…と焦って、調べたらstashってコマンドあるじゃないですか。

で実行して作業ブランチ作って移動してmerge…しようと思ったらなんか怒られる。ブランチの移動もできない。

ニッチもサッチも行かなくなってしまってどう解決したかを記録しておこう、って思ったのです。

きっと1週間後、1ヶ月後、1年後の自分のためになるはず。そう思って どんな小さなことでも、困ったら記録に残す ことを徹底していたりします。

やってみて

こうしてエントリーにまとめてみて思うのですが、 git stash って毎日叩くコマンドでもないので、良い復習になりましたし、アウトプットするためにリポジトリ作って同じことを何度かやったので、もう2度と忘れないですね。

これは普段から発言しているんですが、 インプットとアウトプットの輪廻を作る ことで学習が継続されますし、質も上がりますし、自分の中に刻まれていきます。

かつ、もしかしたら誰かのためになるかもしれないし、内容次第では登壇テーマにもなりうる。

小さなことからコツコツと。 インプットしたら、どんな形でも良いのでアウトプットしよう って思っています。

というわけで、今回は超初心にかえるような内容となりましたが、1つのコマンドについて丁寧に解説するとこんなボリュームになるんだな、と改めて思うとともに、まずは自分にとって不明点がないように書くのって大事だな、ここまでやって初めて 他の人に解説できるようになる な、って思います。

誰かのお役に立てたら良いなと思いつつ、インプット・アウトプットやっていきましょう!

学習記録:12月2日(日):達人に学ぶSQL徹底指南書 第1部 魔法のSQL 1.CASE式のススメ

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の2日目のエントリーです。

常日頃からSQL力(ちから)不足を感じていたところ、以前に購入して積ん読になっていた 達人に学ぶSQL徹底指南書 の第二版が出版されました。

最新のWindow関数も掲載されているということで見た瞬間に購入。しかしこのまま積ん読も怖い…。

と思っていたら、[秋葉原] 達人に学ぶSQL徹底指南書 輪読&勉強会 第1部 魔法のSQL で輪読会をやるということに。なんとありがたいことだ…。

ということで、これは参加しない手はない、読み進めよう!ということで進めています。

まずCASE式から

CASE式だけで24ページあります。正直あんまり使いこなしていなかった式からがっつり入るんだな、と思いつつ、読み進めていく。 これが非常に丁寧でわかりやすい。

  • なぜ必要なのか
  • どういう場面で使うのか
  • IFやDECODEと比べてどうなのか
  • 応用すると真価を発揮する
  • Oracleなどにもきてほしいね

ということが実際のデータとSQLを例に解説されていきます。

僕自身も実際にデータを作ってSQLを実行してやってみているんですが、ちょうど今の仕事でPostgreSQLを使っているので、そちらで試しています。

試したコードも貼っていこうと思うんですが、まずは読み進めということで、書籍に準ずる形でメモを残してます。

esa-pages.io

感想Tweet

いただいたリプ

なんとMySQLユーザー会副会長の 坂井 恵(SAKAI Kei) さん!

安心してください!第二版です!!!!

感想

書籍の導入部分に 実務でのSQLプログラミング半年〜1年 と書かれている通り、SQLの基本的な構文は一通りやっている前提なのかな、と思います。

その上で、じゃあどんな場面でどんな式を使うのか、という内容を歴史や実例を伴って解説してくれています。

まだまだ第1章の途中までしか読んでいませんが、ずっと前から名著と呼ばれる理由をしょっぱなから実感してます。

もしSQL力(ちから)を高めたい、雰囲気で使ってる部分をわかりたい、 プログラマのためのSQL を読んで難しすぎて投げ出した!って人は必読かなって思います。

あと、次のページに出てくるんですが、 WHERE句で条件分岐させるのは素人のやること、プロはSELECT句で分岐させる と明言されていて、非常に使い勝手のいいテクニックとのこと。

ひとつずつ基礎が身についていくこの感覚、すごく楽しいです。