主夫ときどきプログラマ

プログラミング、Webエンジニアリング、etc

Vuexのはじめかた

これはVue.jsアドベントカレンダー#2 14日の記事です。

Vuexはflaxアーキテクチャを採用した公式プラグインで、vue.jsで利用することができます。 公式の日本語ドキュメントもあるので導入しやすい状況になっています。

とはいえ、いざ使ってみようという時には気が引けてしまうもの。 自分が導入を試みた時もいろいろと悩むポイントがありました。

  • どこから手をつけていいかわからない
  • ファイル構成のお手本が知りたい
  • スタンダードな書き方を知りたい

このあたりのことを、どう解決したかについて今回は紹介します。

はじめにすること

公式ドキュメントを読みましょう。苦手な人もいると思いますが なにを意識しながら読むかが決まっていれば読みやすくなるものです。

fluxを理解する

事前にfluxアーキテクチャについて理解する必要があります。fluxを知った上でVuexのドキュメントを読むとより理解しやすくなります。 アーキテクチャの構成要素と一方向性の特徴がつかめる程度でOKです。

このあたりの記事に解説があります。

WEB+DB PRESS Vol.97があれば特集記事があります。一番わかりやすいです。

公式ドキュメントを読む

Vuex とは何か?

事前に公式ドキュメントを一通り読んでおきましょう。流し読みでよいです。 コアコンセプトに具体的な実装方法が記載されているので、これを中心に読むと良いでしょう。 初めての時は書き方を覚えることもできないし、ぼんやりとしか理解できないと思います。 でもそれでOKです。 全体として何が出来るのかをつかむことが重要です。

  • Vuex とは何か?
  • Vuex 入門
  • コアコンセプト

の3つの章を読めばとりあえずは大丈夫です。

小さく始めよう

コアコンセプトにある5つの要素をいきなり全て取り入れることは、初心者にとって困難なことです。 そうなった時は、何から手を付けていいかわからないという状況に陥ります。 なので小さく始めて少しずつVuexを取り入れていきましょう。

サンプルコードは $ vue init webpack sample で作成されたプロジェクトを基に進めていきます。

まずはステートから

まずはステートの導入から初めましょう。以下のような何でもない Sample.vue を例にします。
ステートのドキュメント

// components/Sample.vue
<template>
  <div class="sample">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'sample',
  data() {
    return {
      msg: 'Welcome to My Vuex Sample',
    };
  },
};
</script>

Vuex ストアを作成

msg プロパティを表示するだけのシンプルなコンポーネントです。この msg プロパティを Vuex に移していきます。 まず、Vuexストアを作成しVueに入れます。

// main.js
import Vuex from 'vuex';
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    msg: 'Hello Vuex Store.',
  },
});

new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App },
  store,
});

これでVuexを使う準備が整いました。

コンポーネントから Vuex ストアを参照する

次はVuexストアから msg ステートを読み込んで Sample.vue に表示しましょう。 コンピューテッドプロパティからストアを参照するように変更します。

// components/Sample.vue
export default {
  name: 'sample',
  data() {
    return {
      // msg: 'Welcome to My Vuex Sample.',
    };
  },

  computed: {
    msg() {
      return this.$store.state.msg;
    },
  },
};

Hello Vuex Store. が表示されましたね!おめでとう!

mapState ヘルパーを使う

msgのようなステートを複数読み込む場合には mapState ヘルパーを使うことで簡潔に記述することができます。

// components/Sample.vue
import { mapState } from 'vuex';

export default {
  name: 'sample',
  data() {
    return {
    };
  },

  computed: {
    ...mapState({
      msg: state => state.msg,
    }),
  },
};

msg の長さを取得するコンピューテッドプロパティを定義したい場合にもローカルステートのようにthisでアクセスできます。

// components/Sample.vue
computed: {
  msgLength() {
    return this.msg.length;
  },
  ...mapState({
    msg: state => state.msg,
  }),
},

これでVuexのステートを利用することが出来るようになりました。

ステートの値を変更するには

ステートの msg を更新するにはどうしたら良いでしょうか。更新ボタンをクリックすると、フォームの入力値でステートを更新するようにします。

// components/Sample.vue
<input type="text" v-model="newMsg" />
<button @click="update">更新</button>

export default {
  name: 'sample',
  data() {
    return {
      newMsg: null,
    };
  },

  methods: {
    update() {
      this.$store.state.msg = this.newMsg;
    },
  },
};

update() メソッドで msgnewMsg に入力された値に更新します。 しかしこの方法だとアプリケーションが大きくなるにつれ、どこで msg が更新されるかが追いづらくなります。 そこでミューテーションの出番です。

ミューテーションを定義する

Vuex のストアの状態を変更するためにミューテーションを定義します。
ミューテーションのドキュメント

// main.js
const store = new Vuex.Store({
  state: {
    msg: 'Hello Vuex Store.',
  },
  mutations: {
    updateMsg(state, newMsg) {
      state.msg = newMsg;
    },
  },
});

この時 updateMsg をタイプ、定義された関数をハンドラと呼びます。updateMsg というイベントを登録するようなものです。 これを起動するには store.commit('updateMsg') を呼び出す必要があります。
air-bnb のESLintルールにしているとエラーがでるので .eslintrc.js"no-param-reassign": 0 を追加します。

// components/Sample.vue
methods: {
  update() {
    // this.$store.state.msg = this.newMsg;
    this.$store.commit('updateMsg', this.newMsg);
  },
},

