NokogiriでXMLに子要素を追加すると要素名が小文字になる

環境

問題

  • XMLノードに子要素を追加する際にNokogiri::XML::Node#add_childを使いつつ引数を文字列で渡すと、要素名が小文字になる。要素名はdowncaseしないでほしい。

解決

  • Nokogiri::XML::DocumentFragment#newを使う。長ったらしくてめんどくさいし、もっといい方法がある気がするんだけど見つけられていない。

ソースコード

#!/usr/bin/env ruby
# coding: utf-8

require "nokogiri"

doc = Nokogiri::XML("<RootNode>")

doc.root.add_child "<AddChildByString>"
doc.root.add_child Nokogiri::XML::DocumentFragment.new(doc, '<AddChildByDocumentFragment>')

puts doc.to_xml
# =>
# <?xml version="1.0"?>
# <RootNode>
#   <addchildbystring/>
#   <AddChildByDocumentFragment/>
# </RootNode>

[追記 2013/03/07]Nokogiri::XML::Builderという便利なものがあった

Nokogiriのドキュメントを漁っていたら下記のような書き方で生成できることが分かった。こっちの方が便利そう。

#!/usr/bin/env ruby
# coding: utf-8 
 
require "nokogiri"
 
document = Nokogiri::XML::Builder.new do |doc|
  doc.RootNode {
    doc.AddChildByString
    doc.AddChildByDocumentFragment
  }
end
 
puts document.to_xml

メソッド内でメソッドの引数の一覧を取得する

def hoge(a, b, c)
  p self.method(__callee__).parameters
  #=> [[:req, :a], [:req, :b], [:req, :c]]
end

hoge(1, 2, 3)

Kernel.#__callee__Method#parametersを使う。
__callee__で今いるメソッドの名前がSymbolとして取れるので、それをself.methodの引数に渡して、Method#parametersで引数の一覧を取得する、という流れ。

なんだか自分の好きな本を10冊挙げるのが流行ってるみたいなので流行に乗りました!!!

自分の好きな本を挙げるのがにわかに流行っているみたいなので。順番は思いついた順です。

About Face 3 インタラクションデザインの極意

About Face 3 インタラクションデザインの極意

安村研に入って初めてのサブゼミ(輪読)の題材がこれだった。スマートフォンが出始めた当初くらいの本だからデスクトップアプリケーションに関する記述が多くてスマートフォンに関してはほぼ載っていないんだけど、インタラクティブなものを作る際の基礎的な知識はこれ一冊あれば大体カバーできるんじゃないでしょうか。
Linuxカーネル2.6解読室

Linuxカーネル2.6解読室

昔は割とOS寄りの方面の勉強をしていて、その際によく参考にしていた。Linuxはついこの間メジャーバージョンが3になったけど、内容自体はまだまだ古びていないと思う。
プログラミング言語 Ruby

プログラミング言語 Ruby

最近はdoc.ruby-lang.orgを見ることの方が多いのだけど、Rubyでプログラミングし始めた当初はよく参考にしていた。
Metaprogramming Ruby: Program Like the Ruby Pros (Facets of Ruby)

Metaprogramming Ruby: Program Like the Ruby Pros (Facets of Ruby)

こっちは上の本とは違い、Rubyの内部的な話が多い。合わせてRHGも参考にするといいですね。
ノンデザイナーズ・デザインブック [フルカラー新装増補版]

ノンデザイナーズ・デザインブック [フルカラー新装増補版]

僕はデザイナーではないしどちらかというとデザインは苦手なんだけど、とりあえずそれっぽい感じで作るときにどうやればそれっぽくなるかがすごく分かりやすく書いてるので割と参考にしてる。
誰のためのデザイン?―認知科学者のデザイン原論 (新曜社認知科学選書)

誰のためのデザイン?―認知科学者のデザイン原論 (新曜社認知科学選書)

使いにくいものというのはデジタルなものに限らず沢山あって、それを使いこなせないからといって諦めてはいけない、使いにくいものの方が悪いんだということを教えてくれた一冊。
フロー体験 喜びの現象学 (SEKAISHISO SEMINAR)

フロー体験 喜びの現象学 (SEKAISHISO SEMINAR)

ちょっと前にもはてブでこの本を紹介した記事が話題になっていたアレです。エンジニアの皆様は是非読んでおくべきですね。
JavaScript: The Definitive Guide: Activate Your Web Pages (Definitive Guides)

JavaScript: The Definitive Guide: Activate Your Web Pages (Definitive Guides)

