Już na samym początku - aby uniknąć niepotrzebnych dyskusji o tym, który młotek jest najlepszy - przywołamy magiczne słowo "warunkowe". Z matematki taki zapis : P(B|A)=... , określa, że będziemy liczyć wystąpienie prawdopodobieństwa B pod warunkiem, że zaszło zdarzenie A. I dalej idąc w bardzo luźną analogie rozpatrzymy dzisiaj sytuację : U(scala.js|scala) czyli
U - szansa, że uda ci się coś zrobić w scala.js jeśli już robisz w scali i nie masz czasu co trzy godziny uczyć się nowego frameworka javascriptowego.
Czas i (mała) przestrzeń
Czasem w niektórych firmach istnieje niewidzialny klej, który przytwierdza ludzi do jednego projektu na całe ich życie (a w kontrakcie jest zapisane, że po śmierci ich prace będą kontynuować ich dzieci bo nikt inny nie skuma tego kodu). Często poletko, którym się zajmują ma 1 metr kwadratowy i pomija wiele ciekawych aspektów programowania. I tak jak ktoś bijący się z przeglądarką może znać wątki ale tylko te z mody na sukces. A ktoś działający na back-endzie widzi javascript jako doskonałe narzędzie do wyświetlania alertów.
Ponieważ sam ostatnie półtora roku miałem takie, że główny kontakt z webem był gdy dokumentacje do sparka otwierałem w przeglądarce także jeśli istnieje szansa na robienie weba przy pomocy ekosystemu, który jako tako znam to zamiast ogarniać całą osobną gałąź przemysłu od początku (jakieś grunty srunty) to takiej możliwości warto się przyjrzeć.
To nie jest tutorial bo tutorial już jest : http://www.lihaoyi.com/hands-on-scala-js/. Chodzi bardziej o wrażenia. Wrażenia i odczucia a może nawet i uczucia. To nieprawda, że programiści nie mają uczuć. ja np. często czuję, że coś nie zadziała.
robić Łeba
Bardzo dobry tutorial na wstęp jest tutaj : https://www.scala-js.org/tutorial/basic/ i jak to tutorial - działa dopóki robi się wszystko według tutoriala. Ale warto trochę zbaczać i psuć bo wtedy można zrozumieć to co się robi w szerszym kontekście.
Ten tutorial tworzy prostą stronkę z javascriptem. Może i warto na początek za dużo nie kombinować i go po prostu zrobić ale dalszą ciekawsza wariacją będzie próba takiego para-produkcyjnego użycia scala.js w aplikacji webowej. Do tworzenia wara użyjemy scalatry. Scalatra jest bardzo przyjemnym narzędziem bo już chyba ku*wa prostszego frameworku do tworzenia aplikacji webowej wymyślić nie można.
Dodatkowy zysk edukacyjny pracy na scalatrze jest taki, że łatwiej zrozumieć jak współdziałają te wszystkie pluginy SBT. W Playu wszystko jest takie od razu scalone - "which is good" - ale można przez tę fasadę nie dojrzeć wielu szczegółów.
Scala.js ma swój plugin, który kompiluje do "targetu" a Scalatra siedzi na XSBT-WEB, który zbiera javascript z katalogu "src/main/webapp". Jest kilka szablonów na necie, które pokazują jak zgrać prawidłowo te dwa narzędzia (na przykład : ) zazwyczaj w postaci dwóch niezależnych modułów.
Na potrzeby bloga wybiorę prostsze podejście czyli kopiowanie plików na pałę.
val kopiujNaPałęJavascript: TaskKey[String] =taskKey[String]("compileJavascript") kopiujNaPałęJavascript := { val jsHome = sourceDirectory.value / "main" / "webapp" / "js" val dependenciesFileName = "scalatra-js-jsdeps.js" val generatedFile: File =(fastOptJS in Compile).value.data val generatedDepFile=new File(generatedFile.getParent,dependenciesFileName) val destinationFile = jsHome / generatedFile.getName val dependenciesFile = jsHome / dependenciesFileName IO.copyFile(generatedFile,destinationFile) IO.copyFile(generatedDepFile,dependenciesFile) jsHome.getAbsolutePath }
"scalatra-js-jsdeps.js" to plik z wszyskimi zależnościami takimi jak jquery. A no bo właśnie - można sobie zainstalować fasadę na jquery i mieć takie "w-pół" statyczne typowanie w tym jakże wygodnym frameworku.
jQuery("#demoButton").click(processInput _) def processInput():Unit={ .... }
No i to też się do testowania przyda później. Apropo JQuery to jeszcze taka dygresja
Dygresja o rzeczach niefizycznych
W arcy ciekawej książce Antifragile Nicholas Taleb opisuje koncepcję żywotności rzeczy niematerialnych, która wybiega trochę na przeciw naszej intuicji. Dzieje się tak gdyż w przypadku rzeczy materialnych im dłużej dana rzecz istnieje tym prawdopodobnie będzie istnieć krócej. I tak np. mamy 1 letni i 10 letni samochód tej samej marki - intuicja nam podpowiada, że - wyłączając zdarzenia losowe - ten starszy szybciej pójdzie na złom.
Teraz weźmy rzecz niematerialną jak choćby język programowania. Mamy dwa języki - jeden wydany rok temu a drugi przed 10ciu laty. Pytanie, który wcześniej się rozpłynie w nicości. No i tutaj może być trudniej to ogarnąć bo generalnie język jako koncept nie rdzewieje, nie wpieprzają go korniki ani nic takiego. żywotność konceptu jest zależna tylko od nośnika i jeśli jakieś nośniki od 10 lat przenoszą ten koncept to oznacza, że z jakiegoś powodu wygrał w ruletce "selekcji naturalnej" języków programowania. A ten co ma rok czy nawet miesiąc - nie wiadomo.
No i mamy takie JQuery, które już ma 10 latek i przetrwało selekcje a 10 lat temu był w użyciu też inny wynalazek - prototype.js. Na przełomie 2006/2007 był jednym z niewielu szczepionek na popularną w webdeveloperce chorobę zwaną IE6 (gimby nie znajo i ich kurwa szczęście...). A teraz według wiki używa go 2,2% stron czyli pewnie twórca, jego sąsiad i kilka korpo z miód-maintenecem. Na wiki możecie sobie poczytać co poszło nie tak.
Także jak z dnia na dzień zobaczysz jakiś super nowy hiper frejmowrk weź wdech, dokończ robote i w wolnej chwili na spokojnie się temu przyjrzysz.
Moje pole
Celem laboratorium będzie walidacja jednego pola po stronie klienta, wysłanie go na serwer , ponowna validacja i odesłanie jakiejś odpowiedzi.
Nie ma sensu wklejać całego kodu bo to luźno zwisający HTML i kilka instrukcji scali także skoncentrujemy się na najważniejszych fragmentach. No i tak na poniższym wycinku rejestrujemy "onklika" , po którym zgarniemy wartość z pola i przy pomocy scalowego (a jakże) pattern matchingu sparsujemy wartość i podążymy jedną z dwóch ścieżek : błędu lub szczęścia.
jQuery("#demoButton").click(processInput _) def processInput():Unit={ jQuery("#wynik").remove() val mojePole: Dynamic =jQuery("#mojePole").value() Try(mojePole.toString.trim.toInt) match { case Success(v) => validInput(v) case Failure(e) => wrongInput(e) } }
Jak sie popsuło to będzie na czerwono :
def wrongInput(e: Throwable)= { jQuery("body").prepend(s""" |<div class="alert alert-danger" role="alert" id="wynik">ERROR : ${e}</div> """.stripMargin) }
Do serwera
I oto dwa kawałki kodu napisane w jednym i tym samym silnie typowanym języku siedzące w dwóch różnych miejscach internetu (w teorii bo w praktyce w tym przykładzie oba są na moim kompie) komunikują się ze sobą!
//scala.js - browser Ajax.post(s"/validate?value=$v").onComplete{ case Success(xhr) => prependResponse(xhr.responseText) case Failure(e) => prependResponse(s"zjebało się : $e") } //scalatra-serwer post("/validate"){ Try(params("value")).map(v=> Ok(s"z serwera $v") ) getOrElse halt(BadRequest()) //400 }
Scalatra jak mówiłem prosta i przyjemna także taki prosty kodzik obsługuje post. No i wynik :
I działa
Błędy w runtime
W runtime działa już sobie javascript, który w teorii powinien być dobry jeśli tylko został wygenerowany z kompilującej się scali (plus pewnie bliżej nieokreślone N nieprzewidzianych bugów). Natomiast nic się nie dzieje gdy wyszukuję nieistniejący element na stronie i coś tam działam - w sensie nie dzieje się na chromie bo w testach ładnie się wywala. A testy pisze się np. w tym : https://github.com/lihaoyi/utest czyli narzędzia są.
Wygląda obiecująco
Brak komentarzy:
Prześlij komentarz