ruby

2009-08-29

sinatra + html + extjs で郵便番号検索

http://moriwaki.net/cms/?p=254
として、extjs で郵便番号検索 画面を作成している例をみつけた。
(郵便番号の頭の一部や、住所の一部を入力すると、候補が表示されて その中から確定することができる。候補が多い場合は paging して表示される)

それを sinatra + haml に書き換えてみた。
Zipsearch
http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/public/zip.js?view=markup&root=ruby-xbrl

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/views/zip.haml?view=markup&root=ruby-xbrl

$ ruby web-app.rb
としてから、http://localhost:3456/ へアクセスすればよい。

こういった候補表示が簡単に出来るのを知ると、一般の仮名漢字変換や入力サジェスチョンの ユーザーインターフェースを簡単にweb アプリに取り入れられそうに感じるなぁ。

 

| | コメント (0) | トラックバック (0)

2009-08-18

prawn のサンプル (ruby での PDF 生成)

Output01
Output02

prawn をつかった ruby での PDF 生成の練習をしてみました。

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/prawn/?root=ruby-xbrl
  *.rb    // スクリプト
  *.pdf  // 生成結果

このスクリプトは ruby, jruby のどちらでも動作します。

日本語の ttf を設定しておく必要があります。
See
- http://d.hatena.ne.jp/seuzo/20080819/1219134139
> PDF生成ライブラリ「Prawn」 - 名もないテクノ手

- http://blog.s21g.com/articles/858
> Rails:prawn & prawntoで日本語のPDF生成 - satoko's blog - s21g

簡単な構造の表の生成はこれで十分だな。
財務表の形式の表を生成できるかを調査してみる予定です。

| | コメント (0) | トラックバック (0)

2009-08-16

EDINET サイトを firewatir で自動閲覧しデータダウンロードする

firewatir の使い方が分かったので、EDINET サイトから XBRL データをダウンロードする ruby スクリプトを書いてみた。
- http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/watir/edinet/edint-download.rb?view=markup&revision=93&root=ruby-xbrl
> View of /trunk/Edinet/tools/watir/edinet/edint-download.rb - ruby-XBRL - SourceForge.JP

# firefox の download 動作の設定を
#     "ダイアログを開かずに 特定フォルダ以下に保存する"
# ようにしておくことが必要。

20090816firewatir

XBRLデータは、これで完全に download することができそうだ。
PDF データもダウンロードしたかったが、こちらは まだ完全にはできていない。
PDF ダウンロード時に 別window がひらくのだが、これを close する方法が不明...
20ファイルまでは download されるのだが、それ以上は 別window がひらかなくなり、download もされない。
(エラーにはならないので、XBRL データの download は それ以降も問題な実行はされる)

これで、SEC と EDINET の 最新 XBRL データを集めることができるようになったことになる。
次は これらのファイルを簡易検索して download できるようなサービスを sinatra でつくる予定。

| | コメント (0) | トラックバック (0)

2009-08-15

watir で web サイトの自動アクセス

watir というruby のライブラリーを見つけた。

- http://wtr.rubyforge.org/
> Watir is an open-source library for automating web browsers. It allows you to write tests that are easy to read and maintain. It is simple and flexible.

firefox 用
- http://wiki.openqa.org/display/WTR/FireWatir
> FireWatir - Watir - OpenQA Wiki

jruby 用
- http://celerity.rubyforge.org/
> Celerity | Easy and fast functional test automation for web applications

google の image サーチで "micro bikini" の検索結果画像や、
yahoo image サーチで "micro bikini" の検索結果画像を
根こそぎ download する例を書いてみた。

- http://homepage2.nifty.com/youichi_kato/src.html
 
firewatir で google image 検索結果を すべてdownload する。
   
images-google.rb  (firewatir)
   celerity で yahoo image 検索結果を 1000 件ぐらいを download する。
   
images-yahoo.rb  (celerity)

| | コメント (0) | トラックバック (0)

2009-08-10

sinatra + haml + sass + jquery (その2)

20090810ciksearcher

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/?root=ruby-xbrl
を更新した。

更新内容は、submit ボタンに disabeld 制御を追加した事だ。
検索条件が空の場合にsubmit ボタンを 無効にし、そうでないときにボタンを有効にするようにした。

これは、jquery に本を買ってきて、selector や イベントに処理関数を割り当てる方法を勉強した成果である。

| | コメント (0) | トラックバック (0)

2009-08-09

sinatra + haml + sass + jquery

20090809sinatra

SEC の SIC コードの検索 web アプリを sinatra で作成している。
favicon.ico の設定、jquery の tablesorter, pager の組み込みもできるようになった。

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/?root=ruby-xbrl

つぎのいずれの方法でも起動できる。

$ jruby web-app.rb    // このあと ブラウザで http:;pcalhost:4567 にアクセス
$ ruby web-app.rb   // このあと ブラウザで http:;pcalhost:4567 にアクセス
$ rackup config.ru   // このあと ブラウザで http:;pcalhost:9292 にアクセス

この後は SIC, CIK コードの検索アプリ/Restful サービスとして完成をさせていこう。

| | コメント (0) | トラックバック (0)

2009-08-03

SIC コード検索を sinatra + haml で

20090803sicsearch

先日は SEC サイトの SIC コード全体を取得するプログラムを作ったが、
取得データの検索をする web アプリを sinatra + haml で作成してみた。
http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/?root=ruby-xbrl
(ruby web-app.rb として走らせる)

favicon.ico の表示ができるようにする、
jquery で、検索結果表のソートをできるようにする、
SIC, CIK の両方の検索を可能にする、
などの機能も追加していく予定。

| | コメント (0) | トラックバック (0)

2009-08-01

sqlitel3 から mysql5 に変更したら

sqlite3 での DB 作成時間、検索時間は使用に耐えないので、
利用する DB を sqlite3 から mydql5 に変えてみた。

 DataMapper.setup(:default, "sqlite3://#{File.dirname(File.expand_path(__FILE__))}/db.sqlite3")

    DataMapper.setup(:default, "mysql://root:root@localhost/dm_test")
に変更するだけ。
ただし、  コンソールから Database を作成しておく必要がある。
    $ mysql -u root -p
    > create database dm_test;
    > quit

しかし、走らせるといくつかエラーが発生した。

その1:
======
$ ruby XbrlDatamap.rb file init

  Incorrect date value: '-4712-01-01' for column 'period_instant'  のエラーが出た。
対処:
  Date.new => Date.today
に変更。
Date 値に対するチェックが sqlite3 は甘いみたいだ。

その2:
======
$ ruby sample00.rb -i /Users/youichikato/work/www/EDGAR/data/*/*/*-*[0-9].xml

   Data too long for column 'val' at row 1 (MysqlError) のエラーがでた。
対処:
fact の DataMap 定義
  property :val, Text
  =>
  property :val, String, :length => 4096
デフォルトの文字列の長さの扱いが sqlite3 と mysql では異なってる?

上記変更後の DB 作成は
364.710000   9.420000 374.130000 (465.242776)
約 8 分!
sqlite3 の 25 倍の速さ!

NetIncomeLoss 科目の検索も
  2.040000   0.040000   2.080000 (  2.368310)
と 2 秒程度で、sqlite3 の 8 倍の速さ!

変更したソースコードは 以下に commit 済み。
http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/datamap/?root=ruby-xbrl

つぎは、この検索機能を sinatra で web アプリに仕立てる予定。
(jruby でも動作するようにするのは、その後だな...)

| | コメント (0) | トラックバック (1)

2009-07-31

解決:libxml-ruby/datamapper は不安定?

次の2つの情報を net 上で得た。
- http://osdir.com/ml/lang.ruby.modules.libxml.devel/2008-07/msg00094.html
> Re: [ libxml-Bugs-8337 ] Opening many files causes: msg#00094 lang.ruby.modules.libxml.devel

GC.start をファイルオープン前に呼ぶと良いらしい。

- http://www.iteray.com/archives/133
> libxml-ruby problems on Mac OS X | Iteray

sudo bash
ARCHFLAGS='-arch i386' gem install -r libxml-ruby -- --build-flags --with-opt-lib=/opt/local/lib
のようにして、libxml-ruby を insutall すると良いらしい。

これらをおこなったところ、やっと SEC で zip 公開されている XBRL データの
全インスタンスをDB 格納できた。
(ただし、 901491/000110465908020683/pzza-20071230.xml.org は
mlns:pzza="http://www.pzza.com\"
=>
mlns:pzza="http://www.pzza.com"
へ書き換えをして、XML 処理時にエラーが発生しないようにした)

$ ruby sample00.rb -i /Users/youichikato/work/www/EDGAR/data/*/*/*-*[0-9].xml

"facts:    190135"
"units:    1644"
"contexts: 10574"
インスタンスファイル数:658 ファイル
DB 作成: 約3 時間30分 (12259秒)
DB (b.sqlite3) のサイズ   89MB

NetIncomeLoss の検索(925箇所): 15 秒

sqlite3 ではちょっと遅いな。
Mysql5 で試してみようと思う。

| | コメント (0) | トラックバック (4)

2009-07-29

libxml-ruby/datamapper は不安定?

大量のxml を libxml でパース、datamapper で保持しようとするとエラーになる。
しかも 同じ条件で走らせてもエラーの発生状況がまちまち...

$ ruby sample00.rb -i /Users/youichikato/work/www/EDGAR/data/*/*/[Aa]*[0-9].xml
"--- init DB"
reading 1:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000075/atg-20080331.xml
reading 2:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000130/atg-20080630.xml
reading 3:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000144/atg-20080930.xml
reading 4:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415509000014/atg-20081231.xml
reading 5:    /Users/youichikato/work/www/EDGAR/data/1004155/000114036108003746/agl-20071231.xml
reading 6:    /Users/youichikato/work/www/EDGAR/data/1091587/000104746909002416/abb-20081231.xml
reading 7:    /Users/youichikato/work/www/EDGAR/data/1310243/000114036108015489/anr-20080331.xml
reading 8:    /Users/youichikato/work/www/EDGAR/data/1310243/000131024308000026/anr-20080630.xml
reading 9:    /Users/youichikato/work/www/EDGAR/data/1310243/000131024309000008/anr-20081231.xml
reading 10:    /Users/youichikato/work/www/EDGAR/data/2969/000095012308012951/apd-20080630.xml
reading 11:    /Users/youichikato/work/www/EDGAR/data/4281/000119312507209888/aa-20070630.xml
reading 12:    /Users/youichikato/work/www/EDGAR/data/4281/000119312507233651/aa-20070930.xml
reading 13:    /Users/youichikato/work/www/EDGAR/data/4281/000119312508072455/aa-20071231.xml
reading 14:    /Users/youichikato/work/www/EDGAR/data/4281/000119312508107986/aa-20080331.xml
./parseritem.rb:22: [BUG] Segmentation fault
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9]

