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

実践 Rails アソシエーションリファクタリング / Rails association refactoring in practice

実践 Rails アソシエーションリファクタリング / Rails association refactoring in practice

Kei Shiratsuchi

October 22, 2022
Tweet

More Decks by Kei Shiratsuchi

Other Decks in Technology

Transcript

  1. ࣮ફ


    Rails ΞιγΤʔγϣϯ


    ϦϑΝΫλϦϯά
    ന౔ܛ, Kei Shiratsuchi, @kei_s
    Kaigi on Rails 2022, 2022.10.22(Sat)

    View Slide

  2. Πϯτϩ
    • Day 1 ͷ @pndcat ͞ΜʹΑΔʰ৿ཏສ৅ʹʮ͍͍Ͷʯ͢ΔͨΊͷ
    σʔλߏ଄ ʱͷɺٯํ޲ͷϦϑΝΫλϦϯάͷ࿩Λ͠·͢


    • ϙϦϞʔϑΟοΫؔ࿈Λதؒςʔϒϧ΁


    • ͲͪΒ΋ɺཧ༝͕͋ͬͯҙຯ͕͋Δɺͱ͍͏͜ͱΛ࣋ͬͯ
    ؼ͍͚ͬͯͨͩΕ͹ 🙏


    • ͲͪΒ΋࠾༻͍͍ͯͨͩͨ͠ Kaigi on Rails νʔϜ͍͢͝ 👏

    View Slide

  3. ࣗݾ঺հ
    • ന౔ɹܛʢγϥπνɹέΠʣ


    • : kei-s, : @kei_s


    • ANDPAD גࣜձࣾ Tech Lead


    • ॳΊͯ rails new ͔ͯ͠Β 15 ೥͘Β͍


    • ࠓ೔͸ Kaigi on Rails ͷୗࣇαϙʔτΛར༻ͤͯ͞΋Β͍ͬͯ·͢


    • ͱͯ΋ྑ͍औΓ૊ΈͰײँ͍ͯ͠·͢ʂ

    View Slide

  4. എܠ

    View Slide

  5. લఏ
    • ANDPAD : ݐஙɺݐઃۀ޲͚ͷ SaaS


    • த৺ͱͳΔҰͭͷ Rails ΞϓϦͰɺෳ਺ͷۀ຿υϝΠϯͷόο
    ΫΤϯυΛఏڙ͍ͯ͠Δ


    • ࢪ޻؅ཧɺݕࠪɺνϟοτɺetc…


    • ͦΕͧΕͷυϝΠϯʹ։ൃνʔϜ͕͋Δ


    • ͦΕΒͷνʔϜ͕Ұͭͷ Rails ΞϓϦʹରͯ͠ػೳ։ൃͯ͠
    ͍Δ

    View Slide

  6. എܠ
    • ෳ਺ͷυϝΠϯͷػೳ͕૬৐Γ͍ͯ͠ΔͨΊɺRails ΞϓϦ͸͔ͳΓେن໛ʹͳͬ
    ͍ͯΔ


    • Day 1 ͷ makicamel ͞ΜͷൃදΛࢀর͍ͩ͘͞


    • ͜ͷ Rails ΞϓϦͰɺϙϦϞʔϑΟοΫؔ࿈͕ଟ༻͞Ε͍ͯΔ


    • ಛʹʮࣸਅʯ΍ʮࢿྉʯͳͲɺ͍ΘΏΔετοΫσʔλͷؔ࿈෇͚ʹ༻͍Β
    Ε͍ͯΔ


    • ͜ΕΒͷσʔλ͸ɺυϝΠϯΛލ͍Ͱར༻͞Ε͍ͯΔ


    • ͋ΔυϝΠϯͰ࡞੒͞ΕͨσʔλΛɺผͷυϝΠϯ͔ΒࢀরͰ͖Δ


    • ྫɿࢪ޻؅ཧͰΞοϓϩʔυͨࣸ͠ਅΛɺνϟοτʹషΓ෇͚Δ

    View Slide

  7. ϙϦϞʔϑΟοΫؔ࿈ͱ͸
    • ҰͭͷϞσϧΛෳ਺ͷϞσϧʹඥ͚ͮΔ


    • *_type ΧϥϜʹ਌ϞσϧͷΫϥε໊Λ
    อଘ͢Δ


    • ར఺: ͋ΒΏΔϞσϧʹඥ͚ͮΔ͜ͱ͕
    Ͱ͖Δ


    • ઃܭ࣌ʹଘࡏ͠ͳ͔ͬͨϞσϧͱ΋
    ඥ͚ͮΒΕΔ


    • ܽ఺: DBͰ֎෦ΩʔΛઃఆͰ͖ͳ͍
    class Picture < ApplicationRecord


    belongs_to :imageable, polymorphic: true


    end


    class Employee < ApplicationRecord


    has_many :pictures, as: :imageable


    end


    class Product < ApplicationRecord


    has_many :pictures, as: :imageable


    end


    @employee.pictures #
    = >
    [#. .
    >,
    . .
    ]


    @product.pictures #
    = >
    [#. .
    >,
    . .
    ]

    View Slide

  8. Ұ൪ͷͭΒΈ
    • σʔλϞσϧͷॴ༗ʹϙϦϞʔϑΟο
    Ϋؔ࿈


    • σʔλϞσϧͷࢀরʹϙϦϞʔϑΟο
    Ϋؔ࿈


    • ສ෺ͱສ෺Λؔ࿈ͤ͞Δ͜ͱ͕Ͱ͖
    ͯศརʢຊ౰ʹ…???ʣ


    • ࣮ࡍʹ͸ɺॴ༗ͱࢀরͰ࢖͍෼͚Δϝ
    Ϧοτ͕ͳ͍
    ॴ༗
    ࢀর

    View Slide

  9. Կ͕ى͖͍͔ͯͨ
    • ؔ࿈෇͚ͷશ༰೺Ѳ͕೉͍͠


    • σʔλΛͲ͔͜Βར༻͍ͯ͠Δ͔͕໌ࣔతͰͳ͍


    • σʔλϞσϧଆʹఆ͕ٛͳ͘ɺಈ࡞ͷதͰ͔͠ݱΕͳ͍


    • σʔλΛར༻͢Δ৽ػೳͰɺ࠷దͳ࣮૷͕Θ͔Βͳ͍ͨ
    Ίطଘ࣮૷Λ౿ऻ͠ɺෳࡶ͕͞૿େ͍ͯ͘͠


    • υϝΠϯΛލ͍Ͱར༻͞Ε͍ͯΔͨΊมߋͷӨڹൣғ͕େ
    ͖͘ɺ֤υϝΠϯͷ։ൃνʔϜͰ͸मਖ਼͕೉͍͠

    View Slide

  10. ਅͷ՝୊
    • কདྷతʹɺେن໛RailsΞϓϦΛ

    υϝΠϯ͝ͱʹղମ͍͖͍ͯͨ͠


    • ۩ମతͳಓے͸ To Be Determined


    • υϝΠϯΛލ͍Ͱ࢖ΘΕΔσʔλ
    ͷѻ͍͸ඞͣ՝୊ʹͳΔ


    • શ༰͕೺ѲͰ͖ͳ͍ෳࡶͳ࣮૷Λ
    ղফ͢Δඞཁ͕͋Δ
    Rails ΞϓϦ
    ࢪ޻؅ཧ νϟοτ
    ࣸਅ
    Ϟσϧ Ϟσϧ
    ϚΠΫϩαʔϏεʁ
    ϞδϡϥϞϊϦεʁ

    View Slide

  11. ϦϑΝΫλϦϯάํ਑
    • ϙϦϞʔϑΟοΫؔ࿈Λɺதؒ
    ςʔϒϧʹΑΔؔ࿈෇͚ʹஔ͖׵
    ͑Δ


    • ؔ࿈෇͚Λ໌ࣔͰ͖Δ


    • ॻ੶ʮSQLΞϯνύλʔϯʯͰ΋
    ঺հ͞Ε͍ͯΔख๏
    class Photo < ApplicationRecord


    has_many :message_photos


    has_many :messages, through: :message_photos


    end


    class MessagePhoto < ApplicationRecord


    belongs_to :message


    belongs_to :photo


    end


    class Message < ApplicationRecord


    has_many :message_photos


    has_many :photos, through: :message_photos


    end

    View Slide

  12. େن໛ΞϓϦͳΒͰ͸ͷ೉఺
    • ϝϯςφϯελΠϜΛઃ͚ͣʹਐΊ͍ͨ


    • มߋՕॴ͕ଟ͍ͨΊɺ౎౓ఀΊΔͷ͸ඇݱ࣮త


    • Ҡߦதʹෆ۩߹͕ൃ֮ͨ͠Β੾Γ໭͍ͨ͠͠


    • ҠߦՕॴ͕໢ཏ͠੾Ε͍ͯΔ͔ෆ҆


    • ࣗಈςετͰΧόʔͰ͖͍ͯͳ͍Օॴ͕͋ΔͷͰ͸
    ͳ͍͔

    View Slide

  13. ϦϑΝΫλϦϯάͷ࣮ࢪ

    View Slide

  14. ҠߦܭըͰॏࢹ͢ΔϙΠϯτ
    • ແఀࢭͰߦ͏


    • ՄೳͳݶΓ҆શʹߦ͏


    • ͙͢ʹ੾Γ໭͕͠Ͱ͖ΔΑ͏ʹ͢Δ


    • → ৽چΞιγΤʔγϣϯͷಉ࣌ॻ͖ࠐΈ


    • Ҡߦ͖͠Δ


    • Ҡߦ࿙ΕΛ์ஔ͠ͳ͍͜ͱ


    • → ϓϩμΫγϣϯ؀ڥͰൈ͚࿙Ε͕ͳ͍͔ݕ஌

    View Slide

  15. ҠߦͷྲྀΕ
    • 4ճͷϦϦʔε🚀ɺ2ճͷσʔλमਖ਼📝Λߦ͏


    1. 🚀 ҠߦઌϞσϧ࡞੒ɺچˠ৽ͷಉ࣌ॻ͖ࠐΈ


    2. 📝 طଘσʔλΛ৽ΞιγΤʔγϣϯʹొ࿥


    3. 🚀 ৽ΞιγΤʔγϣϯʹஔ͖׵͑ɺ৽ˠچͷಉ࣌ॻ͖ࠐΈ


    4. 🚀 ൈ͚࿙Εݕ஌


    5. 🚀 ࢓্͛


    6. 📝 چσʔλΛ࡟আ

    View Slide

  16. 1. 🚀 ҠߦઌϞσϧ࡞੒ɺچˠ৽ͷಉ࣌ॻ͖ࠐΈ
    • ҠߦઌͱͳΔதؒςʔϒϧΛ࡞੒͢Δ


    • ৽ΞιγΤʔγϣϯΛɺچΞιγΤʔγϣϯͱ͸ผ໊Ͱੜ΍͢


    • after_create ίʔϧόοΫͰɺچΞιγΤʔγϣϯ͕࡞੒͞ΕͨΒ৽ΞιγΤʔ
    γϣϯΛಉ࣌ʹ࡞੒͢Δ


    • ඞཁʹΑͬͯɺߋ৽ɾ࡟আ͞ΕΔ৔߹΋ରԠ


    • 👉 ͜Ε͔Β௥Ճ͞ΕΔσʔλ͸৽چͰಉظ͞ΕΔ
    class Message < ApplicationRecord


    has_many :photos, as: :imageable


    has_many :message_photos


    has_many :renewed_photos, through: :message_photos,


    source: :photo


    end
    class Photo < ApplicationRecord


    belongs_to :imageable, polymorphic: true


    has_many :message_photos


    has_many :messages, through: :message_photos


    after_create :create_message_photos!,


    if:
    - >
    { imageable.is_a?(Message) }


    def create_message_photos!


    message_photos.create!(message: imageable)


    end


    end

    View Slide

  17. 2. 📝 طଘσʔλΛ৽ΞιγΤʔγϣϯʹొ࿥
    • چΞιγΤʔγϣϯʹΑΔطଘσʔλΛಡΈग़͠ɺ৽
    ΞιγΤʔγϣϯʹొ࿥͢ΔόονॲཧΛ࣮ߦ͢Δ


    • 👉 શͯͷσʔλ͕৽چͰಉظ͞ΕΔ

    View Slide

  18. 3. 🚀 ৽ΞιγΤʔγϣϯʹஔ͖׵͑ɺ৽ˠچͷಉ࣌ॻ͖ࠐΈ
    • ผ໊Ͱ࡞੒ͨ͠৽ΞιγΤʔγϣϯΛར༻͢ΔΑ͏मਖ਼͢Δ


    • ৽ΞιγΤʔγϣϯΛ࢖ͬͯɺγϯϓϧͳ࣮૷Λ͢Δ


    • ࡞੒ɾߋ৽Օॴ͸ϑΟʔνϟʔτάϧͰ੾Γ໭ͤΔΑ͏ʹ͢Δ


    • ৽ˠچͷಉ࣌ॻ͖ࠐΈΛ࣮૷͢Δ


    • ࡞੒࣌ʹ *_type ͱ *_id Λద੾ʹຒΊΔ


    • چˠ৽ͷಉ࣌ॻ͖ࠐΈ͸ఀࢭͤͣҡ࣋͢Δ


    • ஔ͖׵͑ʹൈ͚͕͋ͬͨ৔߹΍੾Γ໭ͨ͠͠৔߹ʹ໰୊͕ى͖ͳ͍Α͏ʹ


    • 👉 ·ͩɺશͯͷσʔλ͕৽چͰಉظ͞ΕΔ

    View Slide

  19. 4. 🚀 ൈ͚࿙Εݕ஌
    • چΞιγΤʔγϣϯ͕ར༻͞ΕͨΒɺϩάΛग़ྗͤ͞Δ


    • extend: Φϓγϣϯʹϩά༻ϞδϡʔϧΛ౉͠ɺϞδϡʔϧͷ self.extended ͕ൃ
    Րͨ͠ΒɺͲ͔͜ͰΠϯελϯεԽ͞Εͨ͸ͣ


    • ϩάʹ backtrace Λग़͠ɺͲ͔͜Βݺ͹Ε͔ͨΛௐࠪ͢Δ


    • 👉 ϓϩμΫγϣϯͰϩά͕ग़Δ͔Ұఆظ༷ؒࢠΛݟΔ
    class Message < ApplicationRecord


    has_many :photos, as: :imageable, extend: LoggingLeakage


    end


    module LoggingLeakage


    def self.extended(obj)


    # ผʑͷ obj Ͱ3ճݺͼग़͞ΕΔͨΊɺҰ౓͚࣮ͩߦ͞ΕΔΑ͏ʹ͢Δ


    return if obj.class.to_s
    ! =
    'Photo
    : :
    ActiveRecord_Associations_CollectionProxy'


    backtrace = caller.grep(/
    # {
    Regexp.escape(Rails.root.to_s)}/)


    leakage_logger.info "Replace leakage detected.
    # {
    backtrace}"


    end


    end

    View Slide

  20. 5. 🚀 ࢓্͛
    • چΞιγΤʔγϣϯΛ࡟আ


    • ৽ΞιγΤʔγϣϯΛผ໊͔Βຊདྷͷ໊લʹมߋ


    • ৽ˠچͷಉ࣌ॻ͖ࠐΈͷఀࢭ


    • 👉 ظ଴͢ΔίʔυͰͷಈ࡞͕։࢝

    View Slide

  21. 6. 📝 چσʔλΛ࡟আ
    • ϙϦϞʔϑΟοΫؔ࿈༻ͷΧϥϜʢ*_type, *_idʣΛ null Ͱ
    ຒΊΔ


    • ରԠ͕׬ྃͨ͜͠ͱΛࣔͨ͢Ί


    • 👉 Ҡߦ׬ྃʂ

    View Slide

  22. Ͳ͏΍࣮ͬͯࢪ͍ͯ͠Δ͔

    View Slide

  23. ઐ೚νʔϜ
    • ϦΞʔΩςΫςΟϯάνʔϜͱ໋໊͞Ε
    ͨɺυϝΠϯΛԣஅ͢ΔՕॴͷվળΛߦ
    ͏νʔϜ


    • طଘͷ࢓૊ΈͷͭΒ͍෦෼Λղফ͠ɺ
    ͓खຊͱͳΔΑ͏ͳ΋ͷΛ࡞Δ


    • ։ൃνʔϜશମͰɺվળ׆ಈΛܧଓ͠
    ͯ΍Δͧɺͱ͍͏จԽΛৢ੒͍ͨ͠


    • ·ͣ͸σʔλϞσϧपΓͷΞιγΤʔ
    γϣϯվળʹணख
    Rails ΞϓϦ
    ࢪ޻؅ཧ νϟοτ
    ࣸਅ
    Ϟσϧ Ϟσϧ

    View Slide

  24. εύΠΫίʔυͷॏཁੑ
    • ࣮ࡍʹҠߦΛ࢝ΊΔલʹɺҠߦεςοϓͷશͯΛ࣮૷ͯ͠ΈΔ


    • ࣮૷͸ࣺͯͯ΋ྑ͍


    • ׬੒·Ͱͷෆ֬ఆཁૉΛશͯ௵͓ͯ͘͠


    • ໎͏ͱ͜ΖΛϖΞϓϩͳͲͰղܾ͢Δ


    • ʮௐࠪʯͱʮ࣮ࢪʯͰϑΣʔζΛ෼͚Δ


    • εύΠΫ࡞ۀͷݟੵΓ͸೉͍͕͠ɺ࣮ࡍͷҠߦ࡞ۀ͸ݟੵΓ͕͠
    ΍͍͢


    • ʮ࣮ࢪʯظؒதʹɺผՕॴͷʮௐࠪʯΛฒߦͰ͖Δ

    View Slide

  25. ࡉ͔ͳ Tipsʢ࣌ؒతʹׂѪʣ
    • ΞιγΤʔγϣϯʹ dependent: :destroy ͕͋Δ৔߹ɺಈ࡞͕มΘΔ


    • ϙϦϞʔϑΟοΫؔ࿈Ͱ͸ࢠϞσϧ΋࡟আ͞ΕΔ͕ɺதؒςʔϒϧؔ࿈Ͱ
    ͸ɺதؒཁૉϞσϧͷΈ࡟আ͞ΕΔ


    • callback ͳͲͰࢠϞσϧ΋࡟আ͢ΔΑ͏ʹ͢Δ


    • چˠ৽ͱ৽ˠچͷಉ࣌ॻ͖ࠐΈ͕྆ํଘࡏ͢ΔλΠϛϯά͕͋ΔͨΊɺೋॏʹ
    ൃՐ͢ΔͷΛճආ͢Δ


    • อଘ࣌ʹ :skip_double_write ͷΑ͏ͳ attribute Λੜ΍੍ͯ͠ޚ͢Δ


    • ൈ͚࿙Εݕ஌ͷϩά͸ಉ͡ՕॴͰଟྔʹग़ΔՄೳੑ͕͋ΔͨΊɺ1ϦΫΤετͰ
    ॏෳ͢ΔϩάΛ uniq ͢ΔػߏΛ࢓ࠐΉ

    View Slide

  26. ·ͱΊ
    • σʔλϞσϧઃܭΛஔ͖׵͑Δͷ͸݁ߏେม


    • ՄೳͳݶΓϦϦʔεલʹݕ౼͠Α͏…!


    • ͱ͸͍͑ɺ౰ॳ૝ఆͱมΘΔ͜ͱ͸͋ΓಘΔ


    • ෳࡶͰྑ͘ͳ͍෦෼͸ɺ์ஔ͍ͯ͠Δͱ૿͑Δ


    • ෳࡶͰ೉͍͔͠Βͦ͜ɺલྫΛ౿ऻͯ͠͠·͏


    • ϦϑΝΫλϦϯάʹΑͬͯʮՁ஋Λఏڙ͠ଓ͚Δʯʹد༩͢Δ


    • ʮ͠ଓ͚ΔʯͨΊʹɺ෼͔Γ΍͘͢ɺखΛೖΕ΍͘͢อͭ

    View Slide