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

All About RuboCop (RubyConf.by 2018)

All About RuboCop (RubyConf.by 2018)

Slide deck from my talk at RubyConf.by 2018 in Minsk.

Bozhidar Batsov

April 21, 2018
Tweet

More Decks by Bozhidar Batsov

Other Decks in Programming

Transcript

  1. Божидар

    View full-size slide

  2. Sofia, Bulgaria
    Sofia, Bulgaria

    View full-size slide

  3. Balkan Ruby
    @balkanruby
    https://balkanruby.com
    25-26 May Sofia, Bulgaria

    View full-size slide

  4. I love rakiya!
    And Sofia!

    View full-size slide

  5. https://github.com/toptal/granite

    View full-size slide

  6. Ruby & Rails
    style guides

    View full-size slide

  7. All
    About
    RuboCop
    by Bozhidar Batsov
    (a.k.a. bug)

    View full-size slide

  8. RuboCop in a Nutshell

    View full-size slide

  9. A Ruby static code analysis tool
    aimed to enforce the Ruby
    Community Style Guide

    View full-size slide

  10. In other words…

    View full-size slide

  11. It keeps your codebase
    consistent

    View full-size slide

  12. It saves you time

    View full-size slide

  13. It advances the Ruby
    language forward

    View full-size slide

  14. It doesn’t work very well…

    View full-size slide

  15. 314 open issues

    View full-size slide

  16. 100 volunteers

    View full-size slide

  17. 3.14 issues/person

    View full-size slide

  18. Coincidence?

    View full-size slide

  19. And your code is still
    shit afterwards!

    View full-size slide

  20. A prettier shit, but
    still - shit.

    View full-size slide

  21. A Brief History of Time

    View full-size slide

  22. A Brief History of RuboCop

    View full-size slide

  23. 2011
    The Ruby Style Guide

    View full-size slide

  24. May, 2012
    RuboCop 0.0.0

    View full-size slide

  25. Static code analysis with
    regular expressions …

    View full-size slide

  26. Nov, 2012
    Along Came Jonas

    View full-size slide

  27. Spring, 2013
    Renewed push

    View full-size slide

  28. Using a proper parser!

    View full-size slide

  29. Struggles with Ripper

    View full-size slide

  30. Ripper supported just MRI

    View full-size slide

  31. I wanted to build a cross-
    platform tool

    View full-size slide

  32. Ripper has a very hard to
    work AST representation

    View full-size slide

  33. pry(main)> Ripper.sexp('alias :some :test')
    => [:program,
    [[:alias,
    [:symbol_literal, [:symbol, [:@ident, "some", [1, 7]]]],
    [:symbol_literal, [:symbol, [:@ident, "test", [1, 13]]]]]]]

    View full-size slide

  34. 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

    View full-size slide

  35. And many quirks…

    View full-size slide

  36. 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"]]

    View full-size slide

  37. 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, ")"]]

    View full-size slide

  38. Which no one really
    considered problematic…

    View full-size slide

  39. Parser: A New Hope

    View full-size slide

  40. parser was cross-platform

    View full-size slide

  41. well documented

    View full-size slide

  42. easy to work with ast format

    View full-size slide

  43. p Parser::CurrentRuby.parse("2 + 2")
    # (send
    # (int 2) :+
    # (int 2))

    View full-size slide

  44. node source location
    mappings

    View full-size slide

  45. p Parser::CurrentRuby.parse("2 + 2").loc
    # ## @dot=nil,
    # @begin=nil,
    # @end=nil,
    # @selector=#,
    # @expression=#>
    p Parser::CurrentRuby.parse("2 + 2").loc.selector.source
    # "+"

    View full-size slide

  46. $ 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

    View full-size slide

  47. powerful code rewriting
    capabilities

    View full-size slide

  48. Struggles with Parser

    View full-size slide

  49. Parser was brand new

    View full-size slide

  50. Had no real users

    View full-size slide

  51. And had plenty of bugs…

    View full-size slide

  52. 28th May, 2013
    State of Unity

    View full-size slide

  53. 1st July, 2013
    RuboCop 0.9

    View full-size slide

  54. Auto-correct

    View full-size slide

  55. Commissioner
    (single parsing run triggers all cops)

    View full-size slide

  56. Results caching
    (rubocop —-cache)

    View full-size slide

  57. Parallel Checks
    (rubocop —-parallel)

    View full-size slide

  58. Pattern Matching
    (regex-style matching for ast nodes)

    View full-size slide

  59. 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)

    View full-size slide

  60. Style/ArrayJoin (now)
    def_node_matcher :join_candidate?, '(send $array :* $str)'
    def on_send(node)
    join_candidate?(node) { add_offense(node, location: :selector) }
    end

    View full-size slide

  61. RuboCop Today

    View full-size slide

  62. Started out with no
    configuration at all…

    View full-size slide

  63. Today things are infinitely
    configurable

    View full-size slide

  64. Metrics/LineLength:
    Enabled: false

    View full-size slide

  65. Metrics/LineLength:
    Max: 100

    View full-size slide

  66. Style/StringLiterals:
    Enabled: false

    View full-size slide

  67. Style/StringLiterals:
    EnforcedStyle: double_quotes

    View full-size slide

  68. You can limit cops to certain
    folders with Exclude/Include
    directives

    View full-size slide

  69. You can have different settings
    for the same cops in different
    directories

    View full-size slide

  70. You can set a target
    Ruby/Rails version

    View full-size slide

  71. You can inherit between
    configuration files

    View full-size slide

  72. You can auto-generate an initial
    configuration for your project

    View full-size slide

  73. rubocop —-auto-gen-config

    View full-size slide

  74. gry is smarter alternative of
    the built-in command
    https://github.com/pocke/gry

    View full-size slide

  75. RuboCop started just as a
    code style checker

    View full-size slide

  76. •Style
    •Lint
    •Layout
    •Naming
    •Security
    •Performance
    •Performance
    •Rails
    •Bundler
    •Metric
    •Gemspec

    View full-size slide

  77. RuboCop is a great code
    formatter

    View full-size slide

  78. rubocop —-only Layout

    View full-size slide

  79. RuboCop does some linting
    better that
    ruby -w

    View full-size slide

  80. rubocop —-lint

    View full-size slide

  81. You can extend RuboCop with
    your own cops

    View full-size slide

  82. https://rubocop.readthedocs.io/en/latest/
    development/

    View full-size slide

  83. $ bundle exec rake new_cop[Department/Name]

    View full-size slide

  84. 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

    View full-size slide

  85. RuboCop has a rich ecosystem
    of extensions

    View full-size slide

  86. Notable Extensions
    • rubocop-rspec
    • guard-rubocop
    • rubocop-sequel
    • rubocop-cask

    View full-size slide

  87. Integration with code quality services
    • Code Climate
    • HoundCI
    • SideCI
    • Codacy

    View full-size slide

  88. RuboCop has integrations
    with many editors and IDEs

    View full-size slide

  89. And you can read about all of
    this in RuboCop’s manual…

    View full-size slide

  90. rubocop.readthedocs.io

    View full-size slide

  91. mry makes RuboCop
    upgrades less painful
    https://github.com/pocke/mry

    View full-size slide

  92. The Road to
    RuboCop 1.0

    View full-size slide

  93. #1 question I get asked about
    RuboCop?

    View full-size slide

  94. When is RuboCop 1.0 coming?

    View full-size slide

  95. Remove Rails and Performance
    cops from RuboCop’s core

    View full-size slide

  96. Provide a proper (and stable)
    API for writing RuboCop
    extensions

    View full-size slide

  97. Review the config of all cops and
    come up with the best defaults

    View full-size slide

  98. Adjust the list of enabled by
    default cops

    View full-size slide

  99. Agree on what’s going to be
    constituting breaking changes
    down the road

    View full-size slide

  100. Come up with a less painful
    process for dealing with new
    cops and configuration changes

    View full-size slide

  101. That’s a lot of work!

    View full-size slide

  102. Apes Together Strong!

    View full-size slide

  103. Rubyists Together Strong!

    View full-size slide

  104. Thanks!
    twitter: @bbatsov
    github: @bbatsov
    http//batsov.com
    http://emacsredux.com
    RubyConf BY
    Minsk,
    Belarus
    21.04.2018

    View full-size slide