Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails 整頓術 ActiveSupport::Concern と委譲

Avatar for Loose Loose
August 03, 2025

Rails 整頓術 ActiveSupport::Concern と委譲

Avatar for Loose

Loose

August 03, 2025

Other Decks in Programming

Transcript

  1. AI コーディングにて Claude Code で、あるプロダクトの User モデルに変更を加えようとしたとき > User からxxx

    関連のロジックを Concern に抽出してください。 • Read(app/models/user.rb) ⎿ Error: File content (28761 tokens) exceeds maximum allowed tokens (25000). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content. Fat Model は AI にも優しくない 分割統治はトークン消費量という面でも有効な可能性あり 4
  2. ActiveSupport::Concern ActiveSupport::Concern はクラスからロジックを module として切り出すための便 利機能 # app/models/user.rb class User

    < ApplicationRecord include Friendable include Subscriptable : end # app/models/user/friendable.rb module User::Friendable extend ActiveSupport::Concern included do has_many :friendships has_many :friends, through: :friendships scope :with_friends, -> { includes(:friends) } end def add_friend(other_user) return false if self == other_user friendships.create(friend: other_user) end def friends_with?(other_user) friends.include?(other_user) end end Rails: 私の好きなコード(3 )" 正しく書かれた" concerns (翻訳)|TechRacho by BPS 株式会社 37signals Dev — Good concerns 5
  3. ActiveSupport::Concern の注意点 複数の Concern が 1 つのクラスに include されたとき、他の Concern

    の private メソッドが参照できてしまう → private メソッドにも関わらず、どこか ら参照されているか(参照されていない か)を確認するのに手間がかかる module Foo extend ActiveSupport::Concern private def foo = "foo" end module Bar extend ActiveSupport::Concern def bar = foo end class MyClass include Foo, Bar end MyClass.new.bar #=> "foo" 7
  4. 委譲 Ruby には Delegator と Forwardable 、 Rails (ActiveSupport )には

    delegate がある。 しかし、ここでの委譲は一般的なプログ ラミングテクニックとしてのもの。 特定の public メソッドと、そこから参照 される一連の private メソッドをグルー プ化し、PORO としてまとめる。 # app/models/user.rb class User < ApplicationRecord def subscribe(plan) SubscriptionCreator.new(self, plan).run end end # app/models/user/subscription_creator.rb class User::SubscriptionCreator def initialize(user, plan) @user = user @plan = plan end def run # private メソッド呼び出し end end 37signals Dev — Vanilla Rails is plenty 8
  5. 委譲のメリット 実装の詳細を隠蔽できる 委譲先クラスの private メソッドはクラス 外から参照されない class User::SubscriptionCreator def initialize(user,

    plan) @user = user @plan = plan end def run create_subscription send_notification log_activity end private # これらのメソッドは User::SubscriptionCreator 内でのみ使用される def create_subscription # ... end def send_notification # ... end def log_activity # ... end end 9
  6. 分割統治の効果(イメージ) Before app/models/ └── user.rb (581 行) spec/models/ └── user_spec.rb

    (1200 行) After app/models/ ├── user.rb (70 行) └── user/ ├── friendable.rb (90 行) ├── messageable.rb (76 行) : ├── achievable.rb (14 行) └── achievable/ └── granter.rb (45 行) spec/models/ ├── user_spec.rb (156 行) └── user/ ├── friendable_spec.rb (320 行) ├── messageable_spec.rb (195 行) : ├── achievable_spec.rb (39 行) └── achievable/ └── granter_spec.rb (132 行) 11
  7. まとめ 1. ActiveSupport::Concern クラスからロジックを module として切り出せる 元モデルの特徴を凝集した形で抽出 2. 委譲 特定の

    public メソッドとそこから参照される private メソッドをグループ化でき る 実装の詳細を隠蔽 13