現実世界のJRuby(ショートバージョン)

2,218 views

Published on

JavaOne Tokyo 2012 JVM言語BOF JRuby発表

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,218
On SlideShare
0
From Embeds
0
Number of Embeds
33
Actions
Shares
0
Downloads
7
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

現実世界のJRuby(ショートバージョン)

  1. 1. 現実世界のJRuby (ショートバージョン) 日本JRubyユーザ会 中村浩士 @nahi nahi@ruby-lang.org https://github.com/nahiロングバージョン: http://bit.ly/RealWorldJRubyJa
  2. 2. 自己紹介ネットワークセキュリティ関連のシステム開発 C/C++ (18年)、Java (13年)、Ruby (13年)余暇のOSS開発 CRuby (8年) とJRuby (2年) のコミッタ soap4r、httpclient他の開発
  3. 3. JRubyとは - http://jruby.org/最新リリース版は1.6.7JVM上で動作するRuby(動的型言語)Open Source (CPL, GPL, LGPL)開発開始から10年
  4. 4. 日本でのJRuby@yokolet @nahi @koichirooJRubyコミッタ3人日本JRubyユーザ会:http://bit.ly/JRubyUsersJp昨年度の勉強会実施実績: 0回
  5. 5. 本日のゴールJava開発者、他のJVM言語利用者向けRubyとJRubyについて学ぶJRubyはどんなところで使われている?
  6. 6. Rubyの特徴人に優しい文法豊富なメタプログラミング機能高い生産性Ruby on Rails + githubの存在
  7. 7. Rubyツアー 1/8: クラス定義public class Circle extends Shape { class Circle < Shape private final int radius; def initialize(radius) public Circle(int radius) { @radius = radius this.radius = radius; end } attr_reader :radius public int getRadius() { def area return radius; Math::PI * (@radius ** 2) } end public double getArea() { end return Math.PI * Math.pow(radius, 2); puts Circle.new(2).area } public static void main(String[] args) { double area = new Circle(2).getArea(); extends → < 継承は単一継承 System.out.println(area); }} メソッド定義 → def コンストラクタ → initialize
  8. 8. Rubyツアー 2/8: インスタンス変数public class Circle extends Shape { class Circle < Shape private final int radius; def initialize(radius) public Circle(int radius) { @radius = radius this.radius = radius; end } attr_reader :radius public int getRadius() { def area return radius; Math::PI * (@radius ** 2) } end public double getArea() { end return Math.PI * Math.pow(radius, 2); puts Circle.new(2).area } public static void main(String[] args) { double area = new Circle(2).getArea(); this → @ System.out.println(area); }} attr_readerはアクセサメソッド定義用メソッド
  9. 9. Rubyツアー 3/8: 動的型付けpublic class Circle extends Shape { class Circle < Shape private final int radius; def initialize(radius) public Circle(int radius) { @radius = radius this.radius = radius; end } attr_reader :radius public int getRadius() { def area return radius; Math::PI * (@radius ** 2) } end public double getArea() { end return Math.PI * Math.pow(radius, 2); puts Circle.new(2).area } public static void main(String[] args) { double area = new Circle(2).getArea(); 変数に型なし System.out.println(area); } duck-typing} 引数の型・数の違いによるメソッドoverloadなし
  10. 10. Rubyツアー 4/8: 全てが値を持つpublic class Circle extends Shape { class Circle < Shape private final int radius; def initialize(radius) public Circle(int radius) { @radius = radius this.radius = radius; end } attr_reader :radius public int getRadius() { def area return radius; Math::PI * (@radius ** 2) } end public double getArea() { end return Math.PI * Math.pow(radius, 2); puts Circle.new(2).area } public static void main(String[] args) { double area = new Circle(2).getArea(); return不要 } System.out.println(area); 文の値は最後の式}
  11. 11. Rubyツアー 5/8: 全てがオブジェクト、全てがメソッドpublic class Circle extends Shape { class Circle < Shape private final int radius; def initialize(radius) public Circle(int radius) { @radius = radius this.radius = radius; end } attr_reader :radius public int getRadius() { def area return radius; Math::PI * (@radius ** 2) } end public double getArea() { end return Math.PI * Math.pow(radius, 2); puts Circle.new(2).area } public static void main(String[] args) { double area = new Circle(2).getArea(); Circle: 定数 System.out.println(area); } a*2 == a.*(2)} Circle.new: クラスオブジェクトのnewメソッドを呼び出す
  12. 12. Rubyツアー 6/8: ブロック(クロージャ) def aaa(name, &block) File.open(name) do |file| (1) File.open用ブロック file.each_line do |line| yield line ブロック実行後に自動close(2) end (1) end end (2) each_line用ブロック 1行読み込む毎に呼ばれる aaa(a.txt) do |line| p line (3) end (3) aaa用ブロック people.group_by { |e| e.lang } aaa内部のyieldに呼ばれる button1 = ... label1 = ... button1.on_action do |event| label1.text = sending... end ← その他利用例
  13. 13. Rubyツアー 7/8:Mix-in、オープンクラスmodule Utils def name Mix-in: 実装の継承 self.class.name 実装 Utilsモジュールの実装を endendclass Book BookクラスにMix-in include Utils 継承 def say "Hello from #{name}" endendobj = Book.newp obj.say #=> "Hello from Book"class Book オープンクラス: def say Bookクラスのsayメソッド "Im #{name}" end を再定義endp obj.say #=> "Im Book"
  14. 14. Rubyツアー 8/8: フックメソッドclass Base @@all = [] inherited: クラスが継承 def self.inherited(klass) @@all << klass された場合に、継承したクラ end スを引数に呼ばれるendclass Sub < Base p @@all その他: included、end method_added、class SubSub < Sub method_removed、 p @@allend method_missing、※@@はクラス変数の接頭辞 const_missing等※クラスオブジェクトのキャッシュは リークの元なので普通やらない
  15. 15. Ruby言語の特徴動的型付け(Groovyと同様)オブジェクト指向: 全てオブジェクト、全てメソッドブロック(クロージャ)の活用メタプログラミング支援 Mix-in、オープンクラス、各種フックメソッド
  16. 16. JRubyの特長Ruby on Railsを含む100%の互換性C言語版Rubyと同等の実行速度高いスケーラビリティ(並行動作)Javaとの親和性の高さ
  17. 17. Real-World JRuby: JRuby利用実例Java連携 (Java -> Ruby)Java連携 (Ruby -> Java)Javaテスト (RSpec, JtestR)開発支援 (Ant, Maven, Jenkins)ウェブ開発 (JRuby on Rails)
  18. 18. Java連携 (Java -> Ruby)JavaからRubyライブラリを利用import org.jruby.embed.ScriptingContainer;public class HelloWorld { public static void main(String[] args) { ScriptingContainer ruby = new ScriptingContainer(); ruby.runScriptlet("puts "hello,world!""); } source http://localhost/} group :development do host localhost port 12345 reloadable true例: 独自定義ファイル解析の debug true end DSL処理系として group :production do host www.example.com end
  19. 19. Java連携 (Java -> Ruby)例: gitdiff.rb - gitライブラリを利用し、リビジョンの変更サマリを取得するRubyコード require rubygems require git def diff_summary(dir, from, to) diff = Git.open(dir).diff(from, to) diff.stats[:files].map { |file, st| insertions = st[:insertions] || 0 deletions = st[:deletions] || 0 "#{file} +#{insertions} -#{deletions}" } end # =>[ "src/org/jruby/Ruby.java +32 -20", # "src/org/jruby/RubyArray.java +93 -17", # "src/org/jruby/RubyBasicObject.java +7 -0", ...
  20. 20. Java連携 (Java -> Ruby)Javaからの呼び出しと抽出 public class GitDiff { public static void main(String[] args) throws Exception { ScriptingContainer ruby = new ScriptingContainer(); ruby.runScriptlet("require gitdiff"); ruby.put("dir", "/home/nahi/git/jruby/"); ruby.put("from", "8c6dba0f..."); ruby.put("to", "7837c84a..."); List array = (List) ruby.runScriptlet( "diff_summary(dir, from, to)"); for (Object obj : array) { System.out.println(obj.toString()); } ...
  21. 21. Java連携 (Ruby -> Java)RubyからJavaの機能を利用するJavaの対話環境としての利用も可能 % jruby -S irb > require java => true > ni = java.net.NetworkInterface.networkInterfaces.to_a.first => #<Java::JavaNet::NetworkInterface:0x4d33b92c> > ni.getName => "eth0" > ni.isUp => true > ni.getMtu => 1500 > ni.inetAddresses.map { |addr| addr.to_s } => ["/fe80:0:0:0:20c:29ff:fead:4bed%2", "/192.168.96.129"]
  22. 22. Java連携 (Ruby -> Java)Flying Saucerを使ってHTMLをPDF変換http://code.google.com/p/flying-saucer/% ls flyingsaucer-R8core-renderer.jar iText-2.0.8.jar ...% jruby -S irb -Iflyingsaucer-R8> require java> require iText-2.0.8.jar> require core-renderer.jar> rr = org.xhtmlrenderer.pdf.ITextRenderer.new> doc = <<EOD<html><body><h1>Hello JRuby</h1><p>from <a href="http://code.google.com/p/flying-saucer/">FlyingSaucer</a>.</p></body></html>EOD> rr.set_document_from_string(doc)> rr.layout> File.open("out.pdf", "w") { |f| rr.create_pdf(f.to_outputstream) }
  23. 23. Javaテスト (RSpec) RubyとJRubyの利点を活かしてJavaをテスト describe ScriptingContainer#put do before :each do RSpec: @x = org.jruby.embed. ScriptingContainer.new end 振る舞いをテスト it "sets an object to local variable" do obj = Object.new http://rspec.info @x.put("var", obj) @x.run_scriptlet("var").should == obj% jruby -S rspec jruby_spec.rb end.. it "overrides the previous object" do obj = Object.newFinished in 0.044 seconds @x.put("var", obj)2 examples, 0 failures @x.put("var", nil)% @x.run_scriptlet("var").should be_nil end end
  24. 24. Javaテスト (JtestR)JtestR: 各種Ruby用テストライブラリ同梱http://jtestr.codehaus.org/ describe "X509Name" do it "should use given converter for ASN1 encode" do converter = mock(X509NameEntryConverter) name = X509Name.new(CN=localhost, converter) converter.stubs(getConvertedValue). with(DERObjectIdentifier.new(CN), localhost). returns(DERPrintableString.new(converted)). times(1) name.toASN1Object.to_string.should == ... end end
  25. 25. Javaテスト (JtestR)Ant/Maven統合 + テストサーバ <?xml version="1.0" encoding="utf-8"?> <project basedir="." default="test" name="simple1"> <taskdef name="jtestr" classname="org.jtestr.ant.JtestRAntRunner"% ant test classpath="build_lib/jtestr.jar" />Buildfile: /path/to/build.xml <taskdef name="jtestr-server" classname="org.jtestr.ant.JtestRAntServer"test: classpath="build_lib/jtestr.jar" /> <target name="test"> [jtestr] Other Spec: 4 examples, 0 failures, 0 errors [jtestr] <jtestr port="20333"/> </target> [jtestr] Total: 4 tests, 0 failures, 0 errors, 0 pending [jtestr] <target name="test-server" > <jtestr-server port="20333" runtimes="3"/> </target>BUILD SUCCESSFUL </project>Total time: 9 seconds
  26. 26. 開発支援 (Ant連携) desc "Build JRuby"Rake: Rubyの記述力 task :build do ant "jar"を活かして end task :jar => :buildビルド手順を記述 desc "Clean all built output" task :clean do delete_files = FileList.new do |fl| fl.Ant、Rakeから相互 include("#{BUILD_DIR}/**"). exclude("#{BUILD_DIR}/rubyspec").にタスクを利用可能 include(DIST_DIR). include(API_DOCS_DIR) end<target name=”load-rake-task”> ... <taskdef name=”rake” classname=”org.jruby.ant.Rake”/></target><target name=”default” depends=”load-rake-task”> <rake task=”jar”/></target>
  27. 27. 開発支援 (Maven連携)Maven配布物はrubygemsとしてインストール可能開発環境の部分的Ruby化を支援 % jruby -S gem install bouncycastle:bcprov-jdk15 require rubygems require maven/bouncycastle/bcprov-jdk15 ...
  28. 28. 開発支援 (Jenkins連携)Ruby Plugins for Jenkinshttp://bit.ly/JenkinsRubyJenkinsのプラグインをRubyで記述可能
  29. 29. 開発支援 (Jenkins連携)例: Travis CI設定を読んで自動ビルドclass TravisScriptBuilder < Jenkins::Tasks::Builder def prebuild(build, listener) travis_file = build.workspace + .travis.yml unless travis_file.exist? listener.error "Travis config `#{travis_file} not found" raise "Travis config file not found" end ... def perform(build, launcher, listener) run_scripts(setup_env) ... def run_scripts(env) %w{before_script script after_script}.each do |type| scan_multiline_scripts(config[type]).each do |script| launcher.execute(env, script, :chdir => workspace, :out => listener) ...
  30. 30. ウェブ開発 (JRuby on Rails)Ruby on Rails - http://rubyonrails.org ウェブアプリケーションフレームワーク フルスタック CoC: (XML)設定より規約(に従って開発) DRY: 同じことを繰り返さない
  31. 31. ウェブ開発 (JRuby on Rails)Railsの全ての機能 + 既存Javaライブラリ活用Javaアプリと同居可能SpringMVCからRailsへのリファクタリング事例 1) "Petclinic"にJRubyでREST APIを追加 2) Railsの同居 3) Spring利用の機能をRailsで置き換えhttp://bit.ly/refactoring-to-rails
  32. 32. JRuby on RailsのデプロイWAR形式 → 任意のJavaアプリサーバで動作専用アプリサーバ: Trinidad(Tomcatベース) https://github.com/trinidad/trinidad TorqueBox(JBossベース) clustering、messaging、scheduling他 http://torquebox.org/
  33. 33. まとめ: JRuby - http://jruby.org/JavaとRuby両方の豊富な資産を利用可能 38624 in search.maven.org 36713 in rubygems.org (as of 20120403)現実世界で使われるフレームワーク、ライブラリ Rails、RSpec、JtestR、Jenkins、scripting、DSLJava開発者のツールベルトに

×