Ruby on Rails ใน WebAssembly เส้นทางแบบ Full-Stack ในเบราว์เซอร์

Vladimir Dementyev
Vladimir Dementyev

เผยแพร่เมื่อวันที่ 31 มกราคม 2025

ลองจินตนาการว่าคุณใช้งานบล็อกที่ทำงานได้อย่างสมบูรณ์ในเบราว์เซอร์ ไม่ใช่แค่ส่วนหน้าเท่านั้น แต่ยังรวมถึงส่วนหลังด้วย โดยไม่ต้องใช้เซิร์ฟเวอร์หรือระบบคลาวด์ มีเพียงคุณ เบราว์เซอร์ และ WebAssembly WebAssembly กำลังทำให้ขอบเขตของการพัฒนาเว็บแบบคลาสสิกเบลอลงและเปิดโอกาสใหม่ๆ ที่น่าตื่นเต้นด้วยการอนุญาตให้เฟรมเวิร์กฝั่งเซิร์ฟเวอร์ทำงานในเครื่อง ในโพสต์นี้ Vladimir Dementyev (หัวหน้าฝ่ายแบ็กเอนด์ของ Evil Martians) จะแชร์ความคืบหน้าในการทำให้ Ruby on IDE พร้อมใช้งานใน Wasm และเบราว์เซอร์

  • วิธีนํา Rails มาใช้ในเบราว์เซอร์ภายใน 15 นาที
  • เบื้องหลังการแปลง Rails เป็น WASM
  • อนาคตของ Rails และ Wasm

"บล็อกใน 15 นาที" อันโด่งดังของ Ruby on Rails ทำงานได้ในเบราว์เซอร์ของคุณแล้ว

Ruby on Rails เป็นเฟรมเวิร์กเว็บที่มุ่งเน้นประสิทธิภาพของนักพัฒนาซอฟต์แวร์และการนำส่งอย่างรวดเร็ว ซึ่งเป็นเทคโนโลยีที่ผู้นำอุตสาหกรรม เช่น GitHub และ Shopify นำมาใช้ เฟรมเวิร์กนี้ได้รับความนิยมมาตั้งแต่หลายปีก่อนจากการเผยแพร่วิดีโอ"วิธีสร้างบล็อกใน 15 นาที" ที่โด่งดังซึ่งเผยแพร่โดย David Heinemeier Hansson (หรือ DHH) เมื่อย้อนกลับไปในปี 2005 การสร้างเว็บแอปพลิเคชันที่ใช้งานได้อย่างสมบูรณ์ในเวลาอันสั้นนั้นแทบจะเป็นไปไม่ได้ รู้สึกเหมือนเป็นปาฏิหาริย์

วันนี้ เราอยากนำความรู้สึกมหัศจรรย์นี้กลับมาอีกครั้งด้วยการสร้างแอปพลิเคชัน Rails ที่ทำงานได้เต็มรูปแบบในเบราว์เซอร์ เส้นทางของคุณเริ่มต้นด้วยการสร้างแอปพลิเคชัน Rails พื้นฐานตามปกติ จากนั้นจึงแพ็กเกจแอปพลิเคชันสำหรับ Wasm

พื้นหลัง: "บล็อกใน 15 นาที" ในบรรทัดคำสั่ง

สมมติว่าคุณได้ติดตั้ง Ruby และ Ruby on Rails ในเครื่องแล้ว ให้เริ่มด้วยการสร้างแอปพลิเคชัน Ruby on Rails ใหม่และสร้างโครงสร้างพื้นฐานของฟังก์ชันการทำงานบางอย่าง (เช่นเดียวกับในวิดีโอ "สร้างบล็อกใน 15 นาที" ต้นฉบับ)


$ rails new --css=tailwind web_dev_blog

  create  .ruby-version
  ...

$ cd web_dev_blog

$ bin/rails generate scaffold Post title:string date:date body:text

  create    db/migrate/20241217183624_create_posts.rb
  create    app/models/post.rb
  ...

$ bin/rails db:migrate

== 20241217183624 CreatePosts: migrating ====================
-- create_table(:posts)
   -> 0.0017s
== 20241217183624 CreatePosts: migrated (0.0018s) ===========

ตอนนี้คุณเรียกใช้แอปพลิเคชันและดูการทํางานได้โดยไม่ต้องแตะโค้ดฐานเลย

$ bin/dev

