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.

1be785d1d788b82929e55fc83a9f0aaa?s=128

Bozhidar Batsov

April 21, 2018
Tweet

Transcript

  1. Hello!

  2. Божидар

  3. Божo

  4. Sofia, Bulgaria Sofia, Bulgaria

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

  6. I love rakiya! And Sofia!

  7. None
  8. https://github.com/toptal/granite

  9. bbatsov

  10. Ruby & Rails style guides

  11. None
  12. None
  13. None
  14. None
  15. All About RuboCop by Bozhidar Batsov (a.k.a. bug)

  16. RuboCop in a Nutshell

  17. A Ruby static code analysis tool aimed to enforce the

    Ruby Community Style Guide
  18. In other words…

  19. It keeps your codebase consistent

  20. It saves you time

  21. It advances the Ruby language forward

  22. It doesn’t work very well…

  23. foo.each

  24. foo.each

  25. 314 open issues

  26. 100 volunteers

  27. 3.14 issues/person

  28. Coincidence?

  29. None
  30. And your code is still shit afterwards!

  31. A prettier shit, but still - shit.

  32. A Brief History of Time

  33. A Brief History of RuboCop

  34. 2011 The Ruby Style Guide

  35. None
  36. May, 2012 RuboCop 0.0.0

  37. None
  38. Static code analysis with regular expressions …

  39. None
  40. Nov, 2012 Along Came Jonas

  41. None
  42. None
  43. Spring, 2013 Renewed push

  44. Using a proper parser!

  45. None
  46. Struggles with Ripper

  47. Ripper supported just MRI

  48. I wanted to build a cross- platform tool

  49. Ripper has a very hard to work AST representation

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

    "some", [1, 7]]]], [:symbol_literal, [:symbol, [:@ident, "test", [1, 13]]]]]]]
  51. 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
  52. And many quirks…

  53. 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"]]
  54. 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, ")"]]
  55. Which no one really considered problematic…

  56. None
  57. None
  58. Parser: A New Hope

  59. parser was cross-platform

  60. well documented

  61. None
  62. easy to work with ast format

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

    # (int 2))
  64. node source location mappings

  65. 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 # "+"
  66. $ 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
  67. powerful code rewriting capabilities

  68. None
  69. Struggles with Parser

  70. Parser was brand new

  71. Had no real users

  72. And had plenty of bugs…

  73. None
  74. 28th May, 2013 State of Unity

  75. RuboCop 0.8

  76. 1st July, 2013 RuboCop 0.9

  77. Formatters

  78. Auto-correct

  79. None
  80. Commissioner (single parsing run triggers all cops)

  81. Results caching (rubocop —-cache)

  82. Parallel Checks (rubocop —-parallel)

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

  84. 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)
  85. Style/ArrayJoin (now) def_node_matcher :join_candidate?, '(send $array :* $str)' def on_send(node)

    join_candidate?(node) { add_offense(node, location: :selector) } end
  86. RuboCop Today

  87. 30,745,153

  88. Started out with no configuration at all…

  89. Today things are infinitely configurable

  90. Metrics/LineLength: Enabled: false

  91. Metrics/LineLength: Max: 100

  92. Style/StringLiterals: Enabled: false

  93. Style/StringLiterals: EnforcedStyle: double_quotes

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

  95. You can have different settings for the same cops in

    different directories
  96. You can set a target Ruby/Rails version

  97. You can inherit between configuration files

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

  99. rubocop —-auto-gen-config

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

  101. RuboCop started just as a code style checker

  102. •Style •Lint •Layout •Naming •Security •Performance •Performance •Rails •Bundler •Metric

    •Gemspec
  103. RuboCop is a great code formatter

  104. rubocop —-only Layout

  105. RuboCop does some linting better that ruby -w

  106. rubocop —-lint

  107. You can extend RuboCop with your own cops

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

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

  110. 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
  111. RuboCop has a rich ecosystem of extensions

  112. None
  113. None
  114. 189!!!

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

  116. Integration with code quality services • Code Climate • HoundCI

    • SideCI • Codacy
  117. RuboCop has integrations with many editors and IDEs

  118. And you can read about all of this in RuboCop’s

    manual…
  119. rubocop.readthedocs.io

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

  121. The Road to RuboCop 1.0

  122. #1 question I get asked about RuboCop?

  123. When is RuboCop 1.0 coming?

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

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

  126. Review the config of all cops and come up with

    the best defaults
  127. Adjust the list of enabled by default cops

  128. Agree on what’s going to be constituting breaking changes down

    the road
  129. Come up with a less painful process for dealing with

    new cops and configuration changes
  130. That’s a lot of work!

  131. Apes Together Strong!

  132. Rubyists Together Strong!

  133. Felina

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

    Belarus 21.04.2018