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
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
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
86
Featured
See All Featured
5 minutes of I Can Smell Your CMS
philhawksworth
202
19k
Visualization
eitanlees
144
15k
Faster Mobile Websites
deanohume
304
30k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
Thoughts on Productivity
jonyablonski
67
4.3k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
Adopting Sorbet at Scale
ufuk
73
9k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Learning to Love Humans: Emotional Interface Design
aarron
272
40k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
504
140k
What's new in Ruby 2.0
geeforr
342
31k
Teambox: Starting and Learning
jrom
132
8.7k
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]