現実世界のJRuby
      (ショートバージョン)
       日本JRubyユーザ会 中村浩士
      @nahi nahi@ruby-lang.org
      https://github.com/nahi

ロングバージョン: http://bit.ly/RealWorldJRubyJa
自己紹介

ネットワークセキュリティ関連のシステム開発
 C/C++ (18年)、Java (13年)、Ruby (13年)

余暇のOSS開発
 CRuby (8年) とJRuby (2年) のコミッタ
 soap4r、httpclient他の開発
JRubyとは - http://jruby.org/

最新リリース版は1.6.7

JVM上で動作するRuby(動的型言語)

Open Source (CPL, GPL, LGPL)

開発開始から10年
日本でのJRuby




@yokolet @nahi @koichiroo
JRubyコミッタ3人

日本JRubyユーザ会:
http://bit.ly/JRubyUsersJp
昨年度の勉強会実施実績: 0回
本日のゴール

Java開発者、他のJVM言語利用者向け

RubyとJRubyについて学ぶ

JRubyはどんなところで使われている?
Rubyの特徴

人に優しい文法

豊富なメタプログラミング機能

高い生産性

Ruby on Rails + githubの存在
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
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はアクセサメソッド定義用メソッド
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なし
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);
                                             文の値は最後の式
}
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メソッドを呼び出す
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
                                         ← その他利用例
Rubyツアー 7/8:
Mix-in、オープンクラス
module Utils
  def name                        Mix-in: 実装の継承
    self.class.name    実装         Utilsモジュールの実装を
  end
end
class Book
                                  BookクラスにMix-in
  include Utils      継承
  def say
    "Hello from #{name}"
  end
end
obj = Book.new
p obj.say #=> "Hello from Book"

class Book
                                  オープンクラス:
  def say                         Bookクラスのsayメソッド
    "I'm #{name}"
  end                             を再定義
end
p obj.say #=> "I'm Book"
Rubyツアー 8/8: フックメソッド
class Base
  @@all = []                  inherited: クラスが継承
  def self.inherited(klass)
    @@all << klass
                              された場合に、継承したクラ
  end                         スを引数に呼ばれる
end

class Sub < Base
  p @@all
                              その他: included、
end                           method_added、
class SubSub < Sub            method_removed、
  p @@all
end                           method_missing、
※@@はクラス変数の接頭辞                 const_missing等
※クラスオブジェクトのキャッシュは
 リークの元なので普通やらない
Ruby言語の特徴

動的型付け(Groovyと同様)

オブジェクト指向: 全てオブジェクト、全てメソッド

ブロック(クロージャ)の活用

メタプログラミング支援
  Mix-in、オープンクラス、各種フックメソッド
JRubyの特長

Ruby on Railsを含む100%の互換性

C言語版Rubyと同等の実行速度

高いスケーラビリティ(並行動作)

Javaとの親和性の高さ
Real-World JRuby: JRuby利用実例
Java連携 (Java -> Ruby)

Java連携 (Ruby -> Java)

Javaテスト (RSpec, JtestR)

開発支援 (Ant, Maven, Jenkins)

ウェブ開発 (JRuby on Rails)
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
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", ...
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());
        }
        ...
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"]
Java連携 (Ruby -> Java)

Flying Saucerを使ってHTMLをPDF変換
http://code.google.com/p/flying-saucer/
% ls flyingsaucer-R8
core-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/">Flying
Saucer</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) }
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.new
Finished 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
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
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
開発支援 (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>
開発支援 (Maven連携)

Maven配布物はrubygemsとしてインストール可能

開発環境の部分的Ruby化を支援

  % jruby -S gem install bouncycastle:bcprov-jdk15

  require 'rubygems'
  require 'maven/bouncycastle/bcprov-jdk15'
  ...
開発支援 (Jenkins連携)

Ruby Plugins for Jenkins
http://bit.ly/JenkinsRuby

JenkinsのプラグインをRubyで記述可能
開発支援 (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)
        ...
ウェブ開発 (JRuby on Rails)

Ruby on Rails - http://rubyonrails.org

  ウェブアプリケーションフレームワーク

  フルスタック

  CoC: (XML)設定より規約(に従って開発)

  DRY: 同じことを繰り返さない
ウェブ開発 (JRuby on Rails)

Railsの全ての機能 + 既存Javaライブラリ活用

Javaアプリと同居可能

SpringMVCからRailsへのリファクタリング事例
  1) "Petclinic"にJRubyでREST APIを追加
  2) Railsの同居
  3) Spring利用の機能をRailsで置き換え
http://bit.ly/refactoring-to-rails
JRuby on Railsのデプロイ

WAR形式 → 任意のJavaアプリサーバで動作

