jruby

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-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-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-05-17

jruby + swing で EDINET2009 タクソノミを閲覧(その9)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)

EDINET サイトで、XBRL データを download する場合、一度に複数企業のデータを指定できる。
その場合 1 つの zip の複数企業データが含まれれる。
そのような zip の対しても csv ファイルを全企業分 生成できるようにした。

2企業のデータを含む zip の処理例を示す。
$ ruby edinet2csv.rb ../../sample2009-edinet/XBRL_20090517_155420.zip > 1.csv
(このプログラムは jruby と ruby のどちらでも動作する)

出力結果を openoffice で開いてみる。
2009051701
2009051702

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

2009-05-10

jruby + swing で EDINET2009 タクソノミを閲覧(その8)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)

- zip ファイルを扱う
- 簡易表示テンプレートの追加

20090510edinetinstance01 20090510edinetinstance02 20090510edinetinstance03 20090510edinetinstance04
EDINET サイトから download する XBRLデータは、zip 圧縮されています。
アプリケーション画面への Drop, [file] - [open]で この zip ファイルそのもの処理できるようにしました。
(アプリ側で、./temp/ 以下に zip を自動解凍し、そのファイルに対して処理をするようにしただけ)
本当は、xbrl データ (*.xbrl, *.zip) などの URL を webブラウザから drop できたらと考えていたが、
EDINET サイトでの XBRL データ参照は、URL で簡単に 指し示せるようになっていないのであった....

今 書籍 財務マネジメントの基本と原則 を読んでいます。
この書籍中の BS, PL, CF, 基本値 の表示を得るようなテンプレートをつくりました。
書籍では、株発行数、株価も指標としていますので、次は これらの値を web サービスなどで取得して表中に表示したいと思う。

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

2009-05-07

jruby + swing で EDINET2009 タクソノミを閲覧(その7)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)
メニュー構築の実装を整理し、同時にツールバーも付けました。
ツールバーは表示位置を左、右へ移動したり、フロート表示にすることもできます。
20090505edinetinstance00
20090505edinetinstance01
20090505edinetinstance02

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

2009-05-06

jruby + swing で EDINET2009 タクソノミを閲覧(その6)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)
EDINET の表示テンプレートだけでなく、ユーザー定義の表示テンプレートを
適用することができるようにしました。
次のスクリーンショットは、 バランスシートの概要だけを表示する
テンプレートを指定している様子です。

20090505edinetinstance

memo.txt には、TODO を記載しています。

現時点では、jrubyc して classfile 化しての実行ができない、
Netbeans6.5 下での debug 実行ができない 等の問題も抱えています。

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

2009-05-04

jruby + swing で EDINET2009 タクソノミを閲覧(その5)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)

タブシート表示にした。
 左画面のインスアンス値タブは、シート名を企業名にした。
 右画面は、タクソノミ情報と、表示適用テンプレート一覧にした。
   表示適用テンプレート一覧上では、ダブルクリックで表示テンプレートを切り替えられる。
いまは 未だ用意していないが、ユーザー定義のテンプレートの追加も考えている。
20090504edinetinstance00 20090504edinetinstance01

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

2009-05-02

jruby + swing で EDINET2009 タクソノミを閲覧(その4)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
更新しました。(jruby + swing)
文字列検索機能を追加しました。
Edinetinstance20090502

tree 部、table 部中の文字列(部分一致、大文字/小文字を区別) で検索し、
一致する行が選択状態になります。
// 正規表現での検索、インクリメンタル検索にするなど いろいろな発展バリエーションは考えられるが、まずは 固定文字列の一括検索だけ。

ツリー部を折り畳んでいても、一致する行があれば、自動的に展開されて表示される。(これは JXTreeTabel 自体が持っている機能動作)

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

2009-04-26

jruby + swing で EDINET2009 タクソノミを閲覧(その3)

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
の EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラムを
また 更新しました。
負値を赤表示にするとか、合計値は、上辺に線を引くとか...
20090426edinetinstance
次は、表示データ中の検索機能、csv/Excel での保存機能、http://  データの扱い etc... を追加したい。

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

2009-04-25

jruby + swing で EDINET2009 タクソノミを閲覧(その2)


http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinetinstance.rb?root=ruby-xbrl&view=log
として EDINET 2009 のインスタンス情報を GUI で閲覧できるプログラム を登録しました。(jruby 1.2.0 で開発してます)

20090425edinetinstance

http://homepage2.nifty.com/youichi_kato/video/2009-04-25-edinetinstance.swf
として、1分ほどの動画を置いています。
# 動画の作成は、jing ( http://www.jingproject.com/ ) を使いました。

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

2009-04-18

jruby + swing で EDINET2009 タクソノミを閲覧

http://svn.sourceforge.jp/view/trunk/Edinet/lib/src2009/edinettree.rb?root=ruby-xbrl&view=log
として、EDINET 2009 のタクソノミ情報を GUI で閲覧できるプログラム登録しました。
Edinettree

これは EDINET サイトにある EXCEL のタクソノミ情報を閲覧しているだけです。
この情報を ruby で扱い、かつ GUI で表示する練習です。
これを発展させて、インスタンスファイルの情報を 表示内容に付加するなどが可能なはずです。

この folder にある main.rb は、インスタンスファイルから
 (科目ID, コンテキスト、値) を 取得できるプログラムです。 
次は、この main.rb プログラムの GUI 化をする予定です。
(フィアルを Drag&Drop したら、treetable 形式で、 科目を階層表示 context/値を table 表示する)

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

2009-04-12

jruby で swingx

swingx という swing を拡張したライブラリーがあることを知った。
これをつかって、FileTree を表示させてみた。

Swingxfiletree_2

$ cat filetree.rb

# See - http://boulayp.free.fr/articles.php?lng=en&pg=731
#       > GuppY - Java.Swing - Using JXTreeTable from SwingX
#     - http://wiki.java.net/bin/view/Javadesktop/SwingLabsSwingXJXTableHowTo#ColumnControl
#       > TWiki . Javadesktop . SwingLabsSwingXJXTableHowTo

require 'pp'
require 'java'
require 'swingx-0.9.7.jar'

import javax.swing.JFrame
import javax.swing.JScrollPane

import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.FileSystemModel
import org.jdesktop.swingx.decorator.HighlighterFactory

frame = JFrame.new "FileTree using Swingx"
frame.setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE)
frame.set_size(600, 400)

# File Tree
rootPath = '.'
rootPath = ARGV[0] if ARGV.size > 0

file = java.io.File.new(rootPath)

treeTableModel = FileSystemModel.new(file)
treeTable = JXTreeTable.new(treeTableModel)
treeTable.addHighlighter(HighlighterFactory::createSimpleStriping(HighlighterFactory::LINE_PRINTER))

# The JXTable Column Control is an icon that renders to the right of the column headers, just above the vertical scrollbar.
# When a user clicks the icon, a popup menu appears, allowing the user to show or hide columns from the view, and to set a couple of other properties.
treeTable.setColumnControlVisible(true)

frame.getContentPane().add(JScrollPane.new(treeTable))
frame.visible = true

スクリーンショットででているメニューは、スクルールバーの上にあるアイコンの
マウスで右クリックすると出てくる。
表示する列を on/off できる。
表の列は drag することで幅変更、順番変更ができる。

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

2009-04-11

jruby で swing

Drag 可能な JTree を java で作成し、それを jruby から使ってみた。

Dndtree
Drag 可能な JTree の例は以下にある。
- http://terai.xrea.jp/Swing/DnDTree.html
> JTreeのノードをドラッグ&ドロップ - Java Swing Tips

このソースから、DnDTree クラスだけを抜き出したものをつくり、
DnDTree.jar をつくっておく。

java ソースのすべては引用しないが、
ヘッダ部だけを示す(jar の作り方などをコメント追加したので)

$ cat DnDTree.java
// See
//    http://terai.xrea.jp/Swing/DnDTree.html                                                                           

// javac -d . -encoding utf8 -target 1.5 DndTree.java                                                                  
// jar cvf DnDTree.jar samples                                                                                          

package samples;

//-*- mode:java; encoding:utf8n; coding:utf-8 -*-                                                                      
// vim:set fileencoding=utf-8:                                                                                          
//http://terai.xrea.jp/Swing/DnDTree.html                                                                              
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;

//Java Swing Hacks - HACK #26: DnD JTree                                                                               
//http://www.oreilly.co.jp/books/4873112788/                                                                           
public class DnDTree extends JTree implements DragSourceListener, DropTargetListener, DragGestureListener {
    private static final String NAME = "TREE-TEST";
... 省略 ...

$ cat dndtree.rb
# See http://www.bloglines.com/blog/ThomasEEnebo?id=40

require 'pp'
require 'java'

require 'DnDTree.jar'
import 'samples.DnDTree'

import javax.swing.JFrame
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel

def getTreeData(v)
  root = DefaultMutableTreeNode.new("Root")
  DefaultTreeModel.new(vec2tree(root, v))
end

def vec2tree(parent, h)
  h.each do |k, v|
    if v.kind_of? Hash
      child = DefaultMutableTreeNode.new(k)
      parent.add(vec2tree(child, v))
    else
      parent.add(DefaultMutableTreeNode.new(k))
    end
  end
  parent
end

frame = JFrame.new "Dragable JTree"
frame.setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE)
frame.set_size(600, 400)

content = frame.content_pane
content.setLayout(java.awt.BorderLayout.new)

# ツリー
hash = { :A => nil, :B => {:B1 => nil, :B2 => nil}, :C => nil }
tree = DnDTree.new
tree.model = getTreeData(hash)

content.add(tree, java.awt.BorderLayout::CENTER)

frame.visible = true

ここでは、Hash を Tree 表示させてみているが、Array、XML の Dom などいろいろなものを Tree 表示させるように変更するのは簡単だ。

xmlunit の diff 情報を tree 表示にうまく反映させるはどんな風にすればよいだろうか?

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