=> Booting Puma
=> Rails 8.0.1 application starting in development
...
* Listening on http://127.0.0.1:3000

ตอนนี้คุณเปิดบล็อกที่ http://localhost:3000/posts และเริ่มเขียนโพสต์ได้แล้ว

บล็อก Ruby on Rails ที่เปิดจากบรรทัดคำสั่งที่ทำงานในเบราว์เซอร์

คุณมีแอปพลิเคชันบล็อกที่เรียบง่ายแต่ใช้งานได้ซึ่งสร้างขึ้นภายในไม่กี่นาที Ruby on Rails เป็นแอปพลิเคชัน Full Stack ที่ควบคุมโดยเซิร์ฟเวอร์ คุณมีฐานข้อมูล (SQLite) เพื่อเก็บข้อมูล, เว็บเซิร์ฟเวอร์เพื่อจัดการคําขอ HTTP (Puma) และโปรแกรม Ruby เพื่อเก็บตรรกะทางธุรกิจ, แสดง UI และประมวลผลการโต้ตอบของผู้ใช้ สุดท้ายคือ JavaScript บางเลเยอร์ (Turbo) เพื่อปรับปรุงประสบการณ์การท่องเว็บ

การสาธิตอย่างเป็นทางการของ Rails จะยังคงมุ่งเน้นที่การนำแอปพลิเคชันนี้ไปใช้งานบนเซิร์ฟเวอร์แบบ Bare Metal เพื่อให้พร้อมใช้งานจริง เส้นทางของคุณจะดำเนินไปในทิศทางตรงกันข้าม แทนที่จะนำแอปพลิเคชันไปใช้ที่อื่น คุณจะต้อง "ติดตั้งใช้งาน" แอปพลิเคชันในพื้นที่

ขั้นถัดไป: "สร้างบล็อกใน 15 นาที" ใน Wasm

นับตั้งแต่มีการเพิ่ม WebAssembly เข้ามา เบราว์เซอร์ไม่เพียงแต่จะเรียกใช้โค้ด JavaScript ได้เท่านั้น แต่ยังเรียกใช้โค้ดใดก็ได้ที่คอมไพล์เป็น Wasm ได้ และ Ruby ก็ไม่ใช่ข้อยกเว้น แน่นอนว่า Rails ไม่ได้มีแค่ Ruby แต่ก่อนที่จะเจาะลึกความแตกต่าง เรามาลองสาธิตและแปลงเป็น Wasm (กริยาที่มาจากคลัง wasmify-rails) แอปพลิเคชัน Rails ต่อเลย

คุณเพียงต้องเรียกใช้คำสั่ง 2-3 คำสั่งเพื่อคอมไพล์แอปพลิเคชันบล็อกเป็นโมดูล Wasm และเรียกใช้ในเบราว์เซอร์

ก่อนอื่น ให้ติดตั้งไลบรารี wasmify-rails โดยใช้ Bundler (npm ของ Ruby) และเรียกใช้เครื่องมือสร้างโดยใช้ CLI ของ Rails โดยทำดังนี้

$ bundle add wasmify-rails

$ bin/rails wasmify:install

  create  config/wasmify.yml
  create  config/environments/wasm.rb
  ...
  info   The application is prepared for Wasm-ificaiton!

คําสั่ง wasmify:rails จะกําหนดค่าสภาพแวดล้อมการเรียกใช้ "wasm" โดยเฉพาะ (นอกเหนือจากสภาพแวดล้อม "development", "test" และ "production" เริ่มต้น) และติดตั้งข้อกําหนดที่จําเป็น สําหรับแอปพลิเคชัน Rails ใหม่ทั้งหมด การดำเนินการนี้เพียงพอที่จะทำให้แอปพลิเคชันพร้อมใช้งาน Wasm

จากนั้นสร้างโมดูล Wasm หลักที่มีรันไทม์ Ruby, ไลบรารีมาตรฐาน และสิ่งที่ต้องพึ่งพาทั้งหมดของแอปพลิเคชัน ดังนี้

$ bin/rails wasmify:build

==> RubyWasm::BuildSource(3.3) -- Building
...
==> RubyWasm::CrossRubyProduct(ruby-3.3-wasm32-unknown-wasip1-full-4aaed4fbda7afe0bdf4e22167afd101e) -- done in 47.37s
INFO: Packaging gem: rake-13.2.1
...
INFO: Packaging gem: wasmify-rails-0.2.0
INFO: Packaging setup.rb: bundle/setup.rb
INFO: Size: 73.77 MB

