2014年2月28日金曜日

VMWare上のDebianでHDD外してGRUBメニュー修正

VMWare Fusionで仮想マシンにDebian入れて使ってるんだけれども、少し前から/usr/local以下のファイルが消えたり復活したりして気持ち悪い挙動をしていた。再起動のタイミングで起きてるようで、どうもブート時にマウントするハードディスクが勝手に変わっているようだ。

とりあえず、起動時にブートローダであるGRUBのメニューを見てみると選択できるメニューのうちHDD選択に係る記述が root (hd0,msdos1) と root (/dev/sdb,msdos1) の2通りあり、それぞれ選択してみると、案の定/usr/local以下(というかファイルシステム)の内容が変化するのが確認できた。root (/dev/sdb,msdos1) の方がデフォルトになっていた。

原因を調べているうち、hd0で指定されるHDD(これは/dev/sdaでもある)と/dev/sdbのUUIDが同じであることを見つけた。そもそも、この/dev/sdbはバックアップ用に仮想マシンに追加したもので、以前に、dd使ってバックアップした時、UUIDが記録されたセクタもコピーしてしまったことが原因と思われる。で、/etc/fstabでルート( / )にマウントするディスクがUUIDで指定されているので、どちらのHDDか不定だったと推測される。

一時的なバックアップ用途にHDD追加してただけなので、今後の混乱を避けるためHDD自体を削除することにした。まず、 root (hd0,msdos1) の方のメニューで起動して

[root@localhost]# grub-mkconfig -o /boot/grub/grub.cfg

を実行。デフォルトメニューのほうに含まれる記述が root (/dev/sdb,msdos1) から root (hd0,msdos1) に変化したのを確認。

一旦電源をOFFにし、VMWare Fusion の設定から /dev/sdb に相当するHDDを削除。

再び起動。ログインして再度先ほどと同じコマンドでgrub.cfgを生成する。

[root@localhost]# grub-mkconfig -o /boot/grub/grub.cfg

を実行。grub.cfg の中身を覗くと、root (/dev/sdb,msdos1)の方のメニューは消えていた。もう一度再起動し、ブートローダのメニューが変更されてメニューが減ったのを確認。これでもう大丈夫なはず。

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

2014年2月27日木曜日

gitブランチ名とステータスをzshの右プロンプトで表示

ターミナルからgitコマンドでリポジトリを操作するときに、マージしてから自分が現在いるブランチを間違えていた事に気づいてやりなおす・・・なんてミスは僕もやったことがある。それに現在いるブランチを確認するのにgit branchをそのたびごとに打つかわりに、常に表示できていたら便利じゃないだろうか。

実は、gitの配布ファイルの中には、bashとzsh向けに、gitリポジトリの中にいるときにだけ、プロンプトにブランチ名とステータスを表示するためのスクリプトが含まれている。これを使えば、上で上げたようなミスも防げるし、現在の変更状態も把握できて作業しやすくなる。ここではzshについてやり方を書こうと思う。

まず、gitの配布ファイルを解凍してできたディレクトリの contrib/completion/git-prompt.sh を適当な場所にコピーする。(gitの配布ファイルは例えばここからダウンロードできるし、githubから直接落としてきてもいい。)ここでは ~/.zsh/git-prompt.sh にコピーしたとしよう。

そうしたらまず、~/.zshrcに次の行を追加して読み込ませる。

source ~/.zsh/git-prompt.sh

このスクリプトにはブランチ名を表示する __git_ps1という関数が定義されており、gitリポジトリ内にいるかどうかを自動で判別して、いるときだけ動作する。

zshの場合、プロンプトを書き換えるにはシェル変数PS1を変更するのだが、通常のプロンプトに加えてブランチ名まで表示すると、ちょっと長過ぎる。そういう時、zshでは「右プロンプト」が利用できる。これは、入力中の行の画面右端に表示されるプロンプトだ。なお、入力文字列が長くなった時は自動的に消えてくれる優れものだ。

右プロンプトを設定する時は、シェル変数RPROMPTを設定する。また、zshにはプロンプト表示直前に実行されるprecmd()を定義でき、git-prompt.sh内のコメントによるとそこで実行したほうが若干速いらしい。ということで、以下の定義を ~/.zshrc に追加する。

setopt PROMPT_SUBST
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT='$(__git_ps1 "[%s]")'
}

ここで setopt PROMPT_SUBST は、プロンプトに指定した文字列で変数展開を有効にするという意味。こうすると$(...)内部をスクリプトとして展開してくれる。setopt TRANSIENT_RPROMPT は、右プロンプトをコマンド実行後に消すという意味。また、__git_ps1 の引数には%sを含む文字列を指定できて、%sの部分がブランチ名とステータスを表示してくれる。

ここまでの設定だけだと、実はブランチ名しか表示されない。それだけでも便利だが、さらにオプションのシェル変数を設定することで、各種ステータスを記号で表示できる。設定できるシェル変数には次のようなものがある。
  • GIT_PS1_SHOWDIRTYSTATE
    空の値以外を指定すると、未ステージの変更がある時は「*」を、ステージ済の変更がある時は「+」をブランチ名の後に表示する。
  • GIT_PS1_SHOWSTASHSTATE
    空の値以外を指定すると、スタッシュが存在する時は「$」をブランチ名の後に表示する。
  • GIT_PS1_SHOWUNTRACKEDFILES
    空の値以外を指定すると、追跡されていないファイルが存在する時は「%」をブランチ名の後に表示する。この設定はリポジトリごとの設定 bash.showUntrackedFilesにより上書きされる。
  • GIT_PS1_SHOWUPSTREAM
    アップストリーム・リポジトリで対応するブランチとのコミットの差分状態を表示する設定で、次のものを指定できる。
    "auto" : アップストリーム側に新しいコミットがある場合は「<」を、ローカル側に新しいコミットがある場合は「>」を表示する。
    "verbose" : アップストリーム側の新しいコミット数を「u-数字」で、ローカル側の新しいコミット数を「u+数字」で表示する。
    他にも"name" "legacy" "git" "svn"があるようだけど説明は省略。詳しくはgit-prompt.sh内のコメント参照。この設定はリポジトリごとの設定 bash.showUpstream により上書きされる。
  • GIT_PS1_DESCRIBE_STYLE
    特定のコミットやタグをチェックアウトしている時の表示方法を指定できる。"contains" "branch" "describe" "default" から選べる。詳しくはgit-prompt.sh内のコメント参照。
  • GIT_PS1_SHOWCOLORHINTS
    空の値以外を指定すると、GIT_PS1_SHOWDIRTYSTATE を有効にしている時に色付きで表示してくれる。ただし、__git_ps1 を PROMPT_COMMAND または precmd 経由で実行している時に限る。
今回は次のように設定した。

GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="default"
GIT_PS1_SHOWCOLORHINTS=1

以上まとめると、設定は以下のようになる。

source ~/.zsh/git-prompt.sh
setopt PROMPT_SUBST
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT='$(__git_ps1 "[%s]")'
}
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="default"
GIT_PS1_SHOWCOLORHINTS=1


これで、gitリポジトリ内にいる時は右プロンプトでブランチ名とステータスが表示されるようになった。便利便利。

※2014/03/27追記(2014/08/29一部修正)

なんか時々表示がおかしくなるなーと思ってたら、どうやら追跡されていないファイルが存在する時の「%」が、本来ならZSHだと「%%」とエスケープされるはずのところ、単独の「%」のまま出力されることがあるらしい。しかも、ログイン直後は大丈夫だけど、しばらくgitコマンドを使っているとそのうちおかしくなるという意味不明な状況。いろいろ調べたけど原因はわからず、仕方ないので下記のように強制的に置き換えて対処してます。

precmd () {
    RPROMPT=`echo $(__git_ps1 "[%s]")|sed -e s/%/%%/|sed -e s/%%%/%%/|sed -e 's/\\$/\\\\$/'`
}

最初の「%」を「%%」に置き換えますが、もし最初からエスケープされてる場合は「%%%」になってしまうので、その場合は「%%」に戻してます。sedだと正規表現に先読み・戻り読みが使えないのでこうしてます。さらにスタッシュの存在を示す「$」が変数展開と解釈されないようエスケープしてます。スマートじゃないな・・・。もっと良いやり方知っている人いたら教えてください。

その他の設定も若干変更したので該当部分の設定を再度載せておきます。

source ~/.zsh/git-prompt.sh
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT=`echo $(__git_ps1 "[%s]")|sed -e s/%/%%/|sed -e s/%%%/%%/|sed -e 's/\\$/\\\\$/'`
}
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="branch"
GIT_PS1_SHOWCOLORHINTS=0

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

2014年2月26日水曜日

イベント「@IT Security Live UP!」行ってきた

品川で行われたセキュリティ系のイベント @IT Security Live UP! に参加してきた。なんかこういうイベント参加ってめちゃ久しぶりだった。色々話聞いてきたんで、まとめてみたいと思う。スポンサー講演は、どういう流れで製品のセールスに持っていくかも見どころ。僕の受講したものについて以下に感想を述べる。

---

サイバー攻撃の本質的要因と、(攻撃側に対峙する)防御側のあるべき姿
by サイバーディフェンス研究所 名和 利男 氏


基調講演ってことで、サイバー攻撃の現状把握って意味では面白かった。まあどうしても政府機関とか軍隊とかの問題になってくる。「中国」がキーワードだったな。あの国はサイバー攻撃専門部隊も持っていたりして、今後も脅威になるようだ。面白いは面白いのだけれど、ソフト開発の現場にいるエンジニアとはちょっと距離があったかな。

インシデントに即応!各種ログによる迅速な調査・分析の実現手法
by マクニカネットワークス 市川 博一 氏


Apacheのログを例にログ解析に具体例を幾つか紹介していた。例えばアクセス頻度が多すぎるとか間隔が一定とか。そういう風に見やすくサマリにまとめるところまではソフトでやりましょうって話でセールスしてた。ログのサマリを作るのは自力でできなくもないけど手間かかるからね。

標的にされた日、守れる組織のセキュリティ対策とは
by チェック・ポイント・ソフトウェア・テクノロジーズ 宮川 淳一 氏


日々マルウェアの新種は20万種以上生まれてますよ、という話。パターンで対応できないから振る舞い検知で対策します、という最近流行りのやつだね。他ファイアウォールとかIPSとか複数の対策を組み合わせた製品をセールスしてた。

パネルディスカッション 「煽るな危険! 正しいセキュリティ情報の恐がり方」
by NTTデータ先端技術 辻 伸弘 氏,
インターネットイニシアティブ 根岸 征史 氏,
NTTコムセキュリティ 北河 拓士 氏


たとえばXPのパソコンをネットにつなぐと数分でマルウェアに感染するとか、じつはサービスパックが古いなどの特定条件のみで起きうる状況を、恐怖を煽るような表現で目にすることがあるから、まず最初は懐疑的姿勢で見るのも大切だよという話が1点。あと、標的型攻撃で多いメールを利用したサイバー攻撃対策として、メールを開かない訓練をするのは無意味だよという話も。

