ER図 作成ツール Erviz

テキストでテーブル定義を書いて、それを図に変換するという面白いツールを発見

http://www.ab.auone-net.jp/~simply/ja/index.html
http://d.hatena.ne.jp/simply-k/20100702/1278052011

テキストの記述方法がシンプルで出力される図もきれいなので、なかなかよさそう


このテキスト定義をもとにExcelでテーブル定義とか出力できると定義の一元管理ができるのでいいんだけど・・・
JRuby+POIで作れるかな?

Hibernate Validator

http://www.hibernate.org/subprojects/validator.html
JSR 303: Bean ValidationのRI(Reference Implementation)にもなっている。
アノテーションで制約を設定するようで、かなり使いやすそうな印象

Hibernateの名前が入っているがHibernateなしでも使用できる。
Spring MVCでは、コレを使うことが標準になっているみたい

JSPからRailsの機能を使う

制御構造にはJSTLが使えるからいいけど、フォームやリンクを生HTMLで書くのは不便なのでカスタムタグを用いて記述したいところ。
いろいろ考えたが、Railsの機能を使いつつタグファイルによるカスタムタグ作成ができたのでメモ。

JSPエスケープシーケンスを使う

<%や%>はRailsでも使うがJSPでもスクリプトレットとして解釈されてしまう。
これをJSPで解釈されずにRailsに解釈させたい。
そこでエスケープシーケンスを用いて<\%と\%>にすることで、JSPでの解釈を回避できた。
JSPにそのまま書く場合はこんな感じ

<\%= link_to "linkName", :action => "add", :v1 => 3, :v2 => 8 \%>

タグファイルの作成

JSP2.0からは便利なタグファイルの機能があるので、それを使ってカスタムタグを作成する。
まずは/WEB-INF/tags/railsディレクトリを作成し、その中にlink.tagファイルを作成する。
こうすることでJSPの中で以下のようにしてタグを使える。

<%@ taglib tagdir="/WEB-INF/tags/rails" prefix="r"%>
<r:link 〜〜〜/>


link.tagの中身をこんな感じで書けば

<%@ tag pageEncoding="UTF-8"%>
<%@ attribute name="action" required="true"%>
<%@ attribute name="params"%>
<%@ attribute name="attrs"%>
 
<\%= link_to "<jsp:doBody/>", {:action => "${action}"<% 
if(params != null) out.print(", "+ params); %>}<%
if(attrs != null) out.print(", "+ attrs); %> \%>

JSPではこうして使える

<r:link action="add" params=":v1 => 11, :v2 => 12" 
attrs=":id => :someId, :style => 'font-weight:bold'">Link</r:link>

RailsのController実装をJavaで作る

前回でViewにJSPを使えるようになったので今回はController。
とりあえず以下のようなJavaのActionクラスをRailsのControllerにマッピングすることを考える。

package sample;

public class SampleAction {
	public Integer result;
	public String v1;
	public String v2;
	
	public String index(){
		return "test.jsp";
	}
	public String add(){
		result = Integer.parseInt(v1) + Integer.parseInt(v2);
		return "test.jsp";
	}
}

publicメソッドがRailsのControllerで定義したのと同じような感じで使えるようになればいい。
publicフィールドは入出力に使うこととする。

Rails Controller

JavaActionというJRubyの自作Moduleをjava_action_module.rbファイルとして作成しておいて、RailsのControllerではそれを利用してJavaのクラスで宣言されたメソッドを自動的にマッピングすることを考える。

(このコード自体をJavaで作ることも可能かもしれないが、めんどいのでやめておく)

require 'java_action_module'

class PollsController < ApplicationController
  include JavaAction
  def initialize
    add_methods("sample.SampleAction")  
  end
end

module JavaAction

さてここからが本題。
Javaクラスのメソッド情報を取得して、Rubyでメソッドを動的に定義できればよい。
また、JavaクラスのフィールドをViewとの入出力で使えるようにする必要がある。


まずは、後でpublicメソッド・フィールドを取得するときのためにMemberにpublic?メソッドを定義しておく。(Javaの既存クラスにメソッドを追加定義するにはJavaUtilities.extend_proxyを使う)

module JavaAction
  JavaUtilities.extend_proxy "java.lang.reflect.Member" do
    def public?
      java.lang.reflect.Modifier.isPublic(self.getModifiers)
    end
  end

Method,FieldはMemberインターフェースを実装しているので、Method,Fieldでもpublic?メソッドが使えるようになった。
Javaのインターフェースにもメソッドを追加定義できるのがJRubyの面白いところ。



次は、ViewにJSPを使うためのコード(JSP実行結果の取得)。前回のとほぼ同じ

  def jsp(path)
    writer = java.io.StringWriter.new
    req = request.env['java.servlet_request']
    resp = javax.servlet.http.HttpServletResponse.impl do |method|
      case method
      when :getWriter
        java.io.PrintWriter.new(writer)
      else
        logger.warn "method:#{method} called"
      end
    end

    $servlet_context.getRequestDispatcher(path).include(req, resp)
    writer.toString
  end

そして、メソッドを動的追加するためのメソッド。
まずは、Javaクラス内で宣言されたpublicメソッド・フィールドの配列を取得する
(.select &:public? の部分でpublicなものだけ取るようにしている)

  def add_methods(java_class)
    clazz = java.lang.Class.forName(java_class)
    methods = clazz.declared_methods.select &:public?
    fields = clazz.declared_fields.select &:public?


次に呼び出し元のファイルパスからViewファイルが入ったディレクトリのパスを取ってくる。
(RequestDispatcherに渡すときのパスに使うので/WEB-INF/から始まる文字列になる)

    path = caller[0].sub(%r!.*(/WEB-INF/.*/app/).*/(.*)_controller.rb.*!, '\1views/\2/')


Javaクラスのメソッドごとにdefine_methodで動的にメソッドを作成する。
define_methodはModuleのコンテキストで呼ぶ必要があるのでmodule_eval内で呼ぶこととなる。

    methods.each do |m|
      JavaAction.module_eval do
        define_method(m.name) do


define_methodの中では、まずHttpServletRequestの取得とJavaクラスのインスタンス化をして、

インスタンスのフィールドにリクエストのパラメータを入れて、
インスタンスのメソッド実行をする

          req = request.env['java.servlet_request']
          instance = clazz.newInstance

          fields.each do |f|
            instance.send("#{f.name}=", params[f.name]) unless params[f.name].nil?
          end
          page = instance.send(m.name)

最後にリクエストのAttributeにフィールドの名前と値を入れて、JSP呼び出しを行って完了。

          fields.each{|f| req.setAttribute(f.name, f.get(instance))}

          render :inline => jsp(path + page), :layout => true
        end
      end
    end
  end
end


これでとりあえずJavaでのController実装ができることがわかった。
ただし、まだいろいろ課題がある。
バリデーションとかリダイレクトの対応も必要だし、JSPでカスタムタグ使いたいよね


何かフレームワークとか使えるのあるだろうか・・・