Abort trap
$ ruby sample00.rb -i /Users/youichikato/work/www/EDGAR/data/*/*/[Aa]*[0-9].xml
"--- init DB"
reading 1:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000075/atg-20080331.xml
reading 2:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000130/atg-20080630.xml
reading 3:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415508000144/atg-20080930.xml
reading 4:    /Users/youichikato/work/www/EDGAR/data/1004155/000100415509000014/atg-20081231.xml
reading 5:    /Users/youichikato/work/www/EDGAR/data/1004155/000114036108003746/agl-20071231.xml
reading 6:    /Users/youichikato/work/www/EDGAR/data/1091587/000104746909002416/abb-20081231.xml
reading 7:    /Users/youichikato/work/www/EDGAR/data/1310243/000114036108015489/anr-20080331.xml
reading 8:    /Users/youichikato/work/www/EDGAR/data/1310243/000131024308000026/anr-20080630.xml
reading 9:    /Users/youichikato/work/www/EDGAR/data/1310243/000131024309000008/anr-20081231.xml
./parseritem.rb:20:in `parse': undefined method `child' for -532922715:Fixnum (NoMethodError)
    from sample00.rb:53:in `read_data'
    from sample00.rb:139
    from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
    from sample00.rb:108

ruby1.9 で試そうと思ったが、ruby1.9 では dm-more のインストールでエラーが...
$ sudo gem1.9 install dm-more
... 省略...
ferret.c:297: error: ‘struct RString’ has no member named ‘len’
make: *** [ferret.o] Error 1
... 省略...

$ sudo gem1.9 install ferret
ferret.c:297: error: ‘struct RString’ has no member named ‘len’
make: *** [ferret.o] Error 1

うーん、どうする?

$ port list ruby ruby19
ruby                           @1.8.7-p174     lang/ruby
ruby19                         @1.9.1-p129     lang/ruby19
$ port list libxml2
libxml2                        @2.7.3          textproc/libxml2
そういえば、なに別のものを ports, gem でインストールしたもので、
お前がつかっている libxml2 の version は古い とかいわれたことがあったなぁ。
でも port でいれた libxml2 でなく、/usr/lib/libxml2.dylib が使われていることまでわかったことがあった。port でいれた libxml2 を リンクさせることはできず、あきらめていた。
今回の現象も 古い libxml2 が使われてしまっているせいだろうか...

| | コメント (0) | トラックバック (0)

2009-07-27

Datamapper で XBRL データ情報の一部を DB に変換

XBRL データの instans の情報の一部をDatamapper で DB 保存し、
条件検索をするプログラムを試作してみた。

ruby でのソースコード一式は以下にある。
- http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/datamap/?root=ruby-xbrl
> Index of /trunk/Edinet/tools/datamap - ruby-XBRL - SourceForge.JP

実行例を示す。
14 ファイルを処理し、それぞれインスアンの科目数の検索、"NetIncomeLoss" 科目の検索
を行ってみている。
最初は、DB 登録を行う。(時間がかかる)
2回目は作成済みDB を参照して検索をする。(時間はかからない)

$ ruby sample00.rb  -h
Usage: sample00 [options]
    -m                               use memory db
    -i                               init db

$ ruby sample00.rb -i /Users/youichikato/work/www/EDGAR/data/*/*/msft*[0-9].xml
"--- init DB"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312505193215/msft-20050630.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312506128216/msft-20060331.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312506205609/msft-20060630.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312506235137/msft-20060930.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312507039978/msft-20061231.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312507116258/msft-20070331.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312507221457/msft-20070630.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312507253878/msft-20070930.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312507259490/msft-20070930.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312508031558/msft-20071231.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml"
"reading /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml"
"instances: 14"
0: /Users/youichikato/work/www/EDGAR/data/789019/000119312505193215/msft-20050630.xml
    1119, 5, 149
1: /Users/youichikato/work/www/EDGAR/data/789019/000119312506128216/msft-20060331.xml
    779, 4, 131
2: /Users/youichikato/work/www/EDGAR/data/789019/000119312506205609/msft-20060630.xml
    1188, 5, 195
3: /Users/youichikato/work/www/EDGAR/data/789019/000119312506235137/msft-20060930.xml
    409, 4, 56
4: /Users/youichikato/work/www/EDGAR/data/789019/000119312507039978/msft-20061231.xml
    650, 4, 86
5: /Users/youichikato/work/www/EDGAR/data/789019/000119312507116258/msft-20070331.xml
    669, 4, 117
6: /Users/youichikato/work/www/EDGAR/data/789019/000119312507221457/msft-20070630.xml
    1060, 5, 111
7: /Users/youichikato/work/www/EDGAR/data/789019/000119312507253878/msft-20070930.xml
    438, 5, 62
8: /Users/youichikato/work/www/EDGAR/data/789019/000119312507259490/msft-20070930.xml
    449, 3, 77
9: /Users/youichikato/work/www/EDGAR/data/789019/000119312508031558/msft-20071231.xml
    846, 3, 135
10: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml
    849, 3, 147
11: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml
    1447, 5, 167
12: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml
    706, 5, 110
13: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml
    903, 5, 155
"facts:    11512"
"units:    60"
"contexts: 1698"
"---- Search http://xbrl.us/us-gaap/2008-03-31:NetIncomeLoss --"
"----        http://xbrl.us/us-gaap/2008-01-31:NetIncomeLoss --"
  1: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            8,168,000,000 2004-06-30
  2: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           12,254,000,000 2005-06-30
  3: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           12,599,000,000 2006-06-30
  4: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           14,065,000,000 2007-06-30
  5: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           17,681,000,000 2008-06-30
  6: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,289,000,000 2007-09-30
  7: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,707,000,000 2007-12-31
  8: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,388,000,000 2008-03-31
  9: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,297,000,000 2008-06-30
10: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,478,000,000 2006-09-30
11: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,626,000,000 2006-12-31
12: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,926,000,000 2007-03-31
13: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,035,000,000 2007-06-30
14: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,141,000,000 2005-09-30
15: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,653,000,000 2005-12-31
16: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,977,000,000 2006-03-31
17: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,828,000,000 2006-06-30
18: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml            4,289,000,000 2007-09-30
19: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml            4,373,000,000 2008-09-30
20: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            4,707,000,000 2007-12-31
21: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            4,174,000,000 2008-12-31
22: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            8,996,000,000 2007-12-31
23: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            8,547,000,000 2008-12-31
24: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml           11,030,000,000 2007-03-31
25: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml           13,384,000,000 2008-03-31
26: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml            4,926,000,000 2007-03-31
27: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml            4,388,000,000 2008-03-31
65.380000  31.090000  96.470000 (142.322403)

$ ruby sample00.rb /Users/youichikato/work/www/EDGAR/data/*/*/msft*[0-9].xml
"--- open DB"
"instances: 14"
0: /Users/youichikato/work/www/EDGAR/data/789019/000119312505193215/msft-20050630.xml
    1119, 5, 149
1: /Users/youichikato/work/www/EDGAR/data/789019/000119312506128216/msft-20060331.xml
    779, 4, 131
2: /Users/youichikato/work/www/EDGAR/data/789019/000119312506205609/msft-20060630.xml
    1188, 5, 195
3: /Users/youichikato/work/www/EDGAR/data/789019/000119312506235137/msft-20060930.xml
    409, 4, 56
4: /Users/youichikato/work/www/EDGAR/data/789019/000119312507039978/msft-20061231.xml
    650, 4, 86
5: /Users/youichikato/work/www/EDGAR/data/789019/000119312507116258/msft-20070331.xml
    669, 4, 117
6: /Users/youichikato/work/www/EDGAR/data/789019/000119312507221457/msft-20070630.xml
    1060, 5, 111
7: /Users/youichikato/work/www/EDGAR/data/789019/000119312507253878/msft-20070930.xml
    438, 5, 62
8: /Users/youichikato/work/www/EDGAR/data/789019/000119312507259490/msft-20070930.xml
    449, 3, 77
9: /Users/youichikato/work/www/EDGAR/data/789019/000119312508031558/msft-20071231.xml
    846, 3, 135
10: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml
    849, 3, 147
11: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml
    1447, 5, 167
12: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml
    706, 5, 110
13: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml
    903, 5, 155
"facts:    11512"
"units:    60"
"contexts: 1698"
"---- Search http://xbrl.us/us-gaap/2008-03-31:NetIncomeLoss --"
"----        http://xbrl.us/us-gaap/2008-01-31:NetIncomeLoss --"
  1: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            8,168,000,000 2004-06-30
  2: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           12,254,000,000 2005-06-30
  3: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           12,599,000,000 2006-06-30
  4: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           14,065,000,000 2007-06-30
  5: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml           17,681,000,000 2008-06-30
  6: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,289,000,000 2007-09-30
  7: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,707,000,000 2007-12-31
  8: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,388,000,000 2008-03-31
  9: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,297,000,000 2008-06-30
10: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,478,000,000 2006-09-30
11: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,626,000,000 2006-12-31
12: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            4,926,000,000 2007-03-31
13: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,035,000,000 2007-06-30
14: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,141,000,000 2005-09-30
15: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            3,653,000,000 2005-12-31
16: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,977,000,000 2006-03-31
17: /Users/youichikato/work/www/EDGAR/data/789019/000119312508162828/msft-20080630.xml            2,828,000,000 2006-06-30
18: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml            4,289,000,000 2007-09-30
19: /Users/youichikato/work/www/EDGAR/data/789019/000119312508215214/msft-20080930.xml            4,373,000,000 2008-09-30
20: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            4,707,000,000 2007-12-31
21: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            4,174,000,000 2008-12-31
22: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            8,996,000,000 2007-12-31
23: /Users/youichikato/work/www/EDGAR/data/789019/000119312509054006/msft-20081231.xml            8,547,000,000 2008-12-31
24: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml           11,030,000,000 2007-03-31
25: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml           13,384,000,000 2008-03-31
26: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml            4,926,000,000 2007-03-31
27: /Users/youichikato/work/www/EDGAR/data/789019/000119312508089475/msft-20080331.xml            4,388,000,000 2008-03-31
  1.930000   0.200000   2.130000 (  2.225345)
ntsitm298093:datamap youichikato$

| | コメント (0) | トラックバック (0)

2009-07-25

SEC の cik, sic の一覧

SEC で配布されている XBRL データには、cik, sic という企業のID 番号や、業種ID 番号が含まれている。
それらの番号の一覧表を、ruby の hash として取得するプログラムを作ってみた。

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/?root=ruby-xbrl
  get-cik.rb
  get-sic.rb

run させると こんな風になる。 (ruby1.9 でも動作します)
$ ruby get-sic.rb
reading... http://www.sec.gov/info/edgar/siccodes.htm
445
save to sic.yml
445
  0.240000   0.060000   0.300000 (  1.614466)
ntsitm290106:sec youichikato$ ruby get-sic.rb
reading... http://www.sec.gov/info/edgar/siccodes.htm
Data count = 445
save to sic.yml
Data count = 445
  0.240000   0.070000   0.310000 (  1.797561)

$ ruby get-cik.rb
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-123.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-a.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-b.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-c.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-d.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-e.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-f.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-g.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-h.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-i.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-j.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-k.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-l.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-m.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-n.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-o.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-p.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-q.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-r.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-s.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-t.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-uv.htm
reading... http://www.sec.gov/divisions/corpfin/organization/cfia-wxyz.htm
Data count = 11506
save to cik.yml
Data count = 11506
  4.960000   0.490000   5.450000 ( 38.125122)

| | コメント (0) | トラックバック (0)

2009-07-23

なぜか rackup から treemap へ辿りついた

- http://www.rubyinside.com/make-any-ruby-object-rack-friendly-with-rackable-2088.html
> Make any Ruby object Rack-friendly with Rackable

上の記事を読んで、rackup  の使い方を調べた。
しsて そこから いろいろ辿って、treemap と その利用アプリを知る事ができた。
Disk Inventory X は便利だ。これを web ベースで動作させたいなぁ。

- http://labs.unoh.net/2007/05/rackweb.html
> ウノウラボ Unoh Labs: RackでWebアプリのWebサーバー依存を無くす

- http://mono.kmc.gr.jp/~yhara/d/?date=20081013#p01
> [ruby][mac] Ruby treemap gem を使って、ディスク使用量を二次元グラフ化するスクリプトを書いた - Greenbear Diary (2008-10-13)

- http://www.derlien.com/
> Disk Inventory X

- http://www.cs.umd.edu/hcil/treemap-history/index.shtml
> Treemaps for space-constrained visualization of hierarchies

XBRL の Dimension 情報を Treemap 風に interective に表示/ナビゲートしている例は既にあるのだろうか?

| | コメント (0) | トラックバック (0)

2009-07-21

XBRL を datamapper で処理する試作

datamapper で XBRL データを保持する試作をしてみた。
まずは instance データ中の情報の一部だけを 変換する例をつくり、
どの程度の速度が出るかをみてみよう。

10000 項目のデータ (10 インスタンスがそれぞれ 10 コンテキスト * 100 科目を持つ) を作り、ちょっとした検索を行う。

以下が、ソースコードと実行結果例だが、速度的にはちょっと遅いかもしれない。
検索は、致命的に遅い訳ではないので、今後 実装を進めていこうと思う。

$ cat samler.rb

require 'rubygems'
require 'dm-core'
require 'dm-more'
require 'dm-types'
require 'pp'
require 'benchmark'

NUM_INSTANCE = 10
NUM_CONTEXT = 10
NUM_FACT_PER_CONTEXT = 100

class Xbrlinstance
  include DataMapper::Resource
  property :id, Integer, :serial => true

  property :fpath, String

  has n, :units
  has n, :contexts
  has n, :facts
  has n, :footnotes
end

class Unit
  include DataMapper::Resource
  property :id, Integer,  :serial => true

  belongs_to :xbrlinstance
  has n, :facts

  property :name, String
  property :measure, String
  property :numerator, String
  property :denominator, String
end

class Context
  include DataMapper::Resource
  property :id, Integer, :serial => true

  belongs_to :xbrlinstance
  has n, :facts

  property :name, String
  property :identifier, String
  property :period_startDate, Date, :default => nil
  property :period_endDate,  Date, :default => nil
  property :period_instant,  Date, :default => nil
  property :period_forever,  Boolean, :dafault => false

  property :senario, Text   # not yet impl
  property :segment, Text   # not yet impl
end

class Footnote
  include DataMapper::Resource
  property :id, Integer,  :serial => true

  belongs_to :xbrlinstance

  property :val, Text     # not yet impl
end

class Fact
  include DataMapper::Resource
  property :id, Integer,  :serial => true

  belongs_to :xbrlinstance

  property :element, String
  property :val, Text

  belongs_to :context   # @contxtRef
  belongs_to :unit      # @unitRef

  def inspect
    super.to_s +
      " contextRef='#{Context.all('id' => self.context_id)[0].name.to_s}'" +
      " unitRef='#{Unit.all('id' => self.unit_id)[0].name.to_s}'" +
      " val='#{val}'\n"
  end
end

def generate_data
  # instance を登録
  1.upto(NUM_INSTANCE) do |i|
    inst = Xbrlinstance.find_or_create(:fpath => "A-#{i}.xml")
    u1 = inst.units.build(:name => "unit-01", :measure => "measure")

    # fact を登録
    1.upto(NUM_CONTEXT) do |j|
      c = inst.contexts.build(:name => "context-#{j}", :period_instant => Date.new)

      1.upto(NUM_FACT_PER_CONTEXT) do |k|
        f = inst.facts.build(:element => "foo:bar-#{k}", :val => "#{i*10000 + j*1000 + k}")
        u1.facts << f
        c.facts << f
      end
    end
    inst.save
  end
end

def usage
  puts "usage: ruby #{__FILE__} [mem | file [init]]"
  exit(0)
end

usage if ARGV.size == 0

puts Benchmark.measure {

  while ARGV.size > 0
    opt = ARGV.shift
    if (opt == "mem")
      puts "--- using memory"
      DataMapper.setup(:default, 'sqlite3::memory:''')
      puts "--- init data"
      DataMapper.auto_migrate!
      generate_data()
    elsif (opt == "file")
      puts "--- using file"
      DataMapper.setup(:default, "sqlite3://#{   File.dirname(File.expand_path(__FILE__))}/db.sqlite3")
      opt2 = ARGV.shift
      if (opt2 == nil)
        puts "--- open exist data"
        DataMapper.auto_upgrade!
      elsif (opt2 == "init")
        puts "--- init data"
        DataMapper.auto_migrate!
        generate_data()
      else
        usage
      end
    else
      usage
    end
  end

  # pp Xbrlinstance.first('id' => '2')
  # pp Xbrlinstance.all

  pp Unit.all('name' => 'unit-01').size
  pp Unit.all('xbrlinstance.fpath' => 'A-5.xml', 'name' => 'unit-02').size

  pp Context.all.size
  pp Context.all('xbrlinstance.fpath' => 'A-5.xml').size

  pp Fact.all.size
  pp Fact.all('unit.name' => 'unit-01').size
  pp Fact.all('xbrlinstance.fpath' => 'A-5.xml').size
  pp Fact.all('xbrlinstance.fpath' => 'A-5.xml', 'context.name' => 'context-9').size
  pp Fact.all('xbrlinstance.fpath' => 'A-5.xml', 'element' => 'foo:bar-5').size
  pp Fact.all('xbrlinstance.fpath' => 'A-5.xml', 'element' => 'foo:bar-5', 'context.name' => 'context-2').size
}

# ------- 実行結果例 -----
# ruby sample.rb file init
#  ...
#  31.190000  13.210000  44.400000 ( 73.532679)
# -----------------------------------------------
# $ ruby sample.rb
# "file"
# --- using file
# --- open exist data
# 10
# 0
# 100
# 10
# 10000
# 10000
# 1000
# 100
# 10
# 1
#   1.280000   0.020000   1.300000 (  1.311618)
# -----------------------------------------------
# ruby sample.rb mem
# ...
# 24.110000   0.180000  24.290000 ( 24.516180)
# -----------------------------------------------

| | コメント (0) | トラックバック (0)

2009-07-19

SEC サイトから XBRL データを一括ダウンロード

http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/SimpleCrawler/examples/download_xbrl.rb?view=markup&revision=71&root=ruby-xbrl
として、 SEC サイトから XBRL データを一括 download する例を置いた。

http://www.sec.gov/Archives/edgar/xbrldata.tar.gz  として、March 23, 2009 時点の全データが配布されているが、
それ以降のデータは RSS から拾ってくるかなどして補充していく必要がある。
RSS からの XBRL データ download は大分 前に書いているけど。
 http://sourceforge.jp/projects/ruby-xbrl/svn/view/trunk/Edinet/tools/sec/read-rss.rb?view=markup&revision=32&root=ruby-xbrl

兎も角、SEC のデータを集める手段は確立できた。
次の作業は これを Detaouse 化することだ。
(数値情報/非数値データの検索、XML データそのものの取出し, ruby オブジェクトとしての取出し)
を web-service/RESTFull で提供できればと思っている。

EDINET サイトからも XBRLデータを 一括 download しようと思ってが、
mecahanize は javascript を処理できないらしい。
# そもそも EDINET サイトからの XBRLデータ取得が SEC サイトのように簡単
# にできるようになっていない事が問題と思うけど。

| | コメント (0) | トラックバック (0)

gibbler ! (ruby オブジェクトを git 管理)

ruby の sinatra の調査中に、
  ginatra, git-wiki
など、git との連携例を見つけた。

ginatra:  Sinatra製のGitリポジトリブラウザ
     - http://www.moongift.jp/2009/06/ginatra/
git-wiki:  git にデータ保存する wiki
     - http://atonie.org/2008/02/git-wiki

そして、gibbler なんてのも見つけた。
   - http://suke.cocolog-nifty.com/blog/2009/07/ruby-gibbler.html
   > Rubyのオブジェクトに対して git のようなハッシュと履歴の仕組みを持たせるようにするライブラリ

undo/redo 操作なんて、git で管理してしまうことが可能?
データ永続化のストレージとして、git が使える場面はそれなりに存在すると思う。

- http://delano.github.com/gibbler/ には jrubyでも動作するとかいてある。
実際にそこに記載されているサンプル2が ruby 1.8.7, jruby 1.2 での動作を確認できた。
// jruby 1,3 に update するのをわすれているのに 気がついた ...

datamapper の datastore として、git を使う事もできそうだ。
(sqlite3 を memory モードで使って、適宜 git に 保持するなんて方法でもよいかもしれない)

| | コメント (0) | トラックバック (0)

2009-06-28

web スパイダー を ruby で (その2)

- http://homepage2.nifty.com/youichi_kato/src.html
>   web スパイダー (2009-06-27)
をさらに、更新した。

リンクのネスト数での制限をできるようにした。

| | コメント (0) | トラックバック (0)

web スパイダー を ruby で

- http://homepage2.nifty.com/youichi_kato/src.html
>   web スパイダー (2009-06-27)
に ruby での web スパイダーを置いた。

gem で install できる simplecrawler を使って書いてみたのだが、
そのままでは上手く動作しないところがあったので、simplecrawler 自体にも手を入れた。
// リンク先を読むときに site_uril を base に解釈してしまっているみたい。
// リンクが書いてあるページを base に解釈するようにして、リンクのネットをキチンと
// 辿れるようにした。
// パッチ的に変更してみただけ。
// オリジナルのソースコードは単純ながら機能的に面白いので、
// 折りをみて 全面的に書き直してみたい。

この spider  と ferret を組み合わせようと思っている。
ferret をつかって会社のチーム内サーバーに設置した 全文検索は、おかげさまで上手く動作している。 4万ファイルほどに対して index を作成しているが、検索は
0.03 〜 0.4 秒ぐらいの処理時間。

社内の他マシンの html  公開されているファイルについても index 作成しようとおもって、スパイダーを書いたのだ。

| | コメント (0) | トラックバック (0)

2009-05-16

pdftohtml で日本語を

PDFをプログラムから検索できるようにしたいとおもった。
porticus で pdftohtml をインストールした。
$ pdftohtml -c -euc UTF-8 foo.pdf
としたが、
Error: Unknown character collection 'Adobe-Japan1'
とでるし、変換結果も変だった。
xpdf-japanes も install  したが、やはり同じ。
しかし、
$ cp /opt/local/var/macports/software/xpdf-japanese/2004-jul-27_0+autoactivate/opt/local/share/xpdf/xpdf-japanese/add-to-xpdfrc ~/.xpdfrc
としてから、pdftohtml を実行したら、うまくできた。

http://www.oreilly.co.jp/editors/archives/000094.html
にある 『Binary Hacks』サンプルPDF の変換結果を示そう。

オリジナル PDF を MacOS のプレビューで。
2009051601

pdftohtml 結果を firefox で。
2009051602

この html に対して、ferret + ruby で検索させようと思っている。

$ pdftohtml -help
pdftohtml version 0.40 http://pdftohtml.sourceforge.net/, based on Xpdf version 3.01
Copyright 1999-2003 Gueorgui Ovtcharov and Rainer Dorsch
Copyright 1996-2005 Glyph & Cog, LLC

Usage: pdftohtml [options] <PDF-file> [<html-file> <xml-file>]
  -f <int>          : first page to convert
  -l <int>          : last page to convert
  -q                : don't print any messages or errors
  -h                : print usage information
  -help             : print usage information
  -p                : exchange .pdf links by .html
  -c                : generate complex document
  -i                : ignore images
  -noframes         : generate no frames
  -stdout           : use standard output
  -zoom <fp>        : zoom the pdf document (default 1.5)
  -xml              : output for XML post-processing
  -hidden           : output hidden text
  -enc <string>     : output text encoding name
  -dev <string>     : output device name for Ghostscript (png16m, jpeg etc)
  -v                : print copyright and version info
  -opw <string>     : owner password (for encrypted files)
  -upw <string>     : user password (for encrypted files)

標準出力へ xml 形式で出力もできるから、 パイプ処理も可能だな。

| | コメント (0) | トラックバック (0)

2009-03-22

jruby + swing で画像閲覧(その2)

Dropuri

web ブラウザから URL (または画像) をドラッグして画像が表示できるようにもしてみた。
前のプログラムを次のように変更するだけだ。(太字部分を追加)

URI_FLAVOR = DataFlavor.new(
'application/x-java-url; class=java.net.URL')

class MyDropTargetAdapter < DropTargetAdapter
  def setZoom(zoom)
    @zoom = zoom
  end

  def drop(e)
    transfer = e.getTransferable();
    if transfer.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
      # image ファイルのパス
      e.acceptDrop(DnDConstants::ACTION_COPY_OR_MOVE)
      fileList = transfer.getTransferData(DataFlavor.javaFileListFlavor)
      icon = ImageIcon.new(fileList[0].getAbsolutePath())
      @zoom.setIcon(icon)
    elsif transfer.isDataFlavorSupported(URI_FLAVOR)
      # image ファイルの URL
      e.acceptDrop(DnDConstants::ACTION_COPY_OR_MOVE)
      uri = transfer.getTransferData(URI_FLAVOR)
      icon = ImageIcon.new(URL.new(uri.to_s))
      @zoom.setIcon(icon)

    end
  end
end

書籍  Java Swing Hacks―今日から使える驚きのGUIプログラミング集 を参考にした。

 

| | コメント (0) | トラックバック (0)

2009-03-14

EDINET 2009 のタクソノミ作成がよくわからない

- http://www.fsa.go.jp/search/20090309/tguide20090309.pdf
> 企業別タクソノミ作成ガイドライン
の 58 ページの  "8-8 ラベルの追加について" に次の文がある。

> 別記事業において一般商工業タクソノミに用意されている勘定科目を使用する場合、既存のラベルでは業法等と不整合になる場合があります。その場合、業種毎に用意されたラベルロールのうち適切なものに、新規にラベルを追加することができます(この場合でも既存のラベルを上書きすることはできないことに留意してください)。

うーん、よくわからないなぁ。
具体的にはどんな記述を ラベルリンクベースファイルに追加すればいいんだろう?

上書きというのは prohibited で priority を高くした arc を定義したうえで、
別の arc で 別定義を書くということだよな。(多分)

では、追加という場合は、prohibited な arc で既存 arc を無効にせずに、
単に 別定義を書けばよいということなのか?
そして、arcrole に既存のものとは異なる指定をして、
表示リンクでは、arcrol でどのラベルを使うかを指定させるということなのか?

http://svn.sourceforge.jp/view/trunk/Edinet/tools/misc/add-label.rb?view=markup&root=ruby-xbrl
として、リンクベースファイルに定義を追加する実験の途中コードを置いてみた。

ラベル文字列を定義する場合と、既存のレベル文字列を流用する場合の
両方を考慮しないといけないよね。
サンプルインスタンスに、ラベルの追加例があると嬉しいのだが...

| | コメント (0) | トラックバック (0)

2009-03-12

ruby でEDINETの2009年のXBRLサンプルデータをCSV化

昨年 、2008 年の EDINET の XBRLサンプルデータを CSV 化するプログラムを作成した。
今日は、これをもとに 2009 年版のサンプルデータを CSV化するプログラムを作った。
こんな感じの CSV が一瞬で得られる。
Edinet2009samplecsv
プログラム
- http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/?root=ruby-xbrl
プログラムが参照するデータ
- http://svn.sourceforge.jp/view/trunk/Edinet/lib/edinet-2009-03-01/?root=ruby-xbrl
EDINET のサンプルデータ
- http://svn.sourceforge.jp/view/trunk/Edinet/sample2009-03-09/?root=ruby-xbrl

このプログラムは、インスタンスデータのみを解析する。
タクソノミー情報は、EDINETサイト ( http://www.fsa.go.jp/search/20090309.html ) で提供されている 勘定科目リスト (タクソノミー設計書) の情報を流用して利用している。

| | コメント (0) | トラックバック (0)

2009-03-09

ruby で DSL (その2)

http://svn.sourceforge.jp/view/trunk/Edinet/tools/parser/?root=ruby-xbrl
に parser_schema.rb を commit した。

この中では、メイン処理は次のようになっている。

  xml = '../data/td-net/081220090203088072/tdnet-qcedjpfr-33500-2008-11-30-01-2009-02-20.xsd'

  xsd = Xsd.new.parse(xml)

  if xsd.has_element?('ResearchAndDevelopmentExpense_Div_OperatingRevenue_OneYearDelta')
    pp xsd.ResearchAndDevelopmentExpense_Div_OperatingRevenue_OneYearDelta
  end

ここで指定している xsd ファイルでは、1つの element だけが定義されている。
そして このプログラムからの出力は、こんなふうになる。

#<Element:0x07fe66c
@id=
  :"tdnet-qcedjpfr-33500_ReversalOfAccumulatedImpairmentLossOnLeasedAssetsOpeCF",
@name=:ReversalOfAccumulatedImpairmentLossOnLeasedAssetsOpeCF,
@nillable=:true,
@periodtype=:duration,
@substitutiongroup=:"xbrli:item",
@type=:"xbrli:monetaryItemType">

つまり、このプログラムでは、
xsd ファイルを処理して、あるインスタンスを得るのだが、
そのインスタンスは、
 1. xsd ファイル中で定義された element 名を名前に持つ method が定義されている。
   2. その method の返値は、 Element というクラスのインスタンスであり、
  該当する element の属性を保持している。
のである。

いまは、タクソノミーの import 処理をしていないし、
各種リンクベースも処理していない。
でも、これらを クラスの多重継承、複合として処理し、
かつ、XBRL インスタンスファイルの対しても行うなら、
XBRL文書から ruby クラス/インスタンスを生成できたことになる。

| | コメント (0) | トラックバック (0)

2009-03-08

ruby で DSL

- http://www.infoq.com/jp/articles/properties-metaprogramming
> InfoQ: メタプログラミングを使ってRubyにプロパティを追加する

この記事では、最終的なコードは示されていないが、
次の記事では、(最終的な実装  + アルファ機能) のコード例が示されている。

- http://electricsheep.blogspot.com/2007/04/metaprogramming-and-dsl-resources.html
> dreaming of electric sheep: Metaprogramming and DSL Resources?

property の設定変更のリスナー設定だけでなく、設定できる値の範囲設定をしている。素晴らしい!

ruby, ruby1.9, jruby いずれでも動作する。

property  foo に対して add_foo_listener() をよび出さなかった場合に
実行時にエラーが発生したので、次のように修正をした。

      def fire_event_for(sym, arg)
        @listener ||= { }       // 追加
        @listener[sym] ||= [] // 追加
        @listener[sym].each { |l| l.call(arg) }
      end

もっとよい修正法がある気もするが。
(追加したコードが definemethod("add_#{sym_listener)
  にあるものと重複してしまっているので...)

http://svn.sourceforge.jp/view/trunk/Edinet/tools/dsl/?root=ruby-xbrl
に 変更を加えたソースコードを commit  してある。

| | コメント (0) | トラックバック (0)

2009-03-07

libxml-ruby-0.9.9 March 5, 2009

libxml-ruby-0.9.9      March 5, 2009

libxml-ruby がアップデートされていた。
- http://rubyforge.org/projects/libxml
> RubyForge: LibXML: Project Info

gem で早速、update した。
0.9.8 で動作していたプログラムは、すこし試した限りでは、特に問題なく動作しているようだ。
gem update が失敗していた。 現在、調査中...

調査結果:

- http://rubyforge.org/forum/forum.php?thread_id=31975&forum_id=2129
という投稿に従って install できた。
rake test すると 1 つエラーが起きてるけど。
自作プログラムが動作することも確認できた。しばらくすれば、正式に gem で update できるようになるだろう。

// 0.9.x -> 0.9.8 では、このプログラムが動作しなくなってしまって、ちょっと驚いた事があったので。

http://libxml.rubyforge.org/rdoc/index.html
にあるベンチマークも走らせてみた。
   hpricot (0.6.164)
   libxml-ruby (0.9.9)
   nokogiri (1.2.1)

Ruby version: MRI 1.8.7 (2008-08-11 rev 72), platform: i686-darwin9
                 user     system      total        real
rexml       9.110000   0.580000   9.690000 (  9.753665)
hpricot     2.040000   0.030000   2.070000 (  2.096508)
libxml       0.470000   0.020000   0.490000 (  0.497450)
nokogiri   0.430000   0.010000   0.440000 (  0.453979)

Ruby version: MRI 1.9.1 (2009-01-30 rev 0), platform: i386-darwin9
               user     system      total        real
rexml     11.110000   0.110000  11.220000 ( 11.248732)
libxml       0.460000   0.020000   0.480000 (  0.478099)
nokogiri   0.410000   0.010000   0.420000 (  0.418684)

Ruby version: JRuby 1.8.6 (2008-12-20 rev 114) [i386-jruby1.1.6], platform: Java, version 1.6.0_07
                                               user     system      total        real
rexml                                     5.558000   0.000000   5.558000 (  5.558404)
hpricot                                   2.080000   0.000000   2.080000 (  2.079561)
jdom_document_builder   0.417000   0.000000   0.417000 (  0.416432)

jruby -J-server ./xml_benchmarks.rb
Ruby version: JRuby 1.8.6 (2008-12-20 rev 114) [i386-jruby1.1.6], platform: Java, version 1.6.0_07
                                               user     system      total        real
rexml                                     5.396000   0.000000   5.396000 (  5.395802)
hpricot                                   2.054000   0.000000   2.054000 (  2.053571)
jdom_document_builder   0.422000   0.000000   0.422000 (  0.421661)

| | コメント (0) | トラックバック (0)

2009-02-28

ruby-xbrl に commit

業務がいそがしくて、全然 作業がすすんでいないが、
ruby-xbrl をすこしだけ update した。

http://svn.sourceforge.jp/view/trunk/Edinet/?root=ruby-xbrl  に
  - XBRLデータのスキャン (import を辿る) の実験コード。
  - RSpec の使い方の例
を cimmit。

RSpec の使い方の例 は、
  - http://japan.zdnet.com/blog/yoshimi/2008/02/22/entry_27016449/
  > RSpecチュートリアルやってみる - 吉見和也(Kazuya Yoshimi) - ZDNet Japan
を読んで、自分でもやってみた結果だ。

| | コメント (0) | トラックバック (0)

2009-01-26

XBRL ファイルの リンクをたどる (その2)

前に XBRL インスタンスデータから ファイル import 関係ツリーを作ることをした。
今度は、ファイル自体をローカルファイルに保存し、2回目からは ネット経由しないでデータアクセスするようなプログラムを作ってみた。

ソースコードは後で示すとして、実行例をまず 示そう。

$ ruby filescan.rb
"3740c8e0eedfad9f135dd69f9296f709"
"readed  100 files. 17439149 bytes."
  3.910000   2.450000   6.360000 (149.429177)

$ ruby filescan.rb
"3740c8e0eedfad9f135dd69f9296f709"
"readed  100 files. 17439149 bytes."
  1.190000   0.060000   1.250000 (  1.255830)

これは microsoft データを読んでみたものだ。
読み込みデータ全体の MD5 値、ファイル数, byte 総数と、benchmark 値を表示している。
2 回目の実行は、1.2 秒だ!

ソースを示そう。(150 行ほどある)

$ cat filescan.rb

# 2009-01-25 katoy
#  Scaw and cache all importd file for XBRL-instnce data.

require 'rubygems'
require 'xml/libxml'
require 'open-uri'
require 'pathname'
require 'pp'
require 'benchmark'
require 'md5'
require 'set'

class FileCache
  CACHE = './cache'   # Cache Folder. 末尾に / は付けない。

  attr_reader :md5
  attr_reader :count
  attr_reader :size
  attr_reader :readed

  def initialize
    @md5 = MD5.new
    @count = 0
    @size = 0
    @readed = Set.new
  end

  def visit(path, dir = '')
    from_path = Utils::normalize_path(path, dir)
    return if @readed.include?(from_path)

    parent = Utils::parent(from_path)
    data = ""

    if from_path.index('http:/') != 0
      cache_file = from_path
      open(cache_file) { |src| data = src.read }
    else
      # ネット上ファイルの場合
      cache_file = CACHE + from_path.gsub('http:/', '/http')   
      # キャッシュから読み込む
      if File.exists?(cache_file)
        open(cache_file) { |src| data = src.read }
      else
        # 読み込んでから、キャッシュにも保存する
        Utils::multi_mkdir(File::dirname(cache_file))  if !File.exists?(File::dirname(cache_file))
        open(from_path) { |src|
          data = src.read
          open(cache_file, "w") { |dest| dest.write(data) }
        }
      end
    end

    @readed << from_path
    @md5.update(data)
    @count += 1
    @size += data.length

    reader = nil

    begin
      reader = XML::Reader.new(data)
      while 1 == reader.read
        href = nil
        name = "#{reader.namespace_uri}:#{reader.local_name}"
        case name
        when 'http://www.w3.org/2001/XMLSchema:import'
          href = reader['schemaLocation']
        when 'http://www.xbrl.org/2003/linkbase:schemaRef'
          href = reader['xlink:href']
        when 'http://www.xbrl.org/2003/linkbase:linkbaseRef'
          href = reader['xlink:href']
        end

        if href != nil
          to_path = Utils::normalize_path(href, parent)
          visit(to_path, parent)
        end
      end
    rescue
      puts "---- error reading #{from_path} (#{cache_file})"
      exit 1
    ensure
      reader.close if reader != nil
      reader = nil
    end
  end
end

class Utils
  def self.cleanpath(path)
    Pathname.new(path).cleanpath
  end

  def self.parent(full_path)
    p = full_path.rindex('/') - 1
    full_path[0..p]
  end

  def self.multi_mkdir(mkpath)
    path = ''
    mkpath.split('/').each do |f|
      path.concat(f)
      Dir.mkdir(path) unless path == '' || File.exist?(path)
      path.concat('/')
    end
  end

  def self.normalize_path(path, dir='')
    if path.index('http://') == 0
      uri = URI.parse(path)
      full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
    elsif dir.index('http://') == 0
      uri = URI.parse("#{dir}/#{path}")
      full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
    else
      path = path[5.. path.length] if path.index('file:') == 0
      dir = File::expand_path('.') if dir == ''
      full_path = Utils::cleanpath(File::expand_path(path, dir))
    end

    full_path.to_s
  end
end

# See http://www.sec.gov
# SEC - adobe
# pat = "http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml"
# SEC - microsoft
pat = "http://www.sec.gov/Archives/edgar/data/789019/000119312508215214/msft-20080930.xml"

# pat = "file:/Users/youichikato/work/www/xbrl.org/XBRL-CONF/Common/instance/397-00-ConsistentInstance-valid.xbrl"
# pat = "/Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl"
# pat = "/Users/youichikato/NetBeansProjects/ruby-xbrl/Edinet/sample/**/*.xbrl"
# pat = "data/X99001-000/jpfr-asr-X99001-000-2008-03-31-01-2008-06-27.xbrl"
# pat = ARGV[0]

puts Benchmark.measure {
  fscan = FileCache.new

  if pat == nil
    puts "--No specified XBRL instance."
    exit 1
  elsif pat.index('http:') == 0
    full_path = Utils::normalize_path(pat)
    fscan.visit(full_path)
  else
    Dir.glob(pat).each do |f|
      full_path = Utils::normalize_path(f)
      fscan.visit(full_path)
    end
  end

  pp fscan.md5.hexdigest
  pp "readed  #{fscan.count} files. #{fscan.size} bytes."
}

ファイル読み込みが、この程度の処理時間で済むなら、どの言語で処理しても人間にとっては大差は無いかもしれない。

| | コメント (0) | トラックバック (0)

2009-01-12

XBRL ファイルの リンクをたどる

libxml-ruby を使って、XBRL データを読み込む事にした。
(REXML は動作が遅いらしいので)

以前に、XBRLインスタンスファイルを指定すると、
そこからの参照ファイルの Tree 状態をコンソールに表示するものを作った。

今回、REXML -> libxml に書き換えてみた。
(実は 以前のプログラムでは、参照をすべて辿れていなかったことも気がついた)

$ cat filetree.rb

# 2009-01-10 katoy
#   Show imported tree list for XBRL-instnce data.

require 'rubygems'
require 'xml/libxml'
require 'open-uri'
require 'pathname'
require 'pp'
require 'yaml'
require 'benchmark'

class FileTree
  YAML_NAME = 'links.yaml'

  attr_reader :links    # from_url => [to_url, ...] のハッシュ

  def initialize
    @links = { }
#   if File.exist?(YAML_NAME)
#     @links = YAML.load_file(YAML_NAME)
#   end
  end

#  def save_links
#    @links.reject!{ |k, v| k.index("http") != 0 }
#    File.open( YAML_NAME, 'w' ) do |f|
#      YAML.dump( @links, f )
#    end
#  end

  def generate(path, dir = '')
    from_path = Utils::normalize_path(path, dir)
    parent = Utils::parent(from_path)

    if @links[from_path] != nil
      puts "------- skip #{from_path}"
      return
    end

    reader = nil

    begin
      reader = XML::Reader.file(from_path)
      while 1 == reader.read
        href = nil
        name = "#{reader.namespace_uri}:#{reader.local_name}"
        case name
        when 'http://www.w3.org/2001/XMLSchema:import'
          href = reader['schemaLocation']
        when 'http://www.xbrl.org/2003/linkbase:schemaRef'
          href = reader['xlink:href']
        when 'http://www.xbrl.org/2003/linkbase:linkbaseRef'
          href = reader['xlink:href']
        end

        if href != nil
          to_path = Utils::normalize_path(href, parent)
          generate(to_path, parent)
          @links[from_path] = [] if @links[from_path] == nil
          @links[from_path] << to_path
        end
      end
    rescue
      puts "---- error reading #{to_path}"
    ensure
      reader.close if reader != nil
    end
  end

  def print_tree(path, indent=0)

    return if @links[path] == nil
    @links[path].each do |f|
      puts "#{' ' * (4*indent)}#{f}"
      print_tree(f, indent + 1)
    end
  end

end

class Utils
  def self.cleanpath(path)
    Pathname.new(path).to_s
  end

  def self.parent(full_path)
    p = full_path.rindex('/') - 1
    full_path[0..p]
  end

  def self.normalize_path(path, dir='')

    if path.index('http://') == 0
      uri = URI.parse(path)
      full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
    elsif dir.index('http://') == 0
      uri = URI.parse("#{dir}/#{path}")
      full_path = "#{uri.scheme}://#{uri.host}#{Utils::cleanpath(uri.path.to_s)}"
    else
      path = path[5.. path.length] if path.index('file:') == 0
      dir = File::expand_path('.') if dir == ''
      full_path = Utils::cleanpath(File::expand_path(path, dir))
    end

    full_path
  end
end

# See http://www.sec.gov
# SEC - adobe
# pat = "http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml"
# SEC - microsoft
# pat = "http://www.sec.gov/Archives/edgar/data/789019/000119312508215214/msft-20080930.xml"

# pat = "397-00-ConsistentInstance-valid.xbrl"
# pat = "file:/Users/youichikato/work/www/xbrl.org/XBRL-CONF/Common/instance/397-00-ConsistentInstance-valid.xbrl"
# pat = "/Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl"
# pat = "/Users/youichikato/NetBeansProjects/ruby-xbrl/Edinet/sample/**/*.xbrl"
pat = ARGV[0]

  puts Benchmark.measure {
  ftree = FileTree.new

  if pat.index('http:') == 0
    full_path = Utils::normalize_path(pat)
    ftree.generate(full_path)
    ftree.print_tree(full_path)
  else
    Dir.glob(pat).each do |f|
      full_path = Utils::normalize_path(f)
      ftree.generate(full_path)
      ftree.print_tree(full_path)
    end
  end
#  ftree.save_links
}

$ ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
     ... 省略
http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829.xsd
    http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_pre.xml
    http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_cal.xml
    http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_def.xml
    http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080829_lab.xml
     ... 省略
                            http://www.xbrl.org/2003/xl-2003-12-31.xsd
                                http://www.xbrl.org/2003/xlink-2003-12-31.xsd
                            http://www.xbrl.org/2003/xlink-2003-12-31.xsd
  4.520000   0.900000   5.420000 (371.585711)

最後の行は benchmak ライブラリーのよるものだ。ここでは 370 秒かかったことがしめされている。

参照ファイルの数(重複も)が多いことが分かる。( Tree 表示の行数は3000 行ほどになる)
また、この処理時間の遅さは、ほとんど ネットアクセスによるものとおもわれる。
(一度 読み込みをしたファイルは、再度 読み込まない工夫はしてあるが)

実用的なものにするには、次のような工夫をすることが考えられる。
  1. ネット経由でアクセスしたファイルは、キャッシュフォルダーの保持し、次回からはキャッシュしたファイルへアクセスする。
     (ブラウザのキャッシュフォルダと同じ考え)
  2. ネット上ファイルの内容は基本的に変化しないはずなので、 ファイル上ファイルについての 解析した参照情報は
     永続化保持して、再利用するようにする。

ここでは、2 番の方法を実装してみよう。

つまり @links を yaml で保持する。
ソースコード中の
   initialize の変更と
   ファイルの最後の save 呼び出し
を追加するだけだ。
# 実は上で示したコードでは、その部分はコメントにしてあった。
この変更を加えると 一回目の実行は遅いが、2回目はもちろん 劇的に速くなる。

$  ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
     ... 省略
  4.810000   0.950000   5.760000 (382.223170)
$  ruby filetree.rb http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml
     ... 省略
  0.050000   0.020000   0.070000 (  0.109030)

ここでは、File からの import 情報だけを永続化して再利用したが、
XBRL の他の情報も同様の方法で永続化することも可能だろう。

XBRL データの処理の関門はいくつかある。
1. ファイルの参照を辿らないと完全な情報を得ることができない。
2. xlink, Xpointer を処理する必要がある。

でも、すべての情報を得る必要がないケースもある。
( (科目ID, コンテキスト、値) の組を得るだけなら、インスタンスファイルだけの処理で十分だ)

#インスアンスファイルを libxml をつかって処理する例は、作成済み。
# 数日中に 別途 投稿する予定である。

ファイル参照をすべて辿るのは、基本的には上に示した方法 (130 行程度) で可能なことがわかった。
次は、Xlinkm Xpointer をつかっている 各種のリンクベースファイルを処理することを行おう。

| | コメント (0) | トラックバック (0)

2009-01-03

郵便番号と緯度経度情報

google や yahoo には 郵便番号/緯度経度情報/住所 の変換サービスがある。
でも 書籍 "ビジュアライジング・データ —Processingによる情報視覚化手法" 中にある例の
  郵便番号の分布を地図上に配置してみる
といった用途には、これらのサービスは使えない。
(何万という件数のデータ変換を web サービス経由で行うのは無理)

郵便番号データ、住所と緯度経度データは以下にある。

- http://www.post.japanpost.jp/zipcode/download.html
> 郵便番号データダウンロード - 日本郵便

- http://nlftp.mlit.go.jp/isj/
> 位置参照情報ダウンロードサービス

これらを合体させたデータをつくろうと思ったが、簡単にはできない。orz...
俺が半日かけて行ったことを示そう。
1.  郵便番号 (全国) を download し、UTF8 に変換 (zipcode.csv)
2. 位置参照情報 (47 都道府県全部) を download し、UTF8 に変換( *_2007.csv)

2 の作業では、download できるデータは zip である。download は手動でおこなったが、それを解凍、文字コード変換するのは次の shell script を使った。

# ! /bin/sh
rm -r ../*.csv
rm -f *.csv

for i in *.zip
do
  unzip -o $i
  for j in *.csv
  do
    nkf -w $j > 1.txt
    mv -f 1.txt ../$j
  done
  rm -f *.csv *.xml
done

さて、これらのデータに次の操作をしようとした。
3. zipcode.csv に 緯度経度情報を追加する。
4. *_2007.csv に 郵便番号情報を追加する。

3 を行うための実験として次の ruby スクリプトを作ってみた。
そして、わかったのは 単純な突き合わせでは、郵便番号 に 緯度経度を追加することができないと
いうこと。

実験 ruby script と その出力結果の一部を示す。

$ cat addgeo.rb
$KCODE = 'utf8'

require 'rubygems'
require 'fastercsv'
require 'pp'

KEN_TO_GEOCSV = {
  "北海道" => '01_2007.csv',
  "青森県" => '02_2007.csv',
  "岩手県" => '03_2007.csv',
  "宮城県" => '04_2007.csv',
  "秋田県" => '05_2007.csv',
  "山形県" => '06_2007.csv',
  "福島県" => '07_2007.csv',
  "茨城県" => '08_2007.csv',
  "栃木県" => '09_2007.csv',
  "群馬県" => '10_2007.csv',
  "埼玉県" => '11_2007.csv',
  "千葉県" => '12_2007.csv',
  "東京都" => '13_2007.csv',
  "神奈川県" => '14_2007.csv',
  "新潟県" => '15_2007.csv',
  "富山県" => '16_2007.csv',
  "石川県" => '17_2007.csv',
  "福井県" => '18_2007.csv',
  "山梨県" => '19_2007.csv',
  "長野県" => '20_2007.csv',
  "岐阜県" => '21_2007.csv',
  "静岡県" => '22_2007.csv',
  "愛知県" => '23_2007.csv',
  "三重県" => '24_2007.csv',
  "滋賀県" => '25_2007.csv',
  "京都府" => '26_2007.csv',
  "大阪府" => '27_2007.csv',
  "兵庫県" => '28_2007.csv',
  "奈良県" => '29_2007.csv',
  "和歌山県" => '30_2007.csv',
  "鳥取県" => '31_2007.csv',
  "島根県" => '32_2007.csv',
  "岡山県" => '33_2007.csv',
  "広島県" => '34_2007.csv',
  "山口県" => '35_2007.csv',
  "徳島県" => '36_2007.csv',
  "香川県" => '37_2007.csv',
  "愛媛県" => '38_2007.csv',
  "高知県" => '39_2007.csv',
  "福岡県" => '40_2007.csv',
  "佐賀県" => '41_2007.csv',
  "長崎県" => '42_2007.csv',
  "熊本県" => '43_2007.csv',
  "大分県" => '44_2007.csv',
  "宮崎県" => '45_2007.csv',
  "鹿児島県" => '46_2007.csv',
  "沖縄県" => '47_2007.csv',
}

def read_gio_csv(file)

  puts "read ... #{file}"

  gios = []
  open(file) { |f|
    while l = f.gets
      gios << l
    end
  }
  puts "end."

  gios
end

current_gio = ''
gios = []

FasterCSV.foreach("zipcode.csv") do |row|

  gio_file = KEN_TO_GEOCSV[row[6]]
  if current_gio != gio_file
    gios = read_gio_csv(gio_file)
    current_gio =gio_file
  end

  address = '"' + row[7] + '","' + row[8]
  pattern = Regexp.new(address)

  find = ''
  gios.each do |g|
    if pattern =~ g
      find = g
      break;
    end
  end

  if find == ''
    puts "--------------- not found " + ( row * ",")
  else
    # puts find
  end
#  puts row * ","
end

走らせてみる。
$ ruby addgeo.rb >1.txt

1 時間経過しても終わらないので、別端末で進み具合いなどをしらべてみる。
$ wc -l zipcode.cvs
  122535 zipcode.csv

$ head 1.txt
ntsitm157187:gio-data youichikato$ head 1.txt
read ... 01_2007.csv
end.
--------------- not found 01101,060  ,0600000,ホッカイドウ,サッポロシチュウオウク,イカニケイサイガナイバアイ,北海道,札幌市中央区,以下に掲載がない場合,0,0,0,0,0,0
--------------- not found 01101,060  ,0600042,ホッカイドウ,サッポロシチュウオウク,オオドオリニシ(1-19チョウメ),北海道,札幌市中央区,大通西(1〜19丁目),1,0,1,0,0,0
--------------- not found 01101,064  ,0640820,ホッカイドウ,サッポロシチュウオウク,オオドオリニシ(20-28チョウメ),北海道,札幌市中央区,大通西(20〜28丁目),1,0,1,0,0,0
--------------- not found 01101,060  ,0600001,ホッカイドウ,サッポロシチュウオウク,キタ1ジョウニシ(1-19チョウメ),北海道,札幌市中央区,北一条西(1〜19丁目),1,0,1,0,0,0
--------------- not found 01101,064  ,0640821,ホッカイドウ,サッポロシチュウオウク,キタ1ジョウニシ(20-28チョウメ),北海道,札幌市中央区,北一条西(20〜28丁目),1,0,1,0,0,0
--------------- not found 01101,060  ,0600002,ホッカイドウ,サッポロシチュウオウク,キタ2ジョウニシ(1-19チョウメ),北海道,札幌市中央区,北二条西(1〜19丁目),1,0,1,0,0,0
--------------- not found 01101,064  ,0640822,ホッカイドウ,サッポロシチュウオウク,キタ2ジョウニシ(20-28チョウメ),北海道,札幌市中央区,北二条西(20〜28丁目),1,0,1,0,0,0
--------------- not found 01101,060  ,0600003,ホッカイドウ,サッポロシチュウオウク,キタ3ジョウニシ(1-19チョウメ),北海道,札幌市中央区,北三条西(1〜19丁目),1,0,1,0,0,0

上のようなパターン以外にも
--------------- not found 01102,002  ,0028091,ホッカイドウ,サッポロシキタク,ミナミアイノサト,北海道,札幌市北区,南あいの里,0,0,1,0,0,0
なんて、郵便番号データにある住所がヒットしていないケースもある!

$ grep read 1.txt
read ... 01_2007.csv
read ... 02_2007.csv
read ... 03_2007.csv
read ... 04_2007.csv
read ... 05_2007.csv
read ... 06_2007.csv
read ... 07_2007.csv

$ wc -l 1.txt
   11271 1.txt
7 ファイル分を処理した段階で、 1 万以上のマッチしなかったパターンがある!

"以下に掲載がない場合" と "xx 丁目ーxx丁目" パターンの対処をして、どこまで減らせるかなぁ...

| | コメント (0) | トラックバック (2)

2009-01-01

GanttProject のデータを ruby で パースしてみた

HappyMapper をつかって GanttProject のデータファイル(xml形式) をパースして、csv に変換させてみた。
出力書式を csv 形式にしたが、プログラム上では ファイル内容は ruby の object に変換できているので、各種の出力形式に変形させるのは簡単だ。
# xfy/xvcd で ganttproject データを表示/編集できるようにしたいとも思っている。

この作業を通じて、GanttProject の XML データ構造もほぼ 理解できたし、HappyMapper の内部もだいぶ 理解できた。
# freemind のデータや XBRL のインスタンスデータも、 happymapper で十分に扱えそうだ。
# parse した結果の Object を to_yaml して、memcached や、RDB に保持すると面白いかもしれない。

以下にソースコードと、実行例を示す。
# niffty の homepage にもファイル群を upload してある。
# See - http://homepage2.nifty.com/youichi_kato/src.html
#          ganttproject データの解析(2009-01-01)

$ cat ganttproject.rb

# 2009-01-02 katoy
# ganttproject の xml データを happymapper で解釈する。
#     happymapper (0.1.2)
#    ganttroject 2.0.9
#
# happymapper には、次のパッチを当てる
#   - root エレメントを解釈できるようにする。 (:deep の扱い)
#   - 再帰構造を解釈できるようにする          (:deep の扱い)
#
# *** happymapper-0.1.2-org/lib/happymapper.rb2009-01-01 16:29:34.000000000 +0900
# --- happymapper-0.1.2/lib/happymapper.rb2009-01-01 16:49:23.000000000 +0900
# ***************
# *** 62,67 ****
# --- 62,68 ----
#   def parse(xml, o={})
#         options = {
#           :single => false,
# +         :deep => true,
#           :use_default_namespace => false,
#     }.merge(o)
#         
# ***************
# *** 73,79 ****
#          node.register_default_namespace(namespace.chop)
#     node.find("#{ namespace}#{get_tag_name}")
#         else
# !         doc.find(get_tag_name)
#         end
#         
#         collection = create_collection(nodes, namespace)
# --- 74,84 ----
#           node.register_default_namespace(namespace.chop)
#   node.find("#{ namespace}#{ get_tag_name}")
#         else
# !         if options[:deep]
# !           doc.find("//#{  get_tag_name}")
# !         else
# !           doc.find("./#{  get_tag_name}")
# !         end
#         end
#

require 'rubygems'
require 'happymapper'
require 'pp'

$KCODE = 'utf8'

module GANTT_PROJECT

  class Field
    include HappyMapper

    tag "field"
    attribute :id, String
    attribute :name, String
    attribute :width, Integer
    attribute :order, Integer
  end

  class View
    include HappyMapper

    tag  "view"
    attribute :id, String
    element :field, Field
  end

  class DayType
    include HappyMapper

    tag  "day-type"
    attribute :id, String
  end

  class Calendar
    include HappyMapper

    tag  "calendar"
    attribute :id, String
    attribute :name, String
#    has_one :default_week, DefaultWeek
#    has_one :overriden_day_types, overrideDayTypes
#    has_one :days, Days
  end

  class DayTypes
    include HappyMapper

    tag  "day-types"
    has_many :dayType, DayType
    has_one :calendar, Calendar
  end

  class DateStr
    include HappyMapper

    tag  "date"
    attribute :year, String
    attribute :month, String
    attribute :date, String
  end

  class Calendars
    include HappyMapper

    tag  "calendars"
    has_many :date, DateStr
    has_one :day_types, DayTypes
  end

  class Depend
    include HappyMapper

    tag  "depend"
    attribute :id, String
    attribute :type, String
    attribute :difference, String
    attribute :hardness, String
  end

  class Task
    include HappyMapper

    tag  "task"
    attribute :id, String
    attribute :name, String
    attribute :color, String
    attribute :meeting, String
    attribute :start, Date
    attribute :duration, Integer
    attribute :complete, Integer
    attribute :priority, Integer
    attribute :expand, String
    has_many :task, Task, :deep => false
    has_many :depend, Depend

    def print(nest = 0)
      puts "#{'    ' * nest}id=#{id} name=[#{name}]"

      self.task.each do |t|
        t.print(nest + 1)
      end
    end

  end

  class TaskProperty
    include HappyMapper

    tag  "taskproperty"
    attribute :id, String
    attribute :name, String
    attribute :type, String
    attribute :valuetype, String
  end

  class Tasks
    include HappyMapper

    tag  "tasks"
    attribute :color, String
    has_many :taskproperties, TaskProperty
    has_many :task, Task, :deep => false
  end

  class Resource
    include HappyMapper

    tag  "resource"
    attribute :id, String
    attribute :name, String
    attribute :function, String
    attribute :contacts, String
    attribute :phone, String
  end

  class Resources
    include HappyMapper

    tag  "resources"
    has_many :resource, Resource
  end

  class Allocation
    include HappyMapper

    tag  "allocation"
    attribute :task_id, String, :tag=>"task-id"
    attribute :resource_id, String, :tag=>"resource-id"
    attribute :function, String
    attribute :responsible, String
    attribute :load, String
  end

  class Allocations
    include HappyMapper

    tag  "allocations"
    has_many :allocation, Allocation
  end

  class Role
    include HappyMapper

    tag  "role"
    attribute :id, String
    attribute :name, String
  end

  class Roles
    include HappyMapper

    tag  "roles"
    attribute :rolesetName, String
    has_many :roles, Role
  end

  class Project
    include HappyMapper

    tag  "project"
    attribute :name, String
    attribute :company, String
    has_many :view, View
    has_one :description, String
    has_one :calendars, Calendars
    has_one :tasks, Tasks
    has_one :allocations, Allocations
    has_one :resources, Resources
  end

end

if __FILE__ == $0

  GANT_FILE = 'g.xml'
  file_contents = File.read(GANT_FILE)

  proj = GANTT_PROJECT::Project.parse(file_contents, :single => true)

  proj.tasks.task.each do |t|
    t.print
  end

  pp proj.view
  pp proj.calendars
  pp proj.allocations
  pp proj.resources
end

$ gant2csv.rb

# 2009-01-02 katoy
# ganttproject のファイルから、csv 形式を得る。

require "ganttproject"

# resource.id -> name の Hash
@person_name = { }

def make_person_name_info (res)
  res.resource.each do |r|
    @person_name[r.id] = r.name
  end
end

def search_persons(allocs, id)
  p_names = []
  allocs.allocation.each do |a|
    if id == a.task_id
      p_names << @person_name[a.resource_id]
    end
  end
  p_names
end

def csv_task (task, allocs, nest = 1)
  # finish = start + task.duration
  start = task.start.strftime("%y/%m/%d")
  peoples = search_persons(allocs, task.id)

  puts "#{task.id},\"#{' ' * nest}#{task.name}\",#{start},#{task.duration},#{task.complete},\"\",\"#{peoples}\",\"\","
  task.task.each do |t|
    csv_task(t, allocs, nest + 1)
  end
end

GANT_FILE = 'g.xml'
file_contents = File.read(GANT_FILE)
proj = GANTT_PROJECT::Project.parse(file_contents, :single => true)

make_person_name_info(proj.resources)

puts "ID,名前,開始日,期間,進捗,ウェブリンク,リソース,メモ,"
proj.tasks.task.each do |t|
  csv_task(t, proj.allocations)
end

puts

puts "ID,名前,e-mail,電話番号,役割,"
proj.resources.resource.each do |r|
  puts "#{r.id},\"#{r.name}\",\"#{r.contacts}\",\"#{r.phone}\",\"#{r.function}\","
end

$ cat g.xml   // ganttproject の download ファイル中にあるサンプルデータを整形、改名したもの

<?xml version="1.0" encoding="UTF-8"?>
<project name="My house building project" company="Myself LLC"
    webLink="www.myselfllc.net" view-date="2006-01-01" view-index="0"
    gantt-divider-location="369" resource-divider-location="322" version="2.0">
    <description>xxx</description>
    <view zooming-state="default:6" id="gantt-chart" />
    <view id="resource-table">
        <field id="0" name="名前" width="54" order="0" />
        <field id="1" name="役割" width="45" order="0" />
    </view>
    <!-- -->
    <calendars>
        <day-types>
            <day-type id="0" />
            <day-type id="1" />
            <calendar id="1" name="default">
                <default-week sun="1" mon="0" tue="0" wed="0" thu="0"
                    fri="0" sat="1" />
                <overriden-day-types />
                <days />
            </calendar>
        </day-types>
        <date year="2006" month="2" date="14" />
    </calendars>
    <tasks color="#99ccff">
        <taskproperties>
            <taskproperty id="tpd0" name="type" type="default"
                valuetype="icon" />
            <taskproperty id="tpd1" name="priority" type="default"
                valuetype="icon" />
            <taskproperty id="tpd2" name="info" type="default"
                valuetype="icon" />
            <taskproperty id="tpd3" name="name" type="default"
                valuetype="text" />
            <taskproperty id="tpd4" name="begindate" type="default"
                valuetype="date" />
            <taskproperty id="tpd5" name="enddate" type="default"
                valuetype="date" />
            <taskproperty id="tpd6" name="duration" type="default"
                valuetype="int" />
            <taskproperty id="tpd7" name="completion" type="default"
                valuetype="int" />
            <taskproperty id="tpd8" name="coordinator" type="default"
                valuetype="text" />
            <taskproperty id="tpd9" name="predecessorsr" type="default"
                valuetype="text" />
        </taskproperties>
        <task id="0" name="Architectural design" color="#99ccff" meeting="false"
            start="2006-01-09" duration="26" complete="75" priority="1" expand="true">
            <task id="9" name="Create draft of architecture" color="#99ccff"
                meeting="false" start="2006-01-09" duration="10" complete="100"
                priority="1" expand="true">
                <depend id="10" type="2" difference="0" hardness="Strong" />
                <depend id="12" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="10" name="Prepare construction documents" color="#99ccff"
                meeting="false" start="2006-01-23" duration="15" complete="65"
                priority="1" expand="true">
                <depend id="17" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="17" name="Agreement on architectural plan " color="#000000"
                meeting="true" start="2006-02-13" duration="1" complete="0"
                priority="1" expand="true">
                <depend id="1" type="2" difference="0" hardness="Strong" />
            </task>
        </task>
        <task id="11" name="Interior design" color="#99ccff" meeting="false"
            start="2006-01-23" duration="10" complete="33" priority="1" expand="true">
            <depend id="6" type="2" difference="0" hardness="Strong" />
            <task id="12" name="Pre-design" color="#99ccff" meeting="false"
                start="2006-01-23" duration="5" complete="100" priority="1" expand="true">
                <depend id="13" type="2" difference="0" hardness="Strong" />
                <depend id="14" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="13" name="Furniture selection" color="#99ccff"
                meeting="false" start="2006-01-30" duration="5" complete="0"
                priority="1" expand="true" />
            <task id="14" name="Equipment planning" color="#99ccff" meeting="false"
                start="2006-01-30" duration="5" complete="0" priority="1" expand="true">
                <notes><![CDATA[Embedded devices, kitchen, washing machine, dryer etc]]></notes>
            </task>
        </task>
        <task id="7" name="Construction phase" color="#99ccff" meeting="false"
            start="2006-02-15" duration="76" complete="0" priority="1" expand="true">
            <depend id="20" type="2" difference="0" hardness="Strong" />
            <task id="1" name="Foundation building" color="#99ccff" meeting="false"
                start="2006-02-15" duration="15" complete="0" priority="1" expand="false">
                <depend id="2" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="2" name="Ground Floor building" color="#99ccff"
                meeting="false" start="2006-03-08" duration="20" complete="0"
                priority="1" expand="false">
                <depend id="4" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="4" name="First Floor building" color="#99ccff"
                meeting="false" start="2006-04-05" duration="20" complete="0"
                priority="1" expand="false">
                <depend id="5" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="5" name="Roof" color="#99ccff" meeting="false" start="2006-05-03"
                duration="10" complete="0" priority="1" expand="false">
                <depend id="18" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="16" name="Connect to communications" color="#99ccff"
                meeting="false" start="2006-05-18" duration="10" complete="0"
                priority="1" expand="true" />
            <task id="18" name="Construction completed " color="#000000"
                meeting="true" start="2006-05-17" duration="1" complete="0"
                priority="1" expand="true">
                <depend id="6" type="2" difference="0" hardness="Strong" />
                <depend id="16" type="2" difference="0" hardness="Strong" />
            </task>
        </task>
        <task id="8" name="Decoration phase" color="#99ccff" meeting="false"
            start="2006-05-18" duration="11" complete="0" priority="1" expand="true">
            <task id="6" name="Walls" color="#99ccff" meeting="false" start="2006-05-18"
                duration="5" complete="0" priority="1" expand="false">
                <depend id="15" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="15" name="Furniture" color="#99ccff" meeting="false"
                start="2006-05-25" duration="3" complete="0" priority="1" expand="true">
                <depend id="20" type="2" difference="0" hardness="Strong" />
            </task>
            <task id="20" name="Bring your family here" color="#000000"
                meeting="true" start="2006-06-01" duration="1" complete="0"
                priority="1" expand="true" />
        </task>
    </tasks>
    <resources>
        <resource id="1" name="Jack House" function="Default:1"
            contacts="jack.house@myselfllc.net" phone="0044 077345456" />
        <resource id="0" name="John Black" function="4"
            contacts="john.black@myselfllc.net" phone="+44 0794353567" />
        <resource id="2" name="Michelangelo" function="0"
            contacts="mickelangelo@heaven.net" phone="078 9059056" />
        <resource id="3" name="Tom White" function="1"
            contacts="tom.white@myselfllc.net" phone="07978978978" />
        <resource id="4" name="Peter Green" function="1"
            contacts="peter.green@myselfllc.net" phone="0797897856" />
        <resource id="5" name="George Brown" function="1"
            contacts="george.brown@myselfllc.net" phone="07967766447" />
        <resource id="6" name="John Silver" function="2"
            contacts="john.silver@myselfllc.net" phone="07778967889" />
    </resources>
    <allocations>
        <allocation task-id="9" resource-id="1" function="Default:1"
            responsible="false" load="50.0" />
        <allocation task-id="9" resource-id="2" function="0"
            responsible="true" load="100.0" />
        <allocation task-id="10" resource-id="2" function="0"
            responsible="true" load="100.0" />
        <allocation task-id="12" resource-id="1" function="Default:1"
            responsible="true" load="100.0" />
        <allocation task-id="12" resource-id="2" function="0"
            responsible="false" load="50.0" />
        <allocation task-id="13" resource-id="1" function="Default:1"
            responsible="true" load="100.0" />
        <allocation task-id="14" resource-id="1" function="Default:1"
            responsible="true" load="50.0" />
        <allocation task-id="14" resource-id="2" function="0"
            responsible="false" load="50.0" />
        <allocation task-id="1" resource-id="0" function="4"
            responsible="false" load="100.0" />
        <allocation task-id="1" resource-id="5" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="1" resource-id="3" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="1" resource-id="6" function="2"
            responsible="true" load="100.0" />
        <allocation task-id="2" resource-id="5" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="2" resource-id="3" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="2" resource-id="4" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="2" resource-id="6" function="2"
            responsible="true" load="100.0" />
        <allocation task-id="4" resource-id="5" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="4" resource-id="3" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="4" resource-id="4" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="4" resource-id="6" function="2"
            responsible="true" load="100.0" />
        <allocation task-id="5" resource-id="3" function="5"
            responsible="false" load="100.0" />
        <allocation task-id="5" resource-id="6" function="2"
            responsible="true" load="100.0" />
        <allocation task-id="16" resource-id="0" function="4"
            responsible="true" load="100.0" />
        <allocation task-id="16" resource-id="4" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="6" resource-id="5" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="6" resource-id="3" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="6" resource-id="4" function="1"
            responsible="false" load="100.0" />
        <allocation task-id="6" resource-id="6" function="2"
            responsible="true" load="100.0" />
        <allocation task-id="15" resource-id="1" function="Default:1"
            responsible="true" load="100.0" />
        <allocation task-id="15" resource-id="3" function="1"
            responsible="false" load="100.0" />
    </allocations>
    <vacations />
    <taskdisplaycolumns>
        <displaycolumn property-id="tpd3" order="0" width="117" />
        <displaycolumn property-id="tpd4" order="1" width="92" />
        <displaycolumn property-id="tpd5" order="2" width="90" />
    </taskdisplaycolumns>
    <previous />
    <roles roleset-name="Default" />
    <roles>
        <role id="0" name="Architect" />
        <role id="1" name="Bricklayer" />
        <role id="2" name="Foreman" />
        <role id="3" name="Decorator" />
        <role id="4" name="Excavator operator" />
        <role id="5" name="Roofer" />
    </roles>
</project>

$ ruby gant2csv
ID,名前,開始日,期間,進捗,ウェブリンク,リソース,メモ,
0," Architectural design",06/01/09,26,75,"","","",
9,"  Create draft of architecture",06/01/09,10,100,"","Jack HouseMichelangelo","",
10,"  Prepare construction documents",06/01/23,15,65,"","Michelangelo","",
17,"  Agreement on architectural plan ",06/02/13,1,0,"","","",
11," Interior design",06/01/23,10,33,"","","",
12,"  Pre-design",06/01/23,5,100,"","Jack HouseMichelangelo","",
13,"  Furniture selection",06/01/30,5,0,"","Jack House","",
14,"  Equipment planning",06/01/30,5,0,"","Jack HouseMichelangelo","",
7," Construction phase",06/02/15,76,0,"","","",
1,"  Foundation building",06/02/15,15,0,"","John BlackGeorge BrownTom WhiteJohn Silver","",
2,"  Ground Floor building",06/03/08,20,0,"","George BrownTom WhitePeter GreenJohn Silver","",
4,"  First Floor building",06/04/05,20,0,"","George BrownTom WhitePeter GreenJohn Silver","",
5,"  Roof",06/05/03,10,0,"","Tom WhiteJohn Silver","",
16,"  Connect to communications",06/05/18,10,0,"","John BlackPeter Green","",
18,"  Construction completed ",06/05/17,1,0,"","","",
8," Decoration phase",06/05/18,11,0,"","","",
6,"  Walls",06/05/18,5,0,"","George BrownTom WhitePeter GreenJohn Silver","",
15,"  Furniture",06/05/25,3,0,"","Jack HouseTom White","",
20,"  Bring your family here",06/06/01,1,0,"","","",

ID,名前,e-mail,電話番号,役割,
1,"Jack House","jack.house@myselfllc.net","0044 077345456","Default:1",
0,"John Black","john.black@myselfllc.net","+44 0794353567","4",
2,"Michelangelo","mickelangelo@heaven.net","078 9059056","0",
3,"Tom White","tom.white@myselfllc.net","07978978978","1",
4,"Peter Green","peter.green@myselfllc.net","0797897856","1",
5,"George Brown","george.brown@myselfllc.net","07967766447","1",
6,"John Silver","john.silver@myselfllc.net","07778967889","2",

| | コメント (0) | トラックバック (1)

2008-12-31

郵便番号を tokyo-tyrant にいれてみた

郵便番号を tokyo-tyrant にいれてみた

郵便番号データをそのまま tokyo-tyrant にいれ、取り出してみた。
次の順でコードを示そう。
1. 郵便番号の格納 (ruby で)
2. ruby, jruby で 値をとりだす。

1. 郵便番号の格納 (ruby で)
=====================
   - http://www.tuyudaku.net/sqlite/import.html
  > SQLite - CSVファイルのインポート
を参考にして utf8 にした csv ファイルを取り出す。
そのあと、 ruby で行単位に tokyo-tyrant に登録する

$ cat tt-zip.rb
Start server:
#   sudo /usr/local/sbin/ttserver start

$KCODE = 'utf8'

require 'rubygems'
require 'fastercsv'
require 'pp'

require 'tokyotyrant'
include TokyoTyrant

# create the object
rdb = RDB::new

# connect to the server
if !rdb.open("127.0.0.1", 1978)
  ecode = rdb.ecode
  STDERR.printf("open error: %s\n", rdb.errmsg(ecode))
end

CSV_FILE = 'zipcode.csv'
FasterCSV.foreach(CSV_FILE) do |row|
  r = rdb.put(row[2], row * ',')
  if !r
    ecode = rdb.ecode
    STDERR.printf("put error: %s\n", rdb.errmsg(ecode))
  end
end

# close the connection
if !rdb.close
  ecode = rdb.ecode
  STDERR.printf("close error: %s\n", rdb.errmsg(ecode))
end

2. ruby, jruby で 値をとりだす。
======================
$ cat query-zip.rb

# Start server:
#   sudo /usr/local/sbin/ttserver start // サーバー起動
#   ruby tt-zip.ruby  // データ登録 (一度だけ実行すれば良い)
#
# ruby quert-zip 1000000   // 郵便番号 1000000 のデータ
# ruby quert-zip 100000.   // 正規表現での検索

$KCODE = 'utf8'

require 'rubygems'

require 'tokyotyrant'
include TokyoTyrant

# key 集合を得る。(得た情報は DB に保存して再利用する)
def get_keys rdb
  if rdb["_keys"] != nil
    keys = YAML::load(rdb["_keys"])
  else
    keys = []

    rdb.iterinit
    while key = rdb.iternext
      keys << key
    end
    rdb["_keys"] = keys.to_yaml
  end

  keys
end

# key 集合の項目を削除する
def clear_keys rdb
  rdb.out("_keys")
end

# create the object
rdb = RDB::new

# connect to the server
if !rdb.open("127.0.0.1", 1978)
  ecode = rdb.ecode
  STDERR.printf("open error: %s\n", rdb.errmsg(ecode))
end

# hash-like usage
key = ARGV[0]

if rdb[key] != nil
  puts rdb[key]
else
  # 指定された番号のデータがなければ、正規表現に解釈して検索する
  keys = get_keys(rdb)
  r = Regexp.new(ARGV[0])
  keys.each do |k|
    puts "#{k}:#{rdb[k]}\n" if r =~ k
  end
end

# close the connection
if !rdb.close
  ecode = rdb.ecode
  STDERR.printf("close error: %s\n", rdb.errmsg(ecode))
end

$ time ruby query-zip.rb  1000000
13101,100  ,1000000,トウキョウト,チヨダク,イカニケイサイガナイバアイ,東京都,千代田区,以下に掲載がない場合,0,0,0,0,0,0

real    0m0.058s
user    0m0.039s
sys    0m0.017s

$ time jruby query-zip.rb  1000000
13101,100  ,1000000,トウキョウト,チヨダク,イカニケイサイガナイバアイ,東京都,千代田区,以下に掲載がない場合,0,0,0,0,0,0

real    0m1.291s
user    0m1.123s
sys    0m0.125s

指定した key に相当する データがみつからなかった場合は、key を 正規表現として解釈して、検索するようにも作ってある。

$ time ruby query-zip.rb  111111
3111111:08201,31111,3111111,イバラキケン,ミトシ,コイズミチョウ,茨城県,水戸市,小泉町,0,0,0,0,0,0
5111111:24205,51111,5111111,ミエケン,クワナシ,ナガシマチョウコジマ,三重県,桑名市,長島町小島,0,0,0,0,0,0
8111111:40137,81111,8111111,フクオカケン,フクオカシサワラク,ワキヤマ,福岡県,福岡市早良区,脇山,0,0,1,0,0,0

$ time ruby query-zip.rb  100000[1-2]
1000002:13101,100  ,1000002,トウキョウト,チヨダク,コウキョガイエン,東京都,千代田区,皇居外苑,0,0,0,0,0,0
1000001:13101,100  ,1000001,トウキョウト,チヨダク,チヨダ,東京都,千代田区,千代田,0,0,0,0,0,0

real    0m0.453s
user    0m0.397s
sys    0m0.035s

# tokyo-tyrant には、リカバリー/レプリケーション機能もある。
#  - http://alpha.mixi.co.jp/blog/?p=147
#  > mixi Engineers’ Blog » Tokyo TyrantによるHAハッシュDBサーバの構築

イントラネットでの高速なデータベース処理には、memcachedb や tokyo-tyrant は十分に使えそうだ。

ATOK プラグインのバックで走らせる DB としても 十分に 利用できそうだ。

| | コメント (0) | トラックバック (0)

2008-12-30

memcachd を試す

郵便番号を memcached にいれてみた。

郵便番号データをそのまま memcached にいれ、ruby と java で 取り出してみた。
次の順でコードを示そう。
1. 郵便番号の格納 (ruby で)
2. ruby, jruby で 値をとりだす。
3. java で値をとりだす。

1. 郵便番号の格納 (ruby で)
=====================
  - http://www.tuyudaku.net/sqlite/import.html
  > SQLite - CSVファイルのインポート
を参考にして utf8 にした csv ファイルを取り出す。
そのあと、 ruby で行単位に memcached に登録する。

$ cat mem-zip.rb
See http://firewing.wordpress.com/2007/04/20/%E3%83%A1%E3%83%A2-memcached-ruby/

$KCODE = 'utf8'

require 'rubygems'
require 'fastercsv'
require 'memcache'

cache = MemCache::new 'localhost:11211'

CSV_FILE = 'zipcode.csv'
FasterCSV.foreach(CSV_FILE) do |row|
  cache[row[2]] = row.to_s
end

別端末で、$ memcached として、サーバーを走らせる。
そのあと、ruby mem-zip.rb とする。

2. ruby, jruby で 値をとりだす。
======================
$ cat query-zip.rb

$KCODE = 'utf8'

require 'rubygems'
require 'memcache'
require 'pp'

cache = MemCache::new 'localhost:11211'

pp cache.stats()

p cache[ARGV[0]]

ruby と jruby のどちらでも動作する。

$ time ruby query-zip.rb  1000000
{"localhost:11211"=>
  {"get_hits"=>14,
   "bytes"=>21574359,
   "rusage_system"=>2.791334,
   "pid"=>36202,
   "connection_structures"=>7,
   "threads"=>4,
   "limit_maxbytes"=>67108864,
   "evictions"=>0,
   "pointer_size"=>32,
   "time"=>1230610177,
   "version"=>"1.2.6",
   "bytes_written"=>989977,
   "total_items"=>122581,
   "cmd_get"=>15,
   "total_connections"=>24,
   "curr_connections"=>6,
   "uptime"=>4865,
   "cmd_set"=>122581,
   "rusage_user"=>1.36321,
   "bytes_read"=>18076476,
   "get_misses"=>1,
   "curr_items"=>118618}}
"13101100  1000000トウキョウトチヨダクイカニケイサイガナイバアイ東京都千代田区以下に掲載がない場合000000"

real    0m0.752s
user    0m0.176s
sys    0m0.052s

$ time jruby query-zip.rb  1000000
{"localhost:11211"=>
  {"pid"=>36202,
   "uptime"=>4900,
   "time"=>1230610212,
   "version"=>"1.2.6",
   "pointer_size"=>32,
   "rusage_user"=>1.363866,
   "rusage_system"=>2.792644,
   "curr_items"=>118618,
   "total_items"=>122581,
   "bytes"=>21574359,
   "curr_connections"=>6,
   "total_connections"=>25,
   "connection_structures"=>7,
   "cmd_get"=>16,
   "cmd_set"=>122581,
   "get_hits"=>15,
   "get_misses"=>1,
   "evictions"=>0,
   "bytes_read"=>18076496,
   "bytes_written"=>990665,
   "limit_maxbytes"=>67108864,
   "threads"=>4}}
"13101100  1000000トウキョウトチヨダクイカニケイサイガナイバアイ東京都千代田区以下に掲載がない場合000000"

real    0m4.293s
user    0m2.031s
sys    0m0.231s

3. java で値をとりだす。
=================

$ cat JQuery.java

// See http://www.nilab.info/zurazure2/000455.html
// See http://d.hatena.ne.jp/bubbles/20081029/1225291896

import java.util.*;
import com.danga.MemCached.*;

public class JQuery {

    public static void main(String[] args){

    // SockIOPool を初期化
    String[] serverlist = { "localhost:11211" };
    SockIOPool pool = SockIOPool.getInstance();
    pool.setServers(serverlist);
    pool.initialize();

    // memcached からオブジェクトを取り出す
    MemCachedClient mc = new MemCachedClient();
    mc.setPrimitiveAsString( true );
    mc.setSanitizeKeys( false );

    System.out.println(mc);
    String value  = (String)mc.get(key);
    System.out.println(key);
    System.out.println(value);
    }

}

$ cat run.sh
#! /bin/sh

# javac -cp java_memcached-release_2.0.1.jar JQuery.java
java -cp .:java_memcached-release_2.0.1.jar JQuery $*

$ ruby mem-zip.rb
ntsitm384184:memcached youichikato$ time ./run.sh 1000000
com.danga.MemCached.MemCachedClient Tue Dec 30 13:28:03 JST 2008 - ++++ retrieving object and stuffing into a string.
1000000
"�13101100  1000000トウキョウトチヨダクイカニケイサイガナイバアイ東京都千代田区以下に掲載がない場合000000

real    0m0.207s
user    0m0.142s
sys    0m0.054s

value 表示で先頭がすこし文字化けしている。
これは、ruby でmemcashed にいれるときに
   # cache[row[2]] = row.to_s
  cache.set(row[2], row.to_s, 0, true)
として、マーシャリングの制御をしてやると解消する。

以上の実験では、memcached にいれるデータ粒度、構造などはあまり適切でないが、
12 万件というデータ容量の処理は問題なくこなせていることがわかった。

| | コメント (0) | トラックバック (0)

2008-12-24

ruby で web-service API を利用してみる(その2)

- http://soogle.ddo.jp/calapi/
> 料理名をデータとして渡すと、それに対するカロリーとその合計を返してくれるwebserviceです。
というサービスを見つけた。
これを ruby で呼び出す例も作ってみた。

cat calapi.rb

# See http://soogle.ddo.jp/calapi/
#    料理名をデータとして渡すと、それに対するカロリーとその合計を返してくれるwebserviceです。

require 'open-uri'
require 'rexml/document'
require 'pp'

$KCODE = "utf8"

def search_station (key)
  result = []

  url = "http://soogle.ddo.jp/calapi/api?name=" + URI.escape(key)
  open(url) do |http|
    res = http.read
    doc = REXML::Document.new res
    doc.elements.each('/result/kcalInfo/item') do |elm|

      result << {
        :menuName => elm.elements['menuName'].text.to_s,
        :kcalSmall => elm.elements['kcalSmall'].text.to_s,
        :kcalMiddle => elm.elements['kcalMiddle'].text.to_s,
        :kcalLarge => elm.elements['kcalLarge'].text.to_s,
        :kcalMega => elm.elements['kcalMega'].text.to_s }

    end
  end

  result
end

result = search_station '牛丼'
pp result

$ ruby calapi.rb
[{:kcalMega=>"1244.4",
  :menuName=>"牛丼",
  :kcalSmall=>"544",
  :kcalMiddle=>"714",
  :kcalLarge=>"1020"},
{:kcalMega=>"1152.9",
  :menuName=>"牛丼",
  :kcalSmall=>"504",
  :kcalMiddle=>"661.5",
  :kcalLarge=>"945"}]

| | コメント (0) | トラックバック (0)

2008-12-21

ruby で web-service API を利用してみる

- http://homepage2.nifty.com/youichi_kato/src.html
> webapi 呼び出し例 (2008-12-21)
として、yahoo 検索、駅名検索、関連語検索、wikipedia 検索 の web-srvice API の呼び出し
をruby で行ってみた例を置いた。

どれも ほとんど同じコードの繰り返しだ。
汎用的に各種の web-service 呼び出しを利用する仕組みが作れるはずと思う。
// SOAP みたいに複雑にすることなく...

web-service API は次のページを参考にした。
- http://kanamehackday.blog17.fc2.com/blog-entry-89.html
> 日本のAPI一覧表 - Kaname's hackday

- http://www.qunea.com/blog/log/20080729-1740.html
> API一覧(Machup Awards 4)が見づらいのでテキスト一覧 - クネアシ

- http://d.hatena.ne.jp/samori/20081204/1228318558
> Webサービス APIまとめ - PCラボ 〜 興味の赴くままに 〜

| | コメント (0) | トラックバック (0)

2008-12-14

ruby で opengl

ruby で opengl もあつかえることを知った。

- http://d.hatena.ne.jp/verus/20080225/1203880010
> RubyでOpenGL - verus diary

- http://doruby.kbmj.com/akio0911_on_rails/20081009/ruby-opengl_3D_
> ruby-openglでお手軽3Dプログラミング

gem install ruby-opengl するだけで準備 OK なんて、素敵すぎる!

| | コメント (0) | トラックバック (0)

ruby で gui (ドラッグ & ドロップ)

ruby で gui (ドラッグ & ドロップ)
ファイル などを ドラッグ & ドロップできる GUI を つかったツールをつくろうと思っている。
ruby/tk の tkdnd を使おうと思っていたが、 mac ではサポートされていないようだ。

そこで、wxruby を調査した。すると
http://wxruby.rubyforge.org/svn/trunk/wxruby2/samples/dragdrop/dragdrop.rb
として、wxruby での ドラッグ & ドロップの例が見つかった。
これは、 Desktop にあるプログラムアイコンを ドロップした結果だ。
Wxrubydrop
ドロップしたファイルパスを取得できている。
# 上のプログラムでの [clipborad] での [copy image] のボタンはうまく動作していないようだが。

http://www.harukaze.net/~haruka/wxpython/wxpy17.html
には wxpython での ドラッグ & ドロップの解説がある。

# ゆくゆくは、ATOK ダイレクトプラグイン として、起動させた ruby プログラムで GUI 画面を出し、候補の表示/閲覧/選択 UI も ruby  でカスタマイズしてしまうことを考えている。

wxruby では、次のプログラム

equire 'rubygems'
require 'wx'

class MyApp < Wx::App
  def on_init
    frame = Wx::Frame.new( nil , -1 , "MiniApp" )
    tgrid = Wx::Grid.new(frame, -1)
    tgrid.create_grid( 19 , 16 )
    frame.show()
  end
end

MyApp.new().main_loop()

だけで、こんな画面も表示できる。

Wxrubygrid

| | コメント (0) | トラックバック (0)

2008-12-06

druby の利用を考えてみよう。

最近は クラウドが話題らしい。
なら、ruby には druby が既にあるじゃないか!

druby や rinda を ATOK ダイレクト プラグインで利用すると、何か面白いものができそうな気がしてきた。

辞書の共有は簡単にできそうだ。
また、 プラグイン自体を サーバーで管理して、クライアント側で オンデマンドで download するなんてことも可能だろう。

| | コメント (0) | トラックバック (0)

ruby で freemind <-> text 相互変換の実験

ruby で text から freemind ファイルを生成したり、逆に freemind ファイルからテキストを生成したりさせてみた。
# freemind 自体には、 text 出力や text の張りつけ機能があるが、ruby での扱い方ができるようにしておけば、別のプログラムと組み合わせて使う事に利用できるだろう。

テキストの形式としては、TAB でのインデント付けでの node 菜(UTF8) とした。
test.txt として、入力データを用意した。
プログラムは2つを用意した。
text2mm.rb : text -> freemind 変換
mm2text.rb: frremind. -> text 変換
ソースコードは後で示すとして、実行例を先に示そう。

$ cat test.txt
新規ノード

        B
            B1
            B2
            B3-<&>"|'
    C
        C1
            C1-1
    D

$ ruby text2mm.rb test.txt
out.mm に結果が出力される。

$ cat out.mm
<map version='0.9.0'>
  <node MODIFIED='1228559840186' TEXT='新規ノード' ID='IDX_1' CREATED='1228559840186'>
    <node MODIFIED='1228559840186' TEXT='' ID='IDX_2' CREATED='1228559840186'>
      <node MODIFIED='1228559840186' TEXT='B' ID='IDX_3' CREATED='1228559840186'>
        <node MODIFIED='1228559840187' TEXT='B1' ID='IDX_4' CREATED='1228559840187'/>
        <node MODIFIED='1228559840187' TEXT='B2' ID='IDX_5' CREATED='1228559840187'/>
        <node MODIFIED='1228559840187' TEXT='B3-&lt;&amp;&gt;&quot;|&apos;' ID='IDX_6' CREATED='1228559840187'/>
      </node>
    </node>
    <node MODIFIED='1228559840187' TEXT='C' ID='IDX_7' CREATED='1228559840187'>
      <node MODIFIED='1228559840188' TEXT='C1' ID='IDX_8' CREATED='1228559840188'>
        <node MODIFIED='1228559840188' TEXT='C1-1' ID='IDX_9' CREATED='1228559840188'/>
      </node>
    </node>
    <node MODIFIED='1228559840188' TEXT='D' ID='IDX_10' CREATED='1228559840188'/>
  </node>
</map>

freemind で out.mm を読み込んだときのスクリーンショット
Freemind00
$ ruby mm2text.rb out.mm
新規ノード

        B
            B1
            B2
            B3-<&>"|&apos;
    C
        C1
            C1-1
    D

コードを示す。
$ cat text2mm.rb

# TAB でインデント付けした Text ファイルから Freemind ファイルを作成する。
#  2008-12-05 katoy

require 'rubygems'
require 'rexml/document'
require 'cgi'
require 'pp'

$KCODE="UTF-8"

class Mindmap
  attr_reader :current

  def initialize
    @id_prefix = 'IDX'
    @nextId = 1
    @doc = REXML::Document.new
    map = REXML::Element.new('map')
    map.add_attribute 'version', '0.9.0'
    @doc.add_element map
    @current = map
  end

  def open(text = "")
    node = REXML::Element.new('node')
    id = "#{@id_prefix}_#{@nextId}"
    @nextId += 1

    s = CGI.escapeHTML(text)
    s = s.gsub('&amp;', '&')
    s = s.gsub('&lt;', '<')
    s = s.gsub('&gt;', '>')
    s = s.gsub('&quot;', '"')
    s = s.gsub('&apos;', '\'')
    time = (Time.new.to_f * 1000).to_i.to_s
    node.add_attributes( { 'ID' => id, 'TEXT' => s,
                           'CREATED' => time, 'MODIFIED' => time } )

    @current.add_element node unless @current.nil?
    @current = node
  end

  def close
    @current = @current.parent
  end

  def write_to(xml_path)
    out = File.new(xml_path, File::CREAT|File::TRUNC|File::RDWR)
    begin
      @doc.write(out, 2)
    ensure
      out.close
    end
  end

end

file = ARGV.shift

map = Mindmap.new()

prev_indent = -1

open(file) { |f|
  while line = f.gets   
    next if line.chomp.size == 0 # 空行はスキップ

    /(\t*)(.*)(\r*\n*)/ =~ line
    indent = $1.size  # 先頭のタブ数
    (prev_indent - indent + 1).times { map.close }
    (indent - prev_indent - 1).times { map.open }
    map.open($2)
    prev_indent = indent
  end
}
(prev_indent + 1).times { map.close }
raise "assert" unless prev_indent != 0
raise "assert" unless map.current.name == "map"

map.write_to "out.mm"

$ cat mm2text.rb

# See http://www.bytemycode.com/snippets/snippet/504/

# Freemind データのノード名を TAB で indent して出力する。
# 2008-12-05 katoy

require 'rubygems'

require 'rexml/document'
require 'cgi'
require 'pp'

$KCODE="UTF-8"

def out_nodes(elem, indent = 0)
  type = elem.node_type
  if type != :text and type != :comment

    text = elem.attributes.get_attribute 'TEXT'
    if text != nil
      puts "\t" * (indent - 1) + CGI.unescapeHTML(text.to_s)
    end
    elem.each do |c|
      out_nodes(c, indent + 1)
    end
  end
end

f = ARGV.shift
doc = REXML::Document.new File.open(f)

out_nodes doc.root

これらは 特別な ライブラリーは使っていないので jruby でも動作する。

| | コメント (0) | トラックバック (0)

2008-11-24

xml を別の xml 形式に変換するには?(その2)

汎用的なものはみつけられず。
ともかく、XBRL インスタンスの書き換え専用の ruby スクリプトを書いてみた。

$ cat toSimpleXbrl.rb
# 2008-11-23 katoy
# XBRL インスタンスの 科目タグを書き換える。
#   <foo:bar ...> <==> <item prefix='foo' local_name='bar' ...>
#

require 'rubygems'
require "rexml/document"
require 'open-uri'
require 'pp'

#----------------------
class XBRL
  def self.to_simple(doc)

    doc.elements.each do |elem|
      elem.elements.each do |c| 
        to_simple_tree(c)
      end
    end
  end

  def self.to_simple_tree elem

    prefix = elem.prefix
    return if prefix == 'xlink' or prefix == 'link'

    name = elem.name
    return if name == "context" or name == "unit"

    to_simple_element(elem)
    elem.elements.each do |e|
      name = e.name
      to_simple_tree(e)   
    end
  end

  def self.to_simple_element elem
    type = elem.node_type
    if type != :comment and type != :text
      name = elem.name
      elem.add_attributes( { 'prefix' => elem.prefix, 'local_name' => elem.local_name } )
      elem.name = :item
    end
  end

  #----------------------
  def self.to_xbrl(doc)

    doc.elements.each do |elem|
      elem.elements.each do |c|
        to_xbrl_tree(c)
      end
    end
  end

  def self.to_xbrl_tree elem
    to_xbrl_element(elem)
    elem.elements.each do |e|
      to_xbrl_tree(e)   
    end
  end

  def self.to_xbrl_element elem
    type = elem.node_type
    if type != :comment and type != :text   

      if elem.name == nil  # c.name == 'item' ではひかっからない、何故?
        elem.name = "#{elem.attribute('prefix')}:#{elem.attribute('local_name')}"
        elem.delete_attribute("prefix")
        elem.delete_attribute("local_name")
      end
    end
  end
end

def execute doc
  org_xml = doc.to_s

  XBRL::to_simple(doc)
  simple_xml = doc.to_s

  XBRL::to_xbrl(doc)
  xbrl_xml = doc.to_s

  # puts f
  if org_xml != xbrl_xml or simple_xml == xbrl_xml
    puts "--- Error #{f} ------"
    doc.write( File.new('1.org.xml', 'w'))
    File.new('1.simple.xml', 'w').write simple_xml
    File.new('1.xbrl.xml', 'w').write xbrl_xml
    exit
  end

  temp = REXML::Document.new simple_xml
  temp.write( File.new('1.simple.xml', 'w'), 4 )

  temp = REXML::Document.new org_xml
  temp.write( File.new('1.org.xml', 'w'), 4 )

end

# local file
pat = "/Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl"
# pat = "397-00-ConsistentInstance-valid.xbrl"

# SEC - adobe
# pat = "http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml"

count = 0

if pat.index('http:') == 0
  uri = URI.parse(pat)
  doc = REXML::Document.new uri.read
  execute doc
  count += 1
else
  Dir.glob(pat).each do |f|
    doc = REXML::Document.new File.open(f)
    execute doc
    count += 1
  end
end
puts "-- Checked #{count} files in #{pat}"

$ ruby toSimpleXbrl.rb
-- Checked 69 files in /Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl

念のために実行後に 1.org.xml (変換前の XBRL), 1.simple.xml(変換後の XBRL)を出力している。

$ diff 1*
21c21
<     <tx:A decimals='0' contextRef='ctx0' unitRef='u1'>
---
>     <item local_name='A' prefix='tx' decimals='0' contextRef='ctx0' unitRef='u1'>
23,24c23,24
<     </tx:A>
<     <tx:B decimals='0' contextRef='ctx0' unitRef='u1'>
---
>     </item>
>     <item local_name='B' prefix='tx' decimals='0' contextRef='ctx0' unitRef='u1'>
26,27c26,27
<     </tx:B>
<     <tx:C decimals='0' contextRef='ctx0' unitRef='u1'>
---
>     </item>
>     <item local_name='C' prefix='tx' decimals='0' contextRef='ctx0' unitRef='u1'>
29,30c29,30
<     </tx:C>
<     <tx:D decimals='0' contextRef='ctx0' unitRef='u1'>
---
>     </item>
>     <item local_name='D' prefix='tx' decimals='0' contextRef='ctx0' unitRef='u1'>
32c32
<     </tx:D>
---
>     </item>

ローカルファイルだけでなく、SEC サイトのデータも扱える。
// 今は、ソース中の pat で、処理対象ファイルを指定するので、適宜 編集すること。

$ time ruby toSimpleXbrl.rb
-- Checked 69 files in /Users/youichikato/work/www/xbrl.org/XBRL-CONF/**/*.xbrl

real    0m1.131s
user    0m1.030s
sys    0m0.088s

$ time ruby toSimpleXbrl.rb
-- Checked 1 files in http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml

real    0m5.754s
user    0m3.301s
sys    0m0.250s

| | コメント (0) | トラックバック (0)

2008-11-23

xml を別の xml 形式に変換するには?

xml を別の xml 形式に変換するには?

xml を別の xml 形式に変換するには XSLT という方法がある。
でも、XSLT は使いにくい。
(本当に単純な変換ならよいけど。
ちょっと複雑な条件判定、繰り返しのような処理をしようとすると、保守困難なコードになってしまう)

XSLT のような機能を ruby でうまく記述できる DSL な例はないのかなぁ。

# 具体的に考えている利用シーンは、XBRL データの変換だ。
# XBRLそのものの XML 形式は複雑すぎる。
# もっと、Object マッピングしやすい XML に変換してから処理をしたいのだ。
# XBRL のインスタンスデータ中の 各項目が <a:item...> のようにデータ作成側の好き勝手な タグになっているなんて... orz
# (xsd で タグ名をデータ作成者が自由に定義できることの意義は理解できるけど...)
#
# これを <item prefix="a", name="item"...> のように情報量を保ったまま
# 書き換えてしまえば、既存のxml-object マッピングライブラリーで処理しやすくなる。

| | コメント (0) | トラックバック (0)

2008-11-22

happymapper をつかってみる

- http://www.rubyinside.com/happymapper-ruby-xml-object-mapping-1349.html
> HappyMapper: Easy XML / Object Mapping for Rubyists
という記事を読んだ。

早速、このHappyMapper で、google の blog サーチ結果の rss を読み込む例を作ってみた。

$ cat read_rss.rb

# See http://blogsearch.google.co.jp/blogsearch_feeds?hl=ja&q=ruby&lr=&um=1&ie=utf-8&num=100&output=rss

require 'rubygems'
require 'open-uri' 
require 'happymapper'
require 'pp'

module RSS

  class Item
    include HappyMapper

    tag 'item'
    element :title, String, :tag =>'title'
    element :link, String, :tag =>'link'
    element :description, String, :tag =>'description'
    element :publisher, String, :tag =>'publisher'
    element :creator, String, :tag =>'creator'
    element :publisher, String, :tag =>'dc:publisher'
    element :creator, String, :tag =>'dc:creator'
    element :date, DateTime, :tag =>'dc:date'

    def to_s
      "title:#{title}\nlink:#{link}\ndesc:#{description}\npublisher:#{publisher}\ncreator:#{creator}\nDate:#{date.strftime( "%Y-%m-%d %H:%M:%S")}"
    end
  end

  class Channel
    include HappyMapper

    tag 'channel'
    element :title, String, :tag =>'title'
    element :link, String, :tag =>'link'
    element :description, String, :tag =>'description'

    has_many :channel, Item

    def to_s
      "title:#{title}, link: #{link}, desc: #{description}"
    end
  end
end

# google blog 検索で "ruby" を10 件、時間順で検索した結果の RSS
url = "http://blogsearch.google.co.jp/blogsearch_feeds?hl=ja&um=1&scoring=d&q=ruby&lr=lang_ja&ie=utf-8&num=10&output=rss"

open( url ) do |http| 
  response = http.read
  rss = RSS::Channel.parse(response, :single => true, :use_default_namespace => false)
  puts rss

  count = 0;
  rss.channel.each do |item|
    count += 1
    puts "[#{count}]\n#{item}\n"
  end

end

走らせると、こんな出力が得られる。

$ ruby read_rss.rb
title:ruby - Google ブログ検索, link: http://blogsearch.google.co.jp/blogsearch?hl=ja&um=1&scoring=d&q=ruby&lr=lang_ja&ie=Shift_JIS&num=10, desc: Google ブログ検索結果: 検索結果<b>258,087</b>件 <b>ruby</b> - <b>1</b> ~ <b>10</b>件目を表示
[1]
title:<b>Ruby</b> Stone : おはょ~ - livedoor Blog(ブログ)
link:http://blog.livedoor.jp/rubystone/archives/581481.html
desc:<b>Ruby</b> Stone · < withコロナ. 2008年11月22日20:15. カテゴリ. おはょ~ · 60a935fd.jpg 今日も始まりましたぁ~ みなさん今日から三連休だよーん 明日休みだょね? だったら飲み にくるしかないょね~ 今日もニューフェイスがぃますぜぃ☆ きてくだちゃい <b>...</b>
publisher:Ruby Stone
creator:rubystone
Date:2008-11-22 11:28:11
[2]
title:forgot_passwordプラグインを使ってみる - なんとなく日記
link:http://d.hatena.ne.jp/conceal-rs/20081122/1227351842
desc:cd /path/to/app % <b>ruby</b> script/plugin install git://github.com/greenisus/forgot_password.git % <b>ruby</b> script/generate forgot_password user % rake db:migrate. 次はパスワード忘れ用画面へリンクを張ります. <%= link_to "パスワードを忘れた場合 <b>...</b>
publisher:なんとなく日記
creator:conceal-rs
Date:2008-11-22 11:22:41
... 以下 省略...

roxml という xml <-> ruby の mapper もあるようだが、
gem install roxml としても、エラーが発生して、インストールできない。

XBRL タクソノミー/インスタンスを ruby にマップするには、どんな方法がよいだろう?

| | コメント (0) | トラックバック (0)

2008-11-11

tcl/tk -> ruby/tk の書き換えができない

http://www.interq.or.jp/japan/s-imai/tcltk/tkdnd.html
にある tcltk のサンプル2
----------------------------
| package require tkdnd
|
| pack [text .text]
|
| # Target側
| dnd bindtarget .text text/uri-list <Drop> {
|   .text insert end "%D\n"
|}
------------------------
を ruby/tk ではどう書けばよいかが わからない....

----------------------
| require "tk"
| require "tkextlib/tkdnd"
|
| text = TkText.new.pack
| Tk.mainloop
---------------------
まではできるが、肝心の dnd 処理の部分が...

tcl/tk のよい説明サイトや、書籍を探さなければ...

| | コメント (0) | トラックバック (0)

2008-11-09

jruby と ruby での DB アクセスの速度 (その3)

ATOKダイレクトプラグインで郵便番号検索

自宅には、Windows マシンが無いので、休日に会社マシンで、ATOK ダイレクトプラグインの環境を設定し、すこし遊んでみた。

web 上のいくつかのサンプルコードで、作り方を勉強した後、
昨日作った ruby での郵便番号番号検索を、ATOK プラグイン化してみた。
郵便番号の一部 or 住所の一部で検索できるようにしてある。
作ってみて思ったのは、2点。
1. ATOK の選択候補画面のカスタマイズもユーザー側でできるようにして欲しいということ。
   候補画面の GUI も ruby で作りたいものだ。
2. Debug 環境は、もう少し工夫したものが作れそうな気がする。
    Netbeans 上でのデバッガをつかえような、実行用メインメソッドをつくれば済む?

なお、sqlite3 の DB は、 firefox のプラグインの SQLite Manager を使うと内容確認などには便利。

$ cat zip-search.rb
#! /usr/bin/ruby -Ku

$KCODE = 'u'

require 'timeout'
require 'rubygems'
require 'active_record'
require 'nkf'

module Atok_plugin

  class Zip < ActiveRecord::Base
    def to_s
      return  "#{col1}|#{col2}|#{col3}|#{col4.toutf8}|#{col5.toutf8}|#{col6.toutf8}|#{col7.toutf8}|#{col8.toutf8}|#{col9.toutf8}|#{col10}|#{col11}|#{col12}|#{col13}|#{col14}|#{col15}"
    end
  end

  def run_process( a_request_data )
    ActiveRecord::Base.establish_connection(
                                            :adapter  => "sqlite3",
                                            :database => "zips.db",
                                            :timeout  => 5000
                                            )
    # ログをファイルに出す
    # ActiveRecord::Base.logger = Logger.new("debug.log")

    res = []
    timeout(20) do
      key = "%" + a_request_data['composition_string'] + "%"
      res = Zip.find(:all, :conditions => ["col3 like ? or col7 like ? or col8 like ? or col9 like ?", key, key, key, key])
    end
    ans = []
    res.each do |r|
      ans << {'hyoki' => r.col3 + "|" + r.col7 + r.col8 + r.col9, 'comment' => r.to_s}
    end
    {'candidate' => ans}

  rescue Exception => e
    {'candidate' => [{'hyoki' => 'エラーが発生しました', 'comment' => e.to_s}]}
  end

end

ローカルマシンの DB 管理するよりは、memcached でデータを共有したほうが良い気がする。
memcached で、社員名簿 (名前、内線番号、所属部署) などを保持して、それを ATOK + ruby で検索したり、データ内容の保守ができるようにするぐらいが向いているかな。

ともかくここ2日の作業で、10 万件を超える郵便番号の検索が ATOK + ruby で十分可能なことが分かった。
XBRLデータから科目ID, 科目表記名、期間毎の値 をDBに保持して それを検索するようなことは、問題なく実現できそうだとの感触を得られた。

| | コメント (1) | トラックバック (0)

jruby と ruby での DB アクセスの速度 (その2)

scala でも sqlite3 に入れた郵便番号を検索させてみた。

$ cat  search.scala
/
// $ calac search.scala
// Then
// $ scala -classpath .:sqlitejdbc-v053.jar sqlitetest
//  or
// $ java -cp .:sqlitejdbc-v053.jar:/opt/local/share/java/scala-library.jar sqlitetest
//

import java.sql._

object sqlitetest extends Application {

  var conn: Connection = null
  var stat: Statement = null
  var rs: ResultSet = null

  try {

    Class.forName("org.sqlite.JDBC")

    conn = DriverManager.getConnection("jdbc:sqlite:zips.db")
    stat = conn.createStatement 
    rs = stat.executeQuery("select * from zips where col3 like '100000%';")

    while (rs.next()) {
      println(
    rs.getString("col1") + "|" +
    rs.getString("col2") + "|" +
    rs.getString("col3") + "|" +
    rs.getString("col4") + "|" +
    rs.getString("col5") + "|" +
    rs.getString("col6") + "|" +
    rs.getString("col7") + "|" +
    rs.getString("col8") + "|" +
    rs.getString("col9") + "|" +
    rs.getString("col10") + "|" +
    rs.getString("col11") + "|" +
    rs.getString("col12") + "|" +
    rs.getString("col13") + "|" +
    rs.getString("col14"))
    }
  } catch {
    case e => e.printStackTrace
  } finally {
    if (rs == null) rs.close
    if (stat == null) stat.close
    if (conn == null) conn.close
  }
}

$ time scala -classpath .:sqlitejdbc-v053.jar sqlitetest
13101|100  |1000000|トウキョウト|チヨダク|イカニケイサイガナイバアイ|東京都|千代田区|以下に掲載がない場合|0|0|0|0|0
13101|100  |1000004|トウキョウト|チヨダク|オオテマチ|東京都|千代田区|大手町|0|0|1|0|0
13101|100  |1000002|トウキョウト|チヨダク|コウキョガイエン|東京都|千代田区|皇居外苑|0|0|0|0|0
13101|100  |1000001|トウキョウト|チヨダク|チヨダ|東京都|千代田区|千代田|0|0|0|0|0
13101|100  |1000003|トウキョウト|チヨダク|ヒトツバシ(1チョウメ)|東京都|千代田区|一ツ橋(1丁目)|1|0|1|0|0
13101|100  |1000005|トウキョウト|チヨダク|マルノウチ(ツギノビルヲノゾク)|東京都|千代田区|丸の内(次のビルを除く)|0|0|1|0|0
13101|100  |1000006|トウキョウト|チヨダク|ユウラクチョウ|東京都|千代田区|有楽町|0|0|1|0|0

real    0m0.658s
user    0m0.397s
sys    0m0.176s

エラー処理の書き方がだめだが、速度はまあまだ。
jruby が遅かったのは、ソースのコンパイル時間が発生する為と思われる。
jrubyc でコンパイルすれば、速くなるかな?

$ jrubyc jserach.rb

$ java -cp .:$JRUBY_HOME/lib/jruby.jar jsearch
Exception in thread "main" jsearch.rb:4:in `require': no such file to load -- pp (LoadError)
    from jsearch.rb:4
    ...internal jruby stack elided...
    from Kernel.require(jsearch.rb:4)
    from (unknown).(unknown)(:1)

