= 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:一緒にやりたい人はいる?'))