• 検索結果がありません。

自己紹介 株式会社サリオンシステムズリサーチ勤務セキュリティ ネットワーク関連のシステム開発 C/C++ (18 年 ) Java (13 年 ) Ruby (13 年 ) 余暇の OSS 開発 :CRuby (8 年 ) JRuby (2 年 ) soap4r httpclient 他

N/A
N/A
Protected

Academic year: 2021

シェア "自己紹介 株式会社サリオンシステムズリサーチ勤務セキュリティ ネットワーク関連のシステム開発 C/C++ (18 年 ) Java (13 年 ) Ruby (13 年 ) 余暇の OSS 開発 :CRuby (8 年 ) JRuby (2 年 ) soap4r httpclient 他"

Copied!
54
0
0

読み込み中.... (全文を見る)

全文

(1)

現実世界のJRuby

日本JRubyユーザ会 中村浩士(なひ)

@nahi [email protected]

(2)

自己紹介

株式会社サリオンシステムズリサーチ勤務

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

C/C++ (18年)、Java (13年)、Ruby (13年)

余暇のOSS開発:CRuby (8年)、JRuby (2年)、

soap4r、httpclient他

(3)

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

最新リリース版は1.6.5

JVM上で動作するRuby言語

Open Source (CPL, GPL, LGPL)

開発開始から10年

フルタイム開発者登場から5年

(4)

本日のゴール

Java開発者の皆様向け

RubyとJRubyについて学ぶ

(5)

Ruby入門

Java開発者向け

(6)

Rubyの特徴

人に優しい文法

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

高い生産性

(7)

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) end end puts Circle.new(2).area

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

} }

class Circle < Shape

def initialize(radius) @radius = radius end attr_reader :radius def area Math::PI * (@radius ** 2) end end puts Circle.new(2).area

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

this → @

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

(9)

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) end end puts Circle.new(2).area

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

変数に型なし

duck-typing

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

(10)

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) end end puts Circle.new(2).area

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

return不要

文の値は最後の式

(11)

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) end end

puts Circle.new(2).area

Rubyツアー 5/8:

全てがオブジェクト、全てがメソッド

Circle: 定数

a*2 == a.*(2)

Circle.new:

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

(12)

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

def aaa(name, &block)

File.open(name) do |file| file.each_line do |line| yield line end end end

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

(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)

(13)

Rubyツアー 7/8:

Mix-in、オープンクラス

module Utils def name self.class.name end end class Book include Utils def say

"Hello from #{name}" end

end

obj = Book.new

p obj.say #=> "Hello from Book" class Book

def say

"I'm #{name}" end

end

p obj.say #=> "I'm Book"

Mix-in: 実装の継承

Utilsモジュールの実装を

BookクラスにMix-in

オープンクラス:

Bookクラスのsayメソッド

を再定義

実装

継承

(14)

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

class Base

@@all = []

def self.inherited(klass) @@all << klass

end end

class Sub < Base p @@all

end

class SubSub < Sub p @@all end

inherited: クラスが継承

された場合に、継承したクラ

スを引数に呼ばれる

その他: included、

method_added、

method_removed、

method_missing

const_missing

※@@はクラス変数の接頭辞

※クラスオブジェクトのキャッシュは

リークの元なので普通やらない

(15)

Ruby言語の特徴(まとめ)

動的型付け

全てがオブジェクト、全てがメソッド

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

メタプログラミング支援

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

(16)

JRubyの特長

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

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

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

(17)

Real-World JRuby

JRuby利用実例

(18)

Real-World JRuby: JRuby利用実例

Java連携 (Java -> Ruby)

Java連携 (Ruby -> Java)

Javaテスト (RSpec, JtestR)

開発支援 (Ant, Maven, Jenkins)

(19)

Java連携

(Java -> Ruby)

(20)

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 true end group :production do host 'www.example.com' end

(21)

例: 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",

(22)

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

(23)

Java連携

(Ruby -> Java)

(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 }

(25)

Flying Saucerを使ってHTMLをPDF変換

http://code.google.com/p/flying-saucer/

Java連携 (Ruby -> Java)

% 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

(26)

JRubyFX:

JRuby binding for JavaFX 2.0

Java FX 2.0を利用してJRuby GUIアプリ開発

https://github.com/nahi/jrubyfx

(27)

JRubyFX: SVGLoader example

Javaライブラリの組み合わせ

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

(28)

Javaテスト

(RSpec, JtestR)

(29)

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 end

end

% jruby -S rspec jruby_spec.rb

