行動すれば次の現実

テック中心の個人ブログ

Webpacker3から5にアップグレードする方法 | Rails

最近、開発端末をMacBook Pro M1 Maxに乗り換えたところ、webpackのコンパイルが極端に遅くなりました。 思えばwebpack3のままメンテナンスを怠っていたので、これを機にwebpack4にアップグレードすることになりました。

Webpackerをv3からv5にアップグレードする

Webpackerを安定バージョンのv5にアップデートします

現状のwebpacker、webpackのバージョンは以下のとおりです。

webpacker (3.5.5) → webpacker (5.4.3)
webpack(3.12.0) → webpack(4.46.0)

これをwebpack4に対応しているバージョンまで上げたいと思います。

Gemfile

bundle update webpacker

Gemfile.lock

webpacker (3.5.5) → webpacker (5.4.3)

webpackerを再インストール

アップデートが完了したらwebpackerを再インストールします。

bundle exec rails webpacker:install

ファイル単位で上書きするかどうか聞かれます。一旦全て上書きしてから後ほど必要な箇所だけ変更すると良いでしょう。

私の環境ではreactを使用していますので、以下も実行します

bundle exec rails webpacker:install:react

babelとpostcssの設定ファイルを整理

インストールすると以下のように設定ファイルが置き換わります。

.babelrc
.postcssrc.yml

上記ファイルは新しくbabel.config.jspostcss.config.jsに置き換える必要があるので削除しましょう。

babel.config.jspostcss.config.jsの設定内容はほとんどの場合、デフォルトのままで問題ないと思います。

babelの各種設定内容を理解したい方は、以下の記事が参考になりますので一読しておくと良いでしょう。

zenn.dev

アップグレード作業完了

基本的なアップグレードはこれで完了です。Webpackerを使用していると簡単にwebpack周りをアップグレードすることができます。

残りはプロジェクトごとの個別考慮をして、動作確認をすれば終了です。

私の場合、プロジェクトで使用しているNPMライブラリの依存関係でエラーやワーニングが発生したので、それぞれ適切なバージョンにアップグレードしました。

無事アップグレードが完了し、m1 mac環境でのwebpackビルドも元通り高速になりました。

Webエンジニアが「アイデアの作り方」を要約してみた

「新しいサービスを開発したいけど、どんなサービスを開発したら良いのか思いつかない。。」と悩んでいたころ、たまたまアイデアの作り方という書籍を見つけました。

読んでみると目からウロコで、自分自身アイデアが思いつかない理由がはっきりとわかりました。

自分用のメモを兼ねて本を要約してみましたので、アイデアが思いつかないと悩んでいる方はぜひ参考にしてみてください。

アイデアを生むための基礎知識

アイデアとは「既存の要素の新しい組み合わせ」によって生まれます。 それ以上でもそれ以下でもありません。

既存の要素から新しい組み合わせを見つけるには、各要素を十分理解した上で、それぞれにおける関連性を見つけ出す必要があります。

各要素の関連性を見つける方法

各要素の関連性を見つけるためには日々の修練が必要です。一朝一夕で出来ることではありません。

以下の5つの段階を踏むことで体系的にアイデアを生み出す方法を磨いていくことができます。

1.資料を集める

第一段階は資料収集です。集める資料は2種類あります。

顧客や業界に関する特殊資料

一つ目は「特殊資料」と呼ばれるものです。これは、探求しようとしている分野の専門的な資料のことを指します。 例えばすでに貴社のサービスを利用している顧客がいる場合、顧客の属性や業界などの情報などがそれにあたります。

サービスがまだ存在しない場合は、サービスを利用してもらいたい顧客の情報や業界知識などがそれにあたります。

集める資料の量は「こんなものかな」というレベルではなく、自分が十分だと自信持てるレベルまで集める必要があります。

特殊資料は、ある程度の期間を設けて継続的に収集する必要があります。

世間の出来事や社会情勢に関する一般的資料

2つ目の「一般的資料」と呼ばれるものです。 これはジャンル問わず継続的に集める資料のことを指します。例えば日々のニュースや、興味を持った事柄についての知識、趣味の深堀りなどが当てはまります。

知識に貪欲であるほうが資料が集まりやすいので、意識的に情報感度を上げたり、新しい経験をするように心がけることで効率化を図ることができます。

一般的資料は一生涯に渡って継続的に収集する必要があります。

アイデアとは特殊資料と一般的資料の組み合わせから生まれる

アイデアは、顧客や業界に関する特殊資料と世間の出来事や社会情勢に関する一般的資料との新しい組み合わせから生まれます。 これらの要素が多くなればなるほど、様々な要素の組み合わせのパターンが作られることになり、新しいアイデアが生まれてきます。

2.資料を咀嚼する

第2段階は資料の咀嚼です。 まずは集めた資料をじっくりと眺める必要があります。それぞれの資料を並べたり、資料を行ったり来たりして各要素が噛み合うかどうかを調べます。

