• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Single Page Applications with CoffeeScript [Polish]
 

Single Page Applications with CoffeeScript [Polish]

on

  • 1,392 views

Slides from my talk at the Confitura conference in Warsaw.

Slides from my talk at the Confitura conference in Warsaw.

Fallout icons are from http://wikia.fallout.com

Statistics

Views

Total Views
1,392
Views on SlideShare
1,285
Embed Views
107

Actions

Likes
0
Downloads
14
Comments
0

2 Embeds 107

https://twitter.com 106
https://si0.twimg.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Single Page Applications with CoffeeScript [Polish] Single Page Applications with CoffeeScript [Polish] Presentation Transcript

  • Single Page ApplicationsUse cases with CoffeeScript, DCI, AOP, TDD Andrzej Krzywda
  • Kto lubi pisać w JavaScript?
  • Agenda• Frontend - Zmiana sposobu myślenia• Single Page Application• Frontend - architektura
  • Zmiana sposobu myślenia
  • Dumnyprogramistabackendów
  • (2007 - 2008) “JavaScript? My zajmujemy siępoważnymi projektami”
  • Rails consulting since 2007
  • “OK, zrobimyautocompletion”
  • “Znajdźmy jakiś plugin railsowy, który togeneruje i nie patrzmy do środka”
  • “Czy ceny mogąsię zmieniać bezprzeładowania?”
  • “Renderujmy JavaScript po stronie serwera”
  • “Pokazywanie słówek bez przeładowania”
  • Backendy - RubyFrontendy - Coffee
  • Social games for brands(gierki i konkursy na FB i strony firmowe)
  • “Gra w kółko ikrzyżyk, potem wybór nagrody”
  • Hybryda
  • Pierwsza wersja naszejplatformy tak działała.
  • Platforma GameBoxed == jeden backend, wiele frontendów market
  • Gdzie renderujemy html?
  • Cały html nafrontendzie, serwertylko zwraca JSON.
  • HTML na frontendzienie różni się od HTML na backendzie
  • <p>W tej sesji gry niesamowite umiejętnościstrzeleckiepozwolily Ci osiągnąć nowy rekord:<br><b>{{playerMaxScore}} punktów.</b></p><a class="button ok_button">DALEJ</a> handlebar
  • Rewolucja
  • Frontend to osobna aplikacja
  • Market 2.0
  • Pusherweb sockets
  • Pusher
  • Zmiana sposobu myślenia W której fazie jest Twój projekt?• Faza 1: No JavaScript• Faza 2: JQuery explosion• Faza 3: Page/Widget object• Faza 4: Single Page Application
  • Single Page Application Gmail, Twitter, Facebook, Trello
  • CoffeeScript better JSClass oriented language
  • Same zalety
  • JS == assembler
  • Coffee > Ruby
  • underscore.js
  • GameBoxed używa tylko Coffee do frontend’ów
  • Polecamy!
  • Single Page ApplicationArchitektura
  • TrygveMVC, DCI
  • Zapomnijmy o MVC z backendu!
  • Nie ma MVC na backendzie
  • Nie ma View na backendzie
  • Prawdziwe MVC (w uproszczeniu - zmiana w modelupowoduje automatyczną zmianę w GUI) (Rails, Struts, Spring - nie są MVC!)
  • GUI - model
  • Modele• Game• Player• GameSession• Round• Prize• Friend (Invitation)• Life (LifeRequest...)• Team
  • Views• PrizeComponent• FriendsComponent• GameArea• Popups
  • Pierwsze podejście
  • Popupy• Wyświetl popup z zespołem gracza• Po naciśnięciu OK, wyświetl popup z nagrodami
  • Callbacks
  • triggerActionsAfterMove: (move, callback) => @getBonusWhenStartCrossed(move) @getBonusWhenLandedOnCellWithFriends(@board.currentCell()) @pickCardAndNotifyIfAny(@board.currentCell(), callback)
  • pickCardAndNotifyIfAny: (cell, callback) => console.debug "pickCardAndNotifyIfAny #{cell.position}" card = @drawCard(cell) if not card console.debug "no card picked" callback?() return console.debug "card found: #{card.identifier}" card.onPicked(@) if card.onPicked? @eventBroker.trigger("player:picked_card:#{card.identifier}", card, callback) @eventBroker.trigger("player:picked_card", card)
  • class engine.monopoly.controllers.CardItemBargainContoller constructor: (@services, @game) -> @helper = new CardHelperForUsecases(@services) setup: => @services.eventBroker.bind(player:picked_card:CardItemBargain, @execute) execute: (card, callback) => @popup = @helper.showCardGenericPopupAndBindOnOK( (=> @applyFormDataToBargain(card, callback)), null) @popup.bind("popup:opened", => @popup.find(input).focus()) applyFormDataToBargain: (card, callback) => offer = @popup.find(input).val() new CardItemBargain(@services, @game).execute(card, offer, callback)
  • Eventy
  • class engine.shooter.components.StageResultWon constructor: (@eventBroker) -> _.extend(@, Backbone.Events) super() @templateId = "stage_result_won" addMeToScreen: (root, me) => $("#gameArea").append(me) configureElement: (me) => me.find(.okButton).mousedown (event) => @hide() @eventBroker.trigger(stage:result:shown)
  • class engine.shooter.models.Game constructor: (@serverSide, @eventBroker) -> super(@eventBroker) @levels = [] @guns = [] registerEvents: (eventBroker) => eventBroker.bind(game:start:requested, @start) eventBroker.bind("player:clicked:inside-target", @playerTriggeredShotInsideTarget) eventBroker.bind("player:clicked:magazine:reload", @playerWantsToReload) eventBroker.bind("stage:start:clicked", @startStageClicked) eventBroker.bind(countdown:stage:finish, @finishCurrentStage) eventBroker.bind("stage:result:shown", @loadNextStageOrFinishGame)
  • Wymagana duża dyscyplina
  • Gdzie jest główne flow?
  • Use casesUsecaseController DCI
  • PiotrekGame Designer
  • Tomek - programista (praca zdalna w praktyce)
  • class engine.invite_and_win.GameUseCase constructor: (@game, @player) -> ObjectHelper.addRole(@player, engine.shared.models.PlayerWithFriends) @facebookHQ = new engine.invite_and_win.FacebookHQ() tryToEnterGameArea: () => if @amIEnteringGameFirstTime() if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) @teachPlayerHowToPlay() else #n-th time... if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) if not @playerLikesFanpage() @askPlayerToLikeFanpage() if @haveNotYetPickedFavPizzaCountry() @askPlayerToDeclareHisFavCountry()
  • Use case używa dziedzinę
  • Ani use case, anidziedzina, nie wiedzą nic o GUI
  • Ani use case, anidziedzina, nie wiedzą nic o persistence
  • Use case’y mogą działać z innym GUI i innym persistence
  • Gdzie jest DCI?
  • Data Context Interaction Trygve
  • Dane pozostają w obiektach Data
  • Obiekty są dosyć “cienkie”
  • class engine.shared.models.Player constructor: () -> @rank = null @maxScore = 0
  • Use case to zachowanie Context
  • Obiekty mająwstrzykiwane role Interaction
  • Role są dodawane runtime!
  • class engine.invite_and_win.GameUseCase constructor: (@game, @player) -> ObjectHelper.addRole(@player, engine.shared.models.PlayerWithFriends) tryToEnterGameArea: () => if @amIEnteringGameFirstTime() if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) rola @teachPlayerHowToPlay() else #n-th time... if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) if not @playerLikesFanpage() @askPlayerToLikeFanpage() if @haveNotYetPickedFavPizzaCountry() @askPlayerToDeclareHisFavCountry()
  • OOP != COP
  • class engine.shared.models.PlayerWithFriends extends Mixin setup: => @friends = [] @invitedFriends = [] @acceptedFriends = [] setInvitedFriends: (facebookUids) => for facebookUid in facebookUids friend = new Friend({facebookUid: facebookUid}) @invitedFriends.push(friend) setFriends: (friends) => @friends = friends addFriend: (friend) => existing = @getFriendByFacebookUid(friend?.facebookUid) if not existing? @friends.push(friend)
  • class engine.invite_and_win.GameGuiConfiguration constructor: (@gameUseCase, @game, @gui, @services, @sharedComponents) -> execute: () => Around(@gameUseCase, tryToEnterGameArea, @checkFbInvitation) After (@gameUseCase, tryToEnterGameArea, @showTeamArea) After (@gameUseCase, tryToEnterGameArea, @showButtonInviteOrPostPicture) Around(@gameUseCase, tellPlayerHeIsPartOfTeam, @showTeamPopup) Around(@gameUseCase, askPlayerToLikeFanpage, @showLikePopup) Around(@gameUseCase, teachPlayerHowToPlay, @showTutorialPopup) Around(@gameUseCase, playerWantsToKnowWinnersWithPrize, @showWinnersPopup) Around(@gameUseCase, playerWantsToKnowPrizes, @showPrizesPopup) Around(@gameUseCase, askPlayerToDeclareHisFavCountry, @showDeclareCountryPopup) Around(@gameUseCase, iAcceptMyFriendInvitationToATeam, @onIAcceptMyFriendInvitationToATeam)
  • Around(@gameUseCase, tellPlayerHeIsPartOfTeam, @showTeamPopup)
  • showTeamPopup: (proceed, friendsInviting) => data = {inviting_friends: friendsInviting} popup = @popupsComponent.showPopup(team_popup, data) popup.bind(popup:closed, => proceed(friendsInviting))
  • Persistence
  • ServerSide
  • class engine.shared.server.ServerSide constructor: (@gameBasicDetails) -> @gameEngineUrl = "/engine/games/#{@gameBasicDetails.id}" @gameUrl = "/games/#{@gameBasicDetails.id}" @errors = [] gameDetailsLoaded: (gameDetails, callback) => callback(gameDetails) fetchGameDetails: (callback, errback) => $.ajax( type: "GET" url: "#{@gameEngineUrl}.json" success: (gameDetails) => @gameDetailsLoaded(gameDetails, callback) error: errback )
  • My ładujemy dane na starcie
  • Można ładować w trakcie
  • Testowanie
  • scenario "player enters and has no friends", -> @player.enterGame(@playerWithNoFriends) @player.shouldSeeMainAreaWithInviteButton() @player.shouldSeeRemainingFriendsToCompleteTeam(4)scenario "player enters and has collected part team", -> @player.enterGame(@playerWith3AcceptedFriends) @player.shouldSeeMainAreaWithInviteButton() @player.shouldSeeRemainingFriendsToCompleteTeam(1)scenario "player enters and has collected whole team", -> @player.enterGame(@playerWith4AcceptedFriends) @player.shouldSeeMainAreaWithPostToWallButton()
  • TDD
  • Acceptance tests with test.ServerSide
  • Reużycie?
  • Jak reużyć kod po obu stronach?
  • Nie wiem.
  • Czy Googlespozycjonuje SPA?
  • Tak.Ale trzeba renderować html po stronie serwera
  • Frameworks
  • My nie potrzebujemy
  • Ty prawdopodobnie też nie potrzebujesz.
  • Krytyka Backbone.js
  • powiązanie danych z widokami
  • a gdzie logika biznesowa?
  • Backbone Models
  • Przydatne do API, alenie używajmy ich jako dziedziny
  • Relations/objects mismatch
  • Resources/objects mismatch
  • Libraries - yesFrameworks - no
  • Bardzo ważne pytanie
  • A może Twój projekt powinien być SinglePageApp?
  • Co jest lepsze dla użytkowników?
  • tl;dr• Frontend to osobna aplikacja• Używaj CoffeeScript• Nie używaj frameworków• Pisz use case’y w kodzie• Poczytaj o DCI• Twórz fajne frontendy
  • Pytania? Dziękuję! @andrzejkrzywdahttp://andrzejkrzywda.com (ostatnio sporo bloguję o frontendach)