Ruby で手抜きテンプレートを使う

by tanabe on September 08, 2009

たまに手抜きで使うコードをなんとなく載せてみる。テンプレート使いたいんだけど、ちょっとしたスクリプトだしそもそも ERB の API どんなんだったか覚えてないし、リファレンス引くのもちょっとした手間だし。てなときに使ってる。(ERB くらい覚えろよ。という話もある。覚えてもすぐ忘れるんだよなぁ。)

sample_template = Proc.new do |param1, param2|
%Q!
とまぁこんなかんじで始まったわけですが、
この辺はぜんぶテンプレートなんですね。


だから、どんな風に何を書こうが自由なんです。

パラメタについても記法に迷うことなく
いつもの形で出力することができます。
たとえば、
 #{param1}
こんな風に。

文中にあっても#{param2}なんだか見慣れた形式で安心です。

かんたんですね。
!.lstrip
end

# 使うのも簡単。API を調べたりする必要もまずありません。
puts sample_template.call(p1, p2)

こんなん。邪道? lstrip で最初の改行を消しているあたりもださい。

ちなみにコードジェネレータのテンプレートをこれで書いていて、いつのまにかコード行数が育ってきてそろそろちゃんとテンプレートエンジン導入しないといけないなーと思ってるのは内緒。

結論としては require 'erb' と ERB.new(text).result だけなんだから覚えろよ。と。

  

Ruby 1.9 の情報募集中だそうです

by tanabe on January 28, 2009

ruby-list から。

今度,Ruby 1.9.1 がリリースされますが,そのころ,次の Rubyist Magazine をリリースできるよう,作業中です.

 で,Ruby 1.9 の紹介を網羅的に書くのは大変なので,Ruby 1.9 を紹介した記 事を紹介する記事を書こうと考えています.というわけで,以下のような心当た りがあったら,教えて下さい.どんなに短いモノでも歓迎します.

・Ruby 1.9 の記事を読んだことがある人(そのURL)
・Ruby 1.9 の記事を書いたことがある人(そのURL)
・Ruby 1.9 の記事を書いてやろう,という人(その内容とURL)

 日本Rubyの会のWikiにページを作りました.こちらまで情報をお寄せ頂ければ と思います.

 http://jp.rubyist.net/?1.9+Links

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/45818

これならまったくハカーじゃない人でも協力できますね。

てことで、いくつか追加してきました。自分の delicious で ruby+1.9 しただけの手抜きですがそれでもけっこうありました。

# あ、そういえば日本Rubyの会会員ではないなー。編集した後で気付いたよ。。公開されているんだから編集してしまってもよかったんだろう。うん。

  

Ruby1.9.1 で scrAPI (ろうとして失敗)

by tanabe on January 17, 2009

Eventmachine に引き続き scrAPI を Ruby1.9.1 に導入。

gem install scrapi

成功。

ところが、require 'scrapi' するとエラー。

$ git diff
diff --git a/lib/scraper/base.rb b/lib/scraper/base.rb
index 1c77639..ba3c81d 100644
--- a/lib/scraper/base.rb
+++ b/lib/scraper/base.rb
@@ -906,10 +906,10 @@ module Scraper
     #   end
     def skip(elements = nil)
       case elements
-      when Array: @skip.concat elements
-      when HTML::Node: @skip << elements
-      when nil: @skip << true
-      when true, false: @skip << elements
+      when Array; @skip.concat elements
+      when HTML::Node; @skip << elements
+      when nil; @skip << true
+      when true, false; @skip << elements
       end
       # Calling skip(element) as the last statement is
       # redundant by design.

とりあえず、これで終わったかと思ったらこの後 tidy でエラー吐いていて、DL が DL2 に移行しているので片手間で数箇所直しただけでは動かず、こりゃちゃんとコード読まないとダメだなと気付いたところで時間がなかったため一旦断念。

追記。これを書いた直後にそういえば Nokogiri! と思い出し、調べてみたら 1.9 対応済みらしいということに気付く。今ここ。

  

Ruby1.9.1 で Eventmachine

by tanabe on January 17, 2009

Ruby1.9.1 で Eventmachine を使いたくてインストール。

 
gem install eventmachine

失敗。

