2008年9月アーカイブ

Rails を触りだしてはや半年。

正直、emacs-rails(rails.el)の機能は、バッファ切り替えくらいにしか使えてません。シェルでやれる事はシェルでやってしまってます。

簡単な機能リストが欲しかったので、この機会に浅くコードを眺めて機能を洗い出してみました。対象はSubversionリポジトリのrevision 232です。
KOSHIGOE学習帳 - [Ruby] emacs-rails

ちなみに、最近のemacs-railsではRSpecに対応していた事を知りました。とはいえ、バッファ切り替えには対応していない様なので、そこは自力で強引にねじ込んでいたりします。

これでちょっとは効率あがるだろうか。

Rails(ActiveRecord) の STI

使うとスマートじゃない様子。
Ruby on Rails Code Quality Checklist - Articles - Matthew Moore

STI の仕組みを使わなくても、同じ様な事を実現するのは簡単で、STI に伴う弊害を考えると、STI は使うべきじゃない、といった事が書かれている気がします。ネックになっているのは、同一テーブルを使う事による密結合とかそういう話なんでしょうか。それ故に、マイグレーションが楽しくないとか、なんだとか?

原文である上記参考記事を見る前に、「STI 駄目なのか、実装が変なのか?」と勘違いしてコードを追いかけてたりしたわけですが、その存在自体が否定されているようですね?

まだ、駄目さが実感できていないので、何とも言えません。"Polymorphic associations"ってなんでしょう。STI によるアプローチは駄目だけど、ぽりもふぃっくな考え方はスマートだよね、というオチ(?)を付けたという事ですかね。

とりあえず、状況を見つつ、STI じゃないポリモフィックなアソシエーションの実現について、気にしてみようと思います。


おまけです。
KOSHIGOE学習帳 - [Ruby] Rails の STI について調査

アプリ開発に踏み切る際の問題点(敷居の高さ)としてNDAがあるのを忘れてた。

開発において、技術交流を外せないと考えている方々にとって、NDAは敵でしょうね。

素直に、自身の成果物で利用者に利益をもたらし、自分にも利益をもたらす事で満足できれば、問題は全くないわけですが、交流的なところに主題(?)を置いてしまうと、NDAが大きな壁となって立ちはだかるわけです。きっと。

情報の共有は社内外において同一開発プロジェクトに関わる方同士のみ共有可能。
iPhoneをみんなで開発するにはどうすればいいか - ku-sukeのはてなダイアリー

クローズドなグループ内では交流できるようですが、気軽にブログに書き散らす事はできないわけです。まあ、社内外を問わず開発プロジェクトを立ち上げてしまえば、その参加者の間では交流できるわけで、クローズドソースとは言いつつも、仲間内ではオープンソースな関係は作れるわけですね。プロジェクト運営には、結構気を使わないと大問題になりそうですが。

とりあえず、Appleの努力(Safari安定化)に期待しつつ、"iPhone SDK Agreement"を読み進めようと思います。


ところで、SDKの同意書とは別に、"Registered iPhone Developer Terms and Conditions"というものがあるようですが、リンク先が404で困っています。気にせずに、ユーザ登録(した覚えが無い; Apple IDでログインできた気がするけど)をして、更にはSDKをダウンロードしてしまっているので、同意してしまっているわけですが、気になって仕方ありません。"iPhone Dev Center"のログインページにリンクがあるわけですが、404ってのはどういう事でしょうか。

ADCのユーザ登録は、以前に何かのディスクイメージをダウンロードするためにした記憶がありますが、iPhone用に登録した覚えがあるようでないようで、非常に曖昧です。まあ、今のところ問題になる様な事をした覚えが無いし、これからもする予定がないので、いいんですけど。線引きが理解できてないので、非常に不安です。まずいです。

そういえば、交流云々は、ADCのフォーラムとかがあったりするんですかね。相変わらず英語は鬼門としか思えず、積極的に関われないわけですが、Appleから場が提供されていれば、さほど問題(越えられない壁)でも無いのかもしれませんね?

帰省と iPhone

どうも、実家で 3G いけるらしい。

実家で、アンテナ5本たってました。昔の(3Gじゃないやつ)よりアンテナ事情よくなってますね。

おかげさまで、Byline を使って Google Reader の未読消化もできました。

さて、重大な事実に気がつきました。それは、Safari が安定しないのではなく、iPhone (のOS?アプリケーションフレームワーク?)が不安定らしい、という事です。

YouTube アプリをしばらく使い続けていたら、突然(YouTubeアプリ)落ちました。iTunes で音楽聞きながら、メモアプリで5行くらい書いたら(メモアプリが)落ちました(メモ内容は保存されてた)。

あと、アプリケーションを積みすぎたのか、動作(反応)がやけに重くなった気がします。メモリを消費しすぎているのでしょうか?

定期的に本体をリスタート(リセット?)させないと、駄目なんでしょうかね。

単純にウェブアプリ(だけ)で何かしようとはできないね。

Safari は役立たずです。ちょっとした(利用時間が少ない)ビューワとしてはいいんですが、あれこれとアクションを重ねる場合には、本当に役立たずです。

iTunes と並行して動かしていると、特に顕著で、1ページ開いていて、スクロールしただけで落ちます。Safari だけ動かしているつもりでも、落ちます。