タイプに定数を使う

updateMsg のようなタイプに定数を使うのがVuexのスタンダードなのでそれにならいましょう。

// main.js
const UPDATE_MESSAGE = 'UPDATE_MESSAGE';

const store = new Vuex.Store({
  state: {
    msg: 'Hello Vuex Store.',
  },
  mutations: {
    [UPDATE_MESSAGE](state, newMsg) {
      state.msg = newMsg;
    },
  },
});

こうなると、commit する箇所でも同じ定数を使う必要になるので外部ファイルに定義をしましょう。 store/mutation-types.js を作成し底定数を定義します。 main.js ではそのファイルを import します。

// store/mutation-types.js
export const UPDATE_MESSAGE = 'UPDATE_MESSAGE';
export const DUMMY_TYPE = 'DUMMY_TYPE';  // ESLint対策、exportが1つだけだとErrorがでます
// main.js
import * as types from './store/mutation-types';

mutations: {
  [types.UPDATE_MESSAGE](state, newMsg) {
    state.msg = newMsg;
  },
},
// components/Sample.vue
import * as types from '@/store/mutation-types';

methods: {
  update() {
    this.$store.commit(types.UPDATE_MESSAGE, this.newMsg);
  },
},

アクションを利用する

アクションはミューテーションと似ていますが、状態を変更しない点が異なります。 APIコールなどの非同期処理を含むことができ、ミューテーションをコミットすることで状態を変更します。 文字列を繰り返すアクションを登録してみましょう。

// main.js
const store = new Vuex.Store({
  actions: {
    repeat(context) {
      let msg = context.state.msg
      context.commit(types.UPDATE_MESSAGE, `${msg} ${msg}`)
    },
  }
});

context.state で状態にアクセスし、 context.commit を呼び出すことでミューテーションにコミットできます。

アクションのディスパッチ

アクションは store.dispatch により実行されます。 「繰り返す」ボタンをクリックされた時にアクションをディスパッチします。 すると repeat アクションが実行され、表示が更新されます。

// components/Sample.vue
<button @click="repeat">繰り返す</button>

methods: {
  repeat() {
    this.$store.dispatch('repeat')
  },
},

これでVuexを使った一連のコードを実装することできました。

ファイルの分割とモジュール化

main.js に実装したVuexのコードを専用のファイルに分割しましょう。 store/index.js を作成しそちらにコードを移します。 ミューテーションやアクションもそれぞれ別の変数で管理します。

// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import * as types from './mutation-types';

Vue.use(Vuex);

const state = {
  msg: 'Hello Vuex Store.',
};

const mutations = {
  [types.UPDATE_MESSAGE](state, newMsg) {
    state.msg = newMsg;
  },
};

const actions = {
  repeat(context) {
    let msg = context.state.msg;
    context.commit(types.UPDATE_MESSAGE, `${msg} ${msg}`);
  },
};

export default new Vuex.Store({
  state,
  mutations,
  actions,
});
// main.js
import store from './store';

new Vue({
  store,
  el: '#app',
  router,
  template: '<App/>',
  components: { App },
});

機能ごとにファイルが別れて管理しやすくなりました。これでVuexの基本的な機能を使って実装することができました!

アプリケーションの構成

ファイル構成についてはアプリケーションの構成で言及されこのようになっています。

├── index.html
├── main.js
├── api
│   └── ... # API 呼び出しを抽象化する
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # モジュールを集めてストアをエクスポートする
    ├── actions.js        # アクションのルートファイル
    ├── mutations.js      # ミューテーションのルートファイル
    └── modules
        ├── cart.js       # cart モジュール
        └── products.js   # products モジュール

サンプルコードを読んでスタンダードを知る

最後にVuexを使ったプログラムのスタンダードな書き方についてです。 Vuexのリポジトリではサンプルコードが公開されています。 shopping-cartのサンプルは上記のようなアプリケーション構成になっていてとても参考になります。

store の構成に注目すると、問題領域ごとにmodule化され、アクションやミューテーションが定義されています。 また、ミューテーションを区別するミューテーションタイプは一つのファイルに定数として保存され、重複しないようになっています。

最後に

はじめてアドベントカレンダーに参加しましたが、なんとか書き終えてホッとしています。 この記事で使用したサンプルコードはこちらのリポジトリです。 ESLintの設定にAir-bnbを指定しましたが、いくつかの設定でエラーとなってしまったので、webpackの設定をおすすめします。

Hatenaランチに行ってきた

Hatenaランチに行ってきた

【京都開催 feat.はてな】Cookpad Tech Kitchen #11 に参加した時に書いたアンケートがきっかけでお声がけいただいた。 カジュアル面談ということで、ランチをしながら一時間いろいろ話をしてきた。 アンケートはちゃんと書いたほうがいい。

カジュアル面談は過去にも何度かしたことがある。
Wantedlyや転職ドラフトがきっかけで、ランチをしながらとかSkypeでの面談などだ。
カジュアル面談は気楽でいいのだけど、就職の面接とちがってこちらに強い動機がない。
おそらく相手も少し興味をもっている程度なので、うまく話をできなかったこともある。
そんな時には時間を作ってもらったのに申し訳ない気持ちになる。
一方で少しずつ自分の持ってる考え方だとか、仕事で何を大事にしているかなどが見えてくることに繋がった。

