Samba上でTortoiseSVNを扱うときの設定

メモ。

create mask = 664 # ファイル作成時のマスク
directory mask = 664 # ディレクトリ作成時のマスク
delete readonly = yes # readonlyなファイルを削除可能にする

unixだとディレクトリに書き込み権限があればreadonlyなファイルも削除できるけど、windowsでは削除できない。 しかしSubversionは.svn以下のファイルを0444で作成することがあるので、TortoiseSVNだと処理が途中でストップしてしまう。 それを回避するためにはwindows上でreadonlyなファイルを削除できるようにdetele readonly = yesを設定する必要がある。 という認識で合ってるかな?

5.3.1 Creation masksあたりを参照。


rubygemsをバージョンを指定して更新

メモ。

gem install rubygems-update -v <バージョン>
update_rubygems

バージョンの一覧はrubygems.orgなどで確認するとよい。


Catalystで未定義アクションのテンプレートを描画

Railsだとコントローラにアクションが定義されていなくても、リクエストに応じたビューのテンプレートファイルが存在すればそれが描画される(ルーティングはされてる必要がある気がする)。

で、Catalystでもそうなってるのかと思ったけど、どうやらそうなってないらしい(?)ので、自分で書いてみた。

まずはコントローラの共通の基底クラスのdefaultアクションで、リクエストに応じたテンプレートを探す。

package MyApp::Controller::Base;
use Moose;

BEGIN { extends 'Catalyst::Controller' }

sub default :Private {
  my ($self, $c) = @_;

  if (my $template = $self->suggest_template($c)) {
    # テンプレートが存在すればそれをセットする
    $c->stash->{template} = $template;
    
    # 子孫クラスでの処理をendアクションまですっ飛ばす
    $c->detach;
  }
  else {
    # ファイルがなければ404
    $c->res->body('Page not found');
    $c->res->status(404);
  }
}

# テンプレートの描画はお任せ
sub end :ActionClass('RenderView') {}

#
# namespace+action+suffixに対応するビューのファイルが存在すれば
# そのrootからの相対パスを返す。存在しなければundefを返す。
# 例:GET /hoge/fuga => hoge/fuga.html
#
# suffixはstaticとして評価されないtmpl,tt,tt2,html,xhtmlのいずれかで、
# この順序でマッチしたものから優先的に返す。
#
sub suggest_template {
  my ($self, $c) = @_;

  foreach (@{$c->{static}{ignore_extensions}}) {
    my $relative_path = $c->req->{_path}.".".$_;
    my $absolute_path = $c->{root}."/".$relative_path;

    return $relative_path if -e $absolute_path;
  }

  return undef;
}

実コントローラクラスではMyApp::Controller::Baseを継承するだけでOK。

package MyApp::Controller::Root;
use Moose;

BEGIN { extends 'MyApp::Controller::Base' }
__PACKAGE__->config(namespace => '');

もし実コントローラクラスでdefaultアクションをオーバーライドする場合には、親クラスのdefaultを呼ぶようにする。

sub default :Private {
  my ($self, $c) = @_;

  # 親クラス(MyApp::Controller::Base)のdefaultアクションを呼ぶ
  # もしテンプレートファイルが存在すればその時点でdetachされるので以降の処理は呼ばれない
  $self->next::method($c);
  
  # 以下、実コントローラならではの処理
}

ライブラリとか使ったもっとましなやり方もあると思うんだけど見つからなかった。とりはえずはこれで動くので良し。


Catalystでnamed_scope風実装

泣きながらPerl/Catalystを書いている今日この頃です。慣れない言語は辛い。

PerlのORマッパーであるDBIx::Classを使ってるんですが、とりあえずDBIx::Class beginnersを見ると、使い方の指針やFat Modelっぽい書き方が載っていて大変良いです。ActiveRecord大好きです。

で、CatalystにはRailsのnamed_scopeのような便利な記法は標準では提供されていません。多分。でもDBIx::Class::ResultSetのサブクラスを使うようにすれば、それっぽいことはできるようになります。

まず

アプリ名をMyAppとします。

my $rs = $c->("DBIC::Foo")が返すのはDBIx::Class::ResultSetのインスタンスで、これはクエリを投げて返ってきた集合に対して処理をするために用います。

