Capistrano(カピストラーノ)で sudo が使えない問題

CapistranoのGetting Startedを行なっていて、sudo が利用できない問題に直面しました。

実行環境

Capisranoを実行するサーバ

OS CentOS6
OpenSSH OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 Mar 2010
Ruby ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
rubygems 1.3.7
Capistrano 2.9.0
net-scp 1.0.4
net-sftp 2.0.5
net-ssh 2.2.1
net-ssh-gateway 1.1.0
highline 1.6.2

デプロイ対象サーバ(IP を 192.168.100.200 とします。)

OS CentOS6
OpenSSH OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 Mar 2010
Ruby ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
rubygems 1.3.7

再現

% cap invoke COMMAND="df -h" HOSTS=hoge@192.168.100.200 SUDO=1
  * executing `invoke'
  * executing "sudo -p 'sudo password: ' df -h"
    servers: ["192.168.100.200"]
    [hoge@192.168.100.200] executing command
*** [err :: hoge@192.168.100.200] sudo
*** [err :: hoge@192.168.100.200] :
*** [err :: hoge@192.168.100.200] no tty present and no askpass program specified
*** [err :: hoge@192.168.100.200] 
    command finished in 53ms
failed: "sh -c 'sudo -p '\\''sudo password: '\\'' df -h'" on hoge@192.168.100.200

SUDO=1 を外せば実行可能です。

% cap invoke COMMAND="df -h" HOSTS=hoge@192.168.100.200
  * executing `invoke'
  * executing "df -h"
    servers: ["192.168.100.200"]
    [hoge@192.168.100.200] executing command
 ** [out :: hoge@192.168.100.200] Filesystem            Size  Used Avail Use% マウント位置
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_root
 ** [out :: hoge@192.168.100.200] 50G  3.3G   44G   8% /
 ** [out :: hoge@192.168.100.200] tmpfs                 2.0G     0  2.0G   0% /dev/shm
 ** [out :: hoge@192.168.100.200] /dev/sda1             485M   33M  427M   8% /boot
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_home
 ** [out :: hoge@192.168.100.200] 92G  1.5G   86G   2% /home
    command finished in 48ms

つまり、下記もできないということ。仮想端末が割り当てられないようだ。

% ssh hoge@192.168.100.200 'sudo df -h'
sudo: no tty present and no askpass program specified

解決方法1 : 仮想端末が割り当てられていないので ssh -t をつける

普通のshell

ssh の -t オプションは強制的に仮想端末を割り当てる。下記は、実行結果。sudo をつけて実行が可能。

ssh
% ssh -t hoge@192.168.100.200 'sudo df -h'
[sudo] password for hoge: 
Sorry, try again.
[sudo] password for hoge: 
Filesystem            Size  Used Avail Use% マウント位置
/dev/mapper/vg_testbudgearycontrol-lv_root
                       50G  3.3G   44G   8% /
tmpfs                 2.0G     0  2.0G   0% /dev/shm
/dev/sda1             485M   33M  427M   8% /boot
/dev/mapper/vg_testbudgearycontrol-lv_home
                       92G  1.5G   86G   2% /home
Connection to 192.168.100.200 closed.
capistrano

まず、default_run_options[:pty]=true が書かれた capfile を用意します。

default_run_options[:pty]=true

capfileを指定して実行します。

% cap -f capfile invoke COMMAND="df -h" HOSTS=hoge@192.168.100.200 SUDO=1
  * executing `invoke'
  * executing "sudo -p 'sudo password: ' df -h"
    servers: ["192.168.100.200"]
    [admin@192.168.100.200] executing command
 ** [out :: hoge@192.168.100.200] Filesystem            Size  Used Avail Use% マウント位置
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_root
 ** [out :: hoge@192.168.100.200] 50G  3.4G   44G   8% /
 ** [out :: hoge@192.168.100.200] tmpfs                 2.0G     0  2.0G   0% /dev/shm
 ** [out :: hoge@192.168.100.200] /dev/sda1             485M   33M  427M   8% /boot
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_home
 ** [out :: hoge@192.168.100.200] 92G  1.9G   85G   3% /home
    command finished in 70ms

解決方法2 : Defaults visiblepw

sshd に Defaults visiblepw を設定すれば 仮想端末なしでも sudo が許可されます。デプロイされる側のサーバで visudo して、下記のように加えて、ssh を再起動します。

#
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear.
#         You have to run "ssh -t hostname sudo <cmd>".
#
# Defaults    requiretty
Defaults visiblepw

上記のところに パスワードが見えるので ssh -t 使ってくださいと書いてありますけども・・・。

ssh
% ssh hoge@192.168.100.200 'sudo df -h'
[sudo] password for hoge: xxxxxxx            ##←パスワード丸みえ
Filesystem            Size  Used Avail Use% マウント位置
/dev/mapper/vg_testbudgearycontrol-lv_root
                       50G  3.3G   44G   8% /
tmpfs                 2.0G     0  2.0G   0% /dev/shm
/dev/sda1             485M   33M  427M   8% /boot
/dev/mapper/vg_testbudgearycontrol-lv_home
                       92G  1.5G   86G   2% /home
capistrano

Capistranoの場合は、パスワードは見えませんね・・。

% cap invoke COMMAND="df -h" HOSTS=hoge@192.168.100.200 SUDO=1  
  * executing `invoke'
  * executing "sudo -p 'sudo password: ' df -h"
    servers: ["192.168.100.200"]
    [hoge@192.168.100.200] executing command