あなたの知らない、とっても恐ろしいDDoSサイバー攻撃
by 日本ラドウェア 井谷 晃 氏


DDoSの色んなパターンの紹介。SYNフラッド攻撃、HTTPページ・フラッド攻撃、スローレート攻撃、DNSフラッド攻撃、DNSリフレクション攻撃など。全部に対応する製品ってことでセールスしてた。

アプリ脆弱性、危うい現実と究極の解決策!
by ソニーデジタルネットワークアプリケーションズ 松並 勝 氏


これ予想以上に面白かった。Android開発で脆弱性を作りこんでしまう例は多くて、それは単に対策を知らないだけだったりするようだ。で、講演者も関与した業界デファクトスタンダードのガイドラインに沿って開発すれば、Android開発においてセキュアなコードは書けるということで書籍を紹介してた。僕もその本は知ってた(自分では持ってないけど)。で、コードを静的解析して脆弱性のある部分を自動的に発見し、修正のヒントまで教えてくれるってツールをセールスしてた。実際Eclipseを立ち上げてコードをビルドし、そのツールで脆弱性を修正するデモとかやってて見てても面白かった。まあ、ホントは開発者がきちんと脆弱性対策を学ぶのが一番なんだけどね。

HTML5時代のWeb開発者が知らないと“ガチヤバ”な脆弱性対策
by ネットエージェント 長谷川 陽介 氏


このイベントに行ったのはこれを聞きに行くのが目的だったという講演。非常に具体的にコードの例を示してくれたので参考になった。ただし、HTML5でJavascript上での脆弱性の危険性が拡大したと言っても、「ユーザからの入力値を直接採用しない」「エスケープするか、エスケープしても危険なものはあらかじめ選択肢を用意して、その中空選ぶ」という考え方については、従来と全く同じだった。その意味で、基礎的な考え方ができていればよいということが分かったのは収獲だった。その他、セキュリティ機能を使いこなすためのHTTPヘッダーの紹介があってこれも大変参考になった。

---

こんなところ。感想に偏りがあるのは講演そのものよりも、現場のエンジニア目線で面白いと思った部分に重点を置いているということで理解いただきたい。やっぱりこういうイベント来ると業界動向の把握には役立つね。

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

2014年2月25日火曜日

Unicodeのコードポイントとエンコーディング

Joel on Software を読んでて、どうもUnicodeについてよく分かってなかったことに気づいた。とうことで、まとめておこうと思う。

コードポイント

まず、Unicodeでは全ての文字に対してコードポイントと呼ばれるものが割り当てられている。例えば、アルファベットの A には U+0041 というコードポイントが割り当てられている。U+はUnicodeを意味し、後に続く数字は16ビットの16進数だ。「Hello」という単語はコードポイントで表すと、

H → U+0048
e → U+0065
l → U+006C
l → U+006C
o → U+006F

のようになる。コードポイント自体は、メモリ上に保持されたりファイルに記録されたりするデータの形式とは関係がない。コードポイントをどのようにデジタルデータとして具体化させるかを決めるのがエンコーディングだ。

エンコーディング

前述のように、コードポイント自体は文字のIDみたいなもので、それを具体的にどういうデジタルデータで保持するのかを決めなければならない。それがエンコーディングだ。

まず、コードポイントの16ビットの数字をそのままデータとして保持するのが「UTF-16」または「UCS-2」(2バイトだから)と呼ばれる。データはこうなる(16進数)。

H → U+0048 → 00 48
e → U+0065 → 00 65
l → U+006C → 00 6C
l → U+006C → 00 6C
o → U+006F → 00 6F

なお、上位8ビットと下位8ビットを入れ替える事も有り得る。CPUのアーキテクチャによっては、そのほうが処理が速い場合があるからだ。その場合はこうなる(16進数)。

H → U+0048 → 48 00
e → U+0065 → 65 00
l → U+006C → 6C 00
l → U+006C → 6C 00
o → U+006F → 6F 00

ビットを入れ替えたことを示すために、文字列の先頭に FF FE をつけるようにした。これはバイトオーダーマーク(BOM)と呼ばれ、これがBOM付きUTF-16だ。

ところが、UTF-16だと、英語ではコードポイント127以下(U+0000〜U+007F)しか使わないので、常に一文字に16ビット(2バイト)割り当てるのはもったいない。そこで、0~127(U+0000〜U+007F)のコードポイントは8ビット(1バイト)で表現することにし、それ以上のコードポイントをは頭に特定のビット列をつけることにより表現することにしたのが UTF-8 だ。ちなみに「Hello」のデータはこうなる(16進数)。

H → U+0048 → 48
e → U+0065 → 65
l → U+006C → 6C
l → U+006C → 6C
o → U+006F → 6F

UTF-8においては、コードポイントU+0000〜U+007F は1バイトで、前頭のビットは 0 になる(0xxxxxxx)。コードポイントU+0080〜U+07FF は2バイトで、最初のバイトの先頭は110、次のバイトの先頭は10と決められていて、空いたビットにコードポイントが入る(110xxxxx 10xxxxxx)。同様にして、コードポイントU+0800〜U+FFFF は3バイトで、最初のバイトの先頭は1110、後続の2バイトの先頭は10である(1110xxxx 10xxxxxx 10xxxxxx)。

例えば、日本語の「おはよう」のデータはこうなる(2進数)。

お → U+304a → 11100011 10000001 10001010
は → U+306f → 11100011 10000001 10101111
よ → U+3088 → 11100011 10000010 10001000
う → U+3046 → 11100011 10000001 10000110

わかりにくいかもしれない。最初の文字「お」を詳しく見てみよう。
3バイトだが、こうなっている。
11100011 10000001 10001010
UTF-8としての規則であるビットを黄色く塗ってみた。これを除いて、4ビットごとに区切り直してみるとこうなる。
11100011 10000001 10001010

0011 0000 0100 1010
これは、16進数に直せば
304a
となる。すなわち「お」のコードポイントU+304aを表現しているのだ。

とうことで、Unicodeのコードポイントとエンコーディングについてざっとまとめてみた。エンコーディングはここで挙げたもの以外にも数多くあるので、興味があったら調べてみるといいと思う。

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

2014年2月24日月曜日

エンジニアって勉強家だよね

僕は社会人1年目はITとは別の世界いたし、エンジニアですらない文系職だったから、エンジニアが他の仕事とどう違うのか、ある程度実体験から言える部分がある。その1つは、エンジニアって勉強家が多い職種ってことだ。

別に他の職種を悪く言うつもりはないし、エンジニアよりももっと勉強をしている職種も沢山あることは承知してる。ただ、それでも日々技術について新しい知識を吸収したり、より仕事でパフォーマンスを発揮するにはどうすればいいか学んだり、そういったことをエンジニアは当たり前にしているのをみると、やっぱり勉強家だなって感じるんだよね。

実はこれって、エンジニアの世界では当たり前でも、他の仕事では当たり前ではなかったりするんだ。エンジニアがなぜ勉強家なのか、その理由は次のようなことが考えられる。

まず、そもそも勉強し続けないと仕事にならないこと。次々と新技術が登場し、それら新技術を使えることを求められるから、勉強し続けないと仕事自体がなくなる可能性がある。職場以外の時間でも仕事に関する専門書を読んだりする人は結構多い。

次に、転職の多い業界であること。スキルを磨けばより良い職場に移れるチャンスもあるから、それがモチベーションになりうる。たぶんすべての職種の中でもトップクラスに転職の多い業界だろうし、転職市場からの求人情報も「◯◯のスキルを持つエンジニア募集!」なんて感じで日々流れてくるので、実際転職するかどうかは別にしてもスキルアップに必然的に意識が向くようになる。

そして、エンジニアの仕事自体が好きな人が多いこと。あくまで傾向だけれども、例えばシステム設計やプログラミングといった仕事に、全く興味がなくて就く人は少ない。残念だが世間から見たブラックなイメージもあるから(実際は職場による差が激しく、超絶ブラックもあれば普通もあるのだが)、わざわざ選ぶ人は少なくとも面白そうだと思ってこの業界に入る人が多い。でもって楽しく仕事している人は多いと思う。だからこそ進んで勉強するんだな。

結局何が言いたいのかといえば、勉強家のエンジニアは誇りを持っていいということなんだ。いやほんとに。そうやって自分がすごいと思っておけば、日々の勉強がもっと楽しくなるかもね(笑)

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

2014年2月23日日曜日

エンジニアは最高の道具(ツール)を使うべき

「弘法筆を選ばず」なんてことわざがあるけれど、これはどうしても筆を選べない状況で、その時に手元にあった筆をつかうしかないっていう緊急事態に、それでも素晴らしい字を書けるのが本当の実力者だという意味だ。あえて筆を選べる状況で質の悪い筆を選ぶのはナンセンスだ。

例えば、もし仮にプロ野球選手がちょっとどこかのバッティングセンターに行ったら、普段自分が使うバットじゃなくても高スコアを叩きだすだろう。じゃあ、実際プロの試合ではどうかといえば、自分のバットについては選び抜いている。バット職人が選手一人一人のバットをオーダーメイドで手作りするわけだし。その辺のスポーツショップで売ってるバットを使う選手はいない。ポイントは自分に合った最高の道具であるということだ。

ではエンジニアはどうだろう?みなプロとして自分に合った最高の道具(ツール)を使っているだろうか?これはハードもソフトも両方だ。ハードとしてはPCのスペック、キーボートやマウス(ポインティングデバイス)の質、モニターの解像度などだ。ソフトとしては、エディタ、統合開発環境、ブラウザなど。これらは、エンジニア自身に選択の余地があってしかるべきものだ。

まあ、組織の都合によってはPCのスペックやモニタ、使えるソフトなどは制限があるかもしれない。本当はそれらも選択できるようにすべきだけれど。でも、キーボートとマウス(ポインティングデバイス)くらいは、どんな組織であろうと自由が効くんじゃないかと思う。

僕自身は、エンジニアを始めた当初から、学生時代以来使い続けているキーボードをずっと使っている。派遣エンジニアをやっていた時も、派遣先の職場でお願いして持ち込ませてもらっていた。マウス(ポインティングデバイス)も使っているものは変わったが、基本的に自分のものを持ち込んで使ってきた。

エンジニアは道具選びも含めて仕事だと僕は思っている。良い道具は長く使えるし、パフォーマンスも高めてくれる。良い道具を使うのもプロとしての姿勢だと思うんだ。

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

2014年2月22日土曜日

Linuxでデーモン自動起動の順番はどう決まるのか

Linuxではシステムの起動時に自動的に起動するデーモンを登録する仕組みが用意されている。自動起動するデーモンの登録及び解除は、
  • Redhat系では chkconfig
  • Debian系では update-rc.d, sysv-rc-conf など
で行うことが可能だ。

で、上記コマンドの使い方は省略するが、どちらも行っていることは共通している。まず、デーモンの起動・終了などを制御するスクリプトは

/etc/init.d/{デーモン名}

