Tried to dump unspecified class: BigDecimal
概要
以下の状況でタイトルのようなエラーが出ました
- decimal型のカラムにscaleを追加した
- 上記テーブルにはAudited導入済み
エラーが出た際のスタックトレースから、「おそらくaudited内のYAML.dumpでBigDecimalが対応していないことによるエラーなである」ということまではわかったので、それを中心に調査をしてみました。
スタックトレースは以下(一部)
/usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:568:in `accept' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:328:in `block in visit_Hash' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:326:in `each' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:326:in `visit_Hash' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:132:in `accept' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:571:in `accept' /usr/local/bundle/gems/psych-5.2.3/lib/psych/visitors/yaml_tree.rb:114:in `push' /usr/local/bundle/gems/psych-5.2.3/lib/psych.rb:603:in `safe_dump' /usr/local/bundle/gems/activerecord-7.1.5.1/lib/active_record/coders/yaml_column.rb:19:in `dump' /usr/local/bundle/gems/activerecord-7.1.5.1/lib/active_record/coders/column_serializer.rb:26:in `dump' /usr/local/bundle/gems/audited-5.7.0/lib/audited/audit.rb:30:in `dump'
詳細
原因(調査記録)
結論、原因はよくわかっていません。というか確信は持てていません。以下の記事をみると use_yaml_unsafe_loadがdefaultでtrueになったという話は出てるのですが、
https://techracho.bpsinc.jp/hachi8833/2022_07_13/119808
以下の2点を考えるとまだ良くわからないところが多いです。 - 今回問題が出てるのはYAML.loadではなく、YAML.dumpである - unsafe_loadにするとBigDecimalがはじかれるというソースががない
と思っていたんですが、
config.active_record.use_yaml_unsafe_load = true
を追加したらエラーが出なくなりました。
では、これで対処すればいいかと言われると、そういうわけではなく、脆弱性に対処するためにfalseにされたオプションをむやみにtrueにするのは微妙です。
対処法
https://github.com/collectiveidea/audited/issues/631
結論、BigDecimalをホワイトリスト形式で許可するか、auditのaudit_changesカラムをjsonにすることに二択なようです。
さて、ホワイトリストのほうが楽ですがこれだとどうなのでしょうか?
chatGPTに聞くことにします。
注意点としては以下だそうです。 - セキュリティリスクの増加 - 影響範囲が読めない - 将来的にsafe_loadがさらに厳格化される可能性から、早めにaudit_changesのカラムをJSON化するほうに振り切った方が良い
たしかに、と思うと同時に、抽象的でよくわからないとも思います。 特に「セキュリティリスクについて」は詳しく知る必要があると判断したので、さらに聞くことにしました。
まず前提としてyaml_column_permitted_classesに、BigDecimalを許可しただけでは脆弱性はありません。
このオプションにKarnelなどがあった場合、「rm -rf /」などのコマンド(例)が実行可能になる"可能性がある"という話です。
audited_changesでは、そもそも保存は自動でされるので、YAML.loadの悪用は問題ないのでは?という結論に至っています。
Railsのトランザクションについて学び直す
Railsのトランザクションって、勘で書きがちで、自分の理解は下記ぐらいだったので、少し真面目に捉えようと思いました。 まずは学ぶことを決めていきます。
今のところの自分の解像度
曖昧な概念(わからないこと)
派生できる概念も含めてこんなところでしょうか
- そもそもActiveRecordの詳細実装
- requires_newオプション
- ネストしたトランザクションの挙動
- セーブポイントの概念
- 楽観的ロック/悲観的ロックの使い所
- after_commit/after_rollbackコールバックの使い方
<ActiveRecordの内部実装>
これに関しては指針がほしかったのでClaudeに聞いてみました。
https://github.com/rails/rails/tree/main/activerecord 特に lib/active_record/transactions.rb を重点的に
Connection Pool の仕組み
- データベースアダプタの実装
- 分散トランザクション
- デッドロックの検知と対策
- connection.transaction の実行フロー
- ロールバックの発生条件と挙動
- データベースへの実際のクエリ発行タイミング
- トランザクション分離レベル
- ORMの主要パターン(ActiveRecord, DataMapper, Repository)
- レコードの読み込み(遅延ロード vs 即時ロード、キャッシュ戦略)
- 関連の取り扱い(has_many, belongs_toの実装、結合テーブルの処理)
- コレクションの管理(配列のような操作、バッチ処理)
仕事振り返り その3
簡単な振り返り
- group_byでは2カラムセットをキーにしてグループ化できる
- stateful_enumを使ったtransitionはevent名にcanをつけて呼び出すと、対象のインスタンスが遷移可能なステータスなのかを教えてくれる
- ハッシュに対して、{}.defaultでkeyが無い場合のvalueのセットができる
設計手法について
context変数
人の設計をみて思ったことですが、SeriveClassやFormObjectなどを作るときに、「主責務からは逸脱するものの、処理の都合上必要なデータ」などをcontextというハッシュにまとめてしまう、という手法を見かけましたが、かなり良いなと思いました。
ViewObject
ViewObjectを使ったSVGの描画が良いなと思いました。 SVGって、動的なものをベタ書きしようとすると、かなりごちゃついてしまうので、ViewObjectにすることで、要素を置く位置を計算するメソッドをViewObjectにまとめてしまって、さらに各要素のVIewObjectをその中で生成していくことで、ViewとViewHelperだけでは複雑になりがちなメソッドを要素ごとにまとめることができます。
例えば、飲食店などで席のVIewをレンダリングして、一つ一つの席のObjectはhighlightメソッドを持っている。そこに人が座ったときにクリックすることでhighlightメソッドがtrueを返し、その席が埋まっていることをホームページなどで知らせることができるシステムなどに使えますね。
ストリングス - 既存曲からの学び
既存曲からの学び
横軸
- 1stVn 白玉系のときに、前後の音の高低差が合った場合、かけあがりフレーズをいれる
- ノンコードトーンもあり
- 駆け上がったあとはコードトーンに着地するといい感じになる
- 1st と 2ndが必ずしも同時になってるわけではない
- 1stしか鳴ってない部分もある
- ヴィオラは比較的白玉系
- 1st 2ndが白玉系のときにヴィオラが反行することもある
- チェロも当然白玉系だが、ところどころヴィオラに反行したり(1st 2ndに寄せている)、ヴィオラの半分の音価で切り替わることがある
- 主従関係の交代
- 駆け上がりは、ど頭ではなく1拍後などにいれるのもあり
- クロマチックのかけあがり
- 煽り方
縦軸
- 上三声において、開離でも密集でもないパターン
- 次のコードのテンションになるように前のコードの音を繋留させる
ストリングス打ち込みの学び
意識すべきルール
簡単チェックリスト
- 三和音の場合、ルート以外の音が重複してしまっていないか?
- 3度が重複すると微妙
- そのためのオクターブ配置
- 3度が重複すると微妙
- 上三声の各声部同士のインターバルが、1オクターヴを超えていないか?
- 限定進行音を守れているか?
- 禁則は発生していないか?
- ビブラートやエクスプレッションなどを適切に使い切れているか
音域
Vn・・・G2~G5(C5以上からプロ奏者じゃないと難しくなってくる)
Va・・・C2~A4
Vc・・・C1~G3
ボイスリーディング(限定進行音)
| 3rd | 2上 |
|---|---|
| 7th | 2下 |
| ♭9th | 2下 |
| 9th | 2下 |
| #9th | ♭9thへ |
| 11th | 2下 |
| #11th | 2上 |
| ♭13th | 2下 |
| 13th | 2下 |
ボイスリーディングにおけるdimについて
パッシングdimはドミナントのルート抜きと考える
例:G#dim7 ⇔ E7(♭9)
禁則
- 連続8度
- 2つの声部間で完全8度の関係が連続すること
- 連続5度
- 2つの声部間で完全5度の関係が連続すること
- 減5度→完全5度の進行が生じること
- 並達5度、並達8度
- 2声部が並行するときに後続に完全5度、完全8度の関係が生まれてしまう状態(ただし下記条件があるので、ソプラノとバスを反行させておくことで防げる)
- 外声間に並達が生じた場合 → ソプラノとバスの間で並達が生じた場合。
- ソプラノが跳躍進行した場合 → ソプラノが3度以上の音程で進行した場合。
- 2声部が並行するときに後続に完全5度、完全8度の関係が生まれてしまう状態(ただし下記条件があるので、ソプラノとバスを反行させておくことで防げる)
ボイシング
- ポップスでは基本的に開離の方が良い
- また、開離の場合、上三声の隣り合う音の距離が1オクターブ以内に収まるようにする
- 原則としてコードトーンはすべて鳴らす
- 例外
- 限定進行音を守る影響
- 編成以上の音数がある
- 省略可能な場合はする
- 5度の省略
- ベースがルート以外
- 省略可能な場合はする
- 例外
- 第一転回の場合、ベースに3rdが入っているため、3rdは上三声から抜いたほうが安全
- つまりオクターブ配置になる可能性がある
- 逆にいうと、オクターブ配置になるのはこの場合のみ
- バスとテノールは15度以上は離れないほうがいい
横のライン
- Vcと1stVnが反抗するといい感じになる
- トップノートだけを動かし続けず、内声もちゃんと動かす
- メロの重心にリズムを合わせるといいかんじになる
ユニゾンについて
- 必ずしもコピペで同じ音を弾くというわけではない(という風に捉えるのが良い)
- 例えばVn2は6度下でハモるなど
- 目立たせたいフレーズなど
既存曲について思ったこと
カウンターメロディ的にトップノートが動くパターン
- それ以外の声部は必ずしもトップノートと同じようなラインで進まなくてもいい
- かといって、バラバラな方向に進みもせず、譜割りだけ一緒で音は変わらないなどの停滞をしている
- 例
- トップノートはオルタードテンションを含んだつややかなラインをたどっているが、それ以外は譜割りだけ追従して、残りはコードトーン
駆け上がり
- C4→G5などの跳躍駆け上がりもある
- おそらく運指的にやりやすい駆け上がり
- かけ下がりのあとすぐかけあがりもあり
- Vを描く
- 駆け上がり×2回でも良い
- 例:16分3連を2回ずつ
- 駆け上がりのあと必ずしも駆け上がり時より高い音に到達する必要はない。下がっても良い
- ただし比率としては駆け上がり時より高い音に到達していることのほうが多い
- C4→G5などの跳躍駆け上がりもある
- ライン・ボイシング
- 内声は1stVnと結構おなじ動きをすることもある
- 必ずしも連続的にコードトーンを鳴らす必要はない
- 特にフレーズチックなものは柔軟に考える
- ハモらせすぎるとフレーズ感がなくなってよく聞こえなくなる
- 逆に、ここフレーズ感をどのくらい明確にするかでストリングスの主役度が変わってくる
- サブドミナントマイナーなどの部分転調みたいなときは、裏メロでCメジャーにない音を使ってみると立体感が出る
- メロの重心にストリングスのリズムをあわせてみる
- コードの内声やテンションを裏メロと捉えると♭5thや#5thなどもストリングスの段階で足してみるのもあり
- その他
- C5が限界かなと思いつつF5くらいまである曲はある
- スタッカートで徐々に音量を上げるなどのとき以外は、Uを描くようなベロシティになっていることが多い
- Uを描くというのは、例えば5音あったときに
- 強→少し強い→少し弱い→弱い→少し強い→強いのようなベロシティ構成
- Uを描くというのは、例えば5音あったときに
- 強いベロシティが連続することが少なく、上がったあとに落ち着くパターンが多いような気がしてる
- ベースとトップが同じだと重くなる
打ち込みの話
仕事振り返り その2
hotwireを使った部分遅延ロード
RailsのViewでできたページの一部を遅延ロードする必要があった。 ページの一部表示で重めの計算をしているが、その表示はあまり参照しないので、遅延ロードしたいという意図。
いろいろ調べると、Hotwireを使うとよさそうだったので、調べてみた。遅延ロードに関する記事はあまり出てこなかったが、以下のようにすると良い。
表示親のページ
= turbo_frame_tag 'target_page_name(何かしらの識別値を任意で命名する)', src: target_page_path do .loading 計算中...
表示するページ
%turbo-frame{id: "target_page_name(任意で命名した識別値)"} 以下は通常の記法
Controller
def target_page_name render partial: 'target_page_name' end
あとは使いたいインスタンス変数や、通常通りルーティングをセットすればOK
lock!, zipなど
zipメソッド
まぁこれはシンプルに初めて知ったというだけで特筆することはないですが、zipメソッドは競プロの問題に使えそうだなぁとおもいました。
https://docs.ruby-lang.org/ja/latest/method/Array/i/zip.html
lock!メソッド
最近は物流周りのシステムを触っているので、今まで考えもしなかったロック系のことも知る必要があるなと感じました。 実際にソースの中でlock!が出てきたので、一回調べてみようと思った感じです。
- lock!とwith_lockがあり、with_lockはトランザクションも同時に行う
- 悲観的ロックという概念がある(きっと競合するだろうという観点でロックすること)
- 楽観的ロックはデータのバージョンを管理できるカラムを使って、ロックはしないものの、「更新対象取得時と同じバージョンであれば更新する」というもの
- 悲観的ロックは、UI上からロックを解除できる機能があった方が良い
同じ画面を開いている管理者が同時に同じ操作をするとき、操作対象は最初にバックエンドからとってきたものが画面に出ているはずで、例えば
- Aさんが1000個ある在庫のうち、100個の破棄を行う
- Bさんが同じことをしようとするが、Aさんが作業中なのでロックされている
- ロックが開放されたので、Bさんが同じことをする
- 100個でよかったのに200個破棄してしまう
みたいなことは起きないのだろうか、3の時点で、Bさん側の画面には1000個在庫があるように見えないだろうか?Aさんが同じことをしていたということをBさんが知らなければ4みたいなことは起きないだろうか? この辺は運用でなにか回避するか、別のシステム上の工夫が必要な気がしている。
参考
https://qiita.com/NagaokaKenichi/items/73040df85b7bd4e9ecfc
https://qiita.com/kamohicokamo/items/e7c31f61c99c9fc0fe7f
https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
Kaigi on Rails 2022 & 2023
今更ながらKaigi on Rails 2023の気になったものをピックアップして感想を書いていこうかなと思います。 見たら随時追記していきます。
自分のPCの中に2022年のメモが存在したのでそれにも触れておきます。
2022年
before_actionとのつらくならない付き合い方
これは個人的に感動しました。 before_actionというか、コールバック系はなるべく使いたくないなと感じていたものの、「これって自分だけ?玄人はそんなことないのかな」と思ってたので。
とくにRailsのコールバックはクラスの上にまとめて定義するし、複数アクションに適用できるので、どのアクションのときにどのコールバックをつかうか、コールバックの前提条件としてどのコールバックが必要か、みたいな順序を管理しなければいけないですよね。
とはいえ、こういった手法の本質はメリデメの比重にある気がしています。
例えば、「どこで変化してるかを拾いきれないデータの変更を検知してある処理をする場合」など、before_actionを使わなければデメリットが大きいケースなどが実体験としてあったので。(before_actionに限らずafter_commitなどでも)
本質的関心事と横断的関心事の概念は初めて知りました。こういったモヤモヤを抽象度の高い言葉で分類できるとスッキリしますね。
- 本質的関心事とは、そのメソッドが「やるべきことはなにか」ということ
- 横断的関心事とは、そのメソッドを含めたいくつかの処理を行うにあたって必要な処理
ということらしいです。Slack通知などは横断的ですね。決済ログとかも。これらはコールバックを使うメリットが上回るんじゃないかなと思います。
今年できたチームの生産性を向上させたプラクティスの紹介
これは当時PMをやっていたので参考にさせていただきました、といいたいところなんですが、受託開発という性質上、すべて当てはまるわけではなかったです。
ただ、ひとくちにドキュメントといっても、API設計が大事なときもあれば、共通言語というか、概念を表すユビキタス言語みたいなものがまとまっていると嬉しいときもありますし、コードの外に出て、ソフトウェアを使う人の業務のことが詳しく書いてあったほうが嬉しいケースもある、と改めて考えるきっかけになりました。
ペアプロも、ペアプロをがっつりやるのがよいときもあれば、ペアプロ風にIssueとそのIssueの解決方法をモブプロ的にやるのがよいときもありますね。
当時のチームでは後者が効いたかなと思います。
とりあえず抑えておきたい Railsでの「テストの内容」の考え方
特筆すべきことはない、というかありすぎるので別の記事にしたいです!同じようなことを考えていたので、学びの再確認になりました。
個人的な感想は残っていないのがすごく学びになった発表
メモには本当に発表内容のまとめしかしてないので、タイトルだけ記しておきます。 - 既存Railsアプリ攻略法 - CTOが見ること・やること・考えること - 外部サービス連携の設計 - メトリクス可視化から始める Rails ウェブサーバーのチューニング
2023年
コードカバレッジ計測ツールを導入したらテストを書くのが楽しくなった話
https://youtu.be/54sOl5qQMK0?si=D1mHxbTvQifV7KG7
これは本当にタイムリーだなと思いました。 ちょうど勤務先で通常タスクの合間にテストの拡充をしていく業務をやっていて、既存のテストのスタイルが全然違うので、ある程度ルール化したほうがいいなと思っていました。
ルールの指標の一つとして、「どこまでテストを書くか」みたいなことは重要だと思います。
例えば自分の場合、リクエストスペックでインスタンス変数まで見る必要はないし、逆にリクエストスペックでカバーできているサービスクラスの詳細までテストする必要はないと考えています。
ですが、一方でサービスクラスのテストが書きたくなるときもありますし、そういうテストを見かけるときもあります。
この辺を言語化するのって難しいです。
こちらの発表で、「カバレッジという指標は4種類ある」ということを初めて知ったので、このカバレッジに基づいて考えるというのも1つの方針として良いなと思いました。
業務に落としたい時は、バグを起こしやすい部分だったり、バグがクリティカルな障害に繋がる部分だったり、そういった複合的な要素を鑑みる必要がありますね。
定数参照のトラップとそれを回避するために行う2つのこと
https://youtu.be/DprMyMethws?si=o89_tcQWCNmceH1h
本筋とは違うんですが、なぜapp配下のrequireが不要なのかという話はたしかにと思いました。
config.autoload_pathsに設定されたものは自動読み込みになるようです。
ちょうどこの辺は業務でnamespaceの仕切り直しを行った時に軽くハマったので、今後も意識したいところです。
以下は特に感想はなかったんですが、また機会があれば見返そうと思います