もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

自分用 MacBookセットアップメモ

転職して新たなMacBookが支給されたけど、セットアップするときにあれ何入れてたっけ?となったので自分用のメモ。(USキーボード向け)

英数/かな切り替え

  • Google IMEを入れる
  • Karabiner-Elementsを入れる
    • Complex ModificationsからJP向けのruleを入れてコマンドキーで切り替えるルールを有効にする

Homebrew入れる

shell

  • Login Shellをzshにしよう。ついでに下のどちらかを入れるとよい。

パッケージ管理

  • brewコマンドで必要なものを入れるようにしておくためにとりあえずぶっこむ。
  • homebrew
  • home-brew-cask

iTerm2

brew cask install iterm2
  • iTerm2でプロフィールを新しく作成してデフォルトにしておく。
  • プロフィールのWindowタブを開いて、styleを Top of ScreenにRowsを40にしておく。iTerm2を開くとモニター上段が表示領域になる

tmux

  • ターミナルを多重化するツール
brew install tmux
  • tmux plugin用にtpmを入れる

github.com

  • iTerm2のプロフィールのKeysタブを開いて追加する。Send Hex CodeでiTerm2からtmuxにキーを送れるようにする。
ショートカットキー HexCode 動作 tmux のコマンド
Ctrl Shift ] 0×2 0x6e 次のWindowに移動 Ctrl b n
Ctrl Shift [ 0x2 0x70 前のWindowに移動 Ctrl b p
Ctrl N 0x2 0x63 新しいWindowを開く Ctrl b c
Ctrl Shift % 0x2 0x25 Windowを左右分割 Ctrl b %
Ctrl ] 0×2 0x6f 次のPaneに移動 Ctrl b o

ほかいろいろ

brew install go
brew install peco
brew install ctags
brew install nodebrew
brew install rbenv
brew install elixir
brew cask install java
なんかほかあれば

エディタ

brew cask install atom
  • 設定は自分の記事参考

rei19.hatenablog.com

  • sync-setting使ったけど職場のAtomとの共有は失敗した。。。なぜだ。

dot files

だいたいおっけーなはず

  • 何かあったらこの日記に追加していく。

飛騨の古民家を借りて開発合宿に行ってきた

目的

  • 非日常な空間にこもって新しい技術に挑戦したり、作ろうと思っていたけど普段はなかなか時間が・・・というようなものを集中して作る
    • もともと身内でプライベートでも何か作ったりするのが好きな人達が集まってわいわい開発するイベントだったり。今回は2回目で前職のつながりで技術顧問のNaoyaさん含む6人で参戦。

場所

www.satoyama-office.com

  • 飛騨古川駅より徒歩8分で駅からのアクセス抜群。まあ飛騨までが遠いんですけど。
  • 宿泊代は1軒あたりの金額なので参加人数が多いほど一人あたりの金額が安くなります。なお、布団代は別で一人3000円かかりました。

スケジュール

## 11/7(土)
9:00 東京駅集合
9:32 東京駅発
| かがやき507
11:46 富山駅着 昼食取る
13:02 富山駅発
14:17 飛騨古川着 古民家チェックイン
15:00 飲食物買い出し。
15:30 スケジュール確認して開発開始
19:00頃 出前で夕飯
23:00頃 初日は軽く宴。聖地で氷菓観るw

## 11/8(日)

食事以外は基本開発

## 11/9(月)

朝食後開発追い込みと発表準備
10:50頃 成果発表LT。5分から10分
11:30頃 LT終了。チェックアウト。開発はここまで。

12:00 〜 高山の方までいって軽く観光して名古屋回りで帰還

やったこと

  • 最近Kotlinをちょいちょいつまんで触っているので、KotlinでHBFavのAndroid版を再現してみるみたいなことやってました。
  • 完成まではまだ遠いけど進捗 rei-m/HBFav_kotlin · GitHub

行ってみて

  • ちょっと遠いかなと思ったけど、風情ある1軒屋を貸しきって開発するというのはとてもよい時間でした。周りも静かで超集中できます。他人が気になるなら余ってる部屋に行けば一人になれたり。
  • 開発合宿は最後にLTやるの大事です。限られた時間でデモできるものを作ってLTのスライドも作って発表すると達成感がすごい。
  • 場所遠かったのでもう一日くらい使っても良かった感。また来年も行きたいですね!

一休を退職しました