うーん、jrubyc で compile した class の実行法がうまくいかないなぁ...

| | コメント (0) | トラックバック (1)

2008-11-08

jruby と ruby での DB アクセスの速度

- http://www.tuyudaku.net/sqlite/import.html
> SQLite - CSVファイルのインポート
を参考にして、 sqlite3 に郵便番号を import した。
# activerecord で扱うための、テーブル名を zip でなく、zips になるように変更して。

ruby と jruby の両方で、簡単な検索プログラムを書いて、速度を比較してみた。

$ cat search.rb
#See http://d.hatena.ne.jp/urekat/20080923/1222169086

require 'pp'
require 'rubygems'
require 'active_record'
require 'nkf'

# ログを stderrに出す
# ActiveRecord::Base.logger = Logger.new($stderr)
# ログをファイルに出す
#ActiveRecord::Base.logger = Logger.new("debug.log")

ActiveRecord::Base.establish_connection(
  :adapter  => "sqlite3",
  :database => "zips.db",
  :timeout  => 5000
)

class Zip < ActiveRecord::Base
  def to_s
    return  "#{col1}|#{col2}|#{col3}|#{col4.toutf8}|#{col5.toutf8}|#{col6.toutf8}|#{col7.toutf8}|#{col8.toutf8}|#{col9.toutf8}|#{col10}|#{col11}|#{col12}|#{col13}|#{col14}|#{col15}"
  end