ここ数年、自分は主夫だと謳いながらパートタイムでエンジニアの仕事をしているけれど、その中で「仕事の密度」のことを考えるようになってきた。
家事・育児をしながら働こうと思うと、どうしても働ける時間や時間帯に制限がでてくる。 限られた時間の中で成果をだして、やりがいなどの満足感を得るためには、無駄なことはやってられない。 意味のない慣例や慣習で時間を使っていては成果はでないし、満足感も得られない。 そうなると常にいろんな物事を改善して生産性を高めようというマインドが生まれてくる。

そして、それが出来るチームや組織で働きたいと思うようになったし、そういう組織はそれを良しとするマネージャーやリーダーがいないと生まれないだろう、と考えるようになっていった。 短い時間で成果をだし満足感を得るには、密度の濃い仕事をしないといけない。

なので今回は事前にそのあたりのことを整理してから面談に向かった。

はなしたこ

人事の方に案内され日替わりのランチを取りに行き、面談ではCTOの方とチーフエンジニアの方2人に対応いただいた。 和やかな雰囲気ではなしができた。自己紹介から始まり自身のことを簡単に話してからチームや組織のことを伺った。

チームや組織が目指すことについて

サービスやプロダクトが好きで愛情があってインターネットが好きでエンジニアリングが好き。
インターネットやWebの世界に貢献したいと思っている。
そして誠実な人。

という人たちを集めているとのこと。

だからチームごとにカラーが違ったりやり方が違ったりするけど、
向いている方向が同じだから、みんないろんなことを改善しようとするし、
別チームでうまくいったやり方を取り入れたりするし、
自己組織化されていっている。
なので会社や組織として目指すチーム像みたいなものはないけど、いい感じにチームはできていってる。

リーダーがトップダウン的にチームを作っていくもんだとおもっていたけど、エンジニアを信頼しボトムアップでチームが作られていっているようだ。考え方が逆で驚いたし、そういうことができるのってすごいと思う。

その他、社内での勉強会の話やサブグループの話などを聞いて 技術をベースにビジネスや組織を作っている印象を受けたし、 なによりエンジニアを資産として捉えて会社を作っているんだなと感じた。

いいよね、こういう会社。ほんとにいいと思う。

コミュニティ活動について

コミュニティ活動について聞かれたので、ここ1年の活動とその動機あたりを話した。
RubyKaigi2016への参加がきっかけでコミュニティ活動を始めたことや、OSS-Gateのことについての話をした。
OSS開発に参加する「入り口」を提供する取り組みについて共感してもらえたし、組織の長たる人とそういう部分で共感出来るのは嬉しいことだ。

いろいろなものがうまく揃ったらHatena開催も夢じゃないかも。

面談を終えて

会社の雰囲気やアイデンティティみたいなものに触れて、素直にいい会社だなぁと思った。
純粋にこういう環境で働いてみたいと思えた。
そしてHatenaという会社やプロダクトを、中の人を知ることでより一層好きになった。

最後に

対応いただいたスタッフの方、ありがとうございました。

Hatenaのまかないランチはおいしいよ!!

OSS Gate Workshop を開催するためにやったこと

先日 OSS GateWorkshop を懇意にしているスプーキーズという企業で社内向けに開催しました。 この記事では開催にあたり行った準備や参考にした情報などをまとめています。 OSS GateWorkshopに参加した人が、これから同じように社内向けに開催したり、 運営のメンバーになって進行役をやったりする時に役に立つと幸いです。

開催の様子についてはこちらを御覧ください。

labs.spookies.co.jp

筆者とOSS Gateとの関わり

私はOss Gate Workshopw@大坂(2017-02-25)にビギナーとして参加しました。 WorkshopでIssueを起票しOSS開発者の仲間入りをし、その後も継続してIssueの起票やPullRequestを行っています。 体験としてとても有意義なものとなったので、それをスプーキーズの勉強会にて発表しました。 ドキュメントをちょっと良くした程度のただのOSSの開発経験者です。 また、継続して交流を持つためにGitterに参加しています。

開催の日程

通常のWorkshopは5〜6時間のスケジュールで構成されています。 今回は社内向けの開催ということで就業後に自由参加で行っています。 そのため1度にそれだけの時間を確保するのは難しいため2回に分けて開催しました。

1日目では対象OSSを動かしてミニふりかえりを行ない、2日目ではそのOSSに対しフィードバックを行ないました。 タイムテーブルは以下のように構成しています。両日とも150分での構成です。

1日目

時刻 やること
19:00 開始
19:10 OSS Gate の紹介
19:20 アイスブレイク
19:35 OSS開発手順を説明
20:00 対象OSSを動かす
21:00 ミニふりかえり
21:30 終了

2日目

時刻 やること
19:00 開始
前回のおさらい
19:10 プロジェクトにフィードバックする
20:40 まとめ
20:50 アンケート記入
21:00 ワークショップのふりかえり
21:30 終了

進行するための準備

OSS Gate の目的を確認

OSS Gate を冠して開催するには本家の目的を理解しそれに沿った形で実施する必要があります。 これについてはオフィシャルサイトにOSS Gateへようこそ!というページがあるので熟読しました。 これにより自身の「Workshop楽しかったな、同じ思いをする人が増えるといいな」という思いも蘇ります。

過去のレポートを確認

Gitterで開催の旨を伝えたところこちらのレポートを紹介しもらいました。

oss-gate.github.io

