Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Introducing Rubyfmt
Search
Penelope Phippen
November 19, 2019
0
520
Introducing Rubyfmt
Penelope Phippen
November 19, 2019
Tweet
Share
More Decks by Penelope Phippen
See All by Penelope Phippen
How RSpec Works
penelope_zone
0
6.4k
Quick and easy browser testing using RSpec and Rails 5.1
penelope_zone
1
75
Teaching RSpec to play nice with Rails
penelope_zone
2
120
Little machines that eat strings
penelope_zone
1
80
What is processor (brighton ruby edition)
penelope_zone
0
94
What is processor?
penelope_zone
1
340
extremely defensive coding - rubyconf edition
penelope_zone
0
250
Agile, etc.
penelope_zone
2
210
Extremely Defensive Coding
penelope_zone
0
85
Featured
See All Featured
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.3k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
4
120
Documentation Writing (for coders)
carmenintech
65
4.3k
Debugging Ruby Performance
tmm1
73
12k
How To Stay Up To Date on Web Technology
chriscoyier
787
250k
Optimising Largest Contentful Paint
csswizardry
31
2.8k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Visualization
eitanlees
143
15k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
231
17k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
125
18k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
364
22k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
Transcript
@penelope_zone ✨Introducing✨ rubyfmt
@penelope_zone
@penelope_zone Updates
@penelope_zone This is gonna get real emotional
@penelope_zone Penelope She/Her Trans Woman
@penelope_zone Ruby Central Director
@penelope_zone No longer an RSpec maintainer
@penelope_zone Thanks to Jon, Myron, Andy, and David
@penelope_zone Rubyfmt is gonna be my open source focus for
the next while
@penelope_zone What is Rubyfmt?
@penelope_zone @penelope_zone
@penelope_zone
@penelope_zone
@penelope_zone x
@penelope_zone I can’t unsee this
@penelope_zone Ruby autoformatter
@penelope_zone
@penelope_zone
@penelope_zone
@penelope_zone No formatting related configuration options
@penelope_zone “Unix tool”
@penelope_zone stdin/file name
@penelope_zone stdout/in place
@penelope_zone Not a gem!
@penelope_zone Probably > 6 months until something you can use
daily
@penelope_zone Why are you doing this?
@penelope_zone Principle
@penelope_zone
@penelope_zone
@penelope_zone "TDD results in better code"
@penelope_zone This is another way of discussing tradeoffs
@penelope_zone My Principles
@penelope_zone Do one thing well Correctness beats simplicity Speed is
needed at any cost
@penelope_zone Do one thing well
@penelope_zone
@penelope_zone I u Rubocop team
@penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails
@penelope_zone
@penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails
@penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails
@penelope_zone
@penelope_zone $%&%'()*%%*+,)$%&%)%('- )'%$*($,%&%'%(&,-$()+$- +-%)$&+'')-%',*&)$&&'(% )$)-)&$*(+$%'-)&%+)*$'$ &*%)'-%$'-'$(*-(--$,)-- %&*,$+&*$*,))$)**,()'(* ,)++$-'&*'*'-*$)(-$($(+ '(%))$$),-$$+*$*(,%*(%- -&$(**,+&+'(&,,+)'&$)')
@penelope_zone
@penelope_zone Rubyformat is literally a different kind of tool
@penelope_zone
@penelope_zone Doing one thing extremely well
@penelope_zone By definition configuration options imply doing multiple things
@penelope_zone Support every possible combination
@penelope_zone Support every possible combination ❌
@penelope_zone Support one consistent style ✅
@penelope_zone bars = if foo baz else qux end
@penelope_zone bars = if foo baz else qux end
@penelope_zone bars = if foo baz else qux end
@penelope_zone bars = “one” \ “two”
@penelope_zone bars = “one” \ “two”
@penelope_zone bars = “one” \ “two”
@penelope_zone Rubyfmt always indents two relative to the parent construct
@penelope_zone bars = if foo baz else qux end
@penelope_zone bars = “one” \ “two”
@penelope_zone This is a tradeoff
@penelope_zone Correctness beats simplicity
@penelope_zone How do you parse Ruby?
@penelope_zone
@penelope_zone
@penelope_zone The parser output is really simple to deal with
@penelope_zone $ srb tc -p parse-tree -e 'a' Send {
receiver = NULL method = <U a> args = [ ] }
@penelope_zone $ srb tc -p parse-tree -e ‘a(1)’ Send {
receiver = NULL method = <U a> args = [ Integer { val = "1" } ] }
@penelope_zone
@penelope_zone
@penelope_zone For like 99% of Ruby programs this will never
be a problem
@penelope_zone I refuse to build an auto formatter which could
parse your program wrong
@penelope_zone
@penelope_zone Disadvantages
@penelope_zone Ripper requires a booted Ruby interpreter
@penelope_zone Running Ruby is slower than not running Ruby
@penelope_zone Ripper's output is painful
@penelope_zone $ rr 'a' [:program, [[:vcall, [:@ident, "a", [1, 0]]]]]
@penelope_zone $ rr 'a' [:program, [[:method_add_arg, [:fcall, [:@ident, "a", [1,
0]]], [:arg_paren, [:args_add_block, [[:@int, "1", [1, 2]]], false]]]]]
@penelope_zone Rubyfmt uses ripper to ensure compatibility
@penelope_zone Building against this is 90% of the dev time
@penelope_zone I would be done if I used whitequark
@penelope_zone I may end up separating Ripper from the Ruby
interpreter as a general purpose tool
@penelope_zone It’s totally a good thing that Sorbet and Rubocop
did not do this
@penelope_zone Speed is needed at any cost
@penelope_zone Gofmt executes on 25ms on even very large files
@penelope_zone
@penelope_zone
@penelope_zone How fast is fast enough?
@penelope_zone 16ms
@penelope_zone 16ms 16ms 16ms 16ms 16ms 16ms 16ms 100ms
@penelope_zone 100ms on 3k lines of Ruby code is the
goal
@penelope_zone ruby —disable=gems -e ‘’
@penelope_zone 16ms 16ms Ruby boot 25ms
@penelope_zone ruby -e ‘'
@penelope_zone 16ms 16ms Ruby boot with gems 75ms 16ms 16ms
16ms
@penelope_zone We can’t run the parser in the remaining 25ms
@penelope_zone So we can't use gems
@penelope_zone Speed is needed at any cost
@penelope_zone 100 100 100 100 100 100 100 100
@penelope_zone bundle exec <anything>
@penelope_zone 100m 100m 100m 100m 100m 100m 100m 100m Bundler
boot 100 100 100 100 100 100 100 100
@penelope_zone So we definitely can’t use bundler
@penelope_zone bundle exec rubocop <4 line file> 800ms 100m 100m
100m 100m 100m 100m 100m 100m Bundler boot Rubocop 100 100 100 100 100 100 100 100
@penelope_zone And we definitely can’t inherit from Rubocop
@penelope_zone So what does it look like today?
@penelope_zone
@penelope_zone
@penelope_zone
@penelope_zone
@penelope_zone So the complete Ruby version isn’t fast enough
@penelope_zone
@penelope_zone Parse.y isn’t really separable from the ruby interpreter
@penelope_zone ❤
@penelope_zone
@penelope_zone
@penelope_zone The initial test of the Rust version is that
it is fast enough
@penelope_zone I am essentially building an entire set of tooling
in Rust to work with Ruby source code
@penelope_zone Summary
@penelope_zone Do one thing well Correctness beats simplicity Speed is
needed at any cost
@penelope_zone These principles imply a tonne of work
@penelope_zone Rubyfmt is the most technically complex project I have
ever worked on
@penelope_zone Sweating the details
@penelope_zone Thanks to
@penelope_zone https://github.com/ penelopezone/rubyfmt
@penelope_zone That’s all @penelope_zone
[email protected]