Password: 
 ** [out :: hoge@192.168.100.200] Filesystem            Size  Used Avail Use% マウント位置
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_root
 ** [out :: hoge@192.168.100.200] 50G  3.3G   44G   8% /
 ** [out :: hoge@192.168.100.200] tmpfs                 2.0G     0  2.0G   0% /dev/shm
 ** [out :: hoge@192.168.100.200] /dev/sda1             485M   33M  427M   8% /boot
 ** [out :: hoge@192.168.100.200] /dev/mapper/vg_testbudgearycontrol-lv_home
 ** [out :: hoge@192.168.100.200] 92G  1.5G   86G   2% /home
    command finished in 4008ms

おまけ1 : sudoで特定のコマンドをパスワードなしで実行できるようにする

例えば、visudo で下記のように設定して、sshd を restart ます。

%wheel        ALL=(ALL)       NOPASSWD: /bin/df

上記だと、/bin/df コマンドは wheelグループユーザに所属していれば パスワードなしで sudo 実行できます。sudo で実行したいコマンドがたくさんある場合は不向きですね。

おまけ2 : sudo 時の path

visudo で設定を見ると Defaults secure_path というのがあります。これは、sudo 時に下記のパスが設定されますが、例えば、gem コマンドが /usr/local/bin にあった場合は、sudo 時に gem コマンドが使えないということになってしまいます。そのため、Default secure_pathに pathを加えることで対応します。

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

いまさら、RubyKaigi2011を振り返る

2011年7月16日(土) 〜 18日(月) に開催された、RubyKaigi 2011から、もう一ヵ月以上経つのですね。いまさらですが、RubyKaigi2011をつらつらと振り返り、感じたこととメモを残します。

私はRubyKaigiに過去2回参加(2010年筑波2009年東京)していて、今年で3回目です。去年に引き続き個人スポンサーとして参加致しました。今年もとても楽しく、学ぶことが多く、たくさんの人とお話できました。RubyKaigi2011を運営してくださった皆様、スポンサーの皆様、有難う御座いました。

RubyKaigiは1年に1度、日本にRubyist達が全世界から集まる場所です。RubyKaigiの開催により再会できた人、憧れの人に会えた人、インターネットごしでしか知らなかったのにリアルになった人・・・こうした機会というのは多くないです。RubyKaigiはこうした機会を設けてくれています。Rubyコミッタがこんなに集まる会議はなかなかありません。海外のスターエンジニアと出会えることもなかなかありません。たこ焼き仮面さんの日本語キーノートはなかなか見れません。Ruby2.0の開発はtrunkでやろうなどといった話もなかなか聴けません。角谷さんの価値とは何か?多様性は善であるという主張もなかなか聴けません。全世界からキーノートが集まり、選ばれたものだけが発表できるという、全世界レベルのキーノートが聴けるわけです。地域RubyKaigiにはないものがRubyKaigiにはあるのですよね。とても貴重です。ぜひとも来年も開催して欲しいと願っています。

Next version of Ruby 1.8 and 1.9のセッション

Next version of Ruby 1.8 and 1.9のセッションでは、「Ruby 1.8 has no future. We gonna use 1.9.」という言葉があって、もう1.8ではなく、1.9を使おうという話がありました。私はまだ1.8.6をメインにつかっており、そろそろ1.8.7をメインにしたいと思っていて、かなり遅れております。Railsでは、1.8.6 はサポート対象外となっていますし、そろそろ1.8.6 を卒業しなければと感じました。