一方、例えばmy $foo = $rs->firstが返すのはMyApp::Schema::Result::Fooのインスタンスで、これは個々のレコードに対して処理をするために用います。

なので、RailsのActiveRecordの雰囲気的にはDBIx::Class::ResultSetにモデルのクラスメソッド・named_scopeを実装して、MyApp::Schema::Result::Fooにモデルのインスタンスメソッドを実装する、という感じになります。

::Result::Fooに::ResultSet::Fooを対応付ける

デフォルトだとResultSetにはDBIx::Class::ResultSetが使われるため、全てのテーブルで共通の実装をすることになってしまい、あまり嬉しくありません(共通の処理を実装するなら別ですが)。

なのでまずはMyApp::Schema::Result::FooにMyApp::Schema::ResultSet::Fooを対応付ける必要があります。

この機能は各::Result::*で共通で利用したいので、lib/MyApp/Schema/Result/Base.pm を継承して使えるようにしておきます。

package MyApp::Schema::Result::Base;
use strict;
use warnings;

# 継承されたときに呼ばれる、らしい
sub import {
  my $caller = caller; # 継承の子のパッケージ

  # 継承の子側に対して、resultset_classに::ResultSet::Fooを設定する。
  # クラス名はResultをResultSetに置換したもの。
  $caller->resultset_class(sub {
    my $pkg = $caller;
    $pkg =~ s/Result/ResultSet/;
    return $pkg;
  }->());
}

1;

さて、lib/MyApp/Schema/Result/Foo.pm ではBase.pmを継承するだけです。

package MyApp::Schema::Result::Foo;
use strict;
use warnings;

use MyApp::Schema::Result::Base;

1;

以上で MyApp::Schema::ResultSet::Foo があれば$c->model("DBIC::Foo")はそのインスタンスを返し、無ければこれまでどおりDBIx::Class::ResulSetのインスタンスを返すようになります。

なお、MyApp::Schema::ResultSet::FooはDBIx::Class::ResulSetを継承する必要があります。

package MyApp::Schema::ResultSet::Foo;
use base 'DBIx::Class::ResulSet';
1;

named_scope風のものを::ResultSet::Fooに実装する

package MyApp::Schema::ResultSet::Foo;
use base 'DBIx::Class::ResulSet';

sub recent {
  my ($self, $c) = @_;
  return $self->search({}, { order_by => { -desc => [qw/created_at/] }, rows => 10 });
}

sub public {
  my ($self, $c) = @_;
  return $self->search({ is_public => 1 });
}

1;

searchはまた::ResultSet::*を返すので、チェインすることでさらに絞り込めます。

my $foos = $c->model("DBIC::Foo")->recent->public;

遅延評価されるため、SQLの発行も最後の1回だけです。named_scopeっぽい。


git-svnを使う際に参考になるサイト

git-svnを使うことでsubversionのリポジトリをgitで操作できる。 チェックアウトしてからの作業の流れは次のような感じ。

  1. svnリポジトリからgit-svnでチェックアウトしてきてgitのローカルリポジトリに変換
  2. gitでローカルリポジトリに変更をコミット
  3. ローカルリポジトリにコミットされた内容をgit-svnでsvnリポジトリにコミット

以下、参考になるサイトを列挙してみる。

git-svnの使い方を覚えた はgit-svnのチュートリアル。

Git入門 ゼロから始めるGitドリルはgitそのもののチュートリアル。Git/Subversionコマンド対応表なんてものもある。

操作ミスなど困ったときにはGit初心者が絶対に覚えておくべきコマンドgit-svn駆け込み寺などが役立ちそう。

ただgitはsvnとは思想が全く違うので単純に対応はしないと思う。ブランチングのモデルについてはRubyKaigi2009でのgit/githubに関するセッションが、英語だけど図が多用されていてイメージしやすい。

svnを使わずにgitをリモートのリポジトリとして開発する際にはブランチの作成方法等でルールが必要になると思うけど、A successful Git branching model(翻訳版)に従えば絶対に大丈夫という話もあったりする。


perlbrew+cpanmでperlの環境を作る

仕事でperlを触ることになったのでとりえずperlbrewとcpanmを入れてみた。