どこかで見かけましたが、原因はキャッシュやらのメモリ周りだとか、なんだとか?汎用にしたが故の難しさが、安定性欠如につながるんでしょうか?

とりあえず、モバイルだと、それほど多目的にする理由は無いと思うので、特化型アプリをたくさん積んでおくのが良さそうです。例えば、はてな touch とか、そんなんですね。

ウェブアプリだからといって、ウェブブラウザ(Safari)にこだわる必要はありません。使い勝手(ユースケース?)をよく考えて、最適解を導く必要がありますね。

iPhone の仕組みを理解しているわけではありませんが、専用の iPhone アプリとしてインターフェースを提供する方が安定する気がしています。もちろん、開発者の技術力次第ではありますが。

この辺も、実は Apple の狙い通りなんでしょうか?ハードウェアと、OS、操作インターフェース(概念?)は提供するから、あとは自由にしてね、と。不満があるなら、作っちゃいなよ、ユー、と。

iPhone に関する議論等を、ほとんど目に入れていないのでアレですが、App Store にたくさんアプリがあるのは、こういう事ですよね?ビルトインだけど、YouTube とか Google Maps とか、iPhone アプリにしたのは、端末の利用目的としてウェブ(ブラウザ)から切り出すべきなんて考えたのとは別に、検証段階で Safari に問題があったからでしょうし(多分ね)。

CSS などで iPhone 対応するのは、現状では無意味(効果的でない)でしょうね。少なくとも、ユーザが直接訪れる様な、利用目的がはっきりしている場合は、iPhone アプリを作って専用インターフェースを提供した方が、ユーザとしては嬉しいはずです。というか、そうでなければ、多分、使い(え)ません。Safari が不安定ですから。単品でちょっと見たいだけなら、いいんですけどね。

後、情報量が多くて雑然としている場合も、専用インターフェースが必要ですね。自分はともかく、携帯電話でのインターネット利用を普通に経験している、イマドキな皆さんには分かりきった事でしょう。ナビゲーション設計、導線設計も、PC での常識は通用しませんからね。

そんな事をつらつら考えてみると、(ウェブベース?でない端末ベース?の)RSSリーダーって、iPhone にこそ必要じゃないですかね。リーダーというか、RSS を利用してアグリゲート(これは既存サービスを再利用したい)して、蓄積した情報を効率的に閲覧できる仕組み。Safari でのインターネット利用が絶望的だと考えているので、じゃあ、ある程度目的を絞った汎用ビューワってのはどうだろうか、と。HTMLのレンダリングとか考えると、結局 Safari につながってしまいそうですが、そこはサーバ側で変換するなりしたらいいでしょうし(iPhone ではリクエストして結果を受け取ってそのまま表示するだけね)。

「検索エンジンを使って、Web にランダムアクセスしたい」とかいった要望には、完全には応えられないでしょうけど、それこそ、RSS 検索とか用意して API 経由で誘導したらよさそうな気がします。あ、Firefox の拡張で、RSS 使ってスタートページ作りますってのがありましたね。あれの iPhone 版とかないですかね。

App Store で探したら、この辺のアプリってありそうですよね。この辺のアプリはまだ探っていないので、あれば使ってみる予定です。

さて、携帯電話でインターネットを利用できる様になった頃に語り尽くされただろう事を、今更ながら嬉々として夢想しているこのごろです。


情報収集だけなら、PlaggerとGmail使えば十分かも。Plagger使ってリーダーの未読記事をGmailに送って、iPhone のメールアプリで読む、と。ある程度までは、メールを使っておけば、Safari 問題に対応できそう。

増量キャンペーン

着実に増えてる。。。

見事に晴れたので、予定通り健康診断に行ってきたわけですが、年々体重が増えてます。食べる量は増えてないのに。

やっちゃいけない体重の増え方ですね。

運動して、おなかが減って、自然と食べる。そんな生活に憧れます。

iPhone の悩み

困った。

まず、サクサク読めるUIを備えたPDFビューワが欲しい。アプリからPDFファイルを開いたときのビューワ、あれはひどい。Air Sharingを使って、macから転送したPDFを開いて読んでみたわけですが、読み続ける気力を根こそぎ持っていかれます。誤操作で先頭に戻ってしまったり、途中から読む方法が分からなかったり、散々です。

あと、ファイルの中央管理(?)の方法が分かりません。ファイルは、アプリごとに管理するしか無いんでしょうか。だとすると、一度macに転送して、改めて適当なアプリを使って転送する事になるんですかね。1つのマウントポイント(?)を使って簡単に同期がとれないと厳しすぎる気がします。

致命的なのが、突然のiTunes再生ですね。これは、周りで被害にあっていた人がいたので、そういう症状があるらしい事は知っていましたが、自分にも降り掛かってびっくりです。というか、そもそも、iTunesの終了というか、停止が無い(見当たらない)のが気に入りません。一時停止なんて曖昧なことせずに、男らしく(?)停止もしくは終了したいわけです。ヘッドフォンのジャックを抜いたとたんに音が鳴り響くと腰が抜けそうになります。

というかですよ、消音(マナーモードかな?)にしてるのに、iTunesでスピーカから音が出るのはどうなんでしょうかね。iTunesとは別の音量コントロールを触って勘違いしてるんでしょうか。正直、スピーカからiTunesの音を出す意味が分かりません。デフォルトにしておく意味が分かりません。むしろ、ジャック抜いたらiTunesが起動できないくらいでちょうどいいです。あ、そういう制御が可能になるアプリがあったら、いいですね。