2009-04-08

xmlunit の調査 (その2)

XMLUnit の XMLAssert は、うまく使えなかったので、
XMLUnit の Diff を使う事にした。

テスト用の2つの xml ファイルと、テストケースを示す。
$ cat hello1.xml
<struct>
  <int>  3  </int>
  <boolean>false</boolean>
</struct>

$ cat hello2.xml
<struct>
  <boolean>false</boolean>
  <int>3</int>
</struct>

$ cat SampleTest.rb
# $KCODE = 'utf8'

require 'pp'
require 'java'
require 'test/unit'
require 'xmlunit-1.2.jar'

import 'org.custommonkey.xmlunit.XMLUnit'
import 'org.custommonkey.xmlunit.Diff'
import 'org.custommonkey.xmlunit.DetailedDiff'

import 'org.xml.sax.InputSource'

class Utils
  def self.compare(file1, file2)
    source1 = InputSource.new(file1)
    source2 = InputSource.new(file2)
    Diff.new(source1, source2)
  end
end

class SampleTest < Test::Unit::TestCase

  def setup
    # pp "----- setUp"
  end

  def teardown
    # pp "----- tearDown"
  end

  def test_Diff
    myControlXML = "<struct><int>3</int><boolean>false</boolean></struct>";
    myTestXML = "<struct><boolean>false</boolean><int>3</int></struct>";
    myDiff = Diff.new(myControlXML, myTestXML);
    assert_equal(true, myDiff.similar(), "pieces of XML are similar " + myDiff.to_s)
    assert_equal(false, myDiff.identical(), "but are they identical? " + myDiff.to_s)
  end

  def test_DetailedDiff
    myControlXML = "<struct><int>3</int><boolean>false</boolean></struct>";
    myTestXML = "<struct><boolean>false</boolean><int>3</int></struct>";
    myDiff = DetailedDiff.new(Diff.new(myControlXML, myTestXML));

    #    myDiff.getAllDifferences.each do |d|
    #      pp d
    #    end
  end

  def test_Compare
    c = Utils::compare('hello1.xml', 'hello2.xml')
    assert_equal(false, c.similar())    # node の順番を無視して比較
    assert_equal(false, c.identical())
  end

  def test_Compare
    # 空白文字の差を無視する
    XMLUnit::setIgnoreWhitespace(true)

    c = Utils::compare('hello1.xml', 'hello2.xml')
    assert_equal(true, c.similar())     # node の順番を無視して比較
    assert_equal(false, c.identical())
  end
end

実行結果はこんなふうになる。
$ jruby SampleTest.rb
Loaded suite SampleTest
Started
...
Finished in 0.15 seconds.

3 tests, 4 assertions, 0 failures, 0 errors

xml を出力する jruby で書くツールの unit テストには、この方法を使ってみようと思う。
# ファイル経由で比較するなら、 java, ruby, jruby を問わずにテストできる。

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

2009-04-06

xmlunit の調査

テストで xml を比較する必要があり、調べていたら、
- http://www.infoq.com/jp/articles/xml-unit-test
> InfoQ: XMLをユニットテストする
を見つけた。そのなかで xmlunit が説明されている。

java で xmlnunit を利用する例を以下に置いた。
- http://homepage2.nifty.com/youichi_kato/src.html
> xmlunit のサンプル(2009-04-05)
>    my-xmlunit-2009-04-05.tgz
>    xml の比較ツールの利用例。これを xfy から xvcd から使えるようにすることでテストに利用できる。

jruby からの利用を試みたが、うまくいかなかった。

$ cat SampleTest.rb

require 'pp'
require 'java'
require 'junit-3.8.2.jar'
import 'junit.framework.TestCase'
import 'junit.framework.TestSuite'
import 'junit.textui.TestRunner'
import 'junit.framework.Assert'

class SampleTest < TestCase

  def initialize
    super
  end

  def setUp
    pp "----- setUp"
  end

  def tearDown
    pp "----- tearDown"
  end

  def testFoo
    pp "----- testFoo"
    #Assert::assertTrue(true)
  end

end

t = SampleTest.new
t.setName('testFoo')

TestRunner::run(t)

実質的には何もしていないコードであるが、
実行すると、次のようになる。
$ jruby SampleTest.rb
."----- setUp"
"----- tearDown"
F
Time: 0.009
There was 1 failure:
1) testFoo(org.jruby.proxy.junit.framework.TestCase$Proxy0)junit.framework.AssertionFailedError: Method "testFoo" not found
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.jruby.javasupport.JavaMethod.invokeWithExceptionHandling(JavaMethod.java:298)
    at org.jruby.javasupport.JavaMethod.invoke_static(JavaMethod.java:278)
    at org.jruby.java.invokers.StaticMethodInvoker.call(StaticMethodInvoker.java:47)
    at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:273)
    at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:112)
    at SampleTest.__file__(SampleTest.rb:53)
    at SampleTest.load(SampleTest.rb)
    at org.jruby.Ruby.runScript(Ruby.java:577)
    at org.jruby.Ruby.runNormally(Ruby.java:480)
    at org.jruby.Ruby.runFromMain(Ruby.java:354)
    at org.jruby.Main.run(Main.java:229)
    at org.jruby.Main.run(Main.java:110)
    at org.jruby.Main.main(Main.java:94)

FAILURES!!!
Tests run: 1,  Failures: 1,  Errors: 0

となってしまう。

setUp, tearDown は呼び出されているが、
testFoo の呼び出しがうまくできていない。
refrection での java-class, jruby-class のやり取りが問題のようだが)
junit を jruby で使うことは無理なのか?
xmlunit の diff をつかって、jruby の respc や test/unit で利用するしかないのかも知れない。

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

2009-03-29

ruby + poi で Excel データを生成

csv データを Excel シートにする練習を jruby でやってみた。

2 つの csv ファイルを用意し、それぞれを 別シートとして保持する。
セルの書式設定、計算式(合計)の設定も試した。

まず生成した Excel データを OpenOffice で表示した画面を示そう。

シート1
Excel01

コメントの表示
Excel02

シート2
Excel03

次に、csv ファイル、生成スクリプトを示す。(いずれも utf-8)
$ cat 01.csv

"ID","name","sex","age","data"
"1","ichirow","male","20","123456"
"2","hanako","female","40","-1234"

$ cat 2.csv
"ID","名前","性別","年齢","値"
"1","一郎","男","20","1000"
"2","次郎","男","18","0"
"3","三郎","男","16","-2000"
"4","四郎","男","14","1000"
"5","五郎","男","12","1"
"6","花子","女","20","2"

$ cat write-excel.rb

# See http://d.hatena.ne.jp/koush/20080426/1209197281
#     http://www.javadrive.jp/poi/index.html
#     > Jakarta POIでExcelを操作

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

require 'poi-3.5-beta5-20090219.jar'
require 'commons-lang-2.4.jar'

include Java

module HssfUserModel
  include_package 'org.apache.poi.hssf.usermodel'
end

module HssfUtil
  include_package 'org.apache.poi.hssf.util'
end

def makeSheet(workbook, sheetname, data = [])
  sheet = workbook.createSheet(sheetname)

  data.each_with_index do |row_data, r_index|
    row = sheet.createRow(r_index)
    row_data.each_with_index do |cell_data, c_index|
      cell = row.createCell(c_index)
      cell.setCellValue(cell_data)
      cell.setCellValue(cell_data.to_i) if org.apache.commons.lang.math.NumberUtils.isNumber(cell_data)

      style = createStyle(workbook, r_index, c_index, cell_data)
      cell.setCellStyle(style)
    end
  end

  # 合計行
  style = workbook.createCellStyle()
  style.setBorderTop(HssfUserModel::HSSFCellStyle::BORDER_MEDIUM)

  totalRow = sheet.createRow(data.size)
  totalTitleCell = totalRow.createCell(3)
  totalTitleCell.setCellStyle(style)
  totalTitleCell.setCellValue('Total') # ラベル

  totalValCell = totalRow.createCell(4)
  totalValCell.setCellStyle(style)
  totalValCell.setCellFormula("SUM(E2:E#{data.size})")  # 計算式

  # 合計セルにコメントを付ける
  patriarch = sheet.createDrawingPatriarch()
  anchor = HssfUserModel::HSSFClientAnchor.new(0, 0, 6, 3, 4, 2, 6, 5)
  comment = patriarch.createComment(anchor)
  comment.setString(HssfUserModel::HSSFRichTextString.new("コメント\ncomment"))
  comment.setAuthor("Apache Software Foundation")
  totalValCell.setCellComment(comment)
end

def createStyle(workbook, r_index, c_index, cell_data)
  style = workbook.createCellStyle()

  fontBold = workbook.createFont()
  fontBold.setBoldweight(HssfUserModel::HSSFFont::BOLDWEIGHT_BOLD)

  formatDouble = workbook.createDataFormat()

  if r_index == 0
    # 罫線
    style.setBorderBottom(HssfUserModel::HSSFCellStyle::BORDER_MEDIUM)
    # 背景色
    style.setFillForegroundColor(HssfUtil::HSSFColor::YELLOW.index)
    style.setFillPattern(HssfUserModel::HSSFCellStyle::SOLID_FOREGROUND)
    # 右寄せ
    style.setAlignment(HssfUserModel::HSSFCellStyle::ALIGN_CENTER)
    # フォント
    style.setFont(fontBold)
  end

  if org.apache.commons.lang.math.NumberUtils.isNumber(cell_data)
    # 右寄せ
    style.setAlignment(HssfUserModel::HSSFCellStyle::ALIGN_RIGHT)
    # 書式
    f = '#,##0;[RED]-#,##0'
    style.setDataFormat(workbook.createDataFormat().getFormat(f))
  end

  if c_index == 2 and r_index != 0
    # 回転
    style.setRotation(-30)
  end

  if c_index == 1 and r_index != 0
    # インデント
    style.setAlignment(HssfUserModel::HSSFCellStyle::ALIGN_LEFT)
    style.setIndention((r_index - 1) % 3)
  end
  style