同じように社内向けの開催でのレポートでした。こちらに目を通すことで全体の流れを再確認することができます。

進行の動画を確認

開催するためには自分が進行役を務める必要があります。 これが一番大変な部分ですし、成否がかかっているところでもあります。 なのでここに一番労力を使いました。

幸いYouTubeOss Gate Workshopw@大坂(2017-02-25)の動画が公開されています。 今回はこちらの進行を真似しました。

www.youtube.com

動画をしっかり見ることも大事ですが、原稿があったほうが安心なので話していることを文字に書き起こしました。 10分の動画を書き起こすのに約1時間かかりましたが、そのおかげで話す内容が頭に入った感じがします。

書き起こしたテキストはこちらにあります。

使用するリポジトリ

本家のリポジトリをWorkshop参加時にクローンしているのでそれを使いました。 本家同様にIssueを起票するとデフォルトで入力されているので同じように使えます。 Workshopのふりかえりではアンケートを実施しますが、その結果を本家のリポジトリPullRequestすることを想定していたので、それ用のブランチを用意しました。

使用するスライド

OSS Gate Workshopで使用するスライドは公開されておりいくつかの方法で利用できます。

当日の進行はGemのスライドで行ないました。こちらのようなGemfileを用意してインストールして使用しました。 状況に合わせてrabbit Slide Showのスライドも使用しています。

読んでおけばよかったもの

本家のリポジトリで用意されているシナリオには開催・進行するためのいろいろな情報がまとめられています。 これを見落としていたのはもったいないことでした。

メンバー構成

ビギナーに対してサポーターが少ない状況での開催となってしまいました。 1日目は何もわからない状態のビギナーにいろいろと作業をしてもらう必要があります。 そのためサポーターの対応が不十分なためうまくいかないこともありました。 それについては後述します。

一方2日目は全員に慣れた感じもありビギナー、サポーター共に楽しめたようです。 初参加のビギナーもいましたが、うまくサポートをして後追いで作業をしてもらえました。

1日目

  • 進行役(兼サポーター) 1名
  • サポーター 1名
  • ビギナー 6名

2日目

  • 進行役(兼サポーター) 1名
  • サポーター 1名
  • ビギナー(連続参加) 2名
  • ビギナー(初参加) 2名

Workshop のふりかえり

うまくいかなかったこと

OSS選定に時間がかかった、OSS選定がうまくいかなかたった

新卒社員のビギナーはまだOSSに馴染みが薄く「OSSってどんなもの?」という感じでした。 そのため選定に時間がかり、しかも動かしづらいOSSを選んでしまいました。 サポーターが少ないことが一因ではありますが事前に対策はできそうです。

ビギナーに合わせてサポートを優先的に行う

社内開催なので事前にだれにサポートが優先的に必要かが分かったように思います。

事前に OSS Gate の紹介だけを行う

事前に「対象OSSは何にしようかな」と考える時間を作ることもできそうです。

作業ログがうまく書けていない、少ない

通常のWorkshopのIssueとくらべて作業ログのコメント数が少なく感じました。 作業ログは多いほうが後のミニふりかえりがうまくいきやすいのでたくさん書いて欲しいです。 これも以下に対策。

デモの発表を充実させる

動画に倣い、ビギナーを指名して寸劇形式で作業ログの説明を行いましたが、うまくいきませんでした。 よく知っている間柄なので緊張感のないやりとりになり、うまく説明できません。 寸劇をするなら台本ありきでサポーターと演じたほうが良さそうです。 事前に伝えたいポイントを明確にしておかないと伝わらないと思いました。 下手な芝居よりきちんと説明を行ったほうが良かったです。

サポーターが少ない

アルバイトのメンバーがOSS開発者だったのでサポーターをしてもらえましたが、彼がいなければサポーター0名でした。 外部からサポーターを募っても良かったです。

座席がバラバラ

会議室などのスペースがないのでオフィススペースでの開催になりました。 そのためビギナーの横にサポーターが座るという体制は取れず、これもサポート不足の一因になっていたと思います。

途中で仕事をしてしまう

オフィススペースで行うと、周りには仕事中の人もいます。 プロジェクトの状態によってはWorkshopに集中できず仕事をしてしまう人もいました。 Workshopに集中できるように出来る限り隔離された空間で開催すべきだと思います。

仕事の都合で片方だけの参加になった

今回のケースでは1日目だけの参加者が不完全になってしまいました。 OSS開発参加へのハードルを下げることもできなかったような印象です。 一方で2日目だけの参加者はIssue起票までできたので、取り組みとしてはうまく行ったと思います。

社内開催なので今後はお互いにサポートしながらWorkshopの続きをできる可能性はありますが、本人たちの意志次第です。 事前に片方しか参加できないのが分かっているなら2日目を推したほうが良いかもしれません。

英語のサポートを強化したい

コミットメッセージの英語についてはこれとかこれのようなまとめがありますが、IssueやPullRequestの英語についてはあまりないようです。 人気リポジトリのIssueやPullRequestを巡回してまとめると人気者になれるチャンス!?

ビギナー同士のミニふりかえりは難しい

サポーターが少ないため、ミニふりかえりを1対1で行うことが難しいのは事前にわかっていました。 そこでビギナーをペアにしてそこにサポーターを付ける形でミニふりかえりを行いました。 サポーターがペアの1人とフィードバックポイントを探し、2人目はビギナー同士でフィードバックポイントを探す、というものです。 しかし、経験がないビギナー2人でフィードバックポイントを探すことは難しかったようです。

