My way with Ruby

: author

Kouhei Sutou

: institution

ClearCode Inc.

: content-source

RubyKaigi 2018

: date

2018-06-01

: start-time

2018-06-01T09:40:00+09:00

: end-time

2018-06-01T10:35:00+09:00

: theme

.

Ad: Silver sponsor

# img
# src = images/clear-code-rubykaigi-2018-silver-sponsor.png
# relative_height = 100
# reflect_ratio = 0.1

Slide properties

: enable-title-on-image

false

Acknowledgmentn(('note:感謝'))

(('tag:center')) @drbrainn Eric Hodel

(('tag:center')) He fixed English in this sliden (('note:英語をチェックしてくれたよ!ありがとう!'))

Keynote-ish topicn(('note:キーノートっぽい話題'))

Did you think about it?n (('note:考えたことある?'))

Keynote-ish topic1n(('note:キーノートっぽい話題1'))

Futuren (('note:未来のこと'))

Keynote-ish topic2n(('note:キーノートっぽい話題2'))

Focus onn one thingn ((deeply))n (('note:なにかを深掘り'))

Keynote-ish topic3n(('note:キーノートっぽい話題3'))

Overviewn (('note:俯瞰した話'))

My activitiesnas a Rubyistn(('note:私のRubyist活動'))

* Increase what Ruby can do\n
  with free software\n
  (('note:フリーソフトウェアを使ってRubyでできることを増やす'))
* Maintain libraries\n
  (('note:ライブラリーのメンテナンス'))

# of libraries maintainedn(('note:メンテナンスしているライブラリー数'))

About 130n (('note:130くらい'))

Today's topicn(('note:今日の話題'))

Overviewn what we can don ((*with Ruby*))n (('note:Rubyでできるようになったことをたくさん紹介'))

How to find targets?n(('note:そんなにネタがあるの?'))

Just I neededn (('note:単に自分が必要だったから'))

Opening1n(('note:きっかけ1'))

Web feedn (('note:Webフィード'))

RSS Parser

RSS/Atom parsern withn ((validation))n (('note:バリデーション機能付きのRSS/Atomパーサー'))

RSS Parser - Historyn(('note:歴史'))

* 2003-05: The first release\n
  (('note:最初のリリース'))
  * No other RSS/Atom parser that supports validation even now\n
    (('note:今でもバリデーション付きのパーサーは他にない'))
* 2004-01: Ruby bundles this\n
  (('note:RubyがRSS Parserをバンドル'))
  * I became a Ruby committer\n
    (('note:Rubyコミッターになる'))

Validate RSS/Atomn(('note:RSS/Atomのバリデーション'))

* Important for me\n
  (('note:私にとっては大事'))
* Most wild RSS/Atom feeds are\n
  ((*invalid*))\n
  (('note:野生のRSS/Atomの多くは不正'))
  * Validation helps to find problems\n
    (('note:バリデーションがあると問題を見つけやすくなる'))

RSS::Parser.parse

# coderay ruby

# Validates by default
# デフォルトでバリデーション
RSS::Parser.parse(rss)
# Validation can be disabled
# 無効にできる
RSS::Parser.parse(rss, false)

Since Ruby 2.6n(('note:Ruby 2.6以降'))

# coderay ruby

# Supports keyword argument
# キーワード引数対応
parse(rss, validate: false)

REXML

XML parsern written inn ((*pure Ruby*))n (('note:Ruby実装のXMLパーサー'))

REXML - Historyn(('note:歴史'))

* 2001: Started by Sean Russell\n
  (('note:Seanさんが開発を開始'))
  * Based on Electric XML (Java)\n
    (('note:Java実装のElectric XMLを参考に開発'))
  * REXML is "Ruby Electric XML"\n
    (('note:REXMLは「Ruby Electric XML」'))
* 2003-01: Ruby bundles this\n
  (('note:RubyがREXMLをバンドル'))

REXML - Side storyn(('note:おまけ話'))

* Sean was "書運" in Kanji\n
  (('note:Seanさんには「書運」という漢字表記があった'))
* He was interested in Japan\n
  (('note:日本好きだった'))
  * (('tag:x-small'))"How to write your name in Kanji?"\n
    (('note:「君の名前は漢字でどう書くの?」'))
* We can connect with Ruby!\n
  (('note:RubyistはRubyをきっかけにつながれる!'))

Ad: Code Partyn(('note:宣伝:コード懇親会'))

# img
# src = images/code-party.png
# relative_height = 100
# reflect_ratio = 0.1

Slide properties

: enable-title-on-image

false

Ad: Code Partyn(('note:宣伝:コード懇親会'))

(('tag:center')) This is a challengen (('note:実験的な企画'))

* Ruby focus: to have fun\n
  (('note:Rubyは楽しさを大事にしている'))
* We have fun writing Ruby\n
  (('note:Rubyistは楽しくRubyを書いている'))
* We have fun together with writing Ruby at after party!?\n
  (('note:だったら懇親会で一緒にRubyを書くと楽しそう!?'))

Ad: Code Partyn(('note:宣伝:コード懇親会'))

* Matz attends Code Party\n
  (('note:まつもとさんもコード懇親会に参加'))
* Sponsored by Speee, Inc.\n
  (('note:Speeeさんがスポンサー'))

REXML - Recent1n(('note:最近1'))

