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

All About RuboCop (Saint P RubyConf)

All About RuboCop (Saint P RubyConf)

Slide-deck from my talk at the Saint P RubyConf 2018 in Saint Petersburg, Russia.

1be785d1d788b82929e55fc83a9f0aaa?s=128

Bozhidar Batsov

August 12, 2018
Tweet

Transcript

  1. None
  2. Привет, ребята!

  3. Божидар

  4. Божo cool

  5. (The RuboCop Guy)

  6. Sofia, Bulgaria Sofia, Bulgaria

  7. None
  8. Bulgarian Cheese

  9. Болгарка

  10. Слънчев Бряг

  11. Златни пясъци

  12. Банско

  13. None
  14. None
  15. Grigor Dimitrov vs Andrey Rublev

  16. None
  17. Lukanka

  18. Rakia Connecting People

  19. Shopska Salad (use only with Rakia)

  20. None
  21. bbatsov

  22. Ruby & Rails style guides

  23. None
  24. None
  25. None
  26. None
  27. First time in SPB!!!

  28. None
  29. None
  30. None
  31. None
  32. #RubyKaraoke

  33. Много городов у нас в России, Нету пальцев столько на

    ногах,
  34. С каждым годом всё они красивей, Утопают в солнце и

    в снегах.
  35. В Ростове шикарные плюхи, Размером с большую печать,

  36. В Москве оху*тельно нюхать,

  37. В Челябинске лучше торчать.

  38. А в Питере…

  39. None
  40. None
  41. All About RuboCop by Bozhidar Batsov (a.k.a. bug)

  42. rubocop.readthedocs.io

  43. Something About RuboCop by Bozhidar Batsov (a.k.a. bug)

  44. RuboCop in a Nutshell

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

    Ruby Community Style Guide
  46. In other words…

  47. It keeps your codebase consistent

  48. It saves you time

  49. It advances the Ruby language forward

  50. None
  51. 32,608,128

  52. Provides an efficient way for codebases to be updated

  53. Disclaimer

  54. Lint tools are not a replacement for common sense

  55. Why an entire talk about a mere lint tool?

  56. None
  57. None
  58. 314 open issues

  59. 100 volunteers

  60. 3.14 issues/person

  61. Coincidence?

  62. Or providence?

  63. A Brief History of Time

  64. A Brief History of RuboCop

  65. 2011 The Ruby Style Guide

  66. None
  67. May, 2012 RuboCop 0.0.0

  68. May, 2012 RuboCop 0.0.0

  69. None
  70. Static code analysis with regular expressions …

  71. None
  72. Nov, 2012 Along Came Jonas

  73. None
  74. None
  75. Spring, 2013 Return of the Jedi

  76. Using a proper parser!

  77. None
  78. Struggles with Ripper

  79. Ripper supported only MRI

  80. I wanted to build a cross-platform tool

  81. Ripper had (has?) almost no documentation

  82. Ripper is tied to the underlying Ruby runtime

  83. …which means you can’t parse code as Ruby 2.3 while

    running Ruby 2.5
  84. Ripper has a very hard to work with AST representation

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

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

  88. 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"]]
  89. 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, ")"]]
  90. Which no one really considered problematic…

  91. None
  92. None
  93. Parser: A New Hope From Russia with Love

  94. parser is cross-platform

  95. well documented

  96. supports multiple parsing targets (Ruby 1.9 - 2.5)

  97. None
  98. easy to work with ast format https://github.com/whitequark/ast

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

    # (int 2))
  100. AST node source location mappings

  101. 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 # "+"
  102. $ 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
  103. powerful code rewriting capabilities

  104. None
  105. Struggles with Parser

  106. Parser was brand new

  107. Had no real users

  108. And had plenty of bugs…

  109. None
  110. Peter Zotov (@whitequark)

  111. 28th May, 2013 State of Unity

  112. RuboCop 0.8

  113. 1st July, 2013 RuboCop 0.9

  114. Formatters

  115. Auto-correct

  116. None
  117. Commissioner (single parsing run triggers all cops)

  118. Results caching (rubocop —-cache)

  119. Parallel Checks (rubocop —-parallel)

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

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

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

  124. Started out with no configuration at all…

  125. Today things are infinitely configurable

  126. Metrics/LineLength: Enabled: false

  127. Metrics/LineLength: Max: 100

  128. Style/StringLiterals: Enabled: false

  129. Style/StringLiterals: EnforcedStyle: double_quotes

  130. You can set a target Ruby/Rails version

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

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

    different directories
  133. You can inherit between configuration files

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

  135. rubocop —-auto-gen-config

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

    manual…
  137. rubocop.readthedocs.io

  138. gry is a smarter alternative to the built-in command https://github.com/pocke/gry

  139. RuboCop started as a code style checker

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

    •Gemspec
  141. RuboCop is a great code formatter

  142. rubocop —-only Layout -a

  143. RuboCop does some linting better than ruby -w

  144. rubocop —-lint

  145. You can extend RuboCop with your own cops

  146. Granite (business actions architecture for Rails applications) https://toptal.github.io/granite/

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

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

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

  151. None
  152. None
  153. 189!!!

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

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

  156. RuboCop has integrations with many editors and IDEs

  157. Emacs 26.1 is out and it’s amazing!

  158. (spacemacs)

  159. The Road to RuboCop 1.0

  160. #1 question I get asked about RuboCop?

  161. When is RuboCop 1.0 coming?

  162. “Using RuboCop is exciting. You never know if —auto-correct is

    going to work or not!” — Dick Sutterer
  163. RuboCop changed my life! I used to go to horror

    movies to get an adrenaline rush - now I simply upgrade RuboCop! — Anton Devilov
  164. RuboCop’s learning curve is amazing! I’m still amazed it never

    ends! — Aaron Punisherson (@hardlove)
  165. “When faced with overwhelming odds just release 1.0 and it

    will all be OK!”
  166. Remove Rails (and maybe Performance) cops from RuboCop’s core

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

  168. Introducing the brand new RuboCop HQ!

  169. https://github.com/bbatsov/rubocop

  170. https://github.com/rubocop-hq/rubocop

  171. 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)
  172. Review the config of all cops and come up with

    the best defaults
  173. Ideally each default should be backed by solid data/rationale

  174. Adjust the list of enabled by default cops

  175. Come up with a less painful process for dealing with

    new cops and configuration changes
  176. mry makes RuboCop upgrades less painful https://github.com/pocke/mry

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

    the road
  178. Breaking changes •Cop API changes •Extension API changes •Dropping support

    for Ruby versions •Renaming/removing cops •Renaming/changing/removing config values
  179. Cop status “New” in minor releases

  180. Cop status gets changed to Enabled/Disabled in major releases

  181. Extra Cop metadata •Version added •Version default config changed •Version

    deprecated
  182. eslint.org-grade manual/website

  183. 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
  184. None
  185. That’s a lot of work!

  186. Apes Together Strong!

  187. Rubyists Together Strong!

  188. Felina

  189. Thanks! twitter: @bbatsov github: @bbatsov http//batsov.com http://emacsredux.com Saint P RubyConf

    Saint Petersburg, Russia 10.06.2018