ขั้นตอนนี้อาจใช้เวลาสักครู่ เนื่องจากคุณต้องสร้าง Ruby จากซอร์สโค้ดเพื่อลิงก์ส่วนขยายแบบเนทีฟ (เขียนด้วย C) จากไลบรารีของบุคคลที่สามอย่างถูกต้อง ข้อเสีย (ชั่วคราว) นี้จะมีการอธิบายในภายหลังในโพสต์

โมดูล Wasm ที่คอมไพล์แล้วเป็นเพียงรากฐานสําหรับแอปพลิเคชัน นอกจากนี้ คุณยังต้องแพ็กโค้ดแอปพลิเคชันเองและชิ้นงานทั้งหมด (เช่น รูปภาพ, CSS, JavaScript) ด้วย ก่อนทำการแพ็ก ให้สร้างแอปพลิเคชันตัวเปิดใช้งานพื้นฐานที่สามารถใช้เพื่อเรียกใช้ Rails ที่แปลงเป็น Wasm ในเบราว์เซอร์ ในกรณีนี้ ยังมีคำสั่ง Generator ด้วย

$ bin/rails wasmify:pwa

  create  pwa
  create  pwa/boot.html
  create  pwa/boot.js
  ...
  prepend  config/wasmify.yml

คำสั่งก่อนหน้าจะสร้างแอปพลิเคชัน PWA ขั้นต่ำที่สร้างขึ้นด้วย Vite ซึ่งสามารถใช้งานในเครื่องเพื่อทดสอบโมดูล Rails Wasm ที่คอมไพล์แล้ว หรือจะนำไปใช้งานแบบคงที่เพื่อเผยแพร่แอปก็ได้

เมื่อใช้ Launcher แล้ว สิ่งที่คุณต้องทำมีเพียงแพ็กเกจแอปพลิเคชันทั้งหมดเป็นไฟล์ Wasm ไฟล์เดียว ดังนี้

$ bin/rails wasmify:pack
...
Packed the application to pwa/app.wasm
Size: 76.2 MB

เท่านี้ก็เรียบร้อย เรียกใช้แอป Launcher และดูแอปพลิเคชันการเขียนบล็อก Rails ที่ทำงานอย่างเต็มรูปแบบภายในเบราว์เซอร์

$ cd pwa/

$ yarn dev

  VITE v4.5.5  ready in 290 ms

    Local:   http://localhost:5173/

ไปที่ http://localhost:5173 รอสักครู่เพื่อให้ปุ่ม "เปิดใช้งาน" ทำงานได้ แล้วคลิกปุ่มดังกล่าว จากนั้นเพลิดเพลินกับการใช้งานแอป Rails ที่ทำงานในเครื่องในเบราว์เซอร์

บล็อก Ruby on Rails ที่เปิดจากแท็บเบราว์เซอร์ที่ทำงานในแท็บเบราว์เซอร์อื่น

ดูเหมือนจะเป็นเวทมนตร์เลยใช่ไหมที่การเรียกใช้แอปพลิเคชันฝั่งเซิร์ฟเวอร์แบบโมโนลิติกไม่เพียงทำงานในเครื่องของคุณเท่านั้น แต่ยังทำงานภายในแซนด์บ็อกซ์ของเบราว์เซอร์ด้วย สำหรับฉัน (แม้ว่าจะเป็น "ผู้วิเศษ") ก็ยังดูเหมือนเรื่องแต่งอยู่ แต่ก็ไม่ได้มีอะไรวิเศษไปกว่าความก้าวหน้าของเทคโนโลยี

สาธิต

คุณสามารถดูการสาธิตที่ฝังอยู่ในบทความหรือเปิดการสาธิตในหน้าต่างแบบสแตนด์อโลน ดูซอร์สโค้ดใน GitHub

เบื้องหลังของ Rails ใน Wasm

หากต้องการทําความเข้าใจความท้าทาย (และวิธีแก้ปัญหา) ในการแพ็กเกจแอปพลิเคชันฝั่งเซิร์ฟเวอร์เป็นโมดูล Wasm มากขึ้น เนื้อหาที่เหลือของโพสต์นี้จะอธิบายคอมโพเนนต์ที่เป็นส่วนหนึ่งของสถาปัตยกรรมนี้

