1. tanaken on Rails
  2. #020: Kamal, create_schema, ..
2024-05-19 15:34

#020: Kamal, create_schema, touch_all

00:02
第20回のTanaken on Rails始めます。
今週は、3つのプルリクエストを紹介します。
1つ目、Add Kamal by default to Rails 8というプルリクエストです。
Railtiesに関する変更です。
Kamalというアプリケーションデプロイツールが、 Railsのデフォルトになるそうです。
Kamalというのは、アプリケーションの構成と、 デプロイ先のサーバーのIPアドレスなど、
そういった基本情報を設定することで、
仮想マシンだったり、ベアメタルサーバーだったり、 クラウドのサーバーインスタンスだったり、
そういった環境に、Docker環境の構築からアプリのデプロイ、 トラフィックの切り替えまでを自動的に行ってくれるような、
そういったアプリケーションデプロイのツールになっています。
Railsアプリケーションを、 Rails newコマンドを使って生成する際に、
config/.deploy.yamlが生成されるようになります。
このYAMLがKamalというアプリケーションデプロイツールの、 設定が書かれるようなファイルになると。
このファイルの生成をスキップした際は、
--skip-kamalというオプションを設定してくれれば、 スキップされますよということでございます。
以前に、Dockerで環境を作るのが前提になったよ、 みたいな話をしたような気がするし、
あとは、Rails newのときのデフォルト周りで言うと、 CIをGitHub Actionsを前提としたみたいな、
そういった変更も、 確か棚木のRailsで紹介したと思うんですけども、
Railsのプレリクエストを見ることで、
アプリケーション開発のある種のスタンダードと呼ばれるものが、
Rails開発者の視点ではどういうふうに考えられているのか、 というのが分かってちょっと面白いですね。
少なくともDockerで開発して、
そのDockerコンテナをデプロイするというのは、
かなり世の中のスタンダードでしょうという前提で、
考えられているんだなというのが分かるかなと思います。
続いて2つ目。
Add support for if not exist and false options to create schemaというプレリクエストです。
03:03
こちらは、アクティブレコードに関する変更です。
アクティブレコードのマイグレーション関連で、
Create schemaというメソッドがあります。
これはDBに新しいスキーマを作るメソッドです。
名前のメソッド名の通りですね。
このCreate schemaメソッドに今回のプレリクエストで、
2つのオプションが追加されました。
1つがif not existで、もう1つがfalseです。
if not existは、同名のスキーマが存在していない場合のみ新しいスキーマを作るというオプションですね。
もう1つのfalseオプションは、同名のスキーマがあれば削除して、
その上で新しいスキーマを作ると。
そういったオプションになります。
こちらはPostgreSQLで使えるオプションで、
PostgreSQLの機能としては、
同名のスキーマが存在していない場合のみ作るとか、
すでにあったら削除して作るみたいな機能がPostgreの方ではあるんですけど、
それはアクティブレコードのPostgreSQLアダプターには実装がなかったので、
そのPostgreの機能をちゃんと使えるようなアダプター側の修正をしましょうというようなことで実装されています。
こういうプレリクエストも、
Railsから繋がっている先、Railsが利用しているもの、
例えば今回いうと、アクティブレコードがPostgreを使っているというので、
Postgreで単体でならできるのに、アクティブレコード経由だとできないみたいな差分を見つけてコミットしているわけですね。
なので、その周辺技術に知識と興味を持って調べて、
そこからの差分でRails側を修正して追従させるみたいな、
そういうことってあるんだなというのを改めて感じますね。
そういう視点で物事を見てみるのも面白そうだなと思います。
続いて最後3つ目。
Fix Active Record Relation Touch All with Custom Attribute Alias as Attribute for Updateというタイトルです。
こちらはアクティブレコードに関する変更です。
アクティブレコードにTouch Allというメソッドがあります。
こちらは現在のリレーション内のすべてのレコードについて、
Update AtあるいはUpdate On属性を現在の時刻または指定された時刻に更新するというメソッドです。
06:00
RailsのAPIのドキュメントに具体例がいくつか載っていますが、
その中からいくつか紹介すると、
例えばpersonというモデルがあってperson.all.touchallというふうに呼び出したら、
personのすべてのレコードを取ってきて、
そのすべてのレコードというリレーションに対してTouch Allしているので、
personのすべてのレコードのUpdate Atを現在時刻に入れて更新するというようなエクエリ一括で、
すべてのレコードを更新するというような処理になるよと。
現在時刻のところは引数で渡すこともできて、
現在じゃない時刻、過去であったり未来であったりというのを埋めることもできますよと。
person.all.touchall タイムコロンで更新したい時刻を、
例えばtime.new 2024年5月とか入れてやると、
Updateのpeopleテーブルに対してUpdate Atを指定した時刻で更新するというようなことができますよと。
そんなメソッドですね。
今回はTouch Allですけど、
というのはそういう概念ですよね。
属性を更新すると、現在時刻で更新する、あるいは指定した時刻で更新するというようなことになってます。
このTouch Allメソッドには引数として属性名を指定することで、
このUpdate Atとかそういうカラムだけじゃなくて、
別のカラムも同時に更新することができます。
例えばさっきの例でperson.all.touchallでCreated Atのシンボルを渡してあげると。
そうすると、peopleテーブルに対してUpdate Atに対して現在時刻がアサインされ、
かつCreated Atに対しても現在時刻がアサインされ、
で、アップデートされると。
そういうクエリが発行されると。
そういうことですね。
なので、Update At以外の属性も渡して同時に更新できるようということですね。
で、今回このメソッドに対して修正が入ってるんですけども、
そもそもどんな不具合があったのかというと、
Aliasされた属性名を引数に渡した場合に不具合が発生するという場合があったと。
具体的には、このUpdate Atとかの属性に対してAliasが設定されている場合です。
09:10
例えば、usersというテーブルがあるとしましょう。
これはAppleリクエストに記載されていた例ですが、
usersテーブルをマイグレーションで作りますと。
クリエイトテーブルusers。
で、そのブロックの中でt.timestamp。
あれですね、t.timestampでupdate at columnとかcreated at columnとか作るやつ。
で、t.timestampに引数を渡していて、
僕この書き方知らなかったんですけど、
引数を渡していて、引数にシンボルのlegacy updated atというのを渡しています。
なので、更新時刻を指定するcolumn名として、
legacy updated atというのだけ追加しているという感じかな。
update at columnではなく、
legacy updated atというのを追加しているというシチュエーションですね。
だから、users columnにはIDとlegacy updated at columnと
created at columnがあるみたいな感じですね。
この状況で、ユーザーモデルの実装において、
alias attributeというメソッドを使って、
updated at、第1引数にupdated at、第2引数にlegacy updated atというのを書いて、
つまり、user.updatedatという形でアクセスすると、
DBとしては、usersのlegacy updated atからデータを取ってくるみたいな
aliasを貼っているんですね。
なので、ユーザーモデルにupdated atで
legacy updated atを参照するようなaliasを貼ると。
もう一回整理しますね。まず、usersテーブルを作ります。
columnはIDとlegacy updated atとcreated at。
ユーザーモデルにはalias attributeメソッドを使って、
updated atがlegacy updated atにaliasが貼られているような状態を作っています。
この状態で、user.touchall updated atというふうにやると、
エラーになりますというのが、今回の修正する対象の不具合です。
これは何でこんなエラーが起こるのかというと、
エラーの内容がactive recordコロンコロン、statement invalidコロン、
pgコロンコロン、syntax errorで、
失礼しました。
12:01
syntax errorになっており、具体的な中身は、
multiple assignment to same column legacy updated atというエラーになっています。
つまりこれは、legacy updated columnに対して、
複数回のassignment、つまり値の割り当てが行われていますよ、
syntax errorですよというエラーになっています。
一見ね、user.touchall updated atってやってるんだから、
updated atが更新されてよっていうふうに思うんですけど、
いろいろあって、legacy updated atが、
2回値が割り当てられて更新されようとする、
そういうクエリが作られているので、エラーになっていますよというのが、
今回の修正対象です。
何でこんなことが起きてるかというと、
touchallメソッドの中で、updateallメソッドを呼んでおり、
updateallメソッドに渡す値が、
legacy updated at column time、
legacy updated at column time、
legacy updated atとupdated atをキーに持つハッシュで、
値は現在時刻というような値を、
updateallに渡すようになっちゃっていると。
で、updateallは渡されたキーに関しては、
シリアライズしてカタキャストとかもするんで、
このエイリアスがね、
update atの方はエイリアスが剥がれているので、
それを考慮して、考慮というかそれを反映して、
update atがlegacy updated atという変換が起きていると。
なので、legacy updated at time、
かんまはlegacy updated at timeみたいな感じで、
legacy updated atを2回updateallに渡して、
クエリとしては2回そのアサインメントが行われているというようなクエリになると。
なので、シンタックスエラーですよということになっちゃっていました。
なので、今回のプレリクエストでは、
このupdateall、このタッチオールメソッド内部で、
update allを呼ぶときに、
update allに渡す引数、
その引数を修正するというアプローチで修正しています。
修正前までは、
legacy updated atとupdated at、
それぞれを引数として渡しちゃっていたんですけど、
このupdate atというのは、
実際のDBカラムとしては、
legacy updated atなんだよねというのが分かっているので、
つまり重複してカラム名を渡しているみたいな状態になっているんですよね。
そうならないように、
15:00
事前に重複を排除できるように修正をしていると。
なので、修正後はupdate allに
legacy updated atだけ渡すようになったので、
重複の問題が解消されたということですね。
そんな感じの修正でございました。
では、こんなところで、
第20回のたなきゃんレールズを終わりにします。
では、また来週。バイバイ。
15:34

Comments

Scroll