伊勢旅行記

伊勢に一泊二日の旅行に行ってきた。行ったことがなかったのと、島に行きたくて、伊勢だといくつか島があるのでちょうどよかった。

1日目

新幹線で名古屋まで行き、そのあとは特急で伊勢に向かう。近鉄名古屋駅に手羽先餃子なるものがあったのでついでに食べてみる。手羽先を餃子の皮にしていて、ビールも買っておけばと後悔。

IMG_4300
手羽先餃子。見た目は普通の手羽先だけど、中に餃子の具が入っている。

続きを読む →

本棚が壊れかけてる

新品のノートが本棚にあるはず、と思って探していたのだけど、その時に本棚が壊れかけているのに気づいた。
壊れかけてるといっても風がふいたら崩壊するようなものじゃなくて、ある棚のタボ穴が本の重みに耐えきれずに歪んでしまい、横板を支えきれなくなっていた。
下の段の本に干渉して、表紙が曲りかけているものもあったので応急処置としてその棚からは本を除去。
ただえさえ本棚に収まっていないので、ちょっと本格的に対処するかどうか考えるかなー。

HabitatでSinatraアプリのパッケージを作る

6/14にChef社の新しいプロダクト、Habitatが発表されたのでひとまず触ってみました。bignewsとかいってカウントダウンまでしてたのにカウントダウン終了前にTwitterやBlogに書いてて笑った。
チュートリアルを一通りやって、さて簡単なRubyアプリを動かそうということで、SinatraアプリをHabitatで動かした。

Dockerのイメージにエクスポートしてそれを動かす、までしかしていないのでサービスディスカバリとかどうなってるのかはよくわからない。

コツ的なもの

plan.shやhooksの書き方

https://app.habitat.sh/#/pkgs/core にあるパッケージを参考にするのがよい。ブラウザからはplan.shとconfigを見ることができる。
initやrunといったhooksは、pkg_build_depsに指定してstudio上でbuildすると、「/hab/pkgs/core/ruby-rails-sample/0.0.1/20160614073830/」のような場所にダウンロードしてくれるので、hooksディレクトリを覗けばよい。

hooksのデバッグ

最初、全然起動しなくて、しかもエラーが出力されてなかったのでかなり大変だった。
initやrunに「exec 2>&1」を書いておくと標準エラーを標準出力に出すのでエラーログを見ることができる。

作成記録

備忘録。大体チュートリアルと同じ。概念とか間違えてるかも。

docker環境の用意

dockerを使うので用意する。チュートリアルと同じ。環境変数や変数はcore/ruby-rails-sampleを参考にした。

[text]
docker-machine create –driver virtualbox habitat
eval $(docker-machine env habitat)
[/text]

plan.shの作成

Habitatではアプリや付随するライブラリをまとめたものをパッケージと呼ぶらしい。
で、パッケージを作るためにplan.shというファイルを作成する。
これは、どういうパッケージをインストールするか、ビルド時にどういう処理をするのかを定義しているファイル。
シェルスクリプトなので割ととっつきやすい?

今回作成したplan.shは以下。いろいろ試行錯誤したので不要な設定等があるかもしれない。

[text]
$ mkdir habitat_sinatra
$ cd habitat_sinatra
$ vim plan.sh
$ cat plan.sh
pkg_oriigin=origin
pkg_name=habitat_sinatra_sample
pkg_version=0.0.1
pkg_maintainer="Takaishi Ryo <ryo.takaishi.0@gmail.com>"
pkg_license=()
pkg_source=https://github.com/takaishi/${pkg_name}/archive/${pkg_version}.tar.gz
pkg_shasum=2977dff904580d64f72a833e9ce228fdff19ea6956aba36a5d8000ddd9ffe4c7
pkg_filename==${pkg_version}.tar.gz
pkg_deps=(core/ruby core/bundler core/bash)
pkg_build_deps=(core/ruby core/bundler core/bash)
pkg_lib_dirs=(lib)
pkg_include_dirs=(include)
pkg_expose=(8080)
pkg_bin_dirs=(bin vendor/bundle/bin)

do_build() {
local _bundler_dir=$(pkg_path_for bundler)

export GEM_HOME=${pkg_path}/vendor/bundle
export GEM_PATH=${_bundler_dir}:${GEM_HOME}
gem install bundler -v 1.11.2 –no-ri –no-rdoc
bundle install –path vendor/bundle
}

do_install() {
cp Gemfile ${pkg_prefix}
cp Gemfile.lock ${pkg_prefix}
cp app.rb ${pkg_prefix}
cp -r .bundle ${pkg_prefix}
cp -r vendor ${pkg_prefix}
}
[/text]

