Pre-evaluation in Ruby

RailsConf 2019 - Pre-evaluation in Ruby by Kevin Deisz


Cloud 66 - Pain Free Rails Deployments
Cloud 66 for Rails acts like your in-house DevOps team to build, deploy and maintain your Rails applications on any cloud or server.

Get $100 Cloud 66 Free Credits with the code: RailsConf-19
($100 Cloud 66 Free Credits, for the new user only, valid till 31st December 2019)

Link to the website:
Link to sign up:

Ruby is historically difficult to optimize due to features that improve flexibility and productivity at the cost of performance. Techniques like Ruby's new JIT compiler and deoptimization code help, but still are limited by techniques like monkey-patching and binding inspection.

Pre-evaluation is another optimization technique that works based on user-defined contracts and assumptions. Users can opt in to optimizations by limiting their use of Ruby's features and thereby allowing further compiler work.

In this talk we'll look at how pre-evaluation works, and what benefits it enables.

RailsConf 2019

00:00:03.280 [Applause] [Music]
00:00:22.279 okay I think we're gonna get started so hi thank you for coming to my talk this
00:00:27.990 is pre evaluation in Ruby my name is Kevin dice I'm at Katy dice on the
00:00:34.680 Internet you can follow me on Twitter please follow me on Twitter I give terrible hot takes on new Ruby stuff I
00:00:41.730 work at a company called culture HQ that is a small person three person company
00:00:46.949 in Boston we're focused on improving culture in the workplace if you're interested in improving the culture of
00:00:52.710 your workplace you should come talk to me so first of all I'm gonna start off with a warning this is a very technical talk
00:00:59.960 but that is not meant to scare you the the function of me here is to relay this
00:01:07.560 information without completely blowing anything out of the water or alienating
00:01:13.950 anyone so if you are an absolute beginner I hope that you will find value in this talk regardless of the technical
00:01:19.679 content so if you know this is difficult stuff so it should be somewhat difficult to understand but if you labor without
00:01:25.709 any value at the end than I have officially failed so if you have any questions again come talk to me
00:01:31.560 afterward so first we're going to talk about compilers Ruby specifically Ruby's
00:01:37.920 compiler and the various steps that it takes and then we're going to talk about extending that compilation process and
00:01:45.060 the value that we can derive from that extension so when we talk about
00:01:50.849 compilers we typically we talk about a couple of steps first there is the lexical analysis step then semantic
00:01:57.869 analysis instruction generation for the virtual machine that was new starting in
00:02:03.149 1.9 various optimization passes and we're gonna talk a lot about that
00:02:09.200 so if we're gonna talk about lexical analysis what we mean is taking a
00:02:14.489 sentence like this sentence Matz is nice so we are nice I know there's supposed to be an and in there but it fits nicer
00:02:21.360 on a slide so anyway so lexical analysis
00:02:26.730 is taking this kind of sentence and breaking it up into individual tokens that we can then apply a grammar
00:02:33.180 so if we're gonna look at this we have to split it up here we have our individual tokens this is just splitting
00:02:39.480 on whitespace for now and we need to understand this we need to understand what parts of speech these things are
00:02:44.970 right this is still the process that other programming languages will go through but for our purposes we're writing an English language compiler
00:02:52.230 this is what your brain is doing in your head so okay we have nouns we understand
00:02:57.780 that that conceptually is a noun is is the to be verb nice as an adjective so
00:03:03.299 as a conjunction and so on and so forth we have a period to end it out so these
00:03:08.370 are our tokens okay great so we've gotten this far and this is a little bit
00:03:14.459 like regex I mean we semantically we understand that like this is kind of what we're looking at or breaking things
00:03:20.159 up into patterns semantic analysis is the process of taking those kinds of
00:03:25.319 tokens applying a grammar and deriving some kind of semantic meaning from those individual tokens so if we're gonna look
00:03:31.950 at this let's let's just take the the verb and the adjective what we need is a
00:03:38.010 name for this section we need a name so we're gonna call it a verb phrase and the pattern is can be verb adjective and
00:03:44.329 so if we go okay now we have a verb phrase and we have these two little trees and we can say okay that's a verb
00:03:51.510 phrase that's a verb phrase now if we go when we add Matz and we we have nouns
00:03:57.510 and so we need a name for this kind of thing as well and so we can go into our grammar and extend it and say okay let's
00:04:03.090 have a noun and then a verb phrase is going to call the subject phrase now here's a little disclaimer I don't
00:04:08.819 actually remember very much about grammar at all I have no idea what a subject object and subjective object I
00:04:14.159 don't know any of those things but this is what we're gonna call it for this part of the of this talk so this is a
00:04:20.459 subject phrase great we've got two subject phrases so then we add a so in
00:04:25.620 the middle I had to look this one up turns out that is called a subordinating conjunction also someone came out to me
00:04:33.900 after my last time I gave this talk and told me that's actually an adverb I don't care you're still going to get
00:04:39.780 I just don't care that's not the purpose of this talk so thank you very much and
00:04:46.200 so we added we have a subordinating conjunction great ok the last token we have is a period we're gonna say ok for
00:04:53.490 the purposes of our grammar and a sentence is always a subordinating conjunction say that three times that's
00:04:58.620 and then a period great we have a sentence and if you look at this this is
00:05:04.770 a tree it's relatively abstract and it represents syntax so we're going to just
00:05:13.260 off the top of my head call that an abstract syntax tree amazing great so we
00:05:19.740 have an AST we have derived this from plain text we've gotten tokens we've
00:05:25.860 applied our grammar and you see that this is very similar to what is already
00:05:32.220 being done in a lot of places in the world in our apps in our code this is an
00:05:37.470 example from RAC which is a recursive descent some some parser thing that Ruby
00:05:43.050 that Ruby uses and this is an example of a calculator right we have various
00:05:48.090 expressions on the left side you have an expression on the right side of an expression you have a plus in the middle that creates another expression we add
00:05:53.100 the two values together this kind of grammar is also used inside of Ruby and parse dot why this is what is used to
00:06:00.090 generate the parser that generates the semantic analysis for us to understand our Ruby programs this is also the part
00:06:05.700 that does the arithmetic so great so we've jet we've used our grammar to
00:06:11.340 generate an abstract syntax tree we need to walk that tree and generate instructions for the virtual machine so
00:06:18.630 we have our tree and what we need to do is all of those blue nodes all those
00:06:25.470 blue nodes that that aren't the light blue the dark blue nodes all those dark blue nodes represent something that
00:06:31.620 we're doing we are we are doing something within our virtual machine to
00:06:36.930 manipulate the state so if we look at just the bottom just the verb phrases
00:06:43.470 what we're really doing is we're pushing an attribute this is all going to be
00:06:48.840 very not scientific doesn't actually make any sense but it's okay we're pushing an attribute that
00:06:55.740 dollar sign to has been meant to represent the second item of that little pattern we're pushing an attribute on to the stack great okay for our subject
00:07:04.770 phrases we're gonna say okay we're going to save an attribute on to that noun but
00:07:11.460 put pop the attribute that we just pushed onto the stack okay so we're saying for the verb adjective is nice
00:07:17.610 now we have nice as an adjective and we know we need to apply that to something when we get up to the subject phrase
00:07:24.479 we're going to pop that off and apply it to the noun so we have Matt's nice Matt's is nice great okay now we have
00:07:31.439 our subordinating conjunction just love saying that word this is basically an if
00:07:36.990 statement right if you if you really think about that that's this is an if statement this is saying the right side
00:07:43.050 is only true if the left side is true great so we're if statements and in virtual
00:07:52.169 machine instructions often are represented by jumps right you put a label somewhere you jump you skip over
00:07:57.539 some instructions if something doesn't resolve to true so we're gonna say we're going to conditionally skip a couple of steps great finally we have our period
00:08:05.849 and we're just gonna say okay we're going to trace the execution of that sentence and we're good to go we have generated our machine instructions and
00:08:13.199 we're gonna do that just by walking the tree okay we start to eliminate things from the tree this is all very not
00:08:19.740 scientific at all it's totally fine and we're gonna we're gonna pretend that
00:08:26.129 this is like pseudocode for our virtual machine all right I mean you understand that I'm writing a compiler for English
00:08:31.379 this is not real but but that's okay okay so we have our instructions great
00:08:37.260 let's optimize those instructions now you might say that there are no optimizations that are available here
00:08:44.610 technically our grammar could support us saying Matz is not nice but we know Matz
00:08:51.000 it's nice we know he is he's a nice guy but it's basically saying the sky is
00:08:56.069 blue right I mean like what we're really saying is this is a constant part of the
00:09:01.579 virtual machine this is a constant part of the instead of instructions and although yes technically the grammar
00:09:07.770 could support us saying Matz is not nice we know he is and so we're just gonna eliminate that and move it up great
00:09:14.670 we've eliminated dead code or unnecessary checks or whatever it is that you want to call it and we have
00:09:20.340 fewer lines to execute our code is more efficient everyone is happy
00:09:25.550 we are nice that is what remains after you take away the left side of that statement alright great so let's look at
00:09:33.450 Ruby let's look at Ruby let's look at what happens okay if 5 plus 3 is 8 then
00:09:39.180 puts HelloWorld you would think this is always true and for the most part you
00:09:47.400 are correct if we go when we look at the instructions that are generated for this
00:09:52.560 we see a whole bunch of stuff now let's look at what this is doing this is putting 5 on to the stack then it's
00:10:00.180 putting 3 on to the stack then it's you know adding them together and that's great I can tell you right now that will
00:10:07.170 always be 8 you can also tell you I'm lying so if you're not too familiar 5
00:10:14.910 plus 3 and Ruby breaks down to 5 send the method plus with whatever is on
00:10:21.750 the right side so let's just call that 8 for now and if we go further along we
00:10:28.650 can also see opt equal ok 8 is always 8 so let's just put true and if you look
00:10:34.410 at the code that's what this breaks down to and so if true we can always just get
00:10:39.450 rid of that if statement entirely in his patella world except when someone tweets something like this and then someone
00:10:47.280 writes this and now we're availing stuff off of Twitter so you know we've really
00:10:52.530 gone to the dark side but you know now what happens is we need
00:10:57.550 to put five plus three you get two all because I decided to tweet no the thing
00:11:06.370 is we have to go back to our code it's like Oh odd of course because the grammar technically supports overwriting that
00:11:12.460 method so okay so I guess we'll go back here and we'll do this D opt thing D opt
00:11:19.840 D optimization it's a fancy word of way of saying okay well technically this is
00:11:27.940 this value but if someone does something stupid then we need to go take care of it again damn and so we haven't we have
00:11:35.860 that instruction I don't want that instruction in there that's the problem so the real question I want people to be
00:11:40.960 asking is why would you ever do this that's the don't like okay so raise your
00:11:48.040 hand raise your hand if you have ever purposefully monkey patched an
00:11:54.280 arithmetic operator on a core class in a production application yeah that's what
00:12:01.180 I thought and the thing is we are dumbing down our compiler we are as a
00:12:08.320 community dumbing down our compiler to support this use case we are not making this optimization because technically
00:12:14.940 someone could be stupid and do that so I
00:12:22.810 wrote a gem it's called pre Val and pre Val is a
00:12:28.060 little gem that assumes you're not stupid so okay so what we're gonna do
00:12:36.460 we're going to go through that same process wherein we derive semantic
00:12:41.740 meaning from a string in Ruby and then we're gonna do a couple more things
00:12:47.050 before we send it back to Ruby so ripper is a baked in library to Ruby it ships
00:12:52.420 you all have it you can require a ripper and Ripper will give you the abstract syntax tree which is great first two
00:12:58.210 steps are taking care of for us we get this it's a bunch of arrays and we're
00:13:03.880 gonna do stuff two arrays that's what we're doing today so if we go in to prevail source this is
00:13:10.309 kind of a dumbed down version but basically what we're gonna do is we're gonna loop over all of the different
00:13:15.350 types of nodes and we're going to build our own nodes that we can then manipulate we descend from sex builder
00:13:23.720 when s expression builder has some basis in computer science I don't know these things and then we we loop over each of
00:13:31.369 the events we define a method that will handle them and we create a whole bunch of nodes great so we've got our node
00:13:37.429 classes we go into nodes and what we need is a way to go from abstract syntax
00:13:43.249 tree we have a way to go from source to AB sex abstract syntax tree we need a way to go back to source any way to go
00:13:50.779 back to source so we have this two source method and we define this module
00:13:56.540 format which is just a little bit of code basically takes every single
00:14:01.999 possible node type in Ruby and converts it back this is not relative this is not
00:14:07.129 new this has been done before it was done the gem sorcerer which I found like literally a couple hours ago
00:14:15.139 back in like I think was one about eight and then but this idea of transforming
00:14:22.249 abstract syntax trees is not new right people have been doing this back into source for a long time for for matters
00:14:28.370 auto for matters specifically if you look at prettier and this is where I
00:14:33.470 tell you that I'm incredibly biased because I wrote the Ruby plugin for prettier but it does the same thing it
00:14:38.870 takes an abstract syntax tree it converts it into an intermediate representation and then prints it out
00:14:45.040 the thing is this is not prettier ruby this is uglier ruby if you look at this it's horrendous it's actually really fun
00:14:52.549 um basically everything is hash rockets
00:14:57.920 which makes me cry and you know there's parentheses all over the place in the
00:15:03.019 spaces you would never want to look at it but it doesn't actually matter because all we're doing is we're passing this back to Ruby no human ever actually
00:15:09.230 sees this Bart now in order to really go
00:15:14.870 into this process what we're gonna do is we're gonna parse that source parser is just as I showed you earlier just two
00:15:20.089 sons Ripper and what we're gonna do is just loop through all of the different
00:15:25.760 visitors that we build to go and manipulate that abstract syntax tree before we convert it back to source and
00:15:31.100 so all we have as a public API is prevailed up process we're just processing a string and again we're just
00:15:38.720 going to that same process that we did right at the beginning of taking a string of whatever applying lexical
00:15:45.860 analysis to get our tokens applying semantic analysis to get our tree
00:15:51.160 manipulating the tree through optimizations and passing it back to Ruby so if you're within the know this
00:15:58.400 is what we're really doing we're visiting each of those nodes and we can build a visitor that does something like
00:16:05.480 this as simple as this if the left side is an integer and the right side is an
00:16:10.760 integer we can just go ahead and do that we're going to assume we're gonna
00:16:15.860 operate under the assumption that people aren't going to be a monkey patching things they shouldn't be monkey patching anyway I did actually have someone
00:16:22.880 raised their hand like two times ago when I gave this talk and I was like why would you do that like I don't know it
00:16:28.610 was bad and they're just ashamed and you know what I'm okay shaming those people I
00:16:34.250 really am I just don't mind so much okay so I'm gonna give you a quick demo there
00:16:40.850 is a small rack application built into the gem so oh look you can see it and
00:16:50.630 everything so if we just have something like equals one just prints out equals
00:16:58.250 one which is great what's funny is it like even if you do this right
00:17:04.880 indentation is totally not understood it doesn't matter again no one's actually looking at this now
00:17:10.760 this thing is technically if you have an adder reader which was a method that is
00:17:20.540 named after the instance variable that is returning it is more efficient to call adder reader than it is to
00:17:26.480 explicitly define this method so it's just gonna do that it's just gonna do
00:17:32.060 that automatically now technically technically someone could have monkey patched the outer reader method I don't care about
00:17:40.250 those people I just don't now technically if you have
00:17:47.059 a value here you can see it lost the outer reader because it wasn't an outer reader anymore and you set it equal to
00:17:53.090 value two then it's going to maintain it but if you set it equal to value one this is technically an outer writer on
00:17:58.460 the left side is I just get rid of that a equals one on the left side is the
00:18:05.120 code that you are writing on the right side is the code that your Ruby sees and this is more efficient this is better
00:18:11.090 and this is doing it without a senior dev yelling you in a PR even better
00:18:17.559 there are certain other things you can do like while true technically loop do
00:18:24.380 is more efficient I don't actually know why I don't actually care and while
00:18:30.890 false just goes away nothing's ever going to get executed so it doesn't
00:18:36.170 matter you can do the same thing with until and until true wrapping your head
00:18:43.490 around double negative just hurts me and obviously there are plenty of other things baked in the real magic of this
00:18:54.050 gem is how you use it technically there's only one part of the public API
00:19:00.620 that really does the magic it's process prove all that process but if you have
00:19:05.750 to be using boot snap which you all are using if you've boot a few run rails new
00:19:11.750 sense rails 5 or if you just decided to use boot snap boot snap is a gem that
00:19:17.150 actually will look at the code that you're going to be executing and go ahead and generate that those machine
00:19:22.940 instructions and write them out to a file to speed up your boot time well you
00:19:29.000 can do terrible things to boot snap you can monkey patch their input to storage
00:19:34.010 method and run stuff through checking if boots app is available and just instead
00:19:40.640 of just compiling you can just process the stuff before it actually does anything all this to say is you're lying
00:19:47.330 to the snap buuut cent doesn't care what is method privacy anyway so that I just wanted to
00:19:55.970 enable everything but I felt a little badly so I did add one thing every one
00:20:01.309 of these visitors you have to opt into technically if you start running prevail on your production application today
00:20:07.720 then it will do absolutely nothing you need to go and explicitly tell it to opt
00:20:12.739 into all of these different things and the reason you have to do this is because technically you could be doing
00:20:18.289 something dumb I lied earlier I will support that as dumb as it is and but if
00:20:24.289 you go and enable all these things and you are opting into that that volatility
00:20:32.379 shall we say and so ok so this is the current list of what prevail does you
00:20:37.909 don't actually need to be able to read all that but basically it will take care of it arithmetic expressions earth make
00:20:43.850 identities it will take care of the outer accessories it has some various things from the faster or gem which is a
00:20:49.970 static analysis tool that will tell you in third and things are faster depending on the Ruby version and it also do a
00:20:55.039 couple things with loops did you know there's a for loop in Ruby technically
00:21:00.470 there is don't use it right so really
00:21:07.100 these optimizations break down into three different categories there are optimizations that can be done now and
00:21:13.489 in my opinion they're very safe no one's going to monkey patch the outer accessor method if you do god help you but
00:21:19.580 they're an instruction elimination I don't really see a way to make Wow false
00:21:27.139 ever execute anything short of a C extension I just don't see how that's
00:21:33.350 possible so these are things that could be done today préval contains those kinds of optimizations it also contains
00:21:39.590 optimizations that can be done with the optimization and if you look at MJ there are the Ruby just-in-time compiler came
00:21:46.909 in three to six there are D optimization techniques being used and so Ruby is
00:21:52.279 doing some of these things a for loop replacement could be done with the
00:21:57.980 optimization wherein it just gets replaced with dot each but if that each it's monkey patch then it falls back to
00:22:03.169 four loops constant folding as in three plus four could be done with the
00:22:08.690 optimization it actually is done with the optimization with the Ridgid and then there are certain things that the
00:22:14.330 compiler can never do that we can operate under certain assumptions and
00:22:20.090 then work with a compiler is never going to replace certain api's with newer or
00:22:25.909 faster api's a compiler shouldn't do that that would be very scary if a compiler did that because that is
00:22:32.720 inherently unsafe the compiler can't possibly have an oven off context about
00:22:38.360 your application to know whether or not it is safe to make that optimization but we as developers have the capacity to
00:22:45.740 build optimizations like this for our own applications that speed up
00:22:51.559 runtime that reduce memory and are good for our applications that the compiler
00:22:57.200 doesn't even have to see they doesn't even have to worry about it so this is
00:23:02.600 the question I get but why why would you do this and the obvious I mean the
00:23:11.090 obvious answer is performance right but I'll tell you right now I'll tell you
00:23:17.330 right now I put this into applicate into production only temporarily shut down my
00:23:23.720 service and I did put it into production because I wanted to be able to say it's
00:23:28.820 in production it is it's running in production great it didn't improve
00:23:33.830 performance at all that's not the virtue of this gem I'm gonna tell you the
00:23:39.020 virtue of this gem if we look at code style code style as in like the source
00:23:46.309 of most arguments we start out with absolutely nothing code just proliferate
00:23:54.590 sand we have people getting angry at each other leaving work early because of robocop right it just happens
00:24:01.020 and then people are like oh we have a way we do code here our way is the best
00:24:07.170 way so then we have senior devs enforcing code in code review what's
00:24:13.290 funny about this progression that I'm running through I spend a company that went through this entire progression and
00:24:18.660 let me tell you it just gets better as you co senior devs enforcing code review that's terrible okay the reason that's
00:24:23.760 terrible senior devs have to read every single on a code that goes out that's awful no don't do that I don't want to
00:24:30.690 have to review code I want to be able to mentor and program that's all I want to do okay so the senior devs get together
00:24:38.370 they decide style guide is gonna be developed okay great there's a Ruby style guide it exists it's on the repo
00:24:44.700 for reveal cop fine we have a style guide we don't have to agree as a community but the thing is if we just go
00:24:51.630 by the style guide it eliminates a lot of arguments okay good okay
00:24:57.120 style guide is in place that's great but it's still being enforced so winters are developed and they're run locally and
00:25:02.550 hopefully in CI that's all well and good but you know the amount of time I have
00:25:09.090 to sit and wait for bundle exec Robocop the amount of time in my life it pains
00:25:15.120 me to think about that it hurts I run it locally and it makes me sad and you know
00:25:22.290 Marie Kondo has taught me it doesn't spark joy and so the final step of this
00:25:29.400 in my understanding of the SAP regression are auto phone matters and compilers fundamentally if I express an
00:25:38.580 idea in speech in English Matz is nice so we are nice or if I say how y'all
00:25:46.679 doing how y'all doing there is slang in there technically according to Oxford
00:25:53.309 English Dictionary I have not spoken with correct English yet somehow some
00:25:58.559 magical way all of your brains are able to comprehend what I am getting across fundamentally it doesn't matter how I
00:26:04.800 express myself if AI haven't offended and B you understand what I am saying
00:26:11.510 it's 2019 our programming languages should be able
00:26:16.590 to understand multiple ways of speech and still come out on top I don't want
00:26:22.499 to have to sit and wait for a linter to tell me that you know loop do is very
00:26:28.799 very tiny slightly more efficient than while true I don't want to sit and wait for that I don't care the compiler
00:26:35.220 should be able to handle that for me I shouldn't have to think about the way I express myself as long as my idea is
00:26:40.559 getting across so we're here we haven't gotten as a community down to
00:26:47.669 the bottom step there are other languages that do get further than us especially if you look at other
00:26:52.710 implementations of Ruby truffle ruby has a lot of stuff based on the growl vm based on all kinds of stuff MRI we have
00:26:59.909 not quite gotten there and I can show you based on the Travis dot yml in my
00:27:06.210 repository what is that what that is is six different static analysis tools
00:27:14.090 that's horrendous and I wrote it I chose this I chose this life why I hate it I
00:27:23.340 hate it really okay that bundle on it that should be taken care of the Ruby
00:27:28.619 language should be able to take care of that Robocop no no no an auto formatter should take care of the style rules the
00:27:35.549 performance rules should be taken care of a by a compiler bin rails reek same thing break man the security
00:27:40.950 vulnerabilities should be taken care of by different API is being chosen by the compiler or an auto fab format are just
00:27:47.460 changing the method entirely or just an arrow being raised bin rails faster or same thing I just want to test my code
00:27:56.669 and I just wanted to ploy my code so if we look back at this style progression
00:28:03.859 let's think of a world where this is the reality Auto for matters and compilers they just
00:28:11.279 take care of it they just take care of that and we're all good at the end of the day what this means is that you are
00:28:17.159 going to write your code you're going to test your code and then you're going to deploy your code you're not going to run
00:28:24.029 six different static analysis tools it doesn't fundamentally matter the way in which
00:28:31.080 you express your code first of all all the microbes optimization that we've
00:28:36.390 been obsessing over as a community you know those don't matter in your app I'm sorry but they just don't fundamentally
00:28:43.649 most of the time we're running in a micro optimization we actually care about it it's within a framework it's within rails there are hotspots and
00:28:50.580 rails that need to be micro optimized very very few applications actually need that level of optimization more often
00:28:56.460 than not your sales process is more broken than your for loop at the end of
00:29:03.659 the day I just want to write Ruby code I don't want to have to obsess about the
00:29:09.570 multiple steps that I have to take in order to get my code out in order to somehow avoid a conversation with
00:29:15.149 another developer because they like parentheses so so ya prevail is ready
00:29:22.529 for use you can use it in production I promise it won't take down your application the nice thing about the
00:29:28.649 approach is that there is zero runtime performance loss right this is all done a compile time when rails or sorry when
00:29:36.149 Ruby is out is compiling your code there is a step between the time that it generates the abstract syntax tree and
00:29:42.600 the time when it is executing that code that time is before start up your application isn't slowed down by this
00:29:48.840 this isn't doing anything live in production this is just doing things locally and with that that is everything
00:29:55.409 I got thank you very much
00:30:02.770 I had like three cups of coffee so I went really fast turns out so if anyone
00:30:08.470 has any questions I have plenty of time right right yes so the question was can
00:30:15.070 the gem validate that its transformations are safe and it's funny
00:30:20.920 I was about halfway through writing that validation code when I realized the integer is monkey pestered by Rails and
00:30:29.770 I realized oh that won't work turns out that rails monkey patches integer for
00:30:36.130 date adding and stuff like that actually minor key patches a lot of stuff so I'm
00:30:43.360 just going to go ahead and run without validations for this but I'll accept a PR yeah is there a way to reformat the
00:30:53.610 the compiled code back into source yes there is except I hate that the reason I hate
00:31:02.890 that is because the entire virtue of this gem is allowing you to express Ruby
00:31:09.160 code in multiple ways without being forced into one specific way of expressing it so yes
00:31:16.059 you totally can but honestly at that point I would just point you to revoke op - - fix or autoformat or whatever the
00:31:24.160 argument actually is yeah the question is can you add your own extensions you absolutely can yeah it's it's it's in
00:31:31.630 the documentation you just define a class that has any on underscore and
00:31:38.020 then the node type and then you do whatever you like so you can really shoot yourself in the foot any other
00:31:46.960 questions great well I will be available for any questions afterward thank you
00:31:52.210 very much for coming [Applause]
00:31:58.080 [Music]