サクラエディタでカーソルが効かない
どうやら、sakura.iniを残したままexeを入れ替えると起きるらしい。
ので、sakura.iniを消して起動しなおしたら直りました。
CSVファイルでもDynamic Fixturesが使えるのか(結論: fixtures.rbにパッチを当てれば使える)
Class: FixturesのDynamic fixtures with ERbの節に、
In these cases, you can mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing
とか書いてあるので、YAMLだけじゃなくて、CSVでもDynamic Fixturesが使えそうな雰囲気です。
まず、こんなCSVファイルを用意しました。
id,name 1,test 2,hoge <% 10.times do |i| %> <%= (i+3) %>,hoge<%= i %> <% end %>
erbが埋め込まれているより前の部分までならば、正常にロードできます。
ただ、このままだと、
id,name 1,test 2,hoge 3,hoge0 4,hoge1 5,hoge2 6,hoge3 7,hoge4 8,hoge5 9,hoge6 10,hoge7 11,hoge8 12,hoge9
のようなCSVが出力されてしまい、このままFixturesに食わせると、
E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/conne ction_adapters/abstract/database_statements.rb:73:in `transaction': ActiveRecord ::ActiveRecordError: Syntax error: Encountered ")" at line 1, column 33.: INSERT INTO PEOPLE (id) VALUES () (ActiveRecord::StatementInvalid) from E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active _record/fixtures.rb:518:in `create_fixtures' from E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active _record/connection_adapters/abstract_adapter.rb:78:in `disable_referential_integ rity' from E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active _record/fixtures.rb:509:in `create_fixtures' from E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active _record/base.rb:1267:in `silence' from E:/jruby-1.1.4/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active _record/fixtures.rb:508:in `create_fixtures' from hoge.rb:12
のようなエラーとなってしまいます。
そこで、-%>の出番キタ!って感じです。
早速CSVを以下のように修正します。
id,name 1,test 2,hoge <% 10.times do |i| -%> <%= (i+3) %>,hoge<%= i %> <% end -%>
が、これまたエラーになります。
E:/jruby-1.1.4/lib/ruby/1.8/erb.rb:743: (erb):5: , unexpected ';' (SyntaxError)
trim_modeの指定の問題のようです。
で、
lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/fixtures.rb
をみてみると、
def erb_render(fixture_content) ERB.new(fixture_content).result end
trim_modeの指定無いじゃん。ってわけで、こんなパッチを当ててみる。
def erb_render(fixture_content) #ERB.new(fixture_content).result ERB.new(fixture_content, nil, "-").result end
すると、無事動きました。
JRubyで改行コードがCRLFのファイルを入力するとERBの-%が効かない
ようです。LFで保存しましょう。
あんまり関係ないですが、-%使おうと思ったときに出たエラー。
E:/jruby-1.1.4/lib/ruby/1.8/erb.rb:743:in `binding': (erb):2: , unexpected ';' ( SyntaxError) from testerb.rb:9:in `eval' from E:/jruby-1.1.4/lib/ruby/1.8/erb.rb:743:in `result' from testerb.rb:9
erbで-を指定する際の注意点 - お題目うぉっち
を参考に解決させていただきました。
JavaからFixturesを使ってみる - Rubyコードを使わずJavaコードのみで実現する編
といっても、evalScriptletを使っているので、少しはRubyコード出てきますが。。
FixturesをRails外で使ってみるのRubyコードをJavaに移植するイメージ。
import org.jruby.*; import org.jruby.runtime.*; import org.jruby.runtime.builtin.*; public class CallRuby { public static void main(String[] args) throws Exception { // JRuby環境を取得 Ruby runtime = Ruby.newInstance(); ThreadContext context = runtime.getCurrentContext(); // 必要なgemをrequire runtime.evalScriptlet("require 'rubygems'"); runtime.evalScriptlet("require 'active_record'"); runtime.evalScriptlet("require 'active_record/fixtures'"); // runtime.getClass("ActiveRecord::Base")は不可 // ActiveRecordモジュールを取得 RubyModule activeRecordModule = runtime.getModule("ActiveRecord"); // ActiveRecord::Baseクラスを取得 RubyClass activeRecordBaseClass = activeRecordModule.getClass("Base"); // コネクションを確立 RubyHash arg = new RubyHash(runtime); arg.put(RubySymbol.newSymbol(runtime, "adapter"), "jdbcderby"); arg.put(RubySymbol.newSymbol(runtime, "database"), "hoge"); activeRecordBaseClass.callMethod(context, "establish_connection", arg); // Fixturesクラスを取得 System.out.println("insert"); RubyClass fixturesClass = runtime.getClass("Fixtures"); RubyString arg1 = runtime.newString("data"); RubyArray arg2 = runtime.newArray(); arg2.add("PEOPLE"); IRubyObject[] arguments = {arg1, arg2}; // Fixtures#create_fixtures呼び出し IRubyObject fixturesObj = fixturesClass.callMethod(context, "create_fixtures", arguments); // Personクラスを定義 //RubyClass personClass = runtime.defineClass("Person", activeRecordBaseClass, ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR); runtime.evalScriptlet("class Person < ActiveRecord::Base\nend"); RubyClass personClass = runtime.getClass("Person"); // 全レコードを表示 System.out.println("select all"); showAllPersons(runtime, context, personClass); // Fixtures#delete_existing_fixtures呼び出し System.out.println("delete"); fixturesObj.callMethod(context, "delete_existing_fixtures"); // 全レコードを表示 System.out.println("select all"); showAllPersons(runtime, context, personClass); System.out.println("finish"); } /** * PEOPLEテーブルの全レコードを表示 */ private static void showAllPersons(Ruby runtime, ThreadContext context, RubyClass personClass) { RubySymbol arg1 = RubySymbol.newSymbol(runtime, "all"); RubyHash arg2 = new RubyHash(runtime); arg2.put(RubySymbol.newSymbol(runtime, "order"), "ID"); IRubyObject[] arguments = {arg1, arg2}; // Person#find呼び出し IRubyObject personObjArray = personClass.callMethod(context, "find", arguments); RubyArray persons = (RubyArray)personObjArray; for(int i=0; i<RubyNumeric.num2int(persons.length()); i++) { RubyObject personObj = (RubyObject)persons.get(i); IRubyObject id = personObj.callMethod(context, "id"); IRubyObject name = personObj.callMethod(context, "name"); System.out.println(id + "/" + name); } } }
というわけで、なんかうまく動かないところは、evalScriptletに逃げるという策を使えば、
わりとできてしまうことが分かりました。
JRubyのAPIって、JavaのリフレクションAPIをたたくのに似てますね。
Kernelモジュールにメソッド追加&二つの方法で呼び出し
Ruby#evalScriptletでKernelモジュールにメソッドを追加しておいてから、
の二つの方法を試してみる。
import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.RubyString; import org.jruby.runtime.builtin.IRubyObject; public class ExtendModule { public static void main(String[] args) throws Exception { Ruby runtime = Ruby.newInstance(); // Kernelモジュールにメソッドを追加 String scriptlet = "module Kernel\n def hoge(str)\n 'hoge: ' + str\n end\nend\n"; runtime.evalScriptlet(scriptlet); // testメソッドを呼んで、戻り値を取得する IRubyObject obj = runtime.evalScriptlet("hoge('from evalScriptlet')"); System.out.println(obj.getClass().getName()); System.out.println(obj); // Kernelモジュールを取得 RubyModule rubyModule = runtime.getModule("Kernel"); RubyString param = runtime.newString("from runtime.getModule"); IRubyObject obj2 = rubyModule.callMethod(runtime.getCurrentContext(), "hoge", param); System.out.println(obj2.getClass().getName()); System.out.println(obj2); } }
結果はこう。
org.jruby.RubyString hoge: from evalScriptlet org.jruby.RubyString hoge: from runtime.getModule
後者の方法であれば、引数をscriptletの文字列として組み立てなくてもできますね。
Objectクラスにインスタンスメソッドとクラスメソッド追加&呼び出し
Ruby#evalScriptletでObjectクラスにインスタンスメソッドとクラスメソッドを追加しておいてから、
- Ruby#evalScriptletでメソッドを呼び出す
- Ruby#getClass#newInstance#callMethodでインスタンスメソッドを呼び出す
- Ruby#getClass#callMethodでクラスメソッドを呼び出す
を試してみる。
import org.jruby.*; import org.jruby.runtime.*; import org.jruby.runtime.builtin.*; public class ExtendObject { public static void main(String[] args) throws Exception { Ruby runtime = Ruby.newInstance(); // Objectクラスにインスタンスメソッドを追加 String scriptlet = "class Object\n def hoge(str)\n 'hoge: ' + str\n end\nend\n"; runtime.evalScriptlet(scriptlet); // Objectクラスにクラスメソッドを追加 String scriptlet2 = "class Object\n def self.moge(str)\n 'moge: ' + str\n end\nend\n"; runtime.evalScriptlet(scriptlet2); // evalScriptletでメソッドを呼んで、戻り値を取得する IRubyObject obj = runtime.evalScriptlet("Object.new.hoge('from evalScriptlet')"); System.out.println(obj.getClass().getName()); System.out.println(obj); obj = runtime.evalScriptlet("Object.moge('from evalScriptlet')"); System.out.println(obj.getClass().getName()); System.out.println(obj); // Objectクラスを取得 RubyClass rubyObject = runtime.getClass("Object"); IRubyObject rubyObjectObj = rubyObject.newInstance(runtime.getCurrentContext(), new IRubyObject[0], Block.NULL_BLOCK); RubyString param = runtime.newString("from runtime.getClass"); // インスタンスメソッドを呼んで、戻り値を取得する obj = rubyObjectObj.callMethod(runtime.getCurrentContext(), "hoge", param); System.out.println(obj.getClass().getName()); System.out.println(obj); // クラスメソッドを呼んで、戻り値を取得する obj = rubyObject.callMethod(runtime.getCurrentContext(), "moge", param); System.out.println(obj.getClass().getName()); System.out.println(obj); } }
結果はこう。
org.jruby.RubyString hoge: from evalScriptlet org.jruby.RubyString moge: from evalScriptlet org.jruby.RubyString hoge: from runtime.getClass org.jruby.RubyString moge: from runtime.getClass
普通にできてます。