と、文句たらたらなわけですが、基本的には気に入ってます。全体的に狭っ苦しい時代の携帯電話しか使った経験がないので、感覚的に使えて開放感があるiPhoneは凄まじく心地いいです。携帯電話の入力インターフェース(デバイス?)の進化を全く経験してないからかもしれませんが、こだわりを持ってUIにこだわっている風なappleがまた好きになっているところです。まあ、使い続けたら、結局iPhoneのソフトウェアキーボード(?)が使いにくく感じるのかもしれませんが。

そういえば、変な勘違いというか、ものを知らないというか、簡潔にいえば赤っ恥をかいたわけですが、無線LANルータなくても、iPhoneでWi-Fi使ってネットワークにつなげられるんですね。インターネット共有ってものがありましたね。仮想マシンでお世話になっているのに、思いつかないところは、さすがネットワーク音痴です。遺憾なく無知っぷりを発揮してしまいました。

一応、アクティビティモニタを使ってネットワーク状況をモニタした感じでは、LANを通っている様に見えました。ネットワークのセグメント(?)がどうとか、LAN内のホストにアクセスできるか、とかは試し無いので分かりません。あどほっく、覚えました。
Palm + Mac OS XでAdhoc WiFi インターネット接続

さて、結局、iPhoneで何をどうやって見ようか方針が定まりません。URLをメールして、そこをブックマーク代わりにするのがいいんでしょうか。普段、Safariを使わないので、ブックマークの共有も何もないわけです。後で読むとかを使って、gmailにスクラップをためておいて、それをiPhoneで消化するのがいいんでしょうか。何か、フリーで特別なフォーマットのデータファイルを扱うアプリケーションが出てきてくれませんかね。PDFがどうも絶望的な印象なので、上手い事索引などを用いてサクサク読み進められる感じを期待します。適当にHTMLなりTXTなりをそのフォーマットに変換できるmac用ツールもあると嬉しいですね。データフォーマットもオープンだと、あれこれいじる人が出ていいですね。なんてのが本当になったら、野良があふれて大変な事になるんでしょうか。PDFを上手く扱うアプリがあれば、それで解決でしょうけどね。

まったくまとまりませんが、使い始めて5日位での感想でした。

PASMO・イン・ジャケット

【2ch】ニュー速クオリティ:「iPhoneっておサイフケータイ使えないじゃん」という問にAppleが回答
↑を思い出して、今日の帰りは iPhone で改札パス。

ジャケットが iPhone ジャストなので、横っちょが膨らんじゃってますけどね。そのうち、カードが曲がりそうな気がしますが、まあ、いいでしょう。

定期更新の時に投入口に入れられなかったら、そのときに、考え直します。

結構前に目にしてはいたものの、ずっとスルーしていましたが、いい加減まずい気がしたので、調べているところです。

メタクラス

Wikipediaより(日本語です)。

メタクラス (metaclass) とは、クラス-インスタンス関係を持つオブジェクト指向プログラミング言語で、クラス自身も「クラスオブジェクト」として扱われる場合にそのクラスオブジェクトが所属するクラスのことをいう。
メタクラス - Wikipedia

さっぱり、意味が分かりません。文の意味は分かるものの、それが何のためにあるのか、イメージ(実感)できません。

とある実装から(Ruby)

実は、以下のソースを眺めていたのがきっかけで、今回の話題になったわけです。
lib/object.rb at master from github's hubahuba — GitHub

class Object
  def metaclass() class << self; self end end
  def meta_eval(&blk) metaclass.instance_eval(&blk) end
  def meta_def(name, &blk)
    meta_eval { define_method(name, &blk) }
  end
end

メタクラスに関する実装と思わしき部分を抜粋しました。

001:>> class Object
002:1>   def metaclass() class << self; self end end
003:1>   def meta_eval(&blk) metaclass.instance_eval(&blk) end
004:1>   def meta_def(name, &blk)
005:2>     meta_eval { define_method(name, &blk) }
006:2>   end
007:1> end
=> nil
008:>> s = "hoge"
=> "hoge"
009:>> s.meta_def(:to_ss) { return "ss" }
=> #<Proc:0x0033e8ac@(irb):9>
010:>> s.to_ss
=> "ss"

普通に、特異メソッドが追加された程度の印象です。

Ruby自体にメタクラスの概念が存在する

Rubyには、メタクラスという概念があり、すべてのクラスは、同名のメタクラスというものを持っていて、これは、Classクラスのインスタンスになっています。
6.5 クラスメソッドはありますか|FAQ::クラス、モジュール - Rubyリファレンスマニュアル

Rubyを使い始めて半年くらいたちますが、「Rubyではこうなんだ」で覚えた事の中に、メタクラスの概念が自然と入り込んでいたようです。Rubyで特異メソッドおよび特異クラスを実現するために、メタクラスという概念が必要だったという事でしょうか。

「すべてがオブジェクト」という思想を持つプログラミング言語(処理系?)を実装するためには、メタクラスという概念を導入(実装)する必要があり、それを具体的な言葉にすると、「クラスはClassクラスのインスタンスである」となる、のでしょうか。

メタクラスの使いどころ

