以下を実行した際、「Ruby home page」と出力されることを期待していたのですが、画面には、「ruby home page」と表示されます。
# hpricot_prac.rb require 'rubygems' require 'hpricot' doc = Hpricot('<a href="http://ruby-lang.org/">ruby home page</a>') (doc/:a).each do |ele| ele.inner_html.gsub!(/ruby/, "Ruby") p ele.inner_html end
何故だろうと思い、hpricot-0.6/lib/hpricot/elements.rbを見てみたところ、inner_htmlメソッドがあり、そこでは、
def inner_html(*string) if string.empty? map { |x| x.inner_html }.join else x = self.inner_html = string.pop || x end end
というような配列に対する処理をやっているので、gsub!できないんだなと理解したつもりなのですが、このinner_htmlは、Elements#inner_htmlです。
以下のように、(doc/:a)は、Hpricot::Elementsなので、(doc/:a)に対してinner_htmlメソッドを呼び出して実行できるのは納得できるのですが、
p (doc/:a).class #=> Hpricot::Elements p (doc/:a).inner_html #=> "ruby home page"
以下のように、eleは、Hpricot::Elemなので、ele.inner_htmlが実行できるのが不思議な感じがします。(*)
p ele.class #=> Hpricot::Elem
p ele.inner_html
オブジェクト指向は奥が深い…。
追記(2008-08-01)
(*)は不思議ではないです。納得しました。id:rubikitch さんに教えていただきました。
Hpricot::Elem#inner_htmlの実体はHpricot::Traverse#inner_htmlで、hpricot/modules.rb よりこんな定義になっているからです。
module Hpricot module Traverse end module Container::Trav; include Traverse end class Elem; module Trav; include Container::Trav end; include Trav end end
で、hpricot/traverse.rbを見てみると、こうなっています。
module Hpricot module Traverse def html(inner = nil, &blk) if inner or blk altered! case inner when Array self.children = inner else self.children = Hpricot.make(inner, &blk) end reparent self.children else # ここを通る if respond_to? :children children.map { |x| x.output("") }.join end end end alias_method :inner_html, :html alias_method :innerHTML, :inner_html end end
あと,最初に書いた,hpricot_prac.rbは,以下のようにHpricot::Elem#inner_html=を使えばうまくいきます。
# hpricot_prac.rb require 'rubygems' require 'hpricot' doc = Hpricot('<a href="http://ruby-lang.org/">ruby home page</a>') (doc/:a).each do |ele| ele.inner_html = ele.inner_html.gsub(/ruby/, "Ruby") pp ele.inner_html end