ご報告

  • 10月いっぱいで退職しますという話をし、本日が最終出社でした。社内外の方にはお世話になりました。ありがとうございました。気がつけば入社してから6年。。。6年前だとガラケサイトの開発からはじまり、最近だとネイティブアプリの開発とかいろいろやったなあという感じです。
  • 最近の一休は技術顧問としてNaoyaさんがジョインしてから組織改善プロジェクトがはじまり、すごい勢いでいろいろなことが変わっていきました。といっても技術顧問は銀の弾丸ではないので大事なのは自分たちが課題を認識して自分たちで変わろうとする意思を持つことかなと思います。最初の頃から携わってた身としてはよい経験ができました。ありがとうございました。

参考

【前編】CTO不在で、開発組織改善に着手! 一休のエンジニアが語る苦悩の1年 / 飲み会で探るエンジニアのホンネ #naoya_sushi 編

【後編】CTO不在で、開発組織改善に着手! 一休のエンジニアが語る苦悩の1年 / 飲み会で探るエンジニアのホンネ #naoya_sushi 編

今後

  • 2週間ほど休んだ後、表参道の方のベンチャーでまたエンジニアとしてがりがり作ります。新卒でSIerに入ってエンジニアのキャリアがはじまり、次にWebサービス作りたいなーと思い一休に入って、今回で3社目ですね。テクノロジーの力で世の中に面白いものを提供できるようにがんばります。
  • という感じであっさりですが身近な方々へのご報告でした。業界狭いのでまた勉強会とかで顔をあわせるかと思いますが、変わらずお付き合いくださいな。

雑兵MeetUp #1で「REVEAL.jsとMilkcocoaで双方向LTする」というLTをしてきました

  • Twitterのタイムライン上でイベントをみかけて、あまり見ないコンセプトの勉強会で面白いなーと思ったので盛り上げる側で参加してきました。主催の @yodatomato さん、会場を貸してくれた 21cafeさんありがとうございました。ほかの参加者の方々もお疲れさまでした。

イベント

zohyo.connpass.com

まとめ

自分のLT資料

コメント

  • 外でLTするのは超久しぶりだったので案の定、時間内には微妙に収まりきらず、この辺は場数が必要だなという印象ですね。継続して何かネタを投下していきたいと思います。
  • 発表してるときに気づいたのですが、流れるコメントにz-indexつけ忘れてたっぽくてスライドの画像の後ろにコメントが回ってしまっていました。Github上のコードは直しています。
  • 今回はゆるふわなネタで発表しましたが、スライドで使ったMilkcocoaは国産のBaasとしてはめっちゃ簡単に導入できてアイデアも広がるので知らない方はぜひ触ってみてほしいです。npmでNode.js用のパッケージも提供されてるので、IOTにNodeが動かせる環境が乗っていれば簡単にIOTで集めたデータをPublishすることもできます。IOTの入門としてはハードルは低めではないでしょうか。
  • LT後にMilkcocoaの中の人からTシャツをもらいました! @bakuonboogie さんありがとうございました!!

f:id:Rei19:20151030142946j:plain

Android StudioとKotlinでAndroidアプリを作る

Kotlinとは

導入

  • Android Studioは同じJetBrains製品のInteliJベースで作られているだけあって、プラグインを入れるだけで簡単に導入可能。Preferences -> pluginから以下の2つをインストールして再起動するだけです。なお自分の環境はAndroid Studio 1.4、compileSdkVersionは23、minSdkVersionは16にしてます。
    • Kotlin
    • Kotlin Extensions For Android

プロジェクトを作ってJavaのコードをKotlinに変換する

  • 適当にBlankActivityだけ追加したプロジェクトを作ります。そうするとこんなActivityが出来ますね。
package me.rei_m.kotlinsample;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
  • プロジェクトツリーにappがフォーカスされている状態でメニューバーからcodeを選ぶと一番下にConvert Java File to Kotlin Fileが追加されていているのでこれを選択します。すると、Kotlinへのコンバートが開始されます。
  • コンバートが完了するとMainActivityはこのように生まれ変わり、拡張子も.ktになります。ハイライト効かないのでみづらいですね。。。早速、valが見えているあたり、Scalaに近い雰囲気を感じます。
package me.rei_m.kotlinsample

import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.Menu
import android.view.MenuItem

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show()
            }
        })
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        val id = item.itemId

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true
        }

        return super.onOptionsItemSelected(item)
    }
}
  • 次にメニューのTools -> Kotlin -> Configure Kotlin Projectを選択してそのままOKを押します。