เว็บแอปพลิเคชันนั้นอาศัยปัจจัยอื่นๆ อีกมากมายนอกเหนือจากภาษาโปรแกรมที่ใช้เขียนโค้ดแอปพลิเคชัน นอกจากนี้ คุณยังต้องนําคอมโพเนนต์แต่ละรายการไปไว้ใน_สภาพแวดล้อมการนําไปใช้ในเครื่อง_ ซึ่งก็คือเบราว์เซอร์ สิ่งที่น่าตื่นเต้นเกี่ยวกับการแสดงตัวอย่าง "บล็อกใน 15 นาที" คือคุณทําสิ่งเหล่านี้ได้โดยไม่ต้องเขียนโค้ดแอปพลิเคชันใหม่ มีการใช้โค้ดเดียวกันเพื่อเรียกใช้แอปพลิเคชันในโหมดฝั่งเซิร์ฟเวอร์แบบคลาสสิกและในเบราว์เซอร์

คอมโพเนนต์ที่ประกอบขึ้นเป็นแอป Ruby on Rails ได้แก่ เว็บเซิร์ฟเวอร์ ฐานข้อมูล คิว และพื้นที่เก็บข้อมูล รวมถึงคอมโพเนนต์หลักของ Ruby ได้แก่ Gem, ส่วนขยายแบบเนทีฟ, เครื่องมือระบบ และ Ruby VM

เฟรมเวิร์กอย่าง Ruby on Rails จะให้อินเทอร์เฟซซึ่งเป็นการแยกความคิดเพื่อสื่อสารกับคอมโพเนนต์โครงสร้างพื้นฐาน ส่วนต่อไปนี้จะกล่าวถึงวิธีใช้สถาปัตยกรรมเฟรมเวิร์กเพื่อตอบสนองความต้องการในการแสดงผลในพื้นที่ที่ค่อนข้างลึกลับ

รากฐาน: ruby.wasm

Ruby พร้อมใช้งาน Wasm ในปี 2022อย่างเป็นทางการแล้ว (ตั้งแต่เวอร์ชัน 3.2.0) ซึ่งหมายความว่าสามารถคอมไพล์ซอร์สโค้ด C เป็น Wasm และนำ VM ของ Ruby ไปใช้ได้ทุกที่ที่ต้องการ โปรเจ็กต์ ruby.wasm จะจัดส่งโมดูลที่คอมไพล์ไว้ล่วงหน้าและไบน์ด์ JavaScript เพื่อเรียกใช้ Ruby ในเบราว์เซอร์ (หรือรันไทม์ JavaScript อื่นๆ) โปรเจ็กต์ ruby:wasm ยังมีเครื่องมือสร้างที่ช่วยให้คุณสร้าง Ruby เวอร์ชันที่กำหนดเองซึ่งมีไลบรารีเพิ่มเติม ซึ่งสำคัญมากสำหรับโปรเจ็กต์ที่ใช้ไลบรารีที่มีส่วนขยาย C ใช่ คุณยังคอมไพล์ส่วนขยายแบบเนทีฟเป็น Wasm ได้ด้วย (จริงๆ แล้วยังไม่มีส่วนขยายใดเลย แต่ส่วนใหญ่แล้ว)

ปัจจุบัน Ruby รองรับอินเทอร์เฟซระบบ WebAssembly อย่างเต็มรูปแบบ ซึ่งก็คือ WASI 0.1 WASI 0.2 ซึ่งมีโมเดลคอมโพเนนต์อยู่ในสถานะอัลฟ่าแล้วและอีกไม่กี่ขั้นตอนก็จะเสร็จสมบูรณ์ เมื่อรองรับ WASI 0.2 แล้ว ก็จะไม่จำเป็นต้องคอมไพล์ภาษาทั้งหมดอีกครั้งทุกครั้งที่ต้องการเพิ่มการพึ่งพาแบบเนทีฟใหม่ เนื่องจากสามารถแยกเป็นคอมโพเนนต์ได้

ผลข้างเคียงที่ตามมาคือโมเดลคอมโพเนนต์ยังช่วยในการลดขนาดแพ็กเกจด้วย ดูข้อมูลเพิ่มเติมเกี่ยวกับการพัฒนาและขั้นตอนของ ruby.wasm ได้จากทอล์กเรื่องสิ่งที่คุณทําได้กับ Ruby ใน WebAssembly

