Rails

Rails関連のメモ

Administration

  • avo
  • authlogic

Authlogic では Rails::Engine で実装されている。Controller は ActionController::Base を継承しているため、ApplicationController で定義したメソッド helper_method :current_user は共有できない。

以下のように対処。

  config.current_user_method do
    UserSession.find&.user
  end

Rails 7

Rails 6 まで Webpack のラッパーである Webpacker を用いたナレッジが定番だったが、一方で Webpacker では痒いところに手が届かずに様々な Hack が存在していた。Rails 7 からは Webpacker は登場しなくなり、任意のオプションを選択できるようになっている(方針転換の細部は理解していない)。

自由度が増えた反面、どういう時にどのケースを採用すればいいのかが混乱する悩みあり。

用語整理

Introducing Propshaft

Introducing Propshaft

It's an exciting time in web development. After a decade's worth of front-end progress kept demanding ever more complicated setups, we're …

  • Propshaft
    • https://github.com/rails/propshaft
    • Propshaft は、Rails 用のアセット パイプライン ライブラリ。HTTP 接続を節約するためのアセットのバンドルがさほど重要視されなくなった現代、JavaScript や CSS が専用の Node.js バンドラーによってコンパイルされたりブラウザーに直接提供すれば十分(帯域幅の増加等もある)な時代に合わせて作られたもの。Sprocketsなどの以前のオプションと比較して、Asset Pipeline が劇的にシンプルかつ高速になるとのこと。
    • Propshaftの適用範囲はSprocketsよりも狭いので、SprocketsからPropshaftへの移行にはjsbundling-rails gemとcssbundling-rails gemも利用する必要があります。
  • esbuild
    • esbuild は Go 言語で書かれた JavaScript および TypeScript のビルドツール
    • esbuild 単体でトランスパイル + バンドル + ミニファイガァm日
    • JSX / TSX もサポートされている
    • 速い
esbuild rollup webpack
ビルド速度 高速 低速 低速
CommonJSモジュール対応 要プラグイン
ES6対応 要Babel
TypeScriptのトランスパイル 要プラグイン 要プラグイン
JSX構文のトランスパイル 要プラグイン 要Babel
Tree-shaking ×
Minify

CSS

rails new myapp --css [tailwind|bootstrap|bulma|postcss|sass]

https://github.com/rails/cssbundling-rails

You can configure your bundler options in the build:css script in package.json or via the installer-generated tailwind.config.js for Tailwind or postcss.config.js for PostCSS.

Javascript

  • importmap を使う場合は、node_modulesディレクトリは生成されません。それ以外のesbuild / rollup / webpackを使う場合は node_modules ディレクトリが生成されます。
    • Javascript 界隈のトレンドを抑えていなくて(なんとなくWebpackを使ったことのある程度)、これから新規セットアップをする場合は esbuild を選んでおくのが学習コストの面から良さそう。
  • esbuild

esbuild をセットアップしていない既存Railsプロジェクトへ導入したい場合 ./bin/bundle add jsbundling-rails を利用すると Rails タスクに以下のコマンドが追加される。

rails javascript:build                   # Build your JavaScript bundle
rails javascript:clobber                 # Remove JavaScript builds
rails javascript:install:esbuild         # Install esbuild
rails javascript:install:rollup          # Install rollup.js
rails javascript:install:shared          # Install shared elements for all bundlers
rails javascript:install:webpack         # Install Webpack

次に ./bin/rails javascript:install:esbuild を実行すると package.json に build スクリプトが設定されるはずだが、npxのバージョンが関係するとのこと(7.1以上から対応)。

"scripts": {
    "build": "esbuild app/javascript/*.* --bundle --outdir=app/assets/builds"
}

esbuild.config.js

build スクリプトはシンプルに記載して、esbuild の設定を別に切り出すこともできる。

https://www.bootrails.com/blog/custom-esbuild-for-rails/

"scripts": {
  "build": "node esbuild.config.js",
}

esbuild.config.js

const path = require('path');

