Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
All About RuboCop (RubyKaigi 2018)
Bozhidar Batsov
May 31, 2018
Programming
4
7.2k
All About RuboCop (RubyKaigi 2018)
Slide deck from my talk at RubyKaigi 2018 in Sendai.
Bozhidar Batsov
May 31, 2018
Tweet
Share
More Decks by Bozhidar Batsov
See All by Bozhidar Batsov
bbatsov
0
110
bbatsov
0
310
bbatsov
0
110
bbatsov
0
140
bbatsov
0
73
bbatsov
0
150
bbatsov
0
230
bbatsov
1
260
bbatsov
3
100
Other Decks in Programming
See All in Programming
numeroanddev
1
240
larsrh
0
110
abeta
1
210
raulh82vlc
1
270
kimyan
3
440
konstantin_diener
0
130
aftiopk
0
130
grapecity_dev
0
190
mihyaeru21
0
370
lovee
9
2.9k
grapecity_dev
0
190
nearmugi
0
190
Featured
See All Featured
carmenhchung
31
1.5k
tammielis
237
23k
destraynor
146
19k
cromwellryan
104
6.2k
colly
188
14k
rasmusluckow
318
18k
andyhume
63
3.7k
kastner
54
1.9k
maggiecrowley
10
510
jnunemaker
PRO
40
4.6k
malarkey
392
61k
colly
66
3k
Transcript
Hello!
Божидар
None
Bozhidar
Божo
Bozho cool
Bozo not cool
Bozho is not a bozo!
Bozho is not a bozo! (at least he claims so)
Bug cool
(The RuboCop Guy)
Sofia, Bulgaria Sofia, Bulgaria
None
Grigor Dimitrov vs Kei Nishikori
None
Lukanka
Bulgarian Cheese
Rakia Connecting People
Shopska Salad (use only with Rakia)
None
bbatsov
Ruby & Rails style guides
None
None
None
None
First time in Japan!!!
None
None
None
None
None
None
All About RuboCop by Bozhidar Batsov (a.k.a. bug)
Writing Lint for Ruby by Masataka Kuwabara (a.k.a. Pocke) http://rubykaigi.org/2017/presentations/p_ck_.html
Improve Ruby coding style rules and Lint by Koichi Ito
http://rubykaigi.org/2018/presentations/koic.html#jun01
Something About RuboCop by Bozhidar Batsov (a.k.a. bug)
RuboCop in a Nutshell
A Ruby static code analysis tool aimed to enforce the
Ruby Community Style Guide
In other words…
It keeps your codebase consistent
It saves you time
It advances the Ruby language forward
None
32,608,128
Provides an efficient way for codebases to be updated
Disclaimer
Lint tools are not a replacement for common sense
Why an entire talk about a mere lint tool?
None
None
Notable Japanese Contributors • Yuji Nakayama (@yujinakayama) • Masataka Kuwabara
(@pocke) • Koichi Ito (@koic) • Yukihiro "Matz" Matsumoto (@matz) ;-)
None
https://github.com/rubocop-jp/issues
https://github.com/fortissimo1997/ruby-style-guide
314 open issues
100 volunteers
3.14 issues/person
Coincidence?
Or providence?
A Brief History of Time
A Brief History of RuboCop
2011 The Ruby Style Guide
None
May, 2012 RuboCop 0.0.0
May, 2012 RuboCop 0.0.0
None
Static code analysis with regular expressions …
None
Nov, 2012 Along Came Jonas
None
None
Spring, 2013 Return of the Jedi
Using a proper parser!
None
Struggles with Ripper
Ripper supported only MRI
I wanted to build a cross-platform tool
Ripper had (has?) almost no documentation
Ripper is tied to the underlying Ruby runtime
…which means you can’t parse code as Ruby 2.3 while
running Ruby 2.5
Ripper has a very hard to work with AST representation
pry(main)> Ripper.sexp('alias :some :test') => [:program, [[:alias, [:symbol_literal, [:symbol, [:@ident,
"some", [1, 7]]]], [:symbol_literal, [:symbol, [:@ident, "test", [1, 13]]]]]]]
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
And many quirks…
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"]]
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, ")"]]
Which no one really considered problematic…
None
None
Parser: A New Hope
parser is cross-platform
well documented
supports multiple parsing targets (Ruby 1.9 - 2.5)
None
easy to work with ast format https://github.com/whitequark/ast
p Parser::CurrentRuby.parse("2 + 2") # (send # (int 2) :+
# (int 2))
AST node source location mappings
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 # "+"
$ 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
powerful code rewriting capabilities
None
Struggles with Parser
Parser was brand new
Had no real users
And had plenty of bugs…
None
Peter Zotov (@whitequark)
28th May, 2013 State of Unity
RuboCop 0.8
1st July, 2013 RuboCop 0.9
Formatters
Auto-correct
None
Commissioner (single parsing run triggers all cops)
Results caching (rubocop —-cache)
Parallel Checks (rubocop —-parallel)
Pattern Matching (regex-style matching for ast nodes)
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)
Style/ArrayJoin (now) def_node_matcher :join_candidate?, '(send $array :* $str)' def on_send(node)
join_candidate?(node) { add_offense(node, location: :selector) } end
RuboCop Today
Started out with no configuration at all…
Today things are infinitely configurable
Metrics/LineLength: Enabled: false
Metrics/LineLength: Max: 100
Style/StringLiterals: Enabled: false
Style/StringLiterals: EnforcedStyle: double_quotes
You can set a target Ruby/Rails version
You can limit cops to certain folders with Exclude/Include directives
You can have different settings for the same cops in
different directories
You can inherit between configuration files
You can auto-generate an initial configuration for your project
rubocop —-auto-gen-config
And you can read about all of this in RuboCop’s
manual…
rubocop.readthedocs.io
gry is a smarter alternative to the built-in command https://github.com/pocke/gry
RuboCop started as a code style checker
•Style •Lint •Layout •Naming •Security •Performance •Performance •Rails •Bundler •Metric
•Gemspec
RuboCop is a great code formatter
rubocop —-only Layout -a
RuboCop does some linting better than ruby -w
rubocop —-lint
You can extend RuboCop with your own cops
Granite (business actions architecture for Rails applications) https://toptal.github.io/granite/
$ bundle exec rake new_cop[Department/Name]
https://rubocop.readthedocs.io/ en/latest/development/
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
RuboCop has a rich ecosystem of extensions
None
None
189!!!
Notable Extensions • rubocop-rspec • guard-rubocop • rubocop-sequel • rubocop-cask
Integration with code quality services •Code Climate •HoundCI •SideCI •Codacy
RuboCop has integrations with many editors and IDEs
Emacs 26.1 is out and it’s amazing!
(spacemacs)
The Road to RuboCop 1.0
#1 question I get asked about RuboCop?
When is RuboCop 1.0 coming?
Remove Rails (and maybe Performance) cops from RuboCop’s core
Provide a proper (and stable) API for writing RuboCop extensions
Introducing the brand new RuboCop HQ!
https://github.com/bbatsov/rubocop
https://github.com/rubocop-hq/rubocop
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)
Review the config of all cops and come up with
the best defaults
Ideally each default should be backed by solid data/rationale
Adjust the list of enabled by default cops
Come up with a less painful process for dealing with
new cops and configuration changes
mry makes RuboCop upgrades less painful https://github.com/pocke/mry
Agree on what’s going to be constituting breaking changes down
the road
Breaking changes •Cop API changes •Extension API changes •Dropping support
for Ruby versions •Renaming/removing cops •Renaming/changing/removing config values
Cop status “New” in minor releases
Cop status gets changed to Enabled/Disabled in major releases
Extra Cop metadata •Version added •Version default config changed •Version
deprecated
eslint.org-grade manual/website
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
None
That’s a lot of work!
Apes Together Strong!
Rubyists Together Strong!
Felina
Thanks! twitter: @bbatsov github: @bbatsov http//batsov.com http://emacsredux.com RubyKaigi Sendai, Japan
31.05.2018