ปัญหาด้าน Ruby ของสมการ Wasm จึงได้รับการแก้ไขแล้ว แต่ Rails เป็นเฟรมเวิร์กเว็บที่ต้องใช้คอมโพเนนต์ทั้งหมดที่แสดงในแผนภาพก่อนหน้า อ่านต่อเพื่อดูวิธีใส่คอมโพเนนต์อื่นๆ ลงในเบราว์เซอร์และลิงก์เข้าด้วยกันใน Rails

เชื่อมต่อกับฐานข้อมูลที่ทํางานในเบราว์เซอร์

SQLite3 มาพร้อมกับการจำหน่าย Wasm อย่างเป็นทางการและตัวแฝง JavaScript ที่เกี่ยวข้อง จึงพร้อมที่จะฝังในเบราว์เซอร์ PostgreSQL สำหรับ Wasm พร้อมใช้งานผ่านโปรเจ็กต์ PGlite คุณจึงต้องหาวิธีเชื่อมต่อกับฐานข้อมูลในเบราว์เซอร์จากแอปพลิเคชัน Rails on Wasm เท่านั้น

คอมโพเนนต์หรือเฟรมเวิร์กย่อยของ Rails ที่รับผิดชอบการสร้างโมเดลข้อมูลและการโต้ตอบกับฐานข้อมูลเรียกว่า Active Record (ใช่ ชื่อนี้มาจากรูปแบบการออกแบบ ORM) Active Record จะแยกการติดตั้งใช้งานฐานข้อมูลที่พูดภาษา SQL จริงออกจากโค้ดแอปพลิเคชันผ่านอะแดปเตอร์ฐานข้อมูล Rails มีอะแดปเตอร์ SQLite3, PostgreSQL และ MySQL ให้ใช้งานทันที อย่างไรก็ตาม คำสั่งทั้งหมดจะถือว่าเชื่อมต่อกับฐานข้อมูลจริงที่พร้อมใช้งานผ่านเครือข่าย คุณสามารถเขียนอะแดปเตอร์ของคุณเองเพื่อเชื่อมต่อกับฐานข้อมูลในเบราว์เซอร์แบบในเครื่องเพื่อแก้ปัญหานี้ได้

วิธีสร้างอะแดปเตอร์ SQLite3 Wasm และ PGlite ที่นำมาใช้ในโปรเจ็กต์ Wasmify Rails มีดังนี้

  • คลาสอะแดปเตอร์จะรับค่ามาจากอะแดปเตอร์ในตัวที่เกี่ยวข้อง (เช่น class PGliteAdapter < PostgreSQLAdapter) เพื่อให้คุณนําการเตรียมคําค้นหาจริงและตรรกะการแยกวิเคราะห์ผลลัพธ์มาใช้ซ้ำได้
  • คุณจะใช้ออบเจ็กต์อินเทอร์เฟซภายนอกที่อยู่ในรันไทม์ JavaScript แทนการเชื่อมต่อฐานข้อมูลระดับต่ำ ซึ่งเป็นบริดจ์ระหว่างโมดูล Rails Wasm กับฐานข้อมูล

ตัวอย่างเช่น การใช้งานบริดจ์สําหรับ SQLite3 Wasm มีดังนี้

export function registerSQLiteWasmInterface(worker, db, opts = {}) {
  const name = opts.name || "sqliteForRails";

  worker[name] = {
    exec: function (sql) {
      let cols = [];
      let rows = db.exec(sql, { columnNames: cols, returnValue: "resultRows" });

      return {
        cols,
        rows,
      };
    },

    changes: function () {
      return db.changes();
    },
  };
}

จากมุมมองของแอปพลิเคชัน การเปลี่ยนจากฐานข้อมูลจริงเป็นฐานข้อมูลในเบราว์เซอร์เป็นเพียงเรื่องของการกำหนดค่าเท่านั้น

# config/database.yml
development:
  adapter: sqlite3

production:
  adapter: sqlite3

wasm:
  adapter: sqlite3_wasm
  js_interface: "sqliteForRails"

การทำงานกับฐานข้อมูลในเครื่องนั้นไม่ยุ่งยาก อย่างไรก็ตาม หากจำเป็นต้องซิงค์ข้อมูลกับแหล่งข้อมูลที่เชื่อถือได้ส่วนกลาง คุณอาจต้องเผชิญกับปัญหาในระดับที่สูงขึ้น คำถามนี้อยู่นอกขอบเขตของโพสต์นี้ (เคล็ดลับ: ดูการสาธิต Rails ใน PGlite และ ElectricSQL)