end

FILE_NAME = 'test.xls'

fileOut = java.io.FileOutputStream.new(FILE_NAME)
wb = HssfUserModel::HSSFWorkbook.new
makeSheet(wb, 'シート-01', FasterCSV.read('01.csv'))
makeSheet(wb, 'シート-02', FasterCSV.read('02.csv'))
wb.write(fileOut)
fileOut.close

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

2009-03-21

jruby + swing で画像閲覧

- http://terai.xrea.jp/Swing/Zoom.html
> > MouseWheelで画像のズームイン・アウト表示 - Java Swing Tips

のプログラムをベースに、少し拡張してみた。
// darg & drop で画像を入れ替えたり、
// マウスで画像をつかんでスクルールさせるなど..

起動時には最初、ainomukidashi.jpg を表示している。
Ainomukidashi
// 2009-03-22 19:00 ソースコードを差し替えた
$ cat wheel.rb
# See - http://terai.xrea.jp/Swing/Zoom.html
#       MouseWheelで画像のズームイン・アウト表示 - Java Swing Tips

# 2009-03-21 katoy

# - デスクトップなどから、File を Drop して、表示ファイルを切り替えることが可能。
# - MouseWheelで画像のズームイン・アウトする。
# - Mouse ドラッグでもスクロールできる。
# - Mouse クリックすると、そこが表示中心になる。

require 'java'
require 'pp'

import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.Graphics2D
import java.awt.Point
import java.awt.Rectangle
import java.awt.Cursor

import java.awt.event.ActionListener
import java.awt.event.MouseListener
import java.awt.datatransfer.DataFlavor
import java.awt.dnd.DnDConstants
import java.awt.dnd.DropTarget
import java.awt.dnd.DropTargetAdapter
import java.awt.dnd.DropTargetListener

import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.JScrollPane
import javax.swing.JButton
import javax.swing.UIManager
import javax.swing.JComponent
import javax.swing.AbstractAction
import javax.swing.ImageIcon
import javax.swing.Box

START_ICON_PATH = 'ainomukidashi.jpg'

class MainPanel < JPanel

  def initialize
    super(BorderLayout.new)

    icon = ImageIcon.new(START_ICON_PATH)
    zoom = ZoomImage.new
    zoom.setIcon(icon)
    scrollpane = JScrollPane.new(zoom)
    vport = scrollpane.getViewport

    vport.addMouseWheelListener(MyWheelListener.new(zoom, vport))
    mml = MyMouseListener.new
    mml.setZoom(zoom, vport)
    vport.addMouseMotionListener(mml)
    vport.addMouseListener(mml)

    dta = MyDropTargetAdapter.new
    dta.setZoom(zoom)
    DropTarget.new(zoom, dta)

    button1 = RButton.new("Zoom In")  { zoom.changeScale(-5, vport) }
    button2 = RButton.new("Zoom Out")  { zoom.changeScale(5, vport) }
    button3 = RButton.new("Original size")  { zoom.initScale(vport) }

    box = Box.createHorizontalBox()
    box.add(Box.createHorizontalGlue())
    box.add(button1)
    box.add(button2)
    box.add(button3)

    add(scrollpane)
    add(box,  BorderLayout::SOUTH)
  end
end

class RButton < JButton
  def initialize(text)
    super(text)
    self.addActionListener Proc.new if block_given?
  end
end

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

  def drop(e)
    transfer = e.getTransferable();

    if transfer.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
      e.acceptDrop(DnDConstants::ACTION_COPY_OR_MOVE)
      fileList = transfer.getTransferData(DataFlavor.javaFileListFlavor)
      icon = ImageIcon.new(fileList[0].getAbsolutePath())
      @zoom.setIcon(icon)
    end
  end
end

class MyWheelListener
  include java.awt.event.MouseWheelListener

  def initialize(zoom, vport)
    @zoom = zoom
    @vport = vport
  end

  def mouseWheelMoved(e)
    @zoom.changeScale(e.getWheelRotation() * 0.6, @vport)
  end
end

class MyMouseListener < javax.swing.event.MouseInputAdapter

  def initialize
    @pp = Point.new
  end

  def setZoom(zoom, vport)
    @zoom = zoom
    @vport = vport
    @defCursor = Cursor::getPredefinedCursor(Cursor::DEFAULT_CURSOR)
    @hndCursor = Cursor::getPredefinedCursor(Cursor::HAND_CURSOR)
  end

  def mouseDragged(e)
    cp = e.getPoint()
    vp = @vport.getViewPosition()
    vp.translate(@pp.x - cp.x, @pp.y - cp.y)

    @zoom.scrollRectToVisible(Rectangle.new(vp, @vport.getSize))
    @pp.setLocation(cp)
  end

  def mouseClicked(e)
    cp = e.getPoint()
    size = @vport.getSize
    vp = @vport.getViewPosition
    vp.translate(cp.x - size.width / 2, cp.y - size.height / 2)
    @zoom.scrollRectToVisible(Rectangle.new(vp, @vport.getSize))
  end

  def mousePressed(e)
    @zoom.setCursor(@hndCursor)
    @pp.setLocation(e.getPoint())
  end

  def mouseReleased(e)
    @zoom.setCursor(@defCursor)
  end

end

class ZoomImage < JComponent

  attr_reader :scale

  def initialize
    super
  end

  def setIcon(icon)
    @icon = icon
    @iw = icon.getIconWidth * 1.0
    @ih = icon.getIconHeight * 1.0
    @scale = 1.0
    revalidate
    repaint
  end

  def paintComponent(g)
    g.scale(@scale, @scale)
    g.drawImage(@icon.getImage, 0, 0, @iw, @ih, self)
  end

  def getPreferredSize
    Dimension.new(@iw * @scale, @ih * @scale) 
  end
 
  def initScale(vport)
    @scale = 1.0
    setPreferredSize(Dimension.new(@iw * @scale, @ih * @scale))
    scrollRectToVisible(Rectangle.new(Point.new(0, 0), size))
    revalidate
    repaint
  end

  def changeScale(iv, vport)
    old_scale = @scale
    @scale = java.lang.Math.max(0.05, java.lang.Math.min(5.0, @scale - iv * 0.05))

    pos = vport.getViewPosition
    size = vport.getSize

    # 古い scale で, 表示中心を求めてから、新しい scale での 表示左上端を座標を求める。
    cx = @scale * (pos.x / old_scale + size.width / 2.0 / old_scale - size.width / 2.0 / @scale)
    cy = @scale * (pos.y / old_scale + size.height/ 2.0 / old_scale - size.height/ 2.0 / @scale)

    setPreferredSize(Dimension.new(@iw * @scale, @ih * @scale))
    scrollRectToVisible(Rectangle.new(Point.new(cx, cy), size))
    revalidate
    repaint
  end
end


UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)

frame = JFrame.new("Zoom")
frame.get_content_pane.add(MainPanel.new)

frame.set_default_close_operation(JFrame::EXIT_ON_CLOSE)
frame.pack

# フレームをデスクトップの中央に表示
frame.setLocationRelativeTo(nil)
frame.visible = true

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

2009-03-19

JRuby 1.2.0 released

- http://www.h-online.com/open/JRuby-1-2-0-released--/news/112871
> JRuby 1.2.0 released - News - The H Open Source: News and Features

- http://d.hatena.ne.jp/xibbar/mobile?date=20081220
> JRubyのコードをexeファイルにする方法とMacの.app形式にする方法

Jruby 1.2 の sample には、webstart の例も含まれている。
exe や Macの.app をつくる方法も見つけた。
ユーザーに jruby をインストールさせずに、アプリを動作させる方法は
十分に揃っているな。

http://www.oreilly.de/catalog/9780596519803/toc.html
では、書籍 JRuby Cookbook の一部をみることができる。

| | コメント (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-01-02

Processing 本を買った

新春の 洋書 20 % OFF セールで、
   Processing: A Programming Handbook for Visual Designers and Artists
を購入した。

processing 関係のサイトを列挙しておく。

- http://d.hatena.ne.jp/MineAP/20080225/1203933323
> Processing を NetBeans 上でコンパイル、実行する。 - MineAPの(開発)日記

- http://github.com/jashkenas/ruby-processing/wikis
> Home — ruby-processing — GitHub
このサイトには、興味深い情報が満載だ。

- http://www.slideshare.net/mochikoAsTech/118jruby-presentation
> 11月8日イケテルjruby勉強会@東京 - SlideShare
勉強会のスライドが掲載されている。

- http://ukstudio.jp/2008/12/08/action-coding/
> UK STUDIO - JRubyとProcessing1.1でaction-coding!

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

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-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-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-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-14

jruby のダイアログ

- http://xfy.justblog.jp/labs/2008/08/ruby-on-xfy-rub.html
> xfy 実験室: Ruby on xfy : 拡張コマンドをRubyで作る!

にある、jruby コードを試してみた。(MacOSX 10.5)
日本語ラベルも問題は発生しないなぁ。

Jruby
jruby, java のバージョンは次のとうり。
$ jruby -v
jruby 1.1.3 (ruby 1.8.6 patchlevel 114) (2008-07-21 rev 6586) [x86_64-java]

$ java -version
java version "1.6.0_05"
Java(TM) SE Runtime Environment (build 1.6.0_05-b13-120)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0_05-b13-52, mixed mode)

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

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-06-04

prosessing の例

- http://red-treasure.com/report/?p=209
> > コスミー報告書[社外秘] - Javascriptのすごいライブラリみつけた  Processing.js
このページにある Prosessing の サンプルを action-coding 用に変更してみた。

xfy 画面中で Processing で生成した画像、アプリを表示するようにするにはどうしたらよいだろう...