* 2010-08: RubyKaigi 2010
  * I became the maintainer\n
    (('note:私がメンテナーになった'))
  * Because RSS Parser uses it\n
    (('note:RSS Parserが使っているから'))

REXML - Recent2n(('note:最近2'))

* 2016: (({element[attribute_name]}))
  * Ruby 2.5 ships it\n
    (('note:Ruby 2.5以降で使える'))

REXML - Recent3n(('note:最近3'))

* 2018: Fix XPath related bugs\n
  (('note:XPath関連のバグ修正'))
  * Ruby 2.6 ships it\n
    (('note:Ruby 2.6以降で使える'))

REXML - Future?n(('note:未来はあるの?'))

* ((*Pure Ruby*)) is valuable\n
  (('note:Rubyだけで書かれていることには価値がある'))
  * Easy to install\n
    (('note:インストールが簡単'))
  * JIT may improve performance\n
    (('note:NOTE: We should improve general logic before we expect JIT to improve performance😉'))\n
    (('note:JITで速くなるかもしれない'))\n
    (('note:JITの前に普通にロジックを改良するのが先だけどね😉'))\n

Recent my worksn(('note:最近の仕事'))

* XML/HTML libraries for LuaJIT\n
  (('note:LuaJIT用のXML/HTMLライブラリー'))
  * XMLua: (('tag:xx-small:((<URL:https://clear-code.github.io/xmlua/>))'))\n
    libxml2 based XML/HTML parser
  * LuaCS: (('tag:xx-small:((<URL:https://clear-code.github.io/luacs/>))'))\n
    CSS Selectors→XPath converter
* Found what is lacking in REXML API\n
  (('note:REXMLのAPIに足りないものはなにか考えた'))

REXML - Future1n(('note:未来1'))

Introduce NodeSetn (('note:NodeSetが足りないんじゃないか'))

REXML - NodeSet

# coderay ruby
doc.
  search("//list"). # => NodeSet
  search("item"). # => All <item> in <list>
  text # All texts in <item> in <list>

REXML - Future2n(('note:未来2'))

Supportn CSS Selectorsn (('note:CSSセレクターが足りないんじゃないか'))

REXML - CSS Selectorsn(('note:CSSセレクター'))

# coderay ruby
doc.css_select("ul li, dl dt")

REXML - Future3n(('note:未来3'))

Supportn HTML5 supportn (('note:HTML5対応が足りないんじゃないか'))

REXML - HTML5

# coderay ruby

doc = REXML::HTML5Document.new(html5)
doc.search("//li")
doc.css_select("ul li")

REXML - Futuren(('note:未来'))

* Low priority in my activities\n
  (('note:優先度は高くない'))
* Do you want to work with me?\n
  (('note:一緒にやりたい人はいる?'))

Opening2n(('note:きっかけ1'))

Presentationn (('note:プレゼンテーション'))

Rabbit

(('tag:center')) (('tag:large')) Presentation tooln for ((Rubyist))n (('note:Rubyist用のプレゼンツール'))

((' '))

Rabbit - Historyn(('note:歴史'))

* 2004-07: The first release\n
  (('note:最初のリリース'))
  * No other presentation tool for a Rubyist even now\n
    (('note:今でもRubyist用のプレゼンツールは他にない'))
* 2010: Matz migrated to Rabbit\n
  (('note:まつもとさんがRabbitに乗り換えた'))
  * Since RubyKaigi 2010?\n
    (('note:RubyKaigi 2010から?'))

For Rubyist?n(('note:Rubyist向けに必要なもの'))

RD supportn (('note:RDサポート'))

RD

* ((*R*))uby ((*D*))ocument\n
  * Designed by Matz (('note:(Right?)'))\n
    (('note:まつもとさんがデザインしたはず'))
* A text based markup language\n
  (('note:テキストベースのマークアップ言語'))
  * Version controllable\n
    (('note:バージョン管理できる'))

For Rubyist?n(('note:Rubyist向けに必要なもの'))

Publishn our slidesn as usualn (('note:いつも通りスライドを公開できる'))

Publish as usualn(('note:いつも通り公開'))

# coderay console
% gem push your-slide-1.0.gem

Published!n(('note:公開完了!'))

# img
# src = images/rabbit-slide-show.png
# relative_height = 78

(('tag:center')) (('tag:small')) ((<URL:slide.rabbit-shocker.org/>))

What's needed for presentation tool?n(('note:プレゼンツールに必要なもの'))

GUI

Ruby/GTK3

Multi-platform GUI toolkitn (('note:複数プラットフォーム対応GUIツールキット'))

Ruby/GTK3 - Historyn(('note:歴史'))

* 1998-01: 1st release by Matz\n
  ((<[ruby-list:5877]|URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/5877>))\n
  (('note:まつもとさんが最初のリリース'))
* 2004-05: I joined development\n
  (('note:私が開発に参加'))

Example - Window

# coderay ruby
require "gtk3"
app = Gtk::Application.new
app.signal_connect(:activate) do
  window = Gtk::ApplicationWindow.new(app)
  window.show_all
end
app.run

Approaches onnmissing libraries(1)n(('note:ライブラリーがない時のやり方(1)'))

(1) Implement only the needed features\n
    (('note:必要な機能だけ実装'))