Service Worker เป็นเว็บเซิร์ฟเวอร์

องค์ประกอบสําคัญอีกอย่างหนึ่งของเว็บแอปพลิเคชันคือเว็บเซิร์ฟเวอร์ ผู้ใช้โต้ตอบกับเว็บแอปพลิเคชันโดยใช้คำขอ HTTP คุณจึงต้องมีวิธีกำหนดเส้นทางคำขอ HTTP ที่เรียกให้แสดงโดยการนำทางหรือการส่งแบบฟอร์มไปยังโมดูล Wasm โชคดีที่เบราว์เซอร์มีคำตอบสำหรับปัญหานี้ นั่นก็คือ Service Worker

Service Worker คือ Web Worker ประเภทพิเศษที่ทำหน้าที่เป็นพร็อกซีระหว่างแอปพลิเคชัน JavaScript กับเครือข่าย โดยสามารถดักรับคำขอและดัดแปลงคำขอ เช่น แสดงข้อมูลที่แคชไว้ เปลี่ยนเส้นทางไปยัง URL อื่น หรือ… ไปยังโมดูล Wasm ภาพร่างของบริการที่ทำงานเพื่อให้บริการคำขอโดยใช้แอปพลิเคชัน Rails ที่ทำงานใน Wasm มีดังนี้

// The vm variable holds a reference to the Wasm module with a
// Ruby VM initialized
let vm;
// The db variable holds a reference to the in-browser
// database interface
let db;

const initVM = async (progress, opts = {}) => {
  if (vm) return vm;
  if (!db) {
    await initDB(progress);
  }
  vm = await initRailsVM("/app.wasm");
  return vm;
};

const rackHandler = new RackHandler(initVM});

self.addEventListener("fetch", (event) => {
  // ...
  return event.respondWith(
    rackHandler.handle(event.request)
  );
});

"การดึงข้อมูล" จะทริกเกอร์ทุกครั้งที่เบราว์เซอร์ส่งคําขอ คุณสามารถรับข้อมูลคําขอ (URL, ส่วนหัว HTTP, เนื้อหา) และสร้างออบเจ็กต์คําขอของคุณเอง

Rails เช่นเดียวกับเว็บแอปพลิเคชัน Ruby ส่วนใหญ่ใช้อินเทอร์เฟซ Rack เพื่อจัดการกับคำขอ HTTP อินเทอร์เฟซ Rack จะอธิบายรูปแบบของออบเจ็กต์คำขอและการตอบกลับ รวมถึงอินเทอร์เฟซของตัวแฮนเดิล HTTP ที่อยู่เบื้องหลัง (แอปพลิเคชัน) คุณแสดงพร็อพเพอร์ตี้เหล่านี้ได้ดังนี้

request = {
   "REQUEST_METHOD" => "GET",
   "SCRIPT_NAME"    => "",
   "SERVER_NAME"  => "localhost",
   "SERVER_PORT" => "3000",
   "PATH_INFO"      => "/posts"
}

handler = proc do |env|
  [
    200,
    {"Content-Type" => "text/html"},
    ["<!doctype html><html><body>Hello Web!</body></html>"]
  ]
end

handler.call(request) #=> [200, {...}, [...]]

หากพบว่ารูปแบบคำขอคุ้นเคย คุณอาจเคยทำงานกับ CGI มาก่อน

ออบเจ็กต์ JavaScript RackHandler มีหน้าที่แปลงคำขอและคำตอบระหว่างอาณาจักร JavaScript กับ Ruby เนื่องจากเว็บแอปพลิเคชัน Ruby ส่วนใหญ่ใช้ Rack การติดตั้งใช้งานจึงกลายเป็นแบบสากล ไม่ใช่เฉพาะสำหรับ Rails แต่การใช้งานจริงนั้นยาวเกินกว่าจะโพสต์ที่นี่

เวิร์กเกอร์บริการเป็นหนึ่งในองค์ประกอบหลักของเว็บแอปพลิเคชันในเบราว์เซอร์ ไม่เพียงแต่เป็นพร็อกซี HTTP เท่านั้น แต่ยังเป็นเลเยอร์แคชและตัวสลับเครือข่ายด้วย (กล่าวคือ คุณสามารถสร้างแอปพลิเคชันที่เน้นใช้อุปกรณ์เครื่องแรกหรือสามารถใช้งานแบบออฟไลน์ได้) นอกจากนี้ ยังเป็นคอมโพเนนต์ที่ช่วยให้คุณแสดงไฟล์ที่ผู้ใช้อัปโหลดได้อีกด้วย