pkg\_\*\*の変数が基本的な設定で、do\_\*\*の関数はコールバック。コールバックでイメージの作成時にどういう処理を行うか定義します。
変数とコールバックは結構いろいろ種類があるみたい

このplan.shでやっているのは、主に

  1. pkg_sourceからアプリのリリースファイルをダウンロード&展開
  2. アプリを動かすのに必要なgemをbundleコマンドでダウンロード
  3. アプリを動かすのに必要なファイルを自分のパッケージにコピー

の3つ。

まず、ダウンロード。このplan.shには書かれていないけど、デフォルトで、do_downloadコールバックでpkg_sourceで指定したファイルをダウンロードするようになっている。
ダウンロードしたファイルのチェックや展開も、それぞれdo_verifyやdo_unpackでやる。

次にgemのダウンロード。do_buildでやっている通り。do_buildはデフォルトではmakeしているけど、このplan.shではそのコールバックを上書きしてgemをダウンロードしてる。

最後にファイルのコピー。デフォルトだとmake installを実行するけどここでは必要なファイルをコピーする。

hookの作成

plan.shはできたけど、これだけじゃアプリが動かない。動かすために、hookを作成する。

initの作成

initは起動時に実行される処理を書くみたい。
以下のように作成した。

[text]
$ mkdir hooks
$ vim hooks/init
$ cat hooks/init
#!/bin/sh
#
export GEM_HOME={{pkg.path}}
export GEM_PATH="$(hab pkg path core/ruby)/lib/ruby/gems/2.3.0:$(hab pkg path core/bundler):$GEM_HOME"

ln -sf {{pkg.path}}/Gemfile {{pkg.svc_path}}
ln -sf {{pkg.path}}/Gemfile.lock {{pkg.svc_path}}
ln -sf {{pkg.path}}/vendor {{pkg.svc_path}}
ln -sf {{pkg.path}}/app.rb {{pkg.svc_path}}

cd {{pkg.svc_path}}
exec 2>1&
exec bundle install –path vendor/bundle
[/text]

リンクを貼っているが、これはパッケージのディレクトリに置いてあるGemfileやapp.rbをサービス用のディレクトリに配置している。
plan.shで一度コピーしてその後リンクを貼るというには2度手間な気もするけどどうなんだろう…
その後、bundle install。do_installでコピーしたはずの.bundleがどうもコピーされていないので再度ここで実行。

runの作成

最後に、Sinatraアプリを実行するrun。

[text]
$ vim hooks/run
$ cat hooks/run
#!/bin/sh
#
export GEM_HOME={{pkg.path}}
export GEM_PATH="$(hab pkg path core/ruby)/lib/ruby/gems/2.3.0:$(hab pkg path core/bundler):$GEM_HOME"
cd {{pkg.svc_path}}
exec 2>&1
exec bundle exec ruby app.rb -o 0.0.0.0 -p 8080
[/text]

サービス用ディレクトリに移動してapp.rbを実行する。

hab studioでパッケージを作る

開発用のサンドボックスとしてstudioというものがあって、そこでパッケージのビルドやDockerイメージへのエクスポートができる。

[text]
$ hab studio enter
[1][default:/src:0]# ls
README.md hooks plan.sh results
[2][default:/src:0]# ls /
bin dev etc hab home lib lib64 mnt opt proc root run sbin src sys tmp usr var
[/text]

ビルドとエクスポートは以下のようにする。まぁチュートリアルと同じ。

[text]
[3][default:/src:0]# build
[4][default:/src:0]# hab pkg export docker r_takaishi/habitat_sinatra_sample
[/text]

実行

いよいよ実行する。とはいってもチュートリアルと大体同じである。

[text]
$ docker run -it -p 8080:8080 r_takaishi/habitat_sinatra_sample
hab-sup(MN): Starting r_takaishi/habitat_sinatra_sample
hab-sup(GS): Supervisor 172.17.0.4: 6c921b33-1872-4c14-8ab2-4ce6821fcd67
hab-sup(GS): Census habitat_sinatra_sample.default: ebb498f9-7c52-4ec1-810f-5245a3d2aa5b
hab-sup(GS): Starting inbound gossip listener
hab-sup(GS): Starting outbound gossip distributor
hab-sup(GS): Starting gossip failure detector
hab-sup(CN): Starting census health adjuster
init(PH): Don’t run Bundler as root. Bundler can ask for sudo if it is needed, and
init(PH): installing your bundle as root will break this application for all non-root
init(PH): users on this machine.
init(PH): Using rack 1.6.4
init(PH): Using tilt 2.0.5
init(PH): Using bundler 1.11.2
init(PH): Using rack-protection 1.5.3
init(PH): Using sinatra 1.4.7
init(PH): Bundle complete! 1 Gemfile dependency, 5 gems now installed.
init(PH): Bundled gems are installed into ./vendor/bundle.
habitat_sinatra_sample(SV): Starting
habitat_sinatra_sample(O): [2016-06-15 12:12:49] INFO WEBrick 1.3.1
habitat_sinatra_sample(O): [2016-06-15 12:12:49] INFO ruby 2.3.0 (2015-12-25) [x86_64-linux]
habitat_sinatra_sample(O): == Sinatra (v1.4.7) has taken the stage on 8080 for development with backup from WEBrick
habitat_sinatra_sample(O): [2016-06-15 12:12:49] INFO WEBrick::HTTPServer#start: pid=72 port=8080
[/text]