require("esbuild").build({
  entryPoints: ["application.js"],
  bundle: true,
  outdir: path.join(process.cwd(), "app/assets/builds"),
  absWorkingDir: path.join(process.cwd(), "app/javascript"),
  watch: true,
  // custom plugins will be inserted is this array
  plugins: [],
}).catch(() => process.exit(1));

設定可能な項目はリファレンスを参照

https://esbuild.github.io/api/#build-api

https://esbuild.github.io/api/#build-api

Notice: You are in development mode or could not get the remote server

開発コマンド

You can also use ./bin/dev, which will start both the Rails server and the CSS build watcher (along with a JS build watcher, if you’re also using jsbundling-rails).

開発(Development)モードであっても、 bundle exec rails server コマンドで起動しても JS や CSS がビルドされない。Tailwind を使うようにセットアップしたのにCSSが使えなくて困った。

結論は、 ./bin/dev コマンドを用いて起動する必要がある。

#!/usr/bin/env bash

if ! command -v foreman &> /dev/null
then
  echo "Installing foreman..."
  gem install foreman
fi

foreman start -f Procfile.dev

中身は単純で foreman によりビルドプロセスを同時に起動しているだけなので、 Procfile.dev の中身に記載されているコマンドをそれぞれ起動するのと意味は同じ。

bin/rails server -p 3000
bin/rails tailwindcss:watch

もともと foremanhivemind などのプロセスマネージャーを利用している場合は、Procfileを書き換えて慣れた使い方をすればよい。

Importmap を無効化する

まず Rails 7 の思想を理解する。

https://world.hey.com/dhh/rails-7-will-have-three-great-answers-to-javascript-in-2021-8d68191b

  • 従来の Turbolink を置き換える形で Hotwire が用いられている部分があること
  • Hotwire のセットアップのデフォルトとして importmap があること
  • importmap だけでも十分にアプリケーション役割を全うできるレベルでもあること

そのうえで削除してみる。手探りなので抜け漏れの可能性あり。以下の記載を一通りコメントアウトあるいは削除する。

# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
# gem "importmap-rails"

# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
# gem "turbo-rails"

# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
# gem "stimulus-rails"

app/javascript/application.js

import "@hotwired/turbo-rails"

app/views/layouts/application.html.erb

<%= javascript_importmap_tags %>

以下ファイルは不要

  • app/javascript/controllers/application.js
  • app/javascript/controllers/hello_controller.js
  • app/javascript/controllers/index.js
  • config/importmap.rb

esbuild ビルドエラー例

No loader is configured for “.png” files

 [ERROR] No loader is configured for ".png" files: node_modules/lightbox2/dist/images/close.png

    node_modules/lightbox2/dist/css/lightbox.min.css:1:2240:

npmで lightbox2 モジュールを入れたとき。画像等のファイルに対する Loader の設定をすればよい。

loader: {
      ".png": "file",
      ".jpeg": "file",
      ".jpg": "file",
      ".gif": "file",
      ".svg": "file",
    },

ncaught ReferenceError: global is not defined

利用しているモジュールの実装によるが、 global オブジェクトを用いている場合はブラウザでは動かない。define を用いて globalwindow に置き換えることが可能。

define: {
      global: "window",
    },

昨今は globalThis を利用するのがトレンド。

https://javascript.info/global-object

https://javascript.info/global-object

Notice: You are in development mode or could not get the remote server

The global object provides variables and functions that are available anywhere. By default, those that are built into the language or the environment.

In a browser it is named `window`, for Node.js it is `global`, for other environments it may have another name.

Recently, `globalThis` was added to the language, as a standardized name for a global object, that should be supported across all environments. It’s supported in all major browsers.

[実装例] SimpleLightbox

https://simplelightbox.com/

https://simplelightbox.com/

app/javascript/application.js

// CSS
import "simplelightbox/dist/simple-lightbox.css";

// JS
import SimpleLightbox from "simplelightbox";

let gallery = new SimpleLightbox(".gallery a");
gallery.on("show.simplelightbox", function () {
  // do something…
});

gallery.on("error.simplelightbox", function (e) {
  console.log(e); // some usefull information
});

Rails Engine

Rails関連のメモ