All About RuboCop (RubyKaigi 2018)

All About RuboCop (RubyKaigi 2018)

Slide deck from my talk at RubyKaigi 2018 in Sendai.

1be785d1d788b82929e55fc83a9f0aaa?s=128

Bozhidar Batsov

May 31, 2018
Tweet

Transcript

  1. 1.
  2. 3.
  3. 5.
  4. 10.
  5. 13.
  6. 15.
  7. 16.
  8. 20.
  9. 21.
  10. 23.
  11. 24.
  12. 25.
  13. 26.
  14. 28.
  15. 29.
  16. 30.
  17. 31.
  18. 32.
  19. 33.
  20. 36.

    Improve Ruby coding style rules and Lint by Koichi Ito

    http://rubykaigi.org/2018/presentations/koic.html#jun01
  21. 44.
  22. 50.
  23. 51.
  24. 52.

    Notable Japanese Contributors • Yuji Nakayama (@yujinakayama) • Masataka Kuwabara

    (@pocke) • Koichi Ito (@koic) • Yukihiro "Matz" Matsumoto (@matz) ;-)
  25. 53.
  26. 64.
  27. 67.
  28. 69.
  29. 71.
  30. 72.
  31. 75.
  32. 83.

    pry(main)> Ripper.sexp('alias :some :test') => [:program, [[:alias, [:symbol_literal, [:symbol, [:@ident,

    "some", [1, 7]]]], [:symbol_literal, [:symbol, [:@ident, "test", [1, 13]]]]]]]
  33. 84.

    each(:method_add_arg, sexp) do |s| next if s[1][0] != :call receiver

    = s[1][1][1] method_name = s[1][3][1] if receiver && receiver[1] == 'Array' && method_name == 'new' && s[2] == [:arg_paren, nil] offences.delete(Offence.new(:convention, receiver[2].lineno, ERROR_MESSAGE)) add_offence(:convention, receiver[2].lineno, ERROR_MESSAGE) end end
  34. 86.

    pry(main)> Ripper.lex(':one') => [[[1, 0], :on_symbeg, ":"], [[1, 1], :on_ident,

    "one"]] pry(main)> Ripper.lex(':alias') => [[[1, 0], :on_symbeg, ":"], [[1, 1], :on_kw, "alias"]]
  35. 87.

    pry(main)> Ripper.lex('def alias(arg)')
 => [[[1, 0], :on_kw, "def"],
 [[1, 3],

    :on_sp, " "],
 [[1, 4], :on_kw, "alias"],
 [[1, 9], :on_lparen, "("],
 [[1, 10], :on_ident, “arg"],
 [[1, 13], :on_rparen, ")"]] pry(main)> Ripper.lex('def aliass(arg)')
 => [[[1, 0], :on_kw, "def"],
 [[1, 3], :on_sp, " "],
 [[1, 4], :on_ident, "aliass"],
 [[1, 10], :on_lparen, "("],
 [[1, 11], :on_ident, "arg"],
 [[1, 14], :on_rparen, ")"]]
  36. 89.
  37. 90.
  38. 95.
  39. 99.

    p Parser::CurrentRuby.parse("2 + 2").loc # #<Parser::Source::Map::Send:0x007fe5a1ac2388 # @dot=nil, # @begin=nil,

    # @end=nil, # @selector=#<Source::Range (string) 2...3>, # @expression=#<Source::Range (string) 0...5>> p Parser::CurrentRuby.parse("2 + 2").loc.selector.source # "+"
  40. 100.

    $ ruby-parse -L -e "2+2" (send (int 2) :+ (int

    2)) 2+2 ~ selector ~~~ expression (int 2) 2+2 ~ expression (int 2) 2+2 ~ expression
  41. 102.
  42. 107.
  43. 112.
  44. 114.
  45. 119.

    def on_send(node) receiver_node, method_name, *arg_nodes = *node return unless receiver_node

    && receiver_node.array_type? && method_name == :* && arg_nodes.first.str_type? add_offense(node, location: :selector) end Style/ArrayJoin (before)
  46. 120.

    Style/ArrayJoin (now) def_node_matcher :join_candidate?, '(send $array :* $str)' def on_send(node)

    join_candidate?(node) { add_offense(node, location: :selector) } end
  47. 147.

    class SimplifyNotEmptyWithAny < Cop MSG = 'Use `.any?` and remove

    the negation part.'.freeze def_node_matcher :not_empty_call?, <<-PATTERN (send (send (...) :empty?) :!) PATTERN def on_send(node) return unless not_empty_call?(node) add_offense(node) end end
  48. 149.
  49. 150.
  50. 151.
  51. 165.

    RuboCop HQ •RuboCop •RuboCop JP •RuboCop RSpec/Rails/Performance •guard-rubocop, etc •Ruby

    & Rails Style Guides •Libraries extracted from RuboCop (e.g. node extensions, node pattern matching, etc)
  52. 169.

    Come up with a less painful process for dealing with

    new cops and configuration changes
  53. 172.

    Breaking changes •Cop API changes •Extension API changes •Dropping support

    for Ruby versions •Renaming/removing cops •Renaming/changing/removing config values
  54. 177.

    RuboCop.org •Easy to navigate & mobile-friendly •Published using MkDocs (or

    similar) •Separate User and Developer manual •Getter getting started resources (e.g. video tutorials) •Improved cop documentation
  55. 178.
  56. 182.