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

Railsの気持ちを考えながらコントローラとビューを整頓する/tidying-rails-co...

 Railsの気持ちを考えながらコントローラとビューを整頓する/tidying-rails-controllers-and-views-as-rails-think

RailsTokyo #3 での発表資料です

Avatar for MOROHASHI Kyosuke

MOROHASHI Kyosuke

February 19, 2026
Tweet

More Decks by MOROHASHI Kyosuke

Other Decks in Programming

Transcript

  1. ίϯτϩʔϥͱϏϡʔ w 'BU$POUSPMMFSΑ͘ͳ͍ΑͶɺ͘Β͍͔͠ޠΒΕ͍ͯͳ͍ w ແيಓʹ'BUͳίϯτϩʔϥ͸Α͘ͳ͍ w ωΨςΟϒදݱ ͳ ର৅ ͸Α͘ͳ͍ɻͦΕ͸ͦ͏

    w ίϯτϩʔϥͷ੹຿͚ͩ΍ΔίϯτϩʔϥͳΒ͹Α͍ͷͰ͸  w ϙδςΟϒදݱ ͳ ର৅ ͳΒΑ͍ͷͰ͸ ͦΕ͸ͦ͏ w ͋Μ·ΓޠΒΕͯ͜ͳ͔ͬͨͱ͜ΖͳͷͰߟ͑ͯΈ͍ͨ
  2. վળલϩʔυͨ͠σʔλͷݖݶΛνΣοΫ͢Δ class IssuesController < ApplicationController def update @project = Project.find_by(name:

    params[:project_name]) member_ids = @project.memberships.pluck(:person_id) unless member_ids.include?(current_person.id) render plain: 'ͩΊͩΑʔ' return end @issue = Issue.find(params[:id]) unless @issue.project_id == @project.id render plain: 'ͩΊͩΑʔ' return end @issue.update!(params.expect(issue: %i(title description status))) # ...
  3. վળޙΞΫηεՄೳͳू߹͔Βର৅Λ୳͢ class IssuesController < ApplicationController def update @project = current_person.projects.find_by!(name:

    params[:project_name]) @issue = @project.issues.find(params[:id]) @issue.update!(params.expect(issue: %i(title description status))) # ... rescue ActiveRecord::RecordNotFound render plain: 'ͩΊͩΑʔ' end end
  4. rails-tokyo-sample(dev):001> Project.first.issues.class.ancestors Project Load (0.1ms) SELECT "projects".* FROM "projects" ORDER

    BY "projects"."id" ASC LIMIT 1 /*application='RailsTokyoSample'*/ => [Issue::ActiveRecord_Associations_CollectionProxy, Issue::GeneratedRelationMethods, ApplicationRecord::GeneratedRelationMethods, ActiveRecord::Delegation::ClassSpecificRelation, ActiveRecord::Associations::CollectionProxy, ActiveRecord::Relation, ActiveRecord::SignedId::RelationMethods, ActiveRecord::TokenFor::RelationMethods, ActiveRecord::FinderMethods, ActiveRecord::Calculations, ActiveRecord::SpawnMethods, ... ࠓ೔͸ͦͬͪ۷Γ·ͤΜ͕ɺ*TTVF"DUJWF3FDPSE@"TTPDJBUJPOT@$PMMFDUJPO1SPYZͱ͔΋໘ന͍
  5. ෳࡶͳؔ܎ΛUISPVHIͷUISPVHIͰදݱͰ͢Δ class Assignment < ApplicationRecord belongs_to :issue belongs_to :person end

    w *TTVFͷ୲౰ऀΛBTTJHO͢ΔέʔεΛߟ͑Δ w ૉ๿ʹCFMPOHT@UPQFSTPO͢Δͱྑ͘ͳ͍ w QFSTPO͕཭೚͢Δͱ͖ʹ΋ɺQFSTPOࣗମΛফ͢Θ͚ʹ͸͍͔ͳ͍ w ͢ΔͱBTTJHONFOUΛݸผʹফ͢ඞཁ͕͋Δ ͳΜ͔ΊΜͲ͍͘͞
  6. class Issue < ApplicationRecord has_many :assignments has_many :memberships, through: :assignments

    has_many :assignees, through: :memberships, source: :person end class Assignment < ApplicationRecord belongs_to :issue belongs_to :membership end class Membership < ApplicationRecord belongs_to :person belongs_to :project has_many :assignments, dependent: :destroy end UISPVHIΛUISPVHI͢Δ
  7. rails-tokyo-sample(dev):002> project.issues.includes(:assignees) .map { [it, it.assignees.size] } Issue Load (0.3ms)

    SELECT "issues".* FROM "issues" WHERE "issues"."project_id" = 152639770 /*application='RailsTokyoSample'*/ Assignment Load (0.1ms) SELECT "assignments".* FROM "assignments" WHERE "assignments"."issue_id" IN (77551669, 540637335) / *application='RailsTokyoSample'*/ Membership Load (0.1ms) SELECT "memberships".* FROM "memberships" WHERE "memberships"."id" IN (1010729577, 996949822, 664416693) / *application='RailsTokyoSample'*/ Person Load (0.1ms) SELECT "people".* FROM "people" WHERE "people"."id" IN (786122151, 902541635, 663665735) /*application='RailsTokyoSample'*/
  8. rails-tokyo-sample(dev):003> project.issues.eager_load(:assignees) .map { [it, it.assignees.size] } Issue Eager Load

    (0.4ms) SELECT "issues"."id" AS t0_r0, "issues"."created_at" AS t0_r1, "issues"."creator_id" AS t0_r2, "issues"."description" AS t0_r3, "issues"."project_id" AS t0_r4, "issues"."status" AS t0_r5, "issues"."title" AS t0_r6, "issues"."updated_at" AS t0_r7, "people"."id" AS t1_r0, "people"."created_at" AS t1_r1, "people"."updated_at" AS t1_r2 FROM "issues" LEFT OUTER JOIN "assignments" ON "assignments"."issue_id" = "issues"."id" LEFT OUTER JOIN "memberships" ON "memberships"."id" = "assignments"."membership_id" LEFT OUTER JOIN "people" ON "people"."id" = "memberships"."person_id" WHERE "issues"."project_id" = 152639770 /*application='RailsTokyoSample'*/
  9. w "DUJWF3FDPSE3FMBUJPO͸ɺ3%#ͷؔ܎୅਺తͳૢ࡞ બ୒XIFSF ࣹӨTFMFDU ݁߹KPJO Λ3VCZΦϒδΣΫτͱͯ͠ѻ͑Δ w ཧ࿦ͦͷ΋ͷͰ͸ͳ͍͕ɺయܕతͳ8FCΞϓϦͰ΍Γ͍ͨ͜ͱ͸͍ͩͿͰ͖Δ w 3FMBUJPOಉ࢜ͷνΣʔϯ΍NFSHFͱ͔Ͱ͖Δͷ͘͢͝ͳ͍Ͱ͢

     w ͜ͷϛϥΫϧ🦄ͳ"DUJWF3FDPSEΛ࢖͍౗͠·͠ΐ͏ w ʮͪΌΜͱͨ͠ςʔϒϧߏ଄ʯΛఆٛͯ͠ɺͦΕΛਏ͘ͳ͘׆༻Ͱ͖Δ ͜ͷΞϓϩʔνͷ3BJMTͷؾ࣋ͪ ͦ͏͸͍ͬͯ΋ूܭΫΤϦΑɺɺɺΈ͍ͨͳͷ͸͋ΔΜͰ͕͢ɺ͋Ε͸ʮΞΫςΟϒϨίʔυύλʔϯʯΛ ద༻͠ͳ͍ํ͕ྑ͍ϑΥʔε͕͋ΔΘ͚ͳͷͰɺ͞ΒʹผʹΞϓϩʔνͷ΄͏͕ྑ͍ͱࢥͬͯ·͢ɻ
  10. ίϯτϩʔϥͷΠϯελϯεม਺ΛݮΒ͢ ͜͜Βล͔Βࢥ૝͕ڧ͘ͳ͖ͬͯ·͢ɻภΓ͕͋Δ࠙਌ձ΍ΠϯλʔωοτͰײ૝Λฉ͔͍ͤͯͩ͘͞ɻ class IssuesController < ApplicationController def index @project =

    current_person.projects.find_by!(name: params[:pj_name]) @issues = @project.issues.order(updated_at: :desc) # ͜͏͍͏΍ͭΛݮΒ͍ͨ͠ @news = @project.news.order(updated_at: :desc).limit(3) end w αΠυόʔͷ߲໨ͳͲɺΞΫγϣϯͷຊ෼Ͱ͸ͳ͍σʔλ͸ ʮίϯτϩʔϥͷΠϯελϯεม਺ʯʹ͸͠ͳ͍ɻ
  11. ίϯτϩʔϥͷJWBS͸$7ͷΠϯλʔϑΣʔε w લఏͱͯ͠ɺ3BJMTίϯτϩʔϥͷΠϯελϯεม਺͸ɺ $Ͱ༻ҙͨ͠σʔλΛ7ʹ౉͢ΠϯλʔϑΣʔεͰ͋Δ w ͍ΘΏΔڱٛͷ001ʹ͓͚ΔΠϯελϯεม਺ͱ͸ɺͱ͔ɺ .7$  ͷίϯτϩʔϥͱ͸ɺΈ͍ͨͳ͜ͱΛߟ͑͗͢ͳ͍ w

    ΤϯτϦϙΠϯτͱͳΔϝιου ΞΫγϣϯ ͕ҧ͏ͱɺηοτ͞ΕΔΠϯελϯεม਺ͷ छྨࣗମ͕มΘΔͱ͔ɺ001ͱͯ͠͸μ ϝͳײ͕͢͡ΔX w 3BJMTͷ͜ͱΛߟ͑·͠ΐ͏ 3BJMTͱ઀ଓͨ͠ઌͷɺϞσϧͷϩδοΫΛॻ͘ͱ͖ͳΜ͔͸ɺ΋ͪΖΜ001ͷྑ͍ݪଇΛଚॏ͠·͠ΐ͏ɻ ݴ͍׵͑Δͱɺ͜ͷ઀໘Λհͯ͠3BJMTੈքͱυϝΠϯքΛڥք͚ͮΔͱ͍͏͔
  12. ϏϡʔͰσʔλΛϩʔυ͢ΔͷΞϦ🐜 w ϏϡʔͰσʔλΛϩʔυ͢ΔʺϏϡʔʹ42-Λॻ͘ w ੜ42-͸͕͢͞ʹಡΈͮΒ͍͔Βμϝͩͱࢥ͍·͢🍐 w Ͱ΋͋͘·ͰϓϥΫςΟΧϧͳಡΈਏ͞ͷ໰୊Ͱ͋Γɺ࡞๏ͷ࿩Ͱ͸ͳ͍ <% query =

    'SELECT * FROM news WHERE ...ORDER BY created_at DESC LIMIT 3' %> <% ActiveRecord::Base.connection.execute(query).each do |row| %> ... <% end %> ͜ͷʮϏϡʔʹ42-ॻ͘ͳʯ Θ͔Δ ͕ͩΜͩΜͱʮϏϡʔͰ%#ʹΞΫηε͢Δͳʯʹͳͬͨͷ͔ͳͱࢥͬͯ·͢ɻ ·ͨ͸ϨΠϠ෼ׂΞʔΩςΫνϟͷӨڹ͔ɻͰ΋.7$ҰൠͰ͸ͳ͘ɺ3BJMTݸผͷ࿩ͳͷͰɺͱ͍͏ͷ͕࿦ࢫͰ͢
  13. "DUJWF3FDPSEͷϝιουνΣʔϯΛॻ͘ͷ͸ΞϦ🐜 w ϞσϧʹQVCMJTIFEͳͲͷTDPQFΛఆٛͯ͠࢖ͬͨΓɺPSEFS ΍ MJNJU ͳͲ"3ͷϝιουΛݺΜͩΓ͢Δͷ͸͋Γ w νΣΠϯ͕௕ͯ͘ಡΈͮΒ͘ͳͬͨΒɺదٓ͞ΒʹTDPQF΍ ΫϥεϝιουΛఆٛͨ͠Γɺϔϧύʔʹநग़ͨ͠Γ͢Δ <%

    project.news.published.order(created_at: :desc).limit(3).each do |news| %> ... <% end %> நग़ઌͱͯ͠ɺίϯτϩʔϥ΋3VCZίʔυΛॻ͖΍͍͢ͷͰͦ͜Ͱ͍͍͡ΌΜɺͱ͍͏ͷ͸ҰपճͬͯΞϦ͔΋ɻ ผϝιουʹͯ͠IFMQFSએݴͰ࢖͑ΔΑ͏ʹ͢Δͱ͔͸ྑ͍ͱࢥ͍·͢ɻOFXTͰ΍Δ͜ͱ͡Όͳ͍͚Ͳɻ
  14. ͦ΋ͦ΋"33FMBUJPOͷΫΤϦ͸MB[ZʹධՁ͞ΕΔ w QSPKFDUOFXTQVCMJTIFE ͷ࣌఺Ͱ͸·ͩ42-͸ൃߦ͞Εͣɺ "DUJWF3FDPSE3FMBUJPO͕ฦΔ w FBDI ͰϧʔϓΛճ࣌͢఺ͰॳΊͯ42-͕ൃߦ͞ΕΔ w ͜Ε͸ɺίϯτϩʔϥͰJWBSʹೖΕͯ΋ಉ͜͡ͱ

    w ΫΤϦൃߦΛΘ͟ͱMB[Zʹ͢Δ3BJMTͷҙਤΛײ͡ΔͷͰɺ ࢖͏ଆ΋ؤுͬͯؾʹ͠ͳ͍Α͏ʹ͍ͨ͠ ͦΕΛආ͚Δʹ͸ίϯτϩʔϥͰUP@B͢Δͱ͔ʹͳΔ͚ͲɺͦΕ͸΍Γͨ͘ͳ͍͡Όͳ͍Ͱ͔͢
  15. ڞ௨ύʔπΛίϯϙʔωϯτʹ͢Δ # app/views/projects/_news.html.erb <%# locals: (project:) %> <ul> <%# projects

    ͷΈΛೖྗʹͯ͠ɺ͔ͦ͜ΒඞཁͳnewsΛҾ͍ͯϨϯμϦϯά͢Δ %> <% project.news.published.order(created_at: :desc).limit(3).each do |news| %> <li> <%= link_to news.title, project_news_path(project, news) %> </li> <% end %> </ul>
  16. class IssuesController < ApplicationController def index @project = current_person.projects.find_by!(name: params[:pj_name])

    # HTMLදࣔ༻ͷؔ࿈σʔλ·ͰίϯτϩʔϥͰϩʔυ @issues = @project.issues .includes(creator: :avatar) # HTML༻ .order(updated_at: :desc) end end # app/views/issues/index.html.erb <% @issues.each do |issue| %> <div> <%= image_tag issue.creator.avatar.url %> <%= issue.title %> </div> <% end %> ͜͏͍͏ͷΛ
  17. class IssuesController < ApplicationController def index @project = current_person.projects.find_by!(name: params[:pj_name])

    # ίϯτϩʔϥ͸࠷খݶɻAR::Relationͷ··౉͢ @issues = @project.issues.order(updated_at: :desc) end end # app/views/issues/index.html.erb <% @issues.includes(creator: :avatar).each do |issue| %> <div> <%= image_tag issue.creator.avatar.url %> <%= issue.title %> </div> <% end %> ͜͏͍ͨ͠
  18. දࣔ͢Δؔ࿈ઌΛ൑அ͢Δͷ͸Ϗϡʔͷ੹຿ w JTTVFͷىҊऀͰ͋ΔQFSTPOͷΞΠίϯը૾Λग़͔͢Ͳ͏͔ɺΛ ൑அ͢Δͷ͸Ϗϡʔͷ੹຿Ͱ͋Δ w )5.-දݱʹ͓͍ͯ͸ग़͕͢ɺ+40/දݱͰ͸ग़͞ͳ͍ɺͳͲ w !JTTVFTJODMVEFT DSFBUPSBWBUBS ΛͲ͜Ͱݺͼग़͔͢

    w ίϯτϩʔϥͰݺͿͱશͯͷςϯϓϨʔτͰಉ͡JODMVEFT͕ద༻͞Εͯ͠·͏ w ϏϡʔͰݺͿͱɺ֤දݱͰඞཁͳ΋ͷΛJODMVEFͰ͖Δ ੲ͸࢖͍͚ͬͯͨͲϏϡʔΛม͑Δ͏ͪʹෆཁʹͳͬͨJODMVEF͕࢒͍ͬͯͯɺ๨ΕͨࠒʹύϑΥʔϚϯε ѱԽͷݪҼʹͳͬͨɺΈ͍ͨͳܦݧ͋Δਓ΋ଟ͍͔ͱࢥ͍·͢ɻࢲ΋͍ͬͺ͍͋Γ·͢X
  19. “ 設計 決定 際 、 共有 知識 、 知識 移動

    距離 注意 払 必要 。(略) 最終的 、 結合 影響 注意 。近接 互 影響 与 。 7MBE,IPOPOPWʮιϑτ΢ΣΞઃܭͷ݁߹όϥϯεʯ ୈষڑ཭
  20. ੔಴͸ՄѪͯ͘;Θ;Θͨ͠খ͞ͳϦϑΝΫλϦϯά w ͜ͷ࿩Λؾʹೖͬͯ͘Εͯ΋ɺҰؾʹશ෦΍Ζ͏ͱ͠ͳ͍Ͱ⚠ w ةͳ͍͔ΒͶ w ίϯτϩʔϥͷɺΞΫγϣϯʹɺUJQT͚ͩΛద༻͢Δ w ͦΕͰɺ໨ͷલͷιϑτ΢ΣΞ͕গͣͭ͠੔಴͞Ε͍ͯ͘ w

    ͦΕ΋·ͨEZOBNJDͳ׆ಈͳͷͩͱࢥ͍·͢ ͱɺ"1*ͳͲ͔Β3BJMTͷؾ࣋ͪΛ উखʹ ಡΈͱΓͭͭɺΞϓϦέʔγϣϯ͕͍͍ײ͡Ͱ͋ΕΔΑ͏ʹ೔ʑ ੔಴ͯ͠म෮͠ଓ͚Δͷ͕ʮࢲͷ3BJMTͷ࿩ʯͰ͢ɻ஍ຯͰ͚͢Ͳ΋΍ͬͯΈΔͱ໘ന͍Ͱ͢Α