やはり最低限のサポートが必要だとわかりました。 なのでペアそれぞれのフィードバックポイントを1つずつ見つけるところまでをサポートし、それ以降はペアで議論しながらさらなるフィードバックポイントを見つける、という方法だとうまくいくかもしれません。 これは次回もチャレンジしたいことです。

うまくいったこと

普段とちがうコミュニケーションの形

サポーターがアルバイトメンバーということもあり、普段とは立場が逆転した光景がみられました。 アルバイトが役員に対しIssueの書き方をレクチャーするなど、新しいコミュニケーションが生まれていました。 後輩や新人が先輩をサポートする、というような光景は他の現場でも見られそうです。

社内のメンバーのことを知るきっかけの1つになった

OSSを選ぶところや作業ログの内容に個性がでるようで、普段とは違う面を見ることができました。 作業の合間の雑談で会話が弾んだりと、メンバー同士のことを知るいい機会になっていました。

継続したOSS開発参加ができそう

OSSの日」というのを定期的に開催して、継続してOSSへ貢献しようという話が出ました。 こういう話題も社内開催だから出るのかな、と思います。今後にも期待したいところです。

GitHubを使うよい機会になった

普段はGitLabを使って開発を行っているので、ほとんどGitHubを使ったことのないメンバーもいました。 そのためGitHubを使う良い機会にもなりましたし、GitHubの使いやすさを肌身に感じたようです。

OSSに対する意識の変化

OSS開発への敷居は下げられたと同時に、OSSのIssueやPullRequestを見てみようという意識の変化も起こったようです。 こういう変化も今後のOSS開発参加への継続性には影響しそうです。

OSS開発参加者が増えた

これが一番の結果ですね。

個人的な感想・反省

進行で伝えるべき内容はもれなく話せたと思いますが、スライドの内容を全然把握してなかったので、話とスライドがズレズレだったところはだめだったところです。 サポーターからのアンケートではちゃんと指摘してくれていました。 とはいえアンケートでは皆さん満足してもらえたようで、開催してよかったなと思います。こういうフィードバックがあるから勉強会やコミュニティの運営ってできるのかなと思いました。

今後について募集

今回は社内開催でしたが今後はオープンなWorkshopとして京都で継続的に開催したいと思っています。 会場の提供やサポーターの参加など協力してくれる方を募集しています。 今回と同じように社内開催をしてほしいというご相談もあれば賜ります。

TwitterやBlogのコメントでご連絡ください!

twitter.com

スプーキーズの開発合宿に参加してきた

3月下旬に淡路島で行われたスプーキーズの開発合宿に参加してきました。 合宿所はこちらのLa Terrasse Awajiで、とても広くて美しい素晴らしい施設でした。

合宿の目的はこちらの2つ。

合宿を通じてメンバー同士が普段とは違ったコミュニケーションをとり親睦を深めること、その中で生まれる発想や試行錯誤をボードゲームという成果に落とし込むことの2つを目指しての2泊3日でした。

とまあ、合宿の詳しい内容はオフィシャルブログに譲るとして合宿の様子を写真中心で紹介しようと思います。

合宿所に到着。きれいなエントランスです。

f:id:masayuki14:20170401115011j:plain

早速メンバーが集まって今回の趣旨の説明。

f:id:masayuki14:20170331152049j:plain

自己紹介をして和やかなムード。

f:id:masayuki14:20170331153023j:plain

僕はガジェットが好きなんですよ。

f:id:masayuki14:20170331152759j:plain

僕はロクロを回すんですよ。

f:id:masayuki14:20170331153234j:plain

4人✕3チームに分かれてボードゲームを作りますよ!

f:id:masayuki14:20170331154042j:plain

f:id:masayuki14:20170331154414j:plain

さて、どうしようか。

f:id:masayuki14:20170331161435j:plain

コマ使うやつとか。

f:id:masayuki14:20170331180652j:plain

f:id:masayuki14:20170331173533j:plain

議論も白熱します。

f:id:masayuki14:20170331161505j:plain

f:id:masayuki14:20170331160412j:plain

とりあえずボードゲームで遊びますか。

f:id:masayuki14:20170331155845j:plain

えー、まじでー。

f:id:masayuki14:20170331161531j:plain

山盛りいちごで休憩。

f:id:masayuki14:20170331163734j:plain

薪がなくて使えなかった暖炉。

f:id:masayuki14:20170331163039j:plain

外はあいにくの雨模様。

f:id:masayuki14:20170331161727j:plain

廊下の奥には

f:id:masayuki14:20170331173712j:plain

きれいなベッドルーム。

f:id:masayuki14:20170331173826j:plain

ちょっと1人で考え中。

f:id:masayuki14:20170331180643j:plain

そろそろ夕食に行きますよ〜。

f:id:masayuki14:20170331174038j:plain

なんだか楽しそう。

f:id:masayuki14:20170331193842j:plain

淡路の海の幸、山の幸

f:id:masayuki14:20170331194747j:plain

f:id:masayuki14:20170331200536j:plain

夜が更けても煮詰まらない!

f:id:masayuki14:20170331220636j:plain

やっと形になってきたか!?

f:id:masayuki14:20170401004659j:plain

我々はもう出来たのでカタンで遊びますよ。

f:id:masayuki14:20170331234521j:plain