(2) then back to Rabbit\n
    (('note:必要な機能ができたらRabbitに戻る'))

Approaches onnmissing libraries(2)n(('note:ライブラリーがない時のやり方(2)'))

(1) Implement not only the needed features\n
    (('note:必要な機能だけじゃなく'))
(2) but also (('note:almost')) all features\n
    (('note:ほぼすべての機能を実装'))
(3) then back to Rabbit\n
    (('note:終わったらRabbitに戻る'))

My approachn(('note:私のやり方'))

Implementn all featuresn (('note:testing with Rabbit'))n (('note:すべての機能を実装'))

My priorityn(('note:私の優先度'))

* Rabbit is important\n
  (('note:Rabbitは大事だけど'))
* Increasing what Ruby can do is important too\n
  (('note:Rubyでできることを増やすのも大事'))

GTK+ 3 - Sizen(('note:サイズ'))

3000 over APIsn (('note:3000以上のAPI'))

How to implementn(('note:実装方法'))

Handwritingn (('note:手書き'))n ↓n Auto generationn (('note:自動生成'))

Ruby/GI

(('tag:center')) (('tag:large')) Generate bindingsn automaticallyn at run-timen (('note:実行時に自動でバインディングを生成'))

(('note:GI: GObject Introspection'))

Ruby/GI - Historyn(('note:歴史'))

* 2012: The first commit by me\n
  (('note:最初のコミット'))
* 2014: Ruby/GTK3 used Ruby/GI\n
  (('note:Ruby/GTK3をRuby/GIベースに移行'))

Handwritingn(('note:手書き'))

# img
# src = images/ruby-gtk3-without-ruby-gi.svg
# relative_width = 100

Slide properties

: enable-title-on-image

false

Auto generationn(('note:自動生成'))

# img
# src = images/ruby-gtk3-with-ruby-gi.svg
# relative_width = 100

Slide properties

: enable-title-on-image

false

Performance(('note:(性能)'))

* Slower than handwriting\n
  (('note:手書きより遅い'))
* Overhead\n
  (('note:オーバーヘッド'))
  * Dynamic arguments conversion\n
    (('note:動的な引数の変換'))
  * libffi based function call\n
    (('note:libffiを使った関数呼び出し'))

Improve idea(('note:(改善案)'))

JIT compilingn (('note:JITコンパイル'))

JIT compiling(('note:(JITコンパイル)'))

# coderay c

VALUE rb_method_generic() {
  func = dlsym(name); ...
  ffi_call(func, ..., &result);
  return C2RB(result);
} ↓
// Build rb_method() at run-time and call it.
VALUE rb_method() {return C2RB(name(...));}

Ruby/GI - See alson(('note:参考情報'))