クックパッドさんのセッション

Ruby を利用した大規模ウェブサービスの開発・運用では、クックパッドさんがRuby を用いてどのように開発・運用されているのかを話されていました。クックパッドさんのプレゼンは去年も聴きましたが、去年よりパワーアップしているのが印象的でした。メモしたことを箇条書します。※誤字脱字ある可能性あり。

  • Ruby 1.8.7
  • Rails 2.3
  • haml
  • sass
  • JQuery(Protope を jQuery に 変えた。ブログで検索すれば出てくる)
  • Passenger 2.2
  • MySQL 5.0
  • memcached
  • サーバ構成は定石(サーバ/インフラを支える技術)
  • キャッシュ / バーニッシュ 2.1.5
  • イメージサーバ EC2 に設置
  • Image serve => Tofu(Rails3で処理は書いている)
  • Upload Server on EC2
  • Rails3 => S3
  • URLの中で画像サイズ等を決めている。
  • Searcher
  • MySQL + Tritonnから変わった。
  • Solr に変えた理由。柔軟な検索が可能。早くなったわけではない。柔軟で開発がしやすくなった。
    • Facet(like group by)
      • 集合扱う
    • Boost Search
      • 重み付け検索
    • Dynamicfields
      • 動的なフィールド追加
  • Log Analytics
  • Speed
    • Ruby / Rails 遅い? Slow? 実際に遅い?実際には遅くないかも。No.
    • Cookpad は 200ms 以下を実現。定石をきちんと行うことで、スピードを維持している。
  • 早くする工夫
    • "Good はやらない。Bestのみ集中"
    • シンプルさを保つ
    • Keep it Simple
    • ほんとうに必要な機能のみに絞る
    • 無駄に複雑なことをやらない
    • キャッシュに乗りやすくなる
    • シンプルなクエリ
    • あとでやる(Async Loading)。非同期レンダリング
    • 36ms、195ms(第一レスポンスは 36ms、他の非同期の部分は 195ms )
    • Ruby Rails not slow.
    • ウェブオペレーション(日本の料理のインフラ)
  • クックパッドは 30人のエンジニア。
  • Railsアプリの多人数開発
    • 開発 Dev → デプロイ → フィードバック →(戻る)
    • これをどれだけ早く回すのかが大切。
    • Tests
    • Rspec 1.x
      • unit
      • functional
      • integration
  • 最近はUnit と integration を沢山書き、functional は少なくなってきた。
  • Integration のテストが大切。javascript のテストがかける。
  • テストの処理速度が遅い。20分かかる。ティータイムになる。
  • テストをリモート上で行った。
  • テスト用のサーバをアップして、remote spec ×4 server。テストの話は大江戸Ruby会議で話したよ
  • Deploy Rule
  • CIが通るか?
  • 皆テストを書く・実行するように
  • CIで動かないテスト = 将来的に価値がないテスト
  • 継続することの重要性
  • 新しい技術を導入する = CIで動くテストが書けるを認識
  • RubyStudy@Sapporo#18
    • カピトラーノ
      • デプロイ時にメッセージを記入
      • 新機能の追加・大きな変更
      • Skype による通知
  • Error のモニタリング
    • インフラモニターがあって、それで見てる。何があると、画面が真っ赤になる。
  • エラーログの監視システム
  • フィードバック
    • Extensionsを使っている
    • Rails拡張
      • 条件指定で有効化
      • 一部ユーザのみ限定公開
    • プロトタイプ開発向き
    • Spec を書かなくても良いルール(まずは作る)
        • 例外発生時”自動で無効化”(extensionが落ちても今の機能に切り替わるので安心)
  • 新機能の利用率を知れる。
  • 配置ディレクト
  • リリースしない決断したら、rm app/extensions/foo_bar
  • 近いうちに、クックパッドのブログで公開

2008年に聞いたクックパッドさんのセミナーでは、Rubyは1.8.6、Railsも2.0でした。バージョンアップを意識的に行っていることが伺えます。クックパッドさんはColdFusionからRuby on Railsへ移行するなど、開発の改善を常に考え進化しています。