..

Finished in 0.044 seconds 2 examples, 0 failures %

(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

(31)

Javaテスト (JtestR)

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

% ant test

Buildfile: /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 SUCCESSFUL

Total 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>

(32)

開発支援

(Ant, Maven, Jenkins)

(33)

開発支援 (Ant連携)

Rake: Rubyの記述力

を活かして

ビルド手順を記述

Ant、Rakeから相互

にタスクを利用可能

desc "Build JRuby" task :build do

ant "jar" end

task :jar => :build

desc "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”/>

(34)

開発支援 (Maven連携)

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

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

%

jruby -S gem install bouncycastle:bcprov-jdk15

require

'

rubygems

'

require

'

maven/bouncycastle/bcprov-jdk15

'

(35)

Ruby Plugins for Jenkins

http://bit.ly/JenkinsRuby

JenkinsのプラグインをRubyで記述可能

開発支援 (Jenkins連携)

(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)

ウェブ開発

(JRuby on Rails)

(38)

ウェブ開発 (JRuby on Rails)

Ruby on Rails - http://rubyonrails.org

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

フルスタック

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

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

(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)

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)

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)

scaffoldだけ

でも動く

%

jruby -S rails server

=> 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)

Railsツアー 5/7: 生成されたコード

モデル

ビュー

コントローラ

class Todo < ActiveRecord::Base end

class TodosController < ApplicationController

def index

@todos = Todo.all

respond_to do |format|

format.html # index.html.erb

format.json { render :json => @todos } end end def create ... 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>

<div class="actions">

<%= f.submit %>

</div>

(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

(45)

JSONでのCRUD

Railsツアー 7/7: RESTインターフェース

% 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)

ウェブ開発 (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)

JRuby on Railsのデプロイ

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

専用アプリサーバ:

Trinidad (Tomcatベース)

http://www.engineyard.com/

TorqueBox (JBossベース)

clustering、messaging、scheduling他

http://torquebox.org/

PaaS: Engine Yard Cloud

(48)

JRubyのこれから

InvokeDynamic と IR

(49)

Java SE 7: InvokeDynamic

新たなメソッド呼び出しバイトコード

bootstrapメソッド

dynamic language support (java.lang.invoke.*)

MethodHandle

MethodType

SwitchPoint

CallSite

(50)

JRubyにおけるInvokeDynamic効果

Java 5/6での

メソッド

呼び出し

(51)

JRubyにおけるInvokeDynamic効果

Java 7 +

JRuby 1.7.0.dev

(52)

JRubyにおけるInvokeDynamic効果

Java 6/7上で動作させたJRubyの比較

※横軸は速度(大きいほうが速い)

(53)

IR: JRubyの新しい内部表現形式

最適化方式の

抜本的な変更を

模索

構文木ベースの

最適化から

新中間表現

ベースへ

(54)

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

Java開発者にも学び易いRuby言語

JavaとRubyの連携方式が豊富

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

Rails、RSpec、JtestR、Jenkins

git、scripting、DSL

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

参照

関連したドキュメント

In this diagram, there are the following objects: myFrame of the Frame class, myVal of the Validator class, factory of the VerifierFactory class, out of the PrintStream class,

分野 特許関連 商標関連 意匠関連 その他知財関連 エンフォースメント 政府関連 出典 サイト BBC ※公的機関による発表 YES NO リンク

社会システムの変革 ……… P56 政策11 区市町村との連携強化 ……… P57 政策12 都庁の率先行動 ……… P57 政策13 世界諸都市等との連携強化 ……… P58

強化 若葉学園との体験交流:年間各自1~2 回実施 新規 並行通園児在籍園との連携:10園訪問実施 継続 保育園との体験交流:年4回実施.

大正13年 3月20日 大正 4年 3月20日 大正 4年 5月18日 大正10年10月10日 大正10年12月 7日 大正13年 1月 8日 大正13年 6月27日 大正13年 1月 8日 大正14年 7月17日 大正15年

定時株主総会 普通株式 利益剰余金 286 80.00 2021年3月31日 2021年6月30日. 決議 株式の種類 配当の原資

法人と各拠点 と各拠点 と各拠点 と各拠点 の連携及び、分割 の連携及び、分割 の連携及び、分割 の連携及び、分割. グループホーム

13年度 14年度 15年度中間 自己資本比率 (%) 15.0 15.8 16.5 時価ベースの自己資本比率 (%) 23.0 21.6 23.0. 債務償還年数 年 6.5