f:id:Rei19:20151017201814p:plain

  • するとbuild.gradleにKotlinでビルドするための設定が追加されるので、この状態でビルドをかけて実行します。

f:id:Rei19:20151017204542p:plain

  • 動きました!
  • 次にKotlin Extensionを使うための設定を追加します。build.gradleを開いてdependentciesにextensionsを追加します。
buildscript {
    ext.kotlin_version = '0.14.451'
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" <- これを追加
    }
}
  • そしてMainActivity.ktを開いてimportを追加します。syntheticから後ろはxmlの名前です。
import kotlinx.android.synthetic.activity_main.*
import kotlinx.android.synthetic.content_main.*
  • これで何ができるようになるかというとfindViewByIdを使わなくてもViewのコンポーネントを参照できるようになります。content_main.xmlTextViewのテキストをActivityから書き換えてみましょう。参照するためにはIDが必要なのでxmlを開いて適当にTextViewにidを追加します。ここではhelloとしています。
  • MainActivity.ktに戻り、findViewByIdを使っているところを書き換えてみましょう。
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // val toolbar = findViewById(R.id.toolbar) as Toolbar
        // setSupportActionBar(toolbar)
        setSupportActionBar(this.toolbar)

        // val fab = findViewById(R.id.fab) as FloatingActionButton
        // fab.setOnClickListener(object : View.OnClickListener {
        this.fab.setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show()
            }
        })

        // TextViewを書き換える処理を追加
        this.hello.text = "Hello Kotlin !!"
    }
  • 今までfindViewByIdを使っていたところはimportしたことでthisから参照できるようになりました。わかりやすくthisを書いてますが、もちろん無くても動きます。これでビルドして動かしてみましょう。TextViewの表示が変わったはずです。

f:id:Rei19:20151017210329p:plain

  • まだReferenceを読んでいる途中ですが、Javaで書くよりだいぶ簡潔に書ける印象です。しばらく触ってみて既存の資産の使い方など確かめながら何か作ろうかと思います。

追記

  • 早速書き始めたらいきなりExtensionが原因でこけた。。。下のようにViewに追加するとクラスが見つからないと言われて死にます。
    <AppCompatButton
        android:id="@+id/open_hogehoge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="なんか開く" />
  • AppCompatButtonをフルパスで指定したらビルド通りました。Support library系のコンポーネント使うときは注意しないといけないですねー
    <android.support.v7.widget.AppCompatButton
        android:id="@+id/open_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Open sample of listView" />

Atomがだいたい使える感じになってきたので設定を整理してみる

Atom始めました

  • 仕事だと.Net開発してるので大部分の時間はWindows/VisualStudioを使ってますが、家だとMacBook使うので普通にテキストエディタ使ってます。で、世の中的にはVimEmacsで2分している中、Sublime Text 2でHTMLやらJavaScriptやらNode.jsやらGoやら書いてましたが、ここ最近はAtomに乗り換えてます。だいぶ馴染んだかなと思うので現状を整理。

使ってるPlugin

  • atom-material-ui : UI Themeに設定。Material Designっぽい感じの見た目になる
  • seti-syntax : Syntax Themeに設定。
  • atom-cli-diff : diffツール
  • autocomplete-emojis : 絵文字のAutoCompleteを追加
  • autocomplete-paths : ファイルパスを入力する際のAutoComplete
  • color-picker : カラーピッカーを表示してRGBを入力できたり
  • file-icons : ファイルアイコンが追加される。楽しい。
  • highlight-selected : ファイル内で選択した単語と同じ単語をハイライトする。
  • maximize-panes : 複数Pane開いている場合、ActiveなPaneを全画面表示できるように
  • merge-conflicts : Atom上でgitのconflictをいい感じに編集できる。らしい。ほぼソロ開発なのでまだお世話になってない。
  • regex-railroad-diagram : 正規表現書くとグラフィカルにその表現を図で表示してくれる。
  • project-manager : プロジェクトの管理をしやすくする。色んな場所にプロジェクトがあってもこのパッケージで管理されていれば簡単にたどり着ける
  • tabs-to-spaces : tabをspaceに変換してくれる
  • term2 : Atomのpaneでコンソール開ける
  • ask-stack : Atom上からStackOverFlowに検索をかけて検索結果を見ることができる。
  • language-babel : Babelサポート
  • language-elixir : Elixirサポート
  • react : Reactサポート
  • language-haskell : Haskellサポート。言語系のパッケージは必要になったら探して入れるという感じですな。
  • linter : Lintツール。リアルタイムでチェックしてくれる。下で挙げてるLintたちはこれがないと動かない
  • linter-csslint : CSSのLint
  • linter-jshint : JSのLint
  • 番外:atom-pronama-chan : プロ生ちゃんを背景に召喚できる。デフォだと音声付きなので、自宅以外の場所でうっかり試すと悲劇が起きる。

