Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Introducing Rubyfmt
Search
Penelope Phippen
November 19, 2019
0
530
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
84
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
88
Featured
See All Featured
Speed Design
sergeychernyshev
25
640
Designing for Performance
lara
604
68k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
1
210
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
150
How to Think Like a Performance Engineer
csswizardry
20
1.1k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
6
470
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
1.2k
Unsuck your backbone
ammeep
669
57k
Faster Mobile Websites
deanohume
305
30k
A Modern Web Designer's Workflow
chriscoyier
693
190k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
16
2.2k
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]