という名前で用意されている。例えばSSHデーモンは /etc/init.d/ssh だ。これらのスクリプトには引数で start, stop, restart, status などを与えることができ、起動・終了・再起動・状態の取得などを行うことができる。

で、自動起動するデーモンについては、

/etc/rc{ランレベル番号}.d

にデーモン制御スクリプトへのシンボリックリンクを作成する。すると、システムの起動時に実行される /etc/init.c/rc スクリプトに参照され、実行される。例えばランレベル5でシステム起動時に自動起動されるスクリプトへのシンボリックリンクは、/etc/rc5.d 内に存在する。

その際の起動順序はシンボリックリンクの名前で決まる。具体的には

S{起動番号}{デーモン名}

である。この起動番号の小さい方から大きい方に向かって起動していく。例えば Apache2デーモンは S17apache2 、SSHデーモンは S18ssh という名前なので、Apache2 は SSH より先に起動することが分かる。

と、ここまでは前から知ってたんだけれども、じゃあ、この起動順序の番号はどこで決まってるのかについては特に調べもせず知らなかった。でもちょっと必要になったので調べてみた。どうやらRedhat系とDebian系で違うようだ。

Redhat系では、起動スクリプトである /etc/init.d/{デーモン名} の「コメント文」に特定の書式で書いてあり、chkconfig はそのコメントを見に行く。コメントの書式は

# chkconfig: - {起動番号} {終了番号}

となっていて、この起動番号が chkconfig を実行した際にシンボリックリンクの名前に使われる。

一方Debian系では、update-rc.d の場合、作成するシンボリックリンクの起動番号は自動的に決定されるらしい。ちょっと調べただけでは分からなかった。(知っている人いたらぜひコメントください。)

Debian系では別のコマンド sysv-rc-conf もあり(使うには sysv-rc-conf パッケージをインストールする必要がある)、こちらは

/var/lib/sysv-rc-conf/services

に記述された起動順序にそってシンボリックリンクを作成する。こちらの sysv-rc-conf は chkconfig にコマンドの書式が似ていることもあり、使いやすいと思う。

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

2014年2月21日金曜日

gitでローカルブランチをリモートブランチの内容で上書きする

gitでサブモジュールを使っていて、そのサブモジュールはリモートリポジトリからコードを引っ張ってくるためだけの目的で使っている場合、そのサブモジュールの内容は自分ではいじることはなくて、単にリモートリポジトリのコピーであればいい。

で、サブモジュールの内容を最新に更新しなきゃな―、って思って pullしたときに、衝突がワンサカ出た。リモートリポジトリ側でブランチがきちんと管理されていればこういう衝突は起こらないはずだが、まあそこは何か事情もあるのだろうし、完璧を求めても仕方ない。

ってことで、今回はmasterブランチを使っているので、ローカルのmasterブランチをリモートのmasterブランチの内容で強制的に上書きしてやることにする。masterブランチをチェックアウトした状態で、

git fetch origin
git reset --hard origin/master

でOK。すでに"git pull origin"してconflict警告が出た状態なら1行目は不要。

余談だけれど、"git pull origin"は結局"git fetch origin"と"git merge origin/ブランチ名"を一緒にやっているだけだから、pullは使わなくて良かったりする。今回のようなこともあるし、次回からはpullは使うのやめようかな。

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

2014年2月20日木曜日

[PHP]グローバル変数を使っているファイルをインクルードするときの対処

チームで共有しているライブラリにグローバル変数や関数が定義されていて、それを読み込んで使う状況ってあるよね。

PHPの場合だとオブジェクト指向を意識せずに書けてしまうので、クラス化されていない外部ライブラリを読み込むなんて状況もありうるわけで、その場合にはグローバル変数や関数が使われていることもあるだろう。

で、自分が書くクラスでもそのグローバル変数や関数を使うわけだが、いくつかやり方がある。ということで、今回はグローバル変数について自分の書くクラスで使う方法をいくつか挙げてみようと思う。(グローバル関数の方はまた別の機会で)

例えばグローバル変数を次のように定義したファイルがあるとしよう。

<?php
$a = 1;

このファイルをインクルードして、クラス内部で使うことを考える。


1. global 宣言をつける

<?php
include 'inc.php';

class Foo {
  public function bar() {
    global $a;
    return $a + 2;
  }
}

$f = new Foo();
echo $f->bar();
echo "\n";

bar()メソッド内部で普通に変数$aを呼んでもグローバル変数と見なされないが、global宣言をした変数についてはグローバル変数として扱うようになる。ただ、僕個人の意見としては、これは極力使わないほうがいい。ついついグローバルスコープと意識せずに$aを変更するコードを書いてしまい、バグの原因となりかねないからだ。


2. スーパーグローバル変数 $GLOBALS を使う

<?php
include 'inc.php';

class Foo {
  public function bar() {
    $a = $GLOBALS['a'];
    return $a + 2;
  }
}

$f = new Foo();
echo $f->bar();
echo "\n";

こちらは、グローバル変数 $GLOBALS を使うパターン。$GLOBALSはグローバル変数名をキーとし、グローバル変数を要素として持つ配列である。グローバル変数であることを明示している分、1.よりはマシと思う。いちいち$GLOBALS['a']と書くには面倒だし一旦ローカル変数に代入している。phpでは配列の代入はコピーとなるため、グローバル変数を予期せず変更してしまうリスクも回避できる。
しかし、このコードも、クラスの内部にグローバル変数を直接参照するコードが入ってしまう箇所があるのはよろしくない。なぜなら実際にはクラス定義の部分だけ別ファイルにしてインクルードすることが多いし、そうするとグローバル変数が原因のバグが発生した時のデバッグが大変だ。その点については1.と変わらない。


3. コンストラクタの引数として受け取り、インスタンス変数に格納する

<?php
include 'inc.php';

class Foo {
  protected $a;

  public function __construct($a) {
    $this->a = $a;
  }

  public function bar() {
    return $this->a + 2;
  }
}

$f = new Foo($a);
echo $f->bar();
echo "\n";


ということで、コンストラクタの引数でグローバル変数を受けとるパターン。これでクラス内部にグローバル変数を直接参照するコードはなくなった。インスタンス変数に代入しているので、クラス内のメソッドからは自由に使える。僕はこのパターンを使っている。やはりクラスの定義を書いている時はそのクラス内部で色々完結させたい訳で、グローバル変数は外へ出すのが一番いいと思う。

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

2014年2月19日水曜日

gitでローカルリポジトリにローカル専用ブランチを作って開発する

ソースコード管理はgitがもはや標準になっているけれど、ブランチの管理方法は人やプロジェクトによって様々だと思う。今回は、僕が現在採用してる方法を紹介したい。

想定として、中央リポジトリに各開発者がプッシュしていく、ハブ型の構成としよう。中央リポジトリにプッシュする際のブランチは、各開発者の名前(もしくは開発者を区別できる文字列)をつけたブランチとし、中央リポジトリの管理者が、各開発者の名前のついたブランチをmasterブランチにマージすることにする(だから各開発者はmasterにマージしない)。

※2014/03/13追記:ちなみに、masterにマージする際は--no-ffでしたようがよいのだが、それについては別の記事を書いた→ gitでmasterブランチへのマージを --no-ff で行う理由(コミットグラフ例つき)

さて、ここで各開発者は自分の名前のついたブランチ上で開発を行うわけだけれども、ここでコミットの頻度・タイミングをどうするかで悩むことになる。要するにこういうことだ。

  • コミットを細かく行ったほうが開発者としては状態を細かく保存でき、保存した状態からソースコードを比較できて開発を進めやすいし、作業失敗した時の保険にもなって安心だ。しかし、一度行った変更を更に変更したりあるいは戻したりすることも日常茶飯事であり、要するに細かく行ったコミットは「汚い」。中央リポジトリにプッシュする際にそのコミットは見せたくないし、masterブランチにマージする中央リポジトリ管理者も作業しにくいだろう。そしてmasterブランチのコミットログが大変見づらくなる。
  • コミットをまとまった機能ができたタイミングなど、ある程度大きい変更単位にすれば、コミットでの変更目的が分かりやすいし、開発途中での紆余曲折や変遷をいちいち記録しないのでコミットもすっきりする。中央リポジトリの管理者も楽だろう。masterブランチのコミットログも見やすい。ただし、開発者としては作業しづらいし何かあった際に作業内容が吹っ飛ぶ危険性もある。

じゃあどうするのかという話だけれど、僕が採用している方法は、
  • ローカルリポジトリにローカル専用のブランチを作る。
  • 中央リポジトリへプッシュするブランチは、ローカル専用ブランチからmarge --squashで変更を反映する。
  • marge --squashを行った後のローカル専用ブランチはそのまま残し、新たに別のローカル専用ブランチを切る。
というものだ。
ローカルリポジトリには、master、自分の名前、local/作成日付という名前で3種類のブランチを用意する。例えば、master、kasai、local/14.02.19という名前でブランチを作成したとしよう。


作業はlocal/14.02.19ブランチ上で行う。ここでのコミットは汚くなることを気にせず行っていい。例えば何回かコミットした状態としよう。


ここで、ある程度まとまった機能ができたとしよう。中央リポジトリへここまでの変更を1つのコミットとして提出することにする。まず、自分の名前のブランチ(kasai)へ移動する(git checkout kasai)。なお、EclipseのEgitだと太字が現在のブランチを表すので、移動しているのが分かる。


そして、先程まで作業していたローカル専用リポジトリの変更をmerge --squashで取り込む。(git merge --squash local/14.02.19)
変更を取り込んだら、git diff local/14.02.19を実行し、local/14.02.19とワーキングツリーが同じ内容になったことを確認し、git status で変更がインデックスに記録されたことを確認する。その後 git commit でコミットする。
コミットの際は自動的にそのコミットをまとめたコミットなのかコメントがデフォルトで入っているが、さらにコミットの要約文と、どのブランチのコミットをまとめたのかを書いておくといいだろう(この場合 local/14.02.19)。


これで自分の名前のブランチ(kasai)にローカル専用ブランチの変更を取り込めた。git diff kasai local/14.02.19 を実行し、同じ内容であることを確認しておこう。


開発を継続する時は新たにローカル専用ブランチを作成してそちらで作業する。(例えば git checkout -b local/14.02.20)


これを繰り返していくわけだ。新しく作ったローカル専用ブランチに何回かコミットするとこんな感じ。


ある程度まとまったら先ほどと同様にmerge --squashで自分の名前のブランチ(kasai)へ反映する。


さて、いかがだろうか。これで中央リポジトリのコミットログを汚なくせずに、なおかつ手元で細かくコミットを行って開発できる。ローカル専用ブランチ(local/...)が沢山できるけれど、気にならないならそのままにしておいてもいいし、ある程度時間が経ったら古いものは消してもいいだろう。

関連記事:gitでmasterブランチへのマージを --no-ff で行う理由(コミットグラフ例つき)

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

2014年2月18日火曜日

PHPのタイプヒンティングは配列の参照渡しの時も有効

