Capybaraのtableishもどきを読む
こちらのgist で
page.find('table').all('tr').map { |row| row.all('th, td').map { |cell| cell.text.strip } }
というふうに以前のcucumberでできていたtableishをcapybaraで実現するコードが紹介されています。railsなどのwebアプリのテストで、一覧の出力順を含めたテストなどで非常に役立ちます。ただ、短いコードながら毎度毎度、読んで忘れて読んで忘れてを繰り返しているので、ちょっとした改造や、トラブルが起こった時にいっつも悩んでいたので、メモしておきます。
1行で書かれているものですが、各命令に分割して解読していきます。
解読した方法
基本的にはpryを使ってそれぞれの命令を順に実行して、APIドキュメントで情報を補足していきます。
例えばこんな感じ。
pryで止まった後
page.find('table').all('tr').class => Array
おぉArrayか、まぁ次はmapだからArrayの要素を見た方がいいのねっていうことで
page.find('table').all('tr')[0] #<Capybara::Element tag="tr" path="/html/body/div/div[2]/table/tr[1]">
というところまで、まぁ自分がつまづくところまでいって、そこから一度立ち止まってCapybara::Node::ElementのAPIドキュメントで次に呼ばれるメソッドがどこで定義されているのかとか他の情報を補足していきました。
解読
page
Capybara::Session
が入っている。ようするにwebページの情報が入っているもの。
page.find('table')
指定のタグに該当する Capybara::Node::Element
を取得するもの。なので同一ページで複数のtableタグがあると(html的に)最初のtableしかとらないんだと思う。
Capybara::Node::Element#find
というメソッドが存在しささそうで、継承やらインクルードで、このメソッドがどこで定義されているかがAPIドキュメント上見えない。ただし、定数 NODE_METHODSの中にfindという単語を見かけたので、なんらかうまくしてるんだと思う。
.all('tr')
Capybara::Node::Element#all
は Capybara::Node::Finders
で定義されている。返却されるのはArray。各要素は Capybara::Element
。
ここまでで、tableタグ内のすべてのtrタグ(見出しを含めた全行)を取得している。ここで行列相当の情報がとれているはず。
.map{|row| row.all('th, td')
.all('tr')
返却値の各要素は Capybara::Element
。なので、trタグの中にあるthかtdを取得している(各行にアクセスできる状態になっている)
.map { |cell| cell.text.strip }
cell
というのはそのもの最小単位の要素のこと。
cell.text
とするとよくわからん改行やら空白やらを含んだ文字列が取得される。 なので、 String#strip
で不要な空白などを削除する。String#strip
はrubyの標準のメソッドで前後の空白を削除するメソッド。
改造するには
こちらのgist にあるように 同じページの中にtableタグが複数あってidで取得するものを限定したい時は
page.find('table' + "#" + id)
とidを追加で指定すればいいようです。
同じようにそれぞれのレベル(その中この行は省きたいとか、このcellだけを取りたい)とかある時はそれぞれのレベルの all
を呼び出しているところに指定したclassとかidとかを指定しちゃうとうまくいきそうです。
すっきり!