[sqlite3][ruby]busy_handlerを使ってみる

すっごいいまさらですが、sqlite2(Ruby/DBI)→sqlite3(sqlite-ruby)の移行に着手しています。

sqlite2の時は、アクセスが競合してBusyになった時のリトライ処理が見あたらなかったので、自前でこんなことをしていました。

10.times_retry(DBI::DatabaseError,1) {
@dbh = DBI.connect(@@db_prefix+@db_filename, "", "" ) unless @dbh
}

(DBに接続を試みて、DBI::DatabaseErrorが発生したら、1秒sleepしたあとリトライ、10回試みて、それでも駄目だったらDBI::DatabaseErrorをraiseします。)

Rubyらしいエレガントなコード♪とか思っていたのですが、プログラムのあちこちにこれが散見される状態というのもなんだか悲しい。

sqlite-rubyでは、busy_handlerという機能が実装され、こういう事はライブラリ内でやってくれるようになりました。ただ、これのサンプルが見あたらない。
あれこれ試行錯誤していちおう動くようになったのですが、なんだか謎の部分も残っているので、ご存じの方、教えていただけると嬉しいです。

片方のシェルで、sqlite3を走らせて、ロック状態にしておきます。

sqlite> BEGIN;
sqlite> insert into fragments (user_id,content,lastmod,bornin) values(0,"test","2009-04-01 12:59:15","2009-04-01 12:59:15");

(sqlite2の頃は、BEGIN;した時点でロックされていたのですが、今のバージョンでは、実際にロックが必要になった時点でロックするみたいです)

もう片方でirbを走らせて、こんな感じでロックに激突させます。

require 'rubygems'
require 'sqlite3'
dbh = SQLite3::Database.new("test.sqlite3")
#dbh.busy_timeout(10000)
dbh.busy_handler{|data, retries|
print "in busy_handler data is ",data,"\n"
print "retries is ",retries,"\n"
sleep 1;
(retries<=3)
}
dbh.execute("insert into fragments (user_id,content,lastmod,bornin) values(0,'test','2009-04-01 12:59:15','2009-04-01 12:59:15');")

それいけっ!

irb(main):013:0> dbh.execute("insert into fragments (user_id,content,lastmod,bornin) values(0,'test','2009-04-01 12:59:15','2009-04-01 12:59:15');")
in busy_handler data is nil
retries is 0
in busy_handler data is nil
retries is 1
in busy_handler data is nil
retries is 2
in busy_handler data is nil
retries is 3
/usr/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.2.1/lib/sqlite3/errors.rb:94:in `check': database is locked (SQLite3::BusyException)
from /usr/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.2.1/lib/sqlite3/resultset.rb:76:in `check'
:

よしよし。

まとめ:busy_handlerの使い方

  •  DBに接続したあたりで、
    dbh.busy_handler{|data, retries|
    # BUSYだった時の処理
    }
    

    という具合にして宣言する。このコードはすぐ実行されるわけではなく、DBにアクセスしてBUSYだった時に初めて実行される。

  • retriesに試行回数が入っている。
  • (謎)busy_timeoutの挙動が謎。APIリファレンスを見ると、busy_timeout(1000)ってしたら、一秒おきにリトライしそうな物ですが、実際には丸無視しているように見えます
  • しょうがないので、busy_handler内でsleepします
  • (謎)dataって何でしょうね?
  • busy_handlerのブロックの評価結果がtrueであれば、リトライが実施されます。falseであれば、例外が投げられます。

最後の項目、return false って書くわけじゃなくて、ブロックの評価結果が使われるところがポイントかと。
上のサンプルでは、3回まではリトライして、それで駄目なら例外を投げるようなコードになっています。

gcalapi AuthSubでカレンダーリストを取得する

あけましておめでとうございます。あんまり正月とか関係なしに淡々と開発しております。
年末年始に外出すると混むんだもん。

で、メイドめーるでデフォルト以外のGoogleカレンダーの情報がとれない件。

