rvmでruby-1.9.3-p125をinstallしたらエラー。指示に従ってmakeログを見てみる。

% lv /Users/komagata/.rvm/log/ruby-1.9.3-p125/make.log
[2012-03-20 05:12:02] make 
        CC = clang
(snip)

おお。平然とcコンパイラがclangになってる。ちょっとおっさん、軽く遠い目をしてしまいました。

% vi ~/.zshrc
export CC=gcc-4.2

コンパイラをgcc-4.2にしたら無事installできました。 clangって何か未来的な感じがしますね。今度勉強しよう。

数年前、受託開発の会社を辞めてこれから自社・製品サービスを作ってる会社で働こうと思い、会社を転々としつつ今(FJORD, LLC)に至ります。

上記のような事を思った切欠は下記の様なことがあったからです。

中規模の案件

その時、僕はコンシューマ向けのWebシステムの案件を5〜6人ぐらいのチームで取り組んでいました。データベースに保存されたデータをPHPでXMLを返すAPIを作り、Flashで表示するサイトで、時代が時代だったので「このトラフィックをPHPで構築するなんて。Javaでやるべきだ。」なんて言われてましたが今考えるとおかしいですね。

サーバーとFlashクライアントが連携するのでAPI(XMLのSchema)に関してはデザイナーとも結構密にやり取りしていたように思います。僕はガントチャートとにらめっこしながらも案件の後半になってもそれ程デスマという感じも無く、定時で帰れるメンバーも普通にいて、

「余裕という程では無いにしてもどうやら無事リリースできそうだな」

なんて考えていました。

そんな時、Flashクライントも担当していたデザイナーの@946さんから、

「この部分のUIはこう変更した方が使い易いのではないか?」

という提案がありました。それに対して僕は、

「確かに改善案はもっともですね。僕も使い易くなると思います。しかし、今までのやり取りの感じだとクライアントはその分の工数追加を認めないでしょう。」

と答えました。

そしてその後凄く落ち込みました。

(僕ら受託開発会社とクライアントの利害は必ずしも一致していない。エンドユーザーに至っては受託開発会社の利益と相反してるとさえ言えるじゃないか。)

受託開発という商売

商売の仕組みとして受託開発はとても凄い。ソフトウェアを開発する前から買い手と値段が決まっているんだから。皆がこぞってやるのも当たり前だ。世の中に需要が一定数あるかぎり、ローリスク・ミドルリターン?のとても手堅い商売だ。

僕もその時まではデマルコやワインバーグに始まってAgile関係の書籍を読んだり実践したりして、受託開発がより良く進化していくことに期待していた。しかし「はじめの契約形態、要は契約書になんて書くの?」が解決しない限り本質的に問題は解決しない。(何度か受託でAgile開発系のイベントに行って質問したが、決まって「業務請負契約にしてもらっています」という答えだった。これは僕には妥協に感じられた。)

実際に昨今では永和システムマネジメントの価値創造契約など、その問題に正面から取り組んだ企業出てきた。(これは僕のように逃げずに真正面から受託開発に取り組む本当に素晴らしい姿勢だと思う。)

しかし、そういった問題は経営から考え方を刷新しないと無理だ。その時点で平凡なプログラマーである僕には「まず確実に5年はこの状況がかわることは無いだろう」と考え(実際に5年たったが変わってない)、「ならばエンドユーザーと直接金銭をやり取りする自社製品・サービスを作る仕事をしよう」と考えて転職した。(要は逃げた)

そして現在

現在のFJORD, LLCでも自社サービス(怖話)で利益を上げようと頑張っているが、それだけで食べていける状態に達していないので受託開発も承っているが、僕は上記の様に受託開発を良くしようとする方法論の進化から遠ざかっていた。しかし、下記の2点からまた勉強を始めました。

  • 5年ぐらいたっているので僕の知らないだけで上記の問題が解決しているのではないか?
  • メンバー2人でもAgile開発の恩恵が無視できない。

「ニワカほどよく語る」と申しますが、恥をかくのは得意なので、Agile開発について勉強したこと、疑問なども書いていこうと思います。

追記

新卒時を除いて、僕の経験した受託開発とはほとんどがコンシューマー向けWebシステムなので、エンタープライズシステムのゼネコン体質などといった、更に難しい問題が無かったのは幸運でした。

どうしても先生という感じがしてしまうんだけど、jonesforthのRichard WM Jones先生が作ってるcron replacement。

whenjobs - a simple and powerful cron replacement

Two key advantages over cron are a simpler syntax for writing rules and a powerful dependency system that lets one job depend on variables set when other jobs run (allowing, for example, one job to run only when another job has finished successfully).

