RubyConf AU 2013
Sinatra in SIX lines - How to do crazy stuff with Ruby
Summarized using AI

Sinatra in SIX lines - How to do crazy stuff with Ruby

by Konstantin Haase

In the video "Sinatra in SIX lines - How to do crazy stuff with Ruby," Konstantin Haase presents a playful and engaging exploration of coding in Ruby, particularly focusing on the Sinatra framework. The talk was part of RubyConf AU 2013 and aims to demonstrate how to create compact and effective Ruby code while having fun with the language.

Key points discussed throughout the session include:

- Introduction to Ruby and Sinatra: Haase introduces Ruby's creator, Yukihiro Matsumoto, and mentions his work with Travis CI and various projects including Sinatra and Rinas. He sets the tone by stating that the session will not be a conventional learning environment.

- Obfuscation and Fun Coding: Emphasizing creativity in coding, he argues that obfuscation can be a byproduct of writing concise code. He encourages attendees to think outside traditional coding boundaries.

- Interactive Examples and Exercises: Throughout the talk, Haase engages the audience with code snippets and real-time evaluation, such as evaluating expressions using ternary operators and HEREDOCs. He explains concepts like flip-flops in Ruby, which are useful for implementing simple memory functionality in code.

- Almost Sinatra Project: A substantial portion of the talk is dedicated to showcasing "Almost Sinatra," a six-line implementation of the Sinatra framework that performs essential functionalities such as configuring, handling sessions, and routes. Haase explains how he achieved such brevity by compressing the code while ensuring it remains functional.

- Technique and Coding Style: Haase discusses different coding strategies he employed, including method redefinitions and meta-programming to streamline functionality without bloating the code. He humorously advises against using certain Ruby methods for code efficiency.

- Security and Protection: The later part of the talk covers the importance of security in applications, introducing "Almost Rack Protection" as a middleware that defends against various attacks, further rounding out the practical implications of his playful approach to coding.

- Concluding Thoughts: Konstantin wraps up by underscoring the fun in coding with Ruby and encourages developers to push limits on what can be achieved with minimal lines of code while keeping best practices in mind.

Overall, the talk showcases creativity in software development using Ruby and encourages playful interaction with the code that enhances both learning and practical implementation.