PHPのタイプヒンティングはバグの防止につながるので積極的に使っている。ちょっと気になったのが、配列の参照渡しの時には動作は変わらないのかということだった。

タイプヒンティングは、関数やクラスメソッドの引数が、特定のクラスに属するインスタンスであることや特定のインターフェースを実装していること、あるいは配列であることなどを強制する。たとえば、fooメソッドの引数$argが配列でなければならない時は、次のように書く。
protected function foo(array $arg) {
  ...
}

と、ここまでは公式リファレンスにも書いてある通りだ。で、僕が気になったのは、次のように引数を参照渡しにするときにも、同様にタイプヒンティングが有効なのかどうかだ。
protected function foo(array &$arg) {
  ...
}

では実際に試してみよう。次のような簡単なプログラムを用意する。testClassのメソッドfooの引数は配列を想定しており、ここでタイプヒンティングを使っている。
<?php
class testClass {
  public function foo(array $arg) {
    foreach ($arg as $key => $val) {
      print $val . "\n";
      $arg[$key] += 1;
    }
  }
}

$test = new testClass();
$arg = array(1, 2, 3);
echo "first\n";
$test->foo($arg);
echo "second\n";
$test->foo($arg);

$arg2 = 'string';
$test->foo($arg2);

これを実行すると、次のような結果が得られる。
first
1
2
3
second
1
2
3

Catchable fatal error: Argument 1 passed to testClass::foo() must be of the type array, string given, called in /path/to/phpfile/test.php on line 19 and defined in /path/to/phpfile/test.php on line 3

タイプヒンティングにより、配列以外の引数を与えたことでエラーとなった。
では次に、メソッドfooの引数を参照渡しにしてみる。(1行だけ変更)
<?php
class testClass {
  // public function foo(array $arg) { // 変更前
  public function foo(array &$arg) {
    foreach ($arg as $key => $val) {
      print $val . "\n";
      $arg[$key] += 1;
    }
  }
}

$test = new testClass();
$arg = array(1, 2, 3);
echo "first\n";
$test->foo($arg);
echo "second\n";
$test->foo($arg);

$arg2 = 'string';
$test->foo($arg2);

これを実行すると、次のような結果が得られた。
first
1
2
3
second
2
3
4

Catchable fatal error: Argument 1 passed to testClass::foo() must be of the type array, string given, called in /path/to/phpfile/test2.php on line 20 and defined in /path/to/phpfile/test2.php on line 4

参照渡しにしたことにより、引数として渡した元の配列の中身が変化し、先ほどと違ってメソッドを2回呼ぶと結果が変わっている。そして、参照渡しにしたときもタイプヒンティングは参照渡しにしない時と全く同様に動作した。

ということで、配列で参照渡しの時もタイプヒンティングは気にせず使って大丈夫。まあよく考えれば、オブジェクトのインスタンスを渡す時は参照渡しだから、それと同じ動作と考えれば当然か。

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

2014年2月17日月曜日

SlimBlade TrackBall 向け KeyRemap4MacBook の設定

最近のMac OS X はトラックパッドのジェスチャが相当便利に出来ていて、トラックボールの使い手としては、トラックパッドのジェスチャみたいに色々できたらな〜なんて考えてしまう。

僕は SlimBlade TrackBall を使っていて、4ボタン構成のこれをフル活用すれば、トラックパッドのジェスチャ並みに完全とは行かなくても結構便利に色々な機能を使えるようになるに違いないと考えて、いろいろ試行錯誤した結果かなり便利になった。

ということで、今回は以下のことを実現する方法について書こうと思う。
  • 左右クリック同時押し → ドラッグロック&解除
  • 右上ボタン →「ボールでスクロールするモード」切り替え
  • 左上ボタン → Launchpad
  • ボールを右ひねり回転(ホイール下) → Mission Control
  • ボールを左ひねり回転(ホイール上)→ Application Windows
  • 左上ボタンと左クリック同時押し → Dashboard
  • 左上ボタンと右クリック同時押し → デスクトップを表示
色々組み合わせて上記を実現する。

まずは、TrackBallWorksを下記のように設定する。
  • 右下ボタン→右クリック
  • 左上ボタン→ボタン4
  • 右上ボタン→中央ドラッグ
  • 同時押しは設定しない

右上を「中央ドラッグ」にしているところがポイントだ。これが組み合わせで活きる。また、TrackBallWorksで同時押しの設定もできるが、タイムラグが結構気になるレベルで発生するのでここでは設定せず、後述するTrackBallWorksで設定する。

次は言わずと知れたKeyRemap4MacBookを設定する。入れてない人はインストールしよう。設定画面の「Misc & Uninstall」タブにある「Open private.xml」ボタンをクリックし、表示されたファイル(private.xml)を次のように編集する。
<?xml version="1.0"?>
<root>
  <item>
    <name>Private</name>
    <item>
      <name>Cange MouseButton4</name>
      <appendix>MouseButton4 to Launchpad</appendix>
      <appendix>(Simultaneous)MouseButton4 and LeftClick to Dashboard</appendix>
      <appendix>(Simultaneous)MouseButton4 and RightClick to Show Desktop</appendix>
      <identifier>private.cange_mouse_button4</identifier>
      <autogen>
        __PointingButtonToKey__
        PointingButton::BUTTON4,
        KeyCode::LAUNCHPAD
      </autogen>
      <autogen>
        __SimultaneousKeyPresses__
        PointingButton::BUTTON4, PointingButton::LEFT,
        KeyCode::DASHBOARD
      </autogen>
      <autogen>
        __SimultaneousKeyPresses__
        PointingButton::BUTTON4, PointingButton::RIGHT,
        KeyCode::MISSION_CONTROL, MODIFIERFLAG_EITHER_LEFT_OR_RIGHT_COMMAND
      </autogen>
    </item>

    <item>
      <name>Cange ScrollWheel</name>
      <appendix>ScrollWheel Up to Application Expose</appendix>
      <appendix>ScrollWheel Down to Mission Control</appendix>
      <identifier>private.cange_scrollwheel</identifier>
      <autogen>
        __ScrollWheelToKey__ ScrollWheel::UP,
        KeyCode::MISSION_CONTROL, MODIFIERFLAG_EITHER_LEFT_OR_RIGHT_CONTROL
      </autogen>
      <autogen>
        __ScrollWheelToKey__ ScrollWheel::DOWN,
        KeyCode::MISSION_CONTROL
      </autogen>
    </item>

    <item>
      <name>Drag and Drop via Simultaneous LeftClick &amp; RightClick</name>
      <appendix>(Simultaneous) LeftClick &amp; RightClick to Start Drag</appendix>
      <appendix>(Simultaneous) LeftClick &amp; RightClick to Drop</appendix>
      <identifier>private.drag_drop_simultanious_left_right_click</identifier>
      <autogen>
        __SimultaneousKeyPresses__
        PointingButton::LEFT, PointingButton::RIGHT,
        KeyCode::VK_MOUSEKEY_LOCK_BUTTON_LEFT
      </autogen>
    </item>

  </item>
</root>

保存したら、設定画面の「Change Key」タブに戻り、「ReloadXML」ボタンをクリックする。その後、設定画面で次のものにチェックを入れる。
  • Private配下の3つの設定全部
  • Pointing Device
    • Reverse scrolling direction
      • Reverse Vertical & Horizontal Scrolling
    • CursorMove to ScrollWheel
      • MiddleClick+CursorMove to ScrollWheel
      • CursorMove to ScrollWheel Option
        • Reverse Vertical Scrolling
        • Reverse Horizontal Scrolling
        • Enable dynamic scrollwheel diraction fixation (Horizontal/Vertical)
        • Disable Momentum Scroll

TrackBallWorksで右上ボタンを中央ドラッグにした設定と、KeyRemap4MacBookの中央クリック時にマウスカーソルの動きをホイールスクロールに変える設定が合わさって、あたかも右上ボタンが押されるたびにボールでスクロールするモードのON/OFFが切り替わるように動作するわけだ。

SlimBlade TrackBall はボールを水平方向にひねることでホイール回転を行えるのだが、スクロールをボールで行うよう設定したので、ホイール回転を別動作に割り当てることができる。

余談だが、ボールをひねる動作は1本指でボールの周囲をなぞるようにすることで簡単にできる。しかしスクロール目的のように頻繁に何度も繰り返すと意外と疲れるのだ。だからホイール1目盛り動かせば済むような機能を割り当てている。これなら指の動きもほんのわずかで負担もかからない。

ちなみにスクロールの向きを反転させているのは、より頻繁に使う下方向へのスクロールを指を曲げる方向に合わせるため。指を伸ばす動作よりも指を曲げる方向の方が疲れにくいからだ。同様に、ボールの右ひねりは指を曲げる方向で操作できるので、Application Windowsよりも使用頻度の高いMission Controlを割り当てている。

なお、ブラウザの操作関連(戻る/進む/更新など)はマウスジェスチャで。各ブラウザのプラグインで設定しよう。

さて、これでかなり快適になるはず。MacにSlimBlade TrackBallをつないで使っている方の参考になれば幸いだ。

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

2014年2月16日日曜日

早い話がオブジェクト指向プログラミングとは何だ

僕が初めてオブジェクト指向プログラミングについて学び始めた時、正直言って訳が分からなかった。インターネットでオブジェクト指向プログラミングについて解説したサイトなんて腐るほど大量にあるけれど、抽象的な話ばかりで結局どういうことなのかさっぱり意味不明だった。

それで悶々としていた時に、当時お世話になっていたある人にオブジェクト指向プログラミングが分からないと言ったら、じゃあ一緒にやってみようということで、近くの喫茶店でノートPCに向かって一緒にRubyでオブジェクト指向のプログラムを書いてみることになった。

それで最低限の要素だけを含む、確か10行くらいの短いプログラムを教えられるままに書いたんだ。え?これだけ?今まで読んだあの沢山のサイトに書いてあった仰々しい説明とか何だったの?って思った。なんだか今まで悩んでいたのがバカバカしくなった。それからオブジェクト指向でプログラムを書けるようになった。

で、僕のオブジェクト指向プログラミングへの理解を2行でまとめると、こうだ。

  • 命令をまとめたものが関数。
  • 関数と変数をまとめたものがオブジェクト。

これだけ。僕はこの理解ができた時に初めて分かったと思えた。

経験豊富な方からはあまりにも大雑把過ぎると怒られるに違いない。でも、初めてオブジェクト指向プログラミングというものに触れた人間に向けての一番最初の説明としてはこれでいいと思う。

とまあ、これで話を終わりにしてもいいんだけど、これからオブジェクト指向プログラミングを実際にしてみようという人も読んでくれているかもしれないので、もう少しだけ説明を加えておこう。上で関数との対比をしたので、同様に使い方の違いを説明すると、

  • 関数の使い方:
    呼び出したら処理結果が返ってくる。
  • オブジェクトの使い方:
    クラスに定義を書く。変数に入れて(インスタンスを作って)使う。内部の関数(メソッド)を呼び出したり、変数(プロパティ/フィールド等呼び方色々)を参照できる。