私のところは、やっとこ基幹システムがRails2.3 になろうとしていますが、これはクックパッドさんの影響を受けていると感じています。数年前に作られたシステムをバージョンアップするというのは、簡単ではありません。バージョンアップするだけで、何ヶ月もかかる場合もあります。私のところはRailsMySQLとライブラリのバージョンアップを行うのに半年近くかかりました。表向きは何も変わらず、裏で変わるだけですので、ユーザからすると「半年もかけて何がよくなったの?」となります。クックパッドさんが以前言っていたことで、「クックパッドはこれから100年以上生き続けるサイトである。だから、バージョンアップにも対応し続ける。新しい機能は魅力だし、他の新しいサービスと勝負できなくなってしまう」。私はこの言葉を聞いて、バージョンアップの重要性を知りました。たしかに、開発をしていると「あの機能があれば、1行で終わるのになぁ」とか「ちょ、gem install xxx できないんですけど。。」みたいなことがたくさんありました。スピードはとても大切ですので、適切なペースを保つためにも、寿命の長いアプリは、定期的なバージョンアップは必要だと感じます。これからも、バージョンアップしてきます。

githubのセッション

こんどはgithubのセッションです。英語でしたので、メモしたこともかなり怪しいですが・・・メモを箇条書きで残します。※誤字脱字ある可能性あり。

  • BrowserMob
  • Pingdom
  • akamaiを検討
  • Measure backend Behavior
  • Capacity plan
  • Collented
    • Time Series Information
  • Nagios
    • Monitoring & Alerting
  • Custom
    • Redis Counters
  • Communicate
  • Propane
    • extendable JS
  • Campfire
    • searchble logs
    • streaing API
  • Hubot
    • door me, image me
    • distributed execution
  • Day to Day
    • how tools work togers
  • User Interaction
  • Error
    • Unavoidable
    • cotweet
  • Haystack
    • exception loggin
    • like Hap Toad
    • 例外頻度が見れる
    • stream in realtime
    • fwe users impacted
    • many users impacted
  • Respond
    • failures we may miss
  • continuous integration
  • Branch status
    • any branch, any time
  • Jenkins CI
    • Solid Backend
    • ブランチの状態も常にCIでチェックしている
  • Webhooks
    • janky
  • Trigger Build

たのしいRailsのセッション

たのしいRailsのセッションでは、Railsそのものを開発するためのTips等が紹介されていました。私もOSSに貢献しようと思っているものの、なかなか足が踏み出させていない分野です。自然と貢献できるようになっているのが良いと思っていますが、OSS開発の道には努力しないと乗れないなという感じもしています・・・。セッションを聞いた時のメモを箇条書きしていきます。※誤字脱字の可能性あり。

