メソッドの'()'に気をつける

Hpricotを使ってて、どう考えてもメソッドが使えるはずなのにエラーになるということがあって、小一時間<*1>ほど悩みまくり。もう同じことで悩まないとは思うが、念のため記録しておく。

エラーになるケース

irb(main):137:0> (doc/:Item).each {|item| p (item/'itemPrice').inner_html }; nil

そんなに悪いコードじゃない(気がしていた)。docはXMLで、その中の"Item"ノードを全部検索('/'は'.search()'のAlias)して、"itemPrice"の値を順番に出力するという意図。でも結果(↓)はNG。

(irb):137: warning: don't put space before argument parentheses
# "1974" }]>
NoMethodError: undefined method `inner_html' for nil:NilClass

ここでエラーの意味をちゃんと考えていれば、小一時間悩まなくて済んだのに。最初のwarningは「メソッド名と括弧の間にスペースがあるよ」という警告で、「そのメソッド、括弧つきで解釈するけど、それでいい?」ってこと(大意)。
つまり、「p (item/'itemPrice').inner_html」は「p(item/'itemPrice')」をやってから「.inner_html」をやるよということ。出力をみると確かに「p(item/'itemPrice')」をやった結果が1行出て、「p(item/'itemPrice')」の式の値、nilに対して「.inner_html」しようとするので、「nil:NilClassには`inner_html'みたいなメソッドねぇよ」と言われてる。

エラーにならないケースも

でも、そうだとしたら、こういう場合はなんでエラーにならないんだろ。irbのパースの問題かな。これ(↓)はOKになります。warningも出ません。

irb(main):129:0> p (doc/'Item/itemPrice').inner_html
"197420882499"
=> nil

これ(↓)ももちろんOKです。

irb(main):130:0> p((doc/'Item/itemPrice').inner_html)
"197420882499"
=> nil

これ(↓)は warningが出てNGです。上のケースと同じで、ブロック内がダメなんでしょうか。

irb(main):134:0> 1.times {p (doc/'Item/itemPrice').inner_html }
(irb):134: warning: don't put space before argument parentheses
# "1974" }, 
{elem  "2088" }, 
{elem  "2499" }]>
NoMethodError: undefined method `inner_html' for nil:NilClass

'p'と'('の間のスペースを詰めると warningは出なくなりますが同じことです。

irb(main):135:0> 1.times {p(doc/'Item/itemPrice').inner_html }
# "1974" }, 
{elem  "2088" }, 
{elem  "2499" }]>
NoMethodError: undefined method `inner_html' for nil:NilClass

これ(↓)がまぁ正解なんでしょうね。パースのルールがよくわからないうちは、括弧を省くなということです。特にブロック内は厳しく。

irb(main):136:0> 1.times {p((doc/'Item/itemPrice').inner_html) }
"197420882499"
=> 1

*1:自分はそんなに馬鹿じゃないよということを暗にほのめかすときの時間単位。実際には1時間以上、場合によっては2、3日かかることもあることを総称して小一時間という