googlecalendar.PNG
Googleカレンダーでは、こうやって複数のカレンダーをまとめて表示させることが出来るのですけど、メイドめーるがメールしてくれるのは一番上のものだけになっております。
なんでかというと、Googleカレンダーにアクセスするのに使っているgcalapiで、カレンダーリストを取得するAPIがみつからなかったからです。

正確に言うと、ユーザー名とパスワードを使ったらとれるのですけど、なぜかAuthSub認証の場合だけ実装されていないのです。
Googleのドキュメントを見たら、取得する方法があるみたいなので、コードを追加してみました。

module GoogleCalendar
class ServiceAuthSub
CALENDAR_LIST_PATH = "http://www.google.com/calendar/feeds/default/allcalendars/full"
# get the list of user's calendars and returns http response object
def calendar_list
logger.info("-- get_calendar_list_responce st --") if logger
auth unless @auth
uri = URI.parse(CALENDAR_LIST_PATH)
res = do_get(uri, {})
logger.info("-- get_calendar_list_responce en(#{res.message}) --") if logger
res
end
alias :calendars :calendar_list
end
end

これだけ実装してあげれば、あとは通常認証と同じコードが働くので、こんなふうにして複数のカレンダーを取ってくることが出来ます。

require 'gcalapi'
require 'googlecalendar/service_auth_sub'
require 'googlecalendar/calendar'
srv1 = GoogleCalendar::ServiceAuthSub.new(user.calendarToken)
cal_list1 = GoogleCalendar::Calendar.calendars(srv1)

出来ればこのコード、本家に取り込んでいただけるとみんな幸せになるように思うのですけど。連絡つくかな?

[rails]自分のドメイン名をとるためにはrequest.domainがいいらしい

photo by  flyzipper
 rails上で自分のドメイン名をつかってナンヤカンヤしたい時というのがあって。

ENV[‘HOSTNAME’]

 これは論外っぽい。