$ cat  cat sample000.rb
# See http://red-treasure.com/report/?p=209
def setup
  size(200, 200)
  smooth
  noStroke()
  fill(255, 153)

  @num = 60
  @mx = []
  @my = []
  for i in 1..@num
    @mx[i-1], @my[i-1] = 0, 0
  end
end

def draw
  background(51)

  # Reads throught the entire array
  # and shifts the values to the left
  for i in 1..@num
    @mx[i-1], @my[i-1] = @mx[i], @my[i]   
  end

  # Add the new values to the end of the array
  @mx[@num-1], @my[@num-1] = mouseX, mouseY

  @mx[@num-1] = 0  if @mx[@num-1] == nil
  @my[@num-1] = 0  if @my[@num-1] == nil

  for i in 1..@num
    ellipse(@mx[i-1], @my[i-1], i/2, i/2)
  end
end

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

2008-06-01

書籍 Built with Processing [改訂版] を買った (その4)

processing の 0137 が 公開されたようだ。
- http://labs.uechoco.com/blog/2008/05/processing0137_released.html
> > [Processing]0137リリース

今日は 3D 描画を試した。マウスクリックしている間は、図形移動は止まるようにしてある。

Sample07

$ cat sample07.rb

# See  http://yoppa.jpn.org/presen.php?itemid=186

def setup
    size(300,300,P3D)
    frameRate(30)

    @xspeed = 3.0
    @yspeed = 1.0
    @zspeed = 3.0

    @x = width/2.0
    @y = height/2.0
    @z = 0.0

    @r = 100.0
    @theta = 0.0

    stroke(255,255,255,30)
    fill(64,64,255,50)
    lights()
    translate(@x,@y,@z)
end

def draw
    background(24)
    translate(@x,@y,@z)
    rotateX(@theta)
    rotateY(@theta*1.5)
    box(@r)
    sphere(40)

    @x += @xspeed
    @y += @yspeed
    @z += @zspeed
    @theta += 0.01

    @xspeed *= -1.0 if ((@x > width) || (@x < 0))
    @yspeed *= -1.0 if ((@y > height) || (@y < 0))
    @zspeed *= -1.0 if ((@z > 10) || (@z < -100))
end

def mousePressed
  noLoop
end

def mouseReleased
  loop
end

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

2008-05-30

書籍 Built with Processing [改訂版] を買った (その3)

壁に反射するボールの例を作成した。
# 整理すればもっと短くかけるはずだ!
Sample05_2
$ cat sample05.rb

def setup
  @FRICTION = 0.96
  @radius = 10

  size(200, 200)

  colorMode(HSB, 100)
  set_color
  rectMode(CORNER)
  rect(0, 0, width, height)
  fadeToWhite

  smooth
  noStroke
  frameRate(24)

  @x0 = width / 2
  @y0 = height / 2
  set_speed
end

def set_speed
  @dx = random(-30, 30)
  @dy = random(-30, 30)
end

def set_color
  fill(60, 100, 100)
end

def draw
  fadeToWhite

  @dx *= @FRICTION
  @dy *= @FRICTION
  @x0 += @dx
  @y0 += @dy

  bounce

  set_color
  ellipse(@x0, @y0, @radius * 2, @radius * 2)

end

def bounce
  bounceMinX = @radius
  bounceMaxX = width - @radius
  bounceMinY = @radius
  bounceMaxY = height - @radius

  if (@x0 < bounceMinX) || (@x0 > bounceMaxX)
    @dx = -@dx
    @x0 = bounceMinX - (bounceMinX - @x0) if @x0 < bounceMinX
    @x0 = bounceMaxX - (bounceMaxX - @x0) if @x0 > bounceMaxX

    @x0 = bounceMinX if @x0 < bounceMinX
    @x0 = bounceMaxX if @x0 > bounceMaxX
  end

  if (@y0 < bounceMinY) || (@y0 > bounceMaxY)
    @dy = -@dy
    @y0 = bounceMinY  - (bounceMinY - @y0) if @y0 < bounceMinY
    @y0 = bounceMaxY  - (bounceMaxY - @y0) if @y0 > bounceMaxY

    @y0 = bounceMinY if @y0 < bounceMinY
    @y0 = bounceMaxY if @y0 > bounceMaxY
  end

  set_speed if abs(@dx) + abs(@dy) < 0.3

end

def fadeToWhite
  noStroke
  fill(99, 30)  # fill(90, 100)
  rectMode(CORNER)
  rect(0, 0, width, height)
end

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

2008-05-29

書籍 Built with Processing [改訂版] を買った (その2)

action-coding をつかって、jruby から動作させて遊び中。
  - http://code.google.com/p/action-coding/
  > > action-coding is a prototype environment using with Ruby(Language) + Processing(API).

画像が生成されるまでには少し時間がかかる。リアルタイムのグラフ生成は無理か?

$ jruby aco.rb -r sample01.rb
のように -r を指定して起動させると、ファイル内容の変化をキャッチして自動で再描画してくる。
(私は emacs で編集しているので、 ctrl-s する)
プログラムコードのエラーなどは、コンソールに出る。
println "#{x}, "#{y}" などとして debug プリントが可能。(これもコンソールに出る)

3 つのプログラムと、実行結果を示そう。

$ cat sample01.rb
def setup
  size(200, 200)
  colorMode(HSB, 100)
  background(99)
  noStroke

  x = 0
  y = 0
  while (y < height) do
    x = random(width)
    y  += random(5);

    fill(random(100), y, 99, 30)
    rect(x, y, 30, 30)
  end
end

Sample01
$ cat sample02.rb
def setup
  sample = loadImage("mona_lisa.jpg")
  w = sample.width.to_f
  h = sample.height.to_f
  r = h / w

  # println "#{w}, #{h}, #{scale}, #{r}"

  win_w = 300
  win_h = win_w * r
  scale = win_w / w

  size(win_w + 10, win_h + 10)

  image(sample, 5, 5, w * scale, h * scale)

end
Sample02

$ cat sample03.rb
def setup
  size(200, 200)
  colorMode(HSB, 120)

  background(119)
  smooth
  noStroke

  angle = 360 / 12
  margin = 40

  translate(120, 30)

  0..12.times do |i|
    fill(i * 10, 100, 119, 60)
    rect(0, 0, 30, 30)
    rotate(radians(angle))
    translate(margin, 0)
  end
end

Sample03_2
次は、アニメーションのサンプルを試そう。

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

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-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)

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-12

jruby 1.1 と gem 1.1.1

jruby 1.1 で gem を update して 1.1.1 にした。

$ jruby -S gem --version      
1.1.1

$ jruby -S gem search -r rails
とすると、
Bulk updating Gem source index for: http://gems.rubyforge.org/
ERROR:  While executing gem ... (Gem::RemoteSourceException)
    Error fetching remote gem cache: NameError: uninitialized constant Gem::RemoteFetcher::StringIO reading http://gems.rubyforge.org/yaml

と、エラーになってしまう。なぜ!

- http://rubyforge.org/forum/forum.php?forum_id=23341
> > RubyForge: 1.1.2-gem-released-

gem 1.1.2 がある?

jruby は ソースから build したんだが、binary を入れてやりなおすか?

