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
600
0
Share
Introducing Rubyfmt
Penelope Phippen
November 19, 2019
More Decks by Penelope Phippen
See All by Penelope Phippen
How RSpec Works
penelope_zone
0
6.8k
Quick and easy browser testing using RSpec and Rails 5.1
penelope_zone
1
100
Teaching RSpec to play nice with Rails
penelope_zone
2
170
Little machines that eat strings
penelope_zone
1
120
What is processor (brighton ruby edition)
penelope_zone
0
140
What is processor?
penelope_zone
1
380
extremely defensive coding - rubyconf edition
penelope_zone
0
290
Agile, etc.
penelope_zone
2
250
Extremely Defensive Coding
penelope_zone
0
120
Featured
See All Featured
New Earth Scene 8
popppiees
2
2k
Optimising Largest Contentful Paint
csswizardry
37
3.6k
The SEO identity crisis: Don't let AI make you average
varn
0
430
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
64
53k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
0
950
Paper Plane (Part 1)
katiecoart
PRO
0
6.3k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
290
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Navigating Team Friction
lara
192
16k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
0
260
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.2k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
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]