やっぱり、さっぱり、分かりません。

特異メソッドだけに限って考えるなら、「任意のオブジェクトに対し、動的にメソッドを追加したい」ケースは経験しているので、イメージしやすいです。

ベースとなるクラスがあり、そのインスタンスを利用して、要求を満たす操作が可能なオブジェクトにカスタマイズする、といった場合に、特異メソッドを使います。

ActiveRecord のモデルを動的に作る

もしかしたら、これはメタクラスの概念を利用した実装方法になるのでしょうか。

Rails の ActiveRecord で、動的に作成されるテーブルのモデルオブジェクトを作成する場合、あらかじめモデルクラスを定義する事はできません。テーブルが存在しないために、エラーとなります。

そこで、必要になったときにクラスオブジェクトを作るという方針をとります。

001:>> table_name = 'samples'
=> "samples"
002:>> ActiveRecord::Base.connection.create_table(table_name, :force => true) do |t|
003:>>   t.column :value, :string
004:1> end
=> []
005:>> model = Class.new(ActiveRecord::Base) do |klass|
006:>>   def self.to_s
007:2>     table_name.singularize.camelize
008:2>   end
009:1>   set_table_name table_name
010:1>   reset_column_information
011:1> end
=> Sample(id: integer, value: string)
012:>> silence_warnings do
013:>>   Object.const_set(model.to_s, model)
014:1> end
=> Sample(id: integer, value: string)
015:>> model.create(:value => 'test')
=> #<Sample id: 1, value: "test">
016:>> model.find(:all)
=> [#<Sample id: 1, value: "test">]
017:>> Sample.create(:value => 'hoge')
=> #<Sample id: 2, value: "hoge">
018:>> Sample.find(:all)
=> [#<Sample id: 1, value: "test">, #<Sample id: 2, value: "hoge">]

これは、Rails どころか、Ruby 自体(今より更に)よく理解していない頃に教わりました。上記コードは、今の理解を元に書き起こしたものなので、おかしな事をしているかもしれません。

ただ、やはり、これも、「テーブルを動的に作る場合の ActiveRecord の使い方」と捉えてしまって、メタクラスを使ったテクニックといった話に繋ぐ事ができません。というか、そもそも、メタクラスが関係してくるのかも微妙にわかりません。

Class.new を使って新しいクラスオブジェクトを作り出しているだけです。ただ、「動的にクラスを作れているのは、メタクラスによってクラスがオブジェクトとして扱えるからだ」と考えて、メタクラスに関係しそうだと考えました。

続く

もっと、根本的な話をどこかで見つけて、勉強したいと思っています。

結局、今回はよくわからないまま終わります。正直、本当に何も分からないので、ちゃんとした文献をあたって、根本を知る時間を取らないと駄目だと思っています。続きを書けるか分かりませんが、ある程度の理解を得られるところまでは持っていく予定です。

以上、ギブアップだす。

User-Supplied Identifierの復習

User-Supplied Identifier の怪 - Yet Another Hackadelic
ちょうどいい機会なので、便乗して復習させてもらった。

正規化を無視すれば、言い切っていいんだろうな、と思いました。

「認証を開始するために RP に提示する Identifier が User-Supplied Identifier」だと理解しているので、どこでどうなったとかは関係なさそうな印象です。エンドユーザが直接 RP のログインフォームに記入しようが、OP 経由で RP に教えようが、RP からすれば、認証を開始するための Identifier を受け取るだけでしょうし。

仕様書で定める用語定義としては、やや利用例(?)的な記述が目立ってしまった、という事でしょうか。それはそれで、ユースケース的なセクションをもうけたら良かったりする気がしますが、用語定義にまとめるのがすっきりするんでしょうかね。

ちなみに、先に正規化云々と書いたのは、User-Supplied Identifier は正規化されてなくても受け入れられるだろうけど、Claimed Identifier なんかは正規化されてなきゃ駄目なんじゃないかな、という事です。解説として、「User-Supplied Identifier は具体的に〜」と言うのは問題ないし分かりやすいけど、仕様としては(厳密に隅っこ突っついたら)間違いなんだろうな、と。

とまあ、正直、参照記事について肯定も否定もできる様な理解度ではないので、よけいな茶々を入れただけになってしまいました。が、復習の機会を得られた事に、感謝しています。

今まで、まともに仕組みを調べていなかったので。
Rails Engines

だいたいの理解のまましばらく使い続けてきましたが、いい加減、基本的なところは知っておくべきだろうと思ったので、簡単に調べてみる事にしました。

  • どのようにして Rails を拡張しているのか
  • Rails Engines によって何ができる様になるのか

ひとまずは、このくらいの事が掴めればいいかな、と思っています。

で、そのメモが以下。
KOSHIGOE学習帳 - [Ruby] Rails Engines基礎

調べながら、思考そのままの流れで書き続けたので、色々と穴があったり変な事を書いているかもしれませんが、気づき次第手直ししようと思います。

本当に軽く調べただけなので、メソッド名から動きを推測して書いている事も多々あります。ドキュメントが英語なので、頭が痛くなって、ぼかして書いてある事もあります。ただ、まあ、一応の一連の動きを掴めたと思っているので、それはそれでいいかな、と。

個人的に、今のところ Rails Engines は便利で気に入ってます。

少なくとも、Rack::Auth::Basicの様な手軽さはない気がして仕方がない。

現状では、rackup configでuseするだけで、全体にOpenID認証を適用できるというわけではありません。また、任意のアプリケーションを簡単に保護する事ができるわけでもありません(X.new(Y.new)の様に)。

基本的に、認証用ロケーションにRack::Auth::OpenIDをmapしておき、保護対象のアプリケーションではセッションを見て認証状態を判断する事になるのでしょうか。認証できていない場合は、Rack::ForwardRequestを使って認証に誘導する事になるでしょうか。

あまり整理できていませんが、大体このようなイメージでいます。OpenID認証故の特性があって、ミドルウェア化が難しいという事は無いと思っているので、今後、このモジュールが発展していけば、いつかはRack::Auth::Basicの様な手軽さが実現されるだろうと期待しておきます。想像以上にOpenIDプロトコルを把握できていないので、手を出す自信がありません。

さて、区切りもいいので、Rackについてはこの辺にしようかと思います。


とかいいつつ、また引っぱるかもですがね。

ruby-openid付属のデモOPを利用して、ローカル内で認証処理を完結できる様にして、なんとか認証できる事を確認。

デモOPを使う様にしてすぐに、RPからOPに認証リクエストを投げられるという所までは確認できました。OPにリダイレクトされて認証確認画面が表示される場面です。デモOPなので、一般のログイン処理ではなくusernameの入力だけですが、とにかく認証処理を進める事ができる様になったわけです。

さて、OPでの認証に失敗する事はありえない(実装的にスルーなはず)ので、後はRPへのリダイレクトが成功すれば一段落です。ところが、ここでまたエラーになるわけです。NoMethodエラー。

問題は、ハンドラで起きていて、どうもエラーストリームに文字列として扱えないオブジェクトが混入された事が原因のようです。そのオブジェクトは、OpenID::Message。

どこで、何が原因で混入したのか、すぐに見つける事ができず悩んでいましたが、冷静になってみれば、簡単です。認証は(おそらく)成功しているだろうと考え、Rack::Auth::OpenID#finishの最後の認証成功レスポンスを疑ったわけです。明らかに怪しげな、レスポンスボディへの要素追加処理が書かれています。

この処理をコメントアウトする事で、ぱっと見でRack::Auth::OpenIDの処理が成功したかと思っています。認証成功後のリダイレクト先URLが表示されましたし。

さて、これはどうしたものでしょうか。バグだと考えてもいいんですかね。ここまであからさまだと、見逃しようがない気がするので、自分の試し方がレアケースな様に思えて仕方がありません。

ひとまず、一段落という事で、rackup configをコミットしました。以下、rackup configです。コードよりコメントがやたらと長いのは気にしないでください。戦の爪痕です。


あ、書き忘れてた気がしますが、勘違いでなければOpenID認証の2.0を使っています。プリントデバッグで格闘中に、2.0のverify経路をたどったはずなので。

GitHubがやたらと重い…。

結局は、OpenID認証の仕様を忘れている(というか、そもそも理解できていない)事が分かった。

訳も理解も中途半端に放り投げてしまった結果、トラブルに対応しきれないというのが現状です。

一応、Rack::Auth::OpenIDの使い方についてのイメージは掴めました。結局これは、アプリケーションで、認証保護したいアプリケーションの内部で上手く扱う必要があるのではないでしょうか。ログインアプリ内部でRack::Auth::OpenIDを実行し、OpenID認証の結果を待ち、そのセッションを利用して、保護対象のアプリケーションを保護する、というイメージでとらえています。

ソースをちゃんと読まなかった事が原因でつまずいていた点が、いくつかあります。

  • そもそも、useでは使えない(initializeの第1引数がアプリケーションではない)
  • そもそも、セッション(env['rack.session'])が存在していないと動かない
  • Rack::Auth::OpenIDのアプリケーションへのリクエストURLには、適切なパラメータを渡さないと動かない
  • ここでも、Rack::Lint::LintErrorが立ちはだかる(deploymentで動かせば良い)

今は、YahooをOPとした認証ができないか試しているところですが、どうもRPからOPに投げるリクエストが誤っているようで、Yahooのエラーページが表示されてしまいます。これについては、まずはOpenID認証の仕様書を改めて読まないと解決できないと思っています。以前に用意しておいたYadis IDがあり、それで試しているわけですが、これ自体はfastladder.comをRPとした際にはYahooのログイン画面にまで行けているので、間違っていないだろうと思っています。

ちなみに、この1つ前のエントリで、"~>"について書いたのは、このあたりの調査中に出会った事がきっかけです。ひょっとしたら、OpenID認証の1.1互換モードか、そもそも2.0は使えないのかと疑ってみたりしたわけです。使っている(インストール済み)パッケージは、ruby-openidの2.1.2だけで、かつデバッグ出力に現れたパラメータからネームスペースを確認しても、2.0のsignonを使っているので、互換モードという事も1.1によるものでもない様に思います。

一応、Rack::Auth::OpenIDをrackupで動かすだけのrackup configを貼っておきます。まあ、取り立てて何をしているわけでもありませんが、セッションミドルウェアを事前に有効にしておくのがポイントです。セッションキーは、両者で同じものを使う必要があります(サンプルではデフォルトのままなので、両者ともに'rack.session')。

これ以上については、とりあえず、のんびりと探っていこうと思います。


多分、問題になっているのは、RPのdiscoveryじゃないかという気がしています。ローカルでアプリケーションを動かしているので、OPがreturn_toを使ってRPのdiscoveryを行おうとすると、サーバにつながらずにエラーになるはずです。それが原因で、RPが使えないという意味のエラーページが出ているのでは、とにらんでいます。とりあえずは、ローカルにデモOPをたてて検証をしてみればいいのかな、と。

"~>"という演算子の意味

まったくもって理解していなかったどころか、存在すら知らなかった。

RubyGemsでは、"gem gem_name, version_requirements"のようにして、バージョン縛りのrequireができるという様な事は理解していました。が、実際にソースを読んで何をしているかを調べてはいませんでした。

最近、「gem 'ruby-openid', '~> 2' if defined? Gem」というコードを目にしたわけですが、この"~>"が理解できなかったわけです。はじめ、「大なりの否定?」かと思ったわけですが、そういうわけでもなく。

ソースを眺めた結果、どうも「要求バージョン以上で、かつ最後のリビジョン番号(?)を越えない」という様な意味らしい事が分かりました。例えば、「"~>2"」なら「2以上、3未満」で、「"~>2.1"」なら「2.1以上、2.2未満」と。

さて、間違ってますかね?

Rack::Adapterを調べてみる

Rackのアダプタ(Rack::Adapter)は、アプリケーションというか、ミドルウェアじみたものなんだろうか?

インターフェースを規定した抽象クラスでもあるのかと思ってたわけですが、ネームスペース確保のため(?)に空モジュールが定義されているだけの様子。

それなら、ハンドラ(Rack::Handler)はどうかと思ってみて見ると、2つのクラスメソッドが定義されていて、後は実装済みハンドラのロードと登録をしているだけ。クラスメソッドも、サーバ名を指定してクラスを取得するgetと、サーバ名に紐づけたクラスを登録するregisterのみ。

と思えば、ハンドラはコメントに振る舞いが書かれていますね。アプリケーションを第1引数にとるrunメソッドの呼び出しで活動する様です。任意で、第2引数としてサーバ固有の設定情報などが納められたHashオブジェクトをとるようです。rackupのソースを見ても、最後に"server.run app, options"とあり、振る舞いベースですぐに実装方法がわかるので困りませんね。

さて、アダプタは結局どうしたらいいのでしょうか。同梱されたRack::Adapter::Campingを見る限り、アプリケーションを引数にとるinitializeと、外部から呼ばれるであろうcallメソッドが実装されています。callメソッドは、アプリケーション同様にenvを引数にとります。引数にとったアプリケーションの実行前に、envのセットアップ(CGI環境変数のセットなど)をし、実行後にヘッダの調整をして、Rackプロトコルに従ったレスポンスを返しています。落ち着いてみれば、結局振る舞いベースで仕様が分かった様な気がします。とりあえず、アプリケーションをラップするミドルウェア的なものだと考えておきます。

まあ、アダプタなので、hogeとfooを繋ぐ役割を果たせればよいわけで、Rackの流儀に則るために前後の処置をするだけのミドルウェア(アプリケーション)として実装すればよいわけですね。名前から分かれよ、と自分に言っておきます。

以上をふまえて、どうもssbで無理矢理Rack対応させたコードは(方針的に)よろしくないのではと思い、Rack::Adapter::SSBとして実装してみました。以前と別のファイル(libs/rack/adapter/ssb.rb)にしてみました。が、config.ruをいじってしまったので、微妙なコミットになった気がします。Rack関連で汚らしいコミットをしてしまってい、申し訳ありません。

そんなこんなで、ソースは以下。先述の通り、CodeReposにコミットしてしまっています。

フレームワークでもないのに、アダプタを用意する必要があるのか分かりませんが、システムを汚さないという意味で、多少は意味があるかなと思いやってしまいました。9割以上は興味本意ですが。


多分、もうしばらくはRackネタが(散発的にでも)続くと思います。

後先考えず、空気も読まず、気分がのったので、晒してみます。記事を書いた後で、意外と日本語のRack情報があった事に気づいて愕然としたのは、内緒です。以降、基本的には公式ドキュメント等の劣化コピーなので、注意してください。

Rackのミドルウェアを、(ほぼ)一通り経験したはず。
koshigoe's rackup_examples at master — GitHub

こういうものにGitHubを使うべきかは分かりませんが、気にせずあげておきます。コメントの英語は適当なので、読まずに感じ取ってください。

Rackはメジャーなのかどうなのか、よく分かりませんが、個人的に興味がわいたので、あれこれ触っているところです。現時点では、プロトコルを読んで、ssbでRack対応を経験して、今日ミドルウェアを一通り触った、という所まで。

残念ながら、Rack::Auth::OpenIDだけは、さっぱり使い方が分かりません。これは、(多分)ミドルウェアではないという事は分かるんですが、アプリケーションとしてインスタンスを作って、それをどう使っていいのかが分かりません。Rack::Auth::Basic並みな手軽さを期待したいところですが、OpenIDが(個人的に思うに)複雑なので、難しいのでしょうか。必要なパラメータを与えたら、特定のアプリケーションや全アプリケーションの認証をOpenIDで行う様にできないもんでしょうか。OpenIDは、数ヶ月ほったらかしたせいで、ほとんど覚えていません。仕様書を読み返せば、思い出せる気がしてますが。。。

さて、Rackは社内勉強会にちょうどいいネタになりそうですが、自分の順番まで後2ヶ月弱あった気がします。これだけ間が空くと、発表する気力がなくなりそうで、どうしたものか悩みどころです。鮮度的にもきつい気がしますし。

これから、Rackについて一通りまとめてみて、blogやwikiに載せつつ、勉強会でつつきあえればいいのかも?

最大の難点は、Rackが世間にどう受け止められているのか、さっぱり分からない事でしょうか。もう、普通に使われていて、記事にする様な事もない、とかなんでしょうかね?ミドルウェアの情報をウェブで探した限りで、ほとんど情報が見当たらなかったんですが、ミドルウェアだからでしょうか。ソース読んで自己完結、みたく。

0.xだから様子見中?

手元の環境ではそこそこ動いたので、迷惑をかけない程度にコミット。

放り投げたままになってしまったOpenIDドキュメントの件でコミット権をいただいていたので、コミットしてみる事にしました。OpenIDの件は、申し訳ない限りだとは思っています。

  • ruby 1.8.7でsyntax errorとなる箇所の修正
  • softbankの絵文字取得先URLの変更とすくレーピングコードの修正
  • HTML変換の中のURL変換のテスト修正
  • Rack対応とmod_passenger対応

以上について、コミットしておきました。Rack対応については、config.ruを触らなければ影響がないようになっているはずです。内容については、テストコードは書いていないので、保証はできません。

URL変換のところについて、怪しげなところを見つけて適当に修正したりもしましたが、どうするのがベターかよく分からなくなったのでコミットしていません。ユーザ入力のURLを適切にエスケープするのが苦手です。クエリ部分を確実に問題なく処理するとか。まじめに考えればいいんですけど、ちょっと気力が尽きました。現時点での差分は以下の通り。

ひとまず、こんなところです。

とりあえず、mobile.hatena.ne.jpが(ぱっと見)表示できるところまでは確認した。

まじめにやるなら、WEBrick前提なところを全部Rack前提にいじる必要がある様な気がしますが、まだRack対応についてよく理解できていないので、今回はcallメソッドを追加してごまかしてみました。

以下の様な、libs/ssb/rack.rbという拡張用のライブラリファイルを追加。

rackupで動かすために、config.ruを追加。

後は、rackupでサーバを起動。動かすウェブサーバの指定については、何も考えていないので、結局、WEBrickで動いてましたが、一応、Rack越しにアプリケーションが動かせる事が確認できました。

問題は、call(env)として動かすために、run(cgi_request, cgi_response)で受け取る引数のごまかしが適当すぎる事でしょうか。cgi_requestは、Rack::Requestを使ってごまかしていますが、cookiesのところが個人的に鬼門なので、本当に適当です。挙動の検証すらしていません。動けばよしでやっつけました。cgi_responseは、さらに適当です。使っているインターフェースに適合させただけです。

この状態でmod_passengerを使って動かせるかは未確認ですが、ひとまずRackを使えた事に満足しました。


mod_passenger(2.0.3)でも動く事が確認できました。最初のコードに少々手を加えています。

  • Lintでenv['CONTENT_LENGTH']を文字列で入れろと出るのでLintを省略
  • staticなファイルはmod_passenger側でファイル読み込みになるので省略
  • mod_passengerを使っているかどうかの判断はENV['RACK_ENV']で
  • 一部WEBrickを使用する部分でrequireと名前の絶対表記が必要だったので修正

CONTENT_LENGTHの件は、よく分かりません。WEBrickで動かしたときには問題がなかったので、mod_passengerで動かした場合にLintが妙な動きをするんでしょうかね。


Rack::Lintのcheck_envは、アプリケーションのcallが実行される前に実行される様子。つまりは、レスポンスのCONTENT_LENGTHではなくて、リクエストのCONTENT_LENGTHもしくは、環境変数の初期状態についてチェックしているという事になる。

問題となっているのは、CGIキーが文字列(String)であるかどうか、というところ。rack/lint.rbの140行目あたり。ハッシュのenvをeachでまわしているので、CONTENT_LENGTHというキーは存在している事になる。

mod_passengerから渡るenvは、リクエストヘッダをパースしたもの。ただ、CONTENT_LENGTHは必ず代入されるため、キーは必ず存在する事になる。このときヘッダのHTTP_CONTENT_LENGTHというキーに該当する値が使われる(passenger/abstract_request_handler.rbの293行目あたり)。

Content-Lengthヘッダは、必ず存在するヘッダフィールドではないので、mod_passengerの問題となるのでしょうか。それとも、自分が見たコードは、転送コーディングが施されていない場合にのみとおる部分だったりするんでしょうか。

Rack::Lintが必須キー以外についても、Stringの型判定をしているのが悪いのでしょうか。個人的には、必須でないとされているものについては、nilを許容してもいい気がしています。が、Lintだから厳しい限りは問題ないというか、そうすべきな気もしています。

結局、この件については、放置です。

Rackに興味を持ってみたり

Rubyでのアプリ開発はRailsから入ったので、軽く動かせる仕組みが知りたいわけで。

とりあえず、プロトコル仕様のドキュメントを読みつつ、読み返すために和訳。
KOSHIGOE学習帳 - [Ruby] Rackプロトコル仕様

サンプルコードで目にした引数のenvが何なのか、なんとなく掴めた気がしています。

今は、ここまで。

気がついたら、モバイル端末向けWebコンテンツ(アプリケーション)の開発に手を出す事になってた。

見切り発車な感は拭えませんが、求められているのは良い事だという事で、やれるだけの事をやるだけです。

今までは、個体識別情報やコンテンツ制作ガイドラインなどを調べたり、モバイル対応ライブラリの調査などをするだけでしたが、ようやくコンテンツの検証が必要になる段階まできました。

そんなこんなで、各キャリアが提供しているシミュレータをダウンロードして触ってみたりしたわけですが、auのシミュレータが公開停止になっていました。Openwave SDK6.2Kというやつです。どうしていいのか分からなくなったので、ssbを使ってみる事にしました。
ssb - server side browser | CodeRepos::Share – Trac

どうやら、会社で使っているものの簡易版だという事ですが、基本的な確認用途には十分満足できるだろうと期待しています。ただ、流石にフォームのUIまでは再現していない様子。まあ、そこは本質ではないでしょうし、いいです。

さて、絵文字を表示してみようとえっちらやってたわけですが、どうも、最初のsetup時にちゃんと画像が取得できていなかったようです。特にSoftbank。URLというか、サイト自体が変わってますからね。

絵文字画像の取得と変換には、色々とパッケージをインストールする必要があるわけです。一応、会社のiMac(Leopard)では、以下のインストールで対応できました。基本的なRuby開発環境は整っている前提です。いくらか、不要なものもインストールしているかもしれませんが、それはそれ。

$ sudo port install ImageMagick
$ sudo port install ghostscript
$ sudo port install lha
$ sudo port install rb-rmagick
$ sudo gem install rmagick
$ sudo gem install scrapi
$ cd ~/tmp
$ wget http://user.it.uu.se/~jan/html2ps-1.0b5.tar.gz
$ tar xvzf html2ps-1.0b5.tar.gz
$ cd html2ps-1.0b5
$ sudo ./install

依存関係を解決したら、スクリプトの変更が必要です。先に書いた通り、Softbankの技術情報サイトは移動しているので、それに合わせてスクレーピングできる様にします。一応、以下の様な修正で対応できました。

一応、ちゃんと見られる画像形式に変換される事を確認しています(rake setup)。

上記差分には、ssb.rbの修正も含まれていますが、これは、サーバを動かしたときにSyntax Errorとなったための修正です。未だに、Rubyの括弧省略周辺の文法はよく分かりません。ので、できる限り括弧をつける様にしています。積極的に省略するのは、プロパティ的使い方をする場合くらい(メソッドじゃなくて、普通の属性値を読み取るような)。

これで、絵文字も見られる様になりました。というところまでやっておいて、別のシミュレータの存在を教えられました。
Firefoxでモバイル端末をシミュレートする独自アドオン「FireMobileSimulator」を公開します - 遙かへのスピードランナー

絵文字画像も同梱されているようで、インストールするだけで使えました。どちらがよいのかは、まだ分かりません。1画面内で一通りの操作ができるssbの方が、今のところは好きです。デュアルディスプレイのセカンダリの方にFirefoxを置いているので、メニュー操作が面倒なんです。macなんです。画面サイズも、ssbなら最初から小さくなってますしね。普段、Firefoxは全画面表示なんです。リサイズめんどくさいんです。

今困っているのは、ssbをRack対応にする方法が分からないというところ。SSB::Applicationをどうにかしたらいい様な気がしていますが、どうしたらいいのかよく分かりません。そもそも、Rack自体を触った事がありません。

俗にいう、Passenger厨なので、なんでもmod_passengerで動かそうとしてしまうんです。mod_rubyなんて分かりません。だって、ようやくLeopardで動いたんですもの。


なんか、リダイレクトがうまく扱えない気がする(ssb)。リダイレクトのリンクをクリックすると、ssb_qがおかしくなる。(元URL)/(リダイレクト先)なURLに飛ぶ。こんな修正で大丈夫だろうか。"//"なURLとか、HTTP以外とかは無視。

未だに適当に呼んでるけど、なんて呼ぶもんなんだろう?

ここで言う"エンジン"は、Rails Enginesを使って作ったエンジン。vendor/pluginsに置くやつです。

エンジンを使って、ちょこちょこっとお化粧したアプリケーションを、なんて呼ぶんでしょうかね?

RubyGems パッケージっぽく

jpmobileにモバイル用検索クローラを追加したやつを、変更点だけ切り出してRailsプラグインにした後、毎回プラグイン入れるのもアレかと思い、RubyGemsパッケージ化を目指す。

まったく分からないので、以下を参考に、それっぽく体裁を整えるところまで。

今のところ、コードをGitHubで管理しているので、違う方法がありそうな気配を感じていますが、純粋にRubyGemsパッケージを作る手順を知りたかったので、newgemを使った方法を習ってみました。

RubyForgeにアカウントを持っておらず、今回のパッケージをRubyForgeに置くつもりもなく、その辺の記述はダミーでごまかしています。

ひとまずはライブラリのテストとパッケージ化(gem化)まで。

Railsアプリとの連携はこれからです。

プロフィール

このアーカイブについて

このページには、2008年9月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2008年8月です。

次のアーカイブは2008年10月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。