every 10 minutes :
     <<
       # Get the current load average.
       load=`awk '{print $1}' /proc/loadavg`
       whenjobs --set --type float load=$load
     >>

こんな感じで書けるのでシンプルでパワフルとのこと。

それはそうと、先生はRedhatの社員なんですね。whenjobsもRedhatのライセンスなので仕事の産物でしょうか。殆どがOCaml、Cちょっとって感じで書かれてます。

deviseはデフォルトでuserの更新(Devise::RegistrationsController#update)に現在のパスワード(current_password)が要る。

ソースを見てみるとmodelにupdate_without_passwordというのがあるのでこれかと思いきや、これはpasswordとpassword_confirmation無しでupdateするものだった。

自分でupdate_without_current_passwordを作る。

# app/models/user.rb:
class User < ActiveRecord::Base
  def update_without_current_password(params, *options)
    params.delete(:current_password)                                                                                  

    if params[:password].blank?
      params.delete(:password)
      params.delete(:password_confirmation) if params[:password_confirmation].blank? 
    end

    clean_up_passwords
    update_attributes(params, *options)
  end
end

controllerからもこれを使うようにする。

# app/controllers/registrations_controller.rb:
class RegistrationsController < Devise::RegistrationsController    
  def update
    @user = User.find(current_user.id)
    if @user.update_without_current_password(params[:user])
      sign_in @user, bypass: true
      set_flash_message :notice, :updated
      redirect_to after_update_path_for(@user)
    else
      render 'edit'
    end
  end
end

面倒ですね。

怖話ではさくらVPS512を使ってます。性能的にはまだ問題無いんだけど、HDD容量が20GBとちと不安。先日もproduction.logが1.7GBになってたのでちゃんとローテートする。

$ cat /etc/logrotate.d/kowabana 
/var/www/kowabana/shared/log/*.log {
  weekly
  missingok
  rotate 24
  dateext
  compress
  delaycompress

  lastaction
    pid=/var/www/kowabana/shared/pids/unicorn.pid
    test -s $pid && kill -USR1 "$(cat $pid)"
  endscript
}

newrelicのログとかunicornのログとかも一辺にローテートされるから楽でいいですね。-dをつければdry run。-fで強制実行。

sudo logrotate -df /etc/logrotate.d/kowabana

後は一日14MBぐらいずつ増えるDBのバックアップファイルを何とかしなきゃ。

インターネット中毒なので、@machidaさんと飯食ってる時などは大体「どいういうサービスがあったら便利か?」みたいなことを話す。(飯食ってる時以外はPCの前に居るのでサービスを作ってしまうので)

同業の人も大体そうだと思う。僕は現在、仕事時間中は怖話を作り、趣味のプログラミングはLokkaを作るので「新しく作るべきものを考える」という話は最近しない。

僕らが話す時よく前提となるのは、「2人でできるもの」「スケールするもの」「最初からマネタイズ方法が決まっているもの」「自分たちがオモロイもの」などだ。

前述の通り、当面の作るものはあるので先週飲みに行った時は逆の前提で考えてみた。

「とにかくでっかいこと」「世界を変えるもの」「世の中のためになるもの」「とても2人じゃできないもの」「マネタイズ方法は考えない」

いつもと逆であまり考えたことがない種類のサービスだったので僕も@machidaさんもろくなアイデアが出なかった。

  • 軌道エレベーターを作る(@machidaさん)
  • 軌道エレベーター用地に適した赤道直下の土地を買い占める(@komagata)
  • 隕石を迎撃する何か
  • 石油を作る藻?をいっぱい飼う
  • ウィルスやスパム送信者を攻撃するウィルスを作る。
  • 琥珀を買い占めて恐竜のクローンを作る(ジュラシック・パーク)

これはひどい。

結局最後に出たのは、「CPU vs CPUのファミスタの試合を毎日放送してゲーム内通貨を賭けるゲーム」「マイナーなプロレス団体とかスポーツ競技を中継してゲーム内通貨を賭けるゲーム」といういつも通りなアイデアになってしまった。

KFCで食ってたら隣の50代男性3人+女性1人の会話が気になった。

独特の雰囲気を発してるので宗教かネズミ講の勧誘かと思ったら、

(ロイヤルオーダー・・・)

(宇宙・・・)

(アダムスキー・・・)

などと言った単語が会話の節々から聞こえてくる。

議論に熱が入ってきて、思わずハゲたおっさんAが、

「ライト兄弟が飛行機を発明した年にチベットに行ってる。これはユーエフオー(UFO)ですよ!!」

と声を荒らげた。

ロイヤルオーダーというのはどうやら本の名前らしく、会員制ホームページに行けば全文が見れるとのこと。気になるなあ。

branch名を変更する

% git branch -m old_branch new_branch

git addしたが戻したい

git reset HEAD /path/to/file

色々変更しちゃったけど特定の変更に関するファイルだけ別のcommitにしたい

% git app -p

対話的なモードになるのでy, nなどでstageしたいものだけ選んでいく。最後に普通にgit commitする。

上記プラス1ファイル内に関係無い別々の変更点があるのでそれも別のcommitにしたい。

対話モード中に1ファイルのdiffが一気に表示されるがそこでs(plit)を押せば個別にy, nできる。後は同じ。

@tomykairaさんの素晴らしいエントリーに触発されて、Lokkaの現在の課題と何をしようとしてるのかを書いてみます。

[lokka][ruby][test]lokka コミッタからのお願いをお読みください - tomykaira makes love with codes

テスト問題も重要で@tomykairaさんや皆さんの協力でテスト拡充に向けて動き始めました。それとは別に僕の取り組んでる事について。

優先してやりたいこと

  • プラグインの仕様を決めること。
  • gem化。
  • 普通の人でも使えるようにすること。

Lokkaに足りない機能は色々ありますが、まずは機能を追加して行ける土台を作ることが大事だと思っています。gem化もその土台に必要なものです。

現在の問題

  1. gem化可能なプラグインの仕様がちとむずい(gem化するとviewの場所がわからなくなる)
  2. 以前のプラグインとの互換性をどうするか
  3. プラグインの自動読み込みがむずい
  4. bundle installオプションとかむずい。管理画面にプログラマー向けっぽい項目が最初から出てる。

1. Lokkaのプラグインは管理画面を持つ事が多いのでRailsで言えばEngine的な性質を持つものが多いことになる。ここはSinatraアプリがRackアプリでもあるという性質を使って、Sinatraアプリ(恐らくそれを継承したLokka::Plugin)をプラグインということにしてuseする。

2. 従来のものも普通にregisterする。

bundlerではlokka-hello.gemをBundler.requireしてもlokka/hello.rbはrequireしてくれない。lokka-hello.rbをrequireする。

3. railsでもそういう名前のgemではlokka-hello.rbを用意してその中でrequireしてるので、

# lokka-hello.rb:
require 'lokka/hello'
register Lokka::Hello

みたいに書いてくださいという決まりにする。

4. どうしよう。Lokka本体をgem化する時に簡単になるように考える?

まとめ

要は

  • Before Rails3 style gem -> Sinatra extension style gem
  • Rails3 style gem -> Sinatra App Style gem

って感じでrailsのパクリで行こうと思います。

Railsで綺麗なURLにしたいと思うと一つのControllerに機能が集中して困ることがあります。

/comments
/posts/1/comments
/users/1/comments
# config/routes.rb:
Foo::Application.routes.draw do
  resources :comments
  resources :posts do
    resources :comments
  end
  resources :users do
    resources :comments
  end
end

例えばこんな風にしたい時。

# app/controllers/comments_controller.rb:
class CommentsController < ApplicationController
  def index
    @comments =
      if params[:post_id]
        Post.find(params[:post_id]).comments
      elsif params[:user_id]
        User.find(params[:user_id]).comments
      else
        Comment.all
      end
  end
end

こんな風に書く?えーキモーイ。そもそもそれぞれの場合でviewが全然違うんですけどーみたいな場合。

そんなんねぇ俺の糞みたいな悩みはねぇStack Overflowさんに聞けば一発なんですよ。

Rails Namespace vs. Nested Resource - Stack Overflow

controllerのnamespaceでスッキリ書けるみたいです。

/comments
/posts/1/comments
/users/1/comments
# config/routes.rb:
Foo::Application.routes.draw do
  resources :comments
  resources :posts do
    resources :comments, controller: 'posts/comments'
  end
  resources :users do
    resources :comments, controller: 'users/comments'
  end
end
# app/controllers/comments_controller.rb:
class CommentsController < ApplicationController
  def index
    @comments = Comment.all
  end
end

# app/controllers/posts/comments_controller.rb:
class Posts::CommentsController < ApplicationController
  def index
    @comments = Post.find(params[:post_id]).comments
  end
end

# app/controllers/users/comments_controller.rb:
class Users::CommentsController < ApplicationController
  def index
    @comments = User.find(params[:user_id]).comments
  end
end
$ rake routes
(snip)
comments GET    /comments(.:format)                       comments#inde
post_comments GET    /posts/:post_id/comments(.:format)     posts/comments#index
user_comments GET    /users/:user_id/comments(.:format)     users/comments#index
(snip)

おおお、これはスッキリ!

Stack Overflow脳の恐怖。