อัปโหลดไฟล์ในเบราว์เซอร์ต่อไป

ฟีเจอร์เพิ่มเติมอย่างแรกที่ควรนำมาใช้ในแอปพลิเคชันบล็อกใหม่ของคุณคือการรองรับการอัปโหลดไฟล์ หรืออย่างเจาะจงคือ การแนบรูปภาพไปกับโพสต์ คุณต้องมีวิธีจัดเก็บและแสดงไฟล์จึงจะดำเนินการนี้ได้

ใน Rails ส่วนเฟรมเวิร์กที่มีส่วนรับผิดชอบในการจัดการการอัปโหลดไฟล์เรียกว่า Active Storage Active Storage ช่วยให้นักพัฒนาแอปสามารถทำงานกับไฟล์ได้โดยไม่ต้องคำนึงถึงกลไกพื้นที่เก็บข้อมูลระดับล่าง ไม่ว่าคุณจะจัดเก็บไฟล์ไว้ที่ใด ไม่ว่าจะในฮาร์ดไดรฟ์หรือในระบบคลาวด์ โค้ดแอปพลิเคชันก็จะไม่รับรู้ถึงไฟล์เหล่านั้น

ในทำนองเดียวกับ Active Record หากต้องการรองรับกลไกพื้นที่เก็บข้อมูลที่กำหนดเอง คุณก็แค่ต้องใช้อะแดปเตอร์บริการพื้นที่เก็บข้อมูลที่เกี่ยวข้อง ตำแหน่งที่จะจัดเก็บไฟล์ในเบราว์เซอร์

ตัวเลือกแบบดั้งเดิมคือการใช้ฐานข้อมูล ได้ คุณสามารถจัดเก็บไฟล์เป็น Blob ในฐานข้อมูลได้โดยไม่ต้องใช้คอมโพเนนต์โครงสร้างพื้นฐานเพิ่มเติม และเรามีปลั๊กอินสำเร็จรูปสำหรับการดำเนินการดังกล่าวใน Rails แล้ว นั่นคือ Active Storage Database อย่างไรก็ตาม การแสดงไฟล์ที่จัดเก็บไว้ในฐานข้อมูลผ่านแอปพลิเคชัน Rails ที่ทำงานภายใน WebAssembly นั้นไม่เหมาะอย่างยิ่ง เนื่องจากมีรอบการ(แปลงเป็น)อนุกรมที่เสียค่าใช้จ่าย

โซลูชันที่ดีกว่าและเพิ่มประสิทธิภาพให้เบราว์เซอร์มากขึ้นคือการใช้ File System API และประมวลผลการอัปโหลดไฟล์และไฟล์ที่เซิร์ฟเวอร์อัปโหลดโดยตรงจาก Service Worker โครงสร้างพื้นฐานที่เหมาะสําหรับการใช้งานดังกล่าวคือ OPFS (ระบบไฟล์ส่วนตัวต้นทาง) ซึ่งเป็น API เบราว์เซอร์ล่าสุดที่จะเข้ามามีบทบาทสําคัญสําหรับแอปพลิเคชันในเบราว์เซอร์ในอนาคต

สิ่งที่ Rails และ Wasm ทำได้ร่วมกัน

เราค่อนข้างมั่นใจว่าคุณคงเคยถามตัวเองด้วยคำถามนี้เมื่อเริ่มอ่านบทความนี้ว่า "ทำไมต้องเรียกใช้เฟรมเวิร์กฝั่งเซิร์ฟเวอร์ในเบราว์เซอร์" แนวคิดที่ว่าเฟรมเวิร์กหรือไลบรารีเป็นฝั่งเซิร์ฟเวอร์ (หรือฝั่งไคลเอ็นต์) เป็นเพียงป้ายกำกับ โค้ดที่ดีและโดยเฉพาะอย่างยิ่งการแยกความคิดที่ดีจะใช้งานได้ทุกที่ ป้ายกำกับไม่ควรเป็นอุปสรรคในการสำรวจความเป็นไปได้ใหม่ๆ และขยายขอบเขตของเฟรมเวิร์ก (เช่น Ruby on Rails) รวมถึงขอบเขตของรันไทม์ (WebAssembly) ทั้ง 2 ประเภทอาจได้รับประโยชน์จาก Use Case ที่แหวกแนวเช่นนี้

