Live Coding with Phoenix LiveView by Bruce Tate

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody I think I've seen a lot I've seen a lot of friendly familiar faces here so appreciate you all joining here yeah so just quickly ultimate trick is a digital transformation company headquartered in Southfield Michigan with a global presence so basically helping companies stay ahead of the curve with technology change and and source innovation from their teams kind of at the highest level and Ryan and I Ryan Robinson who I'm gonna point every direction because I don't know where she is on your guy screens right and I work on the collider team which is the community facing branch of the larger ultimate regime where we are building a hub for software engineers specifically concentrated in Detroit Metro Detroit region but now that we are 100% virtual for the time being I suppose that folks could be based anywhere but we are regularly partnering with awesome event organizers like the Detroit tech watch team here to provide you with awesome relevant programs and entertainment that speak to helping software engineers connect and grow in this region so we'll drop a link in the chat here if you're interested in learning more please don't hesitate to reach out with questions I'd be remiss if I didn't mention that we are specifically hiring for react and angular developers right now so if you know anybody or if you yourself are looking for that type of opportunity please do let us know and with that I'm gonna end it back to the Detroit okay thanks for buddy all right yeah absolutely fantastic thanks for that and so tonight our speaker is Bruce Tate thanks for joining us Bruce and Bruce can be talking about Phoenix live view elixir in Phoenix live view and it sounds pretty exciting from from the little preview or discussion we had beforehand so I'll let you take it away Bruce thank you all right we're gonna what kind of time do we have we have at least one hour for you so most people don't need that much but some people on all that plus more and you could probably go further if you needed we'll see how it goes so so basically I am here in support of gracio that's my new company it's gr o X dot IO and Drock is kind of means to intuitively understand something and if you look at our logo really closely the I and the O are written is a 1 and a 0 so Grox IO is like understands computer languages and it's one character longer than a URL shortner right so we have a couple of things happening at the end of the month we have a live view where we have an OTP class at the end of this month and a live view class at the end of next month so into this month so beginning July let me get the date right here July 22nd through 24th we have the intermediate elixir with OTP and at the end of august august 26 through 28th we have a live view class and during this pandemic what we've been doing is offering our normal classes but rather than doing them in person we're doing them remotely and the other thing that we're doing is if we're having very small classes so we're capping the cost so to keep things affordable for folks and we are so it's about half of what we charge a normal training and we're keeping we were taking five paid clients and we offer one scholarship client from someone underrepresented in tech as long as you know as long as these protests are coming on just to kind of give a hat tip to that we make the community that we'd all be proud of so let's talk live view a little bit so I see that so if you open your participants tab and by the way a host should open their participant tab to on the bottom you see a couple of votes how many people have used live view or seen live view click yes or no all right then pretty good so just over half are new to love you okay can we clear that much one of the hosts and how many people have used elixir before or seen elixir before about the same mix a few more people have used to lick sir okay so I'll try to to talk about what we're doing along the way essentially all the concepts that we're going to talk about tonight are basically applicable to general purpose programming in fact I have a couple of mentoring programs once happening in Kenya ones in Chattanooga and so the rules if you want to join the Chattanooga meetup the rules are be someone underrepresented in tech or bring someone under represented in tech and we meet every every Thursday it's six o'clock it's a zoom link and you can pig a pig us you can ping us at gracio learning Gero xio learning on twitter and and we will get you that link if you're interested but what we found is that when we're trying to build opportunities job opportunities for developers we do better off teaching people elixir or a higher level programming language because we feel that we build better developers that way and our graduates or you know the people who have joined the program and gotten jobs are working in all different languages only to an elixir once at Google doing DevOps once at meta track doing Ruby testing another one is starting in an internship at meta track and potentially working on some closure and another one is working on JavaScript but the idea is that these concepts are basically concepts of composition so we're gonna talk a lot about that okay so let's so let's talk a little bit I'm gonna share my screen now I don't really have a lot of prepared material for you but I do want to talk a little bit about the concept of live view so live view is basically a framework like elm like Facebook react like a number of frameworks like this but instead of looking at things in in the way of you get a reset a request you do something and you get a response that's basically a function or a procedure normally with side effects but what we're talking about is we basically build a core set of data and then we can do two things one when the data changes we can render the data and second when so oh the first thing is we can render the data and the second is we can change the data and when the data changes we automatically render so I want to talk about the two phases that that I want you to pay attention and notice as we solve a problem using live view tonight the first the first phase I'm going to call the mount phase this is where we build the initial data for the live view right so what's going to happen is a request is going to come in and it elixir is pure functional that means everything calls a function and normally the output of one function is chained to the input of another function so there's something called an endpoint and that basically establishes the whole configuration for for the stack and it establishes it opens the sockets that are needed and things like that then it goes to a router layer and if you're familiar with Ruby on Rails with any number of the Java frameworks or JavaScript frameworks with the closure frameworks there's this router layer and the router layer essentially establishes it Maps a route for a URL that you type onto some block of code to to to hook it to so in the case of elixir and especially with live view when a route comes in we have the choice to map this thing on to a live view and then the live view the place that you start is a function called mount and all that does is establishes the state for love you right so we're going to build this memory application and so we're going to establish some initial data so probably with just one hour to play with what we'll do is we'll hard-code some of the data and will enable just so that we can slow down so it won't be going at a breakneck speed and then we'll code a special type of function called a reducer and the reducer is is a type of code that takes an accumulator of some type and returns the same data type that's transformed in some way when our memory program is going to do is it'll take some type of I don't know a passage you know literary passage or a poem or something like that and it takes the number of steps that you want to memorise this this passage in and every time that you press erase it will delete a few more characters and hopefully you can recite this passage until it's gone and you've got to memorize so and that's going to use a single reducer and then the last the last piece is a render so I need to juggle my my zoom escape a little bit here okay so the last step is called a render and so that's the first face and then after this phase the life view is going to send down a whole webpage and you know with you a full text page just like loading an initial page would and the reason that that's important is that live view is going to play nice with SEO with search engine optimization then after the first request everything changes so after we've mounted and established our initial data which for us as initially with one say hey we're going to record the number of steps that we're going to use and we're going to record the text passage that we want to memorize and then we're going to build a reducer and that reducer maybe we'll call I don't know like something like an eraser or something like that and then we're going to render that thing so after it's been rendered life view is going to start waiting on requests so when we're in the running face now we're in Phase two we're waiting on a request and Live View does use JavaScript it just doesn't use custom JavaScript right it lets the framework write a common set of JavaScript so that if there are certain decorations on you know certain HTML attributes on a page like if I have something called Phoenix click it will detect any click on that particular web item right and then when it receives the quick click the JavaScript will send a request up into the endpoint up through the up through the socket and it will land in a handler and I can change my state so I'll receive an event it gets mapped onto a handler like handle event and then after I change my state if anything is different on the page it'll render the state so that's what we're going to do okay so enough of the presentation if there's if there's time and if there's interest I'll shift gears a little bit and talk about the layers of this system but for now well heck we have a little bit of extra time I think so so I told you that this is primarily going to be a design session and it is and so I recently wrote a book with with a man named James gray he was really involved in in the text made editor wrote the famous book on that he was really involved in the Ruby community he led a podcast called Ruby would be rogues he wrote faster CSP he's one of the smartest man I've ever known now he's in the elixir community and we wrote this book together but this Madison is cyclopædia and he basically started down the road writing this book and had a hard time organizing the firehose of data and OTP is kind of what Erlang and elixir used to do you know failure control and and inter process communication so it's difficult to think about but we came up with these layers so everybody bring out bring up your chat and I want everybody to type this sentence to help you remember it it it starts at the bottom and works to the top it's do fun things with big loud worker bees that's do fun things with big loud worker bees and so whatever language that you're working on especially a functional language it's nice to have a mental model of how things fit together and most programming it's wise to separate things from core concerns which have predictable properties and kind of well-established sanitized data and boundary layers that have things like process machineries that manage life cycles so these layers stand for data functions tests boundaries life cycles workers that's data functions tests boundaries life cycles workers and if you haven't been typing it into the chat you should be polluting this chat with the sentence do fun things with big loud worker bees and then you should type also data functions tests boundaries life cycles and workers we're going to spend most of our time in the data layer the functions layer and the boundary layer just because we don't have time to do everything but if you're interested in coming back and taking a class we also cover testing philosophy not just for look but for software and in lifecycle management which basically the idea is that if you can establish a way to start the service and take down a service and to detect when a service is down then it's easy to build a lifecycle layer so rather than start a process directly you can ask the lifecycle manager to start the layer right so you could say lifecycle start lifecycle stop and then if there's a failure the lifecycle layer can automatically shut everything down and your process is going to have children too and so they could set those shut those down as well and bring them back up in the last known good state which is normally the startup state so if you've ever seen the IT Crowd um does anybody remember what the guys down in the basement would say and you can unmute to say this because it's that important the British comedy the IT Crowd oh come on did you try turning it off and on again yes yes a cookie for you sir and so so basically the idea is if you have the if you can detect failure if you can stop and you can start you can build systems with extraordinary reliability okay so we're gonna start our we're gonna create an application now mix Phoenix new and we're going to call this memos and this is going to be a lot of you application so this is gonna generate a bunch of files and yes it's going to generate some it's going to have some node that's going to run and watch our assets are our CSS our images and our JavaScript though you know there's basically a minimizer and and that kind of stuff so but this is going to generate an opinionated stack and organization so the two layers are well represented that we talked about the do fun things that's the core and those things are going to be wrapped in the memos application or the memos subdirectory under live under lib and the the boundary layers the big loud wildebeests they're going to be represented in the in the boundary area which is memos web so I'm gonna change it to memos and I'm gonna say mix Phoenix server okay so it's going to start this application and you can see that I've got some errors already it looks like I haven't created my database yet which makes perfect sense so it is said the storage is not created for the repo memo dot repo and then it says type mix ecto create or you could just press this shiny button that says create repo forever okay so we're gonna do that it's the COBIT has to be the Komen okay so this is this is a live view application this was really boring until joe say and chris worked on something called the phoenix dashboard which will allow you to see any process that's running on the site kind of dive in and look at the individual details of any single one of these and you can drop your own metrics on the home page of this of this particular server and it's a pretty incredible service but we're not going to be playing in this dashboard very often we're actually going to build our memorizer game and remember what we're going to do is I don't want this I want the new application so I'm going to change the memos directory and I'm going to edit this application and we said that there were two major divisions one with the core features right so the core features were going to go in here and the boundary features are going to go in here and I like to you a little bit there's a there's a service called a context that can often live in in the Lib directory and in fact our the the file that we create we're gonna actually build a file called an eraser and this is going to serve it's like a hybrid boundary and core for us right so we're gonna mix a little bit of the concerns but I'm going to try to keep this as pure as possible and the reason is that I can do many of the boundary type can concerns in my live view and you'll see what I mean in a little bit but if you've not been a functional developer I'm gonna talk a little bit about an idea a elixir construct this thing a pipe character and a greater than sign when they're together my editor ferns these into triangles but this is an important thing in a liqueur this is a pipe and it means so if I have a function called kernel dot plus and a feed one end to that function it's going to add one as the first argument of the plus function right so it's taking the result of the thing on the left and passing it as the first argument another thing on the right and so a lot of elixir development is shaping your code such that you have these beautiful pipeline flows so it works especially well if you can do something like this if you can have a constructor and this builds it just builds data in some way if you're if you're thinking about a Java constructor that instantiates a class we're on the right track and so we're going to call functions so it's going to look something like this so you can have like a bunch of constructors and a bunch of reducers and your code can tell a really nice story right so when elixir was very young object-oriented developers were kind of stripped of the structure of the glue of their code but when we moved over to elixir eventually we discovered that we could replace some of the flow with this concept of a pipe and that happened when Dave Thomas wrote the book I'm programming elixir and he had this little pipe at the bottom it said pragmatic and concurrent fun something like that right and so when Dave did that and when he built the examples in his book he elevated the importance of the pipe so what we're going to try to conduce our functions such that we have constructors followed by reducers and 4u elixir pros I know that I'm lying to you a little bit right I know that my reducers aren't technically reducers that would work in enum reduce I want you to put that aside and think about it as just a logical concept right so the idea that if I have def step or forward and it has some position I could say position plus 1 right and if I have def backward that would be position minus 1 and right so this is like a pirate's treasure map def origin and then this is this is just a zero that I could do something like this I could say origin pipe to forwarded forward pipe to a forward pipe to backward and that gives me that tells a story this is the structure that we're after in our core right something that that tells us stories okay some say def module this is just a container and a namespacing feature that holds functions in the lick sir and we're in memos dot we're gonna build an eraser and so remember the first type of function that we were going to look at was a constructor right and so this is a constructor and I'm gonna have private functions and public ones if I forget to tell you basically if there's no P this is public and that means that that any other module can see it otherwise its visible only to this module so that's important because the eraser API is determined by the public functions the ones without P okay so I'm gonna take the text and I want to well no let's take the do the text and the steps and I'm going to say I'm going to basically build a structure this looks a little bit like a class if you hold your head sideways a little bit right and in Java Ruby but basically these are the attributes that I'm going to be working with often so one of them is the text so um and we're going to not say let's do have defaults all right and then we'll have steps three I think that this is going to save me a little bit of time so I'm going to throw a little bit of syntax at you but I just have to say trust me at this point right so I'm gonna say oh no I don't yes I do no I'm gonna do it I'm not going to throw syntax at you yeah so this is a function that creates a structure and right and so otherwise if there's none of that if you don't pass texture steps we want to pick up the default values and so all we're going to do is this right so if I so basically elixir these are different functions because they have a different number of arguments about don't specify any arguments I just say give me the struct which is gonna give me this data right here so why don't we go ahead and start IX so we can start playing with some concepts right so so I can say alias or even import and this will allow me to call these the functions in here without typing memos dot eraser right so and now I'm gonna Dingley is it this does the same thing only it it will force me to type only the last thing in this namespace chain right so for example I could say eraser dot new right and eraser new gave me how about that I didn't say okay so now I have a new map so percent open mustache or curly brace I'm gonna say mustache because my students love it and if you have a module name it's a map that's also a struct which means it's limited to the keys of text in steps okay so the text is going to be like this the steps is going to be like that let's see this is not going to be the final form of what we need for our eraser right I think that probably is something that's going to work well for us let's say text is equal to v4 so I'm grabbing the result of this line which is this right dot text so now I have text this did you try turning it off and on again and so maybe what we should do is play with this a little bit so the length of this is so if I get lazy and don't type the parentheses don't freak out it's it's really the same thing but so if I do that in the compiled code elixir will complain and tell me I need parentheses most of the time so basically what I'm looking for is a way to think about building not just the first step but to tell elixir at every particular step exactly which characters that I'm going to delete and if I do that it's going to be much easier to to create the the overall effect that we're looking for in the reducers so maybe there's a range of one to 40 so what this is this is essentially a list of every number from 1 to 40 right so let's think about this for a while so maybe if there let's say if there are 2 steps or let's say 4 steps shuffle right so now we have a random list and so maybe if we carved these up into chunks right so maybe enum dot so maybe I could build this into no I basically want to grab what I've done so far right and then pass that to enum Chung every maybe if it's every ten then I would have so ten four four steps right so now this is something that I can work with so this is a schedule based on the length of the text right so I have a little bit of math to do in here I can work with that so if I say that my def struck needs this so the text is that and the schedule and I'm going to actually do this the steps is going to default to three and the the schedule is that oh that's great somebody is right outside my door with a barking dog awesome that's just awesome okay so so that's that's to serve me right for all the times I've stood outside my neighbor's house right okay so I'm gonna say the schedule is gonna be billed and so we know how to build it right so we need the text and the steps we need to do a little bit of math but we know what that looks like because we've been working in the console def P build schedule so what does that def PB somebody new not experienced with elixir is that public or private don't be shy guys speak up I'm very patient I'm not going to say anything I'm on a strike you can guess you only have to save public or private okay I'd answer for you Bruce but I do know the answer so I wanted to let somebody else who didn't yeah you're not allowed to answer I would I would have I would have gone on a strike sighs it's gonna be much more fun if somebody tries to answer public a project that's all you have to say you can guess 50-50 chance hey you can give me the answer I won't go ahead come on tell him Oh me yeah yes the P means private yes so this is not going to participate in our API so I know amend the right track if my constructors return the data type that's associated with this class and these do right and if all the other functions take this argument first if they take an eraser first private ones I don't care I give them a pass this is just a code organization technique right so now we need to build this step size and so the step size let's see that I see if I have the text that's five long and I can do a divided by so I'm using this format so I can pipe it because I just think in pipes often right so let's say that there are two steps right so this is 2.5 what should to the step size be do you think if there are five characters and we're going to memorize this in two shots so we round up or down probably round up right so I could do that with seal okay this looks good to me so this is I could actually say size and steps right step size and the size steps so we're going to start with the size and then we're going to pipe that to let's be pretty or actually I could just say size / / steps right size / steps and then I can pipe that much to seal stands for ceiling so I'm not a fan of all of the all the abbreviations but you know Joe's a was and his language okay so let's see if we're getting a schedule out of this okay so that looks good so now we could say we can call this new one can't we know this isn't gonna work I need to call this you have to say no I'm going to delete this one that would be a shame wouldn't it so our default I'm going to remove this default this is what I'm going to do and I'm going to say there's no good way so I could default both of those let's see if I could do that now I don't think I want to do that so I think I'm just going to leave this alone and then I can uh here it is this is what I'm looking for yes because I've defaulted this much right and then I could say bill the schedule okay and then I can build the schedule the problem is that my build schedule is going to return something so this is the new schedule right okay and what I'm going to do here is I'm going to basically return the new schedule as part of the struct and all this is doing is saying I want to take well let's see and then I want to do I'm getting myself in so much trouble here you know I think I'm gonna go is simple so it's gonna go really pretty so what I was trying to do is is build a really pretty version that managed so I'm gonna okay this is probably the best I can do - I'm gonna say default text so I need this in more than one place right so I'm gonna set the default text to this right this is a module attribute so it's kind of like a I don't know like what would the job equivalent be I feel like the constants right yeah it's a module attribute base achill II it sets at compile time this this value and this can be changed though constant can't but it's it's gone by runtime so basically this is replaced this has been substituted in runtime and so this is going to let me do something like this I could say new and then I can say default text and I'm gonna pick up the default steps right here right so that's great so I haven't repeated any of that any of that work so so now I've got the build schedule that looks good the step size looks good and not gonna recompile this new schedule is unused yeah because we were going to use this in my trick that I don't need anymore right so now okay great so now the mornings what's that I say it's always good to pay attention to the warnings absolutely right now okay yeah so now look we have a schedule that so this tells us this means at step one we're going to delete characters 27 for 32 for two and so on and so forth right so this is cool so now we have to build our reducer remember that there were two major types of functions one what's the constructor that's here now we need a reducer and I think I'm going to move this up because this is really the primary purpose of the API right and a reducer is going to take something of a type and this type will be the one defined by this struct right there okay so now we have an eraser and this eraser look at this we can do some pattern matching right and what that means is I could say hey lick sir I know this thing is coming in it's going to be a map well it's a struct but a struct is also a map and I think that the that the we're going to have a schedule and if schedule is going to be broken down into a head and the rest of the schedule maybe we will call this a step right and so now we can we could actually play some games here we could say we now we have the exact step so if a character is in this this is what's going to be deleted right so now we're going to say we need we need the new text okay so now we're going to have to play around with this text okay so let's start to manipulate this step by step so I've done some internationalization so that means I know that you don't want to try to cut up characters in the middle right so there's this function there's one in every language but an elixir this cuts things into individual strings right and so now I have a perfect a perfect way to represent to iterate through these and what we're going to do is actually attach attach an index to each one right so now hi it's starting to come together because I can filter these by whether or not they're in the list but there's a problem we've started our index at 1 and this is zero based so what we'll do is with that with index we'll build a one offset instead and you can see that now this goes from 1 all the way to 40 so we're in pretty good shape and so now we want to filter this enum dot filter I think that we're far enough along this is I kind of know where this is going so let's go ahead and take this and we're going to say okay here's our eraser so we're gonna start with the text so I'm gonna go ahead and pull the text out of there at the same time so I'm going to start with a text and then I'm going to do all the work that I was doing here right and then there's another one right there okay so now we're with index and now I could say enum dot let's map over this right and so we're gonna take this is a function so enum map calls the function for every character that's in this list actually it's going to be for every tuple right because now we have something that looks like this and so I'm going to say we're going to get a for a tuple that's in there and I'm going to say replace replace replace grapheme and I'm gonna pass it the tuple alright and so now what does the B mean me again private no sorry somebody else give me the answer that he just gave you I'm gonna go on strike that's where I will I feel I feel like everybody's like muted it cannot meet themselves or something it's that's bizarre okay that isn't a host or something all right all right well I don't know okay so I guess I won't go on strike maybe it's a hardware problem okay so I'm going to say this we're gonna replace replace the graphene remember this doesn't participate in the API right this is just a function that a local function that's going to help us solve the problem so in we're going to get this is going to be a graphing a graphene and it's going to be an index let's see oh you know what we can actually do better than this right we could say the the graphene the graphene and the index this is going to be the graphene in index is that index in the step ah that's beautiful I love that line of code right so what we're doing is we're saying hey we're gonna map over we're gonna start at the first one right so that's this one and we also have a step and the step looks like this right so if the character we're gonna replace the character right and we're going to pass the character itself which the first one is going to be the D and the first index is going to be one but we're going to check to see if the index is in the step so the first one will be false and the next one would be true because true is it is in two is in this list right and so it would erase that character and then all we have to do is join those together again okay so now we have to write the replace right so if this is true that means yes we want this to be replaced underscore right so basically together these two function heads make one function and what and basically i'm pattern matching if this is true if the second argument is true then i basically wipe the character out and if it's false I maintain the grapheme as it is so maybe I'll spell that out okay and then I also don't like this name anymore because it might replace it it might not so I'm gonna call this maybe replace the grapheme right and that's going to be give me a good hint that the last thing is a function okay so now we get to return the result so trust me on this syntax all this means is that I'm returning something of of this struct that takes this form right and so that it's going to take text this is the new text after we've done the work right and we don't have to process the whole schedule anymore we're down to the rest of the schedule right beautiful okay so let's see if this works on the console believe it or not our Live View application is almost done so I'm going to recompile hey things police interrupt your flow man but just everybody now should be able to unmute themselves so you guys got no excuse for not saying something okay great okay so somebody help me debug this function it says undefined function maybe replace graphemes last two ah look I have to I have the braces around it right so I don't really take those I take these yeah maybe that was an advanced a little bit too advanced okay a racer is unused so where is this unused eraser okay so we don't need we don't need to this name that's great so let's see okay so now we're recompiling and so I'm gonna start so how do I start my flow what's my constructor here are your choices new or erase which one's a constructor I can't draw this crowd out yeah I know who is the constructor it's new and what is the reducer hint it's not the one that's new the race so what is eraser new going to return there it is right let's call this the this initial state equals eraser dot new because I don't want to keep showing you what's what happens if if we run the random numbers again every time right because that would get really confusing to explain so I'm gonna say now we have an initial state how do i erase some characters will I call eraser erase and I'm gonna recite that because I want to memorize this phrase did you try turning it off or again dang okay so now we're going to erase it again did you try turning it off and on again and again okay and that's done only it would be really nice if we ignored punctuation right so I think what I'm going to do is and this is going to be a question mark and what did I do oh just a fat finger there a question mark a space and a period and I think that's it and so let's go ahead and just add another function head here right we can match G and I don't care that's my test right when G is in so if G is end ignore the replacements I want to return the G I don't want to replace it right okay so now let's try this again ah that's nice right so now if I if I'm in one of these intermediate steps it's gonna look it's gonna give me my clues right so that's what threw me off when I couldn't memorize the time before let's try now did you try turning it off and on again ha it works perfect okay so now let's go ahead and cut into our live view right so we said that there were two phases right there was the initial mount face and there was the next face right here is a live view that's an example and we're gonna go ahead and grant and grab the front of this we also need a router so let's call this live aim and we're gonna call this game live I don't need this this is called a live action I'm gonna leave that off just to keep things simple for the rest of the evening and we're going to go ahead and and establish something that is we're gonna establish our new live view in here so this is going to be game live DX remember this is going to live where the rest of the web controls or with the web constructs to you and I'm going to grab the code that I I stole from the other view I'm gonna strike this imple equals true because I don't want to talk about behaviors you know there's not enough time here but mount essentially I want you to write this open up your chat and if you have not been interactive be interactive now this will help you if you ever get back through the live view remember write down this the socket is the state for a live view I hear people typing I hear more people typing it's beautiful that's kind of beautiful so basically a socket is a map it's gonna look like this but basically inside the map there's going to be some value some key value pairs and there's a special key in Sukkot for the custom data that we're allowed to touch and so we're going to need a couple of things we're gonna need text or actually all we really need is an eraser and then I can say eraser dot new later on if we were working on this application we might have a form that that allowed you to take in the different fields for an eraser right but so here we could take the number of steps in the default text but I'm just gonna go ahead and let that numb and let that sit just like that right and then I'm gonna write a function called render right remember there's that yeah so remember there's that special key in assigns and in I mean the socket called a science and this is this is basically the custom data but I'm gonna write something called a sigil and I want to point out while I'm here that I don't have to drop this code into the view it's helpful for instructional purposes because everything is in one place but I could drop it into a template right this code could sit exactly where this code says right and what this means is that there is a elixir bit of sugar that's custom programmed that's going to take this multi-line string and it's going to return the structures that elixir needs to only send down the changes when when something changes here so I'm going to say h1 memorize this and then I'm going to give it I have little imagination my user interfaces are all age tags so we're gonna say in the eraser and all this is this is sugar for a science that eraser right but I'm gonna specify it this way because it looks like other templating languages eraser dot text right so this is my substitution and the way to read this is that the first pass through remember this is the mount face when when when we hit our route so we hit the route here in the router we said okay we're going to call game live and so game live we're going to call the mount function and we've changed data so that means that this is going to render but there's the full text of this application is going to render it's going to come down right and then I'm gonna go ahead and drop a button in how about just a race Sun like that okay so now I ought to be able to run this application except alias what was it memos eraser okay so now we ought to be able to go to our live view we have a syntax error somewhere but let's go ahead and go to game cannot define page live yet we didn't change that did we okay so page live this is gonna be game live okay beautiful this has not been saved now it has beautiful great okay did you try turning it off and on again and erase um and it didn't do anything but I can change that really quickly right so the wait live view works is that I'm going to decorate this HTML with HTML attributes and one of those is Phoenix click and what its gonna do is send me an event called a race whenever a user clicks that button right so I haven't reloaded this but there's a code watcher that has so if I inspect this element you we should be able to see that there's this Phoenix click attribute on it here right and so if I try to click this boom I get a crash right well that's great because Phoenix has recognized that I'm trying to capture this event but there's no code to capture it in fact this is exactly game live that's our live view and this is the function that it's going to try to call so I'm gonna go ahead and grab that right and so there's my socket so this is exactly what my code should look like great so I'm gonna go here I'm gonna define that erase I don't really care about this metadata we didn't put anything in it this is a socket what is the socket everybody I'm growling okay and so we're going to say erase and that's going to be a reducer over socket right remember we want to slice our code into constructors and reducers in one final form of a transformer right this is a converter or a transformer this is a essentially a reducer right because it comes in with the socket and it returns another form of a socket and I'm just going to build a reducer that lives up here and the reducer all I have to do is call I need to call erase socket science race and I basically need to put this thing remember the socket is the state of the live view and is the special place where I put all of the my custom data an eraser is that thing that I put in there that I built with my constructor right so now all we have to do is take this we have to take we have to assign socket and the eraser is going to be this thing right here right and all this assign function does is drop this into my socket okay okay unexpected so on live view 20 should be easy so that is eraser psyche of the sines eraser you might say oh yeah no I don't see it anybody see it this is telling me let's see undefined function game handle event is undefined a private oh that's the earlier one Oh compilation here right here unexpected parenthesis open parentheses on line 20 so it's probably probably an earlier sin so a sign open parenthesis socket and then eraser and that's the value and you have to see this better so that's lining up this looks good to me anybody seeing anything Oh a word a race a race they're on 18 instead of is that supposed to be a racer or something hey Tina yeah this guy right here no no this is the module this is the module this is our module that services our functional core this is an eraser right so we say well what's the data that's associated with this so that's an eraser right and what's the function that you call to do the erasing that's a way to race okay right so yeah so I found the bug the bug was I had I didn't have a enough parenthesis right it's like it thought it was oh I gotcha it taught us we thought it was Lisp or something right right okay so so now I have the assign no that's that should be correct is it it looked right before to me with the parens but because I was looking at that one too so my editor thinks I'm right now live 20 compilation live 20 okay so deaf erase so I wonder if this is an earlier problem so that is those match right so this is I'm closing that up there's times do that looks good let's see so let's erase this guy let's see what's happening undefined function erase okay so it has to be in there somewhere right or it wouldn't have parsed so I'm going to assign suck it and the eraser so that's basically this right here is a key value pair right so eraser : that's if you if you coat a ruby before you note that the last argument if and this actually came from Ruby syntax so if the last argument is a key value list you can eliminate the brackets around it the square brackets around it so that should be okay so and this is also all my error has to be up here somewhere so let's see so 18 you dog a race socket can we one line this just to kind of change things up is that gonna give us any more data don't say so it has to be earlier in the file right so let's see what if this is but you're able to do the uppercase e in a race there on time I guess 13 now that that's that's kind of throwing me and if see how this is I named tonight the numbers they're just just a race rate shouldn't be a lowercase e that that's yes that's what keeps getting super clear race which is pretty amazing right and so I think that we can kind of we could play some games to you know make this prettier right maybe we do that um boom so it's really pretty cool and so let's try that there we go so it's not getting my spaces correct but I think that that's pretty good for a very short amount of time right oh yeah so what have we learned so far right so what we've done is really very little live you this function doesn't do too much work it doesn't really save is that much work but what you can see is that everywhere that I have interaction on the page then the hardware are I'm sorry that the software the custom software in live view the live library handles all the communication between the client and the server right and when I get communication I don't jump straight to render right what do I do I change my state even if my state changes all I do is rerender right and so one of the things that I want to point out is that when we click the render button right so I'm going to reload this and when I click erase something I want to show how tiny the payload is when I'm working with this right and so I want to show the Java JavaScript editor javascript console right you know I wanted to basically grab one of these sockets I think it might be let's see if it's that first one right yeah and look how much is coming down very tiny amount of data right now I'm gonna click it again it's such a tiny amount right it only sends down anything that has changed in this field here which is amazing right and so your form validation it only sends down data when something changes it only sends down error errors when the errors are tripped so what you get is something that's extremely efficient because elixir and Erlang are very consistent in terms of of not letting threads lag of of detecting when there's problems shutting down problems and starting them again did you try turning it off and on again right it's very high concurrency and this is the concurrency game so you see a lot of frameworks trying to attempt to get a feel that's similar to Live View and they're failing why because with live view I have pretty much a guarantee that if I send down 2,000 events well the slowest one is only going to be 10 10 seconds around 10 seconds 10% slower than the fastest one in Ruby that's very much not true in Java in JavaScript in Python these are very much not true you have things like memory management right you have to you know anytime that that you're fleeing you're you're freeing your block of you're freeing memory you have you could basically you know jump off a cliff as the as the memory management is tripping it right so it's it's really an incredibly efficient system in that we're normalizing the flow right the second thing that this does for me is this puts my head all in one place so this application was not a distributed development experience this application was like developing only on the server because I was right so web development became really easy for a little while because we were able to put our heads in one place on the server and we were able to leave it there but once they started to become these interactive distributed applications again that didn't just band the client and the server they also spanned the client the server and programming languages and database servers it all became too much but live you lets me keep everything in my head in one place and so I think that this is going to be massively important not just for the elixir community but really for programming in general this at programmer Passport that's a program that gracio has where you you you have a subscription you know we're taking our monthly subscription away we're only doing yearly so that we track only serious serious people who are want to improve their career but we're treating both OTP and live view as if they were their own languages very much like we're going to treat tensorflow like it's its own language because there's so many new and important concepts and innovations in the space and I'm going to go ahead and stop talking and take questions all right so I'm gonna stop sharing I'm gonna open up my chat look at all those I'm going to step out and step back in and see if I could see Bruce's screen again sorry for me that's okay okay I'm back now so questions you can type them into the chat if you would like Bruce I have a quick one so I don't know how much you've actually used live view on on let's say you know production type projects but one thing that I was thinking about is like so live these fantastic right like I think this oh my gosh I would love to just just use this right but then I think you know well you look at like react or what not and and react is the one that I'm most familiar with there's like a zillion pretty nice component components that are already essentially built in at libraries that have really nice functionalities right from kind of like a UX and UI experience and that is like like I was just looking and I see they have live components now right which I have to dig into a little bit you know a little bit more but do you have any thoughts on that and he you know have you has anybody talked about that like as far as you know about like hey how do we kind of reproduce what's already here like that this really vibrant ecosystem so the first thing we don't want to reproduce what's done in JavaScript right so the we used model for components and JavaScript is broken right you can see that in the dependencies you can see that in the way things compose or really don't compose right dependencies are in the wrong places elixir is very much a different story because you know basically when Chris and Joe's a worked together really since somewhere around you like 2013 or 2014 they started working on infrastructure right and they didn't move forward until everything composed the way that they wanted it to so with that said when you when you actually build with live view a lot of what you're reusing is actually on the server right and those types of libraries should come around more quickly but the other thing that's happened in the last last three or four months is that the JavaScript integration into the live view flow has become much more secure and it's it's with it's in two places one there's a there's a down there's a hook for down bound client right and that means that I can send data down to the Dom maybe on the timer and stitch it into the Dom without having to go through the event lifecycle yeah that wasn't there that wasn't there when I started playing her out and I said it's got to come right and then I kind of like a junk conjecture and okay that's good to know and the second one is that there's an up bound client or up bound hook also and that's you can hook any JavaScript event and have it participate in the live oh cool all right well that kind of that kind of honestly honestly solves that right okay all right well that's good to know I mean they've kind of come come a long way with that but but again I think that we need to rethink how what we need to re-evaluate what's important to us right so the idea that that you can build these things that compose in an object-oriented like way it's broken right the node dependency stack is broken the security violations in the node dependency stack I mean you could tell you know I have like 847 vulnerabilities and some of those I'm told are related to you know it's just like server-side stuff and some of its not but what do I do you know I'm not I be yeah the the use case I'm kind of talking about is is more like let's say just a simple thing well simply yet not so simple thing right like a type-ahead right or like an auto-fill as you're typing or tags or something right these are things that people don't yeah so there is a validate on forums so one of the things that I demonstrate if I have like two or two-and-a-half hours with this problem it's called a schema let's change that right so if you're working with a database component you basically say hey I want this to be validate required this to be validate length and everything in up slug that in and then I have the the live view component basically live view forms will basically fire validations for every change of the form it's surprisingly efficient now and it also fires fires a saved form when you save the form right so you get at every change and you can D balance if you want to if you if you're worried about against nd too much right yep yep good yeah all right that that's good enough to I'm gonna have to definitely play her out there cuz that's like one of those like it looks simple but then when you start getting into a few complexities and I I know live view is extremely efficient right like like you said you showed those payloads I when I build my project I looked at the same thing I'm like oh my gosh this is like nothing right like yeah so and so what I would encourage you to do is stop thinking of about things as if they were a JavaScript world right you know the problem is that if you're if you're in a if you think about it like it's a JavaScript world then you're gonna be thinking what can't this do that javascript does problem for nodejs because they need some specific permissions for running your machine and create so many security issues and this thing they know tries to how to figure out and create some Vera in safety place for running a JavaScript on your machine or production and so on but I think it could be needs too thanks for try to investments for good apartment for day no development and so on for I don't know say this in English because okay it's touching some point for poor in productions environments you know so I think it's very in stable and they have so many problems to figure out some place for handler for your package installations and so on it's something like that for crate for they know a they don't would like actual create some [Music] central core for your handler your package for instance and so on yeah yeah and so I mean so there's a lot of thoughts there right so so some of the problems don't get completely solved but they get better when you depend on infrastructure rather than custom code to solve a problem right like when also I mean there's a lot of really sophisticated JavaScript behind what's happening here right and there's like a testing model that's like it's not a headless browser but it still has to do dumb replacement so there's a lot of stuff but it's infrastructure and more people are using them to structure that's really important right the second thing is that there are a lot of things that we do that we need to do like the idea of running JavaScript on the client running disconnected that's that's a big deal right it's becoming less of a big deal but it's still a big deal and so there are some something's happening with there's a project called lumen which does web assembly it's working on elixir it's not there yet might get there we hope it gets there but when it does then you're starting to build in some of the same kinds of problems right you're starting to do heavy Davout a lot of heavy custom development on the client and I think that we need to be getting away from from that that we need to to try to be as as clear and precise as possible I mean I think that we're optimizing the wrong thing I think that we're trying to go all the way down and get corner cases for our end users when we really ought to be thinking about user experience and if we can iterate faster on our end user requirements then we're better off than if we get that that last little bit of smoothness and experience and I mean live you can do awful lot so we actually just built a Tetris game that I played internationally you know in a server in the United States and played it from India and it was it it actually ran thanks look up that Tetris demonstration I've been trying and mike was able to convince his company to like sir i've been trying and it's like you know we have these issues with scaling and it's like good lord this has already been solved elixir has done it listen to me you know right right I mean the main thing is is it's it's very hard to to stop the world to do a new implementation right but what you can do is you say if we could do this is this interesting some day and you know if if I can solve this problem that's very difficult to solve so you know in a place in in Pinterest it was how do you do fraud detection right that was small enough and isolated enough where they made a lot of headway that way and Bleacher Report it was there was a a global directory right and they basically started their migration that way and that was a became a five year migration but extremely cost-effective for them right because they were in a place where they had this ruby infrastructure yeah and they had you know they were deciding hey you know when when people land on our site will serve the page right and then when push notifications happen and they built them into the application Kevin Durant gets traded Kevin Durant gets traded 20 million push notifications go out 10 million users lands on the server within seconds and this the Ruby stack of servers glows a dull red and melts too right and and so they did solve that problem where you know my friend Ben marks who's recently moved on but he was one of three people supporting the Super Bowl rather than their whole development team and support meant that he just had he just had to have his fun with them Super Bowl bleacher report right and it out did you get your question answered or did you Alphonse can you unmute your soda thank you press for the example I just have a question about it are there some or did you know some good reference about working with our brother especially distributed deployment like distillery in order to not only like step back from probably it react or just integrate live you and I mean in order to probably patch or rethink or really sign the back end when you are already working with another front-end framework yeah yeah so I think that the nice thing about so the nice thing about working with with a lick sir is there are so many you know if you're willing to go with an iterative approach and there are some places that where it makes a hundred percent sense today you don't have to wait right so yes a couple of questions and I want to get to the ones that I can remember one you asked about distillery so right now that feature is built into elixir as releases right and so that I think that went in and elixir 1.9 might have been 1.8 anybody know for sure but basically so that's what you're looking for so there's a there's a great discussion and about about elixir and deployment really in the dark ages in a book that I wrote with Jose a and in Ben marks my friend Alicia report called adopting elixir it's pretty old but it will tell you it can basically point you to two where where some of the interesting things are happening right but since then a good number of things have changed like we saw the live view dashboard and so telemetry has changed a lot so measuring monitoring is is pretty easy is a lot easier there are better deployment stories I'm working with giggle ixr which is a which is running kubernetes on the Google stack then there are other stacks I mean I think Heroku has one and you know there were a number of other very good solutions Heroku is you know they're parts they're trying to hold on a little bit too tight in that they have some some solutions that open up what's basically the elixir distributed architecture which you need to run observer and basically do all the monitoring measuring that you need to do but not until you raise yourself to a enterprise-class customer so but you know Giga licks are certainly as a good one there are a number of deployment options that have that have come on in the last couple years so you mentioned something about distributed applications I didn't quite get where you were going there I mean if like integrating light bill to like a current project is like do you think is like straightforward to just integrate life you or do you think so the nice thing is that the nice thing is that when you see Phoenix live you you'll see that it is part of Phoenix right and in terms of you know they're always easier integration and harder integrations to do right so when you're in any language if you have to share a database I think that that most people would tell you to run away kicking and screaming right so if the the best thing that you can do is find a vertical service whether that's a service layer that you can make completely independent or whether that's a service layer with a user interface that you can you know that's that's on the side of the application rather than part of a formal application that's that's the way to get started right I think that just about everybody has tried the approach of of doing a of rebuilding the house you know with a you know when your other ones on a trailer are going down the highway I don't know I'm mixing metaphors pretty poorly bit just about everybody is trying to build an application while another one is in deployment and you know and that's that's difficult to do what you really need to be able to do is carve off is do some do all the interface work in advance build a clean separation and then implement a new service that's how everybody has been successful with with with beginning to look at what at least we can do with them do for them question we has one function for handler the entire calls for a live view in Phoenix call it a handler events only or we has other functions to handler this calls okay so our are you an OTP person I know you're on gratia are you on yes I'm just fresh okay so let me let me review a little bit for not for you but for others on the call for your brother maybe so of so basically live view is built Phoenix in Phoenix is built on OTP so the communication with Phoenix is a socket and so you can actually see that socket defined in a file called endpoint es so that's the inbound code the inbound code is very shortly consumed by a Gen server in the gen server does handle calls and handle casts and obviously there are other messages that are sent with with native message passing right so every everywhere that I use to handle event write that is a handle cast right it's really roundtrip but you know only the outbound is done with OTP you know the the communication back down to the client that's handled by the framework so it's really all handled cast which means it's one-way which means it's an asynchronous call ok you'll see another communication mechanism called handle info handle info is able to consume any message that you send with Erlang or elixir send right so you basically if you're in an OTP if there's a message that comes into your mailbox it's going to get processed and if it's structure for OTP it will be a handle call or handle cast right so infos are not structure for OTP they always come in as handle info right and so you can access that from with a broadcast based service called publish subscribe and that means that if you're interested in a family of messages so you know I see the the Jersey so maybe is there's a channel for the Brazilian football team right and and you know there's so I can I could basically subscribe to interest and then there's another server out there you know as as Ronaldo scores as goal right there's a there's a published an event and so all those events come in or something called publish/subscribe now so if you're consuming messages from other other input sources it's really really easy in Phoenix because basically the messages come in and then you publish them out on a channel and then everyone that's listening can consume the message and it's really really efficient and it's implemented in a distributed way right so yes I have another question so live view Ronnie oh we have supervisor for live view or not yes so actually let me share my screen and we'll look at some of these things that we've been talking about and make them a little bit less abstract okay so every elixir application has to make a decision do they want to be a formal OTP application or not right if you decide yes I want to be an OTP application in Phoenix is the way this works is that there is a supervisor right and when you hear the word supervisor don't think supervision first I want you to think lifecycle right so if I go to my IEX I'm in IE X here and I say H gin server or I'm sorry supervisor right right look at the number of what of times that lifecycle words start up show up right everything in blue shutdown that's life cycle restart permanent death that's that's the life cycle life cycle life cycle right I can go up start link life cycle start and shutdown when I go to the code children that's another life cycle term right that's how do I start and stop things right which things depend on me these are life cycle terms so what happens is that when you start Phoenix it's going to start this supervisor and well it's a life cycle thing so it's going to start the database repository start the telemetry and that's that actually makes measurements starts the publish/subscribe things that we just talked about and starts the endpoint right and the endpoint is where we define the communication for the let's say there's the employment right there so there's all the data that comes in four channels right and here's all the information that comes in for the applications of the style that we built called a life suck it right and this is just the set of policies that everything needs to go through so if we're in development mode we do all these things but everything is going to do the telemetry plug the request ID you know parse different kinds of code these are all individual functions and you can look them up and see exactly what they are and the very last one in the list is a router right so yes it's an OTP application and this this gets done first then every request comes in the endpoint and then it goes straight to the router and you can see every single function plugs are just functions that gets called alright and so I don't know if I answer your question what I hope that I showed you is yes this is an OTP application that that it's an OTP application because it specifies its supervisor that automatically gets called when it starts up and when it starts up it also starts its children and every request comes in and does exactly this work did that help yes yes I need to think up more about them and read more because some some things I don't comprehension very well because my level on my studying about elixir and so in most Phoenix too because I never touch in Phoenix so anyway you are tremendously helpful with Gracia with all of your work and yeah all of your notes it's very very helpful thank you so very much no problem I very enjoying you know so because I I need to morph focus and attentions for these words and try to figure out the entire examples and yes it's very fun and try things and learn about and I really enjoyed the sections for try yourself because they shake shake shake you and yes man you need to try something different in prepare a book or and in the other contents for Brooks you know it's yes Maggie under $20 thanks for the advertisement no that's that's very kind and just keep pluggin so the OTP stuff is exactly what you're looking for and the live view is chasing right behind that so I think that you're gonna have you're coming in at exactly the right time exactly the right time yes another words for about Roxie oh I see a friend of mine and sine elixir course - and I always stop talking about with him about they they exercise the chapter and excessive - you know so it has more fun because I have another person for talking about and discussing and I think is the most wonderful when you have some doubts or knowledge for fresh air for people and buteo your thoughts for another word to criticize you or say something different for your visions yes it's very helpful and important for us as a community person and so on it's so we actually do we do one scholarship student per class that we do and our last one was from Brazil right so I was to give the keynote if the conference happened in May and I'm sorry that this didn't happen that was learning Portuguese for yeah I'm still learning it I still do my duolingo everyday you know but Adolfo joined us for the class and I'm sure I will have another another Brazilian student it's amazing I need to [Music] know about the course because I will I would like to assign as to because it's a huge investment for Brazilian folks you know yes yes yes so that's what we're doing the so basically with the pandemic we're already half price and that's why we're doing the scholarship program and you're on a list don't worry that's great thank you man seriously hey it's great so Adolfo was a great man and I've been teaching a Kenyan group earlier today yeah Oh Thayer's such brilliant developers I have no idea why everybody isn't going there for their their project work no language barrier just you know plus seven hours instead of plus eleven or will attend and a half for India it's it's man they are amazing they're fantastic and you know as you know as you can see right here there's some rising Brazilian talent as well you know there's great little looks or talent around the world that is a fact a lot of questions to get them all rush anything but yeah he's gonna say are there more questions no but I I say something because in in this chain we we pass around the road about the the corona virus and think you need to put your money for what kind of the thing you would like to invest your money not only food or cross instead and so on and something like that I really like to invest my money in Ingrid so is that they I don't know airline foundation because they has they basically the same a course for Brooke Co and I think it's more investment for community I invest in some person different the organization because they has more more money instead over a person yeah so I don't know I agree with that I think that we decide the world that we want to that we want to live in right and so about six years ago I decided to I was at a conference and there were four hundred three people four hundred were white guys four hundred at a four hundred three white guys and you know that that was really just working for me and so I started laying the groundwork to start this this learning company and our goal is to spend about half our time you know serving underrepresented in technology and right now I'm up to about a quarter of my time but I'd like to I'd like to get that number higher and I know I know that I have some some work to do we've talked about you know pricing a world pricing we're working on that but with one developer that's that's you know building the printing press and writing the books for the printing press it's a little difficult so but thank you for so much for your words of encouragement and your support it's very much who we aspire to be we want to make better developers and you know we we believe that learning multiple languages is the best way to get there well does anybody else anybody else have any questions well I just want to say Bruce thank you so much this was this was super enlightening it was good to come back and live you and I first saw I was like oh and then I said okay I played around with the did my little project and then kind of like let it simmer in the background this C word you know you have to write like you have to like lay a few seats and then you have to like see what happens over the next like eight you know ten months or whatever right so uh yeah this this is the super cool Ryan and I think Jacob took off but they thank you as always for setting setting meeting up and everything you know an Oreo you know thanks for being here I think this was great i Bruce next year I would I would love to have hopefully we could have the event in person yeah I would I would look I think that you know I was talking to an Oreo about and I think that like your philosophy and everything with you know kind of learning learning it's just really a like kind of life of kind of like learning continuous learning right he's huge and it's right on point with why we develop Detroit Tech watched in the first place so I just think it's a real good fit and I've been checking out Rock Co and I may you know it might be something even though I've done a lot of elixir stuff already you know I see that you have like Pony on there and crystal is another one that's interested we had to talk about crystal last year a talk I'm a pony last year and so yeah it's just it's cool to see what you guys are doing over there and I really really really love the underrepresented that you're a really focusing on making sure that you know underrepresented groups are getting the learning too so I think that's that's just fantastic because I'm Nuria and I have talked about this too it's like even with our events it's like we just we don't want a whole bunch of white guys they're like and so trying to break through that barrier is apparently just right I mean look at that look at the list it's you know it looks it's it's a it's a great group yeah thank you and I agree and tech watch is an amazing event if you ever get a chance to go in person go in person it's it's a awesome yes I I know I know it's it's definitely unfortunate but I feel like you know we're gonna get through this it's just it's gonna take some time as you know we've we've all kind of figured out and you know we'll we'll navigate the waters together but you know I really appreciate the fact that we could have these remote events it's great to see everybody's smiling faces and you know it's it's just you know I just feel very very fortunate to be able to continue these events every month so all right well thank you everybody for coming and yeah have a wonderful month and hosting us thank you you know thanks again yep
Info
Channel: Collider
Views: 774
Rating: 5 out of 5
Keywords:
Id: C7uWUFqNN_4
Channel Id: undefined
Length: 107min 27sec (6447 seconds)
Published: Thu Jul 09 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.