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

Breaking Change

Koichi ITO
December 14, 2019

Breaking Change

Koichi ITO

December 14, 2019
Tweet

More Decks by Koichi ITO

Other Decks in Programming

Transcript

  1. #SFBLJOH$IBOHF

    $MPTJOH,FZOPUF
    גࣜձࣾ%3&$0.
    !LPJD&4. *OD
    4BU

    ฏ੒3VCZձٞ

    View Slide

  2. !LPJD,PJDIJ*50
    w 3VCP$PQ$PNNJUUFS
    w "30SBDMFFOIBODFE
    BEBQUFS$PNNJUUFS
    w $PNNVOJUZ.BOBHFS
    BU&4. *OD

    View Slide

  3. 8SJUF 3FWJFX
    $PEF&WFSZ%BZ

    View Slide

  4. View Slide

  5. View Slide

  6. #VOEMFS
    1SZ
    34QFD
    3VCP$PQ
    CZFCVH
    3VCZ,BJHJ
    %3&$0.͞Μϒʔεʹͯ

    View Slide

  7. View Slide

  8. 044ίϛϡχςΟͱͷڞੜΛࢧԉ
    Support coexistence with OSS community

    View Slide

  9. ͋ͱ͸෼͔Γ·͢Ͷʁ
    ࠾༻
    ೥Ҏ্ͷ3BJMTΞϓϦ։ൃͷ࣮੷&4.
    l3BJMT044Λ௨͡੒௕͍ͨ͠ϓϩάϥϚʔz
    @koicまでどうぞ

    View Slide

  10. IUUQTIFJTFJSCHJUIVCJPLBJHJ

    View Slide

  11. IUUQTIFJTFJSCHJUIVCJPLBJHJ

    View Slide

  12. #SFBLJOH$IBOHF

    $MPTJOH,FZOPUF
    גࣜձࣾ%3&$0.
    ,PJDIJ*50&4. *OD
    4BU

    ฏ੒3VCZձٞ

    View Slide

  13. ͜ͷߨԋͰಘΒΕΔ͔΋͠Εͳ͍͜ͱ
    w ςετͱCVOEMFVQEBUFͷॏཁ
    ੑͷ࠶ೝࣝ
    w 3VCP$PQͷࣼΊ্ͷ࢖͍ํ
    w ࢲ͔Βݟͨ044ͷੈք؍
    w ഁյతมߋͷͨΊʹ͋ͳ͕ͨͰ͖Δ
    ͔΋͠Εͳ͍͜ͱ

    View Slide

  14. w ഁյతมߋͱ͸Կ͔ʁ
    w ެ෍ࡁΈΠϯλϑΣʔεΛ࢖͏
    w ඇਪ঑ܯࠂόʔδϣϯΛܦ༝͢Δ
    w ഁյతมߋΛ৐Γӽ͑Δ
    w ΦʔϓϯιʔείϛϡχςΟ
    ΞδΣϯμ

    View Slide

  15. ഁյతมߋ
    ͱ͸Կ͔ʁ

    View Slide

  16. CVHTSVCZMBOHPSHJTTVFT
    3FBMLFZXPSEBSHVNFOU

    View Slide

  17. IUUQTXXXSVCZMBOHPSHKBOFXTSVCZQSFWJFXSFMFBTFE

    View Slide

  18. View Slide

  19. To be continued…

    View Slide

  20. CVHTSVCZMBOHPSHJTTVFT
    3FHFYQ\NBUDI NBUDI ^XJUIBOJMBSHVNFOUBSF
    EFQSFDBUFEBOEXJMMSBJTFB5ZQF&SSPSJO3VCZ

    View Slide

  21. ҆ఆ൛ͷ3VCZ[
    % rbenv shell 2.6.5 && ruby -e 'p /re/.match?(nil)'
    false

    View Slide

  22. ։ൃ൛ͷ3VCZ
    % rbenv shell 2.6.5 && ruby -e 'p /re/.match?(nil)'
    false
    % rbenv shell 2.7.0-preview2 && ruby -e 'p /re/.match?
    (nil)'
    Traceback (most recent call last):
    1: from -e:1:in `'
    -e:1:in `match?': no implicit conversion of nil into
    String (TypeError)
    QSFWJFX

    View Slide

  23. ։ൃ൛ͷ3VCZ
    % rbenv shell 2.6.5 && ruby -e 'p /re/.match?(nil)'
    false
    % rbenv shell 2.7.0-preview2 && ruby -e 'p /re/.match?
    (nil)'
    Traceback (most recent call last):
    1: from -e:1:in `'
    -e:1:in `match?': no implicit conversion of nil into
    String (TypeError)
    % rbenv shell 2.7.0-preview3 && ruby -e 'p /re/.match?
    (nil)'
    -e:1: warning: given argument is nil; this will raise a
    TypeError in the next release
    false QSFWJFX

    View Slide

  24. IUUQTCMPHBHJMFFTNDPKQFOUSZSBJMTPTTQBUDINFFUVQ
    ύονձͰ΋࿩୊ʹ

    View Slide

  25. IUUQTUXJUUFSDPNLBNJQPTUBUVT

    View Slide

  26. IUUQTUXJUUFSDPNLBNJQPTUBUVT

    View Slide

  27. IUUQTUXJUUFSDPNOBMTITUBUVT

    View Slide

  28. View Slide

  29. IUUQTCVHTSVCZMBOHPSHJTTVFT
    3FWFSU

    View Slide

  30. ഁյతมߋ
    ͱ͸Կ͔ʁ

    View Slide

  31. w CVOEMFVQEBUFޙ
    w 3VCZͷόʔδϣϯΛ͋͛ͨޙ
    w ͦͷ΄͔ϛυϧ΢ΣΞΛߋ৽ͨ͠ޙ
    ར༻ίʔυ΁ͷޙํޓ׵Λͳ͘͢มߋ
    ҎલͱҟͳΔ݁ՌΛҾ͖ى͜͠͏Δมߋ

    View Slide

  32. w ެ։͞Εͨ"1*ͷ໊લ͕มΘΔ
    w ެ։͞Εͨ"1*ͷҾ਺͕มΘΔ
    w ެ։͞Εͨ"1*ͷ໭Γ஋͕มΘΔ
    w ެ։͞Εͨ"1*ͷৼΔ෣͍͕มΘΔ
    w όάpYͱඇޓ׵มߋ͸ظ଴ͷҧ͍
    ͲΜͳഁյతมߋ͕͋Δͷ͔ʁ

    View Slide

  33. ެ։"1*
    ඇެ։"1*

    View Slide

  34. %FQFOEPO
    1VCMJTIFE"1*

    公開APIへの依存

    View Slide

  35. IUUQTCMJLJKBHJUIVCJP1VCMJTIFE*OUFSGBDF

    View Slide

  36. 0CKFDU0SJFOUFE
    1SPHSBNJOH

    View Slide

  37. ŠŠŠŠŠŠŠŠŠŠ҆ఆʹґଘͤΑ
    SDP: Stable Dependency Principle

    View Slide

  38. w ιϑτ΢ΣΞ͸มԽ͠ଓ͚Δ
    w ҆ఆͨ͠"1*ͱෆ҆ఆͳ"1*͕͋Δ
    w ઃܭ͸׬શʹݻఆͰ͖ͳ͍ͨΊɺෆ
    ҆ఆΛڐ͢࢓૊Έ͕ඞཁ
    w ෆ҆ఆͳ"1*͕ѱ͍ͷͰ͸ͳ͘ɺෆ
    ҆ఆͳ"1*ʹґଘ͢Δͷ͕ѱ͍͜ͱ
    4%1҆ఆґଘͷ๏ଇ

    View Slide

  39. w ಺෦తͳ"1*ͱ໌ࣔ͞Ε͍ͯΔ"1*
    ͸࢖Θͳ͍
    w 3BJMTʹ͓͍ͯ͸ϝιουίϝϯτ
    ͕͋ΔEPDʹ൓ө͞ΕΔϝιου͕
    ެ։"1*ͱͳΔ
    w ඇެ։"1*ʹґଘ͢ΔͱͲ͏ͳΔʁ
    3VCZ3VCZPO3BJMT

    View Slide

  40. %FQFOEPO
    *OUFSOBM"1*

    ⾮公開APIへの依存

    View Slide

  41. 8JUIHSFBUQPXFS
    DPNFTHSFBU
    SFTQPOTJCJMJUZ
    ⼤いなる⼒には⼤いなる責任が伴う

    View Slide

  42. View Slide

  43. SVCZSVCZ!FGCDB

    View Slide

  44. 3FNPWFBOJOUFSOBM"1*
    ಺෦"1*
    શ࡟আ

    View Slide

  45. SFRVJSFFSSPS
    ಺෦"1*

    View Slide

  46. ಺෦"1*΁ͷґଘΛճආ͢Δ

    View Slide

  47. SBJMTSBJMT
    3FNPWFVTFMFTT
    [email protected]@[email protected]

    View Slide

  48. ಺෦"1*

    View Slide

  49. ಺෦"1*
    3BJMTͰ͸ϝιουίϝϯτ͕ه͞Ε͍ͯͳ͍΋ͷ͸ඇެ։
    "1*ͱ͍͏ѻ͍ͱͳ͍ͬͯΔ

    View Slide

  50. ࡟আࡁ"1*
    ͷஔ׵

    View Slide

  51. % ag 'Migrator\.schema_migrations_table_name' `ghq root`
    HIR؅ཧͷMPDBMSFQPΛ۲ࢗ͠ݕࡧ
    Ͳ͏ͩΖ͏ʁ

    View Slide

  52. % ag 'Migrator\.schema_migrations_table_name' `ghq root`
    HIR؅ཧͷMPDBMSFQPΛ۲ࢗ͠ݕࡧ
    ݁Ռͱ͍͔ͯͭ͘͠ώοτͨ͠

    View Slide

  53. ಺෦"1*΁ͷґଘͷ࣮ྫ
    IUUQTHJUIVCDPNFTNJODBEIPRQVMM

    View Slide

  54. ಺෦"1*΁ͷґଘͷ࣮ྫ
    [email protected]

    View Slide

  55. w ಺෦"1*͸ϥΠϒϥϦఏڙऀ͕࣮૷
    ʹԠͯ͡มߋ͠͏Δ
    w ͭ·Γಥવͦͷ"1*͕ͳ͘ͳͬͯ
    /P.FUIPE&SSPS͕ى͖ΔͳͲ
    w ྫ͑͹3BJMTΛΞοϓσʔτͨ͠Β
    ະରԠґଘ(FNͰΤϥʔ͕ى͖Δ
    %PO`UEFQFOEPOJOUFSOBM"1*

    View Slide

  56. ඇਪ঑ܯࠂͷఏҊ͕͞ΕΔ
    削除の前に⾮推奨警告しませんか。 バックリ
    ンクに⽰された多くのGemが使⽤してる。

    View Slide

  57. w ຊདྷ͸಺෦"1*ʹґଘ͢Δͷ͕ྑ͘
    ͳ͍͕ɺใࠂ݅਺͕ͦΕͳΓʹ͋ͬ
    ͨͱϝϯςφʔʹײ͡ΒΕͨ໛༷
    w ͓ͦΒ͔͘ͳΓಛघέʔε
    w ҰํͰެ։"1*΁ͷมߋ͸Ϣʔβʔ
    ӨڹΛߟྀ͢΂͖
    ඇਪ঑ܯࠂ

    View Slide

  58. ඇਪ঑ܯࠂͷ௥Ճ
    IUUQTHJUIVCDPNSBJMTSBJMTQVMM

    View Slide

  59. ඇਪ঑ܯࠂʹΑΔઆ໌
    IUUQTHJUIVCDPNKSVCZBDUJWFSFDPSEKECDBEBQUFSQVMM

    View Slide

  60. w ॲཧܥ΍ϥΠϒϥϦ͕ܯࠂΛग़͍ͯ
    ΔόʔδϣϯΛܦ༝ͯ͠ɺܯࠂΛ௵
    ͔ͯ͠Β࣍ͷόʔδϣϯʹ্͛Δ
    w ͳΔ΂͘ϋΠδϟϯϓΛආ͚Δ
    w ೔ࠒ͔ΒCVOEMFVQEBUFΛ͠
    ͯɺςετΛ࣮ߦ͢Δ
    ΞϓϦέʔγϣϯ։ൃऀͷᅂΈ

    View Slide

  61. ഁյతมߋ
    ͱ͸Կ͔ʁ
    ෼͔Γ·
    ͔ͨ͠ʁ

    View Slide

  62. w ͦ΋ͦ΋ιϑτ΢ΣΞ։ൃ͸೉͍͠
    w ΋ͬͱ࢖͍΍͍ͩ͢Ζ͏ઃܭ͕͋Ε
    ͹ɺͰ͖Ε͹ͦ͏͍ͨ͠ ᷤ౻

    w ιϑτ΢ΣΞ͸ਓ͕࡞͍ͬͯΔ
    w ͭ·Γެ։"1*Ͱ΋ᷤ౻ͷ຤ɺഁյ
    ͞ΕΔ ͢Δ
    ͜ͱ͕͋Δ
    ެ։"1*Λ࢖͑͹໰୊͸ى͖ͳ͍ʁ

    View Slide

  63. 'BLFS
    #SFBLJOH$IBOHF
    #Z&YBNQMF

    View Slide

  64. View Slide

  65. w ͋Δ೔ɺ3BJMTΞϓϦέʔγϣϯΛ
    CVOEMFVQEBUFͯ͠ςετΛ࣮
    ߦͨ͠ΒಥવͷΤϥʔ͕ى͖Δ
    'BLFSͰى͖ͨഁյతมߋ

    View Slide

  66. 'BLFSZ[
    Faker::Address.zip_code('NY') # => "25771"

    View Slide

  67. 'BLFS[
    Faker::Address.zip_code('NY')
    % bundle exec ruby example.rb
    Traceback (most recent call last):
    1: from example.rb:3:in `'
    /Users/koic/.rbenv/versions/2.6.3/lib/ruby/gems/
    2.6.0/gems/faker-2.1.2/lib/faker/default/
    address.rb:32:in
    `zip_code': wrong number of arguments (given 1,
    expected 0) (ArgumentError)

    View Slide

  68. 'BLFS[ ղܾ൛

    -Faker::Address.zip_code('NY')
    +Faker::Address.zip_code(state_abbreviation: 'NY')
    LXBSHT
    ʹ͢Δ

    View Slide

  69. w ࣗ෼ͷ৔߹͸ॳखʹHFNEJGGΛ࢖ͬ
    ͯࠩ෼Λ֬ೝͨ͠Γɺίϛοτϝοηʔ
    δʹ࢒ͨ͠Γ͍ͯ͠ΔͷͰͦͷϦϯΫ
    ΛݟΔ
    VQTUSFBNͷࠩ෼ΛݟΔ
    % gemdiff
    (snip)
    faker: 2.8.1 > 2.8.0
    https://github.com/faker-ruby/faker/compare/
    v2.8.0...v2.8.1

    View Slide

  70. มߋཤྺͱ
    ͍͏ཏ਑൫
    IUUQKBXJLJQFEJBPSHXJLJ&""&""&##&&##"NFEJB'JMF$BOUJOP1MBOJTQIFSFQOH
    IUUQTHJUIVCDPNGBLFSSVCZGBLFSCMPC
    W$)"/(&-0(NEJNQPSUBOUOPUF

    View Slide

  71. View Slide

  72. "1*

    View Slide

  73. w "SHVNFOU&SSPS͸ݪҼͱͯ͠෼͔
    Γ΍͍͕͢ɺͲͷΑ͏ͳҾ਺͕ਖ਼͍͠
    ͔෼͔Βͳ͔ͬͨ
    w QPTJUJPOBMҾ਺͔ΒΩʔϫʔυҾ਺΁
    ͷมߋͰΩʔϫʔυ͕͙͢ʹ෼͔Βͳ
    ͔ͬͨ
    w "1*ͷҾ਺෼
    ໰୊఺

    View Slide

  74. w ࣗ෼͕౿Μͩ໘౗ͳ͜ͱ͸ɺଞͷਓ
    ʹͱͬͯ΋໘౗͔΋͠Εͳ͍
    w (FNͷଟ͘͸3VCZͰॻ͔Ε͍ͯ
    Δ
    w ύονΛૹΔͷʹࢧোͷ͋ΔϥΠη
    ϯεʹग़ձͬͨ͜ͱ͕ͳ͍
    ෆศͩͱࢥͬͨΒఏҊ͢Ε͹ྑ͍

    View Slide

  75. w ΞοϓάϨʔυޙͷ࣮ߦ࣌Ͱ͙͢ʹ
    Τϥʔʹ͢ΔͷͰ͸ͳ͘ɺઌʹͲͷ
    Α͏ͳΠϯλϑΣʔεʹมΘΔͷ͔
    ܯࠂΛग़͢
    ղܾҊͱͯ͠ߟ͑ͨ͜ͱ

    View Slide

  76. GBLFSSVCZGBLFS
    "EEXBSOGPSQPTJUJPOBMBSHVNFOUTXIFOVTJOH
    'BLFS

    View Slide

  77. $POUFYU
    Կނඞཁ͔

    View Slide

  78. )PX
    ࣮૷ϙΠϯτ

    View Slide

  79. .FSJU
    ͲΜͳಘ͕

    View Slide

  80. &YBNQMF
    ࣮ྫ

    View Slide

  81. ίʔυEJGG جຊతͳมߋ

    ޓ׵*'ͷఏڙ

    View Slide

  82. ίʔυEJGG جຊతͳมߋ

    چ"1*΁ͷܯࠂ

    View Slide

  83. ίʔυEJGG جຊతͳมߋ

    w legacy_sourceͱͯ͠ݩͷҾ਺Ͱ΋
    ड͚ೖΕΔΑ͏ʹ͢Δ
    w legacy_source͕౉͞Εͨ৔߹͸ܯ
    ࠂΛදࣔͯ͠ɺͦͷ஋Λ༏ઌͯ͠࢖͏

    View Slide

  84. ͦΕΛ"1*

    View Slide

  85. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    "1*෼ͷରԠͱ͸

    View Slide

  86. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    ϝιου໊
    "1*෼ͷରԠͱ͸

    View Slide

  87. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    Ҿ਺໊
    "1*෼ͷରԠͱ͸

    View Slide

  88. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    Ҿ਺ͷ਺
    "1*෼ͷରԠͱ͸

    View Slide

  89. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    σϑΥϧτ஋
    "1*෼ͷରԠͱ͸

    View Slide

  90. def saying(legacy_source = NOT_GIVEN, source: nil)
    if legacy_source != NOT_GIVEN
    warn_with_uplevel 'Passing `source` with the \
    1st argument of `Dune.saying` is deprecated. \
    Use keyword argument like \
    `Dune.saying(source: ...)` instead.',
    uplevel: 1
    source = legacy_source
    end
    end
    ܯࠂจ
    "1*෼ͷରԠͱ͸

    View Slide

  91. "1*
    ख࡞ۀ͸
    ͭΒ͍

    View Slide

  92. ୡਓϓϩάϥϚʔ΁ͷجૅٕज़
    ςεςΟϯά
    %FWFMPQFS5FTUJOH
    ࣗಈԽ
    1SPKFDU"VUPNBUJPO
    όʔδϣϯ؅ཧ
    7FSTJPO$POUSPM

    View Slide

  93. View Slide

  94. w LXBSHTʹมΘΔલͷ'BLFSͷίϛο
    τϋογϡΛखʹೖΕΔ
    w खݩͷ'BLFSϦϙδτϦΛͦͷόʔδϣ
    ϯʹ໭͢
    w 'BLFSελΠϧ΁ͷҠߦΠϯλϑΣʔ
    εʹBVUPDPSSFDU͢Δ$PQΛ࡞Δ
    w 'BLFSʹ$PQΛద༻ͨ͠ύονΛૹΔ
    ͜ͷඇਪ঑ܯࠂΛ࡞ΔͨΊߦͬͨ͜ͱ

    View Slide

  95. w LXBSHTʹมΘΔલͷ'BLFSͷίϛο
    τϋογϡΛखʹೖΕΔ
    w खݩͷ'BLFSϦϙδτϦΛͦͷόʔδϣ
    ϯʹ໭͢
    w 'BLFSελΠϧ΁ͷҠߦΠϯλϑΣʔ
    εʹBVUPDPSSFDU͢Δ$PQΛ࡞Δ
    w 'BLFSʹ$PQΛద༻ͨ͠ύονΛૹΔ
    ͜ͷඇਪ঑ܯࠂΛ࡞ΔͨΊߦͬͨ͜ͱ

    View Slide

  96. w 3VCP$PQ͸ظ଴͍ͯ͠ͳ͍ελΠ
    ϧͷίʔυΛظ଴͍ͯ͠Δίʔυʹ
    ࣗಈमਖ਼͢ΔػೳΛඋ͍͑ͯΔ
    w 3VCP$PQ͕ఏڙ͍ͯ͠Δ$PQΫ
    ϥεΛܧঝ͢Δ͜ͱͰΦϦδφϧͷ
    $PQΛ࡞Δ͜ͱ͕Ͱ͖Δ
    3VCP$PQ͓͞Β͍

    View Slide

  97. w ϩʔΧϧϦϙδτϦͷΈͷϒϥϯν
    Ͱॻ͖ͳ͙ͬͨ΋ͷΛൃ۷͠·ͨ͠
    w ͲΜͳҠߦεΫϦϓτΛॻ͍͍ͯͨ
    ͷ͔ൈਮͯ͠งғؾΛ఻͑·͢
    w ϝϯςφϯεΛ΄΅ߟ͍͑ͯͳ͍ɺ
    γϣοτͷίʔυʹͯޚ༰ࣻئ
    ΦϒδΣΫτࢦ޲lεΫϦϓτzݴޠ

    View Slide

  98. ԶDPQ͕ظ଴͢Δ࢓༷
    # bad
    # def email(
    # name: nil,
    # separators: nil
    # )
    #
    # good
    # def email(
    # legacy_name = NOT_GIVEN,
    # legacy_separators = NOT_GIVEN,
    # name: nil,
    # separators: nil
    # )
    ΩʔϫʔυҾ਺ͷΈ
    Ͱߏ੒͞Ε͍ͯΔ

    View Slide

  99. CBEέʔεΛଊ͑Δ࣮૷ྫ
    class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end

    View Slide

  100. CBEέʔεΛଊ͑Δ࣮૷ྫ
    class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end
    EFGϊʔυͷॲཧ

    View Slide

  101. CBEέʔεΛଊ͑Δ࣮૷ྫ
    class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end
    ܯࠂΛग़͢"1*

    View Slide

  102. CBEέʔεΛଊ͑Δ࣮૷ྫ
    class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end
    ܯࠂ͠ͳ͍৚݅

    View Slide

  103. CBEέʔεΛଊ͑Δ࣮૷ྫ
    class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end
    ܯࠂ͠ͳ͍৚݅
    ϊʔυͷλΠϓ
    % ruby-parse -e 'def do_something(foo:, bar: 1);
    end'
    (def :do_something
    (args
    (kwarg :foo)
    (kwoptarg :bar
    (int 1))) nil)

    View Slide

  104. class BreakingChangeArguments < Cop
    def on_def(node)
    return unless node.arguments.all? {|argument|
    argument.kwarg_type? ||
    argument.kwoptarg_type?
    }
    node.arguments.reverse_each do |argument|
    message = format(
    MSG, name: argument.children.first
    )
    add_offense(argument, message: message)
    end
    end
    end
    ܯࠂ͠ͳ͍৚݅
    % ruby-parse -e 'def do_something(foo:, bar: 1);
    end'
    (def :do_something
    (args
    (kwarg :foo)
    (kwoptarg :bar
    (int 1))) nil)
    CBEέʔεΛଊ͑Δ࣮૷ྫ
    ϊʔυͷλΠϓ

    View Slide

  105. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    def autocorrect(node)
    kwarg = node.children.first
    argument_index =
    node.parent.parent.arguments.map {|argument|
    argument.children.first
    }.index(kwarg) + 1
    index = case argument_index
    when 1; '1st'
    when 2; '2nd'
    when 3; '3rd'
    else
    "#{argument_index}th"
    end

    View Slide

  106. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    def autocorrect(node)
    kwarg = node.children.first
    argument_index =
    node.parent.parent.arguments.map {|argument|
    argument.children.first
    }.index(kwarg) + 1
    index = case argument_index
    when 1; '1st'
    when 2; '2nd'
    when 3; '3rd'
    else
    "#{argument_index}th"
    end
    ࣗಈमਖ਼
    ΁ͷίʔϧόοΫ
    ఆٛ

    View Slide

  107. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    def autocorrect(node)
    kwarg = node.children.first
    argument_index =
    node.parent.parent.arguments.map {|argument|
    argument.children.first
    }.index(kwarg) + 1
    index = case argument_index
    when 1; '1st'
    when 2; '2nd'
    when 3; '3rd'
    else
    "#{argument_index}th"
    end
    LXBSHͷ
    ϊʔυ

    View Slide

  108. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    def autocorrect(node)
    kwarg = node.children.first
    argument_index =
    node.parent.parent.arguments.map {|argument|
    argument.children.first
    }.index(kwarg) + 1
    index = case argument_index
    when 1; '1st'
    when 2; '2nd'
    when 3; '3rd'
    else
    "#{argument_index}th"
    end
    LXBSHͷ
    Ҿ਺Ґஔ

    View Slide

  109. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    legacy_argument = "legacy_#{kwarg}"
    class_name = find_class_name(node)
    method_name = node.parent.parent.method_name
    condition += <if #{legacy_argument} != NOT_GIVEN
    warn_with_uplevel 'Passing `#{kwarg}` with
    the #{index} argument of
    `#{class_name}.#{method_name}` is deprecated. Use
    keyword argument like `#{class_name}.#{method_name}
    (#{kwarg}: ...)` instead.', uplevel: 1
    #{kwarg} = #{legacy_argument}
    end
    RUBY

    View Slide

  110. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    legacy_argument = "legacy_#{kwarg}"
    class_name = find_class_name(node)
    method_name = node.parent.parent.method_name
    condition += <if #{legacy_argument} != NOT_GIVEN
    warn_with_uplevel 'Passing `#{kwarg}` with
    the #{index} argument of
    `#{class_name}.#{method_name}` is deprecated. Use
    keyword argument like `#{class_name}.#{method_name}
    (#{kwarg}: ...)` instead.', uplevel: 1
    #{kwarg} = #{legacy_argument}
    end
    RUBY
    ޓ׵ม਺ͷ࡞੒

    View Slide

  111. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    legacy_argument = "legacy_#{kwarg}"
    class_name = find_class_name(node)
    method_name = node.parent.parent.method_name
    condition += <if #{legacy_argument} != NOT_GIVEN
    warn_with_uplevel 'Passing `#{kwarg}` with
    the #{index} argument of
    `#{class_name}.#{method_name}` is deprecated. Use
    keyword argument like `#{class_name}.#{method_name}
    (#{kwarg}: ...)` instead.', uplevel: 1
    #{kwarg} = #{legacy_argument}
    end
    RUBY
    private
    def find_class_name(node)
    if node.class_type?
    return node.identifier.source
    end
    find_class_name(node.parent)
    end

    View Slide

  112. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    legacy_argument = "legacy_#{kwarg}"
    class_name = find_class_name(node)
    method_name = node.parent.parent.method_name
    condition += <if #{legacy_argument} != NOT_GIVEN
    warn_with_uplevel 'Passing `#{kwarg}` with
    the #{index} argument of
    `#{class_name}.#{method_name}` is deprecated. Use
    keyword argument like `#{class_name}.#{method_name}
    (#{kwarg}: ...)` instead.', uplevel: 1
    #{kwarg} = #{legacy_argument}
    end
    RUBY
    ࣗಈमਖ਼ίʔυͷ
    ஔ׵จࣈྻ

    View Slide

  113. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    lambda do |corrector|
    corrector.insert_before(
    arguments_range(node),
    "#{legacy_argument} = NOT_GIVEN, "
    )
    corrector.insert_before(
    node.parent.parent.children.last.source_range,
    condition
    )
    end

    View Slide

  114. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    lambda do |corrector|
    corrector.insert_before(
    arguments_range(node),
    "#{legacy_argument} = NOT_GIVEN, "
    )
    corrector.insert_before(
    node.parent.parent.children.last.source_range,
    condition
    )
    end
    मਖ਼ίʔυ΁
    ͷஔ׵

    View Slide

  115. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    lambda do |corrector|
    corrector.insert_before(
    arguments_range(node),
    "#{legacy_argument} = NOT_GIVEN, "
    )
    corrector.insert_before(
    node.parent.parent.children.last.source_range,
    condition
    )
    end
    ޓ׵Ҿ਺ͷૠೖ

    View Slide

  116. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ

    lambda do |corrector|
    corrector.insert_before(
    arguments_range(node),
    "#{legacy_argument} = NOT_GIVEN, "
    )
    corrector.insert_before(
    node.parent.parent.children.last.source_range,
    condition
    )
    end
    ܯࠂ৚݅ͷૠೖ

    View Slide

  117. View Slide

  118. ͓Ϛʔδ

    View Slide

  119. ŠŠŠŠŠŠŠŠŠŠϢʔβʔͷ൓Ԡ
    Feedback

    View Slide

  120. GBLFSSVCZGBLFS
    USJHHFSTEFQSFDBUJPOXBSOJOHTGSPNJOTJEFUIF
    HFN

    View Slide

  121. ⾮推奨警告の量が半端なくて⼤変だったよ

    View Slide

  122. w ,ಥવͷΤϥʔΛճආ͢Δඇਪ঑
    ܯࠂ͕׆༻͞ΕͨΑ͏Ͱ͋Δ
    w 1ͷഁյతมߋΛ௚ܸ͢Δ
    ͱͭΒͦ͏Ͱ͋Δ
    w 5ࣗಈमਖ਼ՄೳͳπʔϧΛఏڙ͠
    ͯΞοϓάϨʔυ΁ͷ௧ΈΛݮΒ͢
    ϑΟʔυόοΫʹରͯ͠ߟ͑ͨ͜ͱ

    View Slide

  123. LPJDSVCPDPQGBLFS
    "3VCP$PQFYUFOTJPOGPS'BLFS

    View Slide

  124. w ͔ͭͯ34QFD͔Β34QFD΁
    ͷҠߦͰ࢖ͬͨ5SBOTQFDʹӨڹ
    Λड͚ͯ࡞ͬͨ(FN
    w 'BLFSʹඇਪ঑ܯࠂΛೖΕΔ13Λ
    ࡞ΔաఔͰ͍ͭͰʹฒߦͯ͠࡞Ζ͏
    ͱ͍ͯͨ͠Α͏ͳ ͏Ζ֮͑

    Ұܳ͸։ൃΛՃ଎͢Δ

    View Slide

  125. View Slide

  126. View Slide

  127. ઌͷ࣮૷ͱࣅͨ
    Α͏ͳ΋ͷͳͷ
    Ͱ࣮૷ղઆׂѪ

    View Slide

  128. l
    (FN͸࡞͚ͬͨͩͰউख
    ʹ޿·ΔΘ͚Ͱ͸ͳ͘࢖Θ
    ΕΔͨΊͷͻͱ޻෉ॏཁ
    !BNBUTVEB

    View Slide


  129. View Slide

  130. IUUQTXXXNPVOUBJOHPBUTPGUXBSFDPNCMPHBSFPGGFBUVSFTSFBMMZSBSFMZPSOFWFSVTFE

    View Slide

  131. ࢲ͕ࢥ͍͍ͭͨ
    ͜ͷ՝୊ʹର͠
    ࠷ڧͷએ఻ํ๏

    View Slide

  132. VQTUSFBN
    ʹऔΓࠐΜ
    Ͱ΋Β͏

    View Slide

  133. GBLFSSVCZGBLFS
    *ODMVEFSVCPDPQGBLFSBVUPDPSSFDUJOEFQSFDBUJPO

    View Slide

  134. View Slide

  135. $POUFYU
    લྫΛग़͢

    View Slide

  136. )PX
    ղܾࡦ

    View Slide

  137. &YBNQMF
    ࣮ྫ

    View Slide

  138. .FSJU
    ͲΜͳಘ͕

    View Slide

  139. ৺഑ͩͬͨ఺

    View Slide

  140. View Slide

  141. ͓Ϛʔδ

    View Slide

  142. ŠŠŠŠŠŠŠŠŠŠŠŠŠŠͦͷޙ
    Afterwards

    View Slide

  143. GBLFSSVCZGBLFS
    6TJOHLXBSHTGPSNFUIPETUIBUBDDFQUPOMZPOF
    BSHVNFOUJTJOF⒏DJFOU

    View Slide

  144. 引数ひとつにkwargsはやりすぎじゃね?

    View Slide

  145. 8*1GPS'BLFS

    View Slide

  146. 8*1GPS'BLFS
    To be continued…

    View Slide

  147. w ࠷ॳ͔Βऑ఺ͷͳ͍ઃܭΛ͢Δͷ͸೉͍͠
    w ιϑτ΢ΣΞΛΑΓྑ͍ܗʹ͢ΔͨΊʹɺࢭΉΛ
    ಘͣޙํޓ׵Λؾʹ͔͚ͭͭ΋ఘΊΔ৔߹͕͋Δ
    w CVOEMFVQEBUFͰখ͞ͳϦϓϨʔεΛ܁Γฦ
    ͢͜ͱͰେ͖ͳϦϓϨʔεΛ๷͙
    w มߋൣғ͕ڱ͍ํ͕ݪҼͷಛఆ͕༰қ
    w ͋ͳͨͱ·ΘΓ͸ݹ͍όʔδϣϯ͔৽͍͠όʔδϣ
    ϯͲͪΒΛ࢖͏ͷ͕޷ΈͰ͔͢ʁ
    ഁյతมߋΛӽ͑Δ

    View Slide

  148. όʔδϣϯӽ͑
    lޙੈʹ࢒͢΋ͷz
    ະདྷ΁

    View Slide

  149. w ͋ͳͨʹΑͬͯෆඞཁ͔ͩͬͨ΋͠Εͳ͍
    ഁյతͳมߋΛࢭΊΔ͜ͱ͕Ͱ͖Δ͔΋͠
    Εͳ͍
    w ͋ͳͨʹΑͬͯഁյతͳมߋ΁ͷμϝʔδ
    Λ؇࿨Ͱ͖Δ͔΋͠Εͳ͍
    w ·ͣ͸खݩͷඇਪ঑ܯࠂΛݟͯΈΑ͏
    w ຤͸ΈΜͳͰ3VCZ΁ͷΞοϓ
    άϨʔυࣗຫΛ͍͖ͯ͠·͠ΐ͏
    ͋ͳ͕ͨίϛϡχςΟ

    View Slide

  150. l
    ίϛϡχςΟͱ͸୭͔ɻ΋ͪΖ
    Μɺ͋ͳͨͷ͜ͱͩɻ͋ͳ͕ͨ
    ίϛϡχςΟͰ͋ΓɺͦΕҎ֎
    ʹίϛϡχςΟ͸͍ͳ͍ɻ͋ͳ
    ͨͷΑ͏ͳਓʑͷू·ΓΛɺί
    ϛϡχςΟͱݺͿͷͩɻ
    IUUQTNBHB[JOFSVCZJTUOFUBSUJDMFT'PSF8PSEIUNM
    Community is yourself

    View Slide

  151. View Slide