• JUDE APIを jrubyから使う その2

    JUDE 5.3には編集APIのサンプルとしてDBリバースのプログラムが付属しています。
    サンプルといっても十分便利。DB接続可能なときはこれを利用している。


    何かしらのシステムやアプリを引き継ぐと、DDLすら提示されず、「テーブル定義書」「テーブル設計書」といった名称のxlsブックだけ出てくる場合がある。困ったことによくある。
    目視でぽちぽちjudeにエントリしてもいいのだけど、編集APIのプラクティスを兼ねてインポートツールを書いた。

    要するにCSVで記述したERエンティティをjudeモデルとして一括登録、というもの。
    ※注意:judeファイルになんらか問題が発生しても責任を負いかねます。自己責任で。そしてバックアップ or リビジョン管理重要。

    judeユーティリティ


    1. xlsからCSVを作成する
      • 記述例はアーカイブ同梱のsamp1.xlsを参照
      • 「テーブル定義書」のレイアウトは様々。それに合わせて以下のようなCSVファイルを出力するようマクロを調整のこと
        • 1CSVファイル=1エンティティ
        • 1行=1属性
      • CSVファイル内の最初の列が「#」で始まる行はメタ情報行
        • #@entity行
          • 1列目:エンティティの論理名
          • 2列目:エンティティの物理名
        • 次に#で始まる行は各列の値の意味を指定するヘッダ。認識するヘッダは以下の通り
          logicalname属性の論理名
          physicalname属性の物理名
          domain属性のドメイン(の論理名)
          更新対象judeファイル内に同名の論理名を持つドメインがない場合は作成される
          type属性のデータ型
          更新対象judeファイル内に同名のデータ型がない場合は作成される
          length属性の長さ
          nnNOT NULLかどうか
          非空文字ならNOT NULL指定
          default属性のデフォルト値
          pkPRIMARY KEY制約をつけるかどうか
          非空文字ならPRIMARY KEY指定
      • excelがインストールされたwindows上で同梱のmake_csv.jsマクロ実行し、全シートをCSV出力します
        • 出力ファイル名は"シート名.csv"になります。
          シートごとに別ファイルに出力されます

          > cscript.exe make_csv.js samp1.xls
      • 編集APIの仕様で以下の点に注意
        • 論理名のないエンティティは登録できない
        • 論理名のない属性は登録できない
        • 同じ論理名を持つエンティティが既に存在すると登録できない
        • データ型に英小文字を指定しても英大文字になる
          • GUIでは英小文字名称のデータ型を登録できるけど、編集APIでcreateすると大文字になる。
    2. 出来たCSVをjudeファイルにインポートする
      • 実行例

        $ env CLASSPATH=./jude-pro.jar jruby ./import_ermodel.rb --fs=, -o new.jude samp2.csv
        ※インポートのデフォルトのフィールド区切りは「HT」です。--fsでカンマを指定しています。
        ※jude-pro.jarがカレントにある前提
      • 存在しないjudeファイルを指定してもよいし、既存のjudeファイルを指定してもよいです。
        存在しないjudeファイルを指定するとjudeファイルが作成されます。
        新規judeファイルに出力しインポート後の内容を確認して問題なさそうならマージするという手順がいいと思う。
      • 本スクリプトは日本語環境で作成されたjudeファイルを前提としています。
        編集APIサンプルのように createERModel(project, "ER Model");でIERModelを作成すると、日本語環境GUIでマージできなかった。日本語環境GUIで作ったjudeファイルの場合、IERModelの名称が"ERモデル"(これはエクスポートするとわかる)となっていることに関係するのではないかと想像している。

    ■実行環境
     OS: CentOS 5.2 i386
       JRE: Sun Java 1.6.0_07
         jruby: 1.1.4
         JUDE-Pro: 5.3.0

    モデル編集APIは、「図要素」を編集できるようになるとうれしい。図中のモデルの位置だとか大きさ。GUIの自動レイアウトがもうひとつだなぁと感じている。何かしらのレイアウトエンジンを使って独自の整列ができるようになったらかなり素敵だと思うんだけどな。


  • JUDE APIを jrubyから使う その1

    楽々ERDレッスン (CodeZine BOOKS)
    モデリングツールにJUDEを利用している。

    このツールを選んだポイントの1つはjavaを使ったAPIがあること。
    5.3になって、モデル編集のAPIも追加された。

    で、テーブル設計。

    過去に「楽々ERDレッスン (CodeZine BOOKS) 」を読み、「Identifier」の有用さに心打たれて以来、いつもID列を設けるようにしている。

    要求・要望・要件の断片からざっとエンティティを見出し、ID属性を付与、リレーションを付けて多重度を設定して・・・という手順でやっているのだけど、エンティティの数が多いと、ID属性を追加するのも結構手間。。。。

    ということで、モデル編集APIを扱うプラクティスも兼ねて、JUDEプロジェクト内のERエンティティにID属性を追加するスクリプトを書いてみた。

    judeユーティリティ

    ちょこちょことスクリプトを書くときは、screen+viな環境でやると調子がいい(僕は)。なので、linux上でしか確認してない。

    ■実行環境
     OS: CentOS 5.2 i386
       JRE: Sun Java 1.6.0_05
         jruby: 1.1.3
         JUDE-Pro: 5.3.0

    ■実行例

    アーカイブを展開し、jude-pro.jarを展開先ディレクトリに置いてある前提。

    ○ERエンティティ、「ID」という論理名でSMALLINT型の属性を追加する

    % env CLASSPATH=./jude-pro.jar jruby add_identifier.rb --attr-name=ID --datatype=SMALLINT judeファイルのパス
    

    ○ERエンティティに、「ID」という名前・INT型のERドメインで、「ID」という論理名の属性を追加する

    % env CLASSPATH=./jude-pro.jar jruby add_identifier.rb --attr-name=ID --domain=ID judeファイルのパス
    

    ○ERエンティティに、「ID」という名前・SMALLINT型のERドメインで、「エンティティ論理名_ID」という論理名の属性を追加する

    % env CLASSPATH=./jude-pro.jar jruby add_identifier.rb --attr-name=%e_ID --domain=ID --domain-datatype=SMALLINT judeファイルのパス
    
    指定したデータ型、ドメインが存在しない場合は作成します。
    作成された属性にはPK指定が付きます

    ■注意
    • READMEにも書いてありますが無保証です
    • judeファイルを書き換えますので、バックアップ必須です。ファイルが壊れても責任持てません。
    • (JUDEの機能による)ロックがかかりますので、更新対象JUDEファイルをGUIで編集していない状態で実行します
    • 既に主キーがついているエンティティには属性追加しません。
    • 唯一の主キーが作成しようとしてる属性論理名が同じ場合はデータ型/ドメインを更新します
    • 既にERモデルが作成されているJUDEファイル前提です。日本語環境以外で作成されたものがどうなるかは不明です。



  • extend_proxyで javaのクラスにメソッドを生やす


    以前、libxml-rubyで [BUG] object allocation during garbage collection phase で、元々libxml-rubyを使って書いてたコードをjavaのDOM/XPath APIに置き換えるために、ラッパを書いた。

    のだけど、

    「JRuby徹底入門」という本を読んだら、javaのクラス(のproxy)にメソッドを後付けするにはJavaUtilities.extend_proxyを使う、という例が載ってた。

    なるほど便利。

    なんでも実行時に決定できるってすごいことだな。

    jruby:1.1.3
    Sun Java: 1.6.0_05
    OS:CentOS 5.2

    $KCODE='u'

    require 'java'

    # org.w3c.dom.Documentにlibxml-ruby風I/F追加
    JavaUtilities.extend_proxy('org.w3c.dom.Document') do
    def root
    self.documentElement
    end
    end

    # org.w3c.dom.Nodeにlibxml-ruby風I/F追加
    JavaUtilities.extend_proxy('org.w3c.dom.Node') do
    @@xpath = nil

    def to_s
    self.nodeValue.to_s
    end

    def next
    self.nextSibling
    end

    def text?
    self.nodeType == org.w3c.dom.Node::TEXT_NODE
    end

    def [](name)
    self.getAttribute(name)
    end

    def parent
    self.parentNode
    end

    def child
    self.firstChild
    end

    # XPathオブジェクトを返す
    #
    # TODO: XPathオブジェクト1つで使いまわしてみたが・・・
    # evaluateの都度生成よりはいいような気がするがどうだろう
    def get_xpath
    if !@@xpath
    factory = javax.xml.xpath.XPathFactory.newInstance()
    @@xpath = factory.newXPath()
    end

    @@xpath
    end

    # 指定されたノードに対するxpath式のクエリ結果を返す
    #
    # expr::
    # XPath式
    def find(expr)
    nodelist = self.get_xpath.evaluate(expr, self, javax.xml.xpath.XPathConstants::NODESET)

    ret = []
    for i in 0..(nodelist.length - 1)
    ret.push(nodelist.item(i))
    end
    ret
    end
    end


    # 指定されたファイルをopenしてXML文書を返す
    #
    # fn_in::
    # ファイル名。nilの場合、stdinを適用
    def build_doc(fn_in)
    st = java.lang.System.in
    if fn_in
    st = java.io.FileInputStream.new(fn_in)
    end

    factory = javax.xml.parsers.DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()
    builder.parse(st)
    end


    doc = build_doc("test.xml")
    doc.find("//entity").each { |e|
    puts e["name"]
    }