そのようなことを繰り返し行っていると次第に「仮の部分的なアイデア」というものがが生まれてきます。これらは流すのではなく、すぐにどこかに書き留めておくことが重要です。

しばらくこの作業を継続していると心が疲弊して嫌気がさしてくると思います。この作業はとても心的エネルギーを使うので当然なことです。

すべての要素の組み合わせを試した上で、何もかも心の中がぐちゃぐちゃになると第2段階が終了したことを意味します。

3.放棄する

第3段階においては、これらの問題を一旦意識の外に移す作業になります。問題を完全に放棄し、何でも良いので自分の想像力や感情を刺激するものに心を移します。

音楽を聞いたり、映画を見たり、趣味に没頭したりすると良いのではないかと私は考えます。

4.アイデアが生まれてくる

第3段階まで終えると、ふとした何気ないときにアイデアが生まれてきます。 それはお風呂に入っている時や散歩をしている時、はたまた夢の中かもしれません。

問題について何も考えていないときにこそアイデアが生まれてきます。

5.アイデアを具体化する

最終段階では、生まれてきたアイデアをより具体化して育てます。 生まれてくるアイデアはかなり幼稚な状態であることがほとんどです。 それを現実化できるように手を加えていく必要があります。

子供の状態であるアイデアを成長させていくことが重要になります。

まとめ:アイデアを生み出す習慣を作ろう

アイデアを生み出すには良い習慣を作ることが重要だと感じました。

とくに特殊資料と一般的資料の収集は、一朝一夕で結果が出るものではなく、日々の積み重ねが重要だと思います。

習慣化するためには目的が必要です。

ただ情報収集するのではなく、アイデアを作るためという目的を持てば、それがモチベーションとなって習慣化しやすくなるのではないかと思います。

私は現在、この5段階を実施している最中ですので、なにか進展がありましたら記事にして報告したいと思います。

ScoutAPMのトレース情報にユーザーIDを付加する方法

ScoutAPMを使ってモニタリングしているとトランザクションとログインユーザー情報を紐付けたいと思ったことはありませんか?

そのような場合は、ScoutAPMのCustom Contextという機能を使うことでトランザクションに対して付加情報を付けることが出来ます。

Custom Contextとは

ScoutAPMでモニタリングされるトランザクションに対して、任意の付加情報を付ける機能です。 アプリケーション側にシンプルなコードを埋め込むだけで実現することが出来ます。

どのような情報を付加するかはアイデア次第ですので、例えば以下のようなニーズに応えることが可能です。

  • 遅いリクエストによって影響を受けるユーザーは誰か?何人くらい該当するか?
  • 毎週木曜の深夜2時にメモリ負荷が上がる原因は「どのユーザー」の「どの操作」によるものか?

Custom Contextの使用方法

ユーザー情報を付加したい場合

ユーザー情報を付加したい場合は下記のメソッドを使用します。

ScoutApm::Context.add_user({})

例えばIDを付加したい場合は下記のような実装になります。

ScoutApm::Context.add_user(id: @user.id)

それ以外の情報以外を付加したい場合

ユーザー情報ではない一般的な情報を付加したい場合は下記のメソッドを使用します。

ScoutApm::Context.add({})

下記のように任意の情報を付加することが出来ます。

ScoutApm::Context.add(database_shard: @db.shard_id)

ユーザーIDをContextに付加する実装例

各アクション単位でユーザーIDをContextに付加することで実現可能です。 ApplicationController.rbに下記を定義します

before_filter :set_scout_context

def set_scout_context
  ScoutApm::Context.add_user(id: current_user.id) if current_user.is_a?(User)
end

このような実装を加えるだけで簡単にContextを追加することが出来ます。

下記は実際のScoutAPMのContextタブの画面イメージです。

f:id:furu07yu:20220112140327j:plain
ScoutAPMのContextタブ

user idが新しく追加されていることが確認できます。

設定自体はとても簡単なので運用のトレーサビリティを上げたい場合は設定しておくことをおすすめします。

参考

Advanced Features

RailsのLogにuser idを出力する方法

Ruby on Rails5でリクエストログにユーザーIDを追加する方法を説明します。 ミドルウェアの修正は不要ですので、気軽に取り入れることができると思います。

実装内容

config/initializers/logging.rb

Rails.configuration.log_tags = [
  :request_id,
  lambda do |req|
    session_key = (Rails.application.config.session_options || {})[:key]
    session_data = req.cookie_jar.encrypted[session_key] || {}
    user_key = session_data['warden.user.user.key']
    user_id = user_key.present? ? user_key[0][0] : 'guest'
    "user: #{user_id}"
  end,
]

実装解説

config.log_tagsというメソッドを使用して出力したいタグを指定します。

https://railsguides.jp/configuring.html#config-log-tags

Rails5の場合、デフォルトでproduction.rbにconfig.log_tags = [ :request_id ] が指定されていますが、今回は環境問わずlogging.rbの内容で上書きします。