起動したのでアクセスしてみる。

[text]
$ curl http://$(docker-machine ip habitat):8080/
Hello, habitat!%
[/text]

やったぜ。

近況

ここしばらく意識して勉強会や非エンジニアコミュニティの飲み会なんかに行っていろいろ話をしているけど、なかなか刺激があってよい。
家と仕事の往復だとどうしても思考が偏るからね…

カイゼン

仕事をノートPCでしていて、ちょっとこのままだと身体によくないのではないかと思って改善することにした。
第1弾として、Roost Standを買って、家で眠っていたRealforceを使っている。首への負担が結構減ったような気がしている。
今はこんな感じ。

第2弾としては、肩を広げて仕事をするためのErgobox EZを注文中。Realforceと違ってメカニカル式だけど、セパレート式のキーボードは初めて使うので楽しみ。しかし、いつになったら届くのやら…

ミラクルエアー

ユニクロのミラクルエアージーンズがとてもよい。とにかく軽い。土曜日に2着買い増ししてしまった。

最近気になる本

SOFT SKILLは土曜日に買って今読んでる。科学の発見はジュンク堂の1階に平積みなってて驚いた。今見たらKindleで出ているのでそっちで読みたいかな…かなり分厚い。1493は、前作があるようなのでそちらから読みたい気もする。
そもそも4月に入ってから読書量が相当減ってしまっているという…読む気にならないのに読んでも仕方ないので自分のペースで読むだけなんだけど。

 

SOFT SKILLS ソフトウェア開発者の人生マニュアル
ジョン・ソンメズ
日経BP社
売り上げランキング: 319
科学の発見

科学の発見

posted with amazlet at 16.05.22
スティーヴン ワインバーグ 大栗 博司
文藝春秋
売り上げランキング: 289
1493――世界を変えた大陸間の「交換」
チャールズ・C. マン
紀伊國屋書店
売り上げランキング: 15,772

Crystalのテスト周りについての覚え書き

Crystalでテストどうやって書くんだろうと調べたメモ。https://github.com/veelenga/awesome-crystal にいろいろ載っていたのでいくつか試した。
テスト機能としては、言語組み込みでrspecっぽい構文のspecが使える。よりrspecに近い機能を追加してある外部ライブラリのspec2もある。
これにPowerAssertを組み合わせる感じか。
MiniTestもあるけど試してない。

組み込みのSpec

Rubyのrspecのような仕組みが言語に組み込まれていて、「crystal spec」で実行できる。
http://crystal-lang.org/api/Spec.html を参照。
rspecと比べるとcontextやletがなかったり、before/afterもSpec.before_each、Spec.after_eachと書く必要があったりとRubyの感覚で書こうとして結構ハマった。

Crystalの場合

サンプルとしてspec.cr を作成。shouldを使う。

[ruby]
require "spec"

describe "Foo" do
Spec.before_each do
puts "Foo before_each"
end

Spec.after_each do
puts "Foo after_each"
end

describe "Bar" do
Spec.before_each do
puts "Foo::Bar before_each"
end

Spec.after_each do
puts "Foo::Bar after_each"
end

it "hoge" do
puts "hoge"
"hoge".should eq "hoge"
end

it "huga" do
puts "huga"
"huga".should eq "piyo"
end
end
end
[/ruby]

実行結果は以下の通り。

[text]
$ crystal spec spec.cr
Foo before_each
Foo::Bar before_each
hoge
.Foo after_each
Foo::Bar after_each
Foo before_each
Foo::Bar before_each
huga
FFoo after_each
Foo::Bar after_each

Failures:

1) Foo Bar huga
Failure/Error: "huga".should eq "piyo"

expected: "piyo"
got: "huga"

# ./spec.cr:28

Finished in 0.49 milliseconds
2 examples, 1 failures, 0 errors, 0 pending

Failed examples:

