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

33
現実世界のJRuby (ショートバージョン) 日本JRubyユーザ会 中村浩士 @nahi [email protected] https://github.com/nahi ロングバージョン: http://bit.ly/RealWorldJRubyJa

Upload: hiroshi-nakamura

Post on 09-May-2015

1.808 views

Category:

Technology


0 download

DESCRIPTION

JavaOne Tokyo 2012 JVM言語BOF JRuby発表

TRANSCRIPT

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

現実世界のJRuby

(ショートバージョン)

日本JRubyユーザ会 中村浩士@nahi [email protected]://github.com/nahi

ロングバージョン: http://bit.ly/RealWorldJRubyJa

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

自己紹介

ネットワークセキュリティ関連のシステム開発

C/C++ (18年)、Java (13年)、Ruby (13年) 余暇のOSS開発

CRuby (8年) とJRuby (2年) のコミッタsoap4r、httpclient他の開発

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

JRubyとは - http://jruby.org/

最新リリース版は1.6.7 JVM上で動作するRuby(動的型言語)

Open Source (CPL, GPL, LGPL) 開発開始から10年

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

日本でのJRuby

@yokolet @nahi @koichirooJRubyコミッタ3人

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

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

本日のゴール

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

RubyとJRubyについて学ぶ

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

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

Rubyの特徴

人に優しい文法 豊富なメタプログラミング機能 高い生産性 Ruby on Rails + githubの存在

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

Rubyツアー 1/8: クラス定義

public class Circle extends Shape { private final int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public double getArea() { return Math.PI * Math.pow(radius, 2); } public static void main(String[] args) { double area = new Circle(2).getArea(); System.out.println(area); }}

extends → <継承は単一継承

メソッド定義 → defコンストラクタ → initialize

class Circle < Shape def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) endendputs Circle.new(2).area

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

public class Circle extends Shape { private final int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public double getArea() { return Math.PI * Math.pow(radius, 2); } public static void main(String[] args) { double area = new Circle(2).getArea(); System.out.println(area); }}

class Circle < Shape def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) endendputs Circle.new(2).area

Rubyツアー 2/8: インスタンス変数

this → @

attr_readerはアクセサメソッド定義用メソッド

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

public class Circle extends Shape { private final int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public double getArea() { return Math.PI * Math.pow(radius, 2); } public static void main(String[] args) { double area = new Circle(2).getArea(); System.out.println(area); }}

class Circle < Shape def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) endendputs Circle.new(2).area

Rubyツアー 3/8: 動的型付け

変数に型なしduck-typing

引数の型・数の違いによるメソッドoverloadなし

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

public class Circle extends Shape { private final int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public double getArea() { return Math.PI * Math.pow(radius, 2); } public static void main(String[] args) { double area = new Circle(2).getArea(); System.out.println(area); }}

class Circle < Shape def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) endendputs Circle.new(2).area

Rubyツアー 4/8: 全てが値を持つ

return不要文の値は最後の式

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

public class Circle extends Shape { private final int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public double getArea() { return Math.PI * Math.pow(radius, 2); } public static void main(String[] args) { double area = new Circle(2).getArea(); System.out.println(area); }}

class Circle < Shape def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) endendputs Circle.new(2).area

Rubyツアー 5/8:全てがオブジェクト、全てがメソッド

Circle: 定数a*2 == a.*(2)

Circle.new: クラスオブジェクトのnewメソッドを呼び出す

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

Rubyツアー 6/8: ブロック(クロージャ)

def aaa(name, &block) File.open(name) do |file| file.each_line do |line| yield line end endend

aaa('a.txt') do |line| p lineend

(1) File.open用ブロックブロック実行後に自動close (2) each_line用ブロック1行読み込む毎に呼ばれる (3) aaa用ブロックaaa内部のyieldに呼ばれる ← その他利用例

people.group_by { |e| e.lang }

button1 = ...label1 = ...button1.on_action do |event| label1.text = 'sending...'end

(1)(2)

(3)

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

Rubyツアー 7/8:Mix-in、オープンクラス

module Utils def name self.class.name endendclass Book include Utils def say "Hello from #{name}" endendobj = Book.newp obj.say #=> "Hello from Book"

class Book def say "I'm #{name}" endendp obj.say #=> "I'm Book"

Mix-in: 実装の継承Utilsモジュールの実装を

BookクラスにMix-in オープンクラス:Bookクラスのsayメソッドを再定義

実装

継承

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

Rubyツアー 8/8: フックメソッド

class Base @@all = [] def self.inherited(klass) @@all << klass endend

class Sub < Base p @@allend

class SubSub < Sub p @@allend

inherited: クラスが継承された場合に、継承したクラスを引数に呼ばれる その他: included、method_added、method_removed、method_missing、const_missing等※@@はクラス変数の接頭辞