| | コメント (1) | トラックバック (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-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-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-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-01-20

sourceforge.jp へ netbenas からの svn アクセスができない

netbeans6 から sourceforge.jp に ソースを登録しようとしたが、エラーがでて import できない。("Connection closed unexpectedly")

コマンドラインで実行すれば、import できた。
$ svn import . svn+ssh://katoy@svn.sourceforge.jp/svnroot/ruby-xbrl/trunk -m 'Initial import to Subversion'

import できたので netbeans6 から checkout を試みるが、これもダメ。
しかたないので、再度、コマンドラインで checkout した。

$ svn checkout svn+ssh://katoy@svn.sourceforge.jp/svnroot/ruby-xbrl/trunk .

コマンドラインでは、 ssh login もうまくいく。
$ ssh -l katoy -i ~/.ssh/id_dsa shell.sourceforge.jp

netbeans6 での subversion でのダイアログでは次のように設定している。
何が悪いのかなぁ。

Repository URL:  svn+ssh://katoy@svn.sourceforge.jp/svnroot/ruby-xbrl
Tunnel Command:  ssh -l katoy -i ~/.ssh/id_dsa

開発者としてでなく、

Repository URL:  http://svn.sourceforge.jp/svnroot/ruby-xbrl/trunk

として、アクセスすれば、netbeasns6 から checkout  はできるのだが。

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

2008-01-19

XBRLインスタンス作成Tool があるのを知った

http://sourceforge.jp/projects/goingxbrl/
> ...
XBRLインスタンス作成Tool kit for RDBは、XBRLタクソノミーをRDBに展開し、SQLとmethodのコールでXBRLインスタンスを作成することができます。
> ...
なんてものがあることを知った。(java)
RDB アクセスを ActiveRecored にして、ruby や jruby と組み合わせて使えるかな?

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

2008-01-16

ruby で XBRL データの処理をしよう(その2)

sourceforge.jp に project ページができました。

  https://sourceforge.jp/projects/ruby-xbrl

----- 返信メールから引用 ----

Sourceforge Admin <sf-admin@sourceforge.jp>    
2008/01/15 8:48
Your project registration for SourceForge has been approved.

Project Full Name:  ruby-XBRL
Project Unix Name:  ruby-xbrl
CVS Server:         cvs.sourceforge.jp
Project Page:       https://sourceforge.jp/projects/ruby-xbrl
Project Admin Page: https://sourceforge.jp/project/admin/?group_id=3311

Your project registration for SourceForge has been approved.

---- ここまで -----

承認メールの返信は1時間ほどで届いていたのか。素早いな。

他の project の ファイル構成、wiki 書き方 などを参考にして、
どんどんと 更新をしていきた い。

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

2008-01-15

ruby で XBRL データの処理をしよう

http://ja.wikipedia.org/wiki/XBRL
という 財務諸表 のための xml の仕様があります。
日本では、EDINET ( http://ja.wikipedia.org/wiki/EDINET ) として利用が開始されます。

このデータを利用するための オープンソースは少ないです。
sourceforge.net で XBRL で検索したら、12 件がヒットするだけ。

- http://sourceforge.net/search/?words=xbrl&sort=registration_date&sortdir=desc&offset=0&type_of_search=soft&pmode=0&form_cat=18
> > SourceForge.net: Software Search

そこで、 sourceforge.jp に "ruby-xbrl" として、プロジェクト登録の申請をしました。
ここでは、 ruby で、 XBRL/EDINET のデータを表示する為のアリブラリーと、
それを利用したアプリケーションを作っていこうと思います。(ライセンスは GRL で)
個人投資家が XBRL データを有効利用できるためのツールを作成できればと思っています。
# 興味がある方は、連絡を。一緒に作業をしていきたいです。

soruceforge のプロジェクト登録が承認されしだい、活動を開始したい。
現時点での手持ちソースと実行例を以下に示します。

「main.rb」をダウンロード

「xbrl.rb」をダウンロード

「myyaml.rb」をダウンロード

$ ruby main.rb
B:c1=1
C:c1=2
A:c1=3

これは、
- http://www.xbrl.org/frontend.aspx?clk=SLK&val=100
> > Welcome to XBRL
にある
  http://www.xbrl.org/2007/XBRL-CONF-CR3-2007-03-05.zip
というデータ中の XBRL-CONF-CR3-2007-03-05/Common/instance/397-00-ConsistentInstance-valid.xbrl
という XBRL データを処理して 項目名と値を表示している という実行例です。
(処理ファイル名は現状はソース埋め込み。 See main.rb )

これは、私が netbeasn6 をつかって最初の作成した ruby プログラムでもあります。
書き方が下手なせいもあり、370 行ほどあるが、netbeasn6 のような IDE を利用することで
ソースコード編集、debug が比較的 楽にできました。
テストコードと、ドキュメント用コメントも徐々に書いていこうと思っています。

rails での利用、xfy/xvcd での利用も視野に入れたいと考えています。

# myyaml.rb のコードは
# http://d.hatena.ne.jp/Rommy/20061229/1167406811
#   - 日本語をto_yamlするとエンコードされてしまう問題を安直な方法で解決する
# のコードを流用しています。
# sourceforge 登録時は、削除 or 自作のものへの置き換えをします。

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

2008-01-01

書籍 Practical JRuby on Rails を買った

昨日、紀伊国屋の洋書売場を覗いたら
http://www.apress.com/book/view/1590598814
Practical JRuby on Rails Web 2.0 Projects: Bringing Ruby on Rails to Java
があった。買ってしまった... "foweard by Martin Fowler" だ。

MacOS 10.4, ActiveRecord-JDBC (0.5), rails (1.2.6), mysql5
の環境で書籍のサンプルコードを試している。
でも、junit テストが動作しない。(rails アプリは問題なく動作する)

http://d.hatena.ne.jp/ksaito11/20070930
に記されている情報を参照したが解消できず...

$ jruby -S rake test:units
rake aborted!
Task not supported by 'jdbc'

上記 記事では、task ファイルを lib/tasks にcopy すればよいとある。
しかし、copy すると今度は以下のエラーになる。

rake aborted!
undefined method `last' for {}:Hash

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

2007-10-13

rspec で java クラスをテストする例

rspec をつかって java クラスをテストする例を作ってみた。

1. 簡単な java クラスを定義。
2. Rakefile をつくる。
     jruby -S spec ... として、上で作成した java クラスのメソッドの挙動をテストする。

1. java クラス
===========

$ cat MyClass.java
package foo;

public class MyClass {
    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        return a - b;
    }

    public static void main(String[] args) {
        System.out.println("This is MyClass.");
    }
}

2. Rakefile
=========

$ cat Rakefile
require 'rake'

MYLIB ="build/lib"
MYCLASS_JAR = "MyClass.jar"

task :default => :compile

desc "Compile the native Java code."
task :compile do
  mkdir_p "build/classes"
  sh "javac -d build/classes #{FileList['**/*.java'].join(' ')}"
end

desc "Generate jar."
task :jar =>[:compile] do
  mkdir_p "#{MYLIB}"
  sh "jar cvf #{MYLIB}/#{MYCLASS_JAR} -C build/classes/ ."
end

desc "Remove build folder"
task :clean do
  rm_rf "build"
  sh "rm -f *~"
end

desc "Run sample program."
task :run_sample => [:jar] do
  sh "CLASSPATH=#{MYLIB}/#{MYCLASS_JAR}:$CLASSPATH;jruby callMyClass.rb"
end

desc "Run rspec."
task :spec =>[:jar] do
  sh "CLASSPATH=./build/lib/MyClass.jar:$CLASSPATH;jruby -S spec -c -fs MyClass_spec.rb"
end

まだ、Rakefile の書き方に慣れていない。もっと洗練した書き方ができるように勉強しないと...

spec ファイル
===========

$ cat MyClass.rb

require 'rubygems'
require 'java'

include_class 'foo.MyClass'

describe MyClass, "add method" do
it "should return sum" do
   myClass = MyClass.new
   myClass.add(1,2).should == 3
end
end

describe MyClass, "sub method" do
it "should return sub" do
   myClass = MyClass.new
   myClass.sub(1,2).should == -1
end
end

ここでは
  add(1,2) が 3 を返すこと
  sub(1.2) が -1 を返すこと
をチェックしている。

実行例:
======

$ rake spec
javac -d build/classes MyClass.java
jar cvf build/lib/MyClass.jar -C build/classes/ .
Java::Foo::MyClass add method
- should return sum

Java::Foo::MyClass sub method
- should return sub

Finished in 0.339 seconds

2 examples, 0 failures

おまけ:
=======
ruby から java クラスを呼び出す簡単な例

$cat callMyClass.rb
# set CLASSPATH=$CLASSPATH:MyClass.jar
# jruby callMyClass.rb

require 'rubygems'
require 'java'

include_class 'foo.MyClass'

obj = MyClass.new
p obj.add(1,10)
p obj.sub(1,10)

xfy/xvcd 用につくった xpath関数、インストラクションの簡易テストに junit でなく
Rspec をつかうことも面白いかもしれない。

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

2007-09-02

郵便番号検索ページをプログラムから利用してみる(その2)

前回は、結果を cosole に出していた。
xfy/xvcd で利用するには、xml 形式で出力したほうが便利である。
そこで、rss 形式出力するように変更した。

$ ruby zipcode-rss.rb "100-0002" | nkf -s
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>zipcode</title>
    <link>http://www.excite.co.jp/postcode/</link>
    <description>search zipcode</description>
    <item>
      <title>100-0002</title>
      <link>http://www.excite.co.jp/postcode/list/1000002.html</link>
      <description>1, 東京都 千代田区  皇居外苑</description>
      <pubDate>Sun, 02 Sep 2007 12:41:33 +0900</pubDate>
    </item>
  </channel>
</rss>

下に ruby コードを示す。
require 'kconv' を使った 最後の行の .to_s.toutf8 をなくせれば、jruby でも動作するのだが...。
toutf8 をやめると、私の環境では、shift_jis で出力されてしまう...

# excite の郵便番号検索ページを ruby から利用する。
# 検索結果の最初のページ(50件まで) 内容を rss にする。
#   useage: ruby zupcode-rss.rb keyword
#
#   キーワードには、 住所の一部、郵便番号の一部 を指定できる。
#   space で区切って、複数を指定することも可能。
#
# keyword ='赤坂ミッドタウン・タワー(1階)'
# keyword ='100-0002'

$KCODE = "utf-8"

require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'rss'
require 'kconv'

# 郵便番号検索ページの URL
url = 'http://www.excite.co.jp/postcode/'
rdf = 'zipcode.rdf'

keyword = ARGV[0]

doc = Hpricot(open('http://www.excite.co.jp/postcode/search/?keyword=' + keyword))

# rss を生成する
rss = RSS::Maker.make("2.0") { |maker|
  maker.channel.about = rdf
  maker.channel.title = "zipcode"
  maker.channel.description = "search zipcode"
  maker.channel.link = url

  doc.search('//table[4]/tr') { |tr|
    address1 = tr.search('td[1]/font/text()').to_html
    address2 = tr.search('td[1]/text()').to_html
    zipcode = tr.search('td[2]/b/text()').to_html
    if (address2 != "") then
      item = maker.items.new_item

      item.date = Time.new()
      item.title = zipcode
      item.description = address1 + address2
      tr.search('td[4]/font/a').each {|e|
        item.link = 'http://www.excite.co.jp' + e.attributes['href']
      }
    end
  }
}

puts rss.to_s.toutf8
# open(rdf, 'w') {|file|
#  file.write(rss.to_s.toutf8)
#}
#--- End of File ---

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

2007-07-22

HSQLDB で郵便番号を読んだけど動作が遅かった

HSQLDB (1_8_0_7)の ManagerTool で csv データを読み込んでみたが、検索などに時間が掛かりすぎる。
HSQLDB をつかうこともあきらめよう。
結局 MySQL にするか...

HSQLDB の設定法を記録だけしておこう。

サーバーの起動:
=============

$ cat runserver.sh
#!/bin/sh
java -cp ~/work/www/hsqldb/lib/hsqldb.jar org.hsqldb/Server -database zips

ManagerTool の起動:
==================

$ cat runmanager.sh
#!/bin/sh
java -classpath  java -classpath ~/work/www/hsqldb/lib/hsqldb.jar org.hsqldb.util.DatabaseManager -url jdbc:hsqldb:hsql://localhost

テーブルの作成:
==============
ManagerTool で次の sql を実行する。

$ cat create-zips.sql
DROP TABLE IF EXISTS zips;
CREATE TEXT TABLE zips (
  id int NOT NULL ,
  code char(5) DEFAULT '' NOT NULL,
  old_zip char(5) DEFAULT '' NOT NULL,
  new_zip char(7) DEFAULT '' NOT NULL,
  pref_ruby char(12) DEFAULT '' NOT NULL,
  city_ruby char(38) DEFAULT '' NOT NULL,
  town_ruby char(52) DEFAULT '' NOT NULL,
  pref char(8) DEFAULT '' NOT NULL,
  city char(20) DEFAULT '' NOT NULL,
  town char(34) DEFAULT '' NOT NULL,
  divided_flg char(1),
  lower_case_flg char(1),
  blocks_flg char(1),
  merge_flg char(1),
  update_code char(1),
  reason_code char(1),
  PRIMARY KEY (id)
);
SET TABLE zips SOURCE "new.csv;fs=,;encoding=UTF-8";

Hsql003

Hsql002

Hsql001

アマゾンのバーゲン

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

ActiveRecord-JDBC は まだsqlite は未サポートだった

ActiveRecord-JDBC は まだsqlite は未サポートだった
http://jruby-extras.rubyforge.org/ActiveRecord-JDBC/
> > File: README.txt
には サポート済みの DB が列挙されている。(sqliteは含まれていなかった.残念)
    * MySQL
    * PostgreSQL
    * Oracle
    * Microsoft SQL Server
    * DB2
    * FireBird
    * Derby
    * HSQLDB

Derby か HSQLDB をつかうことにしよう。
(自分で sqlite をサポートでいるように変更するベキかもしれないけど、すぐにはできそうもないし...)

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

2007-07-21

ruby/java から sqlite3 をつかう

郵便番号を sqlite3 に格納した。
それを ruby から activerecored で アクセスすることができた。
jruby からのアクセスはまだできていない。
(activerecord-jdbc をつかってできるはず。
その為の準備として、sqkite3 の jdbc ドライバー動作は確認できているが...)

参照:
  - http://d.hatena.ne.jp/rudeboyjet/20060723
  > > よしだメモ: ダミーの住所データを作るスクリプト

  - http://www.tuyudaku.net/sqlite/import.html
  > > SQLite - CSVファイルのインポート

  - http://blog.uhawwwokkwwwww.com/2007/04/sqlitejava.html
  > > SQLiteをJavaで使う

数日中には、xfy/xvcd から jruby でsqlite3 アクセスできるようにしたい。
(java からのアクセスができているから、この方法でxfy/xvcd から
sqlite3 アクセスする xpath関数、instruction をつくるのは簡単なはず。
でも、java で書く データベースアクセスは面倒だから、jruby で書きたいのだ!)

ruby でアクセス例:
=================

$ cat zips-sample.rb
require 'rubygems'
require 'active_record'
require 'sqlite3'

require 'kconv'
require 'pp'

$KCODE = 'u'

ActiveRecord::Base.establish_connection(
   :adapter => 'sqlite3',
   :database => 'zips.db'
)

class Zips < ActiveRecord::Base
end

# z = Zips.find(:all)
# z.each { |v|
#   p v
# }

f = Zips.find_by_id(1)
pp f
pp "id = " + f.id.to_s
pp "city = " + f.city
pp "code = " + f.code

#--- End of File ---

gem で 次のものを install した環境で動作している。
   sqlite3-ruby (1.2.1)
   activerecord (1.15.3)

java でのアクセス例:
=================

$ cat SQLiteTest.java
// See http://blog.uhawwwokkwwwww.com/2007/04/sqlitejava.html
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SQLiteTest {

    // 指定パスにDBファイルが無い場合は勝手に作ってくれるっぽい
    private static final String DB_URL = "jdbc:sqlite:TestDB";
    private static final int INSERT_MAX = 5;

    public static void main(String[] args) throws Exception {

        Connection c = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            Class.forName("org.sqlite.JDBC");
            c = DriverManager.getConnection(DB_URL);

            // AutoCommitはFalseだろ・・・常識的に考えて
            c.setAutoCommit(false);

            // あれこれSQLを実行

            // まずテスト用テーブル作成
            String sql
                = "create table uhawwwokkwwwww("
                    + " key int not null primary key"
                    + ",name varchar);";
            st = c.prepareStatement(sql);
            int ret = st.executeUpdate();
            System.out.println("テーブル作成:" + ret);
            st.close();
            st = null;

            // 次にデータ作成
            sql = "insert into uhawwwokkwwwww"
                    + " values(?,?)";
            st = c.prepareStatement(sql);
            for (int i = 0; i < INSERT_MAX; i++) {
                int cnt = 1;
                st.setInt(cnt++, i + 1);
                st.setString(cnt++, "うはwwwおkk" + repeatString(i, "w"));

                ret = st.executeUpdate();
                System.out.println("データ作成" + i + ":" + ret);
            }
            st.close();
            st = null;

            // データ取得
            sql = "select x1.key, x1.name from uhawwwokkwwwww x1;";
            st = c.prepareStatement(sql);
            rs = st.executeQuery();
            System.out.println("データ取得開始=====================");
            while (rs.next()) {
                int key = rs.getInt("key");
                String name = rs.getString("name");
                System.out.println("Key:" + key + "  Name:" + name);
            }
            System.out.println("データ取得完了=====================");
            rs.close();
            rs = null;
            st.close();
            st = null;
            // テーブル削除
            sql = "drop table uhawwwokkwwwww;";
            st = c.prepareStatement(sql);
            ret = st.executeUpdate();
            System.out.println("テーブル削除:" + ret);
            st.close();
            st = null;

        } finally {
            // この辺は、お約束/おまじないの類
            if (rs != null) {
                rs.close();
                rs = null;
            }
            if (st != null) {
                st.close();
                st = null;
            }
            if (c != null) {
                c.close();
                c = null;
            }
        }
    }

    /**
     * 指定回数指定文字を繰り返すくだらないメソッド
     * @param cnt 繰り返す数
     * @param s 繰り返す文字
     * @return 処理結果の文字列
     */
    private static String repeatString(int cnt, String s) {
        StringBuffer sb = new StringBuffer(s.length() * cnt);
        for (int i = 0; i < cnt; i++) {
            sb.append(s);
        }
        return sb.toString();
    }
}

$ javac cp=. SQLiteTest.java
$ java -cp sqlitejdbc-v036-native.jar:. -Djava.library.path=. SQLiteTest

jdbc ライブラリーは http://www.zentus.com/sqlitejdbc/ から v036 を download した。


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

2007-06-12

ruby で find + grep

ある文字列を含む行を再帰的に検索したくなった。

unix なら
  $ find . -name ¥*.xml| xargs grep version
のようにすれば済む要求だ。

windows ではどうすればよいか。
eclipse の file 検索でもいいが、コマンドファイルを作っておきたい。

すこし ruby のライブラリーを調べて次のようにしてみた。
(自宅では Mac しかないので、windows 上のruby で動作するからは会社でないとチェックできないけど)

require 'find'

def my_grep_version(path, reg)
  File.read(path).grep(reg) {|line|
    puts "#{path}¥t#{line}"
  }
end

Find.find(File.expand_path('.')) {|path|
  my_grep_version(path, /version/) if File.fnmatch("*.xml", path)
}

ruby (1.8.6) でも jruby (1.0) のどちらでも動作するようだ。

汎用的にするには、コマンドラインでファイル名パターンや検索文字列を得るようにするべきだが、今回はそこまでする必要は無い。
やるとしたら、zip などのファイルの中身もできるようにすることだ。
この拡張については、後日 やってみよう。

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

2007-05-22

ruby cairo で svg を生成してみた

http://jp.rubyist.net/magazine/?0019-cairo
るびま 19号の cairo: 2 次元画像描画ライブラリ の記事中の最初のサンプルは
png を出力するものだが、これを svg 出力に変更してみた。

----------

cat hinomaru-svg.rb
# See http://jp.rubyist.net/magazine/?0019-cairo

require "rubygems"
require 'cairo'

width = 300
height = 200
radius = height / 3

surface = Cairo::SVGSurface.new('hinomaru.svg', 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

context.show_page
surface.finish

---------

$ cat hinomaru.svg   
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300pt" height="200pt" viewBox="0 0 300 200" version="1.1">
<defs>
<clipPath id="clip0">
  <rect width="300" height="200"/>
</clipPath>
</defs>
<g id="surface0" clip-path="url(#clip0)">
<path style="stroke: none; fill-rule: nonzero; fill: rgb(100%,100%,100%); opacity: 1;" d="M 0 0 L 300 0 L 300 200 L 0 200 Z M 0 0 "/>
<path style="stroke: none; fill-rule: nonzero; fill: rgb(100%,0%,0%); opacity: 1;" d="M 216 100 C 216 136.45079 186.45079 166 150 166 C 113.54921 166 84 136.45079 84 100 C 84 63.54921 113.54921 34 150 34 C 186.45079 34 216 63.54921 216 100 "/>
</g>
</svg>

jruby でも実行しようとおもったが、
gem install cairo で、mkmf が無いとかのエラーでインストールできない...
ruby の lib 以下には mkmf.rb はあるが、jruby の lib 以下に mkmf.rb が無い。

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

Youtube を扱える ruby API, Rubyで作るProlog処理系, etc...

Youtube を扱える ruby API があることを知った。

- http://d.hatena.ne.jp/t-imaizumi/20070207/1170814526
> > t-imaizumiのMacとかのはなし - YouTubeのAPIを使ってみる。

このページによれば、他にもおもしろそうな API がたくさんあるようだ。

xfy 実験室 (http://xfy.justblog.jp/labs/ )で、 こういった ruby/perl のライブリーを利用した例も取り上げてみて欲しいなぁ。

ああ、こんな例も見つけた。
- http://codezine.jp/a/article/aid/461.aspx
> > CodeZine:Rubyで作るProlog処理系(Ruby, Prolog, 自動推論, 記号処理)

先日、"yaml <-> xml の相互変換 (名前空間に対応)"
http://youichi-kato.cocolog-nifty.com/blog/2007/05/xaml_xml_1777.html
を投稿した。
これを作っていて、思いついたことが1つある。
xfy の xvcd を ruby をつかって生成するのだ。

ちょっとした条件判定は if ()...else()  のような書式で書いておいて、
ruby で変換させるようにするとか...
また、共通で使う xvcd 記述を #incllude "lib/foo.xvcd" みたい記述しておいて、
ruby で include を展開させた結果を生成するとか...

ともかく xvcd の差分プログラミングを可能にする手法を構築したいものだ。
そのための手段 として、java でなく ruby のようなスクリプトを使うのが楽な気がする。

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

2007-05-20

druby での超簡易チャットシステム(jruby版)

jruby で書いたチャットシステムが jruby で動作しなかったのは、
$stdout の druby での扱いが ruby/jruby で異なっているのが原因のようだ。

そこで, $stdout の参照を直接 あつかうのではなく、ruby のクラスで包むことにした。
その版では、ruby, jruby とも動作した。
以下にソースを示す。
jserver.rb, jclient.rb, MyStream.rb がある。

$ cat jserver.rb
# 2007-05-20 katoy
# usage: $jruby server.rb
#     or $ruby server.rb
#

require 'drb/drb'
require 'MyStream'

class Chatroom
  def initialize
    @member = Hash.new
    @member["*"] = MyStream.new  # "*" is id for server.
  end

  def login name, stream
    @member[name] = stream
    broadcast "_", "login: #{name}"  # system information
  end

  def logout name
    broadcast "_", "logout: #{name}" # system information
    @member.delete name
  end

  def broadcast name, str
    @member.each {|key, val|
      val.xxputs "#{name}> #{str}"  if key != name
    }
  end

  def list name
    stream = @member[name]
    @member.each {|key, val|
      stream.xxputs " #{key}" unless key == "*"
    }
  end
end

uri = 'druby://localhost:12345'
DRb.start_service uri, Chatroom.new
puts 'Start Chat server...'
DRb.thread.join
#--- End of File ---

$ cat jclient.rb
# 2007-05-20 katoy
# usage: $ruby kato druby://loalhost:12350
#     or $jruby kato druby://loalhost:12350

require 'drb/drb'
require 'MyStream'

name = ARGV.shift
uri = ARGV.shift

DRb.start_service(uri)
room = DRbObject.new_with_uri 'druby://localhost:12345'

stream = MyStream.new

room.login(name, stream)

while s = gets.chop
  next  if s == ""              # ignore empty line
  break if s == 'exit'          # logout
  room.list(name) if s == "list" # show all member
  room.broadcast(name, s)        # send message to all member
end
room.logout(name)
#--- End of File ---

$ cat MyStream.rb
# 2007-05-20 katoy
#

require 'drb/drb'

class MyStream
  include DRbUndumped

  def xxputs(str)
    puts str
  end
end
#--- End of File ---

今度はさらに、$stdout の代わりに、java の System.out を使ってみよう。

cat MyStream.rb
# 2007-05-20 katoy
#

require 'drb/drb'
require 'java'
include_class 'JMyStream'

class MyStream
  include DRbUndumped

  def initialize
    @jstream = JMyStream.new
  end

  def xxputs str
    @jstream.xxputs str
  end
end
--- End of File ---

$ cat JMyStream.java
public class JMyStream {

    public JMyStream() {
    }

    public void xxputs(final String str) {
        System.out.println(str);
    }
}
--- End of File ---

これで、ruby クラス/java クラスを リモート/ローカルで呼び出せるようになったことになる。
これらのことを応用すれば、
  java クラスを ruby を使って張り合わせ、それらを適宜 分散させて実行するテストフレームワーク
を作れそうな気がする。

来週は java から jruby エンジンを稼働させたとき、呼び出し側のjava 環境の変数/メソッドを  jruby コードからアクセスできるかを試したい。
これができれば、xfy の xvcd で書きにくいビジネスロジックを ruby 構文をつかって記述することの実現がぐっと近くなってくる。

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

druby での超簡易チャットシステム

druby の解説ページ
- http://www2a.biglobe.ne.jp/%7Eseki/ruby/d205.html
> > 5 pass by reference, pass by value
に、stdout の参照をサーバー側に登録する例がある。

これを応用して、簡易チャットシステムを作ってみた。
アイデアは次のとうり。
- サーバー側では、ハッシュで {name, $stdout} の組で、クライアント情報を保持する。
- クライアントからのメッセージを、ハッシュ中のすべての $stdoutに puts する。

使い方は
  ターミナル1: $ ruby server.rb
  ターミナル2: $ ruby client.rb foo druby://localhost:12340
  ターミナル3: $ ruby client.rb foo druby://localhost:12341
                  (クライアント毎に port は異なるものを指定する必要がある)

client では、 "exit" を入力すると、logout してプログラム終了する。
              "list" を入力すると、login しているユーザー名一覧を表示する。
              空行は無視
              それ以外の入力行は、他のクライアント、サーバーのコンソールに表示される。

以下にプログラムを示す。

$ cat server.rb
# 2007-05-20 katoy
# usage: $ruby server.rb
#      

require 'drb/drb'

class Chatroom
  def initialize
    @member = Hash.new
    @member["*"] = $stdout  # "*" is id for server.
  end

  def login name, stream
    @member[name] = stream
    broadcast "_", "login: #{name}"  # system information
  end

  def logout name
    broadcast "_", "logout: #{name}" # system information
    @member.delete name
  end

  def broadcast name, str
    @member.each {|key, val|
      val.puts "#{name}> #{str}"  if key != name
    }
  end

  def list name
    stream = @member[name]
    @member.each {|key, val|
      stream.puts " #{key}" unless key == "*"
    }
  end
end

uri = 'druby://localhost:12345'
DRb.start_service uri, Chatroom.new
puts 'Start Chat server...'
DRb.thread.join
--- End of File ---

$ cat client.rb
# 2007-05-20 katoy
# usage: $ruby kato druby://loalhost:12350
#

require 'drb/drb'

name = ARGV.shift
uri = ARGV.shift

DRb.start_service(uri)
room = DRbObject.new_with_uri 'druby://localhost:12345'

room.login name, $stdout

while s = gets.chop
  next  if s == ""              # ignore empty line
  break if s == 'exit'          # logout
  room.list name if s == "list" # show all member
  room.broadcast name, s        # send message to all member
end
room.logout name
#--- End of File ---

この程度の行数で作れてしまうとは驚き。(エラー処理、mutex 処理などは抜けてるけど)
java で同様のものを作ろうとしたら、それなりの量を記述する必要があるし、
修正の度に compile が必要で、時間もかかるだろう。

上記のプログラムは、jruby では なぜか動作しない。
server は起動できるが、client を起動すると、次のエラーが出て起動できない。
/Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:585:in `load': Ruby "NativeException" object can not be allocated (TypeError)
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:585:in `load'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:633:in `exclusive'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:594:in `load'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:633:in `recv_reply'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:921:in `recv_reply'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1195:in `send_message'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1086:in `method_missing'
        from client.rb:14:in `open'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1087:in `method_missing'
        from client.rb:14:in `with_friend'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1089:in `method_missing'
        from client.rb:14

このチャットシステムの GUI を xfy/xvcd で作る事はできるか?
$stdout へ出力されたことのイベントをキャッチして、xfy の画面更新するが出来る必要がある。どうすれば良い?...

xfy の メモリー上の srcDOMの参照を server に登録して、サーバーが srcDOM に変更を加える?
srcDOM を変更する xfy メソッドを server から呼び出す?

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

2007-05-19

wirble で irb が使いやすくなる

便利なツールを見つけた。

- http://journal.mycom.co.jp/articles/2006/12/22/wirble/index.html
> > 【ハウツー】Rubyの小技 - irbに補完・シンタックスハイライト機能をつけてみる | エンタープライズ | マイコミジャーナル

この機能は便利!
jruby 1.0.0 の jirb でも動作する。

jruby でつかうには install は $ jruby $JRUBY_HOME_bin/gem install wirble
のように jruby を使って行うことが必要。

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

yaml <-> xml の相互変換 (名前空間に対応)

yaml <-> xml の相互変換。
完全な変換はできないが、そこそこの動作をするものが出来た。

(jruby でも動作する。
  gem で builder, xml-simple を install することが必要。
  $ jruby $JRUBY_HOME/bin/gem install buildre
  $ jruby $JRUBY_BOME/bin/gem install xml-simple
)

以下に xml->yaml のソース、yaml->xml のソースを示し、変換結果例を示そう。

xml->yaml は次のようにする。
  xml -> Hash -> yaml

---- yaml2xml.rb ----
# See http://www.namikilab.tuat.ac.jp/%7Esasada/prog/yaml.html#i-2

# gem install xml-simple

require 'yaml'
require 'rexml/document'
require 'rubygems'
require 'xmlsimple'
require 'pp'

good_xml = %{
<groceries>
<bread>Wheat</bread>
<bread>Quadrotriticale</bread>
</groceries>}

# puts "----------------"
# doc = XmlSimple.xml_in good_xml
# pp doc
# puts doc.to_yaml

doc = XmlSimple.xml_in File.open(ARGV[0])
puts "----------------"
puts doc.to_yaml
puts "----------------"
pp doc

yaml->xml は、http://www.unfitforprint.com/articles/2005/09/23/yaml2xml-in-33-lines-or-your-money-back
にあったコードを元に、xml 宣言を出力する/namespace, prefix 指定を可能にするようにしてみた。

---- yaml2xml.rb ----
# See  http://www.xml.com/lpt/a/1637
#      http://www.unfitforprint.com/articles/2005/09/23/yaml2xml-in-33-lines-or-your-money-back
#
# Modifyed by katoy 2007-05-18

require 'yaml'
require 'rubygems'
require 'builder'
require 'pp'

class Yaml2Xml

  def initialize roottagname, prefix="", uri=""
    @roottagname = roottagname
    @prefix, @url = prefix
    @url = uri

    # @builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
    @buffer=""
    @builder = Builder::XmlMarkup.new(:target=>@buffer, :indent=>2)
  end

  def buffer
    return @buffer
  end

  def to_xml src
    @builder.instruct!
    if @prefix == ""
      @builder.method_missing(gentag(@roottagname)) {
        process_source src
      }
    else
      @builder.method_missing(gentag(@roottagname), "xmlns:" + @prefix => @url) {
        process_source src
      }
    end
  end

  def yaml2xmlChild src, tagname
    if src.is_a?(Hash)
      @builder.method_missing(gentag(tagname)) {
        process_source src, tagname
      }
    elsif src.is_a?(Array)
      process_source src, tagname
    else
      @builder.method_missing(gentag(tagname), src)
    end
  end

  def process_source src, tagname=""
    if src.is_a?(Hash)
      src.each do |name, value|
        unless value.kind_of?(Hash) or value.kind_of?(Array)
          @builder.method_missing(gentag(tagname), value)
        else
          yaml2xmlChild value, name
        end
      end
    elsif src.is_a?(Array)
      src.each do |value|
        unless value.kind_of?(Hash) or value.kind_of?(Array)
          yaml2xmlChild value, tagname
        else
          if value.empty?
            yaml2xmlChild "", tagname
          else
            yaml2xmlChild value, tagname
          end
        end
      end
    else
      @builder.method_missing(gentag(tagname), value)
    end
  end

  def gentag tagname   
    return tagname if @prefix == ''
    return @prefix + ":" + tagname
  end

end

yaml = YAML::load(File.open(ARGV[0]))
# puts "---- YAML ----"
# pp yaml
# puts yaml.to_yaml

puts "---- XML ----"
x = Yaml2Xml.new 'root', 'ab', 'http://xxx.com'
x.to_xml yaml
puts x.buffer

これを利用して、xml -> yaml -> xml 変換した結果を示そう。
最初のxml:
========

  xfy のチュートリアル中の住所録 xml の形式をまねたものだ。

<?xml version="1.0" encoding="UTF-8"?>
<ab:root xmlns:ab="http://xxx.com">
  <ab:entry>
    <ab:address>Rochester, NY USA</ab:address>
    <ab:person>George Adams</ab:person>
  </ab:entry>
  <ab:entry>
    <ab:address>Denver, Colorado USA</ab:address>
    <ab:person>Betty Jackson</ab:person>
  </ab:entry>
  <ab:entry>
    <ab:address></ab:address>
    <ab:person></ab:person>
  </ab:entry>
  <ab:entry></ab:entry>
</ab:root>

xml->yaml 結果
=============
実際のコンソール出力から yaml 部分を抜粋したもののみを示す。

$ ruby xml2yaml rb 1.xml
entry:
- address:
  - Rochester, NY USA
  person:
  - George Adams
- address:
  - Denver, Colorado USA
  person:
  - Betty Jackson
- address:
  - {}

  person:
  - {}

- {}

xmlns:ab: http://xxx.com

yaml->xml 結果
=============

上の結果の最後の行の xmlns:ab: http://xxx.com を削除したものについて実行する。(実際のコンソール出力から xml 部分を抜粋したもののみを示す)

$ ruby yaml2xml.rb 2.yaml
<?xml version="1.0" encoding="UTF-8"?>
<ab:root xmlns:ab="http://xxx.com">
  <ab:entry>
    <ab:address>Rochester, NY USA</ab:address>
    <ab:person>George Adams</ab:person>
  </ab:entry>
  <ab:entry>
    <ab:address>Denver, Colorado USA</ab:address>
    <ab:person>Betty Jackson</ab:person>
  </ab:entry>
  <ab:entry>
    <ab:address></ab:address>
    <ab:person></ab:person>
  </ab:entry>
  <ab:entry></ab:entry>
</ab:root>

これで、ruby で生成したデータを xml 化して、xfy/xvcd に送り込むことが出来そうだ。
ruby で RDB などにアクセスして得たデータや、druby や rinda 上で取得したデータなども xml に加工して xfy/xvcd に送り込める。

逆に xfy/xvcd から 渡される xml を ruby 側で hash/yaml にして、それ以降の処理を行うこともできる。

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

2007-05-16

jruby1.0.0 で druby 動作が確認できた

数日前に、jruby 1.0.0 で druby サンプルを動作させてみたときは、上手くできなかったが、やっと解決した。

http://www2a.biglobe.ne.jp/%7Eseki/ruby/d203.html
の druby の例はそのままでは、jruby1.0.0 では動作しなかった。
(もちろん ruby 1.8.6 ではそのままで動作した)

次のように outs00.rb を変更した。(太字が変更部分)

require 'drb/drb'

class Puts
  def initialize(stream=$stdout)
    @stream = stream
  end

public
  def puts(str)
    @stream.puts(str)
  end
end

uri = ARGV.shift
DRb.start_service('druby://localhost:12345', Puts.new)
puts DRb.uri
# sleep
DRb.thread.join

まず、sleep というのは、引数がないということでエラーになってしまう。

次に puts メソッドを public 指定をしないと、次のようなエラーがでた。

/opt/local/lib/ruby/1.8/drb/drb.rb:736:in `open': druby://localhost:12345 - #<Errno::ECONNREFUSED: Connection refused - connect(2)> (DRb::DRbConnError)

明日は jruby で rinda の例を動作させることに挑戦しよう。

その後は、xfy/xvcd からの druby/rinda の利用だ。

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

2007-05-14

jruby から java メソッドを呼び出す

jruby から java メソッドを呼び出す例を試してみた。無事 動作する。
ここでは、
  標準クラスのメソッド呼び出し、
  自作の java クラスのメソッド呼び出し、
  Exception の扱い
を試している。

kato$ cat Sample.java
public class Sample {

    public void sub() {
        System.out.println("sub");
    }
}


kato$ cat java00.rb
# See http://jruby.codehaus.org/The+JRuby+Tutorial+Part+1+-+Getting+Started

require 'java'
include_class 'java.util.TreeSet'

# java の TreeSet を使う
set = TreeSet.new
set.add "foo"
set.add "Bar"
set.add "baz"

set.each do |v|
  puts "value: #{v}"
end

# String は ruby が利用済みなので、
# java.lang.String を JString として使えるようにする。
include_class('java.lang.String') {|package,name| "J#{name}" }

s = JString.new "a"
puts "s.class: #{s.class}"

rs = String.new
puts "rs.class: #{rs.class}"

# java の例外を扱う
begin
  s1 = JString.new "0123456789"
  p s1.substring(2)
#  p s1.substring(20)
rescue RuntimeError => e
  puts "Java or Ruby exception: #{e}"
  raise
end

include_class 'Sample'

sample = Sample.new
sample.sub

#--- End of File ---

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

jruby 1.0.0 で druby は動作しないのか?

- http://www2a.biglobe.ne.jp/%7Eseki/ruby/d203.html
> > 3 Hello, dRuby.
にある、ちいさな druby のサンプルを jruby で動作させてみたが、動作しない。

kato$ jruby puts00.rb druby://localhost:12345
druby://localhost:12345
puts00.rb:[16,16]:[204,236]: wrong number of arguments(0 for 1) (ArgumentError)
kato$

drury は jruby ではまだ動作しないのかなぁ。

サーバーは ruby で動作させ、client を jruby で動作させてみたけど、これも駄目だった。

kato$ jirb
irb(main):001:0> require 'drb/drb'
=> true
irb(main):002:0> there = DRbObject.new_with_uri('druby://localhost:12345')
=> #<DRb::DRbObject:0xe7ed2d @uri="druby://localhost:12345", @ref=nil>
irb(main):003:0> there.puts('Hello, World.')
DRb::DRbConnError: druby://localhost:12345 - #<Errno::ECONNREFUSED: Connection refused>
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:736:in `open'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1189:in `each'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:738:in `open'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1189:in `initialize'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1169:in `new'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1169:in `open'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1087:in `method_missing'
        from (irb):1:in `with_friend'
        from /Users/kato/work/www/jruby-1.0.0RC1/lib/ruby/1.8/drb/drb.rb:1089:in `method_missing'
        from (irb):1:in `binding'
irb(main):004:0>

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

2007-05-13

jruby で gzip を扱う。

Rubyist Magazine の Zlib の紹介記事を読んだ。
   http://jp.rubyist.net/magazine/?0018-BundledLibraries

簡単に zip/gzip を扱えるな。
以下のコードは ruby/jruby ともに動作する。

# See http://jp.rubyist.net/magazine/?0018-BundledLibraries

require 'zlib'

orig = 'foo.txt'
gziped = orig + '.gz'

# create gziped file
#-------------------
Zlib::GzipWriter.open(gziped, Zlib::BEST_COMPRESSION) do |gz|
  gz.mtime = File.mtime(orig)
  gz.orig_name = orig
  gz.write File.open(orig, 'rb'){|f| f.read }
end

# gzcat
#------------------
Zlib::GzipReader.open(gziped) do |gz|
  puts gz.read
end

#--- End of File ---

xfy で、zip 保存したり、zip された xml を開くようなプラグイン
http://youichi-kato.cocolog-nifty.com/blog/2006/11/xfy_m.html
を java でつくって公開したことがあるけど、jruby で書いたほうが極めて簡単にかけるな。

xfy の Open/Save  コマンドへのフックを PRagger の confi.yaml 形式で書いて、そのなかに zip/unzip plagin を指定すると、save 時のダイアログに zip 圧縮をするか否かのチェックボックスが現れるなんていうようになると便利になるだろう。

同様の発想で、cvs アクセス、svn アクセス、webdav アクセス、XML-DB アクセス、gspace アクセスなプラグイン、PDF 化、暗号化、etc... のプラグインをつくればよいかもしれない。

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