นอกจากนี้ยังมีกรณีการใช้งานแบบดั้งเดิมหรือที่ใช้งานได้จริงอีกมากมาย

ประการแรก การนำเฟรมเวิร์กมาใช้กับเบราว์เซอร์จะเปิดโอกาสให้เรียนรู้และสร้างต้นแบบได้มากมาย ลองจินตนาการว่าคุณสามารถเล่นกับไลบรารี ปลั๊กอิน และรูปแบบต่างๆ ได้โดยตรงในเบราว์เซอร์ร่วมกับคนอื่นๆ Stackblitz ทําให้การดำเนินการนี้เป็นไปได้สำหรับเฟรมเวิร์ก JavaScript อีกตัวอย่างหนึ่งคือ WordPress IDE ที่ช่วยให้คุณเล่นธีม WordPress ได้โดยไม่ต้องออกจากหน้าเว็บ Wasm อาจทําให้ Ruby และระบบนิเวศของ Ruby ทำงานได้คล้ายกับ JavaScript

กรณีพิเศษของการเขียนโค้ดในเบราว์เซอร์ที่เป็นประโยชน์อย่างยิ่งสำหรับนักพัฒนาซอฟต์แวร์โอเพนซอร์สคือการจัดลําดับความสําคัญและการแก้ไขข้อบกพร่อง อีกครั้ง StackBlitz ได้สร้างสิ่งเหล่านี้สำหรับโปรเจ็กต์ JavaScript โดยคุณสร้างสคริปต์การจำลองขั้นต่ำ ชี้ไปที่ลิงก์ใน GitHub Issue และช่วยผู้ดูแลให้ไม่ต้องเสียเวลาจำลองสถานการณ์ของคุณ และจริงๆ แล้ว แนวคิดนี้เริ่มเกิดขึ้นแล้วใน Ruby ผ่านโปรเจ็กต์ RunRuby.dev (นี่คือตัวอย่างปัญหาที่แก้ไขด้วยการจำลองในเบราว์เซอร์)

กรณีการใช้งานอีกอย่างหนึ่งคือแอปพลิเคชันที่ทำงานแบบออฟไลน์ได้ (หรือที่รองรับการทำงานแบบออฟไลน์) แอปพลิเคชันที่ทำงานแบบออฟไลน์ได้ ซึ่งปกติจะทำงานโดยใช้เครือข่าย แต่ยังคงใช้งานได้เมื่อไม่มีการเชื่อมต่อ เช่น โปรแกรมรับส่งอีเมลที่ช่วยให้คุณค้นหากล่องจดหมายขณะออฟไลน์ได้ หรือแอปพลิเคชันคลังเพลงที่มีคุณสมบัติ "จัดเก็บในอุปกรณ์" เพื่อให้คุณฟังเพลงโปรดได้อย่างต่อเนื่องแม้ว่าจะไม่มีการเชื่อมต่อเครือข่าย ตัวอย่างทั้ง 2 รายการนี้ขึ้นอยู่กับข้อมูลที่จัดเก็บไว้ในพื้นที่ ไม่ใช่แค่การใช้แคชเหมือนกับ PWA แบบคลาสสิก

สุดท้าย การสร้างแอปพลิเคชันในพื้นที่ (หรือเดสก์ท็อป) ด้วย Rails ก็เหมาะสมเช่นกัน เนื่องจากประสิทธิภาพการทำงานที่เฟรมเวิร์กมอบให้นั้นไม่ได้ขึ้นอยู่กับรันไทม์ เฟรมเวิร์กแบบเต็มรูปแบบเหมาะอย่างยิ่งสําหรับการสร้างแอปพลิเคชันที่มีข้อมูลส่วนตัวและตรรกะมาก และการใช้ Wasm เป็นรูปแบบการเผยแพร่แบบพกพาก็เป็นตัวเลือกที่ใช้งานได้เช่นกัน

นี่เป็นเพียงจุดเริ่มต้นของเส้นทาง Rails on Wasm ดูข้อมูลเพิ่มเติมเกี่ยวกับความท้าทายและวิธีแก้ปัญหาได้ในอีบุ๊ก Ruby on Rails ใน WebAssembly (ซึ่งแอปพลิเคชันนี้ทำงานแบบออฟไลน์ได้ด้วย)