end

puts Zip.find(:all, :conditions => ["col3 like ?", '100000%'])

$ cat jsearch.rb
# See http://d.hatena.ne.jp/urekat/20080923/1222169086

require 'pp'
require 'rubygems'
require 'active_record'
# require 'kconv'
# SJIS 出力したいなら、str.kconv(Kconv::SJIS, Kconv::UTF8) などとすること。

# ログを stderrに出す
# ActiveRecord::Base.logger = Logger.new($stderr)
# ログをファイルに出す
#ActiveRecord::Base.logger = Logger.new("debug.log")

ActiveRecord::Base.establish_connection(
  :adapter  => "jdbcsqlite3",
  :database => "zips.db",
  :timeout  => 5000
)

class Zip < ActiveRecord::Base
  def to_s
    return  "#{col1}|#{col2}|#{col3}|#{col4}|#{col5}|#{col6}|#{col7}|#{col8}|#{col9}|#{col10}|#{col11}|#{col12}|#{col13}|#{col14}|#{col15}" # .kconv(Kconv::SJIS, Kconv::UTF8)
  end
end

puts Zip.find(:all, :conditions => ["col3 like ?", '100000%'])

$ time ruby search.rb
13101|100  |1000000|トウキョウト|チヨダク|イカニケイサイガナイバアイ|東京都|千代田区|以下に掲載がない場合|0|0|0|0|0|0
13101|100  |1000004|トウキョウト|チヨダク|オオテマチ|東京都|千代田区|大手町|0|0|1|0|0|0
13101|100  |1000002|トウキョウト|チヨダク|コウキョガイエン|東京都|千代田区|皇居外苑|0|0|0|0|0|0
13101|100  |1000001|トウキョウト|チヨダク|チヨダ|東京都|千代田区|千代田|0|0|0|0|0|0
13101|100  |1000003|トウキョウト|チヨダク|ヒトツバシ(1チョウメ)|東京都|千代田区|一ツ橋(1丁目)|1|0|1|0|0|0
13101|100  |1000005|トウキョウト|チヨダク|マルノウチ(ツギノビルヲノゾク)|東京都|千代田区|丸の内(次のビルを除く)|0|0|1|0|0|0
13101|100  |1000006|トウキョウト|チヨダク|ユウラクチョウ|東京都|千代田区|有楽町|0|0|1|0|0|0