00:00:08.160 Okay, hi everybody! Let's get going with this.
00:00:13.719 As I already said, I'm Konstantin. I guess you all don't have internet, otherwise that Twitter handle is important. I'm not going to take questions during the talk and not after the talk. If you have any about the talk, please reach out on Twitter.
00:00:25.800 It's also acceptable if you just cry a little. I'm fine with that. I'm from Berlin, where it's snowing right now, cold and everything, so I'm really happy to be here.
00:00:37.840 Josh already said, like him, I work at Travis CI. I maintain Sinatra and work on projects like Wtills and Rinas. To do a little more advertising, together with Alan Harris, I wrote a book where there's this discount code. You can get 50% off the ebook and 40% off the print, which makes the ebook $6.50 USD, which is like a pot of beer here. Even less.
00:00:55.840 One other thing before you get really comfortable: Real-time Rails and Ruby is actually in the other room. Seriously, you will learn nothing useful in here, so if you came to this conference to learn something, then now is the time to leave this room.
00:01:06.600 Okay, no one left. You have been warned. This talk has 99 slides, but a meme ain't one of them. Prepare for the strangest code slides you might have ever seen. In the beginning, Mats gave us Ruby. This is Mats giving us Ruby; that down there says Yukihiro Matsumoto.
00:01:28.840 If you want to learn more about Ruby, here are a few links to check out.
00:01:34.560 One of the things I'm going to talk about today is obfuscation. Basically, the theme of this talk is how to do fun things with your code.
00:01:39.400 When it comes to obfuscation, there’s a quote that I like to recite now and then: "Until programmers stop acting like kids who don't want their food to touch, they are not artists, just kids who don't want their food to touch." Okay, let's get started with fun Ruby code!
00:01:52.959 So, I said you’re not allowed to ask questions, but I’m allowed to ask questions. So first question: Who knows what this evaluates to? Scream at me! Come on! Come on! No, it actually evaluates to false because this is actually a one and, where the question mark and exclamation mark is a string. The string exclamation mark, and in eight, it's an integer.
00:02:26.400 Since we established that this is false, what is this? No, this evaluates to... there’s a ternary operator in here. You don’t need spaces. So on one and twenty, which is coming out in three days, it evaluates to the colon, obviously.
00:02:39.560 Okay, something easier, because so far you've not been really great at this. I have to say, something easier. What does this evaluate to? Correct!
00:02:45.600 If you look at this, this here is an array, and pack, P is your best friend ever if you write code with pack. If you change those to not do integer to character conversion, it’s dot six. That part in there is actually 2s.
00:03:02.240 It’s the same as sending 2s16 to that integer, which is the same as writing that as hexadecimal number. Just wanted to find a useful simple example for pack.
00:03:12.700 Another thing that's really good besides P is HEREDOCs. HEREDOCs are one of the best features in Ruby.
00:03:19.039 Okay, okay guys. What happens if I run this code? It's a NameError: undefined method for variable F. Because this part is a string. This is the marker for HERE, and the limiter for ending the HEREDOC is four spaces, and then you have four spaces down here.
00:03:43.479 Now that we have some tools, one really good thing if you want to have fun with your code is the art of distraction. Just write code that looks like it does something different. For instance, if you run the following code, what will be printed out on the screen? Actually, come on! Any takers? Don’t be afraid! I won’t make fun of you if you say something wrong—maybe a little. But no one? No? This actually just... how did that happen? Correct! It just prints "how did that happen?" Nothing else.
00:04:21.240 And that’s because if you reformat that code, actually looks like this: there's a block up here that never gets evaluated because 'between' does not use a block, but also it does not complain when you handle one. Since one is not between two and three, you end up down there.
00:04:41.320 If you look at stuff like obfuscation, quines, or other things in Ruby, there’s one guy you will come across: Yukihiro Endo. You probably have seen code from him. His most famous code is this: you don’t really see there's Ruby code all over the place here, but this is a valid Ruby program. It’s not a real quine. A quine is a program that outputs itself. Instead, this program, if you run it, it outputs a different program.
00:05:30.079 If you run that, you end up with this if you run that. And so on, and so on, until you're back with that. We will get into that in a second, and I will get to why I showed you that. He has a ton more, just check him out on GitHub and his blog, which has not been updated in three years.
00:06:19.759 Let’s first look at one more really useful feature we have in Ruby. It's called flip-flops. And I'm not talking about these flip-flops. I mean, you would probably not confuse it because they're called thongs here.
00:06:38.480 I’m actually talking about something inspired by these flip-flops. Flip-flops are basically the basic implementation of memory. They store one bit, and you can flip them over. In Ruby, a flip-flop looks like this. Any takers on what will be printed out on the screen if I run this? Come on! Come on! Scream at me! I see some nodding there, yeah. It prints out three, four, and five.
00:07:11.880 Correct, because it starts evaluating the first time it runs. I is one, and one is unequal to three, so it evaluates to false, and so on until it reaches three, then this is true. The next time it evaluates with four, this is false, and that is false. But the last time it visited that if clause, this was actually true, so it just remains true. It's obvious how such features are really useful when you write code.
00:08:10.520 I highly encourage you to use this. So just to check if you understood it, what does this evaluate to? What does that print? It just prints three. Maybe this is not what you want because you want the second clause to be ignored if the first clause is true. Since that is also a really common use case, you can just use three dots instead.
00:08:30.560 Then the value of the second clause will be ignored if the first one is true, and it will print out all numbers starting from three. Perfect! When I see that, I just actually want to stop giving my talk and start coding with flip-flops right now, to be honest. Unfortunately, not everyone thinks they are a good idea. There has been an issue opened, which has not yet been closed, which might lead to flip-flops being removed in Ruby 3.
00:09:39.360 It was opened by Magnus Holm, who said, "Can we please get rid of flip-flops?" But then our good friend Yuki came along and said, "Hello! I'm one of the few users of flip-flops." He pasted some production code that looks like this. If we check out what this actually does, I have it here. You see it in the file ffrb.
00:10:38.199 Let’s see what happens if I execute this. Perfect! Given that, I think we should really keep flip-flops around. Unfortunately, he himself does not necessarily think so. But anyways, this was a real-world use case for flip-flops.
00:11:18.760 Evan from Rinas always said, "I won’t implement flip-flops for Rinas until someone actually has some real-world code." So when I saw that code, I was like, "Yes! I'm going to implement flip-flops for Rinas!" Which I did. Interestingly, I was able to implement it in pure Ruby, and it's the most stable feature of Rinas ever because we had never had a bug report on this.
00:11:57.440 Just to give you an impression, the main part of the implementation is this byte code generator thing. Just to show off about Rinas, it’s actually implementing a crazy feature that takes this amount of code approximately—all just Ruby—and the tests were already there because of RubySpec, which was also amazing. But I'm not actually here to show off Rinas; I'm here to talk about way more real-world use cases.
00:12:40.360 So, let’s talk about Almost Sinatra. It's a project I started over a weekend just to see how I can squeeze something that seems to be Sinatra into as little code as possible. I ended up with something that's just six lines long.
00:12:53.440 What's important here is that obfuscation was never the goal. The only goal was to use as little code, as few bytes as possible, and obfuscation was just a byproduct of that.
00:13:10.920 In the beginning, I said the coding guidelines were to have below 10 lines. I ended up with six, and it couldn't be wider than my editor, so a line could at most be 120 characters.
00:13:30.560 This is the code: this is a Sinatra implementation, way shorter than the official one. So, what can this implementation do? It has configure blocks, it supports sessions—you can store stuff in sessions—you have helper functions, and you have before filters. You can obviously use routes, set instance variables that will be passed onto your views, and you have params.
00:13:56.000 You can access a name if you pass it as a query param and handle it as locals to the views, just like you can. Basically, it does everything Sinatra can do. It supports inline templates. I'm not sure if you use it with Sinatra, which is where you can just say "end" at the end of your Sinatra file.
00:14:12.720 Then you can have all your templates in that one file, which is also really useful. The fun thing was it was just like a fun project I did on the weekend. A few days later, I tweeted about it and put it on RubyFlow. People got really interested.
00:14:37.120 There were at least four different block posts trying to analyze that code—none of that from me, obviously. I have not written about how this thing works, and actually, they were quite useful. I did that nearly two years ago, and some of these were quite useful when I tried to understand what I actually did there.
00:14:55.440 One of the main approaches I used was to simplify and compress to squeeze out code because—if you look at what's actually needed—it needs a few dependencies. It needs signal handling, because you want to send Control-C and stuff.
00:15:13.120 If you look at that in normal code, there would already be six lines. If you remove that empty one, it would be five, so you would have one line for the rest of the functionality. So let's simplify this. Obviously, it's way simpler to use arrays here and loop over them.
00:15:36.680 Now I can see that: okay, for dependencies that and that for signals. Now we can compress it.
00:15:49.280 By compress, I mean take two things that do different stuff and just smash them together, which ends up with this. There’s a lot of boilerplate in here that we can get rid of. First off, all those quotation marks and stuff; just be done with it. And there's another neat thing.
00:16:07.360 As you can see, this would throw a load error for trap. For a term, and then trap would run. But if something ends with a curly bracket, then you don't need a space after it. So if you flip the two around, we can save a space here.
00:16:30.240 Also, it’s really, really important that I discourage everyone from using each, because map is one character shorter. There’s a lot of other things that can be removed too, but then it wouldn't fit on the slide.
00:16:52.919 So, yes, and then there's something cosmetic which actually doesn't save a byte but makes it look smaller. You can use any character instead of the square brackets. One character that’s nice to use here is actually a dot.
00:17:07.360 And if you look at the code, this is actually the first line of the six lines. This is still the eight-line version; I'm sorry. And yes, this is one of the two main techniques. The other one is "fake it till you make it" or just "fake it".
00:17:19.760 If you look at the code, then a lot of the methods that I showed you, where you can say get that, post that, etc., etc., they don't really work properly. But for any simple enough Sinatra app, you will not notice.
00:17:43.440 For instance, if you say it, it does not matter if you use get, post, put, or delete. It actually uses the rack map method that you have in RackUp files. It also means that when you say 'SL FU,' that route actually also matches 'SL F/' something.
00:18:11.560 But whenever you just try out Almost Sinatra, you will not notice. Yes, it just does other stuff like pulling stuff from W—basically, anything W gives us is for free, because it does not really cost us that many characters.
00:18:36.720 You see that I’m looping through the methods, but metaprogramming like I could either do an eval there with 'def that string' or I could do a defined method, etc. But that actually costs a lot of bytes; like defined method is, I don't know, more than 10 characters long. Not going to count now; I’m lazy.
00:19:13.280 So what you can do is just grab that define method object and store it for later, because then it actually just boils down to calling that method object.
00:19:30.080 This is a nice syntax for doing that. You could use—you could use square brackets, but if you use square brackets for call, then you cannot hand on a block.
00:19:41.679 If you use this syntax, you can hand on a block, so it's one character more than square brackets. Unfortunately, but it works, so that's basically what I do all over the place.
00:19:57.440 What you also see is that all the methods are just defined on Object, and that also means that you don't really have to care if something is called on request.params or whatever.
00:20:25.440 Because every object in the world will just respond to that method, it also loops through all the templates that are known to Tilt, which is the rendering engine from Sinatra and Sprockets, and just takes the file extensions that are known.
00:20:39.360 Defines those as methods, which also means that you don't just have a markdown method, you also have an MD method and TT for Textile, which all Sinatra does not have.
00:21:01.840 And yes, and the whole set enable, disable, configure, helpers, use register is all more or less fake. It just tries to send that to the Rack Builder object.
00:21:20.320 It’s all fake, but that doesn’t matter because usually you don’t notice that. And yeah, that’s one of the principles.
00:21:43.680 The last thing that’s really cool in there or that’s actually a real implementation is parsing out the templates. It just uses a regular expression for finding templates.
00:21:55.920 It has the cool feature of you being able to define inline templates before the end, because the end is just ignored.
00:22:01.680 This also means that you should be careful if you use class instance variables, because they also start with '@'.
00:22:19.600 Then all you need is just to run the server. Port is, of course, hardcoded. Everything else is also hardcoded with sane defaults.
00:22:30.560 Since it's just Almost Sinatra, it does not actually support environments. And end is a really, really cool feature in Ruby. Actually, Sinatra itself uses it, except it uses at exit. It basically says whenever you're done with running your code, please do whatever is in that block.
00:23:07.520 So it starts a server. The main difference why Almost Sinatra is so Almost Sinatra is using 'end,' which has two really cool features. The first feature is its syntax instead of a method. It's always better to go with syntax instead of methods.
00:23:33.440 The other feature is that 'end' is simply shorter than 'at exit.' I could have had that, actually, just in the...so why this has to be an end is so that your code can run before you start the server.
00:23:46.480 Because otherwise it would never load the routes and just start the server. The cool thing about such syntax is also that you can basically just place it anywhere in the code.
00:24:09.280 You also see that the request—since we don’t have an application instance or anything, which you would have in normal Sinatra, but that would just be CRAFT—the request object that Rack gives us is just stored in a global.
00:24:24.600 This makes it really, really easy to access it. We are using a global variable here instead of a constant. If you would use a constant like, yeah, we did in other parts I did that in other parts, it's small now, but if I had used a constant here, then you would have gotten warnings on every request that the constant is being redefined.
00:24:48.960 And we don’t really want warnings, do we? So that's also why we have to use Rack lock, which means if you want to scale Almost Sinatra, you need to do it the same way you scale every other Ruby application.
00:25:12.440 You do it by not using threads, but instead, use hundreds of processes. That's really battle-proven.
00:25:30.320 I mean, it's hard-coded to the WEBrick handler, and while WEBrick actually supports threads, the WEBrick Rack handler does not support threads. As you might have noticed, it’s all about the fun, and I wrote extensive coding guidelines.
00:26:06.360 I’ll just hammer you with quotes from myself from now on. If your app does not run with Almost enough, please open a Sinatra issue.
00:26:19.280 Versions are to software what soft version is to git, so the suggestion is that you just copy that code and paste it onto the top of your file.
00:26:36.520 Also, say your require, and then just put the code below it. One other guideline is: don’t include tests. Tests just bloat the codebase. Just commit it; the users will come complain if you break anything.
00:26:54.679 Did I mention I work at Travis CI? Anyhow, apparently this code is really, really stable.
00:27:05.080 I don’t think I had anyone coming up with really serious bugs, so apparently, all the users are really happy with it. I can only recommend you going for Almost Sinatra in production. I mean, it’s such little code you can do a code review in...
00:27:22.679 What else? I played with other stuff too, getting it to be really small. It never got as popular as Almost Sinatra.
00:27:36.960 I think Almost Sinatra probably was the most successful project I’ve ever been working on.
00:27:50.680 So, Almost Sinatra actually has one really downside: Rack is a huge dependency like all that logic in Rack. So I wrote Almost W to prove that Rack is actually simpler than Sinatra.
00:28:09.600 It has some slightly different line rules—for instance, all the lines must be the same length because it looks nice, and it has to be under or it has to be no more than 120 characters per line.
00:28:27.920 Which in this case, so it’s the same rule as with Almost Sinatra, but in this case, I actually had a different idea behind that. The idea was that I could fit it into a tweet and also add a hashtag.
00:28:41.599 And it works just like you would expect. If you take this Rackup file, it just has a middleware that’s in there; that’s also this time really called, not like with the use method from Almost Sinatra where it’s just ignored.
00:29:01.440 You pass in the normal Rack application response, which gives you the status code, headers, and the body object. This is a three-line code, all the same length, and you can’t really read it, but that’s not important.
00:29:16.040 Also put it really large on here, and you don’t really see that either.
00:29:24.360 But anyway, so it’s hard-coded to open on Localhost 9292, like Rack does. So this is a file called AlmostRack; it’s executable. You see that by the shebang line and you can just run that, and it will load your config.
00:29:35.600 What nice things it does is in HTTP. You have that message, for instance: status code 200 is okay, and status code 404 is file not found, etc. This is just hard-coded to okay.
00:30:07.280 It also gives you statistics about how long the request processing actually took, just like you normally have in the log, except benchmarking—this will actually be a lot of code, a lot of crap that we don’t want to have in there.
00:30:33.000 So it just totally makes that up and uses random divided by 321 makes it seem really fast too, like whoa, because random will give you something between zero and one.
00:30:58.760 Also, one real downside is if you use Almost W, the response body cannot be anything that replies to each. It has to be an array of strings, and to be exact, it has to be an array of one single string.
00:31:09.360 That's just a minor downside in my opinion. So you see how we’re eliminating this stack. Now you can run Almost Sinatra with Almost Rack to slim down your things.
00:31:37.079 But there's one thing that's really missing if you use that: you have stable code, really small code, fast code.
00:31:47.320 But what if you get an evil mass assignment hacker or someone attacking your Sinatra application? I have one project that you would use with the normal Sinatra.
00:31:59.440 Actually Sinatra uses that automatically, which is W protection that defends you against attacks. But W protection, again, would be such a bloated codebase, so I did Almost Rack protection.
00:32:24.560 Which protects you against opportunistic attacks; it goes really well with Almost Sinatra, Almost Rack, or Ruby on Rails. Here are a few of the attacks; there are more attacks out there, but those are the main attacks that I was looking at when implementing Almost Rack protection.
00:32:53.000 It protects you against attacks that Rails can do nothing against. No longer do you have to fear for the year 2000 bug.
00:33:07.360 It protects really well against cybercrime. Have you heard about crime? I don’t mean crime out on the street. Well, it will protect you against cybercrime, but crime is also the attack against SSL where you can figure out the session cookie.
00:33:21.320 Even though it's SSL encrypted, Almost Rack protection will totally eliminate that option. There are a few things like usually it protects you against race conditions in your code.
00:33:41.560 Which is really handy, I mean, not with Almost Sinatra, as you can't use threads. But otherwise, because Almost WCK does not care about threads, you could use threads in there; whatever. Knock yourself out.
00:34:00.760 The only exception is if you use it with proper Rack, and your Rack handler has race conditions, then it will not protect against these because basically, it just protects against any issues that are in code that’s after the middleware.
00:34:23.760 Because Almost Rack protection is just the middleware you include. So we all had a good laugh so far, but I have to tell you: security is not a joke.
00:34:41.360 One really important, which was really important for me, is I do not set a line limit. I do not like to squeeze anything together as close as possible.
00:34:55.040 I want this code like I want you to go in and do a proper security audit of the code and understand what’s going on.
00:35:14.480 It would actually still be the largest project in your stack if you use that stack that I’m recommending to you.
00:35:29.720 I had to do a few arrangements to fit all the code onto one slide. This is the Almost Rack protection code. Let's look at this: it has two public methods, or well, one really important public method.
00:35:41.880 Call the normal Rack method, and you can configure it. You can say if it defends you against an attack, you can tell it which status code to use—the default is 403.
00:36:05.920 You can tell it the message to display, which defaults to "Attack prevented." You can set the content type to text. Whenever a request comes in, it checks if there is an attack prevention reply for this attack.
00:36:20.480 If it’s an attack, then there is a prevention for that, and otherwise it just hands it on to the application that it’s protecting.
00:36:41.360 So how does it figure out if it needs to prevent an attack? The logic is ingenious.
00:36:48.720 If the request is an HTTP request, prevent the attack. If it's an HTTPS request, prevent the attack.
00:37:00.960 And if it’s the coffee brewing extension, prevent the brewing attack by pretending to be a teapot.
00:37:16.560 As you can see, it has one array and one string in there. It’s totally compatible with Almost Rack.
00:37:28.840 It also has fancy logging options if you want to have fancy logging; you just override standard error.
00:37:39.360 So if you have it in front of your mental image, it simply rejects every request. No more fear for attacks! It even protects against unknown attacks in the future.
00:37:56.399 It’s perfect! It also has other advantages; it makes scaling your app way easier. You can just turn off your database.
00:38:03.960 I heard from people using it in production telling me how amazing it is protecting against approximately 9,000 requests in an hour.
00:38:16.440 Imagine all those attacks! People didn’t even know they have been under attack that much.
00:38:35.080 So, conclusion: seriously?
00:38:41.760 Thanks!
Explore all talks recorded at RubyConf AU 2013
+21