専用アプリサーバ:
 Trinidad(Tomcatベース)
    https://github.com/trinidad/trinidad
  TorqueBox(JBossベース)
    clustering、messaging、scheduling他
    http://torquebox.org/
まとめ: JRuby - http://jruby.org/

JavaとRuby両方の豊富な資産を利用可能
   38624 in search.maven.org
   36713 in rubygems.org     (as of 20120403)


現実世界で使われるフレームワーク、ライブラリ
 Rails、RSpec、JtestR、Jenkins、scripting、DSL

Java開発者のツールベルトに

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

  • 1.
    現実世界のJRuby (ショートバージョン) 日本JRubyユーザ会 中村浩士 @nahi nahi@ruby-lang.org https://github.com/nahi ロングバージョン: http://bit.ly/RealWorldJRubyJa
  • 2.
    自己紹介 ネットワークセキュリティ関連のシステム開発 C/C++ (18年)、Java(13年)、Ruby (13年) 余暇のOSS開発 CRuby (8年) とJRuby (2年) のコミッタ soap4r、httpclient他の開発
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
    Rubyツアー 1/8: クラス定義 publicclass 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.
    Rubyツアー 2/8: インスタンス変数 publicclass 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.
    Rubyツアー 3/8: 動的型付け publicclass 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.
    Rubyツアー 4/8: 全てが値を持つ publicclass 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.
    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.
    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.
    Rubyツアー 7/8: Mix-in、オープンクラス module Utils def name Mix-in: 実装の継承 self.class.name 実装 Utilsモジュールの実装を end end class Book BookクラスにMix-in include Utils 継承 def say "Hello from #{name}" end end obj = Book.new p obj.say #=> "Hello from Book" class Book オープンクラス: def say Bookクラスのsayメソッド "I'm #{name}" end を再定義 end p obj.say #=> "I'm Book"
  • 14.
    Rubyツアー 8/8: フックメソッド classBase @@all = [] inherited: クラスが継承 def self.inherited(klass) @@all << klass された場合に、継承したクラ end スを引数に呼ばれる end class Sub < Base p @@all その他: included、 end method_added、 class SubSub < Sub method_removed、 p @@all end method_missing、 ※@@はクラス変数の接頭辞 const_missing等 ※クラスオブジェクトのキャッシュは リークの元なので普通やらない
  • 15.
  • 16.
  • 17.
    Real-World JRuby: JRuby利用実例 Java連携(Java -> Ruby) Java連携 (Ruby -> Java) Javaテスト (RSpec, JtestR) 開発支援 (Ant, Maven, Jenkins) ウェブ開発 (JRuby on Rails)
  • 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.
    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.
    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.
    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.
    Java連携 (Ruby ->Java) Flying Saucerを使ってHTMLをPDF変換 http://code.google.com/p/flying-saucer/ % ls flyingsaucer-R8 core-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/">Flying Saucer</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.
    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.new Finished 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.
    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.
    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.
    開発支援 (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.
    開発支援 (Maven連携) Maven配布物はrubygemsとしてインストール可能 開発環境の部分的Ruby化を支援 % jruby -S gem install bouncycastle:bcprov-jdk15 require 'rubygems' require 'maven/bouncycastle/bcprov-jdk15' ...
  • 28.
    開発支援 (Jenkins連携) Ruby Pluginsfor Jenkins http://bit.ly/JenkinsRuby JenkinsのプラグインをRubyで記述可能
  • 29.
    開発支援 (Jenkins連携) 例: TravisCI設定を読んで自動ビルド 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.
    ウェブ開発 (JRuby onRails) Ruby on Rails - http://rubyonrails.org ウェブアプリケーションフレームワーク フルスタック CoC: (XML)設定より規約(に従って開発) DRY: 同じことを繰り返さない
  • 31.
    ウェブ開発 (JRuby onRails) Railsの全ての機能 + 既存Javaライブラリ活用 Javaアプリと同居可能 SpringMVCからRailsへのリファクタリング事例 1) "Petclinic"にJRubyでREST APIを追加 2) Railsの同居 3) Spring利用の機能をRailsで置き換え http://bit.ly/refactoring-to-rails
  • 32.
    JRuby on Railsのデプロイ WAR形式→ 任意のJavaアプリサーバで動作 専用アプリサーバ: Trinidad(Tomcatベース) https://github.com/trinidad/trinidad TorqueBox(JBossベース) clustering、messaging、scheduling他 http://torquebox.org/
  • 33.
    まとめ: JRuby -http://jruby.org/ JavaとRuby両方の豊富な資産を利用可能 38624 in search.maven.org 36713 in rubygems.org (as of 20120403) 現実世界で使われるフレームワーク、ライブラリ Rails、RSpec、JtestR、Jenkins、scripting、DSL Java開発者のツールベルトに