[default] ENV
[default] :{“HOSTNAME”=>”xxxx.mogya.com”,

仮想ドメインの設定を無視してホスト名を返してくれます。HOST_NAMEという値ですから、当たり前か。

request.env:[‘SERVER_NAME’]

request.env:{“SERVER_NAME”=>”maidmail.jp”,

 ねらい通り、ドメイン名を返してくれます。ただ、このヘッダも、あとrequest.env[‘HTTP_HOST’]も、ちょっとしたことで簡単に偽装されてしまうので、迂闊につかえません。

request.domain

[default] request.domain:maidmail.jp

 こっちを見ると、request.env:[‘SERVER_NAME’]が偽装されてしまうような場面でも、正しいドメイン名がとれました。
ソースを見ると、HTTP_X_FORWARDED_HOSTというヘッダを使うみたいです。

# File vendor/rails/actionpack/lib/action_controller/request.rb
318: def domain(tld_length = 1) 319: return nil unless named_host?(host) 320: 321: host.split('.').last(1 + tld_length).join('.') 322: end 280: def host 281: raw_host_with_port.sub(/:\d+$/, '') 282: end 271: def raw_host_with_port 272: if forwarded = env["HTTP_X_FORWARDED_HOST"] 273: forwarded.split(/,\s?/).last 274: else 275: env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" 276: end 277: end

HTTP_X_FORWARDED_HOSTがとれなかった場合に限り、env[‘HTTP_HOST’]やenv[‘SERVER_NAME’]を見てくれるので、HTTP_X_FORWARDED_HOST非対応のサーバでも、request.domainを見ておけば悪いようにはしない実装になっているみたいです。

HTTP_X_FORWARDED_HOSTのうまい説明が見つけきれなかったのですが、これってどういう環境変数なのかなぁ?どれくらい信用のおける値なのでしょう?

[mextractr]gemでインストールできるようになりました。

先日公開したmextractr_webapi.rbが、gem経由でインストールできるようになりました。

こんな具合にして使うことが出来ます。

[daisuke@snares ~]$ sudo gem install mextractr_webapi
Bulk updating Gem source index for: http://gems.rubyforge.org
Successfully installed mextractr_webapi-0.0.1
1 gem installed
[daisuke@snares /var/maidmail]$ script/server
>> require "mextractr_webapi"
>> mextractr = MextractrWebApi.new(API_KEY)
>> res = mextractr.parse("11/29(土) 京都で紅葉を楽しむオフ会を開催します。秋深い京都を撮り歩きましょう。 ")
>> pp res
{"id"=>"20081202_0001",
"where"=>
[{"georss:point"=>nil, "valueString"=>"京都"},
{"georss:point"=>nil, "valueString"=>"京都"}],
"what"=>[{"valueString"=>"オフ会"}],
"when"=>[{"valueString"=>"11/29(土)", "startTime"=>"2008-11-29"}],
"updated"=>"2008-12-02T12:16:50+09:00"
"content"=>
"11/29(土) 京都で紅葉を楽しむオフ会を開催します。秋深い京都を撮り歩きましょう。 ",
"response"=>
"\n\n  Mextractr WebAPI results\n  2008-12-02T12:16:50+09:00\n  \n    Mextractr\n  \n  20081202_0001\n  \n    \n    20081202_0001\n    extracted event metadata\n    \n      Mextractr WebAPI\n    \n    2008-12-02T12:16:50+09:00\n    11/29(土) 京都で紅葉を楽しむオフ会を開催します。秋深い京都を撮り歩きましょう。 \n    \n    \n    \n    \n  \n\n",
}

enjoy!

波線を含むメールが文字化けする問題への対処

 メイドめーるでは日本語のメールを送信するために、
ヽ( ・∀・)ノくまくまー(07-31[長年日記])で書いていただいている、Iso2022jpMailerを使っています。
クラスを継承するだけでつかえるとてもいいアイデアだったのですが、たった一つ問題が。

IMGP8827.JPG

「~」が文字化けするのです。普通のアプリケーションだったら、まあ横棒でいいか、とか言えるし、実際メイドめーるも今日までそうやってごまかしてきたのですけど。萌え萌えなメイドさんがメールをくれるアプリケーションにおいて「~」がつかえないと言うことは、「おはようございます~」がつかえないことになってしまいます。
「おはようございますー」と「おはようございます~」は断じて違うのです!

とまあそんなわけで、技術的に追求した結果、一応「~」が出せるようになりましたのでご報告です。

IMGP8828.JPG

やり方としては、Iso2022jpMailer の

@mail.body = NKF::nkf('-j', @mail.body)

の部分を

@mail.body = NKF::nkf('-Wxm0 --oc=ISO-2022-JP-1', @mail.body)

に置き換えます。
ついでに、タイトルとかに「~」がはいることもあるだろうから、

text = NKF.nkf('-j -m0', text)

も、

text = NKF.nkf('-Wxm0 --oc=ISO-2022-JP-1', text)

にしちゃいました。

技術的な話

正直に言うと、なんでこれで化けないのか、最終的な理由は理解できていません。
ただ、あれこれやっているうちに、化ける時と化けない時があって、比較するとこうなっていることが分りました。

wave_on_jis.PNG

上がうまくいく場合で、下が駄目な場合です。日本語メールなのでJISコードでエンコードしてあります。
OKな時は1B 24 42で始まっていて、駄目な時は1B 24 48で始まる…、つまり、JIS X 0208-1983でエンコードしてあればOKで、JIS X 0212-1990だと駄目なことが分ります。
NKFのコマンド詳細を見ながら、irbでいろいろやってみると。

>> NKF::nkf('-jWxm0', "~").each_byte{|b| print b.to_s(16)+","};print "\n"
1b,24,28,44,22,37,1b,28,42,
=> nil
>>NKF::nkf('-Wxm0 --oc=ISO-2022-JP', "~").each_byte{|b| print b.to_s(16)+","};print "\n"
1b,24,28,44,22,37,1b,28,42,
=> nil
>> NKF::nkf('-Wxm0 --oc=ISO-2022-JP-1', "~").each_byte{|b| print b.to_s(16)+","};print "\n"
1b,24,42,21,41,1b,28,42,
=> nil
>> NKF::nkf('-Wxm0 --oc=ISO-2022-JP-3', "~").each_byte{|b| print b.to_s(16)+","};print "\n"
1b,24,28,50,28,37,1b,28,42,
=> nil

–oc=ISO-2022-JP-1の時だけ、ねらい通りの変換をしてくれるようです。ということで、Iso2022jpMailerにも、同じ引数を渡してあげると、「~」が出るようになった次第。
「ISO-2022-JP-1」って、JIS X 0212に対応させるための引数で。むしろ逆のような気がするし、なんでISO-2022-JP-3で駄目なのかも謎です。

[ruby]mextractor web apiのライブラリ

Mextractrに関する注目が集まっているので、自分が使っているライブラリを公開します。

mextractr_webapi.rb

#RubyForgeのプロジェクトは今申請中なので、gemはちょっとお待ちくださいませ。

使い方としては、APIキーを取得して、

$ script/console
Loading development environment (Rails 2.0.2)
>> require "mextractr_webapi"
>> mextractr = MextractrWebApi.new(MEXTRACTR_KEY)
>> res = mextractr.parse("11/27(木)、京都市中京区のはてな京都オフィスでShibuya Perl Mongersテクニカルトークのパブリックビューイングが開催されます。")
>> pp res
{"id"=>"20081127_0001",
"updated"=>"2008-11-27T10:05:38+09:00"
"response"=>
"\n\n  Mextractr WebAPI results\n  2008-11-27T10:05:38+09:00\n  \n    Mextractr\n  \n  20081127_0001\n  \n    \n    20081127_0001\n    extracted event metadata\n    \n      Mextractr WebAPI\n    \n    2008-11-27T10:05:38+09:00\n    11/27(木)、京都市中京区のはてな京都オフィスでShibuya Perl Mongersテクニカルトークのパブリックビューイングが開催されます。\n    \n    \n    \n  \n\n",
"content"=>
"11/27(木)、京都市中京区のはてな京都オフィスでShibuya Perl Mongersテクニカルトークのパブリックビューイングが開催されます。",
"where"=>
[{"georss:point"=>nil, "valueString"=>"木)、京都市中京区"},
{"georss:point"=>nil, "valueString"=>"京都"}],
"when"=>[{"valueString"=>"11/27(木)", "startTime"=>"2008-11-27"}],
}

という具合です。
res[‘when’][0][‘startTime’]とか、res[‘where’][0][‘valueString’]という具合で各値を取り出すことが出来ます。

あと、MextractrWebApi.new(MEXTRACTR_KEY)の二つ目の引数にloggerを渡すと、取得してきたatomの内容とかを見ることが出来るので、挙動がおかしい時に犯人を突き止める一助になります。

[ruby]gcalapiを使ってAuthSubログイン

photo by Martin LaBar

 昨日はすっごいがんばって自前でGoogleAuthSub認証を通すコードを書いたのですが。

今日、続きでカレンダーを扱おうとしたら、gcalapiに同じ内容のコードがあるのを発見してしまいました。

しかもこっちの方が明らかにきれいです。だったら最初からこっちでよかったんじゃん。

再発明しちゃった車輪をもてあそびつつ、gcalapiでAuthSub認証を使う方法を記述しておきます。

Googleの認証ページ用のURLを生成する

require ‘gcalapi’

require ‘googlecalendar/auth_sub_util’

@uri = GoogleCalendar::AuthSubUtil.build_request_url(

‘http://www.example.com/responce’,

‘http://www.google.com/calendar/feeds/’,

false, #use_secure

true #use_session

)

Googleから戻ってきたトークンをつかってセッショントークンをもらう

authsub_token = ”

one_time_token = params[:token]

session_token = GoogleCalendar::AuthSubUtil.exchange_session_token(one_time_token)

戻ってきたページのURLからワンタイムトークンを取り出すためにget_one_time_tokenというメソッドも用意されているのですが、railsであればparams[:token]でとる方がずっと早いですね。

セッショントークンをつかってカレンダーに予定を書き込む

require ‘gcalapi’

require ‘googlecalendar/auth_sub_util’

require ‘googlecalendar/service_auth_sub’

server = GoogleCalendar::ServiceAuthSub.new(session_token)

calendar = GoogleCalendar::Calendar.new(server, ‘http://www.google.com/calendar/feeds/default/private/full’)

event = calendar.create_event

event.st = Time.parse(“2008-09-19 20:00:01”)

event.en = Time.parse(“2008-09-19 22:00:01”)

event.title = “実験!”

event.desc = “こんにちはこんにちは! ”

event.save!

あとは基本的に、GoogleCalendar::Service の代わりにGoogleCalendar::ServiceAuthSubを使えばいいらしい。calendar_listが動かないのはしょうがないのかなぁ。

[ruby]GoogleAuthSubを使う

GoogleカレンダーやGoogleDocの情報は、ユーザーの同意の下に他のアプリケーションからも読み書きができるようにAPIが定義されています。

で、そういう時に、「ここにユーザー名とパスワードを入れてね」という危険なやり方じゃなくて、ちゃんとユーザーの同意をとりつつ、アプリケーションにはパスワードを渡さなくていいようにするための認証APIが、GoogleAuthSubです。

実際に使っている例としては、携帯電話でGoogleカレンダーを読み書きできるGoogle Calendar Mobile Gatewayが有名です。

で、そのGoogleAuthSubをRails(というかRuby)で実現するためのgoogle_auth_sub.rbというのを作ってみました。

手元では動いていますが、いろいろ自信がないところがあるので、動いたとか動かなかったとか、このコードはまずいだろ、とか、いろいろフィードバックいただけると嬉しいです。

google_auth_sub.rb

使い方

 GoogleAuthSubについての情報は、この辺にまとまっています。



流れを見るのであれば最後のサイトを見るのがわかりやすいと思います。

ここでは、google_auth_sub.rbを使うことを前提に説明してみます。

まず、Googleの認証ページ用のURLを生成して、ユーザーさんに踏んでいただきます。

google_auth_sub.rbでは、こんな感じで生成します。

@uri = GoogleAuthSub.getURLForAuthSubRequest(

“https://www.google.com/calendar/feeds/”,

“http://www.example.com/responce”

)

最初の引数はscope、Googleのどのサービスにアクセスしたいかを示すURLです。

二つ目は、next、ユーザーさんがGoogleのサイトでアクセスを許可した後戻ってくるページのURLです。三番目以下を使いたい人は、Googleのドキュメント見てください:-)

ともあれ、こうやって生成したURLをWEBサイトに表示して、ユーザーさんに踏んでいただきます。

そうするとユーザーさんはGoogleの認証ページに飛んで、

サイト xxxx は次のサービスで使用するため Google アカウントへのアクセスをリクエストしています。
というようなメッセージで、アプリケーションがGoogleカレンダーにアクセスすることへの確認を求められます。

ユーザーさんが「アクセスを許可」を押すと、ユーザーさんは先ほどnextで指定したURLに帰ってきます。この際、URIにToken=XXXX という形でトークンがついてきます。

これは一時トークンといって、一回しか使えませんが、Googleにお願いすると、当分使えるセッショントークンと交換してもらうことができます。

single_use_token = params[:token]

gas = GoogleAuthSub.new()

token = gas.getSessionToken(single_use_token)



セッショントークンがとれたら、あとはGoogleの各種APIにアクセスすることで、情報を取り出すことができます。このさい、セッショントークンをヘッダに入れてアクセスしないといけませんが

gas = GoogleAuthSub.new()

res = gas.googleHttpGet(‘https://www.google.com/calendar/feeds/default/private/full’,token)

とすることで、その辺をラップして情報をとってくることができるようになります。

とってきた情報はGCal namespace element referenceというフォーマットで格納されていますが、ここから先はSubAuthじゃなくて各アプリケーションの処理なので、google_auth_sub.rbが面倒見るのはここまでです。

そのほか

GoogleAuthSub.new()の最初の引数で、CA 証明書ファイルのパスを指定します。nilで省略した場合には、HTTPSのサーバ証明書をチェックしなくなるので、セキュリティ上まずいような気がしますが、ライブラリとしては一応動作します。

CA 証明書ファイルを渡すと、VERIFY_PEERモードで証明書を検証します。でも、これで安全なのかどうなのかあまり分かっていないので、このあたりつっこみいただけるととても嬉しいです。

GoogleAuthSub.new()の二番目の引数にloggerを渡すと、Googleとのやりとりを見ることができます。

getAuthSubTokenInfo()をつかうと、トークンが有効かどうかを確認することができます。セッショントークンであっても、ユーザーが有効性を取り消すことができるので、時々チェックした方がいいのかも。

ちなみに一時トークンをこの関数に渡して有効性をチェックすることもできますが、チェックしたことでトークンを使い終わってしまうので、あんまり役にも立ちません:-)

[ruby]Net::HTTPで無限に302 Movedを繰り返すトラブルにあった

まとめ

 res= http.get( url.path )

って書いていると、30x系のリダイレクトでパラメータ付きのURLを指示された時にはまります。url.request_uriを使いましょう。


内容

Google Calendar APIなんかを使ってWEBサービスからデータをとってくる時、net/httpを使うことはよくあるかと思います。

とってきたデータが30x系のリダイレクトであった場合まで考慮に入れたとすると、こんなコードになりますよね?

 max_retry_count = 5

 max_retry_count.times {|retry_count|

  http = Net::HTTP.new(url.host, url.port)

  http.use_ssl = true if (443==url.port)

  http.ca_file = ‘/var/hogehoge/www.google.com.cer’

  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  http.verify_depth = 5

  res= http.get( url.path )

  

  case res

  when Net::HTTPSuccess

   break

  when Net::HTTPRedirection

   url = URI.parse(res[‘Location’])

   next

  else

   break

  end

 }

 resを使ってあれこれ。

 ところが、これでGoogleカレンダーのデータをとってこようとしたらひどい目に遭います。とりあえずとってくると…

Moved Temporarily

The document has moved https://www.google.com/xxxx?gsessionid=m6Kxxxx

想定の範囲内の302 Moved Temporarilyなので、プログラムはいわれたとおりにリトライして、データをとってきます。

Moved Temporarily

The document has moved https://www.google.com/xxxx?gsessionid=1rJxxxx

え?と思いつつ、プログラムはいわれたとおりにリトライして、データをとってきます。

Moved Temporarily

The document has moved https://www.google.com/xxxx?gsessionid=Cmvxxxx

….たらい回し状態です。いっこうに目的のデータに行き着きません。

やり方が間違っているのかなぁ、それともドキュメントに読み落としがあるかなぁ、とさんざん悩んだ末、やっと原因に気がつきました。

rubyのURIライブラリは、URIを渡すと部品に分解してくれます。host/port/path/query….

そこで、http.get( url.path )を使うと? queryとして指示されている「?gsessionid=Cmvxxxx」が吹っ飛んじゃいますよね。

Googleさんから見ると、

・”xxxx”にクライアントが来たので、”xxxx?gsessionid=….”にリダイレクトを指示しました。

・なぜかクライアントはgsessionidを外して、再び”xxxx”にアクセスしてきました

・仕方がない(というか、別クライアントに見えるので)、”xxxx?gsessionid=….”にリダイレクトを指示しました。

・なぜかクライアントはgsessionidを外して”xxxx”にアクセスしてきたので….

ということになっていたわけです。

….だって みんな“http.get( url.path )” って書くじゃーん!

 res= http.get( url.request_uri )

って書くのがいいみたいです。

MextractrAPIを使ってみた。

 文章を渡すとそこから地名や日付を抽出してくれる、Mextractr WebAPIというAPIがあります。

WEBにある文章から、そこに書かれている日付や場所、金額などが抽出できると、その内容をカレンダーに転記するとか、金額だったらうまい棒○本分に換算して表示するとか、いろいろとワクワクする展開が考えられますよね。



実は、自分も過去に同じようなことを考えて、未踏ソフトウェア創造事業に提案させていただいたことがあります。このときは、やり方にあまり斬新さがなかったので未踏的じゃない、ということでNGだったのですけど。



解析機能を提供していただけるのであれば、実現したいアプリケーションのアイデアはいくつもあるので、早速使ってみました。

使ってみた

 サンプルコードまで出していただいているので、使うのは難しくありません。

Mextractr_api_url = 'http://api.emetadata.net/mextractr?text=[[text]]&out=[[out]]&apikey=[[apikey]]'
#解析して欲しい文字列をエンコードして
text_encoded = CGI.escape( CGI.escapeHTML(text.toutf8) )
#Mextractr APIを呼び出す
uri = URI.parse( Mextractr_api_url.gsub('[[text]]',text_encoded).gsub('[[out]]','atom').gsub('[[apikey]]',@apikey) )
response = nil
Net::HTTP.start(uri.host,uri.port){|http|
response = http.post(uri.path,uri.query)
}

で、帰ってきた結果をrexmlで解析してあげればOKです。



サンプルコードでは、結果をGETでとってきていたのですけど、URLに解析対象の文字列を丸ごと含むのはきついと思うので、POSTするようにしました。試しに投げてみたら値が帰ってきているので、きっとこれでOKです(笑)



ということで、結果のxmlをハッシュに変換するところまでで一つのクラスにまとめてみました。クラスを作って、parseメソッドにテキストを渡したら結果が帰ってきます。

mextractrAPI.rb

[名前] MextractrAPI
[概要] Mextractrサービス(http://api.emetadata.net/)をrubyから使うためのラッパークラス
[作成] 2008-08-27 古川大輔(mogya at mogya.com)
[用法] http://api.emetadata.net/からAPIキーを取得しておく必要があります。
#取得したAPIキーをつかってMextractrAPIクラスを生成
api = MextractrAPI.new('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
#解析したいテキストを渡す
result = api.parse(text)
結果がどのように格納されているかは、 pp result して見てください。

Mashup Awards 4に応募しようと思っている方もそれ以外の方も、自由にお使いください(^^)

気づいたこと

複数の要素を検知したときに、重要度とか確からしさをつけて欲しい。


 文章中に日付とか場所が複数あった場合、それらは並列されて帰ってきます。

"where"=>
[{"georss:point"=>"42 -104.1", "valueString"=>"沖縄"},
{"georss:point"=>"13.15 144.5", "valueString"=>"グアム"},
{"georss:point"=>"13.15 144.5", "valueString"=>"グアム"}],
"when"=>
[{"valueString"=>"27日", "startTime"=>"2008-08-27"},
{"valueString"=>"2009年度", "startTime"=>"2009-01-01"}],

 個人情報フィルタではこれでいいのだと思いますけど、文章からメタデータを取り出す上では、どれか一個、代表値が欲しいです。

たとえば、「日時:」で始まるデータは重要度が高いとか、日付の後ろに時刻まで書いてあったら確からしさが高いとか、そういう情報を付加していただけると嬉しいです。



whenで時刻は検知してくれないの?


 今のところ、whenとして帰ってくるのは日付だけみたいです。

「来る5月23日10時から、定時株主総会を墨田区の弊社本店A会議室にて開催いたします。」と渡しても、2009/5/23までしか見ていただけません。

日付しか返さないのだったらstartTimeじゃなくてstartDateのような。



バージョンアップして時刻も対応していただけることを期待しております(^^)