Slide 1

Slide 1 text

Rubyで鍛える仕組み化 プロヂュース力 2025/12/06 (Sat) 北陸 Ruby 会議 01 @ 石川県立図書館 muryoimpl

Slide 2

Slide 2 text

本日のおしながき - 自己紹介 - 私にとっての Ruby - 仕組み化どうやっている? - どうやって思いつくのか? - 具体的な実装例

Slide 3

Slide 3 text

自己紹介 - 名 前: muryoimpl - 居住地: 石川県金沢市 - Kanazawa.rb によく出没している - LT 大会向け Slack slash command 「kzlt」の創 造主兼管理者 - https://github.com/muryoimpl/kzlt-ruby

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

https://kzrb.doorkeeper.jp/events/193290

Slide 6

Slide 6 text

https://zenn.dev/muryoimpl

Slide 7

Slide 7 text

本日のおしながき - 自己紹介 - 私にとっての Ruby - 仕組み化どうやっている? - どうやって思いつくのか? - 具体的な実装例

Slide 8

Slide 8 text

私にとっての Ruby - システムの第一言語 - 仕事でもプライベートでも使っている - 汎用的で多くの場面で使える - GitHub Actions の image にもインストールされている - https://github.com/actions/runner-images

Slide 9

Slide 9 text

私にとっての Ruby - Monkey patch で上書き自在 - 直接 gems を書き換える必要がない - リポジトリを fork せずとも変更可能 - クラス、モジュール、オブジェクトを抽出しやすい - 親クラスだけでなく、比較的簡単に子クラスも取得できる

Slide 10

Slide 10 text

Ruby で鍛える 仕組み化プロヂュース

Slide 11

Slide 11 text

本日のおしながき - 自己紹介 - 私にとっての Ruby - 仕組み化どうやっている? - どうやって思いつくのか? - 具体的な実装例

Slide 12

Slide 12 text

普段、仕事では 趣味で仕組み化をしている

Slide 13

Slide 13 text

私のいう「仕組み化」とは 作業やその流れを 自分なりに、もしくは、チームとして定義し 、そ の作業が全自動、もしくは、半自動でスムーズに流れるようにす ること。 人のする作業であっても、システムが介在することでスムーズに なるならば、これもその範疇とする。

Slide 14

Slide 14 text

なぜ仕組み化する?

Slide 15

Slide 15 text

なぜ「仕組み化」するのか - 人の考えたり行動したりする負荷が減る💡 - 覚えておかなければいけない -> 不要になる - 確実に実施しなければいけない -> 不要になる - 暗黙知から形式知にできる - コード化することで共通認識を作ることができる - ヌケモレ防止、実行の一貫性確保

Slide 16

Slide 16 text

何を使って仕組み化する?

Slide 17

Slide 17 text

どのような手段で「仕組み化」するのか - Custom Cops - https://github.com/rubocop/rubocop - https://github.com/rubocop/rubocop-rails - Rake tasks or Thor - https://github.com/rails/thor - GitHub Actions ( Rake tasksや Thor を実行する ) - (Ruby 以外だと) actions/github-script - https://github.com/actions/github-script

Slide 18

Slide 18 text

どう使い分けているか - Custom Cops - コードを書くうえで注意すべきこと - Rake tasks or Thor - コード上だけではどうにもできないもの (例: DB) - Thor は引数オプションつけたいときに使う

Slide 19

Slide 19 text

どう使い分けているか - GitHub Actions - 全部この上で動かしている - step 上で Ruby のワンライナー書いたりもしてる - ワークフロー自体に作用するものは + Slack 通知 - actions/github-script - GitHub 上の状態はほぼすべて取得できるので、状態の変化で 何かをさせたいときはこれを使う

Slide 20

Slide 20 text

何を対象にしている?

Slide 21

Slide 21 text

何を対象に仕組み化しているか - 開発のワークフローにある些事を対象にしている - できるだけ、さくっと実装できる範囲 - 時間かかるだけ利益の享受が遅れる - 簡易実装して時間を稼ぐ - できれば複数人いるプロジェクトを対象にするのがよい - ニーズを自分以外からも獲得できる - 仕組み化した後の答え合わせができる

Slide 22

Slide 22 text

本日のおしながき - 自己紹介 - 私にとっての Ruby - 仕組み化どうやっている? - どうやって思いつくのか? - 具体的な実装例

Slide 23

Slide 23 text

どうやったら思いつくのか - レビュー時に困ったことを解決できないか考える - 実はすでにみんな困り事には気づいている - だいたいこれ - ただし実装するとは言っていない => あとはやるだけ - 指示して AI に作ってもらう

Slide 24

Slide 24 text

どうやったら思いつくのか - 一発で格好よく解決しようという考えを捨てる - まず実利を得ることを考える - Custom Cop でAuto Correct が難しいなら まず警告を出す - それも難しければ test を書いてひっかける - 指示して AI に作ってもらう

Slide 25

