どうしても先生という感じがしてしまうんだけど、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脳の恐怖。

怖話をRuby 1.9.3とRails 3.2.1にした。

アプリの動作には影響無く簡単に移行できると思いきや、shoulda-contextがrails 3.2から対応しないのでテストを全てRSpecに書き換えた。

rspecコマンド単体で実行した時とrake specした時で結果が違うのが少し気になるが・・・。

怖話リゾートバイトが途中で切れるバグを直しました。

PostgreSQLの調子で使ってたMySQLのtext型の最長を超えてるだけだった。

text: 65535Byte
mediumtext: 16777215Byte
longtext: 4294967295Byte

リゾートバイトは120KBぐらいの長編なのでmediumtextに変えて対応。

# RAILS_ROOT/db/migrate/20120127081325_alter_body_to_mediumtext_stories.rb 
class AlterBodyToMediumtextStories < ActiveRecord::Migration
  def up
    if Rails.env.production?
      execute 'ALTER TABLE stories MODIFY body mediumtext COLLATE utf8_unicode_ci;'
    end
  end

  def down
    if Rails.env.production?
      execute 'ALTER TABLE stories MODIFY body text COLLATE utf8_unicode_ci;'
    end
  end
end

超えてもエラーが出ないから気づかなかったなあ。