はじめに
仕事でRuby on Railsを用いてバックエンド開発を行っているのですが、その中でトランザクション内で定義した変数をトランザクション外で使いたいケースが出てきたので、その方法について共有します。
インスタンス変数を使う
その方法とは、「インスタンス変数を使う」ことです。インスタンス変数とは、@hogehoge
といったように、変数の前に@
を付与した変数になり、ブロックの外でも変数を使用したいときに用いられます。
ブロックの外でインスタンス変数を使う用途として
-
クラス内のメソッドで定義された変数に対して、インスタンスを生成することで別のメソッドでも利用できるようにする(詳細記事はこちら)
-
Controllerファイルで定義した変数をViewファイルでも利用できるようにする(詳細記事はこちら)
が代表的なものにあります。詳しい内容は添付した記事を参考にしてみてください。
サンプルコードを見てみる
下記Userテーブルがあるとし、トランザクション処理の後にメール送信する処理を想定します。
id | username | nickname | password | created_at | updated_at | |
---|---|---|---|---|---|---|
1 | マイク | マイク | mike@mike.jp |
XXXXXXXXXXXXX |
2024-09-28 | 2024-09-28 |
def update
ActiveRecord::Base.transaction do
user = User.find(params[:id])
user.update!(
email: "mike2@mike.jp",
nickname: "マイクくん"
)
end
UserMailer.with(user: user).welcome_email.deliver_later #エラー発生
redirect_to user_path
end
def update
ActiveRecord::Base.transaction do
@user = User.find(params[:id])
@user.update!(
email: "mike2@mike.jp",
nickname: "マイクくん"
)
end
UserMailer.with(user: @user).welcome_email.deliver_later #メールが送信される
redirect_to user_path
end
上記のように、transaction外でローカル変数を用いようとするとエラーが発生し、インスタンス変数を用いると問題なく挙動します。
なぜインスタンス変数を使うのか?
上記ソースコードのように、なぜtransaction外ではインスタンス変数を用いることが必要なのでしょうか?こちらですが、冒頭でも述べた通り、スコープがブロックの外にまで影響が及んでいるためです。
トランザクションの一連の処理はブロック単位となっているので、トランザクション外の処理はブロック外とみなされます。そのため、トランザクション外で使う場合には、インスタンス変数を使う必要があります。
最後に
これまでインスタンス変数について深掘りしたことがなかったので、このエラーを通して良い機会になったと思います。かといって、インスタンス変数を使いすぎるのも変数の安全性が下がると思うので、ローカル変数でかけるところは書いて、ローカル変数で賄えない処理についてはインスタンス変数を使うのが良いと思いますね。