下準備

とりあえずホームディレクトリにbinを作ってパスに追加する。

$ mkdir -p ~/bin
$ export PATH=$HOME/bin:$PATH # ~/.zshrc等にも追加
$ cd ~/bin

perlbrewを入れる

ホームディレクトリ以下で複数バージョンのperlを管理できる。/usr以下はあまり汚したくないので導入。

perlbrewはデフォルトだと~/.perlbrewに設定ファイルを、~/perl5以下にインストールしたperlを入れてくる。Finderをひらいてperl5が出てくるのも邪魔なので、ここではPERLBREW_ROOTを指定して~/.perlbrewに統一している。もしかしたら悪影響があるかもしれないけど、その時はやり直す。

$ curl -LO http://xrl.us/perlbrew
$ chmod +x perlbrew
$ PERLBREW_ROOT=~/.perlbrew perlbrew init
$ vim ~/.zshrc
$ source ~/.zshrc
$ perlbrew install perl-5.12.2
$ perlbrew switch perl-5.12.2

switchはデフォルトのバージョンを指定するらしい。rvmで言うところのuseコマンドに–defaultを付けたのと同じ。そのセッションでだけ変更したい場合はuseを使う。

~/.zshrcには次を追記する。bashrcって書かれてるけどとりあえずzshでも動いてる。

# === perlbrew
if [ -f $HOME/.perlbrew/etc/bashrc ]; then
  source $HOME/.perlbrew/etc/bashrc
fi

cpanmを入れる

cpanより軽くて使いやすいらしい。

$ curl -LO http://xrl.us/cpanm 
$ chmod +x cpanm
$ cpanm --self-upgrade

CPANモジュール(例えばYAML)を入れるときは次のようにする。

$ cpanm YAML

インストール先は-lオプションで指定できる。 perlbrewで現在使用しているperlは$PERLBREW_ROOT/perls/current以下に入っているので、モジュール類もそこに入れるといいかもしれない。

$ cpanm -l $PERLBREW_ROOT/perls/current YAML

今後

cpanモジュールでcatalystを入れなきゃならないんだけど、どうもまとまった資料がなくてわからない。Catalyst::Runtimeのインストールに失敗しているのかも。catalyst.plはどこにできるんだろう?

できればlocal::libを使って適当に管理したいんだけどイマイチ使い方がわからない。

参考資料


4/20のTokyo Rubyist Meetupに行ってきた

4/20のTokyo Rubyist Meetup – After the Earthquakeに行ってきた。Twitterのハッシュタグは#trbmeetup

(あとで書く)


これはやっておいて損はないよ

これは新卒準備カレンダー 2011春という企画に向けたエントリです。

このエントリでは「エンジニアをやるならこれはやっておいて損はないよ」ということを書いてみようと思います。

まだまだ社会人2年目のヒヨッ子なのでそれ以外にも大事なことはあるかと思いますが、 そこはほかの参加者の方々の記事を読んだり、 「プログラマが知るべき97のこと」を読んだりして 補完していただけたらと思います。

その前に、どんな人がこれを書いているのか

@ikmと申します。

理系の大学院(非コンピュータ系)を卒業したのち、 現在は某社で携帯電話向けWebサービスの開発をやっています。新卒3年目です。

どちらかというと「プロマネになって大きなプロジェクトを動かしたい」というよりは 「技術力を高めて自分でなにか作っていきたい」人です。 プログラミングは高校生の頃から趣味でやってましたが、未だにへっぽこです。 スーパープログラマーへの道はまだまだ遠い…。

課外活動としてRails勉強会@東京などの勉強会などに時折参加しています。 先日は東京Ruby会議05のスタッフをやったりしました

その1: ブログにやったこと・考えたことを吐き出す

ブログを立ち上げましょう。 そして自分でやったことや、考えたことを随時吐き出すようにしましょう。 id:syoko_sasakiさんも書いていますが、 自分のやってきたことをログに残すというのはとても重要なことです。 そうすることによって、

  1. 自分のわかったこと・わからなかったことを整理できる
  2. 自分のことをアピールする材料が増える
  3. 誰か同じことで悩んでいる人の助けになる

といったメリットがあります。