という感じかな。これも超大雑把(笑)だけど、でもやっぱり初めての人にはこのくらいの説明がいいと思う。まあ、あとは実際に使っていくうちにわかる。

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

2014年2月15日土曜日

ITエンジニアに向いている人

ITエンジニアに向いている人ってこんな人だと僕が考えることを書いてみようと思う。ちょうど就職活動の時季でもあるし、ITエンジニアを選択肢として考えている人は、参考にしてもらえれば幸いだ。

・細かいことを気にする人

まず最初はこれ。ネットワークやサーバの構築であれソフトウェア開発であれ、小さな違和感に気づき見逃さないことはITエンジニアとして非常にいい素質だ。具体的には仕様の矛盾やプログラムのバグといったものだが、そのようなミスはプロジェクトの初期に発見すればたいしたことがなくても、発見が遅れてプロジェクトが進行してしまうと大問題になってしまうことはよくある。神経質なくらいが丁度いいと言える。

・こだわりがある人

プログラミングをしていると、こだわりがあるかどうかはハッキリとコードに現れる。それは各種要素の命名規則であったり、制御構造の書き方であったり、構造化の度合いであったり様々だが、こだわりがある人はこれらのことに統一性があるコードを書く。そういうコードはバグも少ないうえ、他人が手を入れるときも作業しやすくて助かる。なにより美しい。これはプログラミングだけに限らず、各種設定ファイルや仕様書などのドキュメント類にもあてはまる。

・完璧主義な人

何事も両極端は良くないのはもちろんだけれど、元々完璧主義の人がスケジュールなどを考えて意図的に完璧さを抑えるのと、元々完璧主義ではない人が、絶対にミスの許されない部分に必死に意識を向けるのでは、前者のほうが容易なのは納得できるだろう。

・マニアックな人

仕事に限らず趣味でも何かに没頭し、普通はそこまで知らないようなことも知っていたり、普通やらないようなこともやっているような人、ということ。ITエンジニアの世界でも技術を追求することはプロとして当然ではあるが、趣味でもマニアックな領域に突っ走れる人のほうが簡単に高いレベルに辿り着けることは言うまでもない。

・空気を読まない人

プロジェクトが失敗するパターンの一つは、きっちり意識合わせを明確に行えてないのに、なんとなく進んでいってしまって、後になって意識のズレが発覚するパターンだ。要するに「こうだろう」と勝手に推測するのが間違いの原因であって、実は空気を読めない人のほうがこの壁を突破できる可能性が高い。「あれ?そもそもこれって、これでいいんでしたっけ?」と流れを無視して突っ込める方がいい。

・付き合いの悪い人

そもそもITエンジニアの職場では飲み会は少ないし、たとえあっても断ったって仕事に支障は出ない。多分、忘年会すら全く出なくても問題ない。むしろ、自分の時間を大切にするなり、独自に技術を研究するなりする人の方が仕事のクオリティも上がるというものだ。

・上司の言うことを聞かない人

エンジニアの世界では技術的に正しい事こそ正しいのであって、例え目上の人だろうが関係ない。相手を悪い気分にさせる必要はないが、自分が正しくて相手が間違っていると思うなら主張を曲げるべきではない。「課長が黒といえば黒、部長が白といえば白」みたいな価値観はエンジニアにはありえない。

さていかがだろうか。よくある特徴から一般的には欠点とされる特徴まで書いてみた。特に後半で挙げた特徴は、他の職種だと苦労するかもしれないが、ITエンジニアにおいてはむしろ活かせる特徴になる。思い当たるフシがある人はITエンジニアの世界に足を踏み入れたら幸せになれるかもしれない。

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

2014年2月14日金曜日

SmartyのデリミタをHTMLコメントにした時にタグ中の属性制御で文法エラーにしない方法

SmartyのデリミタをHTMLコメント(で囲む)形式にすると、テンプレートファイルをHTMLとしてエディタで扱うことができ、シンタックスのハイライトを効かせて色分けできるメリットがある。さらにはエディタのHTML文法チェック機能も使えるから、記述効率もアップしてバグも防げる。

だからデリミタを例えば <!--{ と }--> に設定しているプロジェクトも結構あると思う。これでエディタでシンタックスハイライトと文法チェックもできてパッピー!・・・と思いきや、ちょっと問題があるんだよな。

それは、タグ中の属性を制御したい場合。例えば、ラジオボタンやチェックボックスをチェックしているかどうかは、タグ中にchecked属性が存在しているかどうかで決まるから、条件によってチェックを入れたり外したりするには、HTMLタグの中にSmartyのデリミタを書くことになる。これが文法エラーになるわけ。checked属性だけでなくdisabled属性も同じだよね。具体的には、例えばこんな感じ。
<input type="checkbox" name="chk1" value="val1"
  <!--{if $cond1}--> checked="checked"<!--{/if}-->
  <!--{if $cond2}--> disabled="disabled"<!--{/if}-->
/>

上の例は、チェックボックスで$cond1がtrueの時にチェックを入れ、$cond2がtrueの時は操作できなくするもの。だがこれは当然HTMLの文法違反になる。まあ、Smarty通すから文法エラーなんか無視すれば問題ないと言えばそうなんだけど、だったらなんでわざわざデリミタをHTMLコメントで囲う形式にしたのかって話になってしまう。

そこで、少々トリッキーだが、次のようにすることで文法エラーを回避できる。
<input type="checkbox" name="chk1" value="val1"
  data-dummy1="<!--{if $cond1}-->" checked="checked<!--{/if}-->"
  data-dummy2="<!--{if $cond2}-->" disabled="disabled<!--{/if}-->"
/>

ダミーの属性を追加して左右のデリミタを共にダブルクオーテーションの内側に収めたのがポイントだ。これで例えば$cond1と$cond2がtrueの時は、
<input type="checkbox" name="chk1" value="val1"
  data-dummy1="" checked="checked"
  data-dummy2="" disabled="disabled"
/>

$cond1と$cond2がfalseの時は、
<input type="checkbox" name="chk1" value="val1"
  data-dummy1=""
  data-dummy2=""
/>

となるわけだ。data-*という属性はご存知HTML5の独自定義属性の書き方に従っている。ダミーだと分かりやすいように名前をつけるといいと思う。

美しいやり方ではないけれど、エディタや統合開発環境の文法チェックを活かせるという意味で実用的なメリットは有ると思う。

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

2014年2月13日木曜日

PHPなど動的型付け言語では変数名や関数名は長くていい

PHPなどの動的型付け言語って、Javaなどの型をはっきりさせる言語と違ってEcliseなどの統合開発環境による補完や定義の参照などが効きにくいところがある。

例えば、Javaの場合はインスタンスがコード中に登場したら、型が決まっているからメソッドの候補は自動的に決まり、例えばEclipseを使っていると候補がきっちり出てきてくれる。ところが、PHPの場合は、それがどのクラスのインスタンスなのか、いやそもそもインスタンスなのかどうかすら決まっていないから補完の候補が出せなかったりする。もちろん、EclipseのPDTなんかだと、ある程度文脈を読んでくれるから補完も出せるが、それだっていつも出せるわけじゃない。ということで、PHPを使う人間はしばしば、クラスの定義を自分で探して、どんなメソッドがあるか直接コードから探したりすることがある。

あるいは逆に、コードを読んでいて登場したメソッドがどんな働きをするのか、Javaであれば定義元にショートカット一発で飛んでいけるような場合も、PHPだとそれができないなんてこともある。そういうときも、メソッドを自分で探すことになる。

今述べたのはメソッドの例だけれども、これはインスタンスとか変数名とか関数でも同じようなことが言えて、要するに動的型付け言語では、候補探しや定義元の参照をしたいときには、既存のコードの中から探すという行為が発生しがちなのだ。

その際には既存のコードを一つ一つ探すのは大変だから、検索することになる。Eclipseの検索機能はPHPで開発してる時には上記の目的でよく使う。コードを横断的に検索し、結果を分かりやすく表示してくれるから、統合開発環境の検索機能はコマンドラインでgrepやfindを打つよりもずっと使いやすい。

その時に、スコープが分かれているからといって同じ変数名・関数名・メソッド名などをコードの色々な箇所で使っていると、目的のものを探し出しにくい。いや、もちろん言語仕様的には全く問題ないし、むしろ同じ目的の変数・関数・メソッドの名前を統一したほうがコード体系としては筋が通っているのかもしれない。だがしかし、開発をスムーズに進めていく上では、別の場所に登場した変数名・関数名・メソッド名は、たとえ同じ目的でも違う名前を付けていった方がいいと思う。

よって、区別のために色々と接頭辞や接尾辞をつけていくという意味で、変数名・関数名・メソッド名等は長くていいっていうことになるのだ。だから僕はPHPのコードを書く時にはわざと長めの関数名をつけたりするんだよね。

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

2014年2月12日水曜日

ソースコードはチャートとして見えるようにするべき

他人の書いたプログラムのソースコードは読みにくいものだというのは、プログラミングに関わる人達の共通認識であるように思う。ソースコードはある意味自分の脳内思考回路の具現化とも言えるわけで、他人の思考回路と自分の思考回路が違う以上、読みにくいものであるというのは自然なことだろう。

とはいえ、読みやすいコードと読みにくいコードというものはある。もちろん、インデントをちゃんと揃えることや変数名・関数名・クラス名・メソッド名の命名規則など、基本的な部分を押さえていることは前提だ。そのうえで、コードを読んでいて何を意図してどういう動作をしているのか、分かりやすいコードとわかりにくいコードがある。

分かりやすいコードというものは一大テーマであって、それだけで本ができてしまうくらいのものであり、実際数えきれないほどの本が存在する。だけれど、僕が常に気をつけていることはたった一つだったりする。

それは、

「ソースコードはチャートとして見えるようにする」

ってことだ。

チャートというのはフローチャートでもいいし、PADでもNSチャートでもHCPチャートでもいい。とにかく、コードをパッと読んだ時に、そのコードが処理を表現する何らかのチャートとして見えるようにする、ってことだ。どういう条件で処理が分岐し、あるいは繰り返し、その中でどんな処理をするのか、といったことが「パッと見で」把握できることだ。

もし処理が複雑でチャートとして見えなくなりそうならば、関数やメソッドを作って処理をまとめる。たったそれだけの話だ。何だ当たり前の話だと思われるかもしれないが、それができていないソースコードなんて腐るほど見てきた。しかもプロの現場でだ。

冒頭に述べたように、他人のプログラムはそもそも読みにくいものだ。そして、あるコードを一人が延々とメンテし続けることは極めて稀で、一定期間経つと別の人へとコードのメンテは引き継がれる。その際、他人に少しでも分かりやすいように、「ソースコードはチャートとして見えるようにする」ようにしておけば、無駄な労力も時間も節約できるはずだ。

なんでこんなに熱くなってるかって?まあその辺は察してください(笑)

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

2014年2月11日火曜日