たのしいRails
  • Ride on someone else's "rails"
  • "rails" for yourself
  • "rails" for everyone => "rails" に乗って開発するのではなく、"rails" そのものを開発することが楽しい。
  • プラグイン開発(Development Rails plugins)
  • Rails本体開発(Development Rails
  • 自分以外のためにコードを書く場合、コミュニケーションが必要。That's called "social coding"
  • Social Coding は 人と直接コミュニケーションすること。つまり、The community につながる。
  • a Community
    • 日本Rubyの会
    • asakura.rb
    • => The Community ではない
  • The Community == The World
  • Code Ruby, and be a member of "The community"
10 Pro Tips

1. Read Rails
  read "git log" every morning

  git pull
  git log

  将来のRailsが分かる
  だれが作っているか分かる

  良いコード、悪いコードが分かるようになる
  良いコミットコメントが書けるようになる

2. Know the people

 だれに注目すれば良いかわかる

 Stalk them online
 github
 Twitter
 blog

 BTW
  @tenderlov

3. Imitate good commites

 良いコミットが見えてくる

 A Good commit
 ・Atomic
 ・With tests
 ・with a short commit comment telling
 ・what

 in English, of course
 将来のソーシャルコーディングで避けて通れない道、英語。

4. English

 Know these 26 letters
 Aware of the accents
 表音文字、アクセントの位置が重要(アクセントの位置を間違えると通じない)

 Watch Railscasts

5. Live on the edge

 Railsのコミットを見ていると、新機能をどんどん使いたい

 Bundler素晴らしい

6. Contribute to the documentation
 ドキュメントを書いて、コミットする。
 だれでも、コミットできる

 git clone Info/docrails

 一番被害が少なくて良い

7. Share your monkey patches

 自分で作ったモンキーパッチを世界にだそう

 ブログなどに書くのではなく、パッチを書いてpushしましょう

 fork rails, push ,and send a pull request
 普通の pull request でパッチを送れる

8. Start from a gem

9. write a good README

 自分のgemを作る

10. Attend RailsConf
 Railsはたのしい
 RubyConfより全然楽しい

11. write a book

 BDD
  Book
  Driven
  Development

Rubyマスターへの道

◯準備

1. いろんな分野があること
 これを理解しましょう。
 プログラミングは色々な分野がある
 Rubyならだいたいなんでも作れる

2. 時間がかかること
 とりあえず、10年くらいあれば、だいたい何でもうまくなる。
 継続しましょう。
 料理に似ている。
 実践しないとうまくならないこと
 わりと早い段階から(それなりのものが)手にはいる

3. 何でもできること
 コンピュータの中ではプログラにできないことは何も無い。
 Rubyがだめなら、Cで、cがだめならアセンブラで。

4. 何でも作れること
 何でも作れるプログラマ
 増え続ける分野?

 何でも作れる= 「勉強すればなんでも作れる」という自信のこと

◯初級編

5. ライブラリを知ろう
 ・組み込みライブラリ
  Array, String, etc.
 ・標準添付ライブラリ
  open-uri, tempfile, find, etc.

6. 身近な問題を解決しよう
 テキスト処理
 ファイル名の一括リネーム
 Excel(*.csv)の処理。 require 'csv'

◯中級編

7. 良いコードをかこう
 「美しさ」は宗教やオカルトではない
 美しさ = 圧縮された「良さ」

 Rubyらいしコードを書く
  「初めてのRuby」がおすすめ

 each より map を使う
  短くなる
  意図が分かりやすい
  デバックしやすい、保守しやすい

 読みやすいコード
  Rubyの特徴 = 読みやすい(脳にやさしい)

8. ブログをかこう
  最初は反応がなくてつまらない
  検索エンジンごしの誰かに向けて
  メモをまとめることは自分にとっても役立つ(考えをまとめる、あとで見直す)

 発表しよう
  小さなイベントから少しずつ大きく 
  他人の作ったものの話でも良い

 githubに置こう
  英語だとなお良い
  置いて損することはない

◯上級編

9. 中を開けてみよう

 gem のソースを読む
 gemを書き換える
  gem pristine rails
   もとに戻せる
 Rubyのソース

10. 他の言語を知ろう

 Rubyしかしらない = Rubyの特徴を知らない

◯マスター編

11. 人と違うことをしよう

 Rubyはまだまだいろんなことができる
 手分けをする

12. 生産的であるために
 やる気の問題
  諦める(やりたくなるまで待つ)
  とりあえず始めるとやる気がでる
  嬉しいことがあるとやる気が保たれる
   チケット駆動
   テスト駆動
  やらざるを得ない環境(喫茶店とか)

 生産力の問題

◯終りに
 Rubyマスターになるとなにか良いことがある?

 プログラミングの楽しみとは「動いた!!」ということ。

 「あんなに上手くできたら楽しいだろうな」?
   幸せは現在にしかない
   目の前のコードを楽しもう

  気づいたら、Rubyマスターになっていたというのが良い

Ruby遺産とレガシーコード修理技術

Ruby遺産とレガシーコード修理技術のセッションを聞いた時のメモを箇条書します。※誤字脱字の可能性あり。

  • 25年使われるソフトをメンテする方法
  • tDiary = 日記のソフト
  • 書籍:レガシーコード改善ガイドを使って、開発者の負担を減らしましょう。基本Javaの本なので、考え方をもらった。

1. 変更できるほど十分にコードを理解していない場合
 =>削除する
  ・昨日の追加は慎重に、削除は大胆に
  ・ユーザに求められていてもNoと言えることが重要 ・仕様の却下業

2. 変更する必要が、ありますが、どんなテストを書けばよいかわまりません
 =>仕様化テスト
  ・テストで壊してはいけない骨格を作る
  ・ユニットテスト
  ・エンドツーエンド 
  ・インテグレーションテスト
  ・capybara

3. どうすれば何も壊していないことを確認するのか
 ・沢山のRubyのバージョン(1.8.6, 1.8.7, 1.9.1, 1.9.2, 1.9.3, 2.0.0)
  ・コンパイラ任せ
 ・CI
  ・travis-ci
  ・githubにあれば使える

  • ユーザが開発者
  • 開発者の負担を減らして、新しいことに挑戦してもらう
  • 継続するためには、開発を楽しむことが重要「たのしいRuby」(スライドの背景)

まとめ、書籍:レガシーコード改善ガイドを使って、開発者の負担を減らしましょう。基本Javaの本なので、考え方をもらった。負担を減らし多分、新しいことに挑戦してもらいましょう(燃料投下)

テスティングフレームワークの作り方

テスティングフレームワークの作り方を聞いてメモしたことを箇条書きしていきます。※誤字脱字の可能性あり。
Ket Beck曰く、著者は新しいプログラミング言語に直面すると、xUnitを実装する。TF = テスティングフレームワーク

  • Rabbit(うさぎとかめのタイマーのあれ)
  • test-unit 2
  • Cutter
  • GaUnit
  • Pikzie

判断基準について
 判断基準があるとずいぶん楽
 判断基準を伝える人が少ない
  「盗む」は大変
  盗んでないでもっと先へ行け

大事なこと
 何でもできるものは目指さない
 ターゲットを考える
  誰が使うTF?
  なんのためのTF?

自然であれ
 気づいたらできていた
  AをしたらBになっていた
 思いついたことが正解
  BをするにはAでいい?

自然か考える
 一貫性がある?
 身近なものと同じ手順?
 身近なものと同じルール?

まとめ
 判断基準を決めること
 ターゲットを絞ること

写真集

今回はiPhoneではなく、Mediasで写真を撮りました。


今回のテーマである「最後のRubyKaigi」を示したうちわです。来年も開催されることを願っています。



ステッカーを沢山頂きました。


個人スポンサーでしたので、Tシャツもらえました。


日本全国から来ていますね!


アメリカ、南アメリカ、ヨーロッパ、オーストラリア、アジアから来ていますね!


懇親会は「サンシャイン60 58階 サンシャイン・クルーズ・クルーズ」で行われました。景色がよく料理も美味しく、楽しいひとときでした。


大ホールです。たくさんの素晴らしいセッションがおこなわれました。



アジャイルサムライの本にサインをいただきました!価値ある開発とは何か?と問い続けます。


RubyKaigi本当にありがとう。


クロージングでのシーン。スタッフに向けて、スタンディングオーベーションです。とても感動的でした。スタッフの方々、発表者の方々、参加者の皆様、本当に有難う御座いました!

first time to use ruby-prof

What's ruby-prof

profiler for ruby. would you see http://ruby-prof.rubyforge.org/

Install

would you see http://ruby-prof.rubyforge.org/. I installed version 0.10.7.

% sudo gem install ruby-prof

Usage

There are three ways of running ruby-prof.
http://ruby-prof.rubyforge.org/

The first : ruby-prof executable

for example.

% ruby-prof --printer=flat --file=result --mode=wall hoge.rb

hoge.rb

for i in 1..10000
  p i
end

result(cat result)

% cat result
Thread ID: -604468348
Total: 0.081816

 %self     total     self     wait    child    calls  name
 37.88      0.03     0.03     0.00     0.00    20000  IO#write
 27.31      0.07     0.02     0.00     0.05    10000  Kernel#p
 12.84      0.08     0.01     0.00     0.07        1  Range#each
 11.71      0.02     0.01     0.00     0.01    10000  Kernel#inspect
 10.14      0.01     0.01     0.00     0.00    10000  Fixnum#to_s
  0.08      0.08     0.00     0.00     0.08        1  Kernel#load
  0.03      0.08     0.00     0.00     0.08        1  Global#[No method]
  0.00      0.00     0.00     0.00     0.00        1  <Class::Object>#allocate
The second : ruby-prof API
% ruby hoge.rb

result(STDOUT)

Thread ID: -604386428
Total: 0.082106

 %self     total     self     wait    child    calls  name
 32.35      0.03     0.03     0.00     0.00    20000  IO#write
 28.93      0.07     0.02     0.00     0.05    10000  Kernel#p
 14.90      0.08     0.01     0.00     0.07        1  Range#each
 13.02      0.02     0.01     0.00     0.01    10000  Kernel#inspect
 10.79      0.01     0.01     0.00     0.00    10000  Fixnum#to_s
  0.01      0.08     0.00     0.00     0.08        1  Global#[No method]

hoge.rb

require 'rubygems'
require 'ruby-prof'

# Profile the code
result = RubyProf.profile do

  for i in 1..10000
    p i
  end

end

# print a flat profile to text
printer = RubyProf::FlatPrinter.new(result)

profile_options = {
  :measure_modes => [RubyProf::WALL_TIME],
  :min_percent => 0
}

printer.print(STDOUT, profile_options)
The third : require unprof
% ruby hoge.rb

result(STDOUT)

Thread ID: -604060798
Total: 0.084379

 %self     total     self     wait    child    calls  name
 33.02      0.03     0.03     0.00     0.00    20000  IO#write
 29.09      0.07     0.02     0.00     0.05    10000  Kernel#p
 14.07      0.08     0.01     0.00     0.07        1  Range#each
 13.25      0.02     0.01     0.00     0.01    10000  Kernel#inspect
 10.55      0.01     0.01     0.00     0.00    10000  Fixnum#to_s
  0.02      0.08     0.00     0.00     0.08        1  Global#[No method]

hoge.rb

require 'rubygems'
require 'unprof'

for i in 1..10000
  p i
end

Graph html

% ruby-prof --printer=graph_html --file=result.html --mode=wall hoge.rb

hoge.rb

for i in 1..10000
  p i
end

result

KCacheGrind

% ruby-prof --printer=call_tree --file=callgrind.out --mode=wall hoge.rb

hoge.rb

for i in 1..10000
  p i
end

result view on Windows.

I'm on

OS CentOS release 5.3 (Final)
ruby ruby 1.8.6 (2009-06-08 patchlevel 369) [i686-linux]
ruby-prof 0.10.7

Ruby CGIのオフラインモード

Rubycgiには、オフラインモードがあり、コマンドラインから簡単にcgiを試すことが出来ます。

オフラインモードを使ってみる

まずは、下記の記述があるファイルを作成します。ファイル名は hoge.rb とします。

require 'cgi'
cgi = CGI.new

p cgi.params

オフラインモードで使ってみます。

% irb hoge.rb
hoge.rb(main):001:0> require 'cgi'
=> []
hoge.rb(main):002:0> cgi = CGI.new
(offline mode: enter name=value pairs on standard input)

(offline mode: enter name=value pairs on standard input) となり、入力を求められますので、下記のように入力し、Enterを押します。

hoge=123&fuga=456

Enterを押しただけでは、終了しません。Ctrl-D を押すことで続きのプログラムが実行されます。

=> #<CGI:0xb7c08d40 @output_hidden=nil, @multipart=false, @output_cookies=nil, @params={"fuga"=>["456"], "hoge"=>["123"]}, @cookies={}>
hoge.rb(main):003:0>
hoge.rb(main):004:0* p cgi.params
{"fuga"=>["456"], "hoge"=>["123"]}
=> nil
hoge.rb(main):005:0> %        

CGIをオフラインモードで使うことが出来ました。

実験環境

OS CentOS release 5.3 (Final)
ruby ruby 1.8.6 (2010-02-05 patchlevel 399) [i686-linux]

Symbolでconstantize

Rails の active_support/core_ext/string/inflections.rb で Ruby の String が拡張されて constantize が使えて便利です。

% script/console
Loading development environment (Rails 2.3.11)
>> "Hash".constantize
=> Hash
>> :Hash.constantize
NoMethodError: undefined method `constantize' for :Hash:Symbol
        from (irb):2

constantize を Symbol で使いたいと思い、検索していたら こちらのページを発見しました。以下のように実現しています。

class Symbol 
  def constantize
    self.to_s.camelize.constantize
  end 
end 

使ってみます。

% script/console
Loading development environment (Rails 2.3.11)
>> class Symbol 
>>   def constantize
>>     self.to_s.camelize.constantize
>>   end 
>> end 
=> nil
>>
>> :Hash.constantize
=> Hash
>> :hash.constantize
=> Hash

たしかに出来ますが、camelize は仕事しすぎなのかなと思いました。 String拡張の constantize には「It raises a NameError when the name is not in CamelCase」と記述があるように、"hash".constantize をサポートしていませんし。

154         # +constantize+ tries to find a declared constant with the name specified
155         # in the string. It raises a NameError when the name is not in CamelCase                                                                                                                               
156         # or is not initialized.
157         #
158         # Examples
159         #   "Module".constantize # => Module
160         #   "Class".constantize  # => Class
161         def constantize
162           Inflector.constantize(self)
163         end

なので、camelize は外しました。duck typingをイメージしています。

class Symbol 
  def constantize
    self.to_s.constantize
  end 
end 

使ってみます。

% script/console
Loading development environment (Rails 2.3.11)
>> class Symbol 
>>   def constantize
>>     self.to_s.constantize
>>   end 
>> end 
=> nil
>>
>> :Hash.constantize
=> Hash
>> :hash.constantize
NameError: wrong constant name hash
        from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.11/lib/active_support/inflector.rb:364:in `const_defined?'
        from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.11/lib/active_support/inflector.rb:364:in `constantize'
        from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.11/lib/active_support/inflector.rb:363:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.11/lib/active_support/inflector.rb:363:in `constantize'
        from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.11/lib/active_support/core_ext/string/inflections.rb:162:in `constantize'
        from (irb):3:in `constantize'
        from (irb):8

Symbolにconstantize拡張がされないのは、使われないからですかね?もしくは、他に何か意味があるのでしょうか。

環境

ruby ruby 1.8.6 (2010-02-05 patchlevel 399) [i686-linux]
Rails 2.3.11

ziprubyを使ってみた

環境

OS CentOS release 5.3 (Final)
ruby ruby 1.8.6 (2010-02-05 patchlevel 399) [i686-linux]
zipruby 0.3.6

ziprubyのinstall

% wget http://rubyforge.org/frs/download.php/68803/zipruby-0.3.6.gem
% sudo gem install --no-rdoc -l zipruby-0.3.6.gem

確認

% gem list zipruby
*** LOCAL GEMS ***

zipruby (0.3.6)
    Ruby bindings for libzip.

サンプルファイルについて

hoge.zip を使います。hoge.zip を解凍すると、以下の3つのファイルになります。

  • hoge.html
  • 1.jpg
  • 2.gif

それぞれの中身は以下です。

<html>
  <head>
    <title>サンプルHTML</title>
  </head>
  <body>
    <h1>ほげほげ</h1>
    <div><img src="1.gif"></div>
    <div><img src="2.jpg"></div>
   </body>
</html>
  • 1.jpg

  • 2.gif


irbで使ってみる

% irb -rubygems

require 'zipruby'
=> true

# ファイル数取得
Zip::Archive.open('/tmp/hoge.zip') do |archive|
  archive.num_files
end
=> 3

# ファイルリスト
Zip::Archive.open('/tmp/hoge.zip') do |archive|
  archive.num_files.times do |i|
      p archive.get_name(i)
  end
end
"1.jpg"
"2.gif"
"hoge.html"
=> 3

# ファイルサイズの取得
Zip::Archive.open('/tmp/hoge.zip') do |archive|
  archive.num_files.times do |i|
   entry_name = archive.get_name(i)

   archive.fopen(entry_name) do |f| 
    puts f.name, f.size, f.comp_size
   end
  end
end
1.jpg
9937
9068
2.gif
15031
14275
hoge.html
175
120
=> 3

# ファイルの実体を取得
Zip::Archive.open('/tmp/hoge.zip') do |archive|
 archive.num_files.times do |i|
  entry_name = archive.get_name(i)

  archive.fopen(entry_name) do |f| 
   puts f.read
  end
 end
end

# 名前を配列で取得
Zip::Archive.open('/tmp/hoge.zip') do |archive|
 entry_names = archive.map { |f| f.name }
end
=> ["1.jpg", "2.gif", "hoge.html"]

解凍だけでなく、zip圧縮等できます。詳しくは、http://zipruby.rubyforge.org/ を御覧ください。

CACHE.flush_all がうまく動かないのはなぜだろう・・・

TEST

環境

version
memcache-client 1.7.2
ruby 1.8.6 p368
memcached 1.2.2

コード

require 'rubygems'
require 'memcache'

cache = MemCache.new 'localhost:11211'

cache["bar"] = "hoge"
puts cache["bar"]

cache.flush_all

puts "== after flush_all =="
puts cache["bar"]

cache.delete("bar")

puts "== after delete =="
puts cache["bar"]

実行

% ruby ./memcache_test.rb
fuga
== AFTER flush_all ==
fuga
== AFTER delete ==
nil

flush_all したときは、消えていなくて、delete したときは消えているのが分かる

flush_all で ERROR が吐かれている

/usr/local/lib/ruby/gems/1.8/gems/memcache-client-1.7.2/lib/memcache.rb の下記の部分を以下のようにすれば flush_all 使用可能だが・・・。\r\n は \n でもOK。

- socket.write "flush_all #{delay_time}#{noreply}\r\n"
+ socket.write "flush_all \r\n"

修正後の実行、flush_all で消えている。

% ruby ./memcache_test.rb
fuga
== AFTER flush_all ==
nil
== AFTER delete ==
nil

下記は ERROR になり、flush_all が効かない。そのため、delay_timeが設定できない・・。うーむ。

socket.write "flush_all #{delay_time}\r\n"

socket.write で 引数つきのコマンドを発行する場合の問題とかかな・・・?