Categories

Latest comments

Active Recordマイグレーションでのadd_foreign_keyとadd_referenceの違い

wakairo @wakairo

Railsガイドを一読しただけでは、 add_foreign_keyとadd_referenceが、それぞれどのようなもので、どう違うのかがいまいちよく分かりませんでした。 そこで、add_foreign_keyとadd_referenceによってdb/schema.rbがどのように変化するかを基に、 それぞれの機能について確認してみました。

add_foreign_key

add_foreign_keyは外部キー制約の追加だけを行います。よって、カラムやインデックスの追加は行いません。

外部キー制約とは、子テーブルが参照している親のIDが親テーブルに存在することをデータベースレベルで保証する制約です。

例えば、以下の記述をマイグレーションファイルに行ったとします。

add_foreign_key :products, :users

すると以下の記述がdb/schema.rbに追加されます。

add_foreign_key "products", "users"

この記述は、products(子テーブル)のuser_idカラムに登場するIDが、users(親テーブル)のidカラムに必ず存在するように制約をかけています。

add_reference

add_referenceは、テーブル間の関連付けに関する複数の設定を一度に行える便利メソッドです。

add_referenceの基本機能は、カラムとインデックスの追加です。 例えば、以下の記述をマイグレーションファイルに行ったとします。

add_reference :products, :user

すると以下のように、users(親テーブル)に関連付ける2つの記述、具体的にはuser_idのカラムとインデックスをproducts(子テーブル)に追加する2つの記述が、db/schema.rbに追加されます。

t.integer "user_id"
t.index ["user_id"], name: "index_products_on_user_id"

またadd_referenceは、foreign_key:trueオプションを追加することで、前述の2つに加えて外部キー制約も同時に設定できます。 例えば、以下の記述をマイグレーションファイルに行ったとします。

add_reference :products, :user, foreign_key:true

すると以下のように、前述の2つの記述に加えて、add_foreign_keyの記述がdb/schema.rbに追加されます。

t.integer "user_id"
t.index ["user_id"], name: "index_products_on_user_id"
add_foreign_key "products", "users"

(参考)add_referenceとt.referencesの機能は基本的に同じ

add_referenceとcreate_tableのブロックの中で呼び出すt.referencesは基本的に同じ機能を提供します(オプション以外の引数の部分で違いはありますが。) 実際にt.referencesへ渡せるオプションはadd_referenceと同じです。 また、t.referencesの実装add_referenceの実装のどちらもReferenceDefinition.newを内部で呼ぶ形になっています(v8.1.2で確認)。

0
Raw
https://www.techtips.page/en/comments/1107

ActiveRecord::Rollbackで例外を伝播させずにロールバック後の処理を継続する

wakairo @wakairo

Active RecordのTransactionブロック内で例外が投げられた場合、ActiveRecord::Rollback以外の例外はロールバックの後に再度投げられます。

したがって、処理失敗で例外を投げるメソッド(save!等)を使えば、基本的にその例外はロールバックの後Transactionブロックの外へ伝播します。 これをキャッチしなければ、RailsはHTTPのエラーレスポンスをブラウザ等のクライアントに返します。 つまり、「処理に失敗したら、ロールバックして、あとの処理は切り上げて、エラーレスポンスを返す」という挙動は簡単に実装できます。

一方で、「ロールバック後に例外を出さずに処理を継続する」場合には、ActiveRecord::Rollbackを活用できます。 以下は、Transactionの成否でリダイレクト先を変えるコード例です。

completed = false

ActiveRecord::Base.transaction do
  unless obj1.save && obj2.save
    raise ActiveRecord::Rollback
  end
  completed = true
end

if completed
  redirect_to completed_path
else
  redirect_to uncompleted_path
end
0
Raw
https://www.techtips.page/en/comments/1106

【小ネタ】Hash#mergeはキーワード引数でも動作する

kenicode SatoKen @kenicode
Last edited

RubyのHash#mergeのマニュアルでは、 その引数はハッシュとしか書かれていません。 例えば、以下のような形です。

irb(main):001> {f1: 1, f2: 2}.merge({b3: 3})
=> {:f1=>1, :f2=>2, :b3=>3}

では、キーワード引数を渡すとどうなるのかと言うと、以下のように動きはしますよ、という小ネタです。

irb(main):002> {f1: 1, f2: 2}.merge(b3: 3)
=> {:f1=>1, :f2=>2, :b3=>3}

Rubyの言語仕様上OKなのかは不明ですので、自分から積極的に使うというよりは、 他人のコードを読むときに役立つかもしれない小ネタ知識でした。

0
Raw
https://www.techtips.page/en/comments/1086
❤️1

minitest6にアップデートするとrails testがエラーになる

takuma_tech Takuma @takuma_tech

1/9にリリースされたRails v8.1.2は、minitest6で問題なさそうです。簡単にテストした分には、正常にテストが走りました。

ちなみにv8.1.1は、minitest6にすると、エラーにはならないものの、テストが全く拾われず0 runs, 0 assertions, 0 failures, 0 errors, 0 skipsになっていました。

0
Raw
https://www.techtips.page/en/comments/1085
😄2
🔄1
❤️1

WSLのUbuntu 24.04においてGUIアプリの起動が遅い場合はWSLの更新で解決します。

wakairo @wakairo

WSLでUbuntu 24.04の利用を始めてから、 一部のGUIアプリ(meldなど)の起動に異常に時間がかかる問題にずっと悩まされていました。 本日以下のようにコマンドプロンプトでwsl --updateを実行して、この問題がやっと解決しました。

なお、この問題に対する修正は WSL2.5.1で導入されたそうです。

