Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introducing Rubyfmt

C727e695ffdc7bf95f9f1a97da592648?s=47 Penelope Phippen
November 19, 2019
400

Introducing Rubyfmt

C727e695ffdc7bf95f9f1a97da592648?s=128

Penelope Phippen

November 19, 2019
Tweet

Transcript

  1. @penelope_zone ✨Introducing✨ rubyfmt

  2. @penelope_zone

  3. @penelope_zone Updates

  4. @penelope_zone This is gonna get real emotional

  5. @penelope_zone Penelope She/Her Trans Woman

  6. @penelope_zone Ruby Central Director

  7. @penelope_zone No longer an RSpec maintainer

  8. @penelope_zone Thanks to Jon, Myron, Andy, and David

  9. @penelope_zone Rubyfmt is gonna be my open source focus for

    the next while
  10. @penelope_zone What is Rubyfmt?

  11. @penelope_zone @penelope_zone

  12. @penelope_zone

  13. @penelope_zone

  14. @penelope_zone x

  15. @penelope_zone I can’t unsee this

  16. @penelope_zone Ruby autoformatter

  17. @penelope_zone

  18. @penelope_zone

  19. @penelope_zone

  20. @penelope_zone No formatting related configuration options

  21. @penelope_zone “Unix tool”

  22. @penelope_zone stdin/file name

  23. @penelope_zone stdout/in place

  24. @penelope_zone Not a gem!

  25. @penelope_zone Probably > 6 months until something you can use

    daily
  26. @penelope_zone Why are you doing this?

  27. @penelope_zone Principle

  28. @penelope_zone

  29. @penelope_zone

  30. @penelope_zone "TDD results in better code"

  31. @penelope_zone This is another way of discussing tradeoffs

  32. @penelope_zone My Principles

  33. @penelope_zone Do one thing well Correctness beats simplicity Speed is

    needed at any cost
  34. @penelope_zone Do one thing well

  35. @penelope_zone

  36. @penelope_zone I u Rubocop team

  37. @penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails

  38. @penelope_zone

  39. @penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails

  40. @penelope_zone Formatting Metrics Linting Naming Security Style Bundler Gemspec Rails

  41. @penelope_zone

  42. @penelope_zone $%&%'()*%%*+,)$%&%)%('- )'%$*($,%&%'%(&,-$()+$- +-%)$&+'')-%',*&)$&&'(% )$)-)&$*(+$%'-)&%+)*$'$ &*%)'-%$'-'$(*-(--$,)-- %&*,$+&*$*,))$)**,()'(* ,)++$-'&*'*'-*$)(-$($(+ '(%))$$),-$$+*$*(,%*(%- -&$(**,+&+'(&,,+)'&$)')

  43. @penelope_zone

  44. @penelope_zone Rubyformat is literally a different kind of tool

  45. @penelope_zone

  46. @penelope_zone Doing one thing extremely well

  47. @penelope_zone By definition configuration options imply doing multiple things

  48. @penelope_zone Support every possible combination

  49. @penelope_zone Support every possible combination ❌

  50. @penelope_zone Support one consistent style ✅

  51. @penelope_zone bars = if foo baz else qux end

  52. @penelope_zone bars = if foo baz else qux end

  53. @penelope_zone bars = if foo baz else qux end

  54. @penelope_zone bars = “one” \ “two”

  55. @penelope_zone bars = “one” \ “two”

  56. @penelope_zone bars = “one” \ “two”

  57. @penelope_zone Rubyfmt always indents two relative to the parent construct

  58. @penelope_zone bars = if foo baz else qux end

  59. @penelope_zone bars = “one” \ “two”

  60. @penelope_zone This is a tradeoff

  61. @penelope_zone Correctness beats simplicity

  62. @penelope_zone How do you parse Ruby?

  63. @penelope_zone

  64. @penelope_zone

  65. @penelope_zone The parser output is really simple to deal with

  66. @penelope_zone $ srb tc -p parse-tree -e 'a' Send {

    receiver = NULL method = <U a> args = [ ] }
  67. @penelope_zone $ srb tc -p parse-tree -e ‘a(1)’ Send {

    receiver = NULL method = <U a> args = [ Integer { val = "1" } ] }
  68. @penelope_zone

  69. @penelope_zone

  70. @penelope_zone For like 99% of Ruby programs this will never

    be a problem
  71. @penelope_zone I refuse to build an auto formatter which could

    parse your program wrong
  72. @penelope_zone

  73. @penelope_zone Disadvantages

  74. @penelope_zone Ripper requires a booted Ruby interpreter

  75. @penelope_zone Running Ruby is slower than not running Ruby

  76. @penelope_zone Ripper's output is painful

  77. @penelope_zone $ rr 'a' [:program, [[:vcall, [:@ident, "a", [1, 0]]]]]

  78. @penelope_zone $ rr 'a' [:program, [[:method_add_arg, [:fcall, [:@ident, "a", [1,

    0]]], [:arg_paren, [:args_add_block, [[:@int, "1", [1, 2]]], false]]]]]
  79. @penelope_zone Rubyfmt uses ripper to ensure compatibility

  80. @penelope_zone Building against this is 90% of the dev time

  81. @penelope_zone I would be done if I used whitequark

  82. @penelope_zone I may end up separating Ripper from the Ruby

    interpreter as a general purpose tool
  83. @penelope_zone It’s totally a good thing that Sorbet and Rubocop

    did not do this
  84. @penelope_zone Speed is needed at any cost

  85. @penelope_zone Gofmt executes on 25ms on even very large files

  86. @penelope_zone

  87. @penelope_zone

  88. @penelope_zone How fast is fast enough?

  89. @penelope_zone 16ms

  90. @penelope_zone 16ms 16ms 16ms 16ms 16ms 16ms 16ms 100ms

  91. @penelope_zone 100ms on 3k lines of Ruby code is the

    goal
  92. @penelope_zone ruby —disable=gems -e ‘’

  93. @penelope_zone 16ms 16ms Ruby boot 25ms

  94. @penelope_zone ruby -e ‘'

  95. @penelope_zone 16ms 16ms Ruby boot with gems 75ms 16ms 16ms

    16ms
  96. @penelope_zone We can’t run the parser in the remaining 25ms

  97. @penelope_zone So we can't use gems

  98. @penelope_zone Speed is needed at any cost

  99. @penelope_zone 100 100 100 100 100 100 100 100

  100. @penelope_zone bundle exec <anything>

  101. @penelope_zone 100m 100m 100m 100m 100m 100m 100m 100m Bundler

    boot 100 100 100 100 100 100 100 100
  102. @penelope_zone So we definitely can’t use bundler

  103. @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
  104. @penelope_zone And we definitely can’t inherit from Rubocop

  105. @penelope_zone So what does it look like today?

  106. @penelope_zone

  107. @penelope_zone

  108. @penelope_zone

  109. @penelope_zone

  110. @penelope_zone So the complete Ruby version isn’t fast enough

  111. @penelope_zone

  112. @penelope_zone Parse.y isn’t really separable from the ruby interpreter

  113. @penelope_zone ❤

  114. @penelope_zone

  115. @penelope_zone

  116. @penelope_zone The initial test of the Rust version is that

    it is fast enough
  117. @penelope_zone I am essentially building an entire set of tooling

    in Rust to work with Ruby source code
  118. @penelope_zone Summary

  119. @penelope_zone Do one thing well Correctness beats simplicity Speed is

    needed at any cost
  120. @penelope_zone These principles imply a tonne of work

  121. @penelope_zone Rubyfmt is the most technically complex project I have

    ever worked on
  122. @penelope_zone Sweating the details

  123. @penelope_zone Thanks to

  124. @penelope_zone https://github.com/ penelopezone/rubyfmt

  125. @penelope_zone That’s all @penelope_zone penelopedotzone@gmail.com