翌朝チームごとに制作したゲームを発表してテストプレイ。

f:id:masayuki14:20170401112008j:plain

プレゼンを聞く方も真剣。

f:id:masayuki14:20170401113024j:plain

コマを使ったパズルゲーム!!

f:id:masayuki14:20170401112721j:plain

頭をつかうゲームなのか!?

f:id:masayuki14:20170401113750j:plain

f:id:masayuki14:20170401113913j:plain

こちらのチームはアイドルがモチーフのよう。

f:id:masayuki14:20170401115743j:plain

f:id:masayuki14:20170401115247j:plain

次のチームはカードとチップを使ったゲーム

f:id:masayuki14:20170401124517j:plain

絵のクオリティが。

f:id:masayuki14:20170401123435j:plain

盛り上がってます!

f:id:masayuki14:20170401125543j:plain

ここで第一部が終了。 各人がおもしろいと思ったボードゲームに投票し、優勝チームにはご褒美となりました。

f:id:masayuki14:20170401131359j:plain

お昼休みを挟んでチーム替え。3人✕4チームに分かれて第二部の開始!

f:id:masayuki14:20170401151857j:plain

f:id:masayuki14:20170401151926j:plain

f:id:masayuki14:20170401151939j:plain

夜はデッキでバーベキュー!

f:id:masayuki14:20170401202656j:plain

食後に最後のプレゼンです!

f:id:masayuki14:20170401215725j:plain

盛り上がってますね。

f:id:masayuki14:20170401221125j:plain

f:id:masayuki14:20170401221143j:plain

第二部はカードゲームが多いようです。

f:id:masayuki14:20170401223238j:plain

f:id:masayuki14:20170401223551j:plain

f:id:masayuki14:20170401224857j:plain

優勝おめでとう! このあと夜遅くまで宴会&ボードゲームパーティーが続くのでした。

f:id:masayuki14:20170401231552j:plain

合宿おつかれさまでした!

f:id:masayuki14:20170402101928j:plain

Bash初心者から初級者へのステップアップするためのTips10選

CLIでいろいろとコマンドは使っているんだけど、bashスクリプトが書けるかというと・・・。 という人がbashの文法や機能を知って初級者へとレベルアップするためのTipsを紹介します。

1. ${変数}

シェルで変数を保持することができます。変数には文字列や数値を代入することができ、値を参照する場合は変数名の前に$を付けます。変数値は $VAL でも ${VAL} でも同じように参照出来ますが ${} の方が可読性も上がるのでおすすめです。

$ A=10
$ echo ${A}
10

変数に代入する時 = の前後にスペースを入れてはいけません。変数がコマンドとして解釈されてしまいます。

$ B = 10
bash: B: command not found

bashのバージョン4では ${VAL^} で単語の先頭を大文字に、 ${VAL^^} で単語全体を大文字に変換してくれます。

$ A='hello world'
$ echo ${A}
hello world
$ echo ${A^}
Hello world
$ echo ${A^^}
HELLO WORLD

変数の値は基本的にテキストとして扱われますので一般的なプログラミング言語のように四則演算はできません。

$ A=10
$ B=15
$ echo ${A} + ${B}
10 + 15

この様な場合は後述する算術式展開 $(( 計算式 )) を使います。

2. while read n; do [command] $n; done

パイプから受け取った標準入力を1つずつ変数に保持して引数として別のコマンドに渡したい。そんな時によく使う方法が while read ... のイディオムです。 X.txt Y.txt をそれぞれ X.txt.bak Y.txt.bak との差分をみたい場合、ワンライナー(1行スクリプト)で書くとこのようになります。

$ ls *.txt | while read f ; do diff ${f} ${f}.bak; done

# 通常の書き方
$ ls *.txt |
while read f
do
  diff ${f} ${f}.bak
done

ls の出力を1行ずつ読み込み変数 f に保持し diff コマンドの引数として与えています。 羊を100匹数える場合はこのようになります。

$ seq 100 | while read n; do sleep 1 ; echo "羊が${n}"; done

3. $(( 計算式 )) 算術式展開

$(( 計算式 )) は算術式展開と呼ばれるbashの機能でいろいろな計算ができます。四則演算はもちろんのこと論理積論理和などのビット演算もできます。

# 四則演算
$ A=10
$ B=15
$ echo $(( ${A} + ${B} ))
25
$ echo $(( ${A} * ${B} ))
150

# べき乗
$ echo $(( ${A} ** ${B} ))
1000000000000000
$ echo $(( 2 ** 16 ))
65536

# modulo
$ M=$(( ${A} % ${B} ))
$ echo ${M}
10
$ echo $(( 13 % 8 ))
5

# 論理積
$ echo $(( 15 & 4 )) # 1111 と 0100 の論理積
4

# 論理和
$ echo $(( 10 | 5 )) # 1010 と 0101 の論理和
15

# 排他的論理和
$ echo $(( 13 ^ 11 )) # 1101 と 1011 の排他的論理和
6

4. || && 終了ステータス判定

コマンドはその動作が成功すると「終了ステータス」として0を返します。失敗すると0以外の数字が返ります。直前のコマンドの終了のステータスは $? 変数に保持されます。

# wget コマンドの例
$ wget 'http://google.co.jp' 2> /dev/null
$ echo ${?}
0
$ wget 'http://foo.bar.baz.co.jp' 2> /dev/null
$ echo ${?}
4

