= 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
= Acknowledgment\n(('note:感謝'))
(('tag:center'))
@drbrain\n
Eric Hodel
(('tag:center'))
He fixed English in this slide\n
(('note:英語をチェックしてくれたよ!ありがとう!'))
= Keynote-ish topic\n(('note:キーノートっぽい話題'))
Did you think about it?\n
(('note:考えたことある?'))
= Keynote-ish topic1\n(('note:キーノートっぽい話題1'))
Future\n
(('note:未来のこと'))
= Keynote-ish topic2\n(('note:キーノートっぽい話題2'))
Focus on\n
one thing\n
((*deeply*))\n
(('note:なにかを深掘り'))
= Keynote-ish topic3\n(('note:キーノートっぽい話題3'))
Overview\n
(('note:俯瞰した話'))
= My activities\nas a Rubyist\n(('note:私のRubyist活動'))
* Increase what Ruby can do\n
with free software\n
(('note:フリーソフトウェアを使ってRubyでできることを増やす'))
* Maintain libraries\n
(('note:ライブラリーのメンテナンス'))
= # of libraries maintained\n(('note:メンテナンスしているライブラリー数'))
About 130\n
(('note:130くらい'))
= Today's topic\n(('note:今日の話題'))
Overview\n
what we can do\n
((*with Ruby*))\n
(('note:Rubyでできるようになったことをたくさん紹介'))
= How to find targets?\n(('note:そんなにネタがあるの?'))
Just I needed\n
(('note:単に自分が必要だったから'))
= Opening1\n(('note:きっかけ1'))
Web feed\n
(('note:Webフィード'))
= RSS Parser
RSS/Atom parser\n
with\n
((*validation*))\n
(('note:バリデーション機能付きのRSS/Atomパーサー'))
= RSS Parser - History\n(('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/Atom\n(('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.6\n(('note:Ruby 2.6以降'))
# coderay ruby
# Supports keyword argument
# キーワード引数対応
parse(rss, validate: false)
= REXML
XML parser\n
written in\n
((*pure Ruby*))\n
(('note:Ruby実装のXMLパーサー'))
= REXML - History\n(('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 story\n(('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 Party\n(('note:宣伝:コード懇親会'))
# img
# src = images/code-party.png
# relative_height = 100
# reflect_ratio = 0.1
== Slide properties
: enable-title-on-image
false
= Ad: Code Party\n(('note:宣伝:コード懇親会'))
(('tag:center'))
This is a challenge\n
(('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 Party\n(('note:宣伝:コード懇親会'))
* Matz attends Code Party\n
(('note:まつもとさんもコード懇親会に参加'))
* Sponsored by Speee, Inc.\n
(('note:Speeeさんがスポンサー'))
= REXML - Recent1\n(('note:最近1'))
* 2010-08: RubyKaigi 2010
* I became the maintainer\n
(('note:私がメンテナーになった'))
* Because RSS Parser uses it\n
(('note:RSS Parserが使っているから'))
= REXML - Recent2\n(('note:最近2'))
* 2016: (({element[attribute_name]}))
* Ruby 2.5 ships it\n
(('note:Ruby 2.5以降で使える'))
= REXML - Recent3\n(('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 works\n(('note:最近の仕事'))
* XML/HTML libraries for LuaJIT\n
(('note:LuaJIT用のXML/HTMLライブラリー'))
* XMLua: (('tag:xx-small:(())'))\n
libxml2 based XML/HTML parser
* LuaCS: (('tag:xx-small:(())'))\n
CSS Selectors→XPath converter
* Found what is lacking in REXML API\n
(('note:REXMLのAPIに足りないものはなにか考えた'))
= REXML - Future1\n(('note:未来1'))
Introduce NodeSet\n
(('note:NodeSetが足りないんじゃないか'))
= REXML - NodeSet
# coderay ruby
doc.
search("//list"). # => NodeSet
search("item"). # => All - in
text # All texts in - in
= REXML - Future2\n(('note:未来2'))
Support\n
CSS Selectors\n
(('note:CSSセレクターが足りないんじゃないか'))
= REXML - CSS Selectors\n(('note:CSSセレクター'))
# coderay ruby
doc.css_select("ul li, dl dt")
= REXML - Future3\n(('note:未来3'))
Support\n
HTML5 support\n
(('note:HTML5対応が足りないんじゃないか'))
= REXML - HTML5
# coderay ruby
doc = REXML::HTML5Document.new(html5)
doc.search("//li")
doc.css_select("ul li")
= REXML - Future\n(('note:未来'))
* Low priority in my activities\n
(('note:優先度は高くない'))
* Do you want to work with me?\n
(('note:一緒にやりたい人はいる?'))
= Opening2\n(('note:きっかけ1'))
Presentation\n
(('note:プレゼンテーション'))
= Rabbit
(('tag:center'))
(('tag:large'))
Presentation tool\n
for ((*Rubyist*))\n
(('note:Rubyist用のプレゼンツール'))
((' '))
= Rabbit - History\n(('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 support\n
(('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向けに必要なもの'))
Publish\n
our slides\n
as usual\n
(('note:いつも通りスライドを公開できる'))
= Publish as usual\n(('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'))
(())
= What's needed for presentation tool?\n(('note:プレゼンツールに必要なもの'))
GUI
= Ruby/GTK3
Multi-platform GUI toolkit\n
(('note:複数プラットフォーム対応GUIツールキット'))
= Ruby/GTK3 - History\n(('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 on\nmissing libraries(1)\n(('note:ライブラリーがない時のやり方(1)'))
(1) Implement only the needed features\n
(('note:必要な機能だけ実装'))
(2) then back to Rabbit\n
(('note:必要な機能ができたらRabbitに戻る'))
= Approaches on\nmissing 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 approach\n(('note:私のやり方'))
Implement\n
all features\n
(('note:testing with Rabbit'))\n
(('note:すべての機能を実装'))
= My priority\n(('note:私の優先度'))
* Rabbit is important\n
(('note:Rabbitは大事だけど'))
* Increasing what Ruby can do is important too\n
(('note:Rubyでできることを増やすのも大事'))
= GTK+ 3 - Size\n(('note:サイズ'))
3000 over APIs\n
(('note:3000以上のAPI'))
= How to implement\n(('note:実装方法'))
Handwriting\n
(('note:手書き'))\n
↓\n
Auto generation\n
(('note:自動生成'))
= Ruby/GI
(('tag:center'))
(('tag:large'))
Generate bindings\n
automatically\n
at run-time\n
(('note:実行時に自動でバインディングを生成'))
(('note:GI: GObject Introspection'))
= Ruby/GI - History\n(('note:歴史'))
* 2012: The first commit by me\n
(('note:最初のコミット'))
* 2014: Ruby/GTK3 used Ruby/GI\n
(('note:Ruby/GTK3をRuby/GIベースに移行'))
= Handwriting\n(('note:手書き'))
# img
# src = images/ruby-gtk3-without-ruby-gi.svg
# relative_width = 100
== Slide properties
: enable-title-on-image
false
= Auto generation\n(('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 compiling\n
(('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 also\n(('note:参考情報'))
* "How to create bindings 2016" at RubyKaigi 2016\n
* (('tag:xx-small'))
(())
* "GI Introduction" (('note:(in Japanese)'))\n
(('note:「GObject Introspection入門」'))
* (('tag:xx-small'))
(())
* Build system: Meson + Ninja\n
(('note:ビルドシステム:Meson + Ninja'))
= Ruby/GI based bindings\n(('note:Ruby/GIベースのバインディング'))
Ruby/Pango
= Ruby/Pango
(('tag:center'))
(('tag:large'))
Text layout engine\n
with\n
i18n support\n
(('note:国際化対応のテキストレイアウトエンジン'))
(('note:i18n: Internationalization'))
= Prohibition processing\n(('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 text\n(('note:双方向テキスト'))
Hello
مرحبا
こんにちは
= Ruby/GI based bindings\n(('note:Ruby/GIベースのバインディング'))
Ruby/GdkPixbuf2
= Ruby/GdkPixbuf2
Image manipulation\n
(('note:画像操作'))
= Half image\n(('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 GIF\n(('note:アニメーションGIF'))
# img
# src = images/the-tortoise-and-the-hare.gif
# relative_width = 100
== Slide properties
: enable-title-on-image
false
= Ruby/GI based bindings\n(('note:Ruby/GIベースのバインディング'))
Ruby/Poppler
= Ruby/Poppler
PDF\n
parser/renderer\n
(('note:PDFパーサー・レンダラー'))
= Text extraction\n(('note:テキスト抽出'))
# coderay ruby
require "poppler"
doc = Poppler::Document.new("x.pdf")
doc.each do |page|
puts(page.text) # Extract all texts
end
= Embed PDF\n(('note:PDFの埋め込み'))
# image
# src = images/rubykaigi-2017-extension-by-cpp-title.pdf
# relative_height = 80
# reflect_ratio = 0.1
(('tag:center'))
(('tag:xx-small'))
(())
== Slide properties
: enable-title-on-image
false
= Ruby/GI based bindings\n(('note:Ruby/GIベースのバインディング'))
Ruby/GStreamer
= Ruby/GStreamer
(('tag:center'))
(('tag:large'))
Audio/Video player\n
(('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 output\n
(('note:PDF出力'))
= rcairo
2D graphics renderer\n
(('note:2次元画像レンダラー'))
= rcairo - Outputs\n(('note:出力'))
* PNG・SVG
* ((*PDF*))
* ((*Display*)) (X/macOS/Windows)
* ...
= rcairo - History\n(('note:歴史'))
* 2003-10: The initial commit\n
(('note:最初のコミット'))
* 2005-09: I started developing\n
(('note:私が開発に参加'))
= Red A4 PDF\n(('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 install\n
(('note:簡単インストール'))
= native-package-installer
Install\n
system packages\n
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 gem\n
by\n
cross compile\n
(('note:クロスコンパイルでfat gemをビルド'))
= rake-compiler - History\n(('note:歴史'))
* 2008-11: The first commit\n
(('note:最初のコミット'))
* 2014-12: Orphan\n
(('note:だれかメンテナー変わってー'))
* 2014-12:\n
I became the maintainer\n
(('note:私がメンテナーになった'))
= Opening3\n(('note:きっかけ3'))
Test\n
(('note:テスト'))
= test-unit
Testing framework\n
to write tests\n
((*in Ruby*))\n
(('note:Rubyでテストを書けるテスティングフレームワーク'))
= test-unit - History\n(('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'))(())
= test-unit - New feature\n(('note:新機能'))
Grouping\n
(('note:グループ化'))
= Grouping\n(('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 feature\n(('note:新機能'))
Data driven test\n
(('note:データ駆動テスト'))
= Data driven test\n(('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 feature\n(('note:新機能'))
Reverse backtrace\n
(('note:逆順のバックトレース'))
= Reverse backtrace\n(('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 feature\n(('note:新機能'))
Test double\n
(('note:テストダブル'))
= test-unit-rr
RR integration\n
(('note:RRとの統合'))
= RR - History\n(('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
= Opening4\n(('note:きっかけ4'))
Full text search\n
(('note:全文検索'))
= Rroonga
Full text search\n
((*library*))\n
(('note:全文検索ライブラリー'))
= ((*Library*)) vs Client\n(('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'))
(())
= User - Rurema Search
# img
# src = images/rurema-search.png
# relative_height = 78
(('tag:center'))
(('tag:small'))
(())
= Rurema Search\n(('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
= Source\n(('note:ソース'))
* Shared nothing\n
(('note:共有していない'))
* Copy based share\n
(('note:共有するときはコピー'))
* e.g.:\n
Description,\n
Sample codes,\n
...\n
(('note:例:説明やサンプルコードなどをコピー'))
= From my point of view\n(('note:私が思うこと'))
* Can we share documents?\n
(('note:ドキュメントを共有できないかな'))
* How to work together deeply?\n
(('note:もっと協力してできないかな'))
= I18n\n(('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 support\n(('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 task\n
for\n
YARD i18n\n
(('note:YARDの国際化機能向けのRakeタスク'))
= Setting\n(('note:設定'))
# coderay ruby
# Rakefile
require "packnga"
Packnga::DocumentTask.new(spec) do |task|
task.original_language = "en"
task.translate_languages = ["ja"]
end
= Workflow\n(('note:ワークフロー'))
# coderay console
% rake reference:translate
% editor doc/po/ja/x.edit.po
% rake reference:translate
% editor lib/x.rb
% rake reference:translate
...
= Users\n(('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
= Features\n(('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'))
(())
= groonga-client
Full text search\n
((*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 Rails\n
integration\n
for\n
groonga-client\n
(('note:Ruby on Railsで使う'))
= Architecture\n(('note:アーキテクチャー'))
* Data: RDBMS\n
(('note:データはRDBMSに格納'))
* Full text search: Groonga\n
(('note:全文検索はGroongaで処理'))
= Define app searcher\n(('note:アプリ用サーチャーを定義'))
# coderay ruby
# app/searchers/application_searcher.rb
class ApplicationSearcher <
Groonga::Client::Searcher
end
= Define searcher\n(('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 model\n(('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 also\n(('note:参考情報'))
* Tutorial in Japanese\n
(('note:日本語のチュートリアル'))
* (('tag:xx-small'))
(())
= Ranguba (WIP)(('note:(開発中)'))
Full text search system\n
(('note:全文検索システム'))
= Use cases\n(('note:利用例'))
* File server search\n
(('note:ファイルサーバー検索'))
* E-mail search\n
(('note:メール検索'))
* Web site search\n
(('note:Webサイト検索'))
= Features\n(('note:機能'))
* Crawlers\n
(('note:クローラー'))
* Web UI
* Command line interface
* Update documents\n
(('note:更新'))
* Search documents\n
(('note:検索'))
= ChupaText
Text extractor\n
(('note:テキスト抽出'))
= Supported formats\n(('note:対応フォーマット'))
* PDF
* Office documents(('note:(オフィス文書)'))
* OpenDocument, Word, Excel, ...
* E-mail(('note:(メール)'))
* ...
= Interface\n(('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 use\n(('note:使い方'))
# coderay console
% curl \
--form data=@XXX.pdf \
http://localhost:20080/extraction.json
= Use cases\n(('note:利用例'))
* Ranguba
* Full text search system\n
(('note:全文検索システム'))
* Commit e-mail\n
(('note:コミットメール'))
= git-commit-mailer
Commit e-mail for Git\n
(('note:Git用のコミットメール'))
= Features\n(('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>))
= Users\n(('note:利用者'))
* tDiary
* My products
= commit-email.info
Commit e-mail\n
as a\n
Service\n
(('note:コミットメールのクラウドサービス'))
= How to use\n(('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 (())'))
= Opening5\n(('note:きっかけ5'))
Data processing\n
(('note:データ処理'))
= csv
CSV parser\n
(('note:CSVパーサー'))
= csv - History\n(('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 problems\n(('note:CSVフォーマットの問題'))
* Slow to parse\n
(('note:パースが遅い'))
* Too wild\n
(('note:なんでもあり'))
= Red Arrow
Apache Arrow Ruby
= Red Arrow - History\n(('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 - Position\n(('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 API\n(('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 processable\n
(('note:Rubyでデータ処理できるようにするためのプロジェクト'))
= Red Data Tools - History\n(('note:歴史'))
* 2017-02: Start(('note:(開始)'))
* 2017-11-: Develop events per month at Tokyo\n
(('note:東京で毎月開発イベントを開催'))
= The number of products\n(('note:プロダクト数'))
About 20\n
(('note:including Red Arrow and Red Parquet'))\n
(('note:20くらい'))\n
(('note:Red ArrowやRed ParquetもRed Data Toolsプロダクツ'))
= Red Datasets
Dataset fetcher\n
(('note:データセット取得'))
= Supported datasets\n(('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
Jekyll\n
(('+'))\n
Jupyter Notebook
= Usage\n(('note:使い方'))
{% jupyter_notebook sample.ipynb %}
= Red OpenCV
Computer vision\n
(('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 Ruby\n(('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:東京での毎月の開発イベントに参加'))
* (())
= 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 up\n(('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:一緒にやりたい人はいる?'))