Slide 1

Slide 1 text

Ruby on Browser NSEG 2024-06-22 とみたまさひろ 1

Slide 2

Slide 2 text

自己紹介 • とみたまさひろ • • https://twitter.com/tmtms https://blog.tmtms.net 2

Slide 3

Slide 3 text

Ruby歴27年 3

Slide 4

Slide 4 text

年を取ると新しいことが覚えられない 4

Slide 5

Slide 5 text

もうRuby以外書きたくない 転職先もRuby使えるところを探した 5

Slide 6

Slide 6 text

OSとかデバイスドライバとか以外だとだいたいRubyで 書けるんだけどブラウザではRubyは動かなかった 6

Slide 7

Slide 7 text

ruby.wasm の登場により ブラウザでも Ruby が動くようになった 7

Slide 8

Slide 8 text

やったー 8

Slide 9

Slide 9 text

最近はプライベートではもうJavaScriptを一切書かずにRubyで書いてる 9

Slide 10

Slide 10 text

文字化けを復元するよ ruby.wasm で最初に作ったページ https://tmtms.net/mojibake/ 10

Slide 11

Slide 11 text

前からこういうページを作りたかったんだけど JavaScript で実装するのはムリだと思ったので諦めてた Ruby は文字コード変換処理を内部に持ってるのでできた ruby.wasm すばらしい 11

Slide 12

Slide 12 text

シャッフル seed を指定して結果を再現できる これも JavaScript 標準の乱数ではできない https://tmtms.net/shuffle.html 12

Slide 13

Slide 13 text

MySQL Parameters めずらしく実用的なやつ もともと Vue.js の練習で作ったやつだったんだけど ruby.wasm で作り直した https://mysql-params.tmtms.net/statement/ 13

Slide 14

Slide 14 text

Rabbit on Firefox Firefox で PDF / Reveal.js / Speaker Deck のスライドを表示していると きにブックマークレットを実行するとウサギが表示される Reveal.js用↑ ブックマークレットはJavaScriptで書かないといけない。残念。 https://tmtms.net/rabbit/ javascript:(()=>{if(typeof rubyVM!='undefined'){rubyVM.eval('start');return}; var d=document;var h=d.getElementsByTagName('head')[0];var s=d.createElement('script'); s.src='https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js'; h.appendChild(s);s=d.createElement('script');s.src='https://tmtms.net/rabbit/rabbit_revealjs.rb'; s.type='text/ruby';h.appendChild(s);var x=setInterval(()=>{if(typeof rubyVM!='undefined') {try{rubyVM.eval('start');clearInterval(x)}catch(e){}}},500)})() 14

Slide 15

Slide 15 text

ruby.wasm の使い方 15

Slide 16

Slide 16 text

にRubyを書く 簡単! <!DOCTYPE html> <html> <script src="https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js"> # ここがRubyスクリプト p Time.now #=> 出力はブラウザのコンソール puts "Hello, world!"

Slide 17

Slide 17 text

別ファイルの場合 こっちの方がデバッグが簡単 17

Slide 18

Slide 18 text

JavaScriptの機能を使う JS.eval で JavaScript を実行できる JS.global 経由で JavaScript のグローバルオブジェクト取得や関数実行 ができる require 'js' JS.eval('alert("hoge")') JS.global.alert('hoge') 18

Slide 19

Slide 19 text

JavaScript の値を Ruby から見るとすべて JS::Object Ruby では扱いにくいので to_i や to_s 等で変換できる n = JS.eval('return 123') #=> JS::Object (123) n.typeof #=> "number" s = JS.eval('return "hoge"') #=> JS::Object ("hoge") s.typeof #=> "string" JS.eval('return 123').to_i #=> 123 JS.eval('return "hoge"').to_s #=> "hoge" 19

Slide 20

Slide 20 text

JS::Object#[] でプロパティ取得&設定 JS::Object#call で JavaScript の関数呼び出し JS::Object#func でも呼び出せる s = JS.eval('return "hoge"') #=> JS::Object ("hoge") s[:length] #=> JS::Object (4) s.call(:charAt, 2) #=> JS::Object ("g") s.charAT(2) #=> JS::Object ("g") 20

Slide 21

Slide 21 text

JavaScript の null や undefined も JS::Object のインスタンス Ruby で真偽値として評価すると当然真になるので注意 JS.eval('return null') #=> JS::Object (JS::Null) JS.eval('return undefined') #=> JS::Object (JS::Undefined) !!JS::Null #=> true !!JS::Undefined #=> true 21

Slide 22

Slide 22 text

Promise / await JavaScript の Proimse は await で待てる data-eval="async" の指定が必要 promise = JS.global.fetch("https://tmtms.net") #=> JS::Object (Promise) resp = promise.await #=> JS::Object (Response) promise = resp.text #=> JS::Object (Promise) promise.await #=> JS::Object ("<!DOCTYPE html>\n<html>\n ....") 22

Slide 23

Slide 23 text

DOM ほぼ JavaScript のまんま document = JS.global[:document] hoge = document.getElementById('hoge') fuga = document.createElement('div') fuga[:id] = 'fuga' hoge.appendChild(fuga) 23

Slide 24

Slide 24 text

HTML 要素にイベントを設定するには addEventListener() を使う HTML の onclick 等は JavaScript を呼んでしまうので(それはそう) require 'js' document = JS.global[:document] document.getElementById('b').addEventListener('click') do |ev| JS.global.alert('hoge') end 24

Slide 25

Slide 25 text

rubyVM.eval() を使う手もある require 'js' def hoge JS.global.alert('hoge') end 25

Slide 26

Slide 26 text

require は動かない はできるけど require で別の rb を読むことはできない <script src> は HTTP, require はファイルシステム 26

Slide 27

Slide 27 text

require 'js' ができるのは js.rb が ruby.wasm の仮想ファイルシステムにあるから 27

Slide 28

Slide 28 text

HTML の中でファイルをひとつずつ で読む または仮想ファイルシステム内に必要なファイルをおいた状態で ruby.wasm を作る そういうめんどくさいことはしたくない <script type="text/ruby" src="main.rb"> 28

Slide 29

Slide 29 text

require_relative が動く! 呼び出し元の rb ファイルの URL からの相対パスで サーバーから rb ファイルを読み込む サーバー上に rb ファイルを置いておくだけでいい めっちゃ便利! module Kernel def require_relative(path) JS::RequireRemote.instance.load(path) end end 29

Slide 30

Slide 30 text

まとめ JavaScript でできることならできる JavaScript でできなかったこともできる JavaScript より遅いけど気にしたら負け Ruby でフロントエンドが書けるの最高! 30