real    0m0.774s
user    0m0.559s
sys    0m0.210s

$ time jruby jserach.rb
13101|100  |1000000|トウキョウト|チヨダク|イカニケイサイガナイバアイ|東京都|千代田区|以下に掲載がない場合|0|0|0|0|0|0
13101|100  |1000004|トウキョウト|チヨダク|オオテマチ|東京都|千代田区|大手町|0|0|1|0|0|0
13101|100  |1000002|トウキョウト|チヨダク|コウキョガイエン|東京都|千代田区|皇居外苑|0|0|0|0|0|0
13101|100  |1000001|トウキョウト|チヨダク|チヨダ|東京都|千代田区|千代田|0|0|0|0|0|0
13101|100  |1000003|トウキョウト|チヨダク|ヒトツバシ(1チョウメ)|東京都|千代田区|一ツ橋(1丁目)|1|0|1|0|0|0
13101|100  |1000005|トウキョウト|チヨダク|マルノウチ(ツギノビルヲノゾク)|東京都|千代田区|丸の内(次のビルを除く)|0|0|1|0|0|0
13101|100  |1000006|トウキョウト|チヨダク|ユウラクチョウ|東京都|千代田区|有楽町|0|0|1|0|0|0

real    0m8.645s
user    0m7.928s
sys    0m0.472s

遅いぞ jruby!
ATOKダイレクトプラグインで、ruby + activerecord + sqlite3 は十分 使えそうだな。

| | コメント (0) | トラックバック (0)

ActiveRecord-JDBC で sqlite3 のアクセス

以前、jruby で Activerecord-jdbc で sqlite3 アクセスしようとした際は、
sqlite3 アダプターが無かったので、実現できずにいた。
たまたま、今日 sqlite3 アダプタができていることを知った。
- http://weblogs.java.net/blog/arungupta/archive/2008/07/totd_37_sqlite3.html
> Arun Gupta's Blog: TOTD #37: SQLite3 with Ruby-on-Rails on GlassFish Gem

そこで、ruby + activerecord + sqlite3 のプログラムを探し、次のサイトをみつけて、これを jruby + acriverecord0jdbc + sqlite3 に書き換えてみた。

まずは ruby 版。
$ cat ruby-01.rb
# See http://d.hatena.ne.jp/urekat/20080923/1222169086

require "pp"
require "rubygems"
require "active_record"

commands = <<END
ruby -v
which sqlite3
sqlite3 --version
gem list | grep sqlite3-ruby
gem list | grep activerecord
END
commands.each do |cmd|
  puts "'#{ cmd.strip}' => '#{`#{ cmd}`.strip}'"
end

ActiveRecord::Base.logger = Logger.new($stderr)
ActiveRecord::Base.establish_connection(
  :adapter  => "sqlite3",
  :database => "_sqlite3_ar_test.sqlite3",
  :timeout  => 5000
)

begin
  ActiveRecord::Migration.create_table :users do |t|
    t.column :name       , :string
    t.column :nickname   , :string
    t.column :profile    , :text
    t.column :created_at , :datetime
    t.column :updated_at , :datetime
  end
rescue
end

class User < ActiveRecord::Base
end

User.create(
  :name     => "sasaki takeru",
  :nickname => "urekat",
  :profile  => "hehe hoho."
)

puts "User.count=#{User.count}"
pp User.find(:all)

次が jruby 版。
$ cat jruby-01.rb
# See http://d.hatena.ne.jp/urekat/20080923/1222169086

require "pp"
require "rubygems"
require "active_record"

commands = <<END
jruby -v
which sqlite3
sqlite3 --version
jruby -S gem list | grep activerecord-jdbcsqlite3-adapter
jruby -S gem list | grep ActiveRecord-JDBC
END
commands.each do |cmd|
  puts "'#{ cmd.strip}' => '#{`#{ cmd}`.strip}'"
end

ActiveRecord::Base.logger = Logger.new($stderr)
ActiveRecord::Base.establish_connection(
  :adapter  => "jdbcsqlite3",
  :database => "_sqlite3_ar_test.sqlite3",
  :timeout  => 5000
)

begin
  ActiveRecord::Migration.create_table :users do |t|
    t.column :name       , :string
    t.column :nickname   , :string
    t.column :profile    , :text
    t.column :created_at , :datetime
    t.column :updated_at , :datetime
  end
rescue
end

class User < ActiveRecord::Base
end

User.create(
  :name     => "sasaki takeru",
  :nickname => "urekat",
  :profile  => "hehe hoho."
)

puts "User.count=#{User.count}"
pp User.find(:all)

では、走らせてみよう。

$ ruby ruby-01.rb
ruby -v' => 'ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]'
'which sqlite3' => '/opt/local/bin/sqlite3'
'sqlite3 --version' => '3.6.4'
'gem list | grep sqlite3-ruby' => 'sqlite3-ruby (1.2.4)'
'gem list | grep activerecord' => 'activerecord (2.1.2, 2.1.0)
activerecord-jdbc-adapter (0.8.2)
activerecord-jdbcsqlite3-adapter (0.8.2)'
-- create_table(:users)
  SQL (0.000417)   select sqlite_version(*)
  SQL (0.003166)   CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "nickname" varchar(255), "profile" text, "created_at" datetime, "updated_at" datetime)
   -> 0.0382s
  User Create (0.000448)   INSERT INTO "users" ("name", "created_at", "profile", "nickname", "updated_at") VALUES('sasaki takeru', '2008-11-08 13:30:46', 'hehe hoho.', 'urekat', '2008-11-08 13:30:46')
  SQL (0.000230)   SELECT count(*) AS count_all FROM "users"
User.count=1
  User Load (0.000269)   SELECT * FROM "users"
[#<User id: 1, name: "sasaki takeru", nickname: "urekat", profile: "hehe hoho.", created_at: "2008-11-08 13:30:46", updated_at: "2008-11-08 13:30:46">]

続けて、jruby 版。
$ jruby jruby-01.rb
jruby -v' => 'jruby 1.1.5 (ruby 1.8.6 patchlevel 114) (2008-11-08 rev 6586) [i386-java]'
'which sqlite3' => '/opt/local/bin/sqlite3'
'sqlite3 --version' => '3.6.4'
'jruby -S gem list | grep activerecord-jdbcsqlite3-adapter' => 'activerecord-jdbcsqlite3-adapter (0.8.2)'
'jruby -S gem list | grep ActiveRecord-JDBC' => 'ActiveRecord-JDBC (0.5)'
-- create_table(:users)
  SQL (0.000000)   ActiveRecord::ActiveRecordError: table users already exists: CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name text(0), nickname text(0), profile text, created_at INTEGER, updated_at INTEGER)
  User Create (0.005822)   INSERT INTO users (name, nickname, profile, created_at, updated_at) VALUES('sasaki takeru', 'urekat', 'hehe hoho.', '2008-11-08 13:31:27', '2008-11-08 13:31:27')
  SQL (0.003723)   SELECT SEQ FROM SQLITE_SEQUENCE WHERE NAME = 'users'
  SQL (0.003101)   SELECT count(*) AS count_all FROM users
User.count=2
  User Load (0.006008)   SELECT * FROM users
[#<User id: 1, name: "sasaki takeru", nickname: "urekat", profile: "hehe hoho.", created_at: "2008-11-08 13:30:46", updated_at: "2008-11-08 13:30:46">,
#<User id: 2, name: "sasaki takeru", nickname: "urekat", profile: "hehe hoho.", created_at: "2008-11-08 13:31:27", updated_at: "2008-11-08 13:31:27">]

ちゃんと データ総数が増えている。

| | コメント (3) | トラックバック (0)

2008-10-12

ruby で jar ファイル中の文字列を検索

Getversion

- http://homepage2.nifty.com/youichi_kato/src.html
> jar ファイル中の文字列検索 (2008-10-11)
> http://homepage2.nifty.com/youichi_kato/src/ruby/zipfile/getversion.rb
として、ruby で jar ファイル中の文字列検索の例を作成した。

これは xfy/bin/plugins 以下の jar, xar ファイル中の version 記載を取り出すことに特化してある。

ここでの rubyzip の使い方を利用することで、jar ファイルを自由にスキャンしたり、jar 中のファイルを取り出すことができるだろう。

このスクリプトは、jruby でも実行できる。

| | コメント (0) | トラックバック (0)

2008-10-11

順列で 3x3 魔方陣を

こんな記事をみかけた。

- http://d.hatena.ne.jp/n9d/20071213/1197521285
> すごいぞprolog!魔方陣がワンライナーでかける!(swi-prologで魔方陣を計算その2) - 計算機と戯れる日々

ふーん、すごいな。
MacBook でやってみた。

$ swipl
Welcome to SWI-Prolog (Multi-threaded, 32 bits, Version 5.6.61)

?- time((permutation([1, 2, 3, 4, 5, 6, 7, 8, 9], [A, B, C, D, E, F, G, H, I]),X is A+B+C,X is D+E+F,X is G+H+I,X is A+D+G,X is B+E+H,X is C+F+I,X is A+E+I,X is C+E+G,print([A,B,C,D,E,F,G,H,I,X]),nl,fail)).
[6, 1, 8, 7, 5, 3, 2, 9, 4, 15]
[8, 1, 6, 3, 5, 7, 4, 9, 2, 15]
[8, 3, 4, 1, 5, 9, 6, 7, 2, 15]
[4, 3, 8, 9, 5, 1, 2, 7, 6, 15]
[6, 7, 2, 1, 5, 9, 8, 3, 4, 15]
[2, 7, 6, 9, 5, 1, 4, 3, 8, 15]
[2, 9, 4, 7, 5, 3, 6, 1, 8, 15]
[4, 9, 2, 3, 5, 7, 8, 1, 6, 15]
% 1,154,595 inferences, 0.63 CPU in 0.64 seconds (99% CPU, 1832690 Lips)
false.

ruby でもやってみた
$ cat magicsqure3.rb
#  0 1 2
#  3 4 5
#  6 7 8

[1,2,3,4,5,6,7,8,9].permutation do |a|
  p a if a[0]+a[1]+a[2] == 15 and a[3]+a[4]+a[5] == 15 and a[6]+a[7]+a[8] == 15 and a[0]+a[3]+a[6] == 15 and a[1]+a[4]+a[7] == 15 and a[2]+a[5]+a[8] == 15 and a[0] + a[4] + a[8] == 15 and a[2] + a[4] + a[6] == 15
end

$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

$ time ruby magicsquare3.rb
[2, 7, 6, 9, 5, 1, 4, 3, 8]
[2, 9, 4, 7, 5, 3, 6, 1, 8]
[4, 3, 8, 9, 5, 1, 2, 7, 6]
[4, 9, 2, 3, 5, 7, 8, 1, 6]
[6, 1, 8, 7, 5, 3, 2, 9, 4]
[6, 7, 2, 1, 5, 9, 8, 3, 4]
[8, 1, 6, 3, 5, 7, 4, 9, 2]
[8, 3, 4, 1, 5, 9, 6, 7, 2]

real    0m2.435s
user    0m1.797s
sys    0m0.623s

jruby も試そうと思ったが、jruby (1.1.4) には permutaion メソッドが無いとのエラーになった。

java での 順列生成の例をネットで探してみた。

- http://sakura.bb-west.ne.jp/spr/damayan/algo/PermEnum.java
> 順列を生成する Enumeration
これをつかって、プログラムを作り、実行させてみた。

$ cat MagicSquare3.java
// See http://sakura.bb-west.ne.jp/spr/damayan/algo/PermEnum.java

import java.util.Enumeration;

/**
* 順列を生成する Enumeration
* N 個の要素から順列を生成する個数は N!
* (参考)C言語によるアルゴリズム入門
*/
class PermEnum implements Enumeration {

    private int N;
    private int c[], k;
    private Object[] objs;

    public PermEnum(Object[] items) {
    N = items.length;
    c = new int[N + 1];
    for(int i=0; i<=N; i++) c[i] = i;
    objs = items;
    k = 1;
    }

    public boolean hasMoreElements() {
    return (k < N);
    }

    public Object nextElement() {
    int i = 0;
    if((k & 1) != 0) i = c[k];

    Object tmp = objs[k];
    objs[k] = objs[i];
    objs[i] = tmp;

    k = 1;
    while(c[k] == 0) c[k] = k++;

    c[k]--;
    return objs;
    }
}

public class MagicSquare3 {

    // テスト
    public static void main(String[] args) {
    Integer[] data = {1,2,3,4,5,6,7,8,9};
    System.out.println("N="+data.length);
    Enumeration e = new PermEnum(data);
    int count = 0;
    while(e.hasMoreElements()) {
        Integer[] a = (Integer[])e.nextElement();

        if (a[0]+a[1]+a[2] == 15 & a[3]+a[4]+a[5] == 15 & a[6]+a[7]+a[8] == 15 & a[0]+a[3]+a[6] == 15 & a[1]+a[4]+a[7] == 15 & a[2]+a[5]+a[8] == 15 & a[0] + a[4] + a[8] == 15 & a[2] + a[4] + a[6] == 15) {
        System.out.print("{" + a[0]);
        for(int i=1; i<a.length; i++)
            System.out.print(", "+a[i]);
        System.out.println("}");
        count++;
        }
    }
    System.out.println("count="+count);
    }
}

$ java -version
java version "1.5.0_16"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_16-b06-284)
Java HotSpot(TM) Client VM (build 1.5.0_16-133, mixed mode, sharing)

$ time java -cp . MagicSquare3
N=9
{2, 9, 4, 7, 5, 3, 6, 1, 8}
{2, 7, 6, 9, 5, 1, 4, 3, 8}
{8, 3, 4, 1, 5, 9, 6, 7, 2}
{8, 1, 6, 3, 5, 7, 4, 9, 2}
{6, 7, 2, 1, 5, 9, 8, 3, 4}
{6, 1, 8, 7, 5, 3, 2, 9, 4}
{4, 9, 2, 3, 5, 7, 8, 1, 6}
{4, 3, 8, 9, 5, 1, 2, 7, 6}
count=8

real    0m0.277s
user    0m0.169s
sys    0m0.046s

| | コメント (0) | トラックバック (0)

2008-09-18

ferret を使いたい (その3)

先週末に 書籍 "Ferret" を入手した。

書籍のサンプルを試して、使い方は分かった気がする。

そこでferret で 郵便番号データ(全国版) を index を作り、検索をさせてみた。
index をつくるのが遅いけど、検索は十分な速さがある。
この程度なら、xfy/xvcd から ruby を呼び出して使う際も、十分な速度が得られると思われる。
// index を作るソースが、我ながらいダサイ。もっとよい書き方に変更できると思うが、まずは動作させることから...