一行だけ書き換えてやって、

 
$ git diff
diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp
index a85b707..b05e961 100644
--- a/ext/rubymain.cpp
+++ b/ext/rubymain.cpp
@@ -463,7 +463,7 @@ t_invoke_popen

 static VALUE t_invoke_popen (VALUE self, VALUE cmd)
 {
-       int len = RARRAY (cmd)->len;
+       int len = RARRAY_LEN(cmd);
        if (len > 98)
                rb_raise (rb_eRuntimeError, "too many arguments to popen");
        char *strings [100];
 
cd /usr/local/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.2
ruby ext/extconf.rb
make 
make install

成功。

とりあえず、動いているっぽいのでよしとする。

  

初るびま

by tanabe on October 03, 2008

るびまにレポートを寄稿。感無量です。

今もよくお世話になっていますが、Ruby を使い始めて以来、るびまに育てられたと言っても過言ではないくらい、いろいろな記事を繰り返し読んできました。

Ruby に関するまとまった文章としては、リファレンスマニュアルに継いで世話になった回数が多いのは間違いありません。

ささださんの

るびまを続けていきたい人、何かやってみませんか?

via Rubyist Magazine 4周年に寄せて

の言葉もありますし、次回以降も何かしら関わることができたらいいなと思います。

  

Pathname オブジェクトを返すシンプルなメソッドを定義する

by tanabe on September 21, 2008

一度使うと手放せない便利クラス Pathname なんですが、Pathname.new するのが面倒だったりする。字面が長いから。

そこで、こんなんを追加してみる。

class String
  def -@
    Pathname.new self
  end
end

-'/path/to/somewhere' # => #<Pathname:/path/to/somewhere>

-'/'+'home'+'myacount' # => #<Pathname:/home/myacount>

これは、ちょっと本気でいいかもしれない。

前にもより前衛的な(というか、お遊びの)ものを挙げたことがあって、コードとしてはおもしろかったんだけど、実際に使えるようなもんじゃなかった。でも、今度のは一度実際に使ってみようかなと思える。

ちなみに、アイデアの9割は authorNari さんのこのエントリのおかげです。ほんと、こんなのあったんかいw でした。

  

Ruby 1.9 を svn co してインストールしてみた

by tanabe on September 10, 2008

東京 Ruby 会議の yugui さんのセッションを聞いて、svn head の 1.9 系を使ってみたくなったので、

http://www.ruby-lang.org/ja/documentation/repository-guide

に従い、

$ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby

してダウンロード。

configure がない

./configure しようと思ったら、configure がない。仕方ないから autoconf しようと思ったら、autoconf が未導入だった。

yum install autoconf 

してみたところ、準備されているパッケージでは ruby 1.9 で必要な autoconf のバージョンを満たしていない。(CentOS4)

autoconf のダウンロード

http://www.gnu.org/software/autoconf/ へ行き、http://ftp.gnu.org/gnu/autoconf/ から http://ftp.gnu.org/gnu/autoconf/autoconf-2.62.tar.gz をダウンロード。

wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.62.tar.gz
tar zxvf autoconf-2.62.tar.gz
cd autoconf-2.62.tar.gz
./configure

エラー。。

今度は、GNU M4 のバージョンが足りないらしい。

GNU M4 のインストール

http://www.gnu.org/software/m4/m4.html へ行き、http://ftp.gnu.org/gnu/m4/ から http://ftp.gnu.org/gnu/m4/m4-1.4.11.tar.gz をダウンロード。

wget http://ftp.gnu.org/gnu/m4/m4-1.4.11.tar.gz
tar zxvf m4-1.4.11.tar.gz
cd m4-1.4.11
./configure
make
make install

autoconf のインストール

./configure
make
make install

configure の生成

co した ruby ディレクトリへ移動

autoconf

ruby のコンパイル

./configure --prefix=/usr/local/ruby19
make

コマンド bison なんて知らないらしい。。

bison のインストール

yum install bison

今度こそ ruby のインストール

make
make install

無事インストール完了。お疲れさまでした。

初めてのRuby
初めてのRuby
posted with amazlet at 08.09.10
Yugui
オライリージャパン
売り上げランキング: 2708
おすすめ度の平均: 5.0
5 要点がコンパクトにまとまっています。(中級者以上向け)
  

[Ruby] 二時間の仕事を一秒で片付けてくれる(かもしれない)三行スクリプト。

by tanabe on December 12, 2007

「テキストファイルの各行に一括で同じような処理をして、その結果を得たい」というようなことはよくあります。特に職場が Windows な環境だと CUI のツールが貧弱で、ついエディタでやってみたりしてしまって、半分くらいやったところで睡魔に襲われてどこまで作業したかわからなくなって全部やり直したり。

こんなコードを PATH の通ったところにおいただけで、非常に重宝しているのでご紹介します。はまったときには、笑っちゃうくらい作業効率が上がるかも。

script = ARGV.shift || ''
lines = $stdin.readlines.map {|li| li.chomp}
puts eval(script)

ファイル名に firter とでも名付けてやって呼び出してみます。

たとえば、c:\ruby\bin\rake ファイルからコメントで始まる行だけを抜き出したいときはこんなかんじ。

type c:\ruby\bin\rake | firter "lines.select {|s| s =~ /^#/}"

lines という変数名で標準入力の各行にアクセスできます。

各行に対して、前後の空白とか除去して、重複を除いて、ソートして、空行は除外した結果がほしい、なんてときも、これだけ。

type test.txt | firter "lines.map {|s| s.strip}.uniq.sort.delete_if {|s| s == ''}"

Ruby の強力な Enumerable モジュールと String クラスと正規表現で、大抵の用事は片付きます。

元々すぐコード書いちゃう人はちょっと手間が省けるくらいなんじゃ、とか、それ per(ry とか、それ aw(ry とか、ありますが、慣れると手放せません。

あと、

@ruby "c:\ruby\bin\firter" %* 

とか書いたファイルを firter.cmd として置いておくとうれしいかも。(c:\ruby\bin は適当に調整してください。)

s/firter/filter/ という指摘は受け付けませんのであしからず。  

[Ruby] 海外の「Ruby で GoF のデザパタを実装してみた」が必見

by tanabe on December 06, 2007

Ruby でデザインパターンをやってみたよ、というのはそれほどめずらしくはないと思うけど、これは GoF の元ネタに引きずられず Ruby らしいコードで書かれていて、とても勉強になった。

もちろん、そこ、method_missing でやるのはどうなの?とか、自分なら違う形にするな、とか思うところはあるわけで、そこも含めて非常にためになる&おもしろかった。
読みたい人は、The GoF patterns implemented in Ruby から。

オブジェクト指向における再利用のためのデザインパターン
エリック ガンマ ラルフ ジョンソン リチャード ヘルム ジョン ブリシディース Erich Gamma Ralph Johnson Richard Helm John Vlissides 本位田 真一 吉田 和樹
ソフトバンククリエイティブ (1999/10)
売り上げランキング: 27191
  

Exerb に D&D でパラメタ引数とするファイルを渡したいときの Tips

by tanabe on December 04, 2007

コメントで教えて頂いたとおり、ExerbRuntime の filename, filepath で対応する方が正しいです。便利!

Exerb は Ruby スクリプトを Ruby のインストールされていない環境でも実行可能な exe ファイル化することができるという、仕事で Windows 環境を避けられない Ruby ユーザにとって最高に便利で足を向けて寝られない品の一つ。

その挙動で、軽くはまったのでメモ。(考えてみれば当たり前の動作なのだけど)

Exerb で作成した exe ファイルに対して、引数にしたいファイルを D&D で指定した場合、実行時パスがユーザのホームディレクトリに設定されてしまう。そのため、File.expand_path などで期待したパスを得られなくて困ることがある。

File.expand_path(__FILE__).dirname Pathname.new($0).expand_path.dirname で期待するパスを取得したいときなどは、exe のショートカットを作成し、作業フォルダを exe ファイルのあるフォルダに設定。(デフォルトでそのようになる。)その上で、exe にファイルを D&D したい場合はショートカット経由で利用するようにすると、期待どおりの動作になる。

ついでに、もう一つの注意点として、$0 や __FILE__ で得られる値は rb ファイル時点の名称となる。仮に exe ファイルを作成した後に名称を変更したりした場合も、やっぱり Exerb によって exe 化した際の rb ファイルの名前を維持する。実行中の exe ファイル名を取得する方法は不明。

D&D はドラッグアンドドロップね。念のため。   

[Ruby] Time.parse がボトルネックになったので・・・

by tanabe on November 29, 2007

ログの解析で Ruby を使っているんだけど、二時間かかって死ねる。繰り返し呼んでいる Time.parse がボトルネックみたいなんだけど、どうにかならない?って依頼が来た。

Time.parse は柔軟性ありまくりで便利すぎるから、そりゃ、速度は諦めないとねーと思いつつ、対応案でこんなのを出してみた。

t = "2007/11/28 15:30:28.015".split(/[\/ :.]/)
Time.utc(*t)
t = "2007/11/28 15:30:28.015".split(/[\/ :.]/)
usec = t.last.to_i * 1000
t.last.replace usec.to_s
Time.utc(*t)

無事、劇的に改善。

そういえば、DateTime.strptime てのもあったなーと思い試してみると、むしろ Time.parse よりも遅かった。

ついでに、実行速度といえば VM だよね。ということで、CRuby と JRuby と YARV で比較してみたところ、CRuby と YARV だと少しだけ YARV が速い。JRuby は大体その二倍程度の時間がかかった。

さらに、おまけで JRuby のバグ(?)発見。

t = "2007/11/28 15:30:28"
DateTime.strptime(t, "%Y/%m/%d %H:%M:%S")

が、失敗する。

自分のコードもバグってたので修正。   

[Ruby] head/tail は欲しいなぁ

by tanabe on November 13, 2007

ruby-dev で Array#tail が提案されているけど、たしかに head/tail は欲しいなぁ。再帰を書くときに一々独自実装とかめんどすぎる。

今は dup して呼び先で shift で head 部と tail 部に分けて、みたいなことをしてるけど、first/rest か head/tail が入るなら、そっち使いたい。

名前はここに関してはどちらもそういうもんだと受け止めるからあんまり違和感ないかな。議論自体は面白いからどんどん紛糾してほしいけどw

  

[Ruby] DecisionTable を簡単に作る

by tanabe on October 25, 2007

DecisionTable を書くのに全パターンを書くのはとても面倒。面倒なことはコンピュータにやらせよう。

ということで、Ruby で書いてみた。


class DecisionTable
  attr_accessor :patterns

  def initialize(patterns = [])
    @patterns = patterns
  end

  def horizontal
    rows
  end

  def vertical
    rows.transpose
  end

  def table
    format_table vertical
  end

  private
  def rows
    pattern_map(1, @patterns.dup)
  end

  def pattern_repeats(patterns)
    return 1 if patterns.empty?
    patterns.map {|r| r.size }.inject {|r, n| r * n }
  end

  def pattern_map(repeat, patterns)
    return [] if patterns.empty?
    pattern = patterns.shift
    return pattern.map {|p| [p] * repeat }.flatten * pattern_repeats(patterns), *pattern_map(repeat * pattern.size, patterns)
  end

  def format_table(table)
    table.map {|r| format_row r }.join("\n")
  end

  def format_row(row, separator="\t")
    row.map {|c| "\"#{c}\"" }.join(separator)
  end

end


if __FILE__ == $0
  patterns = [
    %w{m f},
    %w{5 10 15},
    %w{5 10 15},
    %w{0-10 11-45 45-60 60-65}
  ]

  dt = DecisionTable.new(patterns)
  puts dt.table
end


出力を Excel に貼って、各パターンの結果を書けばルールテーブルのできあがり。

  

Asianux Road Show 2007 まつもとゆきひろさん、基調講演のメモ

by tanabe on October 16, 2007

2007/10/16 Miracle Linux 社開催の Asianux Road Show 2007 へ行ってきた。そのうち、まつもとさんセッションのメモ書き。急いでメモったので、理解が違っているところや聞き漏れ、聞き間違いがありえる。読まれる方は、そこを理解したうえで読んでください。

タイトル
Ruby からのメッセージ

自己紹介
  • プログラマ
  • オープンソース開発者
  • 言語デザイナ

世に言語の種は尽きまじ
  • 一説には数千とも数万とも
  • ほとんどは消えていく
  • アイディアの具現化
  • いつか自分の言語を
  • 言語を作りたい人は一定数いる
  • Ruby という名の言語も3つ存在する
  • ただ、ほとんどは寿命が短く使われない
  • 作者しかユーザがいない、とか

先端言語と普及言語
  • 言語における対立軸
    • 一般向け/学術向け
    • 最新技術/枯れた技術

先端言語
  • 特定のアイディアに深く依存
  • 応用範囲が狭い
  • アイディアの実用性を証明(検証?不確か)

先端言語
  • 論理プログラミング: Prolog
  • 関数プログラミング: FP (functional programming/ジョンバッカス), huskell, erlang, ...
  • オブジェクト指向: Simula (Entity のデータ構造をADTで表現), Java, C++, Smalltalk, 各種Lisp, Perl, Python ...
  • イテレータ: CLU (繰り返しの抽象化 MITバーバラリスコフが開発, OOPに近い考え方)
  • ゴール指向: Icon (バックトラッキングが組み込まれている)
  • 玉石混交で他の言語の礎となることが多い
  • 概念が形を変えながら取り込まれる

例外的先端言語
  • Lisp
  • Smalltalk
熱狂的ファンを獲得。実用性もある。が、広まることはない

普及言語
  • 先端から学ぶ
  • 枯れた機能優先
  • 実用的
  • だが、面白みは少ない # 言語で冒険しないから
Java でOOPを知った人は多いだろうが、実際は60年代に考え方は発生している
  • ガベージコレクションも普及した が、 Lisp によって 60 年代頭に登場
  • 先端言語の概念が普及言語に取り込まれるまで30年くらいかかる

普及言語
  • Fortran
  • C
  • C++
  • Java

先端から普及へ
  • 再起
  • 抽象データ型 # データのカプセル化を行う
  • オブジェクト指向
  • 関数指向 # 関数そのものをデータとして扱い、関数そのものをデータとして適用する.
Javascript なんかはオブジェクト指向と関数指向の両方を備えている。化粧し損ねて顔がくずれた Lisp とでもいうか。。 ← 素材は良いという意味だと思われ。

先端技術とはなにか
  • より抽象的
  • より簡潔
  • より生産的
  • より変化に強い

先端的普及言語
Ruby
普及言語でもっとも進化したもの

Ruby の特長
  • オブジェクト指向プログラミング言語(下とかぶってる。写し間違ってる?)
  • 純粋オブジェクト指向
  • 高階関数

純粋オブジェクト指向
  • 文字列
  • 数値
  • 配列
  • その他すべて

文法
  • Lisp: S式
  • Smalltalk: メッセージ
  • Python: インデント
  • Ruby: 保守的
外見がふつう。ブロックを閉じる end は algol からもらった。

DSL
  • Domain
  • Specific
  • Language
  • 専用言語風汎用言語

DSL
Rakefile
Rake = Ruby Make
task :default=>[:test]
task :test do
  ruby "test/unittest.rb"
end
一見すると専用の言語のようだが、実は単なる Ruby のプログラムなので、やろうと思えばなんでもできる。

DSL
単純な文法だと
→ 複雑。(コード省略)
Ruby のような言語の力がなく単純な文法で DSL ぽいものを書くと、括弧などの記法上の制限が多くて複雑な見た目になる。DSL ぽく見えない。

高階関数
  • クロージャ
  • ブロック

Ruby の特徴
  • ブロック 
  • Duck Typing
  • メタプログラミング

ブロック
  • コードの塊
  • メソッドに付加
  • 拡張可能な制御構造

ブロック
  • 繰り返し
  • 条件判断
  • データの加工
  • など

繰り返し
 3.times do
    puts "hello"
  end
  a = [1,2,3]
  a.each do |i|
    puts i
  end 
ブロックを導入することで、ループのための文法を定義しなくても繰り返しを実現できる。文法を変えることなく、制御構造を拡張することができる。

条件判断
 a = [1,2,3,4]
  a.detect {|x| x % 2 == 0}
  a.select {|x| x % 2 == 0}
  a.reject {|x| x % 2 == 0}
ループや制御構造を使って実現するようなことが、簡潔な表現が可能になる。意図をそのまま表現することが可能になる。

データ加工
 a = [1,2,3,4]
 a.collect{|x| x * 2}
  a.sort_by{|x| -x}

スコープ制御
 open(path) {|f|
     ...
  }
  # ブロックを抜けたらファイルが close されるので意図が明確に表現できる

  catch(:foo) {
    throw :foo
  }
  # 専用の制御構造を導入しなくても、単なるメソッドで実現できる

動的型
  • 変数・式に型なし
  • 実行時に型が決まる
  • エラーチェックも実行時
  • 型の宣言不要 → 簡潔
  • 型に束縛されない → 柔軟

Duck Typing
アヒルのように鳴き
アヒルのように歩くのは
アヒルに違いない
ほんとは何かわからないけど、振る舞いからアヒルだとして扱っていい という考え方。オブジェクトの型情報よりもオブジェクトのインターフェイスを重視する。

Duck Typing
型よりも
インターフェイスを重視
メソッドを満たすもの

Duck Typing
StringIO
  • 文字列への入出力
  • 継承関係なし
  • 実装共有なし
  • 外見は同じ
Ruby のプログラムで IO を期待しているところに StringIO を渡すとほぼ間違いなく動く。これが醍醐味であり強み。Java だと型の宣言と一致しないと受け付けない。共通 interface が必要になる。interface がなければ wrapper が必要だったりする。 → 面倒。事前に考えないと分岐ができない。Ruby ならだいたい問題ないよ。

メタプログラミング
  • プログラムを操作
  • 情報取得
  • プログラムを変更

メタプログラミングの力
  • ActiveRecord
    • Object-Relational
    • DBスキーマのみ
    • クラスはスキーマから
オブジェクトのアトリビュートなどはスキーマ情報から取得し、直接書き換えちゃう。

ActiveRecord
クラス定義これだけ
class User < ActiveRecord::Base
end
中身はスキーマから
設定ファイルをいじったりが必要ないので、メンテの負荷が減る。

この辺からかなり速度がアップ。メモの精度がさらに微妙なことに。

メタプログラミング
  • DBスキーマを読み
  • 属性を自動定義
  • 人手いらず
メタプログラミングの力。他の言語でもできるけど、結構面倒。Ruby はメタプログラミングに最適化されているので便利。人手が省ける。

メタプログラミング
真の違いは
哲学に基づいたバランス
他にもできるのはある。

Ruby の哲学とは?
  • 簡潔さ
  • 書きやすさ
  • 単純さを追求しない

簡潔さは力なり
by Paul Graham
  • Fred Brooksの法則
  • 簡潔さ ≒ 効率
あるプログラマが一定期間に生産できるプログラムコードの量は一定。だから言語の力で生産性の勝負が決まる。

書きやすさ
  • 少ない記述
  • 少ないタイプ量
  • 実行可能擬似コード
教科書に出てくるような擬似コード(細かい記法や規則を省略してアルゴリズムだけを表現した簡易コード)を書くと、それが動くというのが言語デザイナの一つの目標。Ruby は良い線いっている。

サンプル:階乗
  • Java で
  • Lisp で: コードが減る
  • Ruby で: もっとコードが減る。読みやすい。
  • Ruby with inject
  • Ruby with inject on Ruby 1.9 # inject(&:*)
コードは写せなかった...

サンプル:ネットワーク
一行で書けちゃいます。これも写せず...

 

続く二枚は気になるスライドだったけど、タイトルだけでほとんどスルー。細かい項目は写せず。読み取れず。

単純さを追求しない
人の好みは単純じゃない

どうでもいいことだけど、写したメモを見たら「人の交尾は単純じゃない」と typo していた orz

Ruby on Rails
web development that doesn't hurt. # 痛くないWeb開発

opinionated software ソフトがプログラミングに影響を与える
自己主張のあるソフトウェア。自分の意見を持ってる/押し付ける。

sapir-whorf仮説
  • 言語は施行に影響を与える
  • 制約を与える
  • (?)

バベル17
言語が思考に影響を与える
バベル17表紙画像。

言語からのメッセージ
メッセージが含まれている

Ruby が与える影響とは
  • プログラミングは楽しい # 昔は楽しかった。仕事でやると面白くない。楽しさを思い出したい。
  • プログラマは素晴らしい
  • プログラミングはわくわくする
  • 苦労はコンピュータへ
  • 人間は楽を
  • 記述は関係
  • 生産性は高く
  • 変化に強く
→ Enjoy Programming!
これが Ruby に振りかかった魔法。

Ruby の今後
  • 趣味から仕事へ
  • スケーラビリティ 
    • データ量
    • アクセス数
    • プロジェクト規模
テラバイトのデータを扱いたいとか、極端に大きなアクセス数をさばきたいとか、Rubyで百万行とか、今後はサポートしていかないとなぁと思ってる。

Ruby 1.9
  • 12月リリース予定
  • 新機能(M17Nなど)
  • パフォーマンス向上

ruby 2.0(2010年?)
  • 未来のいつか
  • より冒険的な機能
    • traits
    • パッージシステム
    • アスペクト指向 など

他のセッションも含めて、とても面白い内容でした。どうもありがとうございました。

追記。Asianux Road Show へのリンクを追加。資料を頂いたので、こちらは特にメモしていませんが、よしおかさんのセッションも非常にためになりました。福岡・大阪の方はチャンスがあるのでぜひ。あと、インテルのアーキテクチャの話も普段あまり触れない分野なので興味深かったです。

  

衝動が正しいものなら調整して実施。独りよがりでないかは要確認。

by tanabe on October 13, 2007

個人的には

この一行がどうにも読みにくくしている。
via 教えながら学ぶRuby: 言語を拡張したくなる衝動に関して
という程度の問題意識に対してオーバースペックな解法を取っている気がするなぁ。

この件については、意図がコードに反映されていない(Ruby に慣れていないと本当に必要なのかが読み取れない)というなら、

next if invalid?(r)
とかしてやれば済むだけの話だと思う。入力パラメータのバリデーションがしたいなら、バリデーションとして明示したほうがわかりやすいよ。という業務システムプログラマ脳の意見。

ただ、オープンクラスな Ruby だとこういうことが誰にでも可能で、いつどこでどういうプログラマが働いているかわからないと影響が大きいよね、という論点には異論ない。そして、多かれ少なかれ、これはコミュニケーションの問題だと思う。密なコミュニケーションが取れていないと言語によらずプロジェクトとしては同種のリスクを持つという意味では、そこをことさら言語の問題として取り上げる必要はないように思う。知らないうちにあっちのチームと仕様がずれてました!みたいな話と一緒じゃないかと。(ビジネス仕様は暗黙的に共用されるグローバル変数。あ、これも業務システム脳かも。)

たとえば今回みたいに

  • NilClass 拡張しようと思います。
  • 理由はこうです。
という話があれば、上記のような理由でぼくは反対するだろうから、それで問題ないんじゃないかなぁ。仮に NilClass が拡張されることになっても、それは(たとえば ActiveSupport のように)周知のことになるからやっぱり問題ないし。

参考リンク。
「Rubyのオープンクラスってば大規模ソフトウェアに不向きじゃない?」という話。

  

[Ruby] テーブルっぽいデータをシンプルにソートする

by tanabe on September 14, 2007

Ruby でテーブルっぽいデータを任意の項目でソートしたいときの Tips 。こんなやり方できんのね。

a = [
  {:id => 4, :name => "abcde", :value => 50, :tag => "zzz"},
  {:id => 3, :name => "abcde", :value => 50, :tag => "zzz"},
  {:id => 3, :name => "abcde", :value => 30, :tag => "zzz"},
  {:id => 2, :name => "abcde", :value => 50, :tag => "zzz"},
  {:id => 2, :name => "bbcde", :value => 50, :tag => "zzz"},
  {:id => 1, :name => "abcde", :value => 50, :tag => "zzz"},
  {:id => 1, :name => "abcde", :value => 50, :tag => "azz"},
  {:id => 0, :name => "abcde", :value => 50, :tag => "zzz"} 
]

a.sort_by {|e| [e[:id], e[:name], e[:value], e[:tag]] }

# => 

[{:tag=>"zzz", :name=>"abcde", :value=>50, :id=>0},
 {:tag=>"azz", :name=>"abcde", :value=>50, :id=>1},
 {:tag=>"zzz", :name=>"abcde", :value=>50, :id=>1},
 {:tag=>"zzz", :name=>"abcde", :value=>50, :id=>2},
 {:tag=>"zzz", :name=>"bbcde", :value=>50, :id=>2},
 {:tag=>"zzz", :name=>"abcde", :value=>30, :id=>3},
 {:tag=>"zzz", :name=>"abcde", :value=>50, :id=>3},
 {:tag=>"zzz", :name=>"abcde", :value=>50, :id=>4}]

一応補足しておくと左にある項目から優先してソートされる。処理の順考えれば当然そうなるのは分かるけど、うまいなぁ。

  

[Ruby] Ruby で列挙型

by tanabe on August 26, 2007

Ruby には言語の機能としての列挙型がない。

ちょっと使いたいことがあって、いくつかのパターンで列挙型ぽく振舞うものを書いてみた。

require 'forwardable'
require 'ostruct'

class Array
  def to_enum_hash(start_from = 0)
    keys = self.dup
    vals = Array.new(self.size) {|i| start_from.to_i + i }
    Hash[*keys.zip(vals).flatten]
  end
end

module Enumeration
  def Enumeration.new(*args)
    initial_number, ordered_symbols = parse(args)
    OpenStruct.new(ordered_symbols.to_enum_hash(initial_number)).freeze
  end

  def enum(*args)
    initial_number, ordered_symbols = parse(args)
    define_enum(ordered_symbols, initial_number, self)
  end

  private
  def parse(args)
    if args.last.is_a?(Hash)
      initial_number = args.pop[:start_from]
    end
    initial_number ||= 0
    ordered_symbols = args.map {|a| a.to_sym }
    [initial_number, ordered_symbols]
  end

  def define_enum(names, value, target)
    return if names.size == 0
    target.const_set(names.shift, value)
    define_enum(names, value.next, target)
  end
end

class Module
  include Enumeration
end

使い方はそれぞれこんなかんじ。

require 'enumeration'
FeeTable = [
  [1200, 600], # sex[:male]
  [800,  400]  # sex[:female]
]

class Sex; enum :Male, :Female; end
class Generation; enum :Adult, :Child; end
p FeeTable[Sex::Male][Generation::Child] #=> 600

sex = Enumeration.new :male, :female
generation = Enumeration.new :adult, :child
p FeeTable[sex.female][generation.adult] #=> 800


sex = [:male, :female].to_enum_hash
generation = [:adult, :child].to_enum_hash
p FeeTable[sex[:female]][generation[:child]] #=> 400

OpenStruct を使った実装が使っているときの見た目が一番 Ruby ぽいかなぁ。

  

[Ruby] しびれる配列のシャッフル方法

by tanabe on August 08, 2007

[ruby-list:43811]で挙がっていた配列のシャッフル方法にシビレタ。

sort_byとrandの連携で。

a=[1,2,3,4,5]                   # => [1, 2, 3, 4, 5]
a.sort_by{rand}                 # => [3, 1, 5, 2, 4]
  

1から1000まで続けて書いてできる整数

by tanabe on June 29, 2007

sumim さんのところrubyco さんのところ経由。

やってることは問題文そのままですが、こんなんとか。

p (1..1000).to_a.join.size 
p (1..1000).to_a.join.scan(/1/).size
  

DuckTyping は破綻しない。。。よねぇ?

by tanabe on June 12, 2007

Rubyco さんの日記へ反応。

そういえば、Duck Typingでは「メソッド名がグローバル」になりますね…。ふと思ったのですが「大規模プロジェクトでメソッド名がコンフリクトしてDuck Typingが破綻する」という可能性はあるでしょうか?

「(1)大規模プロジェクトでもDuck Typingは破綻しない。なぜなら…」インスタンスにとって同名のメソッドは一つだけで、かつ実行時にはインスタンスが特定されるから。

かと思うんですが、問題取り違えてる?

あと、DuckTyping は「あるべきメソッドがそこにあることを期待する」のであって、「あるかどうかわからないけど投げてみる」とは(設計上・オブジェクトの役割上の意味で)違うと思った。(けど、これもぼくの理解が間違っているかもしれない。)  

RubyKaigi2007

by tanabe on June 10, 2007

kdmsnr さんの感想に完全に同意。

事前にタイトルを見たとき、 Daveにはもっとテクニカルでプラグマティックな話をしてもらいたいと思ったけど、 あのテーマで良かったなぁ。感動した。

達人はプレゼンも達人だった。あれほどエモーショナルなプレゼンを見たのは初めて。あれでいやらしくならないのは、やっぱり愛だよなぁ。

けして突飛な話でもなかったし、誰も思いつかないような洞察というようなものでもなかったけど、2007年の Ruby を切り取って少し先の話をするという意味でなんともピタリとはまった人選だった。去年 DHH、今年 Dave という人選をした人、GJ すぎ。

追記:大事なことを書き漏らした。実行委員各位、楽しい場をどうもありがとうございました!  

Ruby の設計思想について質問中

by tanabe on May 26, 2007

はてなで次の質問をしています。

プログラミング言語 Ruby の設計思想について教えてください。

言語としての Ruby についてよりも、設計者まつもとゆきひろさんの考え方について興味があります。言語仕様についてではなく、「このような原則に従って設計した」というような、思想の部分についてご回答ください。MLなどからソースの明示があれば、最高です。

なお、オブジェクト指向スクリプト言語 Ruby は未読です。


プログラミング言語 Ruby の設計思想について教えてください。

有名なものでも結構ですので、ぜひご回答お願いします!

  

[Ruby] Array を複数のパラメータとして受け取る方法

by tanabe on May 01, 2007

メモなので手抜き更新。

foo(a, b, c) というような複数の引数へ Array インスタンス [1, 2, 3] を一つずつ渡したいときは、こうやって渡せば OK。

a = [1,2,3]
foo(*a)

こんなかんじ。

irb(main):001:0> def foo(a,b,c)
irb(main):002:1>   p a,b,c
irb(main):003:1> end
irb(main):004:0> a = [1,2,3]
irb(main):005:0> foo *a
1
2
3
irb(main):006:0> foo a
ArgumentError: wrong number of arguments (1 for 3)
        from (irb):6:in `foo'
        from (irb):6
  

[Ruby] コマンドラインオプションを簡単に扱うライブラリ optparselet

by tanabe on April 28, 2007

optparse は便利なんだけど、ぱっと使うには少し機能が重たく感じるときがあった。

で、機能削って使うときに簡単なやつを書いてみた。(実装は optparse に依存しまくり。)

使い方はこんなかんじ。

# 1. フラグ扱い(指定されていれば true)したければ :act_as_flag 指定
# 2. ショートネームを独自に指定したければ :short_names 指定
# 3. 数字は整数型扱い
# 4. 少数は浮動小数点型扱い
# 5. ただし 0 から始まる数値は文字列扱い
# 6. 他は文字列扱い
# 7. 省略されたオプションをトラックしたいときはブロック渡してその中で適当に処理
# 8. すべてのオプションはデフォルトで省略可

require 'lib/optparselet'

# 一番単純な使い方

parser = OptionParserlet.new([:ability, :bicycle, :cycle])

argv = %w{ -a foo -b bar -c baz }
parser.parse(argv) # => {:ability => "foo", :bicycle => "bar", :cycle => "baz"}

# 1. フラグ扱い(指定されていれば true)したければ :act_as_flag 指定

parser = OptionParserlet.new([:ability, :bicycle, :cycle], :act_as_flag => [:cycle])

argv = %w{ -a foo -b bar -c }
parser.parse(argv) # => {:ability => "foo", :bicycle => "bar", :cycle => true}

# 2. ショートネームを独自に指定したければ :short_names 指定

parser = OptionParserlet.new([:ability, :bicycle, :cycle], :short_names => [:r, :e, :m])

argv = %w{ -a foo -b bar -c baz } # Long Name の短縮も使える
parser.parse(argv) # => {:ability => "foo", :bicycle => "bar", :cycle => "baz"}

argv = %w{ -r foo -e bar -m baz } # 当然、Short Name も使える
parser.parse(argv) # => {:ability => "foo", :bicycle => "bar", :cycle => "baz"}

# 3. 数字は整数型扱い
# 4. 少数は浮動小数点型扱い
# 5. ただし 0 から始まる数値は文字列扱い

parser = OptionParserlet.new([:ability, :bicycle, :cycle])

argv = %w{ -a 23 -b 43.150 -c 0123 }
parser.parse(argv) # => {:ability => 23, :bicycle => 43.15, :cycle => "0123"}

# 7. 省略されたオプションをトラックしたいときはブロック渡してその中で適当に処理

parser = OptionParserlet.new([:ability, :bicycle, :cycle])

argv = %w{ -a foo -b bar } # -c を省略して呼出し
options = parser.parse(argv) do |options|
  options[:cycle] = "default value"
end
options # => {:ability => "foo", :bicycle => "bar", :cycle => "default value"}

# 8. すべてのオプションはデフォルトで省略可

parser.parse(argv) # => {:ability => "foo", :bicycle => "bar"} (エラーにならない)

オプションの取り回しが野暮ったくて拡張性がつぶされてるとか、実装で修正したいところはあるけど、とりあえず公開。

てことで、コードはこちら。

require 'optparse'

class OptionParserlet

  def self.parse(argv, names, options = {}, &block)
    new(names, options).parse(argv, &block)
  end

  def parse(argv, &block)
    argv = ['--help'] if argv.size.zero?

    options = @options
    names = @names

    params = {}
    @parser = OptionParser.new do |opts|
      opts.banner = options[:help] || options[:banner] || "Usage: #{File.basename($0)} [options]"

      if options[:short_names]
        define_option_with_short_name( name_pair(names, options[:short_names]), opts, options, params )
      else
        define_option(names, opts, options, params)
      end
      opts.parse(argv)
    end

    block.call(params) if block
    params
  end

  def initialize(names, options = {})
    @names = names
    @options = options
  end

  private
  def define_option(names, optparse, options, params)
    names.each do |id|
      if flag_option?(id, options)
        optparse.on("--#{id.to_s}") {|f| params[id] = f }
      else
        optparse.on("--#{id.to_s} [val]") {|v| params[id] = parse_val(v) }
      end
    end
  end

  def define_option_with_short_name(names, optparse, options, params)
    names.each_pair do |id, short_name|
      if flag_option?(id, options)
        optparse.on("-#{short_name.to_s}", "--#{id.to_s}") {|f| params[id] = f }
      else
        optparse.on("-#{short_name.to_s}", "--#{id.to_s} [val]") {|v| params[id] = parse_val(v) }
      end

    end
  end

  def flag_option?(id, options)
    options[:act_as_flag] && options[:act_as_flag].include?(id)
  end

  def name_pair(long_names, short_names)
    alist = [long_names, short_names].transpose
    Hash[*alist.flatten]
  end

  def parse_val(str)
    case str
    when /\A0(?:\d+\.\d+|\d+)\z/
      str.to_s
    when /\A\d+\z/
      str.to_i
    when /\A\d+\.\d+\z/
      str.to_f
    else
      str.to_s
    end
  end
end

ショートカットでクラスメソッドを定義したから、実際はそっちだけ使ってれば十分かと。

  

日本 Ruby 会議 2007 予約完了〜!

by tanabe on April 14, 2007

こんな時間でも無事予約できた!

やたっ!

  

[Ruby] SimpleConsole の View を外部ファイルで管理して puts-less に。

by tanabe on March 31, 2007

SimpleConsole で View にいちいち puts とか print とか書くのがめんどい。余計な出力は見せないのが Unix way なのだろうけど、それにしても help とか書くときに puts が並んだのを見るとうんざりする。(ちなみにヒアドキュメントはあまり好かない。)

てことで、Rails の ActionView みたいに外部ファイルのテンプレート読み込んでやればいいんじゃない?とありがちな結論に持ってってみた。

下記のようなオフィシャルサイトのサンプルコードが、

class Controller < SimpleConsole::Controller
  params :string => {:n => :name}

  def whoami
    @name = params[:name]
  end

  # ...
end

class View < SimpleConsole::View
  def whoami
    puts "Hello, your name is " + @name
  end
end

こうなる。当然コントローラでセットしたインスタンス変数の値はテンプレートから参照可能。

class Controller < SimpleConsole::Controller
  params :string => {:n => :name}

  def whoami
    @name = params[:name]
  end

  # ...
end

require 'view_template'

class View < SimpleConsole::View
  def_view_template :whoami
end

# .. inside whoami.rview
Hello, your name is <%= @name %>

ViewTemplate Module はこんなかんじ。テンプレートファイルはとりあえず実行するファイルと同じところに置いておけば動くようになってるけど、今いちなかんじ。どんな構成にするのがいいかなぁ。あんまり大層な構成にしても SimpleConsole らしくないし。

require 'erb'
require 'simpleconsole'

module ViewTemplate
  include ERB::DefMethod
  TEMPLATE_FILE_EXTENSION = '.rview'
  TEMPLATE_METHOD_NAME_PREFIX = 'erb_'

  def def_view_template(*method_names)
    method_names.each {|name| define_view_method(name) }
  end

  def define_view_method(method_name)
    template_file_name = method_name.to_s + TEMPLATE_FILE_EXTENSION
    template_caller = TEMPLATE_METHOD_NAME_PREFIX + method_name.to_s

    module_eval %Q-
      def_erb_method("#{template_caller}", "#{template_file_name}")

      def #{method_name}
        puts #{template_caller}
      end
    -
  end
  private :define_view_method
end

SimpleConsole::View.extend ViewTemplate

SimpleConsole の以前の紹介記事はこちら

  

Ruby の include, extend まとめ

by tanabe on March 26, 2007

この前の話の続き。

include, extend わかった。つか、自分で写した説明のとおりとしか言いようがない。

  • self.include(other) とすると、Mixinにより self に other のインタフェースが実装される。
  • self.extend(other) とすると、 other のインスタンスメソッドを self の特異メソッドとして追加する。

Module はインスタンス化されないから、Module のインスタンスメソッドは extend するか、 include するかしないと使うことができないってだけだった。いったい何が理解できずにはまってたのか、自分でもよくわからず。。

てことで、まとめ。

  • self.include(other) とすると、Mixinにより self に other のインタフェースが実装される。
    • 特異メソッドは実装されない
    • インスタンスメソッドは実装される。public, private, protected もそれぞれの扱いとして実装される
  • self.extend(other) とすると、 other のインスタンスメソッドを self の特異メソッドとして追加する
    • 特異メソッドは実装されない
    • private なインスタンスメソッドは特異メソッドとしては実装されない。private な呼び出しは可能。
module Foo
  def self.a # 特異メソッド
    puts "a"
  end

  def self.b # 特異メソッドからインスタンスメソッドを呼出
    d
  end

  def self.c # 特異メソッドから特異メソッドを呼出
    a
  end

  def self.c_self # 特異メソッドから特異メソッドを呼出
    self.a
  end

  def self.c_foo # 特異メソッドから特異メソッドを呼出
    Foo.a
  end

  def d # インスタンスメソッド
    puts "d"
  end

  def e # インスタンスメソッドから特異メソッドを呼出
    a
  end

  def e_self # インスタンスメソッドから特異メソッドを呼出
    self.a
  end

  def e_foo # インスタンスメソッドから特異メソッドを呼出
    Foo.a
  end

  def f # インスタンスメソッドからインスタンスメソッドを呼出
    d
  end

  def g # インスタンスメソッドから private なインスタンスメソッドを呼出
    h
  end

  def h # private なインスタンスメソッド
    puts "h"
  end
  private :h
end

class Bar
  include Foo
end

class Baz
  extend Foo
end

BarBazFoo
ClassName.method_nameすべてエラーd,e_foo,f,g は問題なく動作a,c,c_self,c_foo は問題なく動作
ClassName.new.method_named,e_foo,f,g は問題なく動作すべてエラー

インスタンス変数の動作。

module Hoge
  def self.set_value
    @hoge = "hoge"
  end

  def self.puts_value
    puts @hoge
  end
end

module Hoge
  def set_instance_value
    @hoge = "hogehoge"
  end

  def puts_instance_value
    puts @hoge
  end
end

class HogeHoge
  include Hoge
end

Hoge.set_value
Hoge.puts_value  #=> hoge

HogeHoge.new.puts_instance_value  #=> nil

ここに書くのが遅くなったから、エントリ上の日付だけ追ってると一週間これに悩んでたみたいだな。ま、いいか。

  

Ruby の include/extend が分からなくなってきた・・・

by tanabe on March 19, 2007

FileUtils は FileUtils.mkdir といった形で利用ができる。つまり、モジュールの特異メソッドになってるのだけど、module_function を探しても見つからない。

それでもコメントに燦然と輝く "# All methods are module function." の文字。

なんだ??と思って探し回った結果。↓

extend self

そりゃ、そうだぁ。そんなやり方もあったのね。。(あれ、もしかして一般的なの?)

というわけでメモ。

  • クラスメソッド=クラスの特異メソッド。モジュールメソッド=モジュールの特異メソッド。
  • モジュール関数=モジュールの得意メソッドであり、モジュールの private メソッド。
  • self.include(other) とすると、Mixinにより self に other のインタフェースが実装される。
  • self.extend(other) とすると、 other のインスタンスメソッドを self の特異メソッドとして追加する。
  • モジュールをクラスに extend するとモジュールのインスタンスメソッドがクラスメソッドになる。
  • include は、クラス(のインスタンス)に機能を追加。
  • extend は、ある特定のオブジェクトだけにモジュールの機能を追加したいときに使用。
  • モジュールの特異メソッドから内部の private メソッドは参照できない。(undefined local variable or method. include した場合は参照可能。extend だとダメ。)

うーん、わかってたつもりが混乱してきた。

class 内部に extend ModuleName して使うのはイレギュラーな使い方?でも、Forwardable モジュールとかでふつうに使うようなぁ。

  

[memo] パターンのメモ。

by tanabe on March 08, 2007

net/smtp から抜粋。

def initialize( address, port = nil )
  @address = address
  @port = (port || SMTP.default_port)
  @esmtp = true
  @socket = nil
  @started = false
  @open_timeout = 30
  @read_timeout = 60
  @error_occured = false
  @debug_output = nil
end

def SMTP.start( address, port = nil,
                helo = 'localhost.localdomain',
                user = nil, secret = nil, authtype = nil,
                &block) # :yield: smtp
  new(address, port).start(helo, user, secret, authtype, &block)
end

def start( helo = 'localhost.localdomain',
           user = nil, secret = nil, authtype = nil ) # :yield: smtp
  if block_given?
    begin
      do_start(helo, user, secret, authtype)
      return yield(self)
    ensure
      do_finish
    end
  else
    do_start(helo, user, secret, authtype)
    return self
  end
end
  

scrAPI をローカルでインストール

by tanabe on October 25, 2006

自分用のメモ。

でいけるはず。