ここでオススメする理由は主に1です。 一度整理しておけば後日同じ問題にぶつかったときに簡単に乗り越えられますし、 もし解決出来ていない問題であっても、ブログに書いている途中で解決方法が思い浮かぶこともあります。

ブログの他にもにも吐き出す方法にはTwitterやWikiなどもありますが、 Twitterは書きやすいのですがログが流れやすく、 Wikiはまとめを作るのには便利なのですが管理が面倒になので、 手軽さと追跡のしやすさの両立という点でブログが一番だと思います。

ブログを書いた際に、人目に付くようにTwitterに「書いたよ!」と報告し、 探しやすくなるように愛用のソーシャルブックマークにタグをつけてブックマークするのもおすすめです。

「こんなことを書いても誰の役にも立たないんじゃ…」と思ったら

個人的に陥りがちなことなので、一応補足しておきます。

誰かの目を気にしながらブログを書いていると、 クオリティや内容の鮮度などで変なプレッシャーを感じてしまい 記事が書けないこともときにはあるんじゃないかと思います。

ですが、そこで悩まなくても大丈夫です。 ここでブログを書くのは「自分のわかったこと・わからなかったことを整理できる」ことが目的なので、 それさえできていればクオリティは気にする必要がありません。書き殴るだけでも大丈夫です。

ただ一つ気をつけたほうがいいのは、あとになって読み返して理解出来る程度には詳しく書きましょう。 読み返すためのログなので情報が不足するのは出来る限り避けるべきです。

そうやってたくさんのことを書き残したなら、 将来同じことで悩んだ人があなたのブログを読んで解決できたということもあると思いますし、 そのブログを読んだ他社の採用担当者から高い評価を得られるかもしれません。

こころの赴くままに書きましょう。

その2: 勉強会やカンファレンスに参加する

社内の勉強があればそれに参加するのもいいと思いますが、 機会があれば外部の勉強会やカンファレンスに参加することをおすすめします。 なぜなら

  1. 新しい・深い技術を知る機会になる
  2. 凄い人と知り合いになれるかもしれない
  3. 指向性の似た人と知り合いになれるかもしれない

というメリットがあるからです。

新しい・深い技術を知る機会になる

社内にこもっていると、情報を仕入れて発信してくれる人が周りにいるか、 自分で調べようとしない限り、なかなか新しい技術に触れる機会というのは少なくなります。 なので新しかったり、深い技術を知りたいと思ったら外に出るのが手っ取り早いかと思います。

でも最近は新しいものはTwitterで流れてくるし、深い技術もネットで調べればいいし…となって、 あまり外に出る必要は無いんじゃないか?と思うかもしれません。

そこで重要なのが2と3です。

凄い人・指向性の似た人と知り合いになれるかもしれない

マニア以上の凄い人って 社内にそうそういるものではないと思います。 でも社外の勉強会や、それこそカンファレンスではそういった凄い人が発表者を務めていたりするので、 生の発表を聞けるかもしれませんし、140文字に収まらないような質問することも可能です。 さらにはその後の懇親会でより深い話を聞けるかもしれません。 こればかりは社外に出るしかありません。

また、同じ会社にいるからといって自分と同じ考え方ややり方を推進している人ばかりとは限りませんし、 それは当然のことだと思います。 でも勉強会やカンファレンスはトピックが絞られていることが多いので、 自分と似たような技術の好みや考え方をしている人が必然的に多くなります。 このような人とはノウハウの共有や、自分のモチベーション維持などのにおいてとても重要になります。 これも勉強会やカンファレンスならではの出会いです。

「書を捨てよ町へ出よう」ではないですが、人との出会いを求めて社外に出ることも エンジニアにとっては結構大事なことだったりします。

とりあえずやってみる

愚直な精神論っぽくて嫌なんですが、 気になったこと・やってみたいことがあったら とりあえずやってみたらいいと思います。

長々と書いてきましたが、これに尽きます。

新しいことを始めるのは体力も頭も使うので大変ですが、 自分の引き出しを増やすためにもやってみるといいと思います。

ここで@bash0C7さんも引用している、 すごく素敵な言葉を引用しておきます。

成功すれば賞賛が得られ、失敗すれば教訓が得られます。 失うものは何もなく、得るものしかありません。

