ctrl-aでDJした
一応備忘録として書いておこうと思う。割と前の話だけど、去る5/28に、秋葉原mograでctrl-aっていうイベントがあって、そこでDJした。
出るきっかけは本当にたまたまで、プライベートで仲良いkazzoneに飲み屋で出る?って言われたからだった気がする。酔っぱらってて出る出るって返事したら本当に出してもらえた。自分のような人見知りでもこういうことがあるのだから、人の縁というのは恐ろしい。
DJ自体は本当に初心者で、去年会社辞めて有給消化してるときにいじりはじめてそれからなんとなく練習してた程度だった。mograといえばアキバ文化の一端を担う聖地だし、自分じゃなくたってあそこにイベントで立ちたい人はいっぱいいると思う。出演前は嬉しいよりも畏れ多い気持ちで一杯だった。
いざイベントが始まると恐ろしいぐらいに人がたくさん来て盛り上がった。正直何が起きてるのかよくわからなかった。実際平日であれだけ人が集まったのは異常だと複数の人から聞いた。
自分は出番がオープニングだったので人は多くなかったのだけど、kazzoneがDJしてる時にVJとして前に立っていたので、場の盛り上がりを肌で感じることができた。好きな曲が流れた時に起こる歓声というか怒声みたいなものと、限界までボリュームが引き上げられたキック音とが混ざって起きる異常な振動は、日常生活ではなかなか感じることはできない。お客さんはちゃんと訓練されたクズで、色々なところで助けられた。本当にありがたかった。
ちなみにVJは本番の2日前にやってくれって言われて、一夜漬けで準備したとかそういう思い出もある。しかも準備してる時に仕事のほうでデカい障害出してしまって頭おかしくなりそうなぐらい脳汁出てた。キツかった。
DJの方は身内には良かったみたいなことは言ってもらえたけど、まぁ身内だし、初めてにしてはという枕詞が省略されていたので、客観的にみてお世辞にも上手くいってはいないと思う。一応ネットのどこかには当日のプレイを再現して上げたものもあるけど、とてもソーシャルアカウントとかで宣伝する気にはならない。選曲は本当に難しい。イベントの文脈、客層、自分の順番、他のDJの趣向、BPM、キー、その他諸々考えなければならないことがたくさんある。みんな軽々と回してるけどその難しさが身にしみた。
ctrl-aはおかげさまで次回もあるし、また出させてもらえそうなので次はもっとうまくやれるように頑張りたい。
goでテスト並列実行させたら楽な気がする
チームにある程度テストがかさばってくると、テストの実行時間が問題になる。せっかくテストを頑張って作っていても、実行時間が10分とか20分になってくるとどうしてもテストを動かすのが億劫になる。 せっかっく開発がノッてきたのに、テストのフィードバックがすぐ得られないと集中が途切れてしまう。手元なら実行範囲を狭めればいいだけだが、 CI 環境ではそうもいかないし、 そこでこけている原因を探るのに一回のトライエラーが10分間隔とかになると辛い。
まぁ、そういう建前は色々あるけど、テストのフィードバックが早く得られると困る人はいないと思う。
そういうわけでテストを並列実行しようという話が出てきて、php 界隈で並列実行を試みようとすると例えば以下のライブラリに行き当たる。
paratest は一番メジャーっぽいんだけど、php で書かれていて心配。php でマルチプロセスを行うのはかなりの黒魔術が必要なはずで、正直地雷踏みそうで近寄りたくない。 あとで見てみるとそんなことなさそう。昔読んだ php でマルチプロセスしてるコードが並列関係なく単純に出来が悪くて悪い印象持っていただけっぽい。
一方で parallel-phpunit は shell でなんか安心感がある。ただやっぱりそこは shell なのでテストの実行単位を分けていったりするのにもう少し自由度があったほうがいいのではという気がする。また少し離れたところでいうと、RRRSpec とかもある。これは確かにすごいのだが、こういう大艦巨砲が必要なわけではない。とにかく楽に学習コストとか下げてやりたい。
そんな感じの文脈で、ある程度費用対効果が良さそうな線を考えると、go で phpunit の並列実行するスクリプト書くのがいいんじゃないかなあという結論に至ってえいやで書いてみた。
package main import ( "fmt" "os/exec" "path/filepath" "runtime" "sync" ) var ( runner = "./vendor/bin/phpunit" target = "./tests/*" exclude = []string{"tests/fixture"} outputs = []byte{} ) func main() { files, err := filepath.Glob(target) if err != nil { panic(err) } var wg sync.WaitGroup runtime.GOMAXPROCS(runtime.NumCPU()) for _, file := range files { wg.Add(1) if contains(file, exclude) { wg.Done() continue } go func(file string) { out, err := exec.Command(runner, file).Output() if err != nil { wg.Done() fmt.Println("failed to execute: " + runner + " " + file) panic(err) } outputs = append(out) wg.Done() }(file) } wg.Wait() for _, output := range outputs { fmt.Printf("%c", output) } } func contains(value string, slice []string) bool { for _, s := range slice { if value == s { return true } } return false }
このスクリプトでは、tests 以下のディレクトリごとに phpunit を並列実行している。今回はテストランナーを phpunit にしているけれど、var
以下を書き換えれば rspec でも karma でも testem でも何でも動かせる。
本当にとりあえずで書いたのでテストの実行単位が雑すぎるというのはあるけど、まぁそれに関しては拡張は難しくないのであまり気にしていない。 それよりも一番弱点になるのは CI 環境で go が必要になることだろうけど、別に致命的ではないと思う。go の書き方がなってない、とかそういう話は素直にすいませんといいたい。
とにかく楽に、費用対効果が高い形でテストを並列実行して高速化したいという話であれば go でスクリプト書くのはよい気がしてる。
PHPUnitを便利にするライブラリ幾つかつくった
年末年始+αの時間で PHPUnit をちょっと便利にするライブラリを幾つか作ったのでそれぞれ書いておく。
assert_chain
https://github.com/gong023/assert_chain
PHPUnit で一つの変数について詳しくアサーションをかけたい場合、以下のように書くことがあると思う。
<?php $arr = [ 'intKey' => 1, 'stringKey' => 'foo', 'boolKey' => true, ]; $this->assertNotEmpty($arr); $this->assertArrayHasKey('intKey', $arr); $this->assertSame(1, $arr['intKey']); $this->assertArrayHasKey('stringKey', $arr); $this->assertSame('foo', $arr['stringKey']); $this->assertArrayHasKey('boolKey', $arr); $this->assertTrue($arr['boolKey']);
一体何度 $this->assert
を何回書けばいいんだという話になるので、assert_chain を使って以下のように書けるようにした。以下は上記のコードと等価だ。
<?php $this->assert() ->notEmpty($arr) ->arrayHasKey('intKey', $arr) ->same(1, $arr['intKey']) ->arrayHasKey('stringKey', $arr) ->same('foo', $arr['stringKey']) ->arrayHasKey('boolKey', $arr) ->true($arr['boolKey']);
また、PHPUnit の assert はほぼ全て $actual
の値(つまりテスト対象の変数)を渡す部分があるが、centralizedAssert
を使うことでテスト対象の変数が自動的にアサーションに渡るようになっている。
<?php $arr = ['key' => 'value']; $this->centralizedAssert($arr) ->notNull() ->notEmpty() ->notCount(0) ->count(1) ->arrayNotHasKey('no existing key') ->arrayHasKey('key') ->notContains('no existing value') ->contains('value') ->equals(['key' => 'value']);
アサーションは PHPUnit_Framework_Assert
にプロキシしてるだけなので、普段 PHPUnit を使っている人は違和感なく使えるはず。当然ロジックも変わらない。
詳しいことは README を見て欲しい。
assertEquals
や assertSame
を使って一度に雑にテストするのもいいが、このように複数のアサーションを重ねて段階的にテストしていくとテストが失敗した時に原因がわかりやすい。
Ayaml
https://github.com/gong023/Ayaml
yml による fixture ライブラリ。yml ファイルから php array を生成する。
# user.yaml valid_user: id: 1 name: Taro created: 2014-01
上記ファイルから以下のように array を作る。
<?php Ayaml::file('user')->schema('valid_user')->dump(); => ['id' => 1, 'name' => 'Taro', 'created' => '2014-01'];
with
メソッドを使って値の上書きもできる。
<?php Ayaml::file('user')->schema('valid_user')->with(['id' => 2, 'name' => 'John'])->dump(); => ['id' => 2, 'name' => 'John', 'created' => '2014-01'];
以下のようにシーケンシャルな配列を作ることもできる。
<?php $validUser = Ayaml::file('user')->schema('valid_user'); Ayaml::seq($validUser)->range('id', 10, 12)->byOne()->dump(); => [ ['id' => 10, 'name' => 'Taro', 'created' => '2014-01'], ['id' => 11, 'name' => 'Taro', 'created' => '2014-01'], ['id' => 12, 'name' => 'Taro', 'created' => '2014-01'], ]; Ayaml::seq($validUser)->between('created', '2014-01', '2014-03')->byMonth()->dump(); => [ ['id' => 1, 'name' => 'Taro', 'created' => '2014-01'], ['id' => 1, 'name' => 'Taro', 'created' => '2014-02'], ['id' => 1, 'name' => 'Taro', 'created' => '2014-03'], ];
詳しいことは README を見て欲しい。
Ayaml の特徴は、テストデータそのものを DB データとして作るのではなく、単純に array を生成する点。
php の fixture ライブラリとして有名なものとして、DBUnit, alice, php-factory-girl がある。これらのライブラリは、Ayaml とは違い DB にデータを入れるところまで責任を持ってくれる。
もちろんそれはそれで良いのだが、一方で DB 周りを扱うライブラリは馴染みのあるものを使いたいという気持ちはないだろうか。少なくとも自分にはある。上記のライブラリは doctrine を使うことになるので、テストのためにわざわざその DSL を覚える羽目になる(DBUnit はそうとも限らないが、あれはあれでよく我慢して使えるなと思ってる)。正直それは馬鹿げているし、あーシャーディングされてる場合どうすんだっけとか考えだすとテストデータひとつ作るのに膨大な時間を費やす羽目になる。
Ayaml は DB にデータを入れるという責任を放棄し、手前で好きに array 作って手前のライブラリに流し込めというスタンスにすることで自由度を上げている。php には active_record のような支配的なライブラリがないので、こういうアプローチもありだと考えた。
ただ一方で、Ayaml を実装する際自分に課した制約として「doctrine の DSL を覚えるより短い時間で実装すること」というのがあり(それ以上かかるなら既存ライブラリ使ったほうが早いから)、実際 ver0.1.0 までは 1h 弱ぐらいの時間で実装したので、特にシーケンシャル array を作る所なんかはパワー不足な面が否めない。 Ayaml 以上に仕事してくれる DSL が欲しいとか、プロダクトで doctrine を使っているとかいう事情があれば、alice を検討するのがよいと思う。
PHPUnityo
https://github.com/gong023/phpunityo
PHPUnit の成功時 or 失敗時に Yo してくれる。travis でも使える。
まぁ自分は CI の結果は普通にメールで見てるのでぶっちゃけそんなに必要ないのだが、Yo で送られてくるとめでたい。それだけ。
設定は Yo の API token を環境変数に突っ込んだ上で phpunit.xml.dist に以下の情報を足せばいい。
<listeners> <listener class="PHPUnit_Yo" file="vendor/gong023/src/PHPUnit_Yo.php"> <arguments> <string name="sendUser">Your Name</string> <string name="onSuccess">false</string> <string name="onFailure">true</string> </arguments> </listener> </listeners>
詳しくは README を見て欲しい。
underscore-rust作ってみた。あとrust書いてみた動機とか
underscore-rust
underscore-rust 作った。名前から分かる通り、underscore.js の機能を rust にしたもの。
ライブラリ自体のドキュメントは上記に入れたつもりなので、必要であれば参照して欲しい。ポイントとしてはオリジナルの underscore.js のように _
の構造体みたいなものを用意するのではなく、標準の struct(Vec
, TreeMap
, HashMap
)にそのまま underscore 系の関数を追加した点。use すれば普通の API と同じ感覚で使うことができる。
作ったみたが、インターフェイスについては結構後悔している部分が多い。例えば HashMap の拡張は
fn invert(self) -> HashMap<V, K>;
とかにしてしまったけどテストしづらかったとかいう理由で ownership 取ってしまったので(ownership については後述)そこに関しては
fn invert(&'a self) -> HashMap<&'a V, &'a K>;
とかにすべきだったかなと思っている。まぁ、同じ型のものが返るので不都合はなさそうだしこっち方が扱いやすくていいだろと考えたのでこういうインターフェイスにしたのだが、オーバーヘッドが大きくなってしまうのは事実だ。所詮初学者が作ったものなので見る人から見ればおかしい部分が多分に含まれているはずで、その辺りの指摘は甘んじて受けていきたい。
作った動機
特に underscore-rust を作ってみたい実用的な用事があったわけではなく、単純に rust を書いてみたくて、教材として便利だったので作ってみた。underscore系のライブラリは、一体何番煎じか知らないが、とりあえず新しい言語を覚える際には良い教材だと思う。難易度は低いし、割と有益なものを作れる。javascript を引き合いに両者の違いを感じることができる点も良い。
自分は普段 LL しか使わない完全なゆとりエンジニアだが、rust はそういった層により低レイヤーを学ばせる用途に向いている。LL とかそれより上流のコミュニケーション一式が主戦場であっても、パフォーマンスチューニングだとかインフラの冗長化を見積もったりする段階で低レイヤーにまで手を伸ばさなければならない場合は多い。ライブラリの選定で知識が必要な時もある。あるいはもっと単純に、低いレイヤーのことを知るのは物事の道理を学ぶにあたって有益だと思う。 最近はインフラ層の技術がより LL がメインの人たちに近づいてきた印象があり、自分もそういうものと付き合っていかなければないので、この辺りで一度レイヤーが低いっぽい言語に触れておきたかった。
rust に手を出してみたきっかけはこういった気持ちがあったからだけど、これより前は同じような理由で go を触ってみたりしていた。ただ、GC が支配的な世界観でなんとなく違う気がして離れてしまった。rust のメモリ管理は基本人間が頑張るのでそういうのに惹かれたんだと思う。
こう書くと C とか C++ とかやればいいという完全に真当なことを言われそうなのだが、何か一つ知らない言語を選べといわれたらそれなりに新しいものを選びたくなる程度にはミーハーなので正直やる気がおきなかった。あとかなり前に読んだ「ふつうのプログラマのための haskwell〜」とか「7つの言語〜」とかで関数型言語は確かに綺麗だなあという気持ちがチラついたのもある。純粋な興味だけで言ったら関数型言語をガッツリやってみたかったのだが、いくらかでも目の前の課題とか明日のメシと地続きにする知識を得ようとしたら、関数型っぽい風味のある rust に手を出すというのは一つの着地点としてアリだった。
rust のちょっといい話
※この項は特に間違えている可能性が多く含まれるので気をつけて下さい
ポジショントークだけだとさすがにアレなので、具体的に rust のちょっといい話を考えてみた。例えば C でこんなコードを書いたとする。
struct hoge { int num; }; int* pick_num(struct hoge *h) { int *copy = &h->num; free(&h); return copy; } int main(void) { struct hoge *x; x = (struct hoge *) malloc (sizeof(struct hoge)); x->num = 5; int *y = pick_num(x); printf("%i\n", x->num); printf("%i\n", *y); return 0; }
上記のコードだと y
は free された値を参照してしまうダンジリングポインタとなる。これはコンパイルは通るが、実行すると落ちる。このコードを rust に置き換えてみる。
struct Hoge { num: int } fn pick_num(x: Box<Hoge>) -> int { x.num } // rust の場合、ブロックを抜けるときに常にメモリ解放されるので RAII は意識しなくていい fn main() { let x = box Hoge { num: 5i }; let y = pick_num(x); println!("{}", x.num); println!("{}", y); }
これをコンパイルしようとすると、以下のようにエラーを吐いてそもそもコンパイルできない。
hoge.rs:13:20: 13:25 error: use of moved value: `x.num` hoge.rs:13 println!("{}", x.num); ^~~~~ note: in expansion of format_args! <std macros>:2:23: 2:77 note: expansion site <std macros>:1:1: 3:2 note: in expansion of println! hoge.rs:13:5: 13:27 note: expansion site hoge.rs:11:22: 11:23 note: `x` moved here because it has type `Box<Hoge>`, which is non-copyable (perhaps you meant to use clone()?) hoge.rs:11 let y = pick_num(x); ^ error: aborting due to previous error
例に挙げた C のコードが阿呆すぎるような気もするが、とにかく、このようにコンパイルの段階で危険なポインタを教えてくれるのが rust の利点の一つだ。
これを支えているのは ownership,borrow,lifetime という概念で、以下に非常に雑な説明をすると
- 全ての変数に ownership が設けられる
- ownership を持つものだけがその変数へのアクセス権を持つ
- 変数の ownership は borrow することができる
- borrower は ownership を取らずに変数を使用できる
- ただし、borrower はメモリ解放や値の書き込みを行うことができない
- borrow には lifetime が設けられる
- コンパイラが自動で付加してくれるので普段はあまり意識しなくてよいが、参照を返す際などは明示的に lifetime を宣言して返したりする
とかになるが、ownership,borrow,lifetime は変数が mutable だった場合の挙動とかが少しずつ違ったりして一口に説明するのが難しいので、できれば公式ドキュメントの ここ とか ここ を読んでみて欲しい。
今回の例だと、let x
の ownership を pick_num
関数に移譲してしまっている。そのため、再度 x
にアクセスしようとしてもできない。こういうコードの場合、pick_num
の引数は単に borrow してくるのが普通である(はず)。
参考になったもの
rust を学ぶ上で役に立ったものを挙げてみる。
- http://doc.rust-lang.org/
- やはり公式ドキュメント。基礎がしっかりある人なら1日あれば読めるかもしれない。自分はかなり時間がかかった。
- http://rustbyexample.com/
- tour of go みたいなもの。tour of go ほど体系立ってるわけではないが、公式ドキュメントからの概念的な説明と並行しながら見るといいと思う。
- http://doc.rust-lang.org/nightly/std/(https://github.com/rust-lang/rust)
- 実際に rust で何か作っていこうとなったとき、rust way がわからない。そんな時に組み込みAPIの実装を読めて強力。一般に流通している rust ライブラリよりも綺麗に設計されている。
- http://rustforrubyists.com/
- rubyist 向けの rust 導入記事。今思うと大したこと書いてない気がするのだが、導入には良さそう。何より自分のようなゆとりでも門戸が開かれている事実が嬉しかった。
効果の高いテストについて考える
テストエンジニアという奇異な立場にいる。 普通にプロダクトメンバーの一員だが、プロダクト自体のコードはあまり書かず、品質という観点から良かれと思ったことをする。大体グーグルのテスト本に載っているSETをロールモデルとしている。
- 作者: ジェームズ・ウィテカー,ジェーソン・アーボン,ジェフ・キャローロ,長尾高弘
- 出版社/メーカー: 日経BP社
- 発売日: 2013/05/23
- メディア: 単行本
- この商品を含むブログ (8件) を見る
SWTは、例えばエンジニアがテストを書きやすいようにライブラリを作ったり、テストがリリースのネックにならないように高速化したり、手動テストを支援するようなサポートツールを作ったりするのが役割となる。普通に単体テストも書く。(が、それはあまり理想ではなくて、本当はそのコードを書いた人が単体テストも書くべきだ。)
しかし、現実世界であまりそういう人を見なくて、先人の知恵を借りられずやきもきしている。もしかしたら同じことを感じている人がいるかもしれないと思ったので、何かのたしになればと思い、最近考えたことをメモしておく。
効果の高いテストと低いテストがある
テストには効果の高い / 低いがあり、効果の低いテストを書いてしまうと、テスト自体のメンテナンスコストの方が大きくなり、開発を阻害する要因となる。まずはこの点について認識を持つべきだと思う。
効果の低いテスト
例えば、セッターとゲッターしかないクラスにテストを書いたとする。ここにはロジックが含まれていないので、テストする価値はほとんどない。だが実際にCIしていると、意外と落ちたりする。落ちる原因はプロパティ名が変わったとかそんな理由である。そしてプロダクトコードではなくテストの方を修正する。テストコードを修正しなければならないのは、言い換えれば良いテストができていなかったと言えるのではないかと思っている。
少し話は逸れるが、TDD がいまいち浸透せず、アイツは死んだとか言われるのは、こういうトレードオフに対して無自覚だからだと思う。TDD はいつもテストを書くことを強制する。つまり、効果の低いテストも書かなければならない。こういった非効率は現場では許容できず、自然淘汰される。TDD は死んだと言われて久しいが、では次にどうあるべきかというのがなかなか出てこない。それは、「テストには効果が高い / 低いが存在する」というスタート地点が共通認識として持てていないからのように思う。
効果の高いテスト
数値計算や正規表現などを扱う単体テストは効果が高い。例えばゲームだと、レベルアップ・ダメージ計算・エネミーエンカウント・アイテムドロップ等のロジックなどがこれにあたる。こういう部分は手動でテストしづらいので機械からテストしたほうがよっぽど効果が高い。もう少し抽象的な言い方をすると、効果が高いのは、入力に対する出力がわかりづらいロジックのテストだ。そりゃそうだろという話になるが、ではどこからが「数値計算的な部分のテスト」でどこからが「セッターとゲッター的な部分のテスト」なのか、線引きが難しい。この線引きを上手く行えるかどうかが勝負という感じがする。
また、APIの結合テストのようなものも効果が高い。単純に、少ないテストコードで多くの部分をカバーできる。ただし、細かな状態まで結合テストでカバーしようとするのは悪手で、色々やり過ぎるとある時点で管理コストがテストによるメリットを凌駕し、効果の低いテストに転換する。
カバレッジ意味なし
どれだけ効果が高いテストができているかは、カバレッジでは測れない。カバレッジが高いからといって十分な品質が保たれていることにはならないし、世間で思われているよりもそれらの相関はずっと低い。というか、カバレッジ至上主義に陥るとセッターとゲッターのテストを書き始めるのでたちが悪かったりする。もうすっかりカバレッジ90%ですとか言われても感動しない体になった。
モバイル端末操作の自動化しんどい
一昔前から selenium でPCブラウザ操作の自動化を行う流れはあったが、これをさらに発展させ、モバイル端末でも行えるようしようという試みがある。代表的なツールとしては、appium, calabash などが挙げられる。これらのツールは、最近はまずまず安定して動くようになった。しかし、ツールがどうこうというより、モバイルをとりまく環境が複雑すぎて、総合的な安定性に欠ける。その部分について少し書きたい。
第一に、appium や calabash といったツールはサードパーティ製だというのが致命的だ。こうすると、どうしても最新のOSに追従するのが後手になる。最新OSのリリースが終わった後に対応完了というのはザラであるどころか、リリース後一週間以内に対応できれば御の字で、確か iOS7 の時は cocoa 側にバグがありしばらく対応できなかった思い出がある。大体最新OSリリース前にテストを済ませたいはずなので、これでは要件を満たせないのではないだろうか。また、IAPだと、自動化できないようにわざわざ課金のダイアログを押せなくしていたりする。そういうのもサードパーティならではの悩みである。では UIAutomation 使えという話になるがそれはそれそれで、じゃあなんで appium とか calabash って開発されたんでしたっけという話になる。
次に、やれ selenium だ、appium だといっても中で書いているのは所詮スクレイピングである。出来上がるコードが元々辛い。ちょっとしたレイアウト変更でエレメントが取れなくなり、テストが失敗するのはよくある話だ。モバイルの場合、この上にさらにモバイル特有の例外処理を載せていくので最終的に本当に辛いコードになる。例えば端末のスペック差を吸収するため sleep を書きまくったりする。さらにきわまってくると、Android というのは本当にクソ端末多くて、突然 wifi が切れてテストが失敗したりする。ここまでくるともうどうしようもない。
追記
sleepせずにある要素が出るまで待ち続ける処理があるのでそれを使えば良いというコメントを頂きました。
確かにそうなのですが、私が appium を触っていた時はエレメント探索の関数が非常にバギーで使い物にならず、javascript のコード生で渡して実行させる手法を多用していました。この手法を使う限り sleep に頼らざるを得なかったのですが、今は状況が改善しているかもしれません。
そもそも受け入れテスト自動化に本当に向き合うなら、デジタルハーツやポールトゥウィンなんていう会社が世に存在する意味を真剣に考えるべきだ。そういった会社のテスターの練度は高く、例えば格ゲーなら格ゲーに知見のある人がいて、音ゲーなら音ゲーに知見のある人がいる。そういう人はテストの勘所がわかっており、仕様ドキュメントを渡すだけで綿密なテストケースを組んでくれる。コンピュータがそこまで追いつくのは何十年か先の話だろうと思う。また、画面を見てそれが正しい状態なのか間違った状態なのか判断させるのは、正直機械ではなく人間がやった方が手っ取り早い。属人性の極みみたいな所はあるが、現状を鑑みた上で現場でどちらか選べと言われたら正直人間のほうを選びたくなる。
まぁそんな調子なので、今は色々な障壁を乗り越える & メンテする覚悟があるとか、よっぽど何回も繰り返される部分のテストをターゲットにするとかすれば(こういう場合「殺虫剤のパラドックス」という言葉が頭をよぎるが)なんとか元は取れるかなという感覚でいる。もしくは、人間と機械の共存を模索し、完全自動化ではなく人間をサポートする役割にとどめるのが合理的ではないかと思う。
渋川剛気
本当に品質の高いプロダクトを作りたいなら、設計で地雷を踏まないのが一番良い。
ああきたらこう捌く、こうきたらこう捌くなんてというのを考えていたら下の下で、真に護身を身に付けた者であれば、もはや技術は無用であり、そもそも危機に近付くことすらできないのだと思う。
一日の行動をEvernoteにpostするサイト作った
前に gem 作ったんだけど、せっかくなので他の人でも使えるように web アプリにした。いくつかの web サービスのアクティビティをまとめて evernote に上げる。
例えば twitter 認証をすればその日のつぶやきを集めるし、github 認証すればその日のコミットを集めて、それらをまとめて evernote に上げる。
技術的に特筆すべきようなことは特にない普通の rails アプリだけど、強いて何かいうなら こっち でやたら丁寧なコードを書いた反動で雑に作りたくなって、とにかく雑に作ったこととか、タダで済ませたかったので queue サーバーと DB サーバーは別に分けたこととか、moves 認証はスマートフォンなのでレスポンシブデザインにするのが面倒だった、とか。
よかったら使ってみてください
追記:
ちょうど書くのが面倒だった部分に質問もらえたので載せておきます
@r7kamura クロールはしてない。queueサーバーでsidekiq使っててそっちにpostしてる
— 完結編 (@gong023) 2014, 6月 25
@r7kamura そう。例えばツイートとかはリクエストがあってはじめて取得しにいく。けどそういうジョブはqueueにつっこんで非同期にやるって感じ
— 完結編 (@gong023) 2014, 6月 25
クソgemできた
これ
最近、自分が何をしていたのかわからなくなる。 一ヶ月、二ヶ月前ははるか昔のように思うけれど、一年前を思い返してみるとあまり今と変わっていないように思う。 例えるなら水の中を手で掻き分けて進むような時間感覚で、毎日息を止めて自分なりに必死にもがくのだけれど、ふと省みると理想とは程遠い距離しか進めていないことに気づく。
当たり前だが、何かをした分だけ時間というものは失われている。自分が何を選択して時間を過ごしているのか、放っておくと無自覚で、そのくせ現在の自分の責任が過去にある事に気づいてそのたびに辛い思いをする。もっと辛いのは、残り時間で自分ができることも大抵予想がついてしまうことで、これは数ある現実の中でも最も辛い部類に入る。タイムリープするアニメには刺さる名作が多いし、人間時間という概念に強い感情を抱く生き物ではとかいう気さえしてくる。
こういう話は普段あまり思い出さずにいれるが、それこそ時間を追うごとに看過できない問題になってきた実感がある。気づいたら二十代も半分を過ぎてしまっていて、もしこの後真人間っぽいライフプランを描くのならば(別にそんな予定はないが)、やれることが残り少ないのはもう目に見えている。なので毎日の行動をなるべく記録して、後で見返せたほうが良いと思った。
そういうわけでこのスクリプトを書いた。自分のネット上の行動を取得して、最終的にこんな感じのものを Evernote にポストする。ちなみに日記も少し書いてみたが、全く続かなかったし、たまに嘘書いてるので使い物にならなかった。
今対応しているのは上記の通りで、それぞれデイリーのアクティビティを取得している。こういう調子で動かしている
require 'everlog' everlog = Everlog::Daily.new everlog.push(:github, { access_secret: access_secret }) everlog.publish('title', access_secret, 'production')
ライブラリにするほど抽象度が高くないのでこれは紛れも無いクソ gem だが、DDD に則ってコードを書いたおかげで勉強になったり、毎日の出社時間が明らかになって詰められたりとか思い出深い感じだったので記念に gem にした。ちなみに一部 OAuth で token を払い出せないものもあるが、サービスが rails でセッションが cookie だったりするアレだと少しうまくやれば API として使えて便利。