C:\Users\wakai>wsl.exe -v
WSL バージョン: 2.4.11.0
カーネル バージョン: 5.15.167.4-1
WSLg バージョン: 1.0.65
MSRDC バージョン: 1.2.5716
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.26200.7462

C:\Users\wakai>wsl --update
更新プログラムを確認しています。
Linux 用 Windows サブシステムをバージョンに更新しています: 2.6.3。

C:\Users\wakai>wsl.exe -v
WSL バージョン: 2.6.3.0
カーネル バージョン: 6.6.87.2-1
WSLg バージョン: 1.0.71
MSRDC バージョン: 1.2.6353
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.26200.7462

参考情報

0
Raw
https://www.techtips.page/en/comments/1084
🔄2
🔧1
💯1
🎉1

シェルスクリプトで壊さずに全ての引数を別コマンドに引き渡す書き方

wakairo @wakairo
Last edited

基本はダブルクォーテーション付きの"$@"

シェルスクリプトに渡された全ての引数を、そのシェルスクリプト内で別のコマンドに引き渡す場合は"$@"を使います。 例えば、以下のように記述します。

grep -n cat "$@" | sed "s/cat/__CAT__/g" | tee -a ~/foo.log

"$*"$@(ダブルクォーテーションなし)を使うと、'foo bar'のようなスペースを含む引数がスペースで分割されて壊れてしまいます。

shコマンドに渡すときはsh -c '..."$@"...' -- "$@"

1つの実行コマンドしか受け付けないsudosshdocker runなどで、 複数コマンドの実行などの複雑な処理を行いたいときに活躍するのがsh -c '...'です。

シェルスクリプト内でsh -c '...'にすべての引数を渡したいときは、sh -c '..."$@"...' -- "$@"と記述します。 例えば、以下のように記述します。

sudo sh -c 'grep -n cat "$@" | sed "s/cat/__CAT__/g" | tee -a /var/log/foo.log' -- "$@"

先頭のいくつかの引数を取り出して残りを渡す場合(おまけ)

検索ワードのような「固定の引数」と、複数のファイルのような「可変長の引数」が混在しているケースです。 これらをまとめてsudosshdocker runなどに投げたい場合は、 -cオプションの文字列内でshiftを使います。

以下は、第一引数が検索文字列、第二引数がその検索文字列を置き換える先の文字列、 第三引数以降はこの検索と置換の対象となるファイル(複数個指定可)となっている例です。

sudo sh -c 'pattern="$1"; replace="$2"; shift 2;
            grep -n "$pattern" "$@" |
            sed "s/$pattern/$replace/g" |
            tee -a /var/log/foo.log' -- "$@"
0
Raw
https://www.techtips.page/en/comments/1083
❤️2
😄1
🔧1
💯1

minitest6にアップデートするとrails testがエラーになる

wakairo @wakairo

現象

2025/12/18にリリースされたminitest 6.0.0のgemにアップデートしたところ、 bin/rails testの実行で以下のエラーが発生して止まりました。ちなみに、テストが1つも無い状態ではこのエラーは発生しませんでした。

Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 32547

# Running:

/x/vendor/bundle/ruby/3.4.0/gems/railties-8.0.4/lib/rails/test_unit/line_filtering.rb:7:in 'run': wrong number of arguments (given 3, expected 1..2) (ArgumentError)
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:467:in 'block (2 levels) in Minitest::Runnable.run_suite'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:463:in 'Array#each'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:463:in 'block in Minitest::Runnable.run_suite'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:505:in 'Minitest::Runnable.on_signal'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:492:in 'Minitest::Runnable.with_info_handler'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:462:in 'Minitest::Runnable.run_suite'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'block in Minitest.run_all_suites'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'Array#map'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:355:in 'Minitest.run_all_suites'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:310:in 'Minitest.run'
        from /x/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.0/lib/minitest.rb:84:in 'block in Minitest.autorun'

とりあえずの回避策

Gemfileのgroup :testのところに以下の記述を追加してminitestのバージョンを6よりも下に設定したところエラーは出なくなりました。

group :test do
  gem "minitest", "< 6"
0
Raw
https://www.techtips.page/en/comments/1082
😿3
🔄1
🔧1

SprocketsからPropshaftへの移行に関する注意点

wakairo @wakairo

Propshaftへ移行したらSprockets用の設定は削除可能

config/environments/production.rb にある以下の設定はSprocketsの設定なので、 Propshaftへ移行したら、削除可能です。

config.assets.compile = false

参考情報

https://www.techtips.page/ja/comments/658

0
Raw
https://www.techtips.page/en/comments/1081

SprocketsからPropshaftへの移行に関する注意点

wakairo @wakairo

Propshaftは.erbを処理しない

Sprocketsは拡張子が .erb のファイル(例: foo.scss.erb)を処理しますが、Propshaftは処理しません。 そのため、Sprockets で .erb を処理している場合、Propshaftへ移行するには次のいずれかの対応が必要になります。

  • .erb の利用をやめ、プレーンなCSS/SCSSなどに書き換える
  • .erb の処理を、Propshaftに渡す前の前処理として別途組み込む
0
Raw
https://www.techtips.page/en/comments/1080

SprocketsからPropshaftへの移行に関する注意点

wakairo @wakairo

Propshaftへの移行に際して、tailwindcss-railsまたはdartsass-railsを選ぶのもあり

Propshaftの公式移行手順では、CSS関連の処理が必要な場合の選択肢として cssbundling-rails のみ紹介されていますが、 tailwindcss-rails と dartsass-rails という選択肢もあります。cssbundling-railsのREADMEには、選び方のヒントが掲載されています。

0
Raw
https://www.techtips.page/en/comments/1079