En dag för 3 år sedan i The Codest-teamet förberedde vi ett fantastiskt Cody-spel för Ruby-programmerare. I dagens artikel skulle jag vilja beskriva hur arbetet med detta projekt såg ut och framför allt visa dig koden för projektet, som från och med nu är offentligt tillgängligt på vår github.
Speldesign
När vi designade spelet var vårt huvudmål att skapa en rolig underhållning för programmerare, samt att göra något intressant som en del av arbetet i vårt företag. Hittills hade vi inte haft någon kompetens i att skapa spel, och därför var det en stor utmaning för oss. Först och främst fokuserade vi på vad det här spelet egentligen var. Efter att ha kommit fram till den första planen tog vi oss an uppgiften.
Som en del av arbetet med spelet bestämde vi oss för att ta ett hackathon och dela upp oss i grupper som utförde specifika uppgifter. Med en sådan 8-timmars arbetsfördelning kunde vi förverkliga utseendet på motståndare i spelet, hela layouten och grunden för både uppgifter och API: er i hela systemet. Under nästa steg samlades vi för 4-timmarsmöten en gång i månaden, vilket gjorde att vi lyckades slutföra spelet på 3 möten.
Implementering
Eftersom vi är specialiserade på RubyOnRails valde vi den tekniken som den ledande. Spelet var dock inte avsett att vara textuellt och därför återspeglades tillvägagångssättet i applikationen av SPA-typ. Som en del av uppgiften arbetade vi med en välkänd pipeline av tillgångar från rails (2016 fanns det inget bättre i princip) och hela javascript baserat på vår egenutvecklade kod med hjälp av TypeScript. I applikationen fanns en standardiserad ansvarsfördelning: Rails som tillgång och API-källa, javascript och relaterat som interaktion med användaren. Här fungerade det dock som en hybrid och vissa vyer renderades helt enkelt från Rails medan vissa av de andra - från JS.
Typsnitt
Det var vårt första experiment inom detta område. Det här var tider då människor trodde på CoffeScripts framgång. Användning av TypeScript krävde introduktion av en typescript-rails gem. Tyvärr var detta inte det sista, eftersom typescript, som är ett statiskt typat språk, också krävde detta från de bibliotek som som standard är kopplade till rails.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/jquery.d.ts (särskilt när man använder det inbäddade systemet för tillgångshantering med rails).
Cody som spel krävde en hel del dynamik på webbläsarsidan, samt modifiering av DOM:s träd. Att använda TypeScript istället för vaniljjavascript var ett stort steg i kvaliteten på koden, själva förekomsten av klasser och inkapsling var mycket frestande för oss.
API och SPA
Under 2019 hanteras SPA-applikationer genom att använda den magnifika React eller Vue bibliotek. År 2015 gjorde vi det dock på ett annat sätt. Det tidigare nämnda typescriptet var till hjälp vid implementeringen av spelet, medan jQuery återkallade allt arbete relaterat till xml http-begäran. Nu kan vi använda fetch, medan tillbaka i dessa dagar `$ .ajax` var allt som behövdes för jobbet. Ta en titt på vår klient api!
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/services/api_client.js.ts
Om det var api så var du tvungen att lösa autentiseringsproblemet på något sätt, eller hur? Jo, det stämmer. Men i det fallet gick vi efter (är det möjligt att skriva här - vi använde bandet?!) bandet och i rails-sessionen skapade vi cookie_key och sparade den sedan i databasen. Därför visste vi att allt var mer än bra.
https://github.com/codesthq/cody_the_game/search?q=cookie_key&unscoped_q=cookie_key
Spelets status lagrades i databasen och information om hur många användare som hade poäng kom från databasen (är det samma databas? Kan vi inte bara byta ut det mot ett pronomen?). ACID kommer alltid väl till pass när det inte finns någon caching på systemsidan;)
När det gäller spa är det det bästa utan att ladda om sidan. Vi har löst det klassiskt och html-ankaret var den bästa lösningen utan att expandera onödiga beroenden. För vem skulle använda turbolänkar?
SnapSVG
Om vi designar ett spel får det bara släppas med fantastisk grafik och animationer. På den tiden ägnade vi många timmar åt att fundera över hur vi skulle kunna uppfylla dessa krav i vår applikation. Å ena sidan kan canvas göra underverk, å andra sidan är det mycket lättare att komma ikapp i en ren html och det vet alla. Efter ett mödosamt sökande efter den bästa lösningen kom vi fram till att kombinationen av dessa två lösningar är svg. Det gör att du enkelt kan presentera grafik i en vektor, den är skriven i markeringsspråket och, vad som är viktigast, den kan ändras i farten. Det är viktigt att det finns ett bibliotek för svg-filer som fungerar på samma sätt som jQuery och tillåter operationer på bilden på ett enhetligt sätt. Detta är: http://snapsvg.io, Vi har mycket fina minnen av just den lösningen.
Ett exempel på hur vi använde snap.svg hittar du nedan:
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/intro.js.ts
Haml-filen i sig med det grafiska skelettet:
https://github.com/codesthq/cody_the_game/blob/master/app/views/game/show.html.haml
Som du kan se är det nästan som ett vanligt DOM-träd och en vanlig rails-app!
Betrodd sandlåda
Ja, äntligen hade vi API, grafik, SPA. Men hur är det med implementeringen av lösningar som skickas av användarna?
Det första man kommer att tänka på är eval-metoden, men vi är inte galna ;) År 2016 var docker på frammarsch, så det kändes som ett naturligt val. Containrarna själva garanterade inte fullständig isolering och skydd, varför vi använde en färdig lösning i Ruby som heter https://github.com/vaharoni/trusted-sandbox. Det gjorde det möjligt att bättre skydda koden innan den lämnade sandlådan och på ett standardiserat sätt konfigurera operativsystemkraven. Det var mycket viktigt att på rätt sätt begränsa kodens exekveringstid, det minne som behövdes för att fungera och CPU-cyklerna. Vår konfiguration finns tillgänglig nedan
https://github.com/codesthq/cody_the_game/blob/master/config/trusted_sandbox.yml.example
Naturligtvis garanterade inte samma betrodda sandlåda någonting, och det är därför vi kom på en speciell webbplats för att köra koden.
https://github.com/codesthq/cody_the_game/blob/master/app/services/task_runner/base_task.rb
Varje uppgift hade sitt eget testfall, vilket gjorde det möjligt för oss att verifiera att den implementerade lösningen var korrekt. Detta gjordes genom att injicera användarkoden i testfallet så att allt kördes isolerat.
https://github.com/codesthq/cody_the_game/blob/master/app/challenges/challenge/case.rb
Naturligtvis kostade denna åtgärd ganska mycket tid och när vi samlade in svaren hade vi inte råd att köra sandlådan, så vi sparade bara koden i databasen, skapade en inlämning och sedan, med hjälp av long pooling, bad vi slutpunkten att få kodstatusen. Detta gjorde det möjligt för oss att avlasta applikationsservern och verifiera uppgifterna på lämpligt sätt. Naturligtvis var vi också tvungna att skydda oss mot att "krascha skriptet" och därför begränsade vi antalet serverförfrågningar med hjälp av ttl-variabeln, som kan ses nedan.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/level_controller.js.ts#L92
Sammanfattning av tävlingen
Fram till september 2011 var spelstatistiken följande:
- antal sessioner: 1945 - skickade uppgifter: 4476 - skickade korrekta svar: 1624 - avslutade matchen: 31
Som du kan se började de största trapporna i uppgift # 2 eftersom det inte längre var ett vanligt hello world-exempel.
Läs också:
En snabbdykning i Ruby 2.6. Vad är det som är nytt?
Att skriva dokumentation har blivit enkelt tack vare VuePress
Vue.js grunderna handledning. Hur börjar man med detta ramverk?