Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7: guard-livereload gemで開発中にライブリロードする

Rails 7の新規アプリで遊んでいると、やはりライブリロード機能(ファイル更新を監視してブラウザを自動リロードする、いわゆるホットリロード)が欲しくなったので、guard-livereload gemでDocker環境にライブリロード機能を導入しました。Rails 7でちょっと便利になった点があります。

guard/guard-livereload - GitHub

環境

  • Docker(docker-compose)環境が前提
    • Docker Desktop for Mac: 4.4.2 (73305)
    • Engine: 20.10.12
    • Compose: v2.2.3
  • Rails 7 + Ruby 3.1
    • Rails 7サーバーを./bin/devで起動できることが前提

インストール方法

  • Gemfileに以下を追加し、bundle installを実行します。
group :development do
# (略)
+  gem "guard-livereload", require: false
+  gem "rack-livereload"
end
  • プロジェクトディレクトリで以下を実行し、設定ファイルを生成します。
guard init livereload

プロジェクトディレクトリの直下に以下のファイルが生成されます。

# Guardfile
# A sample Guardfile
# More info at https://github.com/guard/guard#readme

## Uncomment and set this to only include directories you want to watch
# directories %w(app lib config test spec features) \
#  .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}

## Note: if you are using the `directories` clause above and you are not
## watching the project directory ('.'), then you will want to move
## the Guardfile to a watched dir and symlink it back, e.g.
#
#  $ mkdir config
#  $ mv Guardfile config/
#  $ ln -s config/Guardfile .
#
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"

guard 'livereload' do
  extensions = {
    css: :css,
    scss: :css,
    sass: :css,
    js: :js,
    coffee: :js,
    html: :html,
    png: :png,
    gif: :gif,
    jpg: :jpg,
    jpeg: :jpeg,
    # less: :less, # uncomment if you want LESS stylesheets done in browser
  }

  rails_view_exts = %w(erb haml slim)

  # file types LiveReload may optimize refresh for
  compiled_exts = extensions.values.uniq
  watch(%r{public/.+\.(#{compiled_exts * '|'})})

  extensions.each do |ext, type|
    watch(%r{
          (?:app|vendor)
          (?:/assets/\w+/(?<path>[^.]+) # path+base without extension
           (?<ext>\.#{ext})) # matching extension (must be first encountered)
          (?:\.\w+|$) # other extensions
          }x) do |m|
      path = m[1]
      "/assets/#{path}.#{type}"
    end
  end

  # file needing a full reload of the page anyway
  watch(%r{app/views/.+\.(#{rails_view_exts * '|'})$})
  watch(%r{app/helpers/.+\.rb})
  watch(%r{config/locales/.+\.yml})
end
  • Rack::LiveReloadを有効にします。
# config/environments/development.rb
Rails.application.configure do
  config.after_initialize do
 # (略)
+  config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload
  end
end
  • Procfile.devに以下を追記してguardがDockerコンテナ内で起動されるようにします。./bin/devはこのProcfile.devを呼び出します。
# Procfile.dev
web: bin/rails server -p 3000
css: bin/rails tailwindcss:watch
+livereload: bundle exec guard start -i -g livereload

guardを別途起動する必要がないので、Rails 6以前よりも便利になりました😋。

  • LiveReloadで使うポート35729をDockerの外部に転送します。方法はいろいろありますが、自分はdocker-compose.ymlにポート35729を追加しました。
  # (略)
  rails:
    <<: *backend
    command: bin/dev
    ports:
      - "3000:3000"
+     - "35729:35729"
  • ChromeブラウザにLiveReload拡張をインストールします。

LiveReload – Chrome ウェブストア

  • これで、./bin/devを実行し、http://localhost:3000をブラウザで開けば、ライブリロードが有効になります。
$ ./bin/dev
02:23:21 web.1        | started with pid 8
02:23:21 css.1        | started with pid 9
02:23:21 livereload.1 | started with pid 10
02:23:23 livereload.1 | 02:23:23 - INFO - LiveReload is waiting for a browser to connect.
02:23:23 web.1        | => Booting Puma
02:23:23 web.1        | => Rails 7.0.1 application starting in development
02:23:23 web.1        | => Run `bin/rails server --help` for more startup options
02:23:25 web.1        | Puma starting in single mode...
02:23:25 web.1        | * Puma version: 5.6.1 (ruby 3.1.0-p0) ("Birdie's Version")
02:23:26 web.1        | *  Min threads: 5
02:23:26 web.1        | *  Max threads: 5
02:23:26 web.1        | *  Environment: development
02:23:26 web.1        | *          PID: 8
02:23:26 web.1        | * Listening on http://0.0.0.0:3000
02:23:26 web.1        | Use Ctrl-C to stop
02:23:26 css.1        |
02:23:26 css.1        | Rebuilding...
02:23:26 livereload.1 | 02:23:26 - INFO - Guard is now watching at '/app'
02:23:27 css.1        | Done in 619ms.
02:23:27 livereload.1 | 02:23:27 - INFO - Reloading browser: /assets/tailwind.css
02:23:27 livereload.1 | 02:23:27 - INFO - Browser connected.

ctrl-cでの終了処理もforeman gemがまとめてやってくれます。

^C02:24:42 css.1        | rails aborted!
02:24:42 web.1        | - Gracefully stopping, waiting for requests to finish
02:24:42 css.1        | Interrupt:
02:24:42 livereload.1 |
02:24:42 system       | SIGINT received, starting shutdown
02:24:42 web.1        | === puma shutdown: 2022-02-04 02:24:42 +0000 ===
02:24:42 css.1        |
02:24:42 - INFO - Bye bye...
02:24:42 web.1        | - Goodbye!
02:24:42 css.1        | Tasks: TOP => tailwindcss:watch
02:24:42 web.1        | Exiting
02:24:42 css.1        | (See full trace by running task with --trace)
02:24:43 system       | sending SIGTERM to all processes
02:24:43 web.1        | exited with code 0
02:24:43 css.1        | exited with code 1
02:24:43 livereload.1 | exited with code 0
$

おまけ

Rails 7でimportmap-railsとsprockets(またはpropshaft)のみを使う場合は、./bin/devというbinstubは追加されません。

しかしguard-livereload用に自分でbinstubとProcfile.devを追加すれば、./bin/devでguard-livereloadも起動できるので楽ですね。何となく、今後は./bin/devで起動するのが主流になりそうな気がしています。

# 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
# Procfile.dev
web: bin/rails server -p 3000
livereload: bundle exec guard start -i -g livereload

なお、foreman gemはGemfileに追記する必要はありません。見てのとおり、初期実行時にbundlerの管理の外でgem install foremanで勝手にインストールされます。

関連記事

Rails 7: data-turbo-confirmはformタグに書く


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。