0は成功のステータス、 4は失敗のステータスで Network failure. の意味です。 2> /dev/null標準エラー出力を捨てて表示されないようにしています。

&&|| はこの終了ステータスを判定するbash演算子です。&& で成功した時の処理、|| で失敗した時の処理を書きます。

$ wget 'http://google.co.jp' 2> /dev/null && echo 'success!' || echo 'failure...'
success!
$ wget 'http://foo.bar.baz.co.jp' 2> /dev/null && echo 'success!' || echo 'failure...'
failure...

gerp は何かがパターンマッチすれば0を、何もマッチしなければ1を返します。そこで || 演算子を使うと処理を分岐することができます。

$ ls -aF | grep 'git' || echo 'no git file or dir'
no git file or dir


$  ls -aF | grep 'git' || echo 'no git file or dir'
.git/
.gitignore

5. -- オプションの打ち止め

-Rf~ というようなディレクトリが何かの拍子にできてしまいまった場合どうすればよいでしょうか。

$ ls -F
-Rf -f/ ~/

まちがっても $ rm -Rf ~ なんてコマンドを打ってはいけません。 -Rf はオプションとして解釈されてしまいます。このような場合は -- を使います。-- は特別な引数でそれ以降のオプションが打ち止めとなります。

$ rm -- -Rf
$ rmdif -- -f

~ は通常ホームディレクトリと解釈さるので、 -- を利用しても意味がありません。シングルクォートでくくる、\ でエスケープする、./ でカレントディレクトリを明示するなどが必要です。

$ rm '~'
$ rm \~
$ rm ./~

6. ヒアドキュメント

複数行のテキストをプロセスの入力にします。 << 終了文字列 を記述し複数行の文字列を入力後に 終了文字列 を記載すると入力終了の合図となります。

$ cat -n << EOF
I have a pen.
I have a apple.
EOF
     1 I have a pen.
     2 I have a apple.

標準入力に流し込むので echo ではなく cat で出力しています。この出力をリダイレクトやパイプに渡す場合は、1行目に書く必要があります。

# 出力をリダイレクト
$ cat << EOF > output.txt
I have a pen.
I have a apple.
EOF

$ cat output.txt 
I have a pen.
I have a apple.
# パイプで渡す
$ cat << EOF | jq
[{
"name":"Matz","age":51,"from":"Japan"},
{"name":"Dhh","age":36,"from":"Denmark"}
]
EOF
[
  {
    "name": "Matz",
    "age": 29,
    "from": "Japan"
  },
  {
    "name": "Dhh",
    "age": 36,
    "from": "Denmark"
  }
]

標準入力に流し込むので while イディオムの入力としても利用できます。

$ while read n; do echo $(( ${n} * 2 )); done << EOF
1
2
3
4
5
EOF
2
4
6
8
10

7. <<< ヒアストリング

<<< の右側に書いた文字列や変数の値をコマンドの標準入力に流し込みます。 ヒアドキュメントと同じように扱えます。パイプとリダイレクトも通常通り利用できます。

$ cat <<< 'Hello World!!'
Hello World!!

$ JSON='[{
"name":"Matz","age":51,"from":"Japan"},
{"name":"Dhh","age":36,"from":"Denmark"}
]'

# パイプ |
$ jq . <<< ${JSON}} | grep -i 'matz'
    "name": "Matz",

# リダイレクト >
$ jq .[].name <<< ${JSON} > name.txt
$ cat name.txt
"Matz"
"Dhh"

8. $( コマンド ) コマンド置換

コマンドの実行結果を文字列として利用するための機能です。 コマンドの引数として展開したり引数に保持したりします。

# dateコマンドの結果が文字列に展開される
$ JSON="{\"time\":\"$(date +%s)\"}"
$ echo $JSON
{"time":"1489992386"}

# yamlファイルだけを抽出して処理
$ for f in $( find . -type f | grep '\.yml$' )
do
    [command] ${f} # 何らかの処理
done

9. $(< ファイル )コマンド置換

コマンド置換の拡張的な機能でファイルの中身に置き換えられます。

# cpコマンドを使わずbashの機能だけでファイルをコピー
$ echo "$(< /tmp/foo )" > /tmp/foo.bak

コマンドの処理結果を一時ファイルに保持することで可読性が向上します。

$ find . -type f | grep '\.yml$' > /tmp/yaml.list.txt
$ for f in $(< /tmp/yaml.list.txt )
do
    [command] ${f} # 何らかの処理
done

10. <( コマンド ) プロセス置換

コマンドの入出力をファイルとして扱う機能です。引数にファイルを受け取るコマンドの入力として利用できます。

# cat は引数としてファイルを受け取る
$ cat -n <( echo 'Hello World' ) <( echo 'Dreams come true' )
     1 Hello World
     2 Dreams come true
# ファイルリストの差分を diff で得る
$ ls
a.txt      c.txt      e.txt      h.txt       j.txt      l.txt      o.txt      q.txt
a.txt.bak  c.txt.bak  f.txt      i.txt       j.txt.bak  m.txt      o.txt.bak  q.txt.bak
b.txt      d.txt      g.txt      i.txt.bak   k.txt      n.txt      p.txt      r.txt
b.txt.bak  d.txt.bak  g.txt.bak  index.html  k.txt.bak  n.txt.bak  p.txt.bak  r.txt.bak