Eclipseのエラー抑制:DLTK問題・ビルドパス・バリデータ

EclipseでGitなどのソースコードリポジトリから新たにプロジェクトをチェックアウトした時など、ビルド時や検証時に出現するエラーの対応に苦労するのは僕だけではないはず。必要なところだけにエラーをだして欲しいのだが、外部のライブラリや自分の担当ではない部分のエラーまで出てしまったりする。

中にはエラーを放置していて、数百ものエラーをそのままにしている人も見たことがあるけれど、必要なエラーだけを出すようにすれば、バグを未然に防いだり、非推奨のコードをうっかり使うのを防止してくれたりと、Eclipseのメリットを享受できるのだ。

まあでも、この設定意外とわかりにくい。僕も全部は分かっていないが、とりあえず現時点で自分が分かっている範囲で概要をまとめてみたい。まず、Eclipseでの出るエラーは

  1. DLTK問題
  2. <言語名>問題

の2つに大きく分けられると思う。後者は例えばXML問題とかHTML問題とかPHP問題とかいうやつだ。

で、対処法としては、

  1. DLTK問題 → ビルドパスの設定
  2. <言語名>問題 → バリデータの範囲設定とレベル設定(可能なもの)

という感じ。

まず、「DLTK問題」の場合は、ビルドパスから検証不要なコードを除外すればいい。プロジェクトのプロパティから「ビルドパス」(or相当するもの)を選択する。なお下の画像はPDTを適用しているため「PHP ビルド・パス」となっているが、適当に読み替えて欲しい。


「除外:〜」をクリックして「編集」ボタンをクリックし、除外パターンを追加すればいい。


次に「<言語名>問題 」の場合には、バリデータの範囲設定で不要なソースを除外する。プロジェクトのプロパティから「検証」を選択する。


各バリデータの右側にある設定ボタンをクリックすると、バリデータの対象範囲を設定できる。まず、特定の範囲だけにバリデータを適用したい場合はまず内包グループを追加し、そのグループにさらにルールを追加して適用範囲を指定する。特定の範囲を除外したい場合は同様にまず除外グループを追加し、そのグループにさらにルールを追加して除外範囲を指定する。


ここまでの設定で不要なソースに対してのエラーは抑制できると思う。ただ、必要なソースに対しても、無視して欲しいエラーが出ているケースもあるだろう。例えばWeb系言語のフレームワークではHTMLタグを出力する命令があったりして、その場合はテンプレートをHTMLとしてバリデートすると開始タグがないというエラーが出てしまったりする。

こういう場合には、バリデータのレベル設定を変更してやればいい。例えば、HTMLバリデータの場合は、プロジェクトのプロパティから「検証>HTML構文」を選び、「プロジェクト固有の設定を可能にする」にチェックを入れれば色々いじることができる。


だいたいこんなところ。他にも言語やプラグインによって設定できる箇所は増えると思うけれど、だいたい上記の設定は共通して行うことになるんじゃないかな。

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

2014年2月10日月曜日

BloggerでSyntaxHighlighterをAutoloadしつつObjective-Cもハイライト

このブログでも使っているSyntaxHighlighterは、ブログなどでソースコードのハイライトを手軽に実現してくれる便利なスクリプトで、サーバへのスクリプト配置に制限がある環境でも公式サイトのホスティングを使えば比較的少ない手順でソースコードのハイライトを実現できる。

Brushと呼ばれる言語ごとのハイライトのルールを記述したスクリプトと対応CSSを読み込むことで複数言語に対応しているのだが、ブログの記事ごとにSyntaxHighlighterの設定コードを書き分けると面倒だし、かといって共通部分にSyntaxHighlighterの設定コードを埋め込むなら、ブログで使いそうな全ての言語についてBrushを読み込まなくてはならず、表示速度が遅くなってしまう。

そこでAutoloader機能を使えば、必要なBrushと対応CSSだけを自動的に読み込むことで表示速度を遅くせずに済む。これで記事ごとにSyntaxHighlighterの設定コードを書き分けなくても、共通部分に1つコードを埋め込んでおけばいいってことになる。

ただ、公式ページに掲載されているAutoloader対応スクリプトは、カスタムBrushに対応したコードではない。そこで今回公式にはBrushのないObjective-Cのコードを掲載するにあたり、設定コードを書いてみた。

下記のコードをHTML/Javascriptガジェットとしてタイトル無しでサイドバーの一番下に配置し読み込んで使っている。コード中のObjective-CのカスタムBrushはここからダウンロードして少しだけ色をいじったもの。

<!-- Common Scripts -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<!--<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />-->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeMidnight.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>

<script type="text/javascript">
////////////////////////////////////
// Autoloader
////////////////////////////////////
function path()
{
  var args = arguments,
      result = []
      ;

  for(var i = 0; i < args.length; i++)
      result.push(args[i].replace('@', 'http://alexgorbatchev.com/pub/sh/current/scripts/'));

  return result
};

SyntaxHighlighter.autoloader.apply(null, path(
  'applescript            @shBrushAppleScript.js',
  'actionscript3 as3      @shBrushAS3.js',
  'bash shell             @shBrushBash.js',
  'coldfusion cf          @shBrushColdFusion.js',
  'cpp c                  @shBrushCpp.js',
  'c# c-sharp csharp      @shBrushCSharp.js',
  'css                    @shBrushCss.js',
  'delphi pascal          @shBrushDelphi.js',
  'diff patch pas         @shBrushDiff.js',
  'erl erlang             @shBrushErlang.js',
  'groovy                 @shBrushGroovy.js',
  'java                   @shBrushJava.js',
  'jfx javafx             @shBrushJavaFX.js',
  'js jscript javascript  @shBrushJScript.js',
  'objc obj-c             @shBrushPlain.js', // dummy
  'perl pl                @shBrushPerl.js',
  'php                    @shBrushPhp.js',
  'text plain             @shBrushPlain.js',
  'py python              @shBrushPython.js',
  'ruby rails ror rb      @shBrushRuby.js',
  'sass scss              @shBrushSass.js',
  'scala                  @shBrushScala.js',
  'sql                    @shBrushSql.js',
  'vb vbnet               @shBrushVb.js',
  'xml xhtml xslt html    @shBrushXml.js'
));

////////////////////////////////////
// Custom Brush Objective-C
////////////////////////////////////
/**
 * SyntaxHighlighter - Objective-C Brush
 * http://codepirate.seaandco.com/
 *
 * @version
 * 1.0.0 (February 22 2009)
 *
 * @author
 * Geoffrey Byers
 *
 * @copyright
 * Copyright (C) 2009 Geoffrey Byers.
 *
 * Licensed under a GNU Lesser General Public License.
 * http://creativecommons.org/licenses/LGPL/2.1/
 *
 *  Updated From:
 *  Code Syntax Highlighter for Objective-C.
 *  Version 0.0.2
 *  Copyright (C) 2006 Shin, YoungJin.
 *  http://scottdensmore.typepad.com/blog/2008/12/objective-c-cocoa-syntax-highlighter.html
 */

SyntaxHighlighter.brushes.ObjC = function()
{
  var datatypes =  'char bool BOOL double float int long short id void';

  var keywords = 'IBAction IBOutlet SEL YES NO readwrite readonly nonatomic nil NULL ';
  keywords += 'super self copy ';
  keywords += 'break case catch class const copy __finally __exception __try ';
  keywords += 'const_cast continue private public protected __declspec ';
  keywords += 'default delete deprecated dllexport dllimport do dynamic_cast ';
  keywords += 'else enum explicit extern if for friend goto inline ';
  keywords += 'mutable naked namespace new noinline noreturn nothrow ';
  keywords += 'register reinterpret_cast return selectany ';
  keywords += 'sizeof static static_cast struct switch template this ';
  keywords += 'thread throw true false try typedef typeid typename union ';
  keywords += 'using uuid virtual volatile whcar_t while';
  // keywords += '@property @selector @interface @end @implementation @synthesize ';


  this.regexList = [
    { regex: SyntaxHighlighter.regexLib.singleLineCComments,  css: 'comments' },    // one line comments
    { regex: SyntaxHighlighter.regexLib.multiLineCComments,   css: 'comments' },    // multiline comments
    { regex: SyntaxHighlighter.regexLib.doubleQuotedString,   css: 'string' },      // double quoted strings
    { regex: SyntaxHighlighter.regexLib.singleQuotedString,   css: 'string' },      // single quoted strings
    { regex: new RegExp('^ *#.*', 'gm'),                      css: 'variable' },    // preprocessor
    { regex: new RegExp(this.getKeywords(datatypes), 'gm'),   css: 'functions' },   // datatypes
    { regex: new RegExp(this.getKeywords(keywords), 'gm'),    css: 'functions' },   // keyword
    { regex: new RegExp('\\bNS\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bUI\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bCG\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('@\\w+\\b', 'g'),                     css: 'functions' },   // keyword
    ];

}

SyntaxHighlighter.brushes.ObjC.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.ObjC.aliases = ['objc', 'obj-c'];

////////////////////////////////////
// Settings and initialize
////////////////////////////////////
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
//SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all();
</script>

ポイントはAutoloaderの設定部分で、ファイルタイプに対応するBrushスクリプトのURLにダミーのURLを設定すること(SyntaxHighlighter.autoloader.applyの部分)。その設定後に、カスタムBrushのコードを追加して上書きしている。
実際の動作サンプルは、昨日の記事の下の方をご覧頂ければ。

参考サイト:
SyntaxHighlighter 3.0 にアップデート - 電脳スピーチ blog
アプリ開発の記録 SyntaxHighlighterでObjective-Cをきれいに表示

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

2014年2月9日日曜日

iPhoneアプリでHello World(Xcode5)

これまでモバイルアプリ開発には接点が全然なかったら、実はObjective-Cでコードを書いたことも一度もなかったんだよね。てことで、iPhoneアプリ開発をやってみようということでまずはお決まりのHello Worldしてみることにした。環境はOS X 10.9 MavericksにXcode 5.0。

まずXcodeを起動する。最初の画面で"Create a New Xcode project"を選んでプロジェクトを新規作成するか、メニューから"File > New > Project..."を選ぶ。


するとプロジェクトのテンプレートを選ぶよう言われるので、"Empty Application"を選ぶ。


プロジェクトの初期設定情報を聞かれる。次のように入力する。

Product Name: HelloWorld (製品名)
Organization Name: Takaaki Kasai (組織名)
Company Identifier: com.takaakikasai (ドメインの逆さ形式で指定)
Class Prefix: Hello (クラス名の頭につける文字列)
Devices: iPhone
Use Core Data: チェックは入れない

あとは適当な場所に保存する。


作成したプロジェクトが開くので、左のプロジェクトナビゲータから"HelloAppDelegate.m"を選ぶ。


該当部分のコードを下記のように修正する。

修正前
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.›
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

修正後
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.›
    self.window.backgroundColor = [UIColor whiteColor];
    
    // 追加ここから
    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 100.0f, 280.0f, 100.f)];
    label.text = @"Hello World!!";
    [self.window addSubview:label];
    // 追加ここまで
    
    [self.window makeKeyAndVisible];
    return YES;
}

