 OK. Hi, everyone. So, well, I'm glad this talk is following the previous one. Gives you a bit of context from where I'm coming from. So, I've been writing this library called Rax. I say it's refined web development and borrowing from the closure community, do a little definition. So, refined means to remove impurities and unwanted elements. And this is a great place to start a flame wall, obviously. So, they're not helpful. And I know that. So, there are a few titles which I turned down. Elixir Unplugged, being one of them. The Death and Life of a Phoenix was another one. I might leave this one just long enough. If you want to tweet it out of context, I would suggest a hashtag a TDD is dead, maybe. Or microservices would be another good one. And then finally, Through the Plughole, which was kind of my favorite, but I don't know. So, my name's Peter. I work for paywithkerl.com in the UK. So, I have to say thanks for giving me the time. And I'm kind of all over the internet as crowdhaler, because no one else has come up with such a mad name. No one else has found that anywhere else yet. I have a tendency to talk too quickly. If that happens, you can tell me to slow down, throw stuff at me. This pebble is meant to calm me down. It's a relaxing image. So, what is Rax? So, Rax is exactly the same as plug in many ways. It's an interface layer for frameworks and clients and servers, and it's also a toolkit for making building web applications easier. Here's the 101. So, is that clear enough? That looks to be all right. I like walking around not, so this is a bit weird having a microphone here, so I'll point with, you can't see my arrow. OK, I'll wave. So, in Rax, you handle requests by implementing a handle request function. The handle request function is part of a behavior from the Rax server module. So, when you import, well, when you use Rax server, you're required to fill in this handle request. There are two arguments to handle request, a request, sort of self-explanatory enough, and then a state, which is like the state of your server. And that starts from some initial config. At the moment, we're not caring about either of those. And we just return a response object. So, here it's a pure data structure. That's everything you need. So, a response object has only those three keys. There's nothing more to it. There's some helpers. So, this simple hello world can be cleaned up with sort of wrapping up stuff here. So, we have functions to set headers, set the body, stuff like that. You can pull information out of the request. So, this is how you do dynamic routing. Here, pretty much the only concession that Rax does to making your life easier is to turn the path into a list of segments from the URL. So, this would come from slashname slash anything. This means there's not even a routing DSL necessary. You just match on the path, match on the method. Good to go. And finally, you can make it configurable by pulling information out of the state that you've got. So, to start this service, well, to start this server, we'd say ACE, HTTP service, start link. And we give it the module we just defined, which was a greetings module, the initial configuration, and then some server options, which were keyword lists. So, this is a two argument function. It might look a bit weird this way. The top line is one argument, and then all the rest are just keyword arguments. And so, if we curl this, we'll get what we want. OK, so this was a bit of curveable. So, Rax is an interface in the same way that plug is, which means you actually need a server to run it. So, ACE is a server that will run Rax applications. I dove down quite a few layers in this project. There's no reason they couldn't be an adapter for Cowboy. But when I started this project, Cowboy did not support HTTP2. And I wanted that to be a default thing. So, I went from scratch and built a server as well. ACE provides a few nice helpers. We won't talk about it much more after this slide. But it allows you to, say, I want to define a service. And then you can just say, start my service. And you just pass the server options. So, at the top, we have a load of defaults for how we're going to start the service. So, by default, start it on port 8080. Clear text true means use HTTP. So, actually, to have HTTP2, you need to be serving over HTTPS. So, it, by default, looks for certificates. And you have to explicitly say, you know, I want to go back to HTTP. Yeah, so, I thought the architecture of ACE was a little bit interesting. Fred's talked this morning about supervision trees. So, there's this post that Joe Armstrong did, which says, you know, the power of elixir is because you have processes. Every connection can get its own process. And that's essentially its own server. And it's really easy to write a server for one user. So, write lots of servers for one user, and then put them in a supervision tree. And that's exactly how ACE works. So, the blue dots on the right are servers. And then the rest is all sort of infrastructure to make sure we have a pool of them waiting. OK, so, why did I do this? Obviously, this has taken a little while. Well, functional programming matters. We can go back to yesterday's talk, and perhaps I won't have to argue too much that that's true. HTTP is message passing. So, this is something I found, you know, the specifications talk about sending messages. So, I wanted to model it in a way that was more familiar to the airline ecosystem. And I wanted to support more than MVC. So, this might be where it gets a bit more confrontational on how I've done stuff. So, functional programming matters. So, what is functional programming? Data is immutable, functions return the same values, and functions have no side effects. And I like shrinking this to immutable data, no side of causes, no side effects. So, this is me talking in London in 2016. It was the first lightning talk I gave about elixir. And it's me going, I find this plug con object a little bit weird. Has anyone else thought it was a bit weird? I didn't have any answers at this point. I was just wanting to point out to people that I thought it was a bit weird, and I'll explain why. So, this is the con. Sorry if the simple view we had before, this is everything that's there. And, you know, they're all fine. They all have a purpose. But it's this, where is it? I can't even see it. It's this owner key. This owner key is the one that I found a bit strange. So, this owner key contains a PID. And because it contains a PID, plug can do a lot of impure things. And elixir is a pragmatic language. Erlang is a pragmatic language. What it does is very convenient. We live with it. Like, it's a perfectly acceptable way to do things. But I was like, OK, this isn't very functional. I want to see if I can get rid of this. I want to see if I can come up with a purely functional interface for making web services. And I've run into real world bugs because of this. So, I'll have to look at my local one. OK, so this is a very short snippet of some production code we have. And the issue here is that we have rewritten the JSON parser. And why have we rewritten the JSON parser? Because plug only allows you to read the body from the connection once. It does this to be efficient. So when you say read body, it pulls it directly from the socket. But if you read it twice, there's a risk that it won't be there anymore. So you won't get the same value. And we were doing message signing in our system. So we needed to check that the bits of the body matched a signature in one of the headers. So the plug parser throws away the raw body. So it was impossible for us to get back to the raw body which had been signed. So we needed this verify signature plug. But if we put a verify signature plug in before the parser, the verify signature plug had read the body. So the parsers would be broken. They wouldn't read the JSON. So our plug JSON request parser is a complete copy and paste of the plug parser with this extra line where we say we want to put the body into this key called raw body. So that was this nice composition. That was one place where it fell over. And I felt the root cause was because of this sort of impurity in the system. Another bug we dealt with was when we came to streaming. So this code, again in production, this was only a month or so ago as debugging this, there are probably people who know plug more than me. And admittedly, this was my mistake. So there's a mistake I've made here, which means that this will run out of memory quite quickly. I mean, my mistake's really obvious. You can probably see it. So what this code does, so we'll run through this, so this is a streaming endpoint. So when a client connects, we follow this generic process with the user. So that's just going to send standard Erlang messages back to the server process. So we have this loop. So we receive events. And whenever we receive an event, we serialize it. So I've not put the serialize function in. That's not interesting. And we send it over the chunk connection, and we go back into the loop. So the problem with this is it's possible for our users not to receive messages for months at a time. We stream updates, and normally a client is online making changes to the system and the updates are coming down. It's possible if their account is dormant that this receive block doesn't get called for months. And therefore, it just waits. I haven't put an after at the end of this block at the bottom. And in plug or in cowboy, not quite sure where this comes up, it doesn't force kill a process after any amount of time. You have to sort of let it go back to you. So I think the root cause of this problem is that this interface requires me to write my own receive loop. So that's another sort of impurity thing. I have to wait for this side cause to move on. So again, most of these problems were my mistake, but we solved them out of much difficulty, but this was my motivation for where to go forward from. OK, so back to that thing I said about HTTP being message passing. This is the beginning of one of the RFCs. I searched for the word message in the RFC. It comes up about 100 times. I read through about 30 of them trying to find the most succinct one, proving my point, because you find something that supports what you want to say. But this is the original abstract, and it goes, so it's the fact that the request and response are self-descriptive message payloads. So both of them are messages. And it talks about a request response being a message exchange, so that's a term they use. So this nice picture about HTTP works. That's a message sent from the client to the server. The response is a message sent from the server to the client. So message passing is a thing in Erlang, and servers are a thing in Erlang as well. Well, Erlang and Elixir are used fairly interchangeably. Obviously, they've been brought up to the Elixir world, but it's Erlang that really sort of discovered these patterns. So a gen server, so if we wanted the simplest gen server, a call and response would look like this. You'd get a request message, which is a call, sort of an interchangeable term there, and you'd respond with some data structure. There's no connection in this discussion. And if we compare this to the simplest rack server, I'm hopeful that you can see that there's a similarity, which is what I was going for. I was quite pleased when I got to this point. It took quite a bit of tinkering with how to do things. But this is kind of my reason for doing this. So once I got to this point, I was like, oh no, I need to write a server that will host these apps. There were other to-dos following on from this. So what about streaming? So plug does streaming. And this simple worldview, there's no way to do that. You get the whole request, you send the whole response. It was raised by Joe's a years and years ago about why you shouldn't adopt rack, which is where the name comes from. But I've found a different solution to that. So if you are interested about streaming HTTP requests and responses, you can model that as many messages. So between two Erlang processes, if you send two messages, their order is guaranteed within some caveats of nodes topology and stuff. So in this case, I care about the individual steps. So I've got lots of messages. But again, gen server shows us the way. There's calls, there's casts, and there's handle callbacks for each one. So within rack server, you can do this. So there's a handle head callback. There's a handle data callback, which is whenever a new body chunk arrives. And then there's this handle tail callback, which is pretty much always going to be an empty list. Almost no one uses HTTP trailers, but if you need that in your system, that's where they would appear. So how this works, if you want to stream a file without having to put the whole thing in memory, you start a file that you're going to write it to. Every time new data comes in, I just write it to the file. And then when I get to the end, I send my response. So the interesting thing here is how we sort of say that the state's going on. So instead of the reply no reply keyword from gen server, so every single callback has the same return value, which is either a complete response, in which case your server's finished with, the response has been sent, or it's a list of all the pieces that you would like to send and a new state. So the state in this handle head, at first we don't care what it is, that's the configuration. That would be the initial state. But this is now a long-lived process. We're no longer talking about a stateless protocol. And so this state key is the last argument, same as it is in a gen server. And that gets put between each callback. So in the first one we say, I've got nothing to send to the client. My new state is this file. And the second one I say, I've still got nothing to send to the client. My state is the same as it was before. And in the last one, I'm sending a full response so the state is no longer important. So you don't have to send a state tuple there. So more than MVC. So as much as I like Phoenix and I do, it's what we use at work in some of our services. It's made me probably able to talk about Alexa. A sort of a framework which is quite tied to put the actor when it's not sort of, there's a lot more things you can do with react call rings and stuff like that. So these are some of the architectures that are interesting at the moment, like the sequel, no sequel. We use event sourcing at work in one of our services. I think the memory image idea is quite interesting. If you just Google that in Martin Fowler, he has a nice talk about what he thinks his memory image architecture is all about. And Rax supports all of these architectures, literally every single one, because it supports none of them. You're completely on your own. It's a web layer. I'm not interested in what's going on inside there. By design, like pieces composed together. And so this is a term which I think is definitely going to be a thing. This is what I think MVC should be. Mind your own business, view controller. Rax will have a go at doing the view controller for you. But I mean, I'd love to hear what you're doing in this mind your own business bit. But I'm not going to provide you any helpers to work with that. OK, so this is then the Rax stack, as it were. Cowboy has become Ace. Plug has become Rax. And Phoenix, well, I don't go there anymore. It's your application. You decide what you want to do with that. So no framework. Sounds a little bit like reinventing the wheel. I mean, that's a fair criticism. If you're building a crud app and you have someone who really wants to access the SQL database, it's been done before. You should probably follow the patterns. But OTP is a framework. So you're reinventing less than if you were to try and do this and just see. If someone was to go, oh, I'm just going to write my own C framework and server and middleware. That's a different sort of commitment than doing the thing in Elixir. Because I reuse the supervision trees. My structure is the supervision trees in my application. Mix is a tool that provides me a project structure, like a test framework. I know where my tests live. I know where my core code is. I know how to do configuration. Like I'm not flying blind if I don't adopt a web framework. And also Rob said so. So I haven't asked him yet actually about his role, your own framework. I don't know if it's online. But I would certainly like to look some more at that. OK, so this is my calm down pebble. I don't know if that's been quite quick. I can't exactly see you guys. Normally I'd ask it without questions. But I think we might wait to the end because it's. So let's build something with this. So I'm going to try and build a chat app. I think we've got another 10, 15 minutes to do this. I think it should be fine. So how do we start a racks application? Where do you use mix? Because that's our framework. So we're going to have a supervision tree because we're running something. So we're going to start our project. It's called Water Cooler. I don't know if that, yeah. And we just change into that project. So this is the application. This is the answer. Phoenix is not my application. Racks is not my application. This is my application. It's a chat. I have only one chat room. If you publish a message, it's global. If you join the chat room, you see every message. It's not the best chat room. But this is my application. And so how does it work? So how do I do this published join thing? So what it does is you put the message on the back. And it gets sent from the publisher to the subscriber. So that's it. So as I mentioned, this was a web interface talk. I don't mind. You can send it by pigeons or smoke signals. And if you're interested in writing a pigeon adapter or a smoke signal adapter, it will work. So I will share code for this Water Cooler thing. And in my particular example, I use the PG2 library. It takes about five or six lines. But I'm not going to show you. I'm genuinely not going to show you. I'm moving on. So now we need to add our web layer. So ace depends on racks because it's a rack server. So I don't actually need to include racks. So I use ace. Then I have some extensions, which is this rack static middleware. So I included everything at this point because I was tempted to live code it. And I didn't want to do lots of mixed up gets. But I've decided to just go with slides. And then so live reloading. Well, reloading, that's a nice thing. So there's a library called xsync. And it just works for reloading code. So when a file changes, it will recompile your application. Again, it's just a mixed thing. So there's nothing about that which is tied to waiting for the next web request. So here's our first endpoints. So I normally name space my web interface. If it's for a browser, I tend to call it www because that's the domain you'd point it to. Otherwise, it's API. I avoid calling it web because if you're in a cloud environment, everything will be over the web. If you have an API, an admin panel, and a main page, they're all web interfaces. So that's not their name. So www seems to work as a first guess. I could call this, well, I was tempted not to go there. That was a bit too much of a troll. I was going to say I could call this a racks context. Well, actually, I just called it a module or a namespace. So here you can see us making use of our router. So in the first one, we pattern match on the empty list is the home page. That's the root when we split on that. So to get the home page, we return home page. And then for any other page, we say a not found. So it's like, whoops. And this is how we start it. So a water cooler thing, using that A server, it gives us a nice child spec. So all we need to do is add it here. So again, this is another nice thing about it not being your application is we could actually have water cooler www, water cooler admin, water cooler API, all under the same supervision tree. And when I start mix, I start all of them. OK, grab some more. So I mentioned that racks was a tool kit. I mean, Phoenix, sorry, no, Phoenix, elixir pattern matching is very neat. But there's a few tools which it's nice to have out the box. So if you have racks as a dependency, you get this very basic router. I mean, this is an incredibly basic router. You match on any request strut and give it a module to call instead. And so in the last one, because this uses a macro, the fact that there's no sort of this code that gets passed into the thing. So then that gets interpolated into a load of handle request callbacks. Racks router is actually quite nice. It doesn't do stuff with handle request. It does stuff with handle head. So it will work. So you can have, say, 10 endpoints and only one of them be streaming. Nine of them all can just use the handle request callback. It can be simple. You get a request, you return a response. Super easy to test. And your streaming one can do streaming on its own. So router does some nice things to make sure streaming still works. And logger also works with streaming. So you just add that in. We can log our request and responses. So this is now our home page action again. So actually in the previous talk, I quite liked the use of actions instead of controllers. These actions can get quite large. Again, streaming is sort of where you do this, because it's a stateful thing. You need to manage the state. There's more than one callback. It's a nice thing to do, but I just like that way of breaking things up. Here we can see how Rax does views. So you use eex, it comes with elixir. It's very nice. There's no view layer. You do that. I get quite flippant about all these things, but it's nice lot not to learn new things. Like if you know eex, because you've had to do it to write email or something, or interpolate files somewhere else, why not reuse that knowledge? So there's also a 404 page, which I didn't bother. I think you can probably get that at this point. But that's part of the one piece of code I don't show. I remember what happened here. So we've added this line. So RaxStatic, that's another middleware to use the term. Again, plugs are really nice, complete toolkit. Its convenience is to be applauded. So this looks very similar to use PlugStatic. That path is where all our files are. They get built in at compile time. And so they serve very quickly. So all the magic of using eex to have fast return, this does the same thing as well. So inside www, we have a public file, which I've put a main CSS and a favicon. That's all in those for static content. So now we need to post a message. So where it's chat app. So this form, this is a little trick. I got a little bit carried away trying to avoid JavaScript. So this form makes probably perfect sense to most people. I discovered this little trick. If you give it a target, when you hit the submit button, the page doesn't reload. That iframe reloads. And if you make that iframe size and height zero, you get to do, looks like asynchronous updates without any JavaScript. I think that's a win. OK, so this is our published message action. I keep saying control it. I'm going to say action. I'm going to get there. So using the handle request call back, the body has been buffered in. So we've got the whole body. So the body's there. It's just in the data structure. URI is a standard elixir library. So you just use those, decode what we get. Again, the pattern matching is so nice in the standard elixir language. There's no form helper here. We just go, oh, if it's that, take out the message. If it's not, at the moment, if it's not, this blows up and you'll get a 500. It'd be nicer to put a case clause around that and send a bad response. But this is a demo. So that's what we've done. We published that message through the publish interface. And then we just redirect back to the home page. And that little iframe just ticks over. So this is the subscribe to updates. So this is where how we get live information from our chat-out. So when we get a request to this endpoint, so if we go back to the router, let's not go back to the router, because that's before we added this one. So this can be at any path, but I've put it in the example at slash updates. It's already been routed to, so we don't need to match on the path again. We trust the router is only going to send it here if it's the right path. So what we do is we join the chat room. Again, there's no channel name, so it's just a very simple join the chat room. And at this point, so this is sort of the other half of the solution for streaming. We say that the response is OK. So it's a 200 response, so we say it's an OK response. And we set the header to be this text slash event stream. Now this is where I am going to ask for audience participation. Who knows what text slash event stream does? There's like two hands. You need to look into this, because it's amazing. Like web sockets don't really make sense in HTTP2. This is what you want to look at. So what we do here, and then in this last line, well, not last line, when we set the body, we just set the body to true. So true says to racks, this response has a body, but I don't know what it is yet. And the server will take that, and depending whether you're using HTTP1 or HTTP2, it'll set the transfer encoding to chunked for HTTP1. And for HTTP2, you don't need to do any of that, because HTTP2 has a definitive end flag. So you can just start a request without knowing about the body. So we send this. So this is our state thing. So we send the pieces that we want to send, which consists of that incomplete response and the state that we want to get to. So we don't do anything with the state here. And then later on, this chatroom module we've got is going to send a message to our server. So when we joined, we submitted a PID. And every time a new message comes in to the chatroom, it gets sent out to all the servers that are still listening. So we get an info message. So HandleInfo just borrowed from GenServer. So this is just a message that's not anything to do with HTTP. So I've namespaced it under this water cooler chat thing, so I don't just put garbage into the stream. And so I pull out the message. I use this SSE library, Service and Event, to serialize it. Again, it's not really important how it serializes. It's just as nice things in the browser. And then I go, OK, I want to send that data. So RaxData is the flip of HandleData. All that's saying, all that's doing is saying, this is part of the body. So just to keep streaming this down. And again, OK. In the first one, I've called my state state and the second one config. It doesn't matter, but that's just confused me a whole load. All right, JavaScript. So this is not a framework. This is all the JavaScript it takes to make a chat app if you use Service and Events. We're going to go through this a little bit back to front. This event source thing is supported in nearly every browser. When you do that, it opens a connection to the path given there. And it expects to receive Service and Events. So stuff from a Service and Event endpoint. And whenever it does, whenever it gets an event down that stream, it'll call the onMessage callback. There's a few helpers that you can give IDs to events which you'll then try and reconnect with the IDs so you don't restream messages. And you can give them names so you can sort of listen to only some of a certain type. But it's most basic. That's all you need. And so we provide this displayUpdate function. And all we're going to do is we're going to say, when we get an update, pull out the data, create a div, stick it to this element which we pulled at the beginning. That'll do it. So that's some, yeah, it's not even jQuery in there. That'll work in a browser. And yeah, we're going to go to that experiment later. So yeah, Service and Events. How much time do I have? OK, either I've been going way too fast and you're too quiet, or we're just just going really well. So why to use frameworks? Well, they save you a lot of time, apparently. I mean, you don't know because you never live both worlds. So recently, one of the things I also thought Phoenix was good at, and it is good at, this is, again, not to knock it, is it's management of assets. So it does all the asset compilation and various steps using Brunch. Some of the downsides of that is your front-end people might not like Brunch. So this is a picture I got of the sort of state of development in 2018, and Brunch isn't even on there. I mean, I don't really mind. I don't want to do the JavaScript. Rollup's quite nice. I used that once. I'm not going to tell you about JavaScript. It's fine. Ask someone else. But having your assets compile when you start your server is very nice. So I was like, how much effort will that be to implement on my own? So we're going to go through. So this isn't going to inject the code into your browser. You will still have to hit refresh, hit F5. I think that's an OK work experience for keeping things simple. But admittedly, if you've got a great big single page up, it's not what you want. So we're going to use NodeSas. It's a library for compiling SAS. Use whatever you want. This is just an NPM thing. And the interesting thing we do here is we set up a script called WatchCSS. So at this point, nothing is elixir. We just, this task, if you go into the terminal and you type NPM run watchCSS, it'll recompile. And it'll recompile stuff in the assets folder into that public folder, which we used in the rack static middleware. And because we're using xsync, rack static sets up all the files that it's listening to. xsync will recompile if there are any file changes. So using xsync and this, when you run this task and serve at the same time, when the assets change, they'll get recompiled, the app will get recompiled, and you can just hit F5 and it'll replace it. So having two terminals open, it's kind of annoying. So we'll go a bit further. So this is what it takes to supervise that NPM script from elixir. Again, Phoenix does do more than this. There's a module called Watchers inside Phoenix, which is where I got this inspiration from. But again, most of the work here is done by the OTP sort of power, like a task. I don't want to implement a task from scratch, but a task exists. And this is a long-running task. So when I start Watch CSS, I'm like, start a task, please. Use the system command. So run NPM, watch CSS, and I CD it into the directory. So my application is not a web application. I take to pretty much as far as I can. So even my package.json was inside the www folder. It's not at the top level. So you can quite happily run seven frontends at the same time, because they'll all live under their own namespace group, in which case you would have seven watch modules. And you could put all seven in the supervision tree. It'd be fine. And so to start this watch task from inside the watercooler application, we just add it to the supervision tree. And that's it. I should tell you some of the things maybe that Phoenix do more, because I'm saying this is really easy. Like Phoenix will give you nicer errors if nodes not installed, a few other things. But I mean, I used this for development last week. I set up one like this. And we were making an admin dashboard. It was just CSS and a little bit of JavaScript, but it did what I needed. It was very nice. OK. And that's me. So have a look. It's just another way of doing things. Some people really like pushing functional programming. I'm one of those. That's why I got here. The toolkit's less complete than plug. That's probably self-evident. I've been doing it on my own. But I'd be interested to see if anyone has time to experiment. I mean, there's a Slack channel called Racks, which has a few people talking about interesting things. So yeah, any questions? That's me. There's no actual Fenn slide.