JavaScriptの包括的なリファレンスがネット上には無いのでよく参考にしています。
情熱プログラマー ソフトウェア開発者の幸せな生き方

情熱プログラマー ソフトウェア開発者の幸せな生き方

エンジニアとしてどう生きるかみたいなことが書いてあった気がします。(うろ覚え)
エモーショナル・デザイン―微笑を誘うモノたちのために

エモーショナル・デザイン―微笑を誘うモノたちのために

上の『誰のためのデザイン?』と合わせて。

コマンドの処理が一定時間以上かかった時だけGrowlで通知する

~/.bashrcに書いておけば多分動く。bash以外で動くかは試してない。
Mac用に書いてるのでLinuxとかは適宜コマンドを(notify-sendとかに)置き換えてください。

function notify-last-command {
  # 3秒以上かかったらGrowlで通知する
  if [ 3 -lt $MYDATETIME ]; then
    growlnotify -n notify-last-command -t command -m "$( history 1 | awk '{s="";for(i=2;i<=NF;i++){s=s" "$i}print s}' )"
  fi

  MYDATETIME=0
}

PROMPT_COMMAND='notify-last-command'

MYDATETIME=0

function time_spent () {
  MYDATETIME=$(expr $(date +%s) - $MYDATETIME)
}
trap 'time_spent' DEBUG

仕組み

BashのPROMPT_COMMANDとtrapを利用している。
PROMPT_COMMANDに値を指定すると、プロンプトが表示される直前に指定した値がコマンドとして実行される。*1
trapは、本来はシグナルをトラップして色々処理するためのものらしいんだけど、上記のようにDEBUGを指定するとコマンドを入力してエンターを押した直後とPROMPT_COMMANDの直前で実行されるようなので今回はそれを利用している。*2
動作の順番としては、以下のようになっている。

  1. コマンドを打ってエンターを押す
  2. 1回目のtrapが動き、MYDATETIMEに今の時間をUNIX timeに変換したものが入る
  3. コマンド本体の処理が行われる
  4. 2回目のtrapが動き、MYDATETIMEに今の時間をUNIX timeに変換したものから元のMYDATETIME分引いたもの(つまりコマンドの実行にかかった時間)が入る
  5. PROMPT_COMMANDが動き、MYDATETIMEの値が3以上であればgrowlnotifyが起動する

作った理由

MacPortsのアップデートのような時間がかかる処理を終わったかどうかいちいちチェックするの面倒だし終わったら通知してほしい、みたいな感じです。

思うこと

このままだと

  • vimでファイルを編集して閉じる時
  • 新規にタブやウィンドウ作った時
  • topやman等からプロンプトに戻った時
  • パイプを使った時
  • 単にエンターキーを押した時

等にGrowlされてしまうのでその辺うまく解決出来ないかな〜と思う。

*1: 詳細はman bash(1)

*2: 詳細はhelp trap

はてなブックマークはメニューをmap要素以外で実装するかarea要素のcoords属性をちゃんと指定してほしい

