プログラムの複雑さをレゴブロックで例えてみる

上司に説明するときに何かいい例えはないだろうかと考えて、なんとなくレゴブロックの例を思い付きました。主観的な内容ですのであしからず...。


レゴで遊んだことがあるひとには説明不要かと思いますが、レゴにはいろいろな形や色のブロックがあります。例えば、顧客から「とある形のブロックを別の色に変えて欲しい」という要求があったとします。

きちんと設計されていない、いわゆるスパゲッティ状態のプログラムをレゴで例えると、以下のように雑多にブロックが集められているようなイメージです。

f:id:knt_mr:20180823224523j:plain:w400 (https://www.toysrus.co.jp/s/dsg-489487100)

顧客の要求に応えるには、この雑多なブロックの集まりをガサガサとかき分けて特定の形のブロックを探し出す必要があります。実際、このようなプログラムに手を入れるときも感覚としては近いものがあるような気がします。レゴで遊んだことがあるひとには分かるかもしれませんが、狙った形のブロックを探すのって意外と難しいものです。

一方で、割としっかり作ってはあるが、必要以上にかっちり作り過ぎて複雑になっているプログラムをレゴで例えると、以下のようなイメージです。(あくまでも例えです、この画像の建物はきちんと考えられて作られたものと思われます)

f:id:knt_mr:20180823224531j:plain:w400 (https://www.gizmodo.jp/2017/05/lego-minecraft-the-mountain-cave.html)

顧客の要求に応えるには、同じようにガサガサとかき分けるわけにはいかず、まずどのような構造になっているか把握する必要があります。で、目星が付いたら、もとの形を崩さないように手を入れていく必要があります。もしかしたら屋根の部分や外壁の部分をごそっと取り外して中に手を突っ込むこともあるかもしれません。(※ここではリファクタリングの話とかはしません)
実際、このようなプログラムに手を入れるとき、現状を把握するのにとても苦労します。しかも、こういうときに限ってドキュメントがなかったりします...。

まとめ

シンプルがいちばん。シンプルを保つのは簡単ではないが、できる限りシンプルさを保てるように継続的に改善しよう。

Vue.js の日本語ガイドに PR したら英語の勉強になった

v-for に key 属性を指定するときとしないときの違いの一例 - kntmr-blog

これの余談なんですが、v-forkey の仕様を確認しようと思って公式ガイドを読んでたら、なんとなく誤訳かなと思われるものを見つけたので Pull Request を投げてみました。

当初の翻訳はこう。

可能なときはいつでも v-forkey を与えることが推奨されます。これは、繰り返される DOM の内容が単純でない、または性能向上を標準の動作に意図的に頼っていない場合に限ります。

で、原文はこちら。

It is recommended to provide a key with v-for whenever possible, unless the iterated DOM content is simple, or you are intentionally relying on the default behavior for performance gains.

最初、この unless が接続詞なのかなと思って、それに続く the iterated DOM content is simpleyou are intentionally relying on the default behavior for performance gains を否定してこれらを 除く ものと解釈しました。

可能なときはいつでも v-forkey を与えることが推奨されます。ただし、繰り返される DOM の内容が単純でない、または性能向上のための標準の動作に意図的に頼らない場合を除きます。

が、この解釈は誤っているとコメントがあり、再度 Pull Request を投げることに。あと、前後の文章を入れ替えてみた。

繰り返される DOM の内容が単純でない場合、または性能向上のための標準の動作に意図的に頼る場合を除いて、可能なときはいつでも v-forkey を与えることが推奨されます。

ただ、これも意味的には誤っていて、最終的にはこの Pull Request で修正していただきました。

繰り返される DOM の内容が単純な場合や、性能向上のために標準の動作に意図的に頼る場合を除いて、可能なときはいつでも v-forkey を与えることが推奨されます。

ようするに、この unless は接続詞ではなく、単純に the iterated DOM content is simple を否定するための前置詞なんですね。確かに DOM が複雑というか処理が重くなりそうなら key を付けずに性能向上を図る方がいいのかもしれません。

結局、話をややこしくするだけになってしまって申し訳ない感じに...。一応、当初の内容から変わるきっかけにはなったので少しは役に立っただろうか。いろいろと勉強になりました。

v-for に key 属性を指定するときとしないときの違いの一例

先日、v-forkey 属性を指定しないことが原因で生じる不具合に遭遇しました。別にたいしたものではなんですが、v-forkey 属性の確認を兼ねて簡単にまとめます。

ただ、2.2.0 以降のバージョンでは key は必須なんですね。

2.2.0 以降で、コンポーネントv-for を使用するとき、key は必須です

リストレンダリング #コンポーネントと v-for - Vue.js

雑なサンプルコードはこちら。

kntmr/playground - GitHub


v-forkey を指定しない状態でリストを描画する。で、リスト内のある要素をクリックするとスタイル (文字色とか) が変わるものとする。

<div>
  <ul>
    <li v-for="item in items">
      <p class="item" @click="select">{{ item.name }}</p>
    </li>
  </ul>
  <button @click="toggle">Toggle Data</button>
</div>

ここで、いずれかの要素をクリックしてスタイルを変更した状態で、リストの要素をすべて入れ替える。(サンプルコードの中ではボタンクリックで items の中身をすべて入れ替えるようにしてあります)

すると、リストの内容は書き変わるが、先ほど変更したスタイルがそのまま残る。これは v-forDOM を使い回しているため と思われる。

で、v-forkey を付ける。

<li v-for="item in items" :key="item.id">

こうすると、DOM の使い回しが回避できるため、あるべきスタイルで描画されるようになる。ちなみに、公式ガイドの key の項には以下のように記載されている。

この標準のモードは効率がいいです。しかしこれは、描画されたリストが子コンポーネントの状態や、一時的な DOM の状態に依存していないときにだけ適しています (例: フォームのインプットの値)。

リストレンダリング #key - Vue.js

というわけで、基本的には性能向上を目的として DOM を使い回す動作になっているようです。(が、安全な方に倒すのであれば、key を付けるのが無難な気がする...)

動的にデータを取得する Vue.js のツリーコンポーネント

ツリー表示する方法を調べてたら Vue.js の公式サイトにいい感じのサンプルがありました。このサンプルではコンポーネント再帰することでツリーを実現しています。とても学びがありますね。

ただ、今回はツリーを開くタイミングで動的にデータを取得するようにしたかったのですが、このサンプルは初期表示の時点でツリーのデータをすべて持っています。

というわけで、サンプルをカスタマイズしてみました。

github.com

構成は公式サイトのサンプルとほぼ同じですが、今回必要のなかった箇所は移植していません。

ツリーを開くときに呼ばれる toggle メソッド内でツリーの子要素を取得します。また、2回目以降ツリーを開くたびにリクエストが飛ばないように m-loaded 属性で制御しています。

あと、ローカルで動作確認するために JSON Server を使っています。

> npm install -g json-server
> json-server --watch db/db.json

スタディサプリ ENGLISH で英語の勉強を始めました

2018年の目標に「英語の勉強」を挙げてましたが、進捗ゼロのまま半年以上が過ぎてしまいました...。

2017年のふりかえりと2018年のこと - kntmr-blog

このままではよろしくないので、とりあえず通勤時間を利用して英語の勉強をしてみようかと。とはいえ、本や単語帳アプリでは長続きしなさそうなので、以下のアプリを試してみました。

eigosapuri.jp

で、1週間ほど無料コンテンツを試してみたところ、なかなかいい感じ。

  • ストーリー仕立てで飽きない
  • 文法やリスニングのポイントなどを動画で解説してくれる
  • 自分の弱点を評価してくれる

というわけで、プレミアム会員に登録してみました。月額1080円。頑張ります。

ちなみに英語を勉強するモチベーションですが、特にかっこいいものではなくて、映画を字幕なしで観たいとか、海外ミュージシャンのMCを理解したいとかそんな程度です。

50000PV

特にたいしたものではないんですが、今朝たまたま見たらアクセス数の合計がちょうど 50000 だったのでキリ番ゲット的な気持ちで記念にキャプチャしました。

f:id:knt_mr:20181003161126p:plain

よくアクセスされるページは、Spring や JMockit 関連が多い模様。あと、最近は Vue.js 関連のアクセスが多くなってきました。しかし、アクセス元のサイト (Google, Bing, Yahoo!検索) によってアクセスするページの傾向が違うのはちょっと興味深い。

Docker Compose を使って EC2 に Redmine をインストールする

前回の続きというわけではないんですが、せっかく作成したインスタンスがあるので、Docker Compose を使って Redmine をインストールしてみました。備忘録。

AWS の10分間チュートリアルで EC2 インスタンスを起動してから HTTP アクセスするまで - kntmr-blog

バージョン情報

一応、カーネルのバージョンを確認。

$ uname -r
4.14.47-56.37.amzn1.x86_64

Docker インストール

$ sudo yum update -y
$ sudo yum install -y docker
$ docker -v
Docker version 18.03.1-ce, build 3dfb8343b139d6342acfd9975d7f1068b5b1c3d3
$ sudo service docker start
$ sudo usermod -a -G docker ec2-user # ec2-user を docker グループに追加
$ exit
# ssh 再接続する
$ docker info # sudo なしで OK

Docker Compose インストール

Install Docker Compose | Docker Documentation

$ sudo -i # スーパーユーザーで実行
$ curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose # 実行権限付与
$ exit # スーパーユーザーここまで
$ docker-compose --version
docker-compose version 1.21.2, build a133471

docker-compose.yml 作成

適当なディレクトリに docker-compose.yml を作成する。

$ vi docker-compose.yml

GitHub - sameersbn/docker-redmine: Docker Image for Redmine

内容は上記にある docker-compose-mysql.yml とほぼ同じですが、少しだけ手を入れます。全体は以下の通り。

version: '2'

services:
  mysql:
    image: sameersbn/mysql:latest
    environment:
      - DB_USER=redmine
      - DB_PASS=password
      - DB_NAME=redmine_production
    volumes:
      - /srv/docker/redmine/mysql:/var/lib/mysql
  redmine:
    image: sameersbn/redmine:latest
    depends_on:
      - mysql
    environment:
      - TZ=Asia/Tokyo

      - DB_ADAPTER=mysql2
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_USER=redmine
      - DB_PASS=password
      - DB_NAME=redmine_production

      - REDMINE_PORT=10083
      - REDMINE_HTTPS=false
      - REDMINE_RELATIVE_URL_ROOT=/redmine
      - REDMINE_SECRET_TOKEN=

      - REDMINE_SUDO_MODE_ENABLED=false
      - REDMINE_SUDO_MODE_TIMEOUT=15

      - REDMINE_CONCURRENT_UPLOADS=2

      - REDMINE_BACKUP_SCHEDULE=
      - REDMINE_BACKUP_EXPIRY=
      - REDMINE_BACKUP_TIME=

      - SMTP_ENABLED=false
      - SMTP_METHOD=smtp
      - SMTP_DOMAIN=www.example.com
      - SMTP_HOST=smtp.gmail.com
      - SMTP_PORT=587
      - SMTP_USER=mailer@example.com
      - SMTP_PASS=password
      - SMTP_STARTTLS=true
      - SMTP_AUTHENTICATION=:login

      - IMAP_ENABLED=false
      - IMAP_HOST=imap.gmail.com
      - IMAP_PORT=993
      - IMAP_USER=mailer@example.com
      - IMAP_PASS=password
      - IMAP_SSL=true
      - IMAP_INTERVAL=30

    ports:
      - "10083:80"
    volumes:
      - /srv/docker/redmine/redmine:/home/redmine/data

実行

Docker Compose を実行して Redmine のコンテナを起動する。

$ docker-compose up -d # -d でバックグラウンド実行

10083 ポートで /redmine にアクセスすると Redmine の画面が表示される。

リバースプロキシ

前回インストールした Apache のリバースプロキシを使って、80 ポートの /redmine でアクセスできるようにする。

$ sudo -i # スーパーユーザーで実行
$ vi /etc/httpd/conf.modules.d/00-proxy.conf # 下記参照
$ exit # スーパーユーザーここまで
$ sudo service httpd restart

00-proxy.conf の設定内容は以下の通り。

ProxyRequests Off
ProxyPass /redmine http://127.0.0.1:10083/redmine
ProxyPassReverse /redmine http://127.0.0.1:10083/redmine

80 ポートでアクセスすると Redmine の画面が表示されるはず。admin / admin でログインできます。

まとめ

Docker Compose 便利。