2008-08-12
目次
りんご味Ruby 第30回 藤本 尚邦
前回に引き続き、YahooHonyaku#post_textを実装します。post_textの実装は:
・HTTP POSTのパラメータを準備
・x-www-form-urlencoded形式に変換
・HTTPリクエストのヘッダーを作成
・HTTPリクエストを送信
・受信したHTTPレスポンスを返す
という流れになります。
■ HTTP POSTのパラメータを準備
HTTP POSTのパラメータはキーと値の組になっていますから、Ruby上ではそれをHashリテラルで表現することにします。
params = {
:eid => _eid_(mode), # 変換モード(前回作った_eid_で正規化)
:text => text, # 翻訳したいテキスト
:key => @key, # HTTPレスポンスから取得した値
:time => @time, # HTTPレスポンスから取得した値
:both => "TH" # (謎のパラメータ、おまじない)
}
keyとtimeは、open_top_pageの中から呼ばれるparse_form!によって、HTTPレスポンスから抜き取った値をセットしています。
(余談) たかが一時的なパラメータの準備のためにHashを作るのはコストが高いのではないかという考え方があるかもしれませんが、それを気にしていてはRuby(やPerl)を使うメリットが半減してしまうというものです…と言い切りたいところなのですが、実際にRuby on Railsなどを使って公開するWebアプリを作るときなどは、状況によってはやはり気にした方がよいかもしれません。
■ x-www-form-urlencoded形式に変換
次に、準備したPOSTパラメータを、HTTP POSTリクエストのボディ部に書き出すためのx-www-form-urlencoded形式に変換します。x-www-form-urlencodedは、HTTP GETのURLでも使われている、パラメータを`&’で区切り、各パラメータのキーと値を`=’で区切る形式のことです。
body = params.map{ |key, val| "#{key}=#{val}" }.join('&')
mapメソッドは、paramsのキーと値の組み合わせのそれぞれについて、ブロックで与えられた手続きを実行(ここでは”キー=値”という文字列への変換)して、その結果の配列を返します。出来上がった配列の各要素は、joinメソッドにより`&’で接続された文字列に変換されます。例えば:
{ :eid=>'ej', :text=>'hello', :both=>'TH' }. # Hashの各要素を
map { |key,val| "#{key}=#{val}" }. # 「キー=値」に変換して
join('&') # '&'で接続
を評価すると “eid=ej&text=hello&both=TH” という文字列を返します。これで済めばよいのですが、「キー=値」の部分のkeyとvalはそれぞれURLエンコードする必要があります。ここではcgiライブラリを使ってURLエンコードします:
require 'cgi'
(中略)
body = params.map{ |key, val|
"#{CGI.escape(key.to_s)}=#{CGI.escape(val.to_s)}" }.join('&')
■ HTTPリクエストのヘッダーを作成
第27,28回で、Yahoo!翻訳のトップページをHTTP GETしてパースするためのopen_top_pageを実装しました。post_textではHTTP POSTするわけですが、まずHTTPリクエストのヘッダーやURLなど、両者で共有できる部分を整理します。
URLとHTTPリクエストヘッダーの共通部分を定数として定義します:
class YahooHonyaku
(中略)
URL = "http://honyaku.yahoo.co.jp/"
HTTP_REQUEST_HEADER = {
"User-Agent" => "Mozilla/5.0",
"Accept-Charset" => "UTF-8"
}
(中略)
def open_top_page
html = open(URL, HTTP_REQUEST_HEADER) { |io| io.read }
parse_form!(html)
@top_page_opened = true
end
(中略)
end
これに併わせてopen_top_pageも修正しました。HTTP POSTでは、content-typeにx-www-form-urlencodedの指定を追加します。
request = HTTP_REQUEST_HEADER.merge(
"Content-Type" => "application/x-www-form-urlencoded")
Hash#mergeメソッドは、レシーバのHashオブジェクトに引数で渡されたキーと値の組を追加した新しいHashオブジェクトを生成します。Hash#merge! というメソッドもあって、こちらはレシーバのHashオブジェクト自体に対して破壊的にキーと値の組を追加します。メソッド名に`!’を付けた場合は、レシーバオブジェクトへの破壊的操作を示すという慣習を思い出してください。
■ (補足) Mix-inモジュール、繰り返しなど
paramsからbodyへの変換のところで使ったmapメソッドは、Hashクラスではなく、Enumerableモジュールに実装されています。Enumerableモジュールは、eachメソッド(レシーバオブジェクトの各要素について、任意の手続きを繰り返す)を実装しているオブジェクトに、繰り返しに関わる典型的なメソッド群を組み込むためのモジュールです。Rubyの組込みクラスの中では、Array、Hash、Rangeクラスなどに、Enumerableモジュールが組み込まれています。
繰り返しを扱うようなクラスを書くのなら、eachメソッドを実装してEnumerableモジュールを組み込みましょう。
class YourClass
include Enumerable
def each
...
end
end
これで、YourClassクラスのインスタンス全てがEnumerableになります。これだけで、mapを始めとして、繰り返しに関するたくさんの手続きが使えるようになります。Enumerableのように、任意のオブジェクトに組み込んで使うためのモジュールをMix-inといいます。
(おまけ) ある一つのオブジェクトをEnumerableにすることもできます:
an_object = Object.new
...
def an_object.each # an_objectの特異メソッドとしてeachを定義
...
end
an_object.extend Enumerable
an_object.is_a? Enumerable # => true
これで an_object をEnumerableなオブジェクトになります。Rubyの柔らかさが示されたコードなのではないでしょうか。このようなコードを本物のプログラム内で使うことはあまりないでしょうが、irbで試しながらプログラムを作るようなときには使うことがあるかもしれません。
次回は、残りの部分、HTTPリクエストの送信を実装して post_text を完成させる予定です。
藤本裕之のプログラミング夜話 #143
テレビ番組の話が出たのを幸いに、ちと時事ネタ方面にシフトして行く。いや、これでもかろうじて全体の話の流れちうもんをなんとか保ってはいるんだけど、目の前にそのことを考えるのに絶好のネタであるオリンピックがころがってきたのでこれを見逃す手はないな、と。
思い起こせば……ってもそんな昔の話ではなく、アレは今年の5,6月ごろですか。著作権団体と電子機器メーカーの間で長いことすったもんだが続いていたダビング10の導入が「ぼやぼやしてると北京オリンピックが始まっちまう」からってんで急転直下決まったというハナシがあったではないの。
白状すればオレはもともとこのダビング10なんてどーでもいいと思っていて、上の決定の直前か直後くらいにこのモサの理事でもある元日経MACのハヤシさんとか、個人的に親交のあるMIAU(インターネット先進ユーザーの会)のコデラくんとかの話を聞いたり読んだりしてようやくなるほどそれはスジワルな仕組みですねと初めて気付かされたくらい。
参考までに書いておくと、そのスジワルというのは以下のようなことである。
まず、9回コピーと1回ムーブができてもその孫コピーが作れないのでは、この映像ソフトを永久保存したいと思っても、その「永久」がコピーしたメディアの寿命に依存してしまう。分かりやすく言えばHDレコーダの中に今年のNBAファイナルで我らがセルティックスが憎きレイカーを……あ、私情を入れるのはよくないわね。
とにかくセルティックスが勝った試合の録画がある、とする。この試合に感動したアナタはこれを子々孫々まで伝えたい、と思う。しかしあなたにできるのは、同じ速度で劣化するであろうDVD10枚にそれをコピーすることだけなのだ。遠い(いや近い?)将来、かつてビデオテープがDVDに代わったようにDVDに取って代わる新しい媒体が出現したとき、秘蔵のDVDからそれらの媒体に中身を移動するスベはない。
もうひとつ、こっちはもっとマクロな話。世界中でダビング10のようなセセコましい制限を可能にした機械が売れるのは日本市場だけだということだ。つまりですね、ダビング10という機構を組み込んだ機械はその分だけわずかながらコスト高なのであり、そんな機構を必要としない海外市場での競争力が落ちるのである。
なに? 海外には海外仕様のモノを作るからいい? ちょっと待て。iPodそしてiPhoneと、世界中で同じハードを売れることのメリットを我々は嫌というほどアップルに見せつけられているんと違うんかい。ただでさえ日本製品は人件費その他でコスト高なんだぞ、と。
でもここでオレが言いたいのはダビング10がいかにスジワルな機構かではなくて、それが「北京オリンピックに間に合うように導入されたと」というそっちの方なんである。なにが言いたいかわかりますか? わからない?
ではこちらからお聞きしよう。今オレがこの原稿を書いているのは8月9日の午後である。昨夜はその北京オリンピックの開会式でありました。今これをお読みのモサ伝読者のうちどのくらいのヒトが昨夜の開会式をデジタル録画し(アナログはもともとダビングで画質が落ちるしダビング10の対象ではない)、それをDVDなりに保存するおつもりであろうか。
……あのさ、かつて東京オリンピックのとき、「オリンピックをカラーで観よう」ってんでカラーテレビの売り上げが伸びたってのはわかるのだ。今回の北京だって「オリンピックをハイビジョンで観よう」というCMはオレ、わからないでもない(ノせられて買ったりしないけど)。
でもさ、「オリンピックをダビング10で残そう」なんてヒトが、著作権団体と電子機器メーカーがその根深い対立を棚上げにしてまで発売を間に合わせたいと、商売的に美味しいと思うほどいるもんだろうか? あなたはどう思われます?
(以下次回 2008_08_09)
高橋真人の「プログラミング指南」第141回
〜XcodeによるPowerPlant X入門(30)〜
こんにちは、高橋真人です。
さて、前回ご紹介したプログラムでは、Attachmentという仕組みを使ってViewの拡張を行いました。アタッチメントというのは言ってみれば、何らかの動作部分を独立したオブジェクトの形として作っておいて、動的に、つまりプログラムが走っている状態の時に、任意のオブジェクトに対してくっ付けたり外したりできるものです。
オリジナルのPowerPlantにおいて、このアタッチメントという考え方がLAttachmentというクラスによって実現されていたのですが、それがPPxにも引き継がれたというわけです。ただ、オリジナルのPPが主に対象としていたMac OS 9以前システムに比べると、OS Xではシステム自体が動的に振る舞いの変更ができる仕組みを持っているため、PPxのAttachmentは、そのシステムの仕組みを手助けするような形で成り立っています。
今までにも何度か触れていますが、Carbonイベントモデルにおいてはハンドラ(イベントを処理する関数)をイベントターゲット(ウインドウやビューなど、イベントを受け取る対象物)に対して複数インストールすることができるようになっています。そして、複数のハンドラのインストールされたターゲットにイベントが到達した場合、いちばん最後にインストールされたハンドラが起動されることになっています。
以前もやりましたように、そのハンドラがnoErrを返せばそこでイベントの処理は終結しますし、eventNotHandledErrを返すと次のハンドラ、つまり最後から2番目にインストールされたハンドラに処理が引き渡されるという具合です。また、ハンドラの中で“次の”ハンドラを呼び出すことが可能なのも、以前に見た通りです。
今回のプログラムで新たに作成したMyViewAttachmentクラスですが、クラス全体の構造こそ、今まで見てきたものとは少し異なりますが、内部で使用されている個々のAPIを見てみますと、クラス継承によってViewをカスタマイズするときとほとんど変わりません。特にアタッチメントのインスタンスが生成、初期化される部分(今回のプログラムでは、Application.cpの
DoSpecificCommand()の中です)で行っているのは、PPx::ViewやPPx::BaseViewをサブクラス化して作ったクラスの初期化部分のコードで行っていることそのままと言ってよいくらいです。
サブクラス化の時と唯一違うのが「対象にアタッチすること」です。まあ、アタッチメントなので当然ですが。注意していただきたいのは、AddAttach()関数を持っているのはアタッチメントの方ではなく、Attachable(アタッチメントを保持する側)の方だということです。このAttachableというクラスは、ViewやWindowはもとより、Applicationの継承元でもあるために、ほとんどのクラスにアタッチメントを付けることが可能になってるわけです。
さて、アタッチメントをくっ付けたら、今度は外す方です。
今回のプログラムでは、ウインドウがディアクティベートしたら(アクティブでなくなったらアタッチメントを外す仕掛けになっています。一回外れたアタッチメントはもう二度と戻ってこないので、かなりしょぼい仕様ですが(笑)。
これを行っているのはMyWindowの、その名もDoWindowDeactivate()という関数です。アタッチメントを外す手順としては、
・アタッチされているViewを探す
・外す対象のアタッチメントを探す
・外す
・アタッチメントを始末する
となります。
ここには、今まで登場していないことがたくさんありますので、回を改めて説明をします。
◇MOSAからのお知らせと編集後記は割愛します◇