http://mugiwara.jp/ki2/wifky.pl?p=LTGuide#p5.2.5

長々とした文章をここまで読んでいただき、ありがとうございました。

もっとこうしたらいいよ!という点がありましたら教えてください。 どこかのイベント等で見かけたらぜひぜひお声がけください。

こんな稚拙な文章でも何かのお役に立てたなら幸いです。

最後におすすめの本など

Getting Real

良いサービスを素早く提供するために必要なことについて書かれた文章です。 普通の企業とはかなり違うのですべてがそのまま実現できないかもしれませんが、 なかには取り入れて有効活用できる考えや施策もあると思います。

ちなみにこれを書いている37signalsという会社は中小企業向けのスケジューリング管理やプロジェクト管理などのグループウェアを中心に開発しています。Ruby on Railsというフレームワークを開発したことでも有名です。

プログラマが知るべき97のこと

世界中で活躍するプログラマの書いたエッセイ集です。 プログラミングから外部との調整まで広くにわたってノウハウが詰まっていて、 自分がつまずいたエッセイを読むと「あーあるある」と頷いてしまいます。

日本語版ではさらに日本人のプログラマ10名が寄稿しているため、 実際には「プログラマが知るべき107のこと」になっています。

個人的にはまつもとゆきひろさんの書いた「名前重要」が好きです。 (変数や定数、関数、クラス、プログラムなどの)名前は本当に重要です。

アジャイルプラクティス

ソフトウェア開発における良い習慣についてコンパクトにまとめられています。 なので自分の働く現場が「アジャイル」な開発を実践しているかどうかに構わず読んだほうがいいと思います。

開発の過程ではこの本に書かれているような「悪魔の囁き」が聞こえてくることが何度もあります。 でも、全てを実践するのは確かに困難ではあるんですが、可能なかぎり「天使の助言」に沿うことで より良いソフトウェアを開発することができると思います。

心が折れそうなときにぜひ。

WEB+DB Press

隔月で発行されている、主にWEB開発者向けの技術雑誌です。 トレンドを追った特集が組まれているので「近頃は何がアツいのか」をざっくりと知るには最適です。 WEB系の開発者ならとりあえずチェックしてみるといいと思います。

Fujisan.co.jpで定期購読を申し込むと 発売日より数日早く発送されるのでオススメです。 (それでも新宿紀伊国屋や池袋ジュンク堂の早売りには勝てないのですが…)


Titanium MobileでTableViewのフィルタリング

Ti.UI.SearchBarを使うとTi.UI.TableViewの絞り込みがとても簡単に実現できる。

たとえばuserの一覧を表示しているTableViewを、user.nameの値で絞り込みができるようにするには、

// SearchBarを作る
var searchBar = Ti.UI.createSearchBar({
  showCancel: false
});
searchBar.addEventListener('change', function(e) { return e.value; });
searchBar.addEventListener('return', function(e) { searchBar.blur(); });
searchBar.addEventListener('cancel', function(e) { searchBar.blur(); });

# user個々にTableViewRowを作る
var rows = [];
for (var i = 0; i < users.length; i++) {
  var user = users[i];
  var row  = Ti.UI.createTableViewRow({
      :
    # 検索用の属性をTableViewRow毎に設定する
    # 属性名は任意に設定可能
    name: user.name
  });
  rows.push(row);
}

// TableViewを作る
var tableView = Ti.UI.createTableView({
    data: rows,
    search: searchBar,  // さっき作ったSearchBarのインスタンス
    filterAttribute: 'name'  // TableViewRowに設定した検索用の属性の名前
});

window.add(tableView);

これだけでTableViewのフィルタリングができる。すごく簡単! Titanium Mobileすごいなー。

参考


Titanium Mobileでローカルのjsonファイルを読み込む

ローカルのResources/users.jsonを読み込みたい場合、

// Ti.Filesystem.Fileを返す
var file = Ti.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'users.json');

// Ti.Filesystem.File.read()はTi.Blobを返すのでtoString()して文字列に変換
var json = file.read().toString(); 

// 読み込めていればパースして処理をすすめる
if (json && json.length > 0) {
  var users = JSON.parse(json);
    :
}