見た目こんな感じになる

f:id:Rei19:20151012084758p:plain

よく使うショートカット

ショートカット 内容 自分で追加/変更
cmd-, Setting-Viewを開く。パッケージいれたり何か設定するときはここから
ctrl-h, j, k, l, a, e おなじみのカーソル移動
ctrl-alt-h, l 単語レベルで左右に移動
ctrl-tab, ctrl-tab-shift おなじみのタブ移動
ctrl-enter go-to-declaration。選択した関数やら変数の定義元に飛ぶ。要ctags
ctrl-0 PaneとTreeのフォーカスを切り替え
ctrl-w ActiveなPaneを閉じる
ctrl-cmd-s 今開いているプロジェクトを名前をつけて保存する
ctrl-cmd-p 保存済のプロジェクトの一覧を表示して開く
cmd-k + left, right, top, bottom 指定した方向に新しいPaneを開く
cmd-k + cmd-left, right, top, bottom 指定した方向に新しいTerminalを開く
ctrl-] 次のPaneに移動する
ctrl-[ 前のPaneに移動する
ctrl-- Paneのサイズを小さくする
ctrl-; Paneのサイズを大きくする
cmd-n 新しいファイルを作成
cmd-o ファイルを開く
cmd-d カーソルが当たっている単語を選択される。複数個選択することもできる。
cmd-/ 選択中のコードをコメントアウト/コメントアウト解除
cmd-r ファイル内のシンボルの一覧を表示して移動
cmd-f 現在のファイルから検索
cmd-t プロジェクトからファイルを検索して開く
cmd-shift-F 現在のプロジェクトから検索
cmd-shift-enter maximize-panesを呼ぶ。カレントのpaneを最大表示する。その状態からもう一回叩くと元に戻る
cmd-shift-R プロジェクト内のシンボルの一覧を表示して移動
cmd-shift-C カラーピッカーを表示。ピッカーから選択したカラーコードがエディタに反映される
cmd-shift-A ask-stackを呼ぶ
  • 今のところこんな感じで、それなりに快適。

Elixir 入門 ~ META-PROGRAMMING IN ELIXIR ~ その2 - Macro

Macros

Foreword

  • Macroは通常のElixirのコードよりもわかりづらい。開発者は責任を持ってクリーンなコードを書くように心がける。
  • Elixirはすでに簡潔でわかりやすい仕組みを提供している。Macroを使うのは最後の手段であると理解すべきだろう。

Our first macro

  • Macroはdefmacro/2で定義される。macros.exsを作ってみよう。
defmodule Unless do
  def fun_unless(clause, expression) do
    if(!clause, do: expression)
  end

  defmacro macro_unless(clause, expression) do
    quote do
      if(!unquote(clause), do: unquote(expression))
    end
  end
end
  • iex macros.exs で作成したモジュールと使ってiexを開始して、以下のコマンドを投げてみる。
iex> require Unless
nil
iex> Unless.macro_unless true, IO.puts "this should never be printed"
nil
iex> Unless.fun_unless true, IO.puts "this should never be printed"
"this should never be printed"
nil
  • fun_unless!trueの評価にもかかわらず文字列が表示されている。これはunlessが呼ばれる前にすでにIO.puts ~が評価されているためである。もう少し詳しく見てみる。

  • Unless.macro_unless true, IO.puts "this should never be printed"マクロ が呼ばれた時、macro_unless は以下を受け取っている。

  • Unless.macro_unless(true, {{:., [], [{:aliases, [], [:IO]}, :puts]}, [], ["this should never be printed"]})
  • そしてMacroから返される式は以下になる。
{:if, [], [
  {:!, [], [true]},
  {{:., [], [IO, :puts], [], ["this should never be printed"]}}]}
  • Macro.expand_once/2を使って確認してみよう。
iex> expr = quote do: Unless.macro_unless(true, IO.puts "this should never be printed")
iex> res  = Macro.expand_once(expr, __ENV__)
iex> IO.puts Macro.to_string(res)
if(!true) do
  IO.puts("this should never be printed")
end
:ok
  • Macro.expand_once/2quoted expressionを受け取り、現在の環境に合わせて拡張した結果を返す。
  • このようにMacroはquoted expressionを受け取り何かに変換して返す。実のところunless/2, defmacro/2, def/2, defprotocol/2のようなElixirのコンストラクタはすべてマクロである。すなわち、開発者の環境に応じて拡張されている。
  • 我々はどのような関数やMacroもオーバーライドできる。ただし、Kernel.SpecialFormsは例外で、拡張はできない。

Macros hygiene

  • ElixirのMacroは遅延評価(late resolutionはこの訳でいいのか。。。)を持っている。これはquoted expressionの中の変数とMacroが拡張されるときの変数がコンフリクトしないことを保証している。以下の例を見てみる。
defmodule Hygiene do
  defmacro no_interference do
    quote do: a = 1
  end
end

defmodule HygieneTest do
  def go do
    require Hygiene
    a = 13
    Hygiene.no_interference
    a
  end
end
HygieneTest.go
# => 13
  • no_interference/0の中でaに値を束縛しているが、requireしているTestモジュールのaには影響を与えていない。
  • 次のようにするとどうだろうか。
defmodule Hygiene do
  defmacro interference do
    quote do: var!(a) = 1
  end
end

defmodule HygieneTest do
  def go do
    require Hygiene
    a = 13
    Hygiene.interference
    a
  end
end
HygieneTest.go
# => 1
  • Macro内のavar!で囲むとgo/0内のaに値を再束縛している。

The environment

  • Macro.expand_once/2 を使った時に引数に __ENV__ を指定したが、これはなんだろうか。
  • __ENV__ は、現在のモジュールに関する様々な情報が格納された Macro.Env 構造体のインスタンスを返す。
iex> __ENV__.module
nil
iex> __ENV__.file
"iex"
iex> __ENV__.requires
[IEx.Helpers, Kernel, Kernel.Typespec]
iex> require Integer
nil
iex> __ENV__.requires
[IEx.Helpers, Integer, Kernel, Kernel.Typespec]

Private macros

  • ElixirはPrivate Macroもサポートしている。モジュール内がスコープになるMacroだが、注意しなければならないことがある。
  • Macroは使う前に定義されていなければならない。例えば以下のような場合は、Macroを定義する前に使っているのでエラーになる。
iex> defmodule Sample do
...>  def four, do: two + two
...>  defmacrop two, do: 2
...> end
** (CompileError) iex:2: function two/0 undefined

Write macros responsibly

  • Macroは強力で、ElixirはMacroを責任を持って使うための仕組みを提供している。
  • Macros are hygienic
    • Macroの中で定義された変数はユーザーコードに影響されない。
    • さらにMacroの中での関数呼び出しや、aliasはユーザーのコンテキストを汚染しない
  • Macros are lexical
    • Macroはグローバル空間に注入することはできない。Macroはモジュールの中でrequireあるいはimportしないと使えない。
  • Macros are explicit
    • Macroは明示的に実装されていなければ動かすことができない。
    • Macroはコンパイルの間に明示的にもたらされなければならない。
  • Macros’ language is clear
    • 多くの言語はquoteunquoteのためにシンタックスショートカットを提供している。ElixirはMacroの定義やquoted expressionとの境界を明確にするために、別に明示的に書くようにしている。
  • 上記の仕組みを持ったとしても、開発者は大きな責任を負う。もしMacroを使う場合は、MacroはあなたのAPIではないということを覚えておこう。Macroの定義は短く保つべきである。次の例を見てみよう。
defmodule MyModule do
  defmacro my_macro(a, b, c) do
    quote do
      do_this(unquote(a))
      ...
      do_that(unquote(b))
      ...
      and_that(unquote(c))
    end
  end
end
  • これを次のように書き換える
defmodule MyModule do
  defmacro my_macro(a, b, c) do
    quote do
      # Keep what you need to do here to a minimum
      # and move everything else to a function
      do_this_that_and_that(unquote(a), unquote(b), unquote(c))
    end
  end

  def do_this_that_and_that(a, b, c) do
    do_this(a)
    ...
    do_that(b)
    ...
    and_that(c)
  end
end
  • Macro内のコードが明確でテストしやすくなった。テストを書く場合はdo_this_that_and_that/3を直接呼べばよい。