$ diff <( ls | grep '.txt$' | cut -d. -f1 ) <( ls | grep '.txt.bak$' | cut -d. -f1 )
5,6d4
< e
< f
8d5
< h
12,13d8
< l
< m

シェルプログラミング実用テクニック 入門bash 第3版 ソフトウェアデザイン 2016年 06 月号 ソフトウェアデザイン 2017年 01 月号

2017年の目標

年始からいろいろと考えていたことをまとめるためにも今年の目標を書き出します。

セルフブランディング

Rubykaigi2016に参加したことで改めて個人として活躍するには必要なことだと感じた。 まずは情熱プログラマーにもあるように「Googleで検索し最初の4ページのリンクだけを見てどんな人物だと想像するだろうか」という部分に注視して見ようと思う。 情熱プログラマーは改めて読むとほんとに良い本だ。

コミュニティでの登壇

勉強会やカンファレンスで登壇する。 3年後にはRubykaigiに登壇することを目指す。 オリンピックイヤーか・・・。

ブログ

Qiitaも含めてアウトプットを増やす。 最低でも月1回は書く。

OSSにコミット

GitHubでPullRequestを送る。 今年こそ!!

シェルプログラミング

シェル芸から入って bash が面白くなってきたのでシェルプログラミングでいろいろ出来るようになる。

Elixir

途中で止まってしまったプログラミングElixirをちゃんと読む。 演習問題もちゃんとやって手を動かす。

運用・監視まわり

Mackerelで独自のメトリクスを取りたい、という課題があるのでちゃんと勉強して達成する。 あわせてFuluentdとか使ってログ解析も出来るような基盤を考える。

新春bash書き初めをやった

SoftwareDesignの2017/1号の第1特集である新春bash書き初め シェル30本ノックをやった。
結果は 19/30 でまぁまぁの出来だと思う。
コマンドやbashについていろいろと新しい発見があったのでまとめておく。

各問題の自分なりの回答は GitHub においてある。
雑誌はこちら

コマンドについて

sed awk

抽出や変換など知れば知るほどなんでもできそうなコマンド。

  • sed -n '/開始パターン/,/終了パターン/p'
  • sed 'p' | sed '1d;$d' | paste
  • awk '{print}{fflush()}'

while read n; do [command] $n; done

繰り返しのイディオムで頻出。xargs とうまく使い分けよう。

openssl

暗号関連のいろんなことができるコマンド。使うことはほとんどなさそう。

crontab

コマンドというか @reboot コマンド起動時に1度、指定のコマンドを実行するという設定ができることを知った。

grep

grep -q . とすると、マッチの有無で終了ステータスが変わるので処理を分岐できる。
$ (何らかの処理) | grep -q . && (出力がある場合の処理) || (出力がない場合の処理) と出来る。
-q は標準出力に何も出さないオプション。

paste

それぞれのファイルの行を並列に結合する。

$ paste file1 file2
file1の1行目 file2の1行目
file1の2行目 file2の2行目
...

標準入力も扱える。

$ seq 1 10 | paste - -
1   2
3   4
5   6
7   8
9   10

file

ファイルの種類を推定してくれるコマンド。

tee

標準出力に出力しつつ、内容をファイルに保存する。
リダイレクトでは特権(sudo)が必要なファイルは書き込めないためteeを使うとうまくいく。
$ echo 'foo bar' | sudo tee special_file.txt

rev

反転してくれる。

$ seq 1 10 | xargs
1 2 3 4 5 6 7 8 9 10
$ seq 1 10 | xargs | rev
01 9 8 7 6 5 4 3 2 1

[

test コマンドの別名。if [ xxxx ]; でよく使われるのでオプションを覚えたほうが良い。

set env

set は現在のシェル変数、 env環境変数のみを出力する。

bashについて

${変数}

変数へ代入する時はスペースを入れてはいけない。
変数を使う時は$をつける。

$ n='hello world'
$ echo ${n}
hello world

|| && 終了ステータス判定

直前に実行したコマンド(処理)の終了ステータス($0)を判定するbash演算子
$ コマンド && コマンドが成功した時の処理 || コマンドが失敗した時の処理

<<< 標準入力へ流し込む

<<< の右側に書いた文字列や変数の値を標準入力に流し込む。

$ cat <<< 'hello world'
hello world
$ cat < 'hello world'
bash: no such file or directory: hello world

$(( 計算式 )) 算術式展開

いろいろな計算ができる。四則演算はもちろんのこと、論理和論理積、ビット演算も可能。

$ echo $(( 2 * 3 + 4 ))
10

$( コマンド ) コマンド置換

コマンドの実行結果を文字列として扱える。

$ for n in $( seq 1 5 ); do echo "羊が$n匹"; done
羊が1匹
羊が2匹
羊が3匹
羊が4匹
羊が5匹

:- 変数展開

難しいのでこちら
デフォルト値への置換ができる。

--

オプションの打ち止め。 $ mkdir -- -RF-RFというディレクトリが作成される。--以降はオプションと認識されなくなる。

$(<ファイル) コマンド置換2

コマンド置換の拡張的な機能で「ファイル」の中身に置き換えられる。

$ echo "$(<ファイル)" > foo$ cp ファイル foo と同じ。

<( コマンド ) プロセス置換

コマンドの入出力をファイルとして扱うことが出来る。

$ cat < <(echo 'hello world')
hello world

さいごに

各コマンドやbashの詳しい使い方や実例は雑誌を読んでくれ。

SoftwareDesign2017.01