コードを修正したら、Xcodeの左上の再生マークボタン("Build and then run the current scheme")をクリックする。すると、iOSシミュレータが自動的に起動して、アプリを実行してくれる。


おお、できた。それにしてもXcodeっていう専用の開発環境があるのは便利だな。

参考サイト:
http://php6.jp/iphone/basics/hello-world/

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

2014年2月8日土曜日

gitでサブモジュールを削除する(バージョンごとの方法まとめ)

gitのサブモジュールってバージョンによって削除の方法が違う。
サブモジュールのリポジトリのルートからの相対パスが path/to/submodule の場合、リポジトリのルートで次のようなコマンドを実行すればいいと思う。

v1.8.5以上
$ git submodule deinit path/to/submodule
$ git rm path/to/submodule
$ rm -rf .git/modules/path/to/submodule

v1.8.3以上v1.8.5未満
$ git submodule deinit path/to/submodule
$ git config -f .gitmodules --remove-section submodule.path/to/submodule
$ git rm path/to/submodule
$ rm -rf .git/modules/path/to/submodule

v1.8.3未満
$ git config -f .git/config --remove-section submodule.path/to/submodule
$ git config -f .gitmodules --remove-section submodule.path/to/submodule
$ git rm --cached path/to/submodule
$ rm -rf .git/modules/path/to/submodule
$ rm -rf /path/to/submodule

ちなみに、これまで使ってたgitはDebian7のパッケージ管理システム(apt-get)でインストールしたやつだったけど、バージョンは1.7.10.4と古かったので、サブモジュールの削除も面倒。この際バージョンアップすることにした。他にも表示が親切になるらしいし。

まずコンパイルに必要なパッケージを入れる。
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
 

git最新版のDebian公式パッケージはないので、ソースからコンパイルする。
https://code.google.com/p/git-core/downloads/list
から最新版をダウンロードする。この記事を書いている時点では1.8.5.4が最新だ。ダウンロードしたら解凍して、コンパイル&ルート権限でインストール。
% wget https://git-core.googlecode.com/files/git-1.8.5.4.tar.gz
% tar zxvf git-1.8.5.4.tar.gz
% cd git-1.8.5.4
% ./configure
% make
% su -
# make install

特に迷うこともなくあっさり完了。

ちなみに僕はzsh使いだけれど、git submodule deinitが補完で出てこない。gitのソースにはzsh用の補完関数スクリプトが添付されている(解凍したソースのcontrib/completion/git-completion.zsh)けど、これを使っても補完候補に出てこないので注意。

余談だけれどgitのバージョン上げたらgitコマンドの出力がモノクロからカラーになってて超便利。中でもgit diffで差分を色分けしてくれるのは素晴らしいね。

参考サイト:
git submoduleを今風な感じで削除する - Qiita
git submoduleについてのメモ - rcmdnk's blog
How do I remove a Git submodule? - Stack Overflow

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

2014年2月7日金曜日

Mac OS XでVPN接続が切れたら自動的に再接続する

先日構築したSoftEther VPNによるVPN環境で、遠隔地のMacでVPNを接続しておいたはずが、いつの間にか接続が切れているという問題が度々発生していた。

OS XのL2TPクライアントにKeep-Aliveの機能はついてないみたいだし、かと言ってSSHみたいに接続先のターミナル上で定期的になにか出すというわけにもいかない。

じゃあ、接続を切れないようにするんじゃなくて、切れたら再接続するようにできればいいんじゃないか、と思って探したらあった。

Mac OS X起動時にVPNで自動接続&途切れても再接続するように設定できる : ライフハッカー[日本版]

なるほど。しかし上記リンク先のコードはなぜかインデントが全角空白になってて面倒だと思うので、直したコードを以下に貼っとく。なお"VPN Service Name"の部分は接続したいVPN接続設定の名前に置き換えること(「システム環境設定」の「ネットワーク」に表示されている名前)。あと接続有無の監視間隔が120秒だったのを60秒に変更してある。

on idle
  tell application "System Events"
    tell current location of network preferences
      set myConnection to the service "VPN Service Name"
      if myConnection is not null then
        if current configuration of myConnection is not connected then
          connect myConnection
        end if
      end if
    end tell
    return 60
  end tell
end idle

AppleScriptエディタを開いて上記コードを貼り付け、メニューの「ファイル>保存...」を選択する。ダイアログで
  • ファイルフォーマットは「アプリケーション」を選択
  • オプションの「ハンドラの実行後に終了しない」にチェック
して保存する。


あとは保存したアプリケーションを実行すれば接続が開始され、60秒間隔で接続の有無を監視してもし接続が切れていたら自動的に再接続してくれる。

実際先日構築したVPN環境で試したら、接続が切れた時にちゃんと再接続してくれていたことが確認できた。これでVPNが落ちてないか心配せずに済む。

ちなみに、起動時に自動的にVPNに接続させるには、「システム環境設定>ユーザとグループ」でユーザを選び「ログイン項目」に追加しておけばいい。さらに実行中にDockにアイコンを出さない方法も上のリンク先に書いてあるので必要なら参照のこと。僕の場合、実行されているのが明示的にわかったほうがいいと思ったのでアイコン消す設定はしてない。

Apple Script も極めれば色々自動化できそうで面白そうだな。また機会があったら調べてみよう。

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

2014年2月6日木曜日

Keep-AliveとNATテーブルの関係

SSHなんかで外部のサーバに接続していて、しばらく何もせずに放置しておくと通信できなくなることがあったりする。対策を立てていないと頻繁に起きる。

これの原因は、接続の途中にあるルータ。ルータはプライベートIPアドレスの領域とグローバルIPアドレスの領域の境目では、アドレスを変換してやりとりする(プライベートIPアドレスをもつパケットはインターネットに出るとすぐに破棄されてしまうので)。その際変換元のプライベートIPアドレスと返還後のグローバルIPアドレスの対応関係を記憶しており、これがNATテーブルと呼ばれる。

