現実世界のJRuby

4,586 views

Published on

日本Oracle 「Java Developer Workshop #2」用スライド

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

No Downloads
Views
Total views
4,586
On SlideShare
0
From Embeds
0
Number of Embeds
70
Actions
Shares
0
Downloads
19
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

現実世界のJRuby

  1. 1. 現実世界のJRuby日本JRubyユーザ会 中村浩士(なひ) @nahi nahi@ruby-lang.org
  2. 2. 自己紹介株式会社サリオンシステムズリサーチ勤務セキュリティ・ネットワーク関連のシステム開発C/C++ (18年)、Java (13年)、Ruby (13年)余暇のOSS開発: CRuby (8年)、JRuby (2年)、 soap4r、httpclient他
  3. 3. JRubyとは - http://jruby.org/最新リリース版は1.6.5JVM上で動作するRuby言語Open Source (CPL, GPL, LGPL)開発開始から10年フルタイム開発者登場から5年
  4. 4. 本日のゴールJava開発者の皆様向けRubyとJRubyについて学ぶJRubyはどんなところに使える?
  5. 5. Ruby入門Java開発者向け
  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言語の特徴(まとめ)動的型付け全てがオブジェクト、全てがメソッドブロック(クロージャ)の活用メタプログラミング支援 Mix-in、オープンクラス、各種フックメソッド
  16. 16. JRubyの特長Ruby on Railsを含む100%の互換性C言語版Rubyと同等の実行速度高いスケーラビリティ(並行動作)Javaとの親和性の高さ
  17. 17. Real-World JRuby JRuby利用実例
  18. 18. Real-World JRuby: JRuby利用実例Java連携 (Java -> Ruby)Java連携 (Ruby -> Java)Javaテスト (RSpec, JtestR)開発支援 (Ant, Maven, Jenkins)ウェブ開発 (JRuby on Rails)
  19. 19. Java連携(Java -> Ruby) ユースケース
  20. 20. 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
  21. 21. 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", ...
  22. 22. 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()); } ...
  23. 23. Java連携(Ruby -> Java) ユースケース
  24. 24. 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"]
  25. 25. 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) }
  26. 26. JRubyFX: JRuby binding for JavaFX 2.0Java FX 2.0を利用してJRuby GUIアプリ開発https://github.com/nahi/jrubyfx(デモ)
  27. 27. JRubyFX: SVGLoader exampleJavaライブラリの組み合わせ require jrubyfx # https://github.com/skrb/SVGLoader require SVGLoader.jar java_import net.javainthebox.caraibe.svg.SVGLoader class SVGLoaderApp include JRubyFX def start(stage) root = build(Group) { children << SVGLoader.load("/duke.svg").root } with(stage, title: SVGLoader sample, scene: build(Scene, root)).show end end SVGLoaderApp.start
  28. 28. Javaテスト(RSpec, JtestR) ユースケース
  29. 29. 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
  30. 30. 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
  31. 31. 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
  32. 32. 開発支援(Ant, Maven, Jenkins) ユースケース
  33. 33. 開発支援 (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>
  34. 34. 開発支援 (Maven連携)Maven配布物はrubygemsとしてインストール可能開発環境の部分的Ruby化を支援 % jruby -S gem install bouncycastle:bcprov-jdk15 require rubygems require maven/bouncycastle/bcprov-jdk15 ...
  35. 35. 開発支援 (Jenkins連携)Ruby Plugins for Jenkinshttp://bit.ly/JenkinsRubyJenkinsのプラグインをRubyで記述可能
  36. 36. 開発支援 (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) ...
  37. 37. ウェブ開発(JRuby on Rails) ユースケース
  38. 38. ウェブ開発 (JRuby on Rails)Ruby on Rails - http://rubyonrails.org ウェブアプリケーションフレームワーク フルスタック CoC: (XML)設定より規約(に従って開発) DRY: 同じことを繰り返さない
  39. 39. Railsツアー 1/7:アプリケーションの生成MVC、テスト、サードパーティライブラリ等常に同じディレクトリ構成 % jruby -S rails new myapp create create README create Rakefile ... create vendor/plugins create vendor/plugins/.gitkeep run bundle install Fetching source index for http://rubygems.org/ Using rake (0.9.2.2) Installing multi_json (1.0.3) ... Installing sass-rails (3.1.5) Installing uglifier (1.1.0) Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
  40. 40. Railsツアー 2/7: scaffoldアプリの雛形作り(慣れると不要)% jruby -S rails g scaffold todo done:boolean description:string invoke active_record create db/migrate/20111128065332_create_todos.rb create app/models/todo.rb invoke test_unit create test/unit/todo_test.rb create test/fixtures/todos.yml route resources :todos invoke scaffold_controller create app/controllers/todos_controller.rb invoke erb create app/views/todos ... invoke scss create app/assets/stylesheets/scaffolds.css.scss%
  41. 41. Railsツアー 3/7: DBマイグレーションスクリプトによるDBスキーマ履歴管理 class CreateTodos < ActiveRecord::Migration def change create_table :todos do |t| t.boolean :done t.string :description t.timestamps end end end % jruby -S rake db:migrate == CreateTodos: migrating ==================================================== -- create_table(:todos) -> 0.0040s -> 0 rows
  42. 42. Railsツアー 4/7: サーバ起動 % jruby -S rails serverscaffoldだけ => Booting WEBrick => Rails 3.1.3 application starting in developmentでも動く on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2011-11-28 15:58:15] INFO WEBrick 1.3.1 [2011-11-28 15:58:15] INFO ruby 1.8.7 (2011-11-27) [java]
  43. 43. Railsツアー 5/7: 生成されたコードモデル class Todo < ActiveRecord::Base end <%= form_for(@todo) do |f| %> <div class="field"> <%= f.label :done %><br /> ビュー <%= f.check_box :done %> </div>コントローラ <div class="field"> <%= f.label :description %><br /> <%= f.text_field :description %> </div>class TodosController < ApplicationController<div class="actions"> def index <%= f.submit %> @todos = Todo.all </div> respond_to do |format| <% end %> format.html # index.html.erb format.json { render :json => @todos } end end def create ...end
  44. 44. Railsツアー 6/7:ActiveRelation (Arel)遅延SQL生成用のDSL Todo.where(:done => false) SELECT "todos".* FROM "todos" WHERE "todos"."done" = f Todo.where(:done => false).where(created_at < "2011-11-29") SELECT "todos".* FROM "todos" WHERE "todos"."done" = f AND (created_at < "2011-11-29") Todo.where(:done => false).order("created_at DESC").limit(1) SELECT "todos".* FROM "todos" WHERE "todos"."done" = f ORDER BY created_at DESC LIMIT 1スコープ class Todo < ActiveRecord::Base scope :finished, where(:done => true) end Todo.finished.size SELECT COUNT(*) FROM "todos" WHERE "todos"."done" = t
  45. 45. Railsツアー 7/7: RESTインターフェースJSONでのCRUD% jruby -rubygems -e require "httpclient"; puts HTTPClient.get("http://localhost:3000/todos/1.json").body=>{ "created_at":"2011-11-28T06:59:14Z", "description":"牛乳を買う", ... }% jruby -rubygems -e require "json"; require "httpclient";puts HTTPClient.post("http://localhost:3000/todos", JSON.generate(:todo => {:description => "JRubyのベンチマーク", :done => false}), "Accept" => "application/json", "Content-Type" => "application/json").body=>{ "created_at":"2011-11-28T07:36:19Z", "description":"JRubyのベンチマーク", ... }
  46. 46. ウェブ開発 (JRuby on Rails)Railsの全ての機能 + 既存Javaライブラリ活用Javaアプリと同居可能SpringMVCからRailsへのリファクタリング事例 1) "Petclinic"にJRubyでREST APIを追加 2) Railsの同居 3) Spring利用の機能をRailsで置き換えhttp://bit.ly/refactoring-to-rails
  47. 47. JRuby on RailsのデプロイWAR形式 → 任意のアプリサーバで動作専用アプリサーバ: Trinidad (Tomcatベース) http://www.engineyard.com/ TorqueBox (JBossベース) clustering、messaging、scheduling他 http://torquebox.org/PaaS: Engine Yard Cloud http://www.engineyard.com
  48. 48. JRubyのこれから InvokeDynamic と IR
  49. 49. Java SE 7: InvokeDynamic新たなメソッド呼び出しバイトコードbootstrapメソッドdynamic language support (java.lang.invoke.*) MethodHandle MethodType SwitchPoint CallSite
  50. 50. JRubyにおけるInvokeDynamic効果Java 5/6でのメソッド呼び出し
  51. 51. JRubyにおけるInvokeDynamic効果Java 7 +JRuby 1.7.0.dev(開発中)
  52. 52. JRubyにおけるInvokeDynamic効果 Java 6/7上で動作させたJRubyの比較 ※横軸は速度(大きいほうが速い)
  53. 53. IR: JRubyの新しい内部表現形式最適化方式の抜本的な変更を模索構文木ベースの最適化から新中間表現ベースへ
  54. 54. まとめ: JRuby - http://jruby.orgJava開発者にも学び易いRuby言語JavaとRubyの連携方式が豊富現実世界で使われるフレームワーク、ライブラリ Rails、RSpec、JtestR、Jenkins git、scripting、DSLJava開発者のツールベルトに

×