crystal spec ./spec.cr:26 # Foo Bar huga
[/text]

ネストの浅い箇所で定義したbefore_each/after_eachから順に実行されている。
rspecの場合はbeforeは浅い順に、afterは深い順に実行されるので最初混乱してしまった。
バグだろうか?

参考:Rubyの場合

上で書いたCrystalのspec相当のコードは以下のようになる。

[ruby]
require ‘rspec’

describe "Foo" do
before(:each) do
puts "Foo before_each"
end

after(:each) do
puts "Foo after_each"
end

describe "Bar" do
before(:each) do
puts "Foo::Bar before_each"
end

after(:each) do
puts "Foo::Bar after_each"
end

it "hoge" do
puts "hoge"
expect("hoge").to eq "hoge"
end

it "huga" do
expect("huga").to eq "piyo"
end
end
end
[/ruby]

実行結果。
beforeは浅いところから、afterは深いところから順に実行されている。

[text]
$ rspec ./spec.rb
Foo before_each
Foo::Bar before_each
hoge
Foo::Bar after_each
Foo after_each
.Foo before_each
Foo::Bar before_each
Foo::Bar after_each
Foo after_each
F

Failures:

1) Foo Bar huga
Failure/Error: expect("huga").to eq "piyo"

expected: "piyo"
got: "huga"

(compared using ==)
# ./spec.rb:27:in `block (3 levels) in <top (required)>’

Finished in 0.01156 seconds (files took 0.06919 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec.rb:26 # Foo Bar huga
[/text]

spec2

外部ライブラリ。よりrspecに近づいていて、なじみのある雰囲気。
ドキュメントは https://github.com/waterlink/spec2.cr を参照。
上で書いたspec.crをspec2で書き直すとこうなる。GlobalDSLをincludeすることでいい感じの見た目になってよい。letやsubject、expect等も使えるようになる。

[ruby]
require "spec2"
include Spec2::GlobalDSL

describe "Foo" do
before do
puts "Foo before_each"
end

after do
puts "Foo after_each"
end

context "Bar" do
before do
puts "Foo::Bar before_each"
end

after do
puts "Foo::Bar after_each"
end

it "hoge" do
puts "hoge"
expect("hoge").to eq "hoge"
end

it "huga" do
expect("huga").to eq "piyo"
end
end
end
[/ruby]

実行結果。afterの実行順序はspecと同様(rspecと逆)のようだ。

[text]
$ crystal spec spec2.cr
Foo before_each
Foo::Bar before_each
hoge
Foo after_each
Foo::Bar after_each
.Foo before_each
Foo::Bar before_each
F

In example: Foo Bar huga
Failure: Expected to be equal:
Expected: "piyo"
Actual: "huga"

[4407663170] *CallStack::unwind:Array(Pointer(Void)) +82
[4407663073] *CallStack#initialize<CallStack>:Array(Pointer(Void)) +17
[4407663032] *CallStack::new:CallStack +40
[4407776169] *Spec2::ExpectationNotMet@Exception#initialize<Spec2::ExpectationNotMet, String, Nil>:CallStack +41
[4407776092] *Spec2::ExpectationNotMet::new<String>:Spec2::ExpectationNotMet +108
[4407773796] *Spec2::Expectation(String)@Spec2::Expectation(T)#to<Spec2::Expectation(String), Spec2::Matchers::Eq>:Nil +68
[4407771919] *Spec2__Foo::Spec2__Bar::Spec2__Huga#run<Spec2__Foo::Spec2__Bar::Spec2__Huga>:Array(( -> Void)) +95
[4407769475] *Spec2::Runners::Default#run_context<Spec2::Runners::Default, Spec2::Reporters::Default, Spec2::Orders::Default, Spec2::Context>:Array(Spec2::Context) +355
[4407769950] *Spec2::Runners::Default#run_context<Spec2::Runners::Default, Spec2::Reporters::Default, Spec2::Orders::Default, Spec2::Context>:Array(Spec2::Context) +830
[4407769950] *Spec2::Runners::Default#run_context<Spec2::Runners::Default, Spec2::Reporters::Default, Spec2::Orders::Default, Spec2::Context>:Array(Spec2::Context) +830
[4407764660] *Spec2::HighRunner#run<Spec2::HighRunner>:Int32? +388
[4407765765] *Spec2@Spec2::run:Int32? +21
[4407652288] ~proc(Int32 -> Void)@./libs/spec2/spec2.cr:62 +16
[4407753648] *AtExitHandlers::run<Int32>:Array((Int32 -> Void))? +320
[4407652154] main +74

Finished in 1.16 milliseconds
Examples: 2, failures: 1
[/text]