使用マシンは MacBook, MacOSX 10.5。
$ gem list ferret
*** LOCAL GEMS ***
ferret (0.11.6)

$ ruby -version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

インデックスを作る。
$ time ruby ZiptoFerret.rb
..........................................................................................................................
indexed 122535 zips.

real    2m7.108s
user    1m26.578s
sys    0m36.770s

ferret のワイルドカード検索を使う。
$ time ruby search-zipcode.rb '*有楽町*'
1.0: 0383135  青森県 つがる市 木造有楽町
1.0: 3260801  栃木県 足利市 有楽町
1.0: 3591114  埼玉県 所沢市 北有楽町
1.0: 3591117  埼玉県 所沢市 有楽町
1.0: 1000006  東京都 千代田区 有楽町
1.0: 9188008  福井県 福井市 有楽町
1.0: 5020033  岐阜県 岐阜市 長良有楽町
1.0: 5060013  岐阜県 高山市 有楽町
1.0: 4418035  愛知県 豊橋市 有楽町
1.0: 4750837  愛知県 半田市 有楽町
1.0: 5110079  三重県 桑名市 有楽町
1.0: 7450035  山口県 周南市 有楽町
12 hits.

real    0m0.313s
user    0m0.243s
sys    0m0.065s

AND 指定で絞り込むこともできる。
$ time ruby search-zipcode.rb '*東京* AND *有楽町*'

1.0: 1000006  東京都 千代田区 有楽町
1 hits.

real    0m0.424s
user    0m0.333s
sys    0m0.085s

以下にコードを示す。

$ cat LineAnalyzer.rb

require 'pp'
require 'MeCab'

class LineAnalyzer
  def initialize()
  end
  def token_stream(field, str)
   return LineTokenizer.new(str)
  end
end

class LineTokenizer
  def initialize(str)
    self.text = str
  end

  def text=(str)
    @n = str
  end

  attr_reader :text
  def next
    return nil if @n == nil
    token = Analysis::Token.new(@n, 0, @n.length)
    @n = nil
    return token
  end
end

$ cat ZiptoFerret.rb

# See http://webos-goodies.jp/archives/51369852.html

require 'rubygems'
require 'nkf'
require 'csv'
require 'ferret'

require 'LineAnalyzer'

include Ferret
include Ferret::Index
include Ferret::Analysis

$KCODE='UTF8'

Ferret.locale = 'ja_JP.UTF-8'

index_dir = 'zip_index'
csv_fname = 'KEN_ALL.CSV'

data = NKF.nkf('-Sw80m0', IO.read(csv_fname))

# index = Index.new(:path=> index_dir, :create => true, :analyzer => RegExpAnalyzer.new(/.*/, false))
index = Index.new(:path=> index_dir, :create => true, :analyzer => LineAnalyzer.new)

line_count = 0

# 01101,"060  ","0600000","ホッカイドウ","サッポロシチュウオウク","イカニケイサイガナイバアイ","北海道","札幌市中央区","以下に掲載がない場合",0,0,0,0,0,0

CSV::Reader.parse(data) do |row|
  line_count += 1
  index << {
    :id => line_count,
    :zip => row[2],
    :prefecture => (row[6]||''),
    :city => (row[7]||''),
    :street => (row[8]||'')
  }
  if line_count % 1000 == 0
    printf "."
    $stdout.flush
  end

end

index.optimize

index.close
puts "\nindexed #{line_count} zips."

$ cat search-zipcode.rb

require 'rubygems'
require 'ferret'
require 'fileutils'
require 'MecabAnalyzer'

require 'pp'

include Ferret
include Ferret::Index

$KCODE='utf8'

def usage(message = nil)
  puts message if message
  puts "ruby #{File.basename(__FILE__)} <search_pattern>"
  puts "  examples for search_patter:"
  puts "         '*10000*'  "
  puts "         '*千代田区*' "
  puts "         ':zip:*1000*'"
  puts "         ':city:*千代田区*'"
  puts "         ':street:*有楽町*'"
  puts "         '*千代田区* AND *有楽町*'"
  puts " 注意: 文字コードは UTF-8. "
  exit(1)
end

index_dir ="zip_index"
limit = 100
offset = 0

usage() if ARGV.size != 1

search_phrase = ARGV[0]

index = Index.new(:path => index_dir)

results = []

total_hits = index.search_each(search_phrase,
                               :limit => limit, :offset => offset) do |id, score|
  results << " #{score}: #{index[id][:zip]}  #{index[id][:prefecture]} #{index[id][:city]} #{index[id][:street]}"
end

index.close

puts results.join("\n")
puts "#{total_hits} hits."

| | コメント (0) | トラックバック (0)

2008-08-21

ruby on xfy, jruby on xfy

こんな記事が出た。
- http://xfy.justblog.jp/labs/2008/08/ruby-2065.html
> xfy 実験室: Rubyの世界にはワクワクがいっぱい! あれ?
> ... GEM で追加できる exifr と rmagick4j というモジュールを使ったサンプルをご紹介します...

gem を知ってしまうと、
xfy のプラグインインストール/アンインストールも ruby の gem みたいな
管理にして欲しいと思う人がでてくるはず。
ぜひ gem のような管理の仕組みをつくり、さらに GUI操作環境 を xfy 上に構築して欲しいなぁ。
# netbeasns の ruby gems 管理はなかなか便利だと思う。
# ruby gems や mac ports に対する xfy での GUI クライアントも欲しいけど。

hpricot という gem をつかうと、 html (少しぐらい不整合があってもよい)を処理できる。
その結果を  ruby の標準のxml 操作ライブラリーをつかうことで目的にそった xml に変換ができる。
# rss が提供されていない サイトに対して、自前で rss を生成するなんてことができる。

xml に変換さえできればしめたもの。
xfy/xvcd の template 機能で (レンダリング + 編集可能) なアプリケション画面が自然に作成できる!

net で拾った もこもこ雲の画像。

Kumo5

| | コメント (0) | トラックバック (1)

2008-08-07

java の ScriptEngineManage の実験

- http://d.hatena.ne.jp/koichiarchi/20070821/1187669141
> JavaからRubyを使う - koichi’s swap memory

この記事にある java のサンプルを MacOSX 10.5 で動作させてみた。

その前に まずは、
classpath 指定をして java を実行する shell script をつくった。

$ cat run.sh
#! /bin/sh

JRUBY_LIBS=\
$JRUBY_HOME/lib/bsf.jar:\
$JRUBY_HOME/lib/jruby.jar:\
$JRUBY_HOME/lib/profile.jar:\
/Users/youichikato/work/www/jsr223-engine/jruby/build/jruby-engine.jar

java -cp $JRUBY_LIBS:. $*

まずは、有効な engine の調査だ。
$ cat ScriptEngineSearch.java
// See
//  http://d.hatena.ne.jp/koichiarchi/20070810/1186736204

import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;

/**
* 使用可能なスクリプトエンジンを調べる
*/
class ScriptEngineSearch {
    public static void main(String args[]) {
        ScriptEngineManager manager = new ScriptEngineManager();

        // 使用可能なエンジンの一覧を取得
        for (ScriptEngineFactory engine : manager.getEngineFactories()) {
            System.out.println("enfine full name = " + engine.getEngineName());

            // エンジン生成時に使用出来るショートネーム
            for (String shortName : engine.getNames()) {
                System.out.println("- engine short name = " + shortName);
            }
        }
    }
}

走らせると
$ ./run.sh ScriptEngineSearch
nfine full name = JRuby Engine
- engine short name = jruby
- engine short name = ruby
enfine full name = AppleScriptEngine
- engine short name = AppleScriptEngine
- engine short name = AppleScript
- engine short name = OSA

おー、jruby が認識されている。

次は、外部の jruby scriファイルを実行する例だ。
$ cat tax.rb
def tax(m)
  puts m * 1.05
end

$ cat JRubyFileExecute.java
// See
//  http://d.hatena.ne.jp/koichiarchi/20070821/1187669141

import java.io.FileReader;
import java.io.IOException;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JRubyFileExecute {

    private static ScriptEngine ruby = new ScriptEngineManager().getEngineByName("ruby");

    public static void main(String[] args) throws ScriptException, IOException {
        FileReader reader = new FileReader(args[0]);
        ruby.eval(reader);
        ruby.put("m", 980);
        ruby.eval("tax($m)");
    }
}

走らせると、
$ ./run.sh JRubyFileExecute tax.rb
1029.0
素晴らしい!

- https://scripting.dev.java.net/
> scripting: Project Home Page
には、サポートされる script の一覧がある。

java のソースコードを生成 or 入力し、それを compile して実行
なんてことをすれば、java を script 言語のように扱えるだろう。

| | コメント (0) | トラックバック (0)

2008-08-03

ruby で gmail へ添付ファル付きメールを送る

- http://mac-memo.blogspot.com/2008/02/rubygmail.html                                                                               
   > Macメモ: RubyでGMAILにメール
の記事の ruby コードを試してみた。
問題なく 日本語メールも送れた。すばらしい!

mail-address, password を config.yaml で設定するなどの変更をしてみた。

$ cat config.yaml.template
gmail_address: xxxxxx@gmail.com
gmail_pass: xxxxxx

$ cat sendmail.rb

# See  http://mac-memo.blogspot.com/2008/02/rubygmail.html
#      > Macメモ: RubyでGMAILにメール

require 'net/smtp'
require 'rubygems'
require 'tlsmail'
require 'kconv'
require 'base64'
require 'yaml'

$KCODE = 'utf8'

# mail_address="xxxxxx@gmail.com"
# password='xxxxxx'
# GMAIL アドレス、パスワードを config.yaml に設定する                                                                     
config_file = 'config.yaml'
config = YAML.load_file(config_file)
mail_address = config["gmail_address"]
password = config["gmail_pass"]

def send_with_attach(mail_address, password, from_address, to_address, mail_subject, mail_text, file_path)

  port=587
  helo_domain="gmail.com"
  smtp_host="smtp.gmail.com"
  smtpserver = Net::SMTP.new(smtp_host, port)
  smtpserver.enable_tls(OpenSSL::SSL::VERIFY_NONE)

  encoded = [File.open(file_path).readlines.join('')].pack('m')

  message = <<EndOfMail
From: #{from_address}
To: #{to_address}
Date: #{ Time::now.strftime("%a, %d %b %Y %X %z")}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=\"boundary_string_by_landscape_mail\"
X-Mailer: imput.rb
Subject: #{mail_subject}

--boundary_string_by_landscape_mail
Content-Type: text/plain; charset=\"ISO-2022-JP\"
Content-Transfer-Encoding: 7bit

#{mail_text.tojis}

--boundary_string_by_landscape_mail
Content-Type: application/octet-stream; name=#{ file_path}
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=#{ File.basename(file_path)}

#{ encoded}

--boundary_string_by_landscape_mail--
EndOfMail

  smtpserver.start('gmail.com', mail_address, password, :login) do |smtp|
    smtp.send_message message, from_address, to_address
  end
end

from_address = mail_address
to_address = mail_address
subject = 'TEST'
message = "This is TEST.\テストです。"
file_path = File.basename(ARGV[0])

send_with_attach(mail_address, password, from_address, to_address, subject, message, file_path)

| | コメント (0) | トラックバック (1)

SEC の XBRL データの取得

