Railsアプリのコンパイル成功?

2007/12/11 |jRuby |comments(0)

jRubyでコンパイルしたバイトコードでRailsアプリが動くようになりました。が、Rails本体に手を加えないといけないようだ。

前回、jRubyでコンパイルしてCLASSPATHを通せば、*.classと*.rbを透過的にrequireできることがわかった。
でも、Railsでは*.classをrequireしてくれないので、Railsではどういう風にクラスをロードしているのかを調べてみました。

まずは、models/user.rb をコンパイル後、削除。

 > cd app/models/
 > jrubyc user.rb
 > mv user.rb user.rb_bak
 > ls
  user.class user.rb_bak

Userをロードしてみると失敗する。

 > export CLASSPATH=./app/models
 > jruby script/console
 >> User
 NameError: uninitialized constant User
 from /usr/java/jruby-1.1b1/lib/ruby/gems
  /1.8/gems/activesupport-1.4.2/lib/active_support
  /dependencies.rb:272:in `load_missing_constant'
    :

この missing_constantメソッドが、動的クラスローダーのようです。初めてそのクラスにアクセスするときに、ロードしてキャッシュする。ということをしている。

クラスローダーの動き

この active_support/dependencies.rb のソースを辿ってみると・・・

■流れの抜粋

 # 最初のクラスロード
 def load_missing_constant(from_mod, const_name)
  # ファイルを探す。
  file_path = search_for_file(path_suffix)
  # あれば、load か require
  if file_path
    require_or_load file_path
  end
 end

 # クラスのソースファイルを探す。
 def search_for_file(path_suffix)
  # .rb をがっつりくっつけて、
  path_suffix = path_suffix + '.rb' \
          unless path_suffix.ends_with? '.rb'
  # 存在チェックしている。
 end

 def require_or_load(file_name, const_path = nil)
  # .rbがついてることが前提。
  file_name = $1 if file_name =~ /^(.*)\.rb$/
  expanded = File.expand_path(file_name)
  # キャッシュからとるか、
  return if loaded.include?(expanded)
  # loadするか、
  if load?
  # require するか、
  else
   result = require file_name
  end
end

という風に、クラスの拡張子が .rbでなければならないという制限がありました。

classをrequireするために

ちょっと(かなり)いたずらに改造してみる。

 ■require_or_loadでは、*.class なら、単純に requireする。
 def require_or_load(file_name, const_path = nil)
   if file_name.ends_with? '.class'
    return java_class_require file_name
   end
     :
 end

 def java_class_require(file_name)
  file_name = File.basename(file_name)
  file_name = $1 if file_name =~ /^(.*)\.class$/
  clazz = require file_name
  history << clazz
  clazz
 end

 ■*.class で search_for_fileがtrueを返す。
 def search_for_file(path_suffix)
  #path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb'
  path_suffix = path_suffix + '.class' unless path_suffix.ends_with? '.class'
     :
 end

と、修正すると、動きます。

 > jruby script/console
 >> User
 => User
 >> User.new
 => <#User ...>


とくにsearch_for_fileの仕様がこのままである限り、jrubyでコンパイルしてもRailsにはロードできないはず。

さて、これからどうするか。比較的簡単に実現できそうですが、そこまでしてやるか?というところで、思考停止。


過程で見つけたTips

■ consoleで、ソースの変更を反映するには、

 > ruby script/console
 >> reload!

いままで、その都度console再起動してた。

■ スコープ外でloggerを使いたい。

class SomeLib
 def logger
  ::ActionController::Base.logger
 end
 def your_method
  logger.debug('aa')
 end
end

■jrubyc が生成するpackage
前回は、

 > jrubyc app/**/*.rb

としましたが、これだと、java packageのパスがついてしまうので、(たぶん)よくない。jadしてみたら、こうなってました。

 package app.models;
 class User{
 }

コメント

コメントしてください
お名前:
入力しなければ「匿名さん」。20字以内。

メール:
入力しても表示しません

URL:
入力すればリンクが貼れます


コメント: