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

Breaking Change

Koichi ITO
December 14, 2019

Breaking Change

平成Ruby会議01 (https://heiseirb.github.io/kaigi01/)

Koichi ITO

December 14, 2019
Tweet

More Decks by Koichi ITO

Other Decks in Programming

Transcript

  1. ։ൃ൛ͷ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 `<main>' -e:1:in `match?': no implicit conversion of nil into String (TypeError) QSFWJFX
  2. ։ൃ൛ͷ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 `<main>' -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
  3. w ͦ΋ͦ΋ιϑτ΢ΣΞ։ൃ͸೉͍͠ w ΋ͬͱ࢖͍΍͍ͩ͢Ζ͏ઃܭ͕͋Ε ͹ɺͰ͖Ε͹ͦ͏͍ͨ͠ ᷤ౻ w ιϑτ΢ΣΞ͸ਓ͕࡞͍ͬͯΔ w

    ͭ·Γެ։"1*Ͱ΋ᷤ౻ͷ຤ɺഁյ ͞ΕΔ ͢Δ ͜ͱ͕͋Δ ެ։"1*Λ࢖͑͹໰୊͸ى͖ͳ͍ʁ
  4. 'BLFS[ Faker::Address.zip_code('NY') % bundle exec ruby example.rb Traceback (most recent

    call last): 1: from example.rb:3:in `<main>' /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)
  5. "1*

  6. 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*෼ͷରԠͱ͸
  7. 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*෼ͷରԠͱ͸
  8. 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*෼ͷରԠͱ͸
  9. 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*෼ͷରԠͱ͸
  10. 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*෼ͷରԠͱ͸
  11. 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*෼ͷରԠͱ͸
  12. ԶDPQ͕ظ଴͢Δ࢓༷ # bad # def email( # name: nil, #

    separators: nil # ) # # good # def email( # legacy_name = NOT_GIVEN, # legacy_separators = NOT_GIVEN, # name: nil, # separators: nil # ) ΩʔϫʔυҾ਺ͷΈ Ͱߏ੒͞Ε͍ͯΔ
  13. 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
  14. 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ϊʔυͷॲཧ
  15. 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*
  16. 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 ܯࠂ͠ͳ͍৚݅
  17. 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)
  18. 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έʔεΛଊ͑Δ࣮૷ྫ ϊʔυͷλΠϓ
  19. 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
  20. 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 ࣗಈमਖ਼ ΁ͷίʔϧόοΫ ఆٛ
  21. 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ͷ ϊʔυ
  22. 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ͷ Ҿ਺Ґஔ
  23. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ legacy_argument = "legacy_#{kwarg}" class_name = find_class_name(node) method_name =

    node.parent.parent.method_name condition += <<-RUBY 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
  24. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ legacy_argument = "legacy_#{kwarg}" class_name = find_class_name(node) method_name =

    node.parent.parent.method_name condition += <<-RUBY 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 ޓ׵ม਺ͷ࡞੒
  25. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ legacy_argument = "legacy_#{kwarg}" class_name = find_class_name(node) method_name =

    node.parent.parent.method_name condition += <<-RUBY 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
  26. CBEέʔεΛࣗಈमਖ਼͢Δ࣮૷ྫ legacy_argument = "legacy_#{kwarg}" class_name = find_class_name(node) method_name =

    node.parent.parent.method_name condition += <<-RUBY 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 ࣗಈमਖ਼ίʔυͷ ஔ׵จࣈྻ
  27. 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
  28. 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 मਖ਼ίʔυ΁ ͷஔ׵
  29. 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 ޓ׵Ҿ਺ͷૠೖ
  30. 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 ܯࠂ৚݅ͷૠೖ