はてなブックマーク( http://b.hatena.ne.jp/ )のメニュー(トップ〜動画のあれ)はmap要素を用いて実装されています。この記事を書いた時点でのHTMLを引用します。

<div id="category">
  <span>
    <img src="http://cdn-ak.b.st-hatena.com/images/scategory.png" usemap="#catmap" alt="カテゴリー" style="margin-top: -60px;">
  </span>
  <map name="catmap" id="catmap">
    <area coords="0,0,47,92" href="/" alt="トップ" title="トップ">
    <area coords="0,0,87,92" href="/hotentry" alt="総合" title="総合">
    <area coords="0,0,127,92" href="/hotentry?mode=general" alt="一般" title="一般">
    <area coords="0,0,166,92" href="/hotentry/social" alt="社会" title="社会">
    <area coords="0,0,232,92" href="/hotentry/economics" alt="政治・経済" title="政治・経済">
    <area coords="0,0,297,92" href="/hotentry/life" alt="生活・人生" title="生活・人生">
    <area coords="0,0,403,92" href="/hotentry/entertainment" alt="スポーツ・芸能・文化" title="スポーツ・芸能・文化">
    <area coords="0,0,468,92" href="/hotentry/knowledge" alt="科学・学問" title="科学・学問">
    <area coords="0,0,555,92" href="/hotentry/it" alt="コンピュータ" title="コンピュータ">
    <area coords="0,0,632,92" href="/hotentry/game" alt="アニメ・ゲーム" title="アニメ・ゲーム">
    <area coords="0,0,691,92" href="/hotentry/fun" alt="おもしろ" title="おもしろ">
    <area coords="0,0,733,92" href="/video" alt="動画" title="動画">
  </map>
</div>

これはHTMLのイメージマップという機能を使用しています。img要素にusemap属性を指定すると、そのusemap属性に指定した値を持つmap要素に結び付けられます。そしてそのmap要素の子であるarea要素がimg要素のイメージのどの部分に対応するかは、area要素のcoords属性によって決まります。
area要素は、指定した順番から前面に並びます。上記の例の場合、トップが一番前面に来て動画が一番後ろ側になります。そして、area要素がhref属性を持っている場合、a要素と同じようにハイパーリンクとして扱われます。area要素にはshape属性を指定することもできますが、この属性を指定しない場合はrectangleというキーワードが指定されたものとして扱われます。

ここまでが前提知識。

Vimiumのhit-a-hintとの兼ね合い

Google Chromeの拡張にVimiumというものがあります。Vimiumを使うとウェブブラウジング中の全ての操作をキーボードのみで行うことができます。(デフォルトでは)fキーを押すと画面内のハイパーリンクの矩形内の左上あたりにヒントが浮かび上がり、その状態でヒントに表示されたキーを叩くと、そのリンク先に飛べるようになっています。これを「hit-a-hint」というらしいです。

この記事を書いている時点でのVimium(1.29)では、href属性付きのarea要素はハイパーリンクとして扱われません。つまりhit-a-hintに反応しません。僕個人としては、はてなブックマークはよく利用しているのでメニューもhit-a-hintを使って移動したい所です。実は既に対応させるパッチは書いているのですが、正直実用に耐えるレベルではありません。ちゃんとhref属性付きのarea要素をhit-a-hintに反応するように出来ているのに、です。

というのは、上記のHTMLのarea要素のcoords属性を見てもらうと分かるのですが、全て1番目と2番目が0に指定されています。この場合のcoords属性の4つの値に何を指定するかは以下の通りです。

rectangle 状態 では、area 要素は、正確に 4 つの整数を持たなければいけません。最初の整数は、3 つ目の整数より小さくなければいけません。そして、2 つ目の整数は、4 つ目の整数より小さくなければいけません。この 4 つの整数はそれぞれ、イメージの左端から矩形の左側までの距離、上端から上側までの距離、左端から右側までの距離、上端から下側までの距離を、すべて CSS ピクセルで表したものでなければいけません。

area 要素 - 組込コンテンツ - HTML要素 - HTML5 タグリファレンス - HTML5.JP

上記のはてなブックマークのメニューの場合、coords属性の1番目と2番目の値は0です。イメージの左端から矩形の左側までの距離とイメージの上端から上側までの距離は全てのarea要素で同じになります。
先程も述べましたが、hit-a-hintのヒントはハイパーリンクの矩形内の左上あたりに浮かび上がります。ここで、href属性付きのarea要素をハイパーリンクとして扱い、ヒントを表示させた場合にどうなるでしょうか。coords属性の値からも分かるように、全てのarea要素の左上の位置は同じなので、全てのヒントが重なってしまいます。ヒントが表示されるようにできたとはいえ、全部重なってしまっているのでは流石にユーザビリティ上問題ありです。というわけでまだ公開していません。

個人的にはこの実装は変だと思っていて(なんでcoords属性の1番目と2番目の値を0にしているのかよく分からない)、ちゃんとcoords属性をそれぞれの矩形の大きさに指定するか、それともnav要素なりなんなりを使ってa要素を使う普通のリンクとして実装するかしてほしいなあと思うわけです。

セキュリティ&プログラミングキャンプ「Rubyのバグを探せ」問題を解いてみた

セキュリティ&プログラミングキャンプ「Rubyのバグを探せ」問題 - 西尾泰和のはてなダイアリーを見て、面白そうだなーと思ったので挑戦してみた。
Evernoteにログを残しながらやってて、ダウンロードして解凍が完了したのが20時56分、makeが通ったのが23時49分なので、大体3時間かかった計算になる。Rubyのビルドすらやったことなかったし、難しくて途中で投げ出してしまうかもなーって思ったんだけど、ちゃんとmakeが通ったので良かった。

Evernoteのログは公開してもいいんだけど、ぐっちゃぐちゃなまま&解いた人で答えを公開しているっぽい人が見当たらない(自分が見つけられていないだけかもしれないけれど)ので今は公開していない。