user_idを出力したいので、:request_idの後ろにrequestオブジェクトを引数に取るlambdaメソッドを定義します。

Rails.application.config.session_optionsからsession_key取得して、requestのcookie情報からsession_keyに対応するsession_dataを取得します。

session_dataは以下のような内容になっています。

{
  "session_id" => "c8aead6bfbdfsdfc2dsafasccdda2", 
  "warden.user.user.key" => [
    [12],
    "$2A$1d$Qd775IfaSsdfRfasdfdfmu"
  ],
  "_csrf_token" => "vffdcdfasd-t3rWY8mfDFADSFACEFDAfeccdsfffSA"
}

warden.user.user.keyの[0][0]がuser_idですので、それをログに出力します。

Railsでよくエラーになるlibv8、therubyracer、mini_racerあたりを調べた

Railsアプリを構築しようとするときにbuildエラーになるライブラリの代表にlibv8、mini_racerなどが上げられると思います。(個人的感覚)

毎回gemを最新化することでなんとなく解決していたのですが、これではその場凌ぎしかなく応用が効きません。 しっかり理解しておく必要があると思ったので、libv8周りのことを調べてみました。

そもそもv8とは

  • GoogleのオープンソースハイパフォーマンスJavaScriptエンジンです

v8.dev

libv8とは

  • v8ランタイムのソースとバイナリが格納されたgemです
  • linux,os x, windowsプラットフォームにおける様々なCPUにサポートしています
  • libv8のバージョンによってサポートされているCPUが異なるので、適切なバージョンを使用する必要があります
    • そのためよくbuild時にエラーが起こりがち

github.com

therubyracerとは

  • RubyでJavascriptを実行できるようにするgemです
  • libv8に依存しており、libv8を使用してv8エンジンをインストールしています
  • もともとはRails本体の依存ライブラリでしたが、2017年より開発がストップしてRails本体からも切り離されました

github.com

mini_racerとは

  • RubyでJavascriptを実行できるようにするgemです
  • 2017年にtherubyracerに代わってRails本体に組み込まれました
  • 0.3.1まではlibv8を使用しており、0.4.0からlibv8-nodeを使用するように変更されました
    • GitHub - rubyjs/libv8-node: Package libv8 from Node
    • 2022/01/05 現在まだリリースタグが打たれていないかつ、build失敗のissueも上がっているので不安要素がある
    • 元に筆者のci環境(docker)ではbuildエラーになってしまう

github.com

execjs

  • RubyでJavascriptを実行できるようにするgemです
  • Javascriptを実行するために最適なランタイムを自動で選択します(下記ランタイムをから選択される)
    • therubyrhino
    • Duktape.rb
    • Node.js
    • Apple JavaScriptCore
    • Microsoft Windows Script Host
    • Google V8
    • mini_racer
  • 大抵の環境にはNode.jsがインストールされていると思うので、それが選択されると思います
    • そのためmini_racerのlibv8、libv8-nodeあたりのbuildエラーから開放される

github.com

yarnコマンドで発生したgyp ERR!の原因究明と対処方法

アプリケーションのnode.jsのバージョンを上げて、yarn installコマンドを実行したら以下のようなエラーが発生しました。

1 error generated.
make: *** [Release/obj.target/binding/src/binding.o] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/Users/my_name/my_app/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (node:events:390:28)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
gyp ERR! System Darwin 20.6.0
gyp ERR! command "/usr/local/var/nodebrew/node/v16.13.1/bin/node" "/Users/my_name/my_app/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /Users/my_name/my_app/node_modules/node-sass
gyp ERR! node -v v16.13.1

エラー内容を確認すると下の方にnode-sassと出力されています。

node-sassはpackage.jsonには記載のないライブラリでした。そのため、アプリケーション内で使用している何らかのライブラリがnode-sassに依存しているということがわかります。

原因究明の方法

yarn why [モジュール名]というコマンドで、間接的に依存しているライブラリを特定できます。

$ yarn why node-sass

[1/4] 🤔  Why do we have the module "node-sass"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "node-sass@4.14.1"
info Reasons this module exists
   - "@rails#webpacker" depends on it
   - Hoisted from "@rails#webpacker#node-sass"
info Disk size without dependencies: "2.97MB"
info Disk size with unique dependencies: "11.13MB"
info Disk size with transitive dependencies: "23.48MB"
info Number of sha

"@rails#webpacker" depends on itという記載がありますので、@rails#webpackerで使用されているnode-sassのバージョンに問題があることがわかりました。

対処方法

@rails#webpackerで使用しているnode-sassのバージョンに問題があることがわかりましたので、本体となる@rails#webpackerのバージョンを上げてみようと思います。

package.jsonの記載を以下のように変更しました。

    "@rails/webpacker": "^5.4.3",

その後、yarn installを実行したら正常にインストールが完了しました。

めでたしめでたし。