SEC ( http://www.sec.gov/ ) で公開される アメリカの XBRL データを ダウンロードする ruby スクリプトを書いてみた。
公開データの追加状況は RSS で取得できるので、それを読んで新しいデータをダウンロードするようにした。
http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/tools/sec/read-rss.rb?rev=26&root=ruby-xbrl&view=auto

こんな感じに使う。

$ ruby read-rss.rb X    // 不正パラメータを指定して簡易ヘルプを表示させた
usage: ruby read-rss.rb [n]
     n: SEC の n 日以内の新しい XBRL ダータをダウンロードする。
        指定省略した場合 7
     データは ./Archies/edgar/... に保存される。

$ ruby read-rss.rb 10
downlaod 2 corp(s)
AMEDISYS INC : 100% |oooooooooooooooooooooooooooooooooooooooo| ETA:  00:00:00
SAFEWAY INC (: 100% |oooooooooooooooooooooooooooooooooooooooo| ETA:  00:00:00
$
$ tree  // ダウンロードデータを確認
.
|-- Archives
|   `-- edgar
|       `-- data
|           |-- 86144
|           |   `-- 000119312508160373
|           |       |-- 0001193125-08-160373.txt
|           |       |-- d8k.htm
|           |       |-- swy-20080614.xml
|           |       |-- swy-20080614.xsd
|           |       |-- swy-20080614_cal.xml
|           |       |-- swy-20080614_lab.xml
|           |       `-- swy-20080614_pre.xml
|           `-- 896262
|               `-- 000119312508160221
|                   |-- 0001193125-08-160221.txt
|                   |-- amed-20080630.xml
|                   |-- amed-20080630.xsd
|                   |-- amed-20080630_cal.xml
|                   |-- amed-20080630_lab.xml
|                   |-- amed-20080630_pre.xml
|                   |-- d8k.htm
|                   `-- g42936tx1logo.jpg
`-- read-rss.rb

| | コメント (0) | トラックバック (0)

2008-07-26

Java製拡張コマンド(ExtHelloWorld.java) を改造 (その2)

xfy blog editor から ruby  スクリプトを呼び出す例として、
  - erb を使って九九の表を生成
  - ruby/tk で ファイル選択ダイアログ、色選択ダイアログを開く
をつくってみた。どちらも問題なく xfy blog editor から動作するようだ。
- http://homepage2.nifty.com/youichi_kato/src.html
>  ExtHelloWorld.java の改造を登録 -002(2008-07-26)

xfy/xvcd でダイアログをつくるより、ruby/tk でダイアログをつくるほうが簡単な場合もありそうだ。

Chooser01

上のスクリーンショットは、chooser.rb を xfy blog editor から実行したものだ。
左上の小さな winodow が、ruby/tk でのメインウインドウだ。
[open] ボタンをクリックして、 ファイル選択ダイアログを出している最中の画面だ。
MacOSX(10.5) で動作させており、MacOS の通常のファイル選択ダイアログが出ている。

次は ruby の代わりに jruby を使うことで、 xfy blog editor 中の java クラスを利用することができるかを試す予定。

| | コメント (0) | トラックバック (0)

2008-07-22

ExtHelloWorld.java の改造を登録 (2008-07-22)

- http://homepage2.nifty.com/youichi_kato/src.html
  ProcessBuilder 生成を
  Process p = new ProcessBuilder(command).start();
 ー>
        Process p = new ProcessBuilder("ruby", command).start();
   と書き換えることで、 フルパス指定した ruby スクリプトを実行し、その stdout 出力を blog 記事取り込めた。
    Screen01

次はファイル選択ダイアログを出すようにしよう。
// ダイアログは java で書く? ruby で書く?

さらに ruby から ImageMagic を呼び出すことで、画像処理 (サムネイル作成など)のプラグインを ruby スクリプトで作れそうな気もする...

| | コメント (0) | トラックバック (0)

2008-06-15

プログラムで生成した画像を rails で表示する

gruff, cairo をつかって画像生成し、それを rails で表示させることができた。

view では次のようにする。
<img src="<%= url_for :controller=>'graphview', :action=>'graph_foo' %>">

controller では次のようにする。
class GraphviewController < ApplicationController

  def graph_foo
    # case: using gruff
    #----------------------
    #    @company = Company.find(params[:company_dbid])
    #
    #    g = Gruff::Line.new 500
    #    g.labels = {0 => 'Day 0', 1 => 'Day 2',
    #                2 => 'Day 4', 3 => 'Day 10'}   
    #    g.data("xx", [30, 20, 60, 50])
    #    g.minimum_value = 0
    #
    #    # :disposition => "inline"
    #    send_data(g.to_blob, :type => 'image/png')

    # case: using rcairo
    #----------------------
    format = Cairo::FORMAT_ARGB32
    width = 300
    height = 200
    radius = height / 3

    # 日の丸を描く
    surface = Cairo::ImageSurface.new(format, width, height)
    context = Cairo::Context.new(surface)

    context.set_source_rgb(1, 1, 1)
    context.rectangle(0, 0, width, height)
    context.fill

    context.set_source_rgb(1, 0, 0)
    context.arc(width / 2, height / 2, radius, 0, 2 * Math::PI)
    context.fill

    # surface.write_to_png("hinomaru.png") # ファイルへ出力

    # 文字列として出力
    s = StringIO.new
    surface.write_to_png(s)
    send_data(s.string, :type => 'image/png')
  end

environment.rb では次を追加しておく。
require 'gruff'
require 'cairo'
require 'stringio'

| | コメント (0) | トラックバック (0)

2008-06-08

windows の ini file 形式モドキの処理

windows の ini file 形式モドキ を読み込むクラスを作った。
http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/tools/inifile/?root=ruby-xbrl
ついでに hoe をつかって packaging をする練習をしてみている。

$ rake spec で 単体テスト、カバレージ測定ができるようになっている。
tools/inifie/lib/Inifile.rb     // クラス本体
               sample/samole.rb   // 使用例
               spec/*.rb          // テストケース

使用例はこんな感じ。

$ cat carrot.ini
[.carrot]
  CARROT_VER = "1.9.7"
  SCRIPT_ENCODING = "utf-8"
  SERVER_ACCESSOR = "s"

[SMARTY]
  TEMPLATE_ENCODING = "utf-8"

[FEED]
  CLASS = "Document"
  MESSAGE = "メッセージ"

$ cat sample.rb
#
#  sample for Inifile.
#

require '../lib/Inifile'
require 'pp'
require 'yaml'

$KCODE ="utf8"

ini = Inifile.new
settings = ini.parse_file('carrot.ini')
pp settings

ini.write(settings)
puts settings.to_yaml

$ ruby sample.rb
{"FEED"=>[{:CLASS=>"\"Document\""}, {:MESSAGE=>"\"メッセージ\""}],
"SMARTY"=>[{:TEMPLATE_ENCODING=>"\"utf-8\""}],
".carrot"=>
  [{:CARROT_VER=>"\"1.9.7\""},
   {:SCRIPT_ENCODING=>"\"utf-8\""},
   {:SERVER_ACCESSOR=>"\"s\""}]}
[FEED]
  CLASS = "Document"
  MESSAGE = "メッセージ"

[SMARTY]
  TEMPLATE_ENCODING = "utf-8"

[.carrot]
  CARROT_VER = "1.9.7"
  SCRIPT_ENCODING = "utf-8"
  SERVER_ACCESSOR = "s"

---
FEED:
- :CLASS: "\"Document\""
- :MESSAGE: "\"\xE3\x83\xA1\xE3\x83\x83\xE3\x82\xBB\xE3\x83\xBC\xE3\x82\xB8\""
SMARTY:
- :TEMPLATE_ENCODING: "\"utf-8\""
.carrot:
- :CARROT_VER: "\"1.9.7\""
- :SCRIPT_ENCODING: "\"utf-8\""
- :SERVER_ACCESSOR: "\"s\""
ntsitm302201:~/work/src/netbeans60/Edinet/tools/inifile/sample kato$

" で囲んだ文字の扱いとかをもう少し工夫する余地がある...

| | コメント (0) | トラックバック (0)

2008-06-07

ActiveRecord を 単独で使う例

http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/tools/ar/?root=ruby-xbrl
として,
 ActiveRecord を 単独で使う例
を置きました。
これは、EDINET データを RDB に保存するための練習コードです。
(java で RDB にアクセスするのに比べて簡単だなぁ)

| | コメント (0) | トラックバック (0)

2008-05-26

github, heroku.com にアカウントをつくってみた(その3)

2 つの rails の plugin をいれることで、認証 (メールでの activation をする)の実装も出来た。
  acts_as_state_machine
  restful_authentication/

ログイン画面、ユーザー登録画面、activation メール、ユーザーデータベースの様子
を以下に示そう。
TodosloginTodosregist Todosmail_2 Ttodousers

ユーザーデータベースの画面は SQLite Manager という firefox 拡張機能のものだ。(とても便利な拡張機能!)
status の列がユーザー登録時は "pending"  になるが、 activate をすると、"active" になる。

いまは local マシンから、 nifty の smtp を使ってメールを出している。
heroku で稼働させる際の smtp 関係の設定を調査中。
# 数日中に smtp 関係の設定を yaml で設定するようにした上で sourcefoge.jp (ruby-xbrl) にソースを commit する予定。

| | コメント (0) | トラックバック (0)

2008-05-24

github, heroku.com にアカウントをつくってみた(その2)

web 上の情報を基に heroku 上のアプリ作る事ができた。
// 編集は ローカルマシン上の netbeasn で行い、最後に heroku に git push した。

- http://webos-goodies.jp/archives/51287729.html
> > ブラウザで Ruby on Rails 開発! Heroku を使ってみよう - WebOS Goodies
にある例を試した。
todo リストアプリの作成と、それに pagenate 処理を追加するものだ。

さらに
 簡単な validation の追加と エラーメッセージを gettext で日本語にした。

作成したアプリのスクリーンショットを示す。
Katoysamole01_2
Katoysample02

heroku, git, ローカルでの netbeasn による開発 の組み合わせ という開発は面白い。

次は 認証機能を付けることを予定している。
それができたら、heroku 上のアプリをprivate から public に変更して公開する。

また、ソースコードを github に置こうと考えている。

paginate 処理も、エラーメッセージの日本語化の僅かな変更で出来るのは素晴らしい。
xfy/xvcd の開発にもこういった環境が欲しいものだ。

| | コメント (0) | トラックバック (0)

github, heroku.com にアカウントをつくってみた

- http://blog.tkmr.org/tatsuya/show/419-rails-heroku-com-git
> > Railsホスティングサービスheroku.comとソース管理Git

この記事に刺激され、にアカウントをつくってみた。

http://github.com は、 git のホスティングサービスだ。
- http://d.hatena.ne.jp/keyword/GitHub
> > GitHubとは - はてなダイアリー

http://heroku.com は、Firefox 上で Ruby on Rails 統合開発環境を実現した Web アプリケーションだ。
- http://webos-goodies.jp/archives/51287729.html
> > ブラウザで Ruby on Rails 開発! Heroku を使ってみよう - WebOS Goodies

自作のソースの公開や、履歴管理に使っていこうと思う。

| | コメント (0) | トラックバック (0)

2008-05-19

書籍:Practical Ruby Projects

本屋で を立ち読みした。
  Practical Ruby Projects: Ideas for the Eclectic Programmer
  http://www.apress.com/book/view/159059911X

なかなか面白そうな topic がある。
 最初の章は、 ruby で music として midi をあつかっている。
  最後のほうでは、 lisp インタープリタを作ってるし。

本は買わず、web で sample code を download してみた。
  http://www.apress.com/book/downloadfile/3879

| | コメント (0) | トラックバック (0)

2008-05-18

バランスシートの簡易グラフ表示

財務諸表の中の1つのバランスシートのおおまかな様子を図示する
プログラムを試作しました。
これは [[流動資産、固定資産], [流動負債、固定負債、資金]]をパーセント表示するものです。
出力書式は png とsvg です。
Bs_3

画像生成には cairo ライブラリーを利用しました。(sudo gem install cairo でインストールすること)

ファイナンス系の web ページ, web-service や EDINET から バランスシートの上記の値を取得することは簡単だ。それらを図示するのに使おうと思っています。

以下の ruby でのソースコードを示します。

$ cat graph_bs.rb
# See http://jp.rubyist.net/magazine/?0019-cairo
#     http://d.hatena.ne.jp/miyamuko/20070518/p1
#     http://ashitani.jp/wiki/index.rb?p=rcairo

# 2008-05-18 katoy

require "rubygems"
require 'cairo'

def draw(surface, graph_data)
  context = Cairo::Context.new(surface)

  width = graph_data[:width] * 1.0
  height = graph_data[:height] * 1.0
  font_size = graph_data[:font_size] * 1.0
  offset_x = font_size * 1.5
  offset_y = font_size * 0.5
  context.set_font_size(font_size)
  context.select_font_face("Sans")

  colors = [
    [[0.0, 0.9, 0.0], [0.0, 0.9, 0.9], [0.9, 0.0, 0.0]],
    [[0.9, 0.0, 0.0], [0.9, 0.9, 0.0], [0.9, 0.9, 0.9]],
    [[0.0, 0.9, 0.0], [0.5, 0.9, 0.5], [0.5, 0.9, 0.0]],
  ]
  col_num = graph_data[:data].size

  graph_data[:data].each_with_index do |col, i|
    total = col.to_a.inject(0){|t, a| t += a}
    sub_total = 0
    col.each_with_index do |val, j|

      d0 = 1.0 * sub_total / total * height
      d1= 1.0 * (sub_total + val) / total * height
      w0 = 1.0 * width / col_num * i
      w1 = 1.0 * width / col_num * (i + 1)

      context.fill do
        color = colors[ i % 3][j]
        context.set_source_rgb(color[0],color[1],color[2])
        context.rectangle(w0.round, d0.round, w1.round, d1.round)
      end

      context.stroke do
        context.set_source_rgb(0, 0, 0)  # black
        context.set_line_width(1)
        context.move_to(w0, d1)
        context.line_to(w1, d1)
      end
      # text
      context.set_source_rgb(0, 0, 0)  # black
      p = 100.0 * val / total
      c = height * 0.5 * (sub_total + (sub_total + val)) / total
      w = 0.5 * (w0 + w1)
      context.move_to(w.round - offset_x, c + offset_y)
      text = "#{"%.1f" % p} %"
      context.show_text(text)

      sub_total += val
    end
  end

  context.set_source_rgb(0, 0, 0)  # black
  context.set_line_width(1)
  # vertical line
  context.stroke do
    context.move_to(0, 0)
    context.line_to(0, height)

    (1..col_num).each do |i|
      w0 = 1.0 * width / col_num * i
      context.move_to(w0.round - 1, 0)
      context.line_to(w0.round - 1, height)
    end
  end

  # horizontal line
  context.stroke do
    context.move_to(0, 0)
    context.line_to(width, 0)
  end
end

def output(graph_data, file_name)
  width = graph_data[:width] * 1.0
  height = graph_data[:height] * 1.0

  # PNG 出力
  format = Cairo::FORMAT_ARGB32
  Cairo::ImageSurface.new(format, width, height) do |surface|
    draw(surface, graph_data)
    surface.write_to_png("#{file_name}.png")
  end

  # SVG 出力
  Cairo::SVGSurface.new("#{file_name}.svg", width, height) do |surface|
    draw(surface, graph_data)
  end
end

width = 100
height = 120
font_size = 10
rs = 100  # 流動資産の金額
ks =  60  # 固定試算の金額
rf =  70  # 流動負債の金額
kf =  50  # 固定負債の金額
si = (rs + ks) - (rf + kf)  # 残りは資金

graph_data = {:width => width, :height => height, :font_size => 12,
              :data => [[rs, ks], [rf, kf, si]]}

output(graph_data, "bs")

| | コメント (0) | トラックバック (0)

2008-05-16

ruby の Hash の初期値

俺 は、今日 こんなコードを書いたのだ。
* a = {}
  . . .
  a["new] += 1

ああ、でも これは実行時にエラーになる。
"nil に対しては += 1 は出来ない!" というエラーだ。
新しい key については a[key] は 0 でなく、nil  なのだ。

そして、エラーを回避するために、次のようにした。
* a = {}
   . . .
   a["new"] ||= 0
   a["new] += 1
でも 代入文が2つ続いているようにみえるのは気持ちが悪い!

http://q.hatena.ne.jp/1195489904
では Hash の初期値について、同様の悩みを持った方の質疑のページがある。
そこでは 次のような書き方が記されている。

* a = {}
  x="new"
  a[x]=(a[x]?a[x]+=1:1)

* a=Hash.new(0)
  a["new"]+=1

* a={}
  a.default = 0  #keyに対応するvalueが無いときの初期値を0にする
  a["new"]+=1

* a = {}
  a["new"] ||= 0  #a["new"] = a["new"] ? 0 と同じ。nilなら0にする
  a["new"] += 1

他にはどんなものがあるんだろう。
そして、ruby らしいエレガントな書き方は?

http://blog.livedoor.jp/takaaki_bb/archives/50607824.html
ここにも同じことが述べられているなぁ

戯れに
a = {}(0) なんてしてみたが、シンタックスエラーになった。

# 俺は 空の Hash をつくるのは Hash.new よりは {} と書きたいのだ!

| | コメント (0) | トラックバック (0)

2008-05-10

MSN サイトのファイナンシャルデータを ruby でアクセス(その2)

プログラムを更新しました。

- http://sourceforge.jp/forum/forum.php?forum_id=14849
> > MSN マネーサイトの財務諸表ページから csv を得る

ネストしている項目の処理が変だったのも修正しました。

| | コメント (0) | トラックバック (0)

2008-05-08

MSN サイトのファイナンシャルデータを ruby でアクセス

http://jp.moneycentral.msn.com/investor/invsub/results/statemnt.aspx?Symbol=JP:7974&lstStatement=Balance&stmtView=Ann
任天堂の財務諸表の概要が MSN のファイナンシャルサイトで閲覧できる。

これは HTML なので、ruby でアクセスして、 cvs 形式で保存するようにしてみた。
ソースは subversion に commit してある。(このプログラムは EDINT, XBRL データを扱っていないけど...)
http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/tools/msn-finance.rb?rev=22&root=ruby-xbrl&view=markup

最近、財務諸表の読み方の本が沢山 出版されている。
MSN サイトのデータを CSV にすれば、Excel の取り込んだりすることが可能となる。
そうして、書籍を読みながら沢山の企業のデータで手を使って分析をしてみると、知識が身につくと思う。

任天堂のデータを csv にして、それを NeoOffice で読み込んだスクリーンショットを示す。

Msnfinance01 Msnfinance02

| | コメント (0) | トラックバック (0)

2008-05-07

google chart api のサンプル

- http://gchartrb.rubyforge.org/
> gchartrb is a Ruby wrapper around the Google Chart API, located at code.google.com/apis/chart/
というものがあるのを知った。

これは http://code.google.com/intl/ja/apis/chart/ を ruby から使うための wrapper だ。

rdoc 中のサンプルコードをまとめて走らせてみた。
出力結果は、各グラフ生成要求の url だ。

$ cat sample00.rb
# See http://gchartrb.rubyforge.org/

require 'rubygems'
require 'google_chart'

# ---------------------
bc = GoogleChart::BarChart.new('800x200', "Bar Chart", :vertical, false)
bc.data "Trend 1", [5,4,3,1,3,5], '0000ff'

# Fill Area (Multiple Datasets)
lc = GoogleChart::LineChart.new('320x200', "Line Chart", false) do |lc|
lc.show_legend = false
lc.data "Trend 1", [5,5,6,5,5], 'ff0000'
lc.data "Trend 2", [3,3,4,3,3], '00ff00'
lc.data "Trend 3", [1,1,2,1,1], '0000ff'
lc.data "Trend 4", [0,0,0,0,0], 'ffffff'
lc.fill_area '0000ff',2,3
lc.fill_area '00ff00',1,2
lc.fill_area 'ff0000',0,1
end
puts lc.to_url
puts

# Fill Area (Single Dataset)
lc = GoogleChart::LineChart.new('320x200', "Line Chart", false) do |lc|
lc.show_legend = false
lc.data "Trend 1", [5,5,6,5,5], 'ff0000'
lc.fill_area 'cc6633', 0, 0
end
puts lc.to_url
puts

# ---------------------
flc = GoogleChart::FinancialLineChart.new do |chart|
chart.data "", [3,10,20,37,40,25,68,75,89,99], "ff0000"
end
puts flc.to_url
puts

# ---------------------
# Line Chart
lc = GoogleChart::LineChart.new('320x200', "Line Chart", false)
lc.data "Trend 1", [5,4,3,1,3,5,6], '0000ff'
lc.data "Trend 2", [1,2,3,4,5,6], '00ff00'
lc.data "Trend 3", [6,5,4,3,2,1], 'ff0000'
lc.axis :y, :range => [0,6], :color => 'ff00ff', :font_size => 16, :alignment => :center
lc.axis :x, :range => [0,6], :color => '00ffff', :font_size => 16, :alignment => :center

# Line XY Chart
lcxy = GoogleChart::LineChart.new('320x200', "Line XY Chart", true)
lcxy.data "Trend 1", [[1,1], [2,2], [3,3], [4,4]], '0000ff'
lcxy.data "Trend 2", [[4,5], [2,2], [1,1], [3,4]], '00ff00'
puts lcxy.to_url
puts

# ---------------------
pc = GoogleChart::PieChart.new("600x300", "Food and Drinks Consumed Christmas 2007")
pc.data "Egg nog", 10, '00AF33'
pc.data "Christmas Ham", 20, '4BB74C'
pc.data "Milk (not including egg nog)", 8, 'EE2C2C'
pc.data "Cookies", 25, 'CC3232'
pc.data "Roasted Chestnuts", 5, '33FF33'
pc.data "Chocolate", 3, '66FF66'
pc.data "Various Other Beverages", 15, '9AFF9A'
pc.data "Various Other Foods", 9, 'C1FFC1'
pc.data "Snacks", 5, 'CCFFCC'
puts pc.to_url
puts

# ---------------------
sc = GoogleChart::ScatterChart.new('320x200',"Scatter Chart")
sc.data "Scatter Set", [[1,1,], [2,2], [3,3], [4,4]]
sc.point_sizes [10,15,30,55]
puts sc.to_url
puts

# ---------------------
vd = GoogleChart::VennDiagram.new("320x200", 'Venn Diagram')
vd.data "Blue", 100, '0000ff'
vd.data "Green", 80, '00ff00'
vd.data "Red", 60, 'ff0000'
vd.intersections 30,30,30,10
puts vd.to_url
puts

ruby でも、jruby でも動作する。ここでは jruby で走らせてみた。
$ jruby sample00.rb
http://chart.apis.google.com/chart?chs=320x200&cht=lc&chco=ff0000,00ff00,0000ff,ffffff&chd=s:yy9yy,eeoee,KKUKK,AAAAA&chm=b,0000ff,2,3,0|b,00ff00,1,2,0|b,ff0000,0,1,0&chtt=Line+Chart

http://chart.apis.google.com/chart?chs=320x200&cht=lc&chco=ff0000&chd=s:yy9yy&chm=B,cc6633,0,0,0&chtt=Line+Chart

http://chart.apis.google.com/chart?chs=100x15&cht=lfi&chco=ff0000&chd=s:BGMWYPpu29

http://chart.apis.google.com/chart?chs=320x200&cht=lxy&chco=0000ff,00ff00&chd=s:Pet9,MYkw,9ePt,9YMw&chdl=Trend+1|Trend+2&chtt=Line+XY+Chart

http://chart.apis.google.com/chart?chs=600x300&cht=p&chco=00af33,4bb74c,ee2c2c,cc3232,33ff33,66ff66,9aff9a,c1ffc1,ccffcc&chd=s:YwT9MHkVM&chl=Egg+nog|Christmas+Ham|Milk+(not+including+egg+nog)|Cookies|Roasted+Chestnuts|Chocolate|Various+Other+Beverages|Various+Other+Foods|Snacks&chtt=Food+and+Drinks+Consumed+Christmas+2007

http://chart.apis.google.com/chart?chs=320x200&cht=s&chd=s:Pet9,Pet9,LQh9&chtt=Scatter+Chart

http://chart.apis.google.com/chart?chs=320x200&cht=v&chco=0000ff,00ff00,ff0000&chd=s:9wkSSSG&chdl=Blue|Green|Red&chtt=Venn+Diagram

この blog に引用した 出力 URL には hyperlink を張っておいた。
クリックすると生成されたグラフが瞬時に表示される。

最後のベン図のイメージだけはスクリンショットを示そう。

Ven


出力形式をSVG にしたような類似サービス or API が xfy にもあるとよいと思う。

| | コメント (0) | トラックバック (1)

2008-05-06

ruby で csv, openoffice spreadsheet、google spreadsheets を扱う

roo というライブラリーを見つけた。
- http://roo.rubyforge.org/

> This gem allows you to access the content of
>   Open-office spreadsheets (.ods)
>   Excel spreadsheets (.xls) and
>   Google (online) spreadsheets

sudo gem install roo でインストールできる。(現在のversion は 0.9.4)

サンプルのプログラムの例から2つの例を示す。

$ cat sample00.rb
# See http://roo.rubyforge.org/

require 'rubygems'
require 'roo'

HOURLY_RATE = 123.45

# You can get data from  http://spreadsheets.google.com/pub?key=p7dQJMn9Y4pU59GOICSvNTA
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
4.upto(oo.last_row) do |line|
  date       = oo.cell(line,'A')
  start_time = oo.cell(line,'B')
  end_time   = oo.cell(line,'C')
  pause      = oo.cell(line,'D')
  sum        = (end_time - start_time) - pause
  comment    = oo.cell(line,'F')
  amount     = sum * HOURLY_RATE
  if date
    puts "#{date}\t#{sum}\t#{amount}\t#{comment}"
  end
end

puts oo.to_csv
puts oo.to_csv('out.csv')

ここでは simple_spreadsheet.ods を読み込んで、 csv 形式で標準出力 と、ファイル out.csv に出力している。
(simple_spreadsheet.ods は http://spreadsheets.google.com/pub?key=p7dQJMn9Y4pU59GOICSvNTA に upload してある。
このデータは NeoOffice で作った。)

google spreadsheet は形式なら write することもできる。

http://spreadsheets.google.com/pub?key=p7dQJMn9Y4pVz5Q1n2n_sPQ

を share 公開してある。これを ruby スクリプトで編集する。

$ cat write_me.rb

# See /opt/local/lib/ruby/gems/1.8/gems/roo-0.9.4/examples/write_me.rb

require 'rubygems'
require 'roo'
require 'yaml'

config = YAML.load_file('../config.yaml')
username = config["username"]
password = config["password"]

#-- create a new spreadsheet within your google-spreadsheets and paste
#-- the 'key' parameter in the spreadsheet URL

#-- See http://spreadsheets.google.com/pub?key=p7dQJMn9Y4pVz5Q1n2n_sPQ
key="p7dQJMn9Y4pVz5Q1n2n_sPQ"

MAXTRIES = 10
print "what's your name? "
my_name = gets.chomp
print "where do you live? "
my_location = gets.chomp
print "your message? (if left blank, only your name and location will be inserted) "
my_message = gets.chomp

spreadsheet = Google.new(key, username, password)
spreadsheet.default_sheet = 'Sheet1'

success = false
MAXTRIES.times do
  col = rand(10)+1
  row = rand(10)+1
#  if spreadsheet.empty?(row,col)
    if my_message.empty?
      text = Time.now.to_s+" "+"Greetings from #{my_name} (#{my_location})"
    else
      text = Time.now.to_s+" "+"#{my_message} from #{my_name} (#{my_location})"
    end
    spreadsheet.set_value(row,col,text)
    puts "message written to row #{row}, column #{col}"
    success = true
    break
#  end
  puts "Row #{row}, column #{col} already occupied, trying again..."
end
puts "no empty cell found within #{MAXTRIES} tries" if !success

ここでは、google アクセスのための username, password は yaml ファイルから読み込むようにした。
#  GOOGLE_MAIL, GOOGLE_PASSWORD を環境変数に設定する方法でも良いらしい。

現状では OpenOffice/Excel データは read はできるが write はできない。
write できるようになればもっと便利になるなぁ。

| | コメント (0) | トラックバック (0)

2008-05-04

ferret を使いたい (その2)

ferret の使い方のサンプルとして、次のものが役立った。
- http://zenmachine.wordpress.com/ferret-indexing-with-ruby/
> > Ferret - Indexing with Ruby « the zen machine

あるフォルダー以下のファイルの全文検索のための index 作成コマンドと、サーチコマンド
の例が示されている。

40 M 程度のエリアに対して行ってみたが、index を作るのは時間がかかるが、検索は本当に速かった。

Reils で Ferret を利用するplugin があるようだ。
- http://www.railsenvy.com/2007/2/19/acts-as-ferret-tutorial
> > Rails Envy: Acts_As_Ferret Tutorial

Acts_as_Solor なんてのもあるらしい。
- http://calebjones.blogspot.com/2007/03/benchmarking-actsasferret-and.html
> > Random Thoughts: Benchmarking acts_as_ferret and acts_as_solr

| | コメント (0) | トラックバック (0)

2008-05-03

ruby で XBRL データの処理をしよう(その4)

科目名とその値の一覧の出力だけでなく、
EDINET の タクソノミ設計 CSV ファイルに記載されている財務表の情報に沿った出力
もするようにしました。

- http://sourceforge.jp/projects/ruby-xbrl
http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/lib/main.rb?rev=21&root=ruby-xbrl&view=log

$ ruby main.rb --help
Usage: main.rb [options] xbrl
    -s, --sheet                      output sheet form.
    -i, --item                       output all instance items. (default)
        --lang=[en|ja]               lang for label-name. (default=ja)

$ time ruby main.rb ../sample/X99006-000/jpfr-ssr-X99006-000-2008-09-30-02-2008-12-26.xbrl > 1.csv

real    0m6.705s
user    0m5.286s
sys     0m0.347s

この出力結果の csv をNeoOffice で読み込んだときのスクリーンショットを示します。
2008050300

sheet 形式の出力は、概要をつかむのに向いています。
item 形式で出力は、スプレッドシートソフト上で独自の分析を行う場合などに向いています。

今は、毎回 csv ファイルを読み込んでいますが、 読み込み結果を マーシャリング (dump/load) するようにすれば、もっと速度があがると思います。
# 一度 処理をした xbrl インスタンスデータもその処理結果を マーシャリング (dump/load) することを考えています。

次は、gruff をつかってグラフ化することを行う予定です。

| | コメント (0) | トラックバック (0)

ferret を使いたい

Rubyクックブック で Ferret という ライブラリーを知った。(gem で install できる)

- http://rubyforge.org/projects/ferret/

> ...
Ferret is a high-performance, full-featured text search engine library written entirely in pure Ruby (with an optional C extension). It is inspired by the Java Lucene Project.
> ...

でも クックブック中のコードは動作しない。どう書けばよいかを web で検索中...

google で "英和 ferret" すると

>【名】
>   1. 《動物》フェレット、白イタチ◆ウサギなどを穴から“狩り立てる”のに使う
>   2. 捜索者{そうさく しゃ}、探索者{たんさく しゃ}、探偵

とのこと。名前の付け方が上手だなぁ。

書籍も出ている。(発売日: 2008/03)
http://www.amazon.co.jp/Ferret-David-Balmain/dp/0596519400/


| | コメント (0) | トラックバック (0)

2008-04-27

ruby で "哲学者の食事"

http://www2.ruby-lang.org/ja/20020315.html
にある "哲学者の食事" で、 デッドロックについて少し実験した。

上ページにあるコードは すでに デッドロック を回避するようになっている。
そこで、わざと デッドロック がおこるように編集した。

    $forks[(n+1)%N].lock   # cause deadlock  <--  追加
#    if not $forks[(n+1)%N].try_lock                  <-- 元のコードをコメントに
#      $forks[n].unlock    # avoid deadlock
#      next
#    end

これで デッドロック がおこるはずだが、実際に走らせても なかなか デッドロックにならない。

そこで次の様に 食事時間の調整、2番目のフォークを取るまえに sleep を入れるなどして、
デッドロックを起こりやすくしてみた。

$ cat DiningPhilosophers.rb

# See http://www2.ruby-lang.org/ja/20020315.html
#
# The Dining Philosophers - thread example
#
# o: thinking, *: eating
# -: free. |: using

require "thread"

N = 5    # number of philosophers

def wait(n = 20)
  sleep rand(n) / 10.0
end

def think
  wait(10)
end

def eat
  wait(10)
end

def philosopher(n)
  while true
    think
    $forks[n].lock

    wait(20)

    $forks[(n+1)%N].lock   # cause deadlock
#    if not $forks[(n+1)%N].try_lock
#      $forks[n].unlock    # avoid deadlock
#      next
#    end

    $state[n*2] = ?|;
    $state[(n+1)%N*2] = ?|;
    $state[n*2+1] = ?*;

    print $state, "\n"
    eat

    $state[n*2] = ?-;
    $state[(n+1)%N*2] = ?-;
    $state[n*2+1] = ?o;
    print $state, "\n"

    $forks[(n+1)%N].unlock
    $forks[n].unlock

  end
end

$forks = []
for i in 0..N-1
  $forks[i] = Mutex.new
end
$state = "-o"*N
print $state, "\n"

for i in 0..N-1
  Thread.start{ philosopher(i) }
  sleep 0.3
end
sleep

 


これで デッドロックが起こるタイミングはまちまちだが、確実に デッドロック が起こるようになった。
その上で、もとのコードにあったデッドロック回避コードに戻すと、デッドロックが起こらなくなった。

でも、ほんとうに完全に デッドロック回避できているかの確信は持てない。
なぜなら、最初の デッドロックを起こすように書き換えただけでも、デッドロックを起こさせることはできなかったのだ。
デッドロックの発生回避を確実に確認するよい方法はないのだろうか?

# この実験は ruby 1.8.6, jruby 1.1.1 で行った

| | コメント (0) | トラックバック (1)

2008-04-20

ruby で XBRL データの処理をしよう(その3)

ruby-XBRL に次の機能を実装したものを commit しました。
- http://sourceforge.jp/forum/forum.php?forum_id=14656
> > SourceForge.JP: EDINETインスタンス -> CSV 機能を実装

EDINET のサンプルインスタンスを指定すると、科目、コンテキスト毎の値が CSV 書式で出力されます。

$ ruby main.rb
../sample/X99002-000/jpfr-q1r-X99002-000-2008-06-30-01-2008-08-08.xbrl
, , CurrentQuarterConsolidatedInstant, Prior1YTDConsolidatedDuration, CurrentYTDConsolidatedDuration, Prior1QuarterConsolidatedInstant, DocumentInfo, Prior1YearConsolidatedInstant, Prior2YearConsolidatedInstant
, , 2008-06-30, 2007-04-01 - 2007-06-30, 2008-04-01 - 2008-06-30, 2007-06-30, 2008-08-08, 2008-03-31, 2007-03-31
jpfr-t-cte:OrdinaryIncome, 経常利益, , 15000000000, 15000000000, , , ,
jpfr-t-cte:IntangibleAssets, 無形固定資産, 5000000000, , , , , 5000000000,
jpfr-t-cte:CostOfSales, 売上原価, , 70000000000, 70000000000, , , ,
jpfr-t-cte:AllowanceForDoubtfulAccountsIOAByGroup, 貸倒引当金, -1000000000, , , , , -1000000000,
jpfr-t-cte:NotesAndAccountsPayableTrade, 支払手形及び買掛金, 2000000000, , , , , 2000000000,
... 以下省略 ...

出力された csv を NeoOffice で表示させたときのスクリーンショットを示します。
Rubyxbrl2008042001 Rubyxbrl2008042002 Rubyxbrl2008042003_2
この版では、インスタンスファイル中の科目順で表示していますが、
EDINET の業種別タクソノミ設計書に記載されている 表情報に従って順番や合計関係を加味して出力すれば、
より財務諸表に近い出力を得る事も可能なはずです。

| | コメント (0) | トラックバック (0)

2008-03-29

XBRL を ruby ソースに変換するってどう?

XBRL データを ruby ソースに変換してしまうと良いのではないか? と考え始めている。
ともかく XBRL データを DOM で扱うのは面倒なのだ!

XBRLの本家サイトにあるサンプルデータを使って、考えていることを以下に示していこう。
# ここでは手書きで ruby コードを起こしているが、本当はプログラムで自動変換をさせることになる。

使用したデータは
- http://www.xbrl.org/SpecRecommendations/
   2.1 Conformance Suite (Candidate Recommendation 3)   March 5, 2007
   Full Test Suite zip ( http://www.xbrl.org/2007/XBRL-CONF-CR3-2007-03-05.zip )
   XBRL-CONF-CR3-2007-03-05/Common/397-00-ConsistentInstance-valid.xbrl
                                   397-ABC.xsd
                                   397-ABC-calculation.xml

この3ファイルに相当する ruby ソースをつくる。
  1. XBRl 汎用のクラス定義
  2. 397-ABC.xsd, 397-ABC-calculation.xml に対応するクラス定義
  3. 397-00-ConsistentInstance-valid.xbrl の対応するクラス定義

1. XBRl 汎用のクラス定義

$ cat XbrlBase.rb

#----------------------
class XbrlElement
  attr_reader :name, :id, :type, :substutionGroup, :nillable, :periodType

  def initialize(name, id, type, substutionGroup, nillable, periodType)
    @name = name
    @id = id
    @type = type
    @substutionGroup = substutionGroup
    @nillable = nillable
    @periodType = periodType
  end
end

class XbrlCalculation
  attr_reader :arcs

  def initialaize(arcs)
    @arcs = arcs
  end
end

class CalculationArc
  attr_reader :arcrole, :from, :to, :weight
  def initialaize(arcsrole, from, to, weight)
    @arcsrole = arcrole
    @from = from
    @to = to
    @weight = weight
  end
end

class XbrlInsntance
  attr_reader :xsd
  attr_reader :items, :contexts, :units

  def initialize(items, contexts, units)
    @items = items
    @contexts = contexts
    @units = units
  end
end
#--- End of File ---


2. 397-ABC.xsd, 397-ABC-calculation.xml に対応するクラス定義

まず、xsd ファイルと xml を示す。
$ cat 397-ABC.xsd
<?xml version="1.0"?>
<!-- XBRL 2.1 Tests -->
<!-- Copyright 2003 XBRL International Inc.  See www.xbrl.org/legal.  All Rights Reserved. -->
<schema targetNamespace="http://xbrl.example.com/397/ABC"
  xmlns:abc="http://xbrl.example.com/397/ABC"
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:xbrli="http://www.xbrl.org/2003/instance"
  xmlns:link="http://www.xbrl.org/2003/linkbase"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  elementFormDefault="qualified">

        <import namespace="http://www.xbrl.org/2003/instance"
          schemaLocation="../lib/xbrl-instance-2003-12-31.xsd"/>

  <annotation>
    <appinfo>
      <link:linkbaseRef xlink:type="simple"
                        xlink:arcrole="http://www.w3.org/1999/xlink/properties/linkbase"
                        xlink:href="397-ABC-calculation.xml" />      
    </appinfo>
  </annotation>

        <element  name="A"
            id="A"
            type="xbrli:monetaryItemType"
            substitutionGroup="xbrli:item"
            nillable="true"
            xbrli:periodType="instant"/>

        <element  name="B"
            id="B"
            type="xbrli:monetaryItemType"
            substitutionGroup="xbrli:item"
            nillable="true"
            xbrli:periodType="instant"/>

        <element  name="C"
            id="C"
            type="xbrli:monetaryItemType"
            substitutionGroup="xbrli:item"
            nillable="true"
            xbrli:periodType="instant"/>

</schema>

$ cat 397-ABC-calculation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2005 XBRL International Inc.  See www.xbrl.org/legal.  All Rights Reserved. -->
<linkbase xmlns="http://www.xbrl.org/2003/linkbase"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          >

        <calculationLink xlink:type="extended" xlink:role="http://www.xbrl.org/2003/role/link">

                <loc xlink:type="locator" xlink:href="397-ABC.xsd#A" xlink:label="summationItem" />
                <loc xlink:type="locator" xlink:href="397-ABC.xsd#B" xlink:label="contributingItem" />
                <loc xlink:type="locator" xlink:href="397-ABC.xsd#C" xlink:label="contributingItem" />

    <!-- A = B + C -->
                <calculationArc xlink:type="arc"
                    xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item"
                    xlink:from="summationItem"
                    xlink:to="contributingItem"
                    weight="1"/>

  </calculationLink>

</linkbase>

これに対応する ruby ソースを次に示す。
$ cat ABC_xsd.rb
require 'XbrlBase'

#-------- 397-ABC.xsd ----------
class A < XbrlElement
  def A.inst
    A.new("A", "A", :monetaryItemType, :item, true, :instant)
  end
end

class B < XbrlElement
  def B.inst
    B.new("B", "B", :monetaryItemType, :item, true, :instant)
  end
end

class C < XbrlElement
  def C.inst
    ans = C.new("C", "C", :monetaryItemType, :item, true, :instant)
  end
end

#----- 397-ABC-calculation.xml ---
class ABC_Calc < XbrlCalculation
  def inst
    arc = CalculationArc.new("http://www.xbrl.org/2003/arcrole/summation-item",
                             :summationItem, :contributingItem, 1)
    arcs = [arc]
    ABC_Calc.new(arcs)
  end
end

3. 397-00-ConsistentInstance-valid.xbrl の対応するクラス定義

まず、XBRL データを示す。
$ cat 397-00-ConsistentInstance-valid.xbrl
<?xml version="1.0"?>
<!-- Copyright 2005 XBRL International Inc.  See www.xbrl.org/legal.  All Rights Reserved. -->
<xbrl xmlns="http://www.xbrl.org/2003/instance"
      xmlns:link="http://www.xbrl.org/2003/linkbase"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:abc="http://xbrl.example.com/397/ABC"
      xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
      >

        <link:schemaRef xlink:href="397-ABC.xsd" xlink:type="simple"/>

  <!-- A = B + C -->
        <abc:B contextRef="c1" unitRef="u1" precision="INF">1</abc:B>
        <abc:C contextRef="c1" unitRef="u1" precision="INF">2</abc:C>
        <abc:A contextRef="c1" unitRef="u1" precision="INF">3</abc:A>

        <context id="c1">
                <entity>
                        <identifier scheme="www.example.com">example</identifier>
                </entity>
                <period>
                        <instant>2003-03-31</instant>
                </period>
        </context>

        <unit id="u1">
                <measure>iso4217:USD</measure>
        </unit>

</xbrl>

これに対応する ruby ソースを示す。
$ cat sample01-instance.rb

require 'ABC_xsd'
require 'pp'

#-------- 397-00-ConsistentInstance-valid.xbrl -----------
class Sample01Instance < XbrlInsntance
  @links = [:calc => ABC_Calc.new]

  def Sample01Instance.inst
    items = []
    items << {:element => B.inst, :val =>1, :presoson =>:INF , :contextRef => :c1, :unitRef =>:u1}
    items << {:element => C.inst, :val =>2, :presoson =>:INF , :contextRef => :c1, :unitRef =>:u1}
    items << {:element => A.inst, :val =>3, :presoson =>:INF , :contextRef => :c1, :unitRef =>:u1}

    contexts = {}
    contexts[:c1] = {:entity => "example", :period => "2003-03-31"}

    units = {}
    units[:u1] = :iso4217_USD

    Sample01Instance.new(items, contexts, units)
  end
end

#---- Main ----
if __FILE__ == $0

inst = Sample01Instance.inst

  inst.items.each {|item|
    contextRef = item[:contextRef]
    period = inst.contexts[contextRef][:period]
    puts "#{item[:element].name}   #{item[:val]} [#{period}]"
  }
end
#--- End of File ---

これには, 科目の名前、値、コンテキストを列挙するアプリも追加してある。
この最後のソースを走らせてみれば、次のようになる。

$ ruby sample01-instance.rb
B   1 [2003-03-31]
C   2 [2003-03-31]
A   3 [2003-03-31]

erb を利用するなどすれば、HTML の表形式で出力することも可能だろう。

XBRL データが表現してことを ruby クラス/オブジェクトとして表現しているので、
ruby の機能をつかって柔軟なデータ処理が可能となる。
上記 XBRL では、XBRL 共通の xsd, xml が実際には参照されているが、その 部分の ruby ソース化はしていない。
実際には、すべての xsd, xml を ruby ソースに変換する必要がある。
そうしておけば、XBRL データ中からの XBRL/EDINT の共通フィアルへ参照は、
ruby 中では 単なる require 文に置換することができる。

インスタンス編集は、ruby オブジェクトの変更と同一視することもでき、エディタでの編集補完機能や、
プログラム実行時のリフレクションなどをつかって編集をするなんてことが可能になる。

| | コメント (0) | トラックバック (0)

2008-03-25

DouKaku? の ポーカーの役判定

http://ja.doukaku.org/121/
>> ポーカーの役判定
の いろいろな言語での解答例をいろいろ読んでみている。

Scala, Haskell はなかなか短い。
prolog は思ったより長いし、なぜか理解しにくいなぁ。
java, 長過ぎる...

| | コメント (0) | トラックバック (0)

2008-03-23

google rtranslate を試した

google rtranslate という google の電子翻訳 API を試してみた。

$ sudo gem install rtranslate

コマンドラインでも動作する。
$ rtranslate -f en -t ja "i love you"

アイラブユー
$ rtranslate -f en -t ja "I love you."
あなたを愛しています。
$ rtranslate -f en -t ja "I hate you."
ママなんて大嫌いだ。

上と同じことを ruby スクリプトでも試す。
これがスクリプト。
$ cat rtrans.rb
# See http://stickstack.org/node/245
#     http://code.google.com/p/rtranslate/wiki/SupportLanguage (サポート言語一覧)

require 'rubygems'
require 'rtranslate'

puts Translate.t("japan", Language::ENGLISH, Language::JAPANESE)
puts Translate.t("I love you", Language::ENGLISH, Language::JAPANESE)
puts Translate.t("I love you.",  Language::ENGLISH, Language::JAPANESE)
puts Translate.t("I hate you.", Language::ENGLISH, Language::JAPANESE)

実行してみよう。
$ ruby rtrans.rb
日本
アイラブユー
あなたを愛しています。
ママなんて大嫌いだ。

"I hate you." の you は 何故 ママに限定される?

http://www.google.com/uds/samples/language/translate.html
として、 ajax での翻訳 API もある。

Mac での say コマンド(単語を発発音してくれる) と組み合わせて使うと面白いかもしれない。
xfy/xvcd とからの利用法も考えてみたい...

| | コメント (0) | トラックバック (0)

祝日一覧を google から得る

- http://emasaka.blog65.fc2.com/blog-entry-289.html
> > 本を読む 祝日のリストをGoogle Calendar APIを使って作る
なんて記事を読んだ。

なぜか
  REXML::XPath.match(doc, '//entry/gd:when/@startTime')
がうまく 要素のマッチしないので、
element#attribgutes を使ってデータを得るように書き換えてみた。
以下にスクリプトと実行例を示す。

$cat holiday.rb
# See http://emasaka.blog65.fc2.com/blog-entry-289.html

require 'set'
require 'date'
require 'open-uri'
require 'rexml/document'
require 'pp'

$KCODE="utf8"

HOLYDAY_URL_PTN = 'http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/full?start-min=\
%s&start-max=%s'

class HolidayList < Set
  def initialize(from, to)
    super()
    from_s = from.strftime('%F')
    to_s = to.strftime('%F')
    open(HOLYDAY_URL_PTN % [from_s, to_s]) {|io|
      doc = REXML::Document.new(io)
      REXML::XPath.match(doc, '//entry').each {|elm|
        date = REXML::XPath.match(elm, 'gd:when')[0].attributes['startTime']
        title = REXML::XPath.match(elm, 'title/text()')[0].to_s
        self << date + " " + title
      }
    }
  end
end

year = Time.new.strftime("%Y").to_i
list = HolidayList.new(Date.new(year, 1, 1), Date.new(year, 12, -1))
list.sort.each {|d| puts d}

$ ruby holiday.rb
2008-01-01 元日
2008-01-14 成人の日
2008-02-11 建国記念の日
2008-03-20 春分の日
2008-04-29 みどりの日
2008-05-03 憲法記念日
2008-05-04 国民の休日
2008-05-05 こどもの日
2008-07-21 海の日
2008-09-15 敬老の日
2008-09-23 秋分の日
2008-10-13 体育の日
2008-11-03 文化の日
2008-11-23 勤労感謝の日
2008-11-24 振替休日
2008-12-23 天皇誕生日

| | コメント (0) | トラックバック (0)

2008-03-16

ant + ruby + ERB でテキストファイル生成

可変部を含むテキストファイルを生成する例を作ってみた。
(実は xfy/xvcd, java 用のファイルを生成するための事前の準備実験です)

$ ls
build.xml  config.yaml  sample01.rb  template.txt

こんな風に実行する。
$ ant clean     // 生成ファイルを削除する
$ ant           // ファイルを生成

$ cat out .txt  // 生成したファイルの内容
# Created: 2008-03-16 08:36:03
foo {
  a = AA
  b = BB
  id = ID
  <AA>xxx/</AA>
}
#--- End of File ---

日付けが埋め込まれている。AA, BB, ID という部分も実は可変になっている。

以下に 生成テキストのテンプレート、ruby プログラム、設定ファイル、ant の build ファイルを示す。
まずは、テンプレートファイル。<%=*%> の部分が可変部だ。
$ cat template.txt
# Created: <%= Time.new.strftime('%Y-%m-%d %H:%M:%S')%>
foo {
  a = <%=a%>
  b = <%=b%>
  id = <%=_id%>
  <<%=a%>>xxx/</<%=a%>>
}
#--- End of File ---

次は変数値の設定ファイル。
$ cat config.yaml

a: AA
b: BB
_id: ID

_id は 本当は id としたいところだが、ruby の id メソッドと名前がぶつかるので _id としている。
(ここで示す例において、変数名が ruby のメソッド名と衝突する場合は_ を付けるなど、利用する側で衝突を避けるようにすることが必要。)

次が ruby スクリプト。
method_missing  を利用することで、テンプレートファイル中には
<$=変数名=> と書くだけで、それが変数値に置換されるようにしている。
(スクリプト先頭部に記してある blog 記事のアイデアを参考にした)

$ cat sample01.rb
# See http://jarp.does.notwork.org/diary/200408b.html#20040820
#          YAMLでパラメータを設定して、ERBで展開する方法を摸索。

require 'yaml'
require 'erb'
require 'pp'

module MyTemplate

  @conf = YAML.load_file('config.yaml')

  def self.run(template)
    ERB.new(template).result(binding)    
  end

  def self.method_missing(symbol, *p)
    v = @conf[symbol.to_s]
    raise "unknown symbol: '#{symbol}' #{p}"  if v == nil
    v
  end
end

# run:  ruby sample01 template.txt
template = ARGV.shift

File.open(template) { |ft|
  puts MyTemplate.run(ft.read)
}

この ruby スクリプトは、もちろん jruby でも動作する。
最後にこれらを ant で利用するようにまとめた。
$ cat build.xml
<?xml version="1.0" encoding="UTF-8" ?>

<project name="antsample" default="generate">

  <property name="dest" value="out.txt"/>

  <target name="clean">
    <delete file="${dest}"/>
  </target>

  <target name="generate" depends="clean">
    <apply executable="ruby"
           output="${dest}"
           logError="true"
           ignoremissing="false"
           failonerror="true">
      <srcfile/>
      <filelist files="sample01.rb"/>
      <arg value="template.txt"/>
    </apply>
  </target>

</project>

朱雀門の画像
2008031601

| | コメント (0) | トラックバック (0)

2008-03-15

ruby の method_missing

ruby の method_missing について勉強する機会があった。(社内での ruby 勉強会で)
- http://pub.cozmixng.org/~the-rwiki/?cmd=view;name=%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8%B8%C0%B8%EC%A4%CE%C8%E6%B3%D3%3A%3Amethod_missing
> > スクリプト言語の比較::method_missing

xfy/xvcd に ruby の "method_missing" を真似て、"tag_missing" を導入したらどんなことができるかなぁ。
templete で mathc="*" で すでに tab_missing は実現できているとも言えるけど.

次の URL は、method_missing の利用例だ。
- http://www.kanasansoft.com/weblab/2008/02/rubytypo.html
> > Rubyでtypoしたときに「ひぎぃ」と言わせる (Kanasansoft Web Lab.)

define_method というのもあるのか!
- http://d.hatena.ne.jp/h-yano/20071217/1197884795
> > HashからMethodを動的に作りたい → define_methodでいけるのね! - 今日とは違う明日

method_missing があるなら、
class_missing instance_missinng というのは考えられだろうか?
リアルな世界では、知らないことについては、
   単に "知らない" とだけ答える
   人に聞く、
   無視する,
   後で調べるためにメモだけする...
なんて対応が考えられる。
プログラム言語では、大抵は "知らない" とだけ答えるのが多い。
多くのスクリプト言語で method_missing の類似機構が用意されているのは素晴らしい。

jruby でも method_missing  は動作するので、java と組み合わせると楽しくなるかも。

| | コメント (0) | トラックバック (0)

2008-03-05

ruby + tk の練習

突然はなしが変わってRuby/Tkでアニメーションを作る話
   http://d.hatena.ne.jp/miura1729/20080302/1204443134

 

という記事のコードを基に、自分なりにすこし変形させてみた。
ビリヤードゲームでのボールの動きを真似してみた。
(下は動画ではありません、スイマセン)

20080305ball

ボールが 壁に反射した時/床を転がる時に だんだんと速度を落としていくようになっています。

$ cat ball2.rb

# See http://d.hatena.ne.jp/miura1729/20080302/1204443134

require 'rubygems'
require 'tk'
require 'singleton'

class Viewer < TkFrame
  include TkComposite

  WIDTH, HEIGHT = 512, 512

  def initialize_composite
    @frame.configure(:width=>WIDTH, :height=>HEIGHT)
    @frame.grid_propagate(false)

    @canvas = TkCanvas.new(@frame)
    @canvas.configure(:width=>WIDTH, :height=>HEIGHT)
    @canvas.grid('row'=>0, 'column'=>0, 'sticky'=>'news')
  end

  def draw_oval(x, y, r)
    TkcOval.new(@canvas, x - r, y - r, x + r, y + r, 'fill' => :red)
  end
end

class Field
  include Singleton

  def initialize
    @view = Viewer.new
    @view.pack(:expand=>true, :fill=>:both)
  end

  def draw_oval(x, y, r)
    @view.draw_oval(x, y, r)
  end
end

class Ball
  @@field = Field.instance

  def initialize(r, x, y, vx = 0, vy = 0, factorWall = 0.9, factorFloor= 0.99, minSpeed = 0.05)
    @r, @x, @y, = r, x, y
    @vx, @vy = vx, vy
    @shape = @@field.draw_oval(@x, @y, @r)
    @factorWall = factorWall
    @factorFloor = factorFloor
    @minSpeed = minSpeed
  end

  def resetV(v)
    @vx, @vy = v[0], v[1]
  end

  def redraw
    @shape.coords = [ @x - @r, @y - @r, @x + @r, @y + @r ]
  end

  def speeddownByWall
    @vx, @vy = @vx * @factorWall, @vy * @factorWall
  end

  def speeddownByFloor
    @vx, @vy = @vx * @factorFloor, @vy * @factorFloor
  end

  def compute_next_pos
    speeddownByFloor
    @x += @vx
    @y += @vy

    if (@y <= @r) || (Viewer::HEIGHT - @r <= @y) then
      @vy *= (-1)
      @y += @vy
      speeddownByWall
    end
    if (@x <= @r) || (Viewer::WIDTH - @r <= @x) then
      @vx *= (-1)
      @x += @vx
      speeddownByWall
    end

    if (@vx.abs + @vy.abs) < @minSpeed then
      return false    # delete Ball
    end

    redraw
    return true       # move Ball
  end
end

def newBall
  v = newV
  Ball.new(@r, @initX, @initY, v[0], v[1])
end

def newV
  th = rand * Math::PI * 2
  [@vx * Math.cos(th), @vy * Math.sin(th)]
end

@r = 10
@vx, @vy = 30, 30
@initX, @initY = 15 + @r , 15 + @r

balls = [ newBall ]

steps = 0
timer = TkAfter.start(10, -1, lambda {
  steps += 1

  balls.each do |ball|
    ball.resetV(newV)  if !ball.compute_next_pos
  end

  balls << newBall  if steps % 500 == 0
  # puts balls.size   if steps % 500 == 0
})

Tk.mainloop

ちょっと長くなってしまった。

同様のものを xfy/xvcd + SVG で書いたらどうなるだろう...

| | コメント (0) | トラックバック (0)

2008-03-02

ruby で gmail (imap) にアクセスする練習

ruby で gmail (imap) にアクセスする練習をしてみた。
この作業をしていて gmail は
  http://mail.google.com/mail/feed/atom
で RSS チェックできることにも いまさらながら 気がついた。

以下のスクリプトの実行をするには、gem install tmail しておく必要があります。
$ cat gmaisubject.rb
# See http://mac-memo.blogspot.com/2008/01/rubygmail_30.html
#     http://www.notwork.org/ipr/article/serial10.html

require 'rubygems'
require 'net/imap'
require 'tmail'
require 'cgi'
require 'yaml'
require 'kconv'
require 'pp'

@KCODE = "utf8"

config = YAML.load_file("config.yaml")
username = config["user"]
password = config["pass"]

port=993
usessl=true

imap = Net::IMAP.new("imap.gmail.com", port, usessl)
p imap.greeting
imap.login(username, password)

imap.examine('inbox')

# imap.fetch(1..-1, "ENVELOPE").each do |f|
imap.fetch(1..2, "ENVELOPE").each do |f|

  puts "=========================================================="
  messageNo = f.seqno
  date = f.attr["ENVELOPE"].date
  from_name = f.attr["ENVELOPE"].from[0].name
  subject = f.attr["ENVELOPE"].subject
  subject = "" if subject == nil
  print "#{messageNo}: #{date} #{from_name} #{Kconv.toutf8(subject)}\n"

  imap.fetch(messageNo, "RFC822").each do |m|
    mail = TMail::Mail.parse(m.attr["RFC822"])
    body = NKF::nkf('--sjis', mail.body)
    puts body.toutf8
  end
end

imap.disconnect

$ ruby gmailsubject.rb 
(実際の出力そのままではありません)
#<struct Net::IMAP::UntaggedResponse name="OK", data=#<struct Net::IMAP::ResponseText code=nil, text="Gimap ready for requests from 58.0.0.165 s38if458972rnb.0">, raw_data="* OK Gimap ready for requests from 58.0.0.165 s38if458972rnb.0¥r¥n">
==========================================================
1: Thu, 24 Nov 200* 23:08:35 -0800 (PST) =?ISO-2022-JP?B?R21haWwgGyRCJUEhPCVgGyhC?= Gmail へようこそ
Gmail へようこそ。 このたびは Gmail アカウントをご作成いただきありがとうございます。 Gmail は従来のウェブ
メールとはまったく異なるメール サービスです。 サービスのご利用にあたっては、次の情報をご参照ください。

==========================================================
2: Sat, 4 Mar 200* 01:44:56 -0800 (PST) mixi 変更メールアドレス

****** さん、こんにちは。

mixiからのお知らせです。以下のURLをクリックして登録内容の変更を完了させて
ください。

——————————————————————————

jruby ではなぜか動作しない、なぜ?
$ jruby gmailsubject.rb 
/Users/kato/work/www/jruby-1.0.3/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:26 warning: already initialized constant VERSION
/Users/kato/work/www/jruby-1.0.3/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:26 warning: already initialized constant OPENSSL_VERSION
/Users/kato/work/www/jruby-1.0.3/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:26warning: already initialized constant OPENSSL_VERSION_NUMBER
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant DSS
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant DSS1
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant MD2
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant MD4
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant MD5
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant MDC2
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant RIPEMD160
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant SHA
/Users/kato/work/www/jruby-1.0.3/lib/ruby/gems/1.8/gems/jruby-openssl-0.1.1/lib/openssl/digest.rb:42 warning: already initialized constant SHA1
// このあと ずっと wait 状態になってしまう...

20080302

| | コメント (0) | トラックバック (0)

2008-02-23

ruby mechanize の使い方の練習

社内の web ページを ruby mechanize で操作するための練習として、
mixi の "コミュニティ最新書き込み" を取得する ruby スクリプトを書いてみた。
(yield の使い方の練習もかねているが、こちらのほうは少し怪しい...)

実行結果例と、ソースコードを示す。(jruby でも動作する)

$ ruby getdialy.rb | nkf -w

2008年02月23日 17:28 三字熟語しりとり No.5(817) (言葉が好きだ。) view_bbs.pl?id=27488903&comment_count=817&comm_id=965
2008年02月23日 17:27 好きな映画を好きなだけ挙げて3本以上かぶったらマイミク!(885) (映画愛好会) view_bbs.pl?id=26387907&comment_count=885&comm_id=36
2008年02月23日 17:26 漢字しりとり No.28(771) (言葉が好きだ。) view_bbs.pl?id=27525724&comment_count=771&comm_id=965
... 省略 ...

$cat getdialy.rb
#
# See http://sora2hs.blog70.fc2.com/blog-entry-246.html

# mixi に login して、"コミュニティ最新書き込み" 一覧を得てから、logout する。
# username, password は config.yaml に書いておく。
#
# cat config.yaml
# user: name@gmail.com
# pass: pass
#

require 'rubygems'
require 'mechanize'
require 'yaml'
require 'rss'
require 'kconv'
require 'pp'

$KCODE = 'euc'

class Robot
  MIXI_URL = 'http://mixi.jp/'

  attr_reader :agent   # WWW::Mechanize

  # ログイン
  def login(config)
    # ユーザ名、パスワードを config.yaml に設定する
    config = YAML.load_file(config)
    username = config["user"]
    password = config["pass"]

    @agent = WWW::Mechanize.new
    page = @agent.get(MIXI_URL)
    form = page.forms[0]
    form.fields.find {|f| f.name == 'email'}.value = username
    form.fields.find {|f| f.name == 'password'}.value = password
    form.fields.find {|f| f.name == 'next_url'}.value = '/home.pl'
    page = @agent.submit(form, form.buttons.first)
    # p "-------- login --------"
  end

  # ログアウト
  def logout
    @agent.get('/logout.pl')
    # p "-------- logout --------"
  end

  # アクセス処理
  def Robot.open(config)
    robot = Robot.new
    robot.login(config)
    begin
       yield(robot)
    ensure
      robot.logout
    end
  end
end

def getData(robot)
  # diarylist = agent.get('/new_friend_diary.pl') # マイミクシィ最新日記
  diarylist = robot.agent.get('/new_bbs.pl') # コミュニティ最新書き込み
  doc = diarylist.root
  doc.search("/html/body/div[2]/div/div[2]/ul/li/dl").each {|entry|
    time = entry.at(:dt).inner_text.sub('?', ' ')
    dd = entry.at(:dd)
    title = dd.inner_text.strip
    url = dd.at(:a)["href"]
    print "#{time} #{title} #{url}¥n"
  }
end

robot = Robot.new
Robot.open('config.yaml') { |robot|
  getData(robot)
}

Imagyse37

| | コメント (0) | トラックバック (0)

2008-02-11

scrubyt で http://del.icio.us/ にアクセス(その3)

scrubyt をつかっていろいろ試していたのは、
実は会社でのイントラネット中のあるページの更新を rss で知れるようにしたかったのだ。
今日、その設定をしてみた。

いままでは rdf ファイルらしきものの生成はできていたが、実際に cgi として
社内ネット中のサーバーに設置をしてみると、いくつか壁があった。
1. windows で scrubyt を動作させうことができなかった。
     HOME 環境変数を定義しろとか言われた。設定したら、今度はその設定先 folder の パーミッションが
     甘過ぎると言われた。windows ではどうやって folder のパーミッションを細かく設定したらいいのかが不明なので、挫折。
2. fedoracore 上のサーバーの配置することにした。
    (本当はもともと fedoracore 上のサーバーに設置するつもりでいた。でも script 作成/テストを
     windows 上で行い、linux マシン上では設置だけで済まそうと思っていたのだ )
   端末の shell 上で実行すると問題ないのに、cgi として web ブラウザ経由で実行するとエラーが発生した。
    この対応で少し時間を食った。
    apache ログをみると、HOME 環境変数を指定していないとのエラーがでていたのだ。
    cgi 中で、export HOME=xxx を行い、xxx フォルダを chmod 755 にすることで解決させた。

    しかし、それだけは終わらなかった。rss 中のリンクが変だった。
    原因は、URL が & を含むものなのだが、rss の item 情報の URL では&amp; と ecsape されて
    しまっていたからだ。 CGI.unescape してから item.link の設定することで解決させた。

| | コメント (0) | トラックバック (0)

scrubyt で http://del.icio.us/ にアクセス(その2)

namespace, prefix 付きのデータを生成する場合、その namespace, prefix の値そのものにこだわらないなら、RSS として生成すれるのも良いかもしれない。

その場合次のようにすればよい。

require 'rss'
. . .
hash = bookmarks.to_hash
rss = RSS::Maker.make("1.0") do |maker|
  maker.channel.about = "http://example.com/index.rdf"
  maker.channel.title = "Example"
  maker.channel.description = "Example Site"
  maker.channel.link = "http://example.com/"

  hash.each { |ent|
    ent.each { |t, v|
      item = maker.items.new_item
      item.link = v
      item.title = t
    }
  }
end

実行結果例
$ ruby delisiourss.rb
/opt/local/lib/ruby/gems/1.8/gems/scrubyt-0.3.4/lib/scrubyt/core/scraping/filters/text_filter.rb:25: warning: don't put space before argument parentheses
http://del.icio.us/youichikato
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:image="http://web.resource.org/rss/1.0/modules/image/"
  xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
  xmlns="http://purl.org/rss/1.0/">
  <channel rdf:about="http://example.com/index.rdf">
    <title>Example</title>
    <link>http://example.com/</link>
    <description>Example Site</description>
    <items>
      <rdf:Seq>
        <rdf:li resource="http://www.ibm.com/developerworks/jp/linux/library/l-spider/index.html"/>
        <rdf:li resource="Linux で Web スパイダーをビルドする"/>
. . . 省略 . . .
        <rdf:li resource="Ruby の Jabber(XMPP) ライブラリ - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech"/>
      </rdf:Seq>
    </items>
    <taxo:topics>
      <rdf:Bag/>
    </taxo:topics>
  </channel>
  <item rdf:about="http://www.ibm.com/developerworks/jp/linux/library/l-spider/index.html">
    <title>url</title>
    <link>http://www.ibm.com/developerworks/jp/linux/library/l-spider/index.html</link>
    <taxo:topics>
      <rdf:Bag/>
    </taxo:topics>
  </item>
  <item rdf:about="Linux で Web スパイダーをビルドする">
    <title>title</title>
    <link>Linux で Web スパイダーをビルドする</link>
    <taxo:topics>
      <rdf:Bag/>
    </taxo:topics>
  </item>
. . . 省略 . . .
  <item rdf:about="Ruby の Jabber(XMPP) ライブラリ - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech">
    <title>title</title>
    <link>Ruby の Jabber(XMPP) ライブラリ - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech</link>
    <taxo:topics>
      <rdf:Bag/>
    </taxo:topics>
  </item>
</rdf:RDF>

日本語もそのまま出力されている。
また、この出力結果では最初に URL が出力されてしまっているが、これは scrubyt 中の以下の場所での puts によるものだ。(debug 出力が残っている?)

extractor.rb line 120 付近 (scrubyt 0.3.4)

    def evaluate_extractor
      root_results = []
      current_page_count = 1
      catch :quit_next_page_loop do
        loop do
          url = get_current_doc_url #TODO need absolute address here 2/4
ここ ==>    puts url

| | コメント (0) | トラックバック (0)

2008-02-10

プログラマーの為の XBRL という記事を書き始めました。

プログラマーの為の XBRL という記事を書き始めました。
http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/Edinet/lecture/000-begin.txt?rev=4&root=ruby-xbrl&view=auto

netbeans6.0.1/ruby/jruby を使ってプログラミングして行く予定です。
(netbeans から commti する時に コメントとして日本語を書いたら
  svn: Can't convert string from native encoding to 'UTF-8':
とのエラーになった...どうすれば解消できるのか?)

| | コメント (0) | トラックバック (0)

2008-02-09

scrubyt で http://del.icio.us/ にアクセス

scrubyt で http://del.icio.us/ にアクセスしてみた。

require 'rubygems'
require 'scrubyt'

$KCODE="utf-8"

url = 'http://del.icio.us/youichikato'
bookmarks = Scrubyt::Extractor.define do
  fetch(url)

  entry '/html/body/div/ol/li' do
    title '/h4/a'
    url '/h4/a/@href'
  end
end

bookmarks.to_xml.write($stdout, 1)

(データ場所を指定する xpath は firebug で調べた)
スッキリと書ける。素晴らしい! 

走らせた結果:
$ ruby delisiou0.rb
/opt/local/lib/ruby/gems/1.8/gems/scrubyt-0.3.4/lib/scrubyt/core/scraping/filters/text_filter.rb:25: warning: don't put space before argument parentheses
http://del.icio.us/youichikato
  <root>
    <entry>
      <title>Reference - Scrubyt</title>
      <url>http://wiki.scrubyt.org/index.php?title=Reference#XPath_Example</url>
    </entry>
... 省略 ...
    <entry>
      <title>どう見ても写真並みにリアルなクオリティのベクターアートいろいろ - GIGAZINE</title>
      <url>http://gigazine.net/index.php?/news/comments/20080206_not_photos/</url>
    </entry>
  </root>

これで問題はないけど、namespace, prefix が記されたものを生成したかったので、次のようにしてみた。
# xfy/xvcd で扱うには namespace, prefix が付いていた方が良いので

require 'rubygems'
require 'scrubyt'
require 'builder'
require 'pp'
... 省略 ...

# namespace や prefix が不要なら、これで十分
# bookmarks.to_xml.write($stdout, 1)

# namespace や prefix などが記られたものを得る。
# ただし、日本語は エンティティ 形式 (%#xxxxx;) になってしまう。
prefix ="p"
namespace ="http://sample.com"
roottag = "bookmarks"
entrytag = "entry"
buffer = ""
hash = bookmarks.to_hash

xml = Builder::XmlMarkup.new( :target => buffer, :indent => 2 )
xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
xml.method_missing( prefix + ":" + roottag, "xmlns:" + prefix => namespace) {
  hash.each { |ent|
   xml.method_missing(prefix + ":" + entrytag) {
      ent.each { |k, v|
        xml.method_missing(prefix + ":" + k.to_s, v.to_s)       
      }
    }
  }
}

puts buffer 

走らせた結果:
$ ruby delisiou.rb
/opt/local/lib/ruby/gems/1.8/gems/scrubyt-0.3.4/lib/scrubyt/core/scraping/filters/text_filter.rb:25: warning: don't put space before argument parentheses
http://del.icio.us/youichikato
<?xml version="1.0" encoding="UTF-8"?>
<p:bookmarks xmlns:p="http://sample.com">
  <p:entry>
    <p:url>http://wiki.scrubyt.org/index.php?title=Reference#XPath_Example</p:url>
    <p:title>Reference - Scrubyt</p:title>
  </p:entry>
... 省略 ...
  <p:title>&#12393;&#12358;&#35211;&#12390;&#12418;&#20889;&#30495;&#20006;&#12415;&#12395;&#12522;&#12450;&#12523;&#12394;&#12463;&#12458;&#12522;&#12486;&#12451;&#12398;&#12505;&#12463;&#12479;&#12540;&#12450;&#12540;&#12488;&#12356;&#12429;&#12356;&#12429; - GIGAZINE</p:title>
  </p:entry>
</p:bookmarks>

マッシュアップなページを作る時、web サービスや http はページのデータをすべてつかうわけではない。
scrubyt のようなものを使って、必要な部分だけを単純な構造でまとめてから、ビジネルロジックで処理し、結果をレンダリングするという使い方が重要だ。

|