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
540
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.5k
Quick and easy browser testing using RSpec and Rails 5.1
penelope_zone
1
77
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
95
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
89
Featured
See All Featured
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
46
2.4k
Building Adaptive Systems
keathley
40
2.4k
Speed Design
sergeychernyshev
28
820
How to Think Like a Performance Engineer
csswizardry
22
1.4k
Thoughts on Productivity
jonyablonski
69
4.5k
Agile that works and the tools we love
rasmusluckow
328
21k
Product Roadmaps are Hard
iamctodd
PRO
51
11k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
Faster Mobile Websites
deanohume
306
31k
Producing Creativity
orderedlist
PRO
344
40k
Optimising Largest Contentful Paint
csswizardry
34
3.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
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 penelopedotzone@gmail.com