もっとも、最近は単なるNATではなく複数のプライベートIPアドレスをポート番号を変えつつ通信することで1つのグローバルIPアドレスと対応させるNAPT(IPマスカレード)の方が多いと思う。
(参考 → NAT - ネットワークエンジニアを目指して

まあ、NATにしてもNAPTにしても、プライベートIPアドレスとグローバルIPアドレスの対応一覧表を内部に持っているわけだ。

で、NATテーブルにしろNAPTテーブルにしろ、無限に記録するわけには行かない。だから記録できる数の上限もあるし、一定時間使わなければ破棄していく。

で、接続を確立してから一定時間以上通信がないと、接続経路の途中にあるルータのNATテーブル(またはNAPTテーブル)が破棄されてしまい、いざ通信しようとしたらできない、ということになるわけだ。

で、対策としては、一定間隔で何か通信するということになる。一定間隔でデータが流れていれば、NATテーブルやNAPTテーブルの該当レコードはクリアされない。このような、接続を維持するために一定間隔でデータを流す行為や機能をKeep-Aliveと呼ぶわけね。

例えば簡易な方法としてはSSHではログインした端末でpingをlocalhost宛に打ったりとかする。他にもクライアントソフトが定期的に無害なパケットを流すなど、対策方法はいくつかある。
(参考 → keep-alive for ssh

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

2014年2月5日水曜日

Mac OS X で TUN/TAP 仮想ネットワークデバイス

仮想ネットワークデバイスを Mac OS X で試してみたくて調べてた。TUN/TAP は共に仮想ネットワークデバイスの仕組みで、名前自体は知ってたけれど違いがよく分かってなかったので改めて調べてみると、

TAP : データリンク層を操作する仮想デバイス
TUN : ネットワーク層を操作する仮想デバイス

ていうことらしい。TAPはソフトウェアルータ、TUNはソフトウェアブリッジに使える、っていう理解でいいのかな。

Mac OS X 用の TUN/TAP ドライバは下記からダウンロードできる。
http://tuntaposx.sourceforge.net/

インストールは特に難しいことはなくて、インストーラの指示に従えば完了する。

インストールが完了しても、システム設定画面などに変化はない。ただ、/dev を見てみると、tun0〜tun9 と tap0〜tap9 のデバイスが新たに作られているのが分かる。

ごく簡単な使い方の例としてはこんなスクリプトを見つけた。
http://lakm.us/logit/2013/01/handy-tap-interface-mac-osx/

上記リンク先からちょっと引用しよう。

#!/usr/bin/python
 
import os
while 1:
 try:
  file_path = '/dev/tap1'
  dev_file = os.open(file_path, os.O_RDWR)
  interface = 'tap1'
  while 1:
   pass
 except:
  print "tap interface is closing"
  exit()
  break

これを setup-tap1.py として保存したら、

[root]# ./setup-tap1.py &
 

て感じでバックグラウンドで実行する。
すると ifconfig で tap1 が出現するようになるので、IP アドレスの設定なども可能。

と、この辺まで調べたあたりで、TUN/TAP 仮想ネットワークデバイスとは別の方法を検討し始めたので今回はここまで。

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

2014年2月4日火曜日

SEもプログラマもエディタで空白文字表示してくれ(Vim,Eclipse設定例付)

今日仕事でちょっと気になったのが、設定ファイルやソースコードに全角空白が明らかに意図しない位置に紛れ込んでいること。

実際の動作に支障があるわけではない。しかし、構築や開発を進める上で、例えばバッチファイルや自動処理プログラムに思わぬ形で影響することがある。今日も他人が書いた設定ファイルとソースコードに全角空白が紛れ込んでいて、それで変な動作をするので原因を調べていて、少し時間を余計に取られてしまった。コマンドライン上ではパッと見ではわからないからなぁ。

地味な話だけれど、エディタで全角空白を表示する設定にしていれば気づくはずだ。なお全角空白に限らず、空白文字は表示させておくことを強くお勧めする。インデントにスペースとタブが混在するとか、行末に余計な空白があるとか、そういうのは後々ミスの原因になりかねないんで。

ちなみに僕がよく使うエディタは Vim と Eclipse 内蔵のテキストエディタなので、設定方法を書いておく。参考になれば。

Vim の場合

~/.vimrc(Windows では Vimをインストールしたフォルダの _vimrc )に次のように記述する。

set listchars=tab:>\ ,trail:-                            " 特殊文字の代替文字(タブと行末のスペース)
highlight SpecialKey ctermfg=darkgray                    " タブの色
highlight NonText ctermfg=darkgray                       " 改行の色
highlight ZenkakuSpace cterm=underline ctermfg=darkgray  " 全角スペースの色
match ZenkakuSpace / /                                  "スラッシュの間に全角スペース

Eclipse の場合

メニューから「ウィンドウ>設定」を選択し、設定ダイアログで「一般>エディター>テキスト・エディタ」の画面を出し、「空白文字の表示」にチェックを入れる。その横の「可視性の構成」をクリックすると表示する文字をカスタマイズできる。下の画像参照。


他のエディタについてはよく知らないけれど、大抵ついてる機能だから調べれば見つかると思う。というより、SE・プログラマは空白文字表示できないエディタは頼むから使わないで。

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

2014年2月3日月曜日

エンジニアは上司の言うことを素直に聞くな

これは IT エンジニアに限った話ではないと思うんだけれど、エンジニアは上司に言われたからといって素直に言われた通り受け入れるようではダメだと思うんだ。

というのは、エンジニアというのは技術で仕事上の貢献をしているわけで、その分野の技術に関しては誰にも負けないと思えるのが理想だよね。それは相手が上司だろうが関係ないわけ。技術的に自分のほうがより良い案を持っていると思ったら、相手が上司でも、それは堂々と主張すべきなんだよね。人間関係の調整がいくら上手なろうが、技術的に改善できることがあるのに、それを自ら抑えこんでしまってはダメなんだ。

もちろんそのためには技術的な面で日々自分のスキルアップをし続けなければいけないと思うし、そうしなければハイハイと言うことを聞くだけになる。エンジニアにとって学ぶべき知識なんて無限にあるし時代とともに技術は常に新しくなっていくわけで、上司だってそんなに網羅できているわけがない。その意味では少なくとも自分の今担当している部分に関する知識を徹底的に追求すれば、そんなに長い期間かからずに上司より詳しく慣れる可能性は高いわけだ。

たとえ技術の話以外のことだったとしても、仕事の進め方や組み立て方の話だったら、それだって結局自分なりの考え方を持っているべきなんだよね。まあ IT の分野ではマネジメントすら技術の一部という扱いになってるし、その意味では全部が技術の話と言えなくもないけど。ともかく少なくとも、自分の考え方の主張はしてみて、その上で上司の言うことに従うかどうか決めるべきであって、ただ言われるがままに仕事をするってのは良くないと思うんだ。

エンジニアは技術というモノサシがあるおかげで、上司・部下などの組織の上下関係に関係ない基準を持って話ができる。だから「課長が白と言ったら白、でも部長が黒と言えば黒」みたいな状態とは基本的には無縁であるべきなんだ。だからエンジニアは上司の言うことを素直に聞くなって言ってるわけ。結果的に言うことを聞くことになったとしても、そういう考え方が前提にあるかどうかで全然違うわけだからね。

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

2014年2月2日日曜日

SoftEther VPNでLinux上にVPNサーバ立てて遠隔地と手元のMacで画面共有

Mac OS X にはデフォルトで画面共有の機能がついていて、これがなかなかに使いやすくていい。この画面共有は同じネットワーク内にある Mac にはアカウント名とパスワードさえ分かれば接続することができる。

一方、遠隔地にある Mac に対しては同一の Apple ID を設定して「どこでも My Mac」機能を利用すれば接続はできるけれども、ちょっと事情があって同一の Apple ID を設定していない Mac にも画面共有で接続したかった。

で、以前は LogMeIn の Free 版を使っていたんだけど、なんと有料化されるとのこと。(参照: リモートアクセス「LogMeIn」無料版が突如終了、完全有料制に - ITmedia ニュース)うーむ、これは困った。

実はこの件とは別に自分用にパブリッククラウドサービスを契約してて、仮想サーバを立てて CentOS を入れて運用している。そこにはグローバルIPも割り当ててある。じゃあそれを VPN サーバにしてしまおうと思いついた。それなら新たに費用が発生することもないし。

てことでフリーの VPN サーバを探したら、SoftEther VPN ってのがあるそうで。OpenVPN よりも速いという謳い文句つきだ。これをインストールして使ってみることにした。

SoftEther VPN  Server のダウンロード

SoftEther VPN  のダウンロードページから VPS サーバをダウンロード。コンポーネントは「SoftEther VPN  Server」を選ぶ。

SoftEther VPN  Server のインストール

インストール方法は公式ドキュメントの「7.3 Linux へのインストールと初期設定 - SoftEther VPN プロジェクト」に従えば OK 。

管理者パスワードの設定

管理者パスワードを設定します。サーバ付属の vpncmd で「ServerPasswordSet」コマンドを実行する。マニュアル通りにインストールしたのなら vpncmd のパスは  /usr/local/vpnserver/vpncmd になる。

仮想ハブの作成

仮想ハブを作成するか、最初に「DEFAULT」という名前の仮想ハブができているので、それも使える。今回は「DEFAULT」を使うことにするので、新たに作成はしない。

ユーザの作成

仮想ハブにユーザを作成する。vpncmd で「Hub DEFAULT」コマンドを実行して「DEFAULT」仮想ハブを管理対象とした後「UserCreate」コマンドを実行する。

IPSec 機能の有効化

L2TP over IPsec を使えるようにする。vpncmd で「IPSecEnable」コマンドを実行する。有効にするのは L2TP over IPsec だけでいい(暗号化なしの L2TP と EtherIP / L2TPv3 over IPSec はいらない)。

SecureNAT 機能の有効化

SecureNAT 機能は、簡易 DHCP サーバ機能と仮想 NAT 機能の2つからなる機能。vpncmd で「SecureNatEnable」コマンドを実行する。なお、このコマンド実行後に簡易 DHCP サーバ機能と仮想 NAT 機能は両方とも自動的に有効化される。

簡易 DHCP サーバ機能の設定

SecureNAT 機能のうち、簡易 DHCP サーバ機能を使うので設定を確認する。今回はデフォルトのまま使うので設定変更しないけれど、確認はしておく。vpncmd で「DhcpGet」コマンドを実行する。デフォルトでは 192.168.30.10〜192.168.30.200 の範囲のIPアドレスが配布される設定なっている。

仮想 NAT 機能の無効化

今回は仮想 NAT 機能は使わないので無効化しておく。vpncmd で「NatDisable」コマンドを実行する。

VPN サーバのファイアウォールで必要なポートを開ける

Mac 側からは L2TP over IPSec で接続するので、UDP 500 番と UDP 4500 番のポートの通信が VPN サーバのファイアウォールを通過できるようにしておく。

Mac の VPN 接続設定及び接続実行

ここまでできたら、Mac から VPN に接続できる。この操作は接続元である手元の Mac と接続したい遠隔地の Mac の両方で行う必要がある。もちろん遠隔地の Mac は VPN に接続したままの状態にしておく。公式ドキュメントの「Mac OS X からの接続方法 - SoftEther VPN プロジェクト」に従えば OK 。
なお公式ドキュメントでは「すべてのトラフィックを VPN 接続経由で送信」オプションにチェックを入れるよう書いてあるが、VPN 経由でインターネットに出る経路がない場合はこれにチェックを入れるとインターネットにつながらなくなる。画面共有が用途の場合は画面共有したいマシンとしか VPN 経由で通信しないのでチェックを入れなくてもいいだろう。
ちなみに遠隔地の Mac で VPN 接続が切れたら自動的に再接続させる方法はこちら

DHCP テーブルを確認

ここからは、遠隔地の Mac へ手元の Mac から接続する時の手順になる。あらかじめ両方の Mac が VPN に接続されていること。まず VPN サーバにログインして DHCP テーブルを確認する。vpncmd で「DhcpTable」コマンドを実行する。割り当てられた IP アドレスとクライアントホスト名の対応が分かるので、接続したい遠隔地の Mac に割り当てられた IP を把握しておく。

画面共有を実行

接続元の Mac で Finder を起動して、メニューからの「移動>サーバへ接続...」を選択する。「サーバへ接続」ダイアログが表示されたら「サーバアドレス」欄に「vnc://(遠隔地の Mac の IP アドレス)」を入力して「接続」ボタンをクリック。

これで接続できるはず!僕の環境では、LogMeIn を使っていた時よりもはるかに快適に画面をリモートから操作できた。予想以上に反応速度が改善されてびっくり。スクロールとかもスムーズにできるし。

「グローバル IP の付与された Linux サーバを持っいてなおかつ root  権限を使える」という条件があるけれども、もし当てはまる方はぜ SoftEther VPN による VPN サーバ構築をぜひ試してみては。

※2014/02/03 VPNサーバのファイアウォールのポート開けについて追記。
※2014/02/05 VPNサーバの IPSec 機能有効化について追記。
※2014/02/07 MacのVPN接続設定部分に追記。

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

2014年2月1日土曜日

[ruby] Sinatra + Cucumber + Capybara が良さそうな感じ

さて、最近新しいプロジェクトで Web インターフェースを作っていくにあたり、どの言語でどのフレームワーク使うかについて検討している。

やっぱ言語は ruby だな。コード書いてて一番楽しそうだし、読みやすいからメンテしやすそうだし。となるとフレームワークをどうするか。

で、ruby の軽量フレームワーク Sinatra について調べてた。Rails でももちろんいいし、実際僕は Rails 使ったことないので興味もあるんだけど、関わってる製品の事情で軽量なフレームワークの方が良さそうなんで Sinatra が第一候補になった。

そしてせっかくフレームワーク使うなら、テストが効率よくできる仕組みがすごく重要。やっぱりテスト駆動で開発したいもんで。初めてテスト駆動を実際に仕事で使った時は衝撃だったなぁ。それ以来テスト駆動開発のファンになった。

さて、Sinatra の公式ページを読むと、テストについては基本的には Rack ベースらしい 。Rack は Webアプリケーションインターフェースって言葉だけ聞くとなんじゃそりゃって感じですが、要するに「こういうリクエストが来たらこうレスポンスを返すっていう決まりを書く時の形式を共通化するもの」って理解でいいかな。

で、実際テストを書く時には
・テストケースを書く → Cucumber を使う
・テストの中身を書く → Capybara を使う

という感じに組み合わせれば良さそうな感じ。

Cucumber を使うとテストケースを自然言語に近い形式で書ける。設定すれば日本語も使えるらしい。まあ実際に日本語を使ってテストケースを書くかどうかは別としても、直感的に見て分かりやすいテストケースを書けるのは、テスト駆動開発を行う上でも助かりそうだ。

Capybara は Web アプリケーションの実際のテストの中身を描くのに便利な機能を提供してくれる。例えばレスポンス中の HTML のある要素に特定の文字列が入っていることを確認したい、なんてときに自力でレスポンスボディから要素を抽出するのは面倒だが、それを XPath や CSS セレクタの形式で抽出できるようにするメソッドを提供してくれたりする。同じようなもので Webrat というのもあるらしいが、Capybara のほうが後発らしく使いやすいみたい。

あと、Capybara はテストを Webkit 経由で実行できるドライバが用意されていて、なんと Javascript のテストも一緒にできるらしい。すごい。以前 Javascript のテストは QUnit でやってて分離してたから効率は良くなかったけど、これできたらテスト効率もアップしそうだな。

参考サイト:
Testing Sinatra with Rack::Test
jnicklas/capybara · GitHub
ASCIIcasts - “Episode 257 - request specとCapybara”
あーありがち - 実はCapybaraってよく分からないんです
第21回 Railsアプリの受け入れテストをCucumberで書こう:Ruby Freaks Lounge|gihyo.jp … 技術評論社

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