※クラスオブジェクトのキャッシュは リークの元なので普通やらない

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

Ruby言語の特徴

動的型付け(Groovyと同様)

オブジェクト指向: 全てオブジェクト、全てメソッド ブロック(クロージャ)の活用 メタプログラミング支援

Mix-in、オープンクラス、各種フックメソッド

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

JRubyの特長

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

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

高いスケーラビリティ(並行動作) Javaとの親和性の高さ

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

Real-World JRuby: JRuby利用実例

Java連携 (Java -> Ruby) Java連携 (Ruby -> Java) Javaテスト (RSpec, JtestR) 開発支援 (Ant, Maven, Jenkins) ウェブ開発 (JRuby on Rails)

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

Java連携 (Java -> Ruby)

JavaからRubyライブラリを利用

例: 独自定義ファイル解析の

DSL処理系として

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 trueend

group :production do host 'www.example.com'end

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

例: gitdiff.rb - gitライブラリを利用し、リビジョ

ンの変更サマリを取得するRubyコード

Java連携 (Java -> 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", ...

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

Javaからの呼び出しと抽出

Java連携 (Java -> Ruby)

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());}...

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

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"]

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

Flying Saucerを使ってHTMLをPDF変換http://code.google.com/p/flying-saucer/

Java連携 (Ruby -> Java)

% 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/">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) }

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

RubyとJRubyの利点を活かしてJavaをテスト

RSpec:振る舞いをテストhttp://rspec.info

Javaテスト (RSpec)

describe 'ScriptingContainer#put' do before :each do @x = org.jruby.embed.ScriptingContainer.new end it "sets an object to local variable" do obj = Object.new @x.put("var", obj) @x.run_scriptlet("var").should == obj end it "overrides the previous object" do obj = Object.new @x.put("var", obj) @x.put("var", nil) @x.run_scriptlet("var").should be_nil endend

% jruby -S rspec jruby_spec.rb..

Finished in 0.044 seconds2 examples, 0 failures%

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

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 == '...' endend

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

Javaテスト (JtestR)

Ant/Maven統合 + テストサーバ

% ant testBuildfile: /path/to/build.xml

test: [jtestr] Other Spec: 4 examples, 0 failures, 0 errors [jtestr] [jtestr] Total: 4 tests, 0 failures, 0 errors, 0 pending [jtestr]

BUILD SUCCESSFULTotal time: 9 seconds

<?xml version="1.0" encoding="utf-8"?><project basedir="." default="test" name="simple1"> <taskdef name="jtestr" classname="org.jtestr.ant.JtestRAntRunner" classpath="build_lib/jtestr.jar" /> <taskdef name="jtestr-server" classname="org.jtestr.ant.JtestRAntServer" classpath="build_lib/jtestr.jar" /> <target name="test"> <jtestr port="20333"/> </target> <target name="test-server"> <jtestr-server port="20333" runtimes="3"/> </target></project>

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

開発支援 (Ant連携)

Rake: Rubyの記述力を活かしてビルド手順を記述 Ant、Rakeから相互にタスクを利用可能

desc "Build JRuby"task :build do ant "jar"endtask :jar => :builddesc "Clean all built output"task :clean do delete_files = FileList.new do |fl| fl. 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>

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

開発支援 (Maven連携)

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

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

% jruby -S gem install bouncycastle:bcprov-jdk15

require 'rubygems'require 'maven/bouncycastle/bcprov-jdk15'...

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

Ruby Plugins for Jenkinshttp://bit.ly/JenkinsRuby JenkinsのプラグインをRubyで記述可能

開発支援 (Jenkins連携)

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

開発支援 (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) ...

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

ウェブ開発 (JRuby on Rails)

Ruby on Rails - http://rubyonrails.org

ウェブアプリケーションフレームワーク フルスタック CoC: (XML)設定より規約(に従って開発)

DRY: 同じことを繰り返さない

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

ウェブ開発 (JRuby on Rails)

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

Javaアプリと同居可能

SpringMVCからRailsへのリファクタリング事例

1) "Petclinic"にJRubyでREST APIを追加

2) Railsの同居

3) Spring利用の機能をRailsで置き換えhttp://bit.ly/refactoring-to-rails

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

JRuby on Railsのデプロイ

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

専用アプリサーバ:

Trinidad(Tomcatベース)

https://github.com/trinidad/trinidad

TorqueBox(JBossベース)

clustering、messaging、scheduling他

http://torquebox.org/

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

まとめ: JRuby - http://jruby.org/

JavaとRuby両方の豊富な資産を利用可能

38624 in search.maven.org36713 in rubygems.org (as of 20120403)

現実世界で使われるフレームワーク、ライブラリ

Rails、RSpec、JtestR、Jenkins、scripting、DSL Java開発者のツールベルトに