* "How to create bindings 2016" at RubyKaigi 2016\n
  * (('tag:xx-small'))
    ((<URL:http://rubykaigi.org/2016/presentations/ktou.html>))
* "GI Introduction" (('note:(in Japanese)'))\n
  (('note:「GObject Introspection入門」'))
  * (('tag:xx-small'))
    ((<URL:https://github.com/RubyData/workshop-materials/blob/master/gobject-introspection/introduction.md>))
  * Build system: Meson + Ninja\n
    (('note:ビルドシステム:Meson + Ninja'))

Ruby/GI based bindingsn(('note:Ruby/GIベースのバインディング'))

Ruby/Pango

Ruby/Pango

(('tag:center')) (('tag:large')) Text layout enginen withn i18n supportn (('note:国際化対応のテキストレイアウトエンジン'))

(('note:i18n: Internationalization'))

Prohibition processingn(('note:禁則処理'))

# coderay ruby

widget.signal_connect(:draw) do |_, context|
  layout = context.create_pango_layout
  layout.text = "Helloこんにちは。🍣"
  context.show_pango_layout(layout)
  GLib::Source::CONTINUE
end

Bidirectional textn(('note:双方向テキスト'))

Hello

مرحبا

こんにちは

Ruby/GI based bindingsn(('note:Ruby/GIベースのバインディング'))

Ruby/GdkPixbuf2

Ruby/GdkPixbuf2

Image manipulationn (('note:画像操作'))

Half imagen(('note:画像を半分に'))

# coderay ruby

require "gdk_pixbuf2"
# Load an image: Format is auto detected
pixbuf = GdkPixbuf::Pixbuf.new(file: "x.png")
# Scale to half size
half = pixbuf.scale(pixbuf.width / 2,
                    pixbuf.height / 2,
                    :bilinear)
# Save as different format
half.save("half.jpg")

Animated GIFn(('note:アニメーションGIF'))

# img
# src = images/the-tortoise-and-the-hare.gif
# relative_width = 100

Slide properties

: enable-title-on-image

false

Ruby/GI based bindingsn(('note:Ruby/GIベースのバインディング'))

Ruby/Poppler

Ruby/Poppler

PDFn parser/renderern (('note:PDFパーサー・レンダラー'))

Text extractionn(('note:テキスト抽出'))

# coderay ruby

require "poppler"
doc = Poppler::Document.new("x.pdf")
doc.each do |page|
  puts(page.text) # Extract all texts
end

Embed PDFn(('note:PDFの埋め込み'))

# image
# src = images/rubykaigi-2017-extension-by-cpp-title.pdf
# relative_height = 80
# reflect_ratio = 0.1

(('tag:center')) (('tag:xx-small')) ((<URL:rubykaigi.org/2017/presentations/ktou.html>))

Slide properties

: enable-title-on-image

false

Ruby/GI based bindingsn(('note:Ruby/GIベースのバインディング'))

Ruby/GStreamer

Ruby/GStreamer

(('tag:center')) (('tag:large')) Audio/Video playern (('note:音声・動画プレイヤー'))

(('note:(*) Streaming media framework'))n (('note:本当はストリーミングメディアフレームワーク'))

Camera(('note:(カメラ)'))

# coderay ruby

require "gst"
description = [
  "autovideosrc",  # Camera
  "videoconvert",  # Filter
  "autovideosink", # Window
].join(" ! ")
pipeline = Gst.parse_launch(description)
pipeline.play
until pipeline.bus.poll.type.eos? do
end # Main loop
pipeline.stop

Face detection(('note:(顔認識)'))

# coderay ruby

description = [
  "autovideosrc",  # Camera
  "videoconvert",  # Filter
  "facedetect",    # Face detection (!)
  "videoconvert",  # Filter         (!)
  "autovideosink", # Window
].join(" ! ")
pipeline = Gst.parse_launch(description)
pipeline.play

# = Embed video

# TODO

# # # video # # # src = test.mp4

What's needed for presentation tool?n(('note:プレゼンツールに必要なもの'))

PDF outputn (('note:PDF出力'))

rcairo

2D graphics renderern (('note:2次元画像レンダラー'))

rcairo - Outputsn(('note:出力'))

* PNG・SVG
* ((*PDF*))
* ((*Display*)) (X/macOS/Windows)
* ...

rcairo - Historyn(('note:歴史'))

* 2003-10: The initial commit\n
  (('note:最初のコミット'))
* 2005-09: I started developing\n
  (('note:私が開発に参加'))

Red A4 PDFn(('note:赤いA4のPDFを出力'))

# coderay ruby

require "cairo"
include Cairo
PDFSurface.create("x.pdf", "A4") do |surface|
  Context.create(surface) do |context|
    context.set_source_color(:red)
    context.paint
  end
end

rcairo - GC

  # image
  # src = images/cairo-no-gc-trigger.pdf
  # align = right
  # vertical_align = bottom
  # relative_width = 65
  # relative_padding_right = -7
  # relative_padding_bottom = -10

# coderay ruby
1000.times do
  Cairo::ImageSurface.new(:argb32, 6000, 6000)
end

(('tag:margin-bottom * 20'))

GC - Cause(('note:(原因)'))

  # image
  # src = images/cairo-no-gc-trigger.pdf
  # align = right
  # vertical_align = bottom
  # relative_width = 67
  # relative_padding_right = -11
  # relative_padding_bottom = -10

* GC isn't run often enough\n
  (('note:GCの実行頻度が十分じゃなかった'))
  * Because Ruby doesn't know how much memory used by cairo\n
    (('note:Rubyはcairoのメモリー使用量を知らないから'))

(('tag:margin-bottom * 16'))

GC - Fix(('note:(修正)'))

  # image
  # src = images/cairo-gc-trigger.pdf
  # align = right
  # vertical_align = bottom
  # relative_width = 67
  # relative_padding_right = -11
  # relative_padding_bottom = -10

* (({rb_gc_adjust_memory_usage()}))
  * Improve GC frequency(('note:(GC実行頻度を改善)'))
* Ruby 2.4 ships it(('note:(Ruby 2.4以降)'))

(('tag:margin-bottom * 18'))

What's needed for presentation tool?n(('note:プレゼンツールに必要なもの'))

Easy to installn (('note:簡単インストール'))

native-package-installer

Installn system packagesn on (({gem install}))n (('note:gem install時にシステムのパッケージをインストール'))

extconf.rb/Rakefile

# coderay ruby

require "pkg-config"
require "native-package-installer"
unless PKGConfig.check_version?("gdk-3.0")
  packages = {
    altlinux: "libgtk+3-devel",
    debian: "libgtk-3-dev",
    redhat: "pkgconfig(gdk-3.0)",
    homebrew: "gtk+3",
    macports: "gtk3",
    msys2: "gtk3",
  }
  unless NativePackageInstaller.install(packages)
    exit(false)
  end
end

rake-compiler

Build fat gemn byn cross compilen (('note:クロスコンパイルでfat gemをビルド'))

rake-compiler - Historyn(('note:歴史'))

* 2008-11: The first commit\n
  (('note:最初のコミット'))
* 2014-12: Orphan\n
  (('note:だれかメンテナー変わってー'))
* 2014-12:\n
  I became the maintainer\n
  (('note:私がメンテナーになった'))

Opening3n(('note:きっかけ3'))

Testn (('note:テスト'))

test-unit

Testing frameworkn to write testsn ((*in Ruby*))n (('note:Rubyでテストを書けるテスティングフレームワーク'))

test-unit - Historyn(('note:歴史'))

* 2003-02: Import to Ruby\n
  (('note:Rubyに取り込まれる'))
* 2008-05:\n
  I became the maintainer\n
  (('note:私がメンテナーになった'))
* 2008-10: Removed from Ruby\n
  (('note:Rubyから削除'))

(('note:See also: “The history of testing framework in Ruby”'))n (('tag:x-small'))((<URL:rubykaigi.org/2015/presentations/kou>))

test-unit - New featuren(('note:新機能'))

Groupingn (('note:グループ化'))

Groupingn(('note:グループ化'))

* The most important feature\n
  (('note:一番大事な機能'))
* Keep tests maintainable\n
  (('note:メンテナンスできるテストを維持できる'))

Example(('note:(例)'))

# coderay ruby

class StackTest < Test::Unit::TestCase
  class PushTest < self
    def test_XXX; end
  end
  class PopTest < self
    def test_XXX; end
  end
end

Method style(('note:(メソッド形式)'))

# coderay ruby

class StackTest < Test::Unit::TestCase
  sub_test_case("#push") do
    def test_XXX; end
  end
  sub_test_case("#pop") do
    def test_XXX; end
  end
end

test-unit - New featuren(('note:新機能'))

Data driven testn (('note:データ駆動テスト'))

Data driven testn(('note:データ駆動テスト'))

# coderay ruby

data("positive", [3,  1, 2])
data("negative", [-4, 1, -5])
def test_add(data)
  expected, augend, addend = data
  assert_equal(expected,
               add(augend, addend))
end

test-unit - New featuren(('note:新機能'))

Reverse backtracen (('note:逆順のバックトレース'))

Reverse backtracen(('note:逆順のバックトレース'))

* Reverse backtrace only for terminal output\n
  (('note:ターミナル出力のときだけ逆順'))
* The same change as Ruby 2.5.0\n
  (('note:Ruby 2.5.0と同じ変更'))

test-unit - New featuren(('note:新機能'))

Test doublen (('note:テストダブル'))

test-unit-rr

RR integrationn (('note:RRとの統合'))

RR - Historyn(('note:歴史'))

* 2007-06: The initial commit\n
  (('note:最初のコミット'))
* 2014-12: Orphan\n
  (('note:だれかメンテナー変わってー'))
* 2015-05:\n
  I became the maintainer\n
  (('note:私がメンテナーになった'))

Stub(('note:(スタブ)'))

# coderay ruby

adder = Object.new
adder.add(1, 2) # => Error
stub(adder).add(1, 2) {3}
adder.add(1, 2) # => 3

Opening4n(('note:きっかけ4'))

Full text searchn (('note:全文検索'))

Rroonga

Full text searchn ((library))n (('note:全文検索ライブラリー'))

((Library)) vs Clientn(('note:ライブラリー対クライアント'))

* No server process\n
  (('note:サーバープロセスがいらない'))
* Easy to start\n
  (('note:簡単に使い始められる'))
* Write in Ruby\n
  (('note:Rubyで書ける'))

Create DB(('note:(データベース作成)'))

# coderay ruby
require "groonga"

Groonga::Database.create(path: "/tmp/db")

Define schema(('note:(スキーマ定義)'))

# coderay ruby

Groonga::Schema.define do |schema|
  schema.create_table("docs") do |table|
    # The column to store text
    table.text("content")
  end
  # The index for full text search
  schema.create_lexicon("terms") do |table|
    table.index("docs.content")
  end
end

Add records(('note:(レコード追加)'))

# coderay ruby

docs = Groonga["docs"]
docs.add(content: "String#<< concatenates ...")
docs.add(content: "String#dup duplicates ...")

Search(('note:(検索)'))

# coderay ruby

matches = docs.select do |record|
  record.content.match("concat")
end
p matches.size # => 1
matches.each do |record|
  p record.content # => "String#<< concat..."
end

User - Rabbit Slide Show

# img
# src = images/rabbit-slide-show.png
# relative_height = 78

(('tag:center')) (('tag:small')) ((<URL:slide.rabbit-shocker.org/>))

User - Rurema Search

# img
# src = images/rurema-search.png
# relative_height = 78

(('tag:center')) (('tag:small')) ((<URL:docs.ruby-lang.org/ja/search/>))

Rurema Searchn(('note:るりまサーチ'))

* Super fast!\n
  (('note:すごく速い!'))
* Tuned for Ruby documents\n
  (('note:Rubyのドキュメント用にチューニング'))

User - RDoc Search

* Planning\n
  (('note:考えてはいるけど。。。'))
* Do you want to work with me?\n
  (('note:一緒にやりたい人はいる?'))

Rurema and RDoc

# RT

Project, Language, Target

Rurema, Japanese, Japanese Rubyists
RDoc, English, All Rubyists

Sourcen(('note:ソース'))

* Shared nothing\n
  (('note:共有していない'))
  * Copy based share\n
    (('note:共有するときはコピー'))
  * e.g.:\n
    Description,\n
    Sample codes,\n
    ...\n
    (('note:例:説明やサンプルコードなどをコピー'))

From my point of viewn(('note:私が思うこと'))

* Can we share documents?\n
  (('note:ドキュメントを共有できないかな'))
* How to work together deeply?\n
  (('note:もっと協力してできないかな'))

I18nn(('note:国際化'))

* Source: RDoc\n
  (('note:ソースはRDoc'))
  * For all Rubyists\n
    (('note:これは全Rubyist向け'))
* Translate to Japanese\n
  (('note:RDocのドキュメントを日本語に翻訳'))
  * For Japanese Rubyists\n
    (('note:これは日本人Rubyist向け'))

Add i18n supportn(('note:国際化サポートを追加'))

* YARD
  * Since 0.8.0 at 2012-04
* RDoc
  * Since 4.2.0 at 2014-12

YARD - i18n

# coderay console

# Generates po/yard.pot
# po/yard.potを生成
% yard i18n

YARD - i18n

# coderay console

# Create po/ja.po from po/yard.pot
# po/yard.potからpo/ja.poを作成
% msginit \
    --locale=ja_JP.UTF-8 \
    --input=po/yard.pot \
    --output-file=po/ja.po

YARD - i18n

# coderay console

# Translate messages in po/ja.po
# po/ja.po内のメッセージを翻訳
% editor po/ja.po

YARD - i18n

# coderay console

# Generate documents with
# translated messages
# 翻訳したメッセージを使って
# ドキュメント生成
% yard --locale ja

Packnga

Rake taskn forn YARD i18nn (('note:YARDの国際化機能向けのRakeタスク'))

Settingn(('note:設定'))

# coderay ruby

# Rakefile
require "packnga"
Packnga::DocumentTask.new(spec) do |task|
  task.original_language = "en"
  task.translate_languages = ["ja"]
end

Workflown(('note:ワークフロー'))

# coderay console

% rake reference:translate
% editor doc/po/ja/x.edit.po
% rake reference:translate
% editor lib/x.rb
% rake reference:translate
...

Usersn(('note:ユーザー'))

* test-unit
* Rroonga
* ...

RDoc - i18n

# coderay console

# Generates doc/rdoc.pot
# doc/rdoc.potを生成
% rdoc --format=pot

RDoc - i18n

# coderay console

# Create locale/ja.po
# from doc/rdoc.pot
# doc/rdoc.potからlocale/ja.poを作成
% mkdir -p locale
% msginit \
    --locale=ja_JP.UTF-8 \
    --input=doc/rdoc.pot \
    --output-file=locale/ja.po

RDoc - i18n

# coderay console

# Translate messages in locale/ja.po
# locale/ja.po内のメッセージを翻訳
% editor locale/ja.po

RDoc - i18n

# coderay console

# Generate documents with
# translated messages
# 翻訳したメッセージを使って
# ドキュメント生成
% rdoc --locale ja

RDoc, Rurema and i18n

* No progress...\n
  (('note:ツールの整備まででそれ以降は進んでいない。。。'))
* Do you want to work with me?\n
  (('note:一緒にやりたい人はいる?'))

jekyll-task-i18n

Jekyll + i18n

Featuresn(('note:機能'))

* Support all markups!\n
  (('note:すべてのマークアップ対応!'))
* GitHub Pages ready!\n
  (('note:GitHub Pagesでも使える!'))

Setting(('note:(設定)'))

# coderay ruby

# Rakefile
require "jekyll/task/i18n"
Jekyll::Task::I18n.define do |task|
  task.locales = ["ja"]
  task.files = Rake::FileList["**/*.md"]
  task.files -= Rake::FileList["_*/**/*.md"]
  task.locales.each do |locale|
    task.files -= Rake::FileList["#{locale}/**/*.md"]
  end
end
task default: ["jekyll:i18n:translate"]

Workflow(('note:(ワークフロー)'))

% editor index.md
% rake
% editor _po/ja/index.edit.po
% rake
% git commit -a

User - Red Data Tools

# img
# src = images/jekyll-task-i18n-red-data-tools.gif
# relative_height = 78

(('tag:center')) (('tag:small')) ((<URL:red-data-tools.github.io/>))

groonga-client

Full text searchn ((client))n (('note:全文検索クライアント'))

Library vs ((Client))n(('note:ライブラリー対クライアント'))

* Less dependencies\n
  (('note:依存関係が少ない'))
* Less resources needed\n
  (('note:必要なリソースが少ない'))

Search(('note:(検索)'))

# coderay ruby

require "groonga/client"
url = "http://localhost:10041"
Groonga::Client.open(url: url) do |client|
  response =
    client.select(table: "docs",
                  match_columns: "content",
                  query: "concat")
  p response.n_hits # => 1
end

Asynchronous(('note:(非同期)'))

# coderay ruby

# Call with block
client.select(table: "docs",
              match_columns: "content",
              query: "concat") do |response|  
  p response.n_hits # => 1
end
p :here # => :here then ↑
sleep(0.1)

Asynchronous - wait

# coderay ruby

request =
  client.select(table: "docs",
                match_columns: "content",
                query: "concat") do |response|
  p response.n_hits # => 1
end
p :here # => :here then ↑
request.wait

groonga-client-rails

Ruby on Railsn integrationn forn groonga-clientn (('note:Ruby on Railsで使う'))

Architecturen(('note:アーキテクチャー'))

* Data: RDBMS\n
  (('note:データはRDBMSに格納'))
* Full text search: Groonga\n
  (('note:全文検索はGroongaで処理'))

Define app searchern(('note:アプリ用サーチャーを定義'))

# coderay ruby

# app/searchers/application_searcher.rb
class ApplicationSearcher <
        Groonga::Client::Searcher
end

Define searchern(('note:サーチャーを定義'))

# coderay ruby

# app/searchers/document_searcher.rb
class DocumentsSearcher < ApplicationSearcher
  # Define a full text search index as "content"
  # 全文検索用のインデックスを定義
  schema.column :content, {
    type: "Text",
    index: true,
    index_type: :full_text_search,
  }
end

Bind to modeln(('note:モデルと結びつける'))

# coderay ruby

# app/models/document.rb
class Document < ApplicationRecord
  # DocumentsSearcher searches Document model
  source = DocumentsSearcher.source(self)
  # Bind Document's "content" column to
  # DocumentsSearcher's "content" index
  source.content = :content
end

Search(('note:(検索)'))

# coderay ruby

# app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
  def index
    @query = params[:query]
    searcher = DocumentSearcher.new
    @result_set = searcher.search.
       query(@query).
       result_set
  end
end

See alson(('note:参考情報'))

* Tutorial in Japanese\n
  (('note:日本語のチュートリアル'))
  * (('tag:xx-small'))
    ((<URL:http://www.clear-code.com/blog/2016/12/22.html>))

Ranguba (WIP)(('note:(開発中)'))

Full text search systemn (('note:全文検索システム'))

Use casesn(('note:利用例'))

* File server search\n
  (('note:ファイルサーバー検索'))
* E-mail search\n
  (('note:メール検索'))
* Web site search\n
  (('note:Webサイト検索'))

Featuresn(('note:機能'))

* Crawlers\n
  (('note:クローラー'))
* Web UI
* Command line interface
  * Update documents\n
    (('note:更新'))
  * Search documents\n
    (('note:検索'))

ChupaText

Text extractorn (('note:テキスト抽出'))

Supported formatsn(('note:対応フォーマット'))

* PDF
* Office documents(('note:(オフィス文書)'))
  * OpenDocument, Word, Excel, ...
* E-mail(('note:(メール)'))
* ...

Interfacen(('note:インターフェイス'))

* HTTP
* Web UI
* Command line interface
* API (Library)

Install - Docker

# coderay console
% GITHUB=https://github.com
% git clone \
   ${GITHUB}/ranguba/chupa-text-docker.git
% cd chupa-text-docker
% docker-compose up --build

How to usen(('note:使い方'))

# coderay console
% curl \
    --form data=@XXX.pdf \
    http://localhost:20080/extraction.json

Use casesn(('note:利用例'))

* Ranguba
  * Full text search system\n
    (('note:全文検索システム'))
* Commit e-mail\n
  (('note:コミットメール'))

git-commit-mailer

Commit e-mail for Gitn (('note:Git用のコミットメール'))

Featuresn(('note:機能'))

* HTML mail\n
  (('note:HTMLメール'))
  * Highlighted diff\n
    (('note:diffをハイライト'))
* GitLab/GitHub Web hook\n
  (('note:GitLab/GitHubのWebフック対応'))
  * (('tag:xx-small'))
    By ((<"GitHub:clear-code/github-web-hooks-receiver"|URL:https://github.com/clear-code/github-web-hooks-receiver>))

Usersn(('note:利用者'))

* tDiary
* My products

commit-email.info

Commit e-mailn as an Servicen (('note:コミットメールのクラウドサービス'))

How to usen(('note:使い方'))

* Send a pull request to\n
  ((<"GitHub:kou/commit-email.info"|URL:https://github.com/kou/commit-email.info>))\n
  (('note:pull requestを送る'))
* Register a Web hook\n
  (('note:Webフックを登録'))
* Subscribe your mailing list\n
  (('note:メーリングリストを購読'))

(('note:See also ((<URL:www.commit-email.info/>))'))

Opening5n(('note:きっかけ5'))

Data processingn (('note:データ処理'))

csv

CSV parsern (('note:CSVパーサー'))

csv - Historyn(('note:歴史'))

* 2003: Import\n
  (('note:Rubyに取り込み'))
* 2007: Replaced with FasterCSV\n
  (('note:FasterCSVで置き換え'))
* 2018: I became a co-maintainer with mrkn\n
  (('note:mrknと一緒にメンテナーになった'))

Why?n(('note:なんで?'))

* There are many data sources\n
  in CSV\n
  (('note:CSVのデータはたくさんある'))
* Important to process data\n
  (('note:データを処理するためにCSVパーサーは重要'))

CSV format problemsn(('note:CSVフォーマットの問題'))

* Slow to parse\n
  (('note:パースが遅い'))
* Too wild\n
  (('note:なんでもあり'))

Red Arrow

Apache Arrow Ruby

Red Arrow - Historyn(('note:歴史'))

* 2017-02: The first commit\n
  (('note:最初のコミット'))
* 2018-05: Became the "official" Ruby bindings of Apache Arrow\n
  (('note:Apache Arrowの公式Rubyバインディングになった'))

Apache Arrow

* Super fast data format\n
  (('note:すごく速いデータフォーマット'))
  * For in-memory data\n
    (('note:インメモリーデータ用'))
* Cross-language support\n
  (('note:いろんな言語がサポート'))
  * Easy to share data with Python, Java, ...\n
    (('note:PythonやJavaなどとデータ交換がしやすい'))

Performance(('note:(性能)'))

# image
# src = images/csv-arrow.pdf
# relative_width = 100

Slide properties

: enable-title-on-image

false

Apache Arrow - Positionn(('note:立ち位置'))

* A very important piece\n
  in recent data processing\n
  (('note:最近のデータ処理界隈ではすごく大事な1ピース'))
  * Like JIT for Ruby 3\n
    (('note:Ruby 3で例えるとJITみたいな感じ'))

Red Arrow - Impl.n(('note:実装'))

* Based on Ruby/GI\n
  (('note:Ruby/GIを使っている'))
  * Auto generated bindings\n
    (('note:バインディングを自動生成'))

Extendable load APIn(('note:拡張可能なロードAPI'))

# coderay ruby

# Load Apache Arrow data
Arrow::Table.load("iris.arrow")
# Load CSV data
Arrow::Table.load("iris.csv")
# Load Apache Parquet data
Arrow::Table.load("iris.parquet")

Apache Parquet

* Super fast data format\n
  (('note:すごく速いデータフォーマット'))
  * For storing analysis target data\n
    (('note:解析対象のデータを保存する用'))
* Widely used\n
  (('note:広く使われている'))

Performance(('note:(性能)'))

# image
# src = images/csv-arrow-parquet.pdf
# relative_width = 100

Slide properties

: enable-title-on-image

false

Red Parquet

Apache Parquet

Red Data Tools

A project to make Ruby data processablen (('note:Rubyでデータ処理できるようにするためのプロジェクト'))

Red Data Tools - Historyn(('note:歴史'))

* 2017-02: Start(('note:(開始)'))
* 2017-11-: Develop events per month at Tokyo\n
  (('note:東京で毎月開発イベントを開催'))

The number of productsn(('note:プロダクト数'))

About 20n (('note:including Red Arrow and Red Parquet'))n (('note:20くらい'))n (('note:Red ArrowやRed ParquetもRed Data Toolsプロダクツ'))

Red Datasets

Dataset fetchern (('note:データセット取得'))

Supported datasetsn(('note:対応データセット'))

* Iris
* CIFAR
* Wikipedia

Wikipedia

# coderay ruby

require "datasets"

wikipedia = Datasets::Wikipedia.new
wikipedia.each do |page|
  p page.title
end

Wikipedia search

# coderay ruby

pages = Groonga["pages"]
wikipedia = Datasets::Wikipedia.new
wikipedia.each do |page|
  pages.add(title: page.title,
            content: page.revision.text)
end
ruby_pages = pages.select do |record|
  record.match("Ruby OR Rails") do |target|
    (target.title * 10) | target.content
  end
end
p ruby_pages.size

jekyll-jupyter-notebook

Jekylln (('+'))n Jupyter Notebook

Usagen(('note:使い方'))

{% jupyter_notebook sample.ipynb %}

Red OpenCV

Computer visionn (('note:コンピュータービジョン'))

Camera(('note:(カメラ)'))

# coderay ruby

require "cv"
camera = CV::Camera.new
image = camera.read
image.write("capture.jpg")

Face detect(('note:(顔認識)'))

# coderay ruby

image_gray = image.convert_color(:bgr2gray)
classifier = # Face detector
  CV::CascadeClassifier.new("frontalface_alt")
objects = classifier.detect(image_gray)
color = CV::Color.new(0, 0, 255)
objects.each do |object|
  # Draw detected area
  image.draw_rectangle(object, color)
end
image.write("detect.jpg")

Red OpenCV - Impl.n(('note:実装'))

* Based on Ruby/GI\n
  (('note:Ruby/GIを使っている'))
  * Auto generated bindings\n
    (('note:バインディングを自動生成'))

Ad: RubyData Workshop

* 2018-06-01 15:50/17:20
* Contents:(('note:(内容)'))
  * Workshop by mrkn\n
    (('note:mrknによるワークショップ'))
  * Presentations from\n
    Red Data Tools members\n
    (('note:Red Data ToolsメンバーによるRed Data Toolsでやってきたことの紹介'))

Process data with Rubyn(('note:Rubyでデータ処理'))

* We're working on it\n
  (('note:Red Data Toolsは継続して取り組んでいる'))
* Do you want to work with us?\n
  (('note:一緒にやりたい人はいる?'))

How to join1(('note:(参加方法1)'))

* Join our chat rooms:\n
  (('note:チャットルームに参加'))
  * en: ((<"Gitter:red-data-tools/en"|URL:https://gitter.im/red-data-tools/en>))
  * ja: ((<"Gitter:red-data-tools/ja"|URL:https://gitter.im/red-data-tools/ja>))
* Join monthly events at Tokyo\n
  (('note:東京での毎月の開発イベントに参加'))
  * ((<URL:https://speee.connpass.com/>))

How to join2(('note:(参加方法2)'))

* Hire developers to work on it\n
  (('note:仕事として開発する開発者を雇う'))
  * e.g.: mrkn by Speee, Inc.\n
    (('note:例:Speeeの村田さん'))

How to join3(('note:(参加方法3)'))

* Order ClearCode to work on it\n
  (('note:クリアコードに開発の仕事を発注'))
* Join ClearCode to work on it\n
  (('note:クリアコードに入って仕事として開発を進める'))

Wrap upn(('note:まとめ'))

* I'm working on the following as a Rubyist\n
  (('note:Rubyistとしての私の活動'))
  * Increase what Ruby can do\n
    with free software\n
    (('note:フリーソフトウェアを使ってRubyでできることを増やす'))
  * Maintain libraries\n
    (('note:ライブラリーのメンテナス'))
* Do you want to work with me?\n
  (('note:一緒にやりたい人はいる?'))