Slide 25 text

どうやったら思いつくのか - 具体的なものには個別・具体的な対策でもOK - まず問題の箇所に対して簡単に蓋をして塞ぐのが大事 - 蓋をして止めている間に、考え、実装する - 抽象度を高めて汎用的にする - gem として適用範囲を拡大する

Slide 26

Slide 26 text

どうやったら思いつくのか - 法則をみつける or 法則をでっちあげる - 型( Object#is_a?, Object#kind_of? ) - ActiveSupport::DescendantsTracker.descendants - RuboCop::NodePattern::Macros#def_node_search, def_node_matcher

Slide 27

Slide 27 text

法則を見出す - 特定のクラスを抽出する場合 - 特定のクラスを継承したクラス Rails.application.eager_load! ApplicationRecord.descendants

Slide 28

Slide 28 text

法則を見出す - 特定のクラスを抽出する場合 - 特定のmodule を include したクラス Rails.application.eager_load! ObjectSpace.each_object(Class).select do |klass| klass.included_modules.include?(module) end クラスの配列が返る

Slide 29

Slide 29 text

法則を見出す - 特定のクラスを抽出する場合 - 特定のmodule を extend したクラス Rails.application.eager_load! ObjectSpace.each_object(Class).select do |klass| klass.singleton_class.ancestors.include?(module) end クラスの配列が返る

Slide 30

Slide 30 text

法則を見いだせたら もう勝ちに等しい

Slide 31

Slide 31 text

作るときに考えること

Slide 32

Slide 32 text

作るときに考えること - 早いの?うまいの?安いの? - さくっとできる?効果は十分?追加のインフラかかる? - Generator や Snippet はちょっと微妙… - 最初は使われるが、慣れると皆他からコピペしてくる - AI さんはちゃんと実装したがる - 過剰に品質を気にしすぎる傾向があるので適度なところでやめ ておく

Slide 33

Slide 33 text

本日のおしながき - 自己紹介 - 私にとっての Ruby - 仕組み化どうやっている? - どうやって思いつくのか? - 具体的な実装例

Slide 34

Slide 34 text

簡単な実装例( 1) 1. ApplicationRecord.ignored_columns に無視するカラムが登録さ れっぱなし、もしくは、宣言だけ残っていてカラムは削除されている ものがないかチェックする rake task - ApplicationRecord.descendants を使ってテーブルに対応したモデ ルのみを抽出している - すぐカラム削除する PR 作っておけばいいじゃないって思うでしょ? それでも発生するから作った

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

簡単な実装例( 2) 2. CSVにいれる BOM の定数宣言があちこちに出てきたので 警告を出す Custom Cop を追加 - すべての BOM の宣言が生まれる前に消し去るために、Custom Cop 作って穴塞いだ後に他の宣言を消した

Slide 37

Slide 37 text

※対象のファイルについては、.rubocop.yml で指定している

Slide 38

Slide 38 text

簡単な実装例( 3) 3. 新しくリリースされた Ruby の image が公開されているかどうかを 確認する GitHub Actions - renovate が Dockerfile の RUBY_VERSIONを更新する PR を作っ たときに、container image がリリースされていないとマージボタン 押せないようにするための仕組み

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

簡単な実装例( 4) 4. ActiveRecord のモデルに has_one/has_many が設定されて おり、かつ、dependent option 設定されているかチェック するrake task - rubocop-rails の Rails/HasManyOrHasOneDependent は has_one/has_many を書かないとチェックできないので、 belongs_to だけ書かれていても引っ掛けられるようにした

Slide 41

Slide 41 text

簡単な実装例( 4) - ActiveSupport::DescendantsTracker.descendants の存在に気が ついていなかったため、ApplicationRecord.connection.tables で テーブル取得して table_name からモデルクラス名を組み立ててい た - ↑を処理するクラスを作って、そのテストという建付けでチェックする ようにした(蓋をした)

Slide 42

Slide 42 text

簡単な実装例( 4) - モデル名とテーブル名が異なる(self.table_name=)場合や、関連の キーがテーブル名と不一致の場合があるため、それらに対応して gem 化した - https://github.com/muryoimpl/dependent_option_checker

Slide 43

Slide 43 text

簡単な実装例( 4) - 具体的な内容については、zenn.dev に暫定対応時の記事と、gem 化したときの記事があるのでこちらをみてください - https://zenn.dev/ashita_team/articles/36bc5e77651985 - https://zenn.dev/ashita_team/articles/ed08141e3110e2

Slide 44

Slide 44 text

まとめ

Slide 45

Slide 45 text

まとめ - (半) 自動化で人の負荷が減る、暗黙知から形式知にできる - 要求はあなたの中に既にある、一歩踏み出して実装すべし - 一発解決に時間かかるなら段階を踏んで解決してもいい - 法則を見出すと勝ち。見出せないならでっちあげよ - 些事には意外と少ない行数で対応できるので、レビューの隙間を埋 めてみて