TypeScript w praktyce

Drodzy webdeveloperzy!

Proszę, przestańcie tworzyć oprogramowanie webowe w TypeScript!

Natywnym językiem programowania dla webdevelopmentu jest implementacja EcmaScriptu w postaci Javascriptu. Kod generowany przez kompilator TypeScriptu jest zły, nieoptymalny, przypomina sieczkę generowaną przez GWT (tam: Java->JavaScript). Sam proces kompilacji jest czymś zupełnie zbędnym (TS -> kompilacja -> JS -> interpretacja -> wykonanie). To generuje koszt, jest bezzasadnym utrudnieniem. Źródła są niemodyfikowalne bez znajomości TypeScriptu, który jest zbędny przy tworzeniu bibliotek i produktów javascriptowych o przeznaczeniu webowym.

Używajcie TypeScript tam, gdzie jest on zasadny - we własnych projektach, w swoim Visual Studio, w rozszerzeniach do swoich projektów lub ogólnie projektów "niewebowych". Tam, gdzie efekt prac jest kodem wykonywanym przez przeglądarki webowe, stosujcie JavaScript.

Programując rozwiązania webowe w TypeScript popełniacie ogromny błąd.

Dlaczego kupiłem srajFona

Ponieważ powiedziałem "a", mówię teraz "b". Nikogo nie interesuje przecież kiedy i na jaki telefon zmieniam, jednak jakiś czas temu opublikowałem skondensowany hejt na słuchawkę od Apple. Dziś jestem szczęśliwy, że ją posiadam. Co się zatem stało?

Ano Microsoft zrobił update z Win 8.1 do Win10. Mało tego - przez około rok wodził userów za nos aktualizacjami typu "jeden krok w przód, trzy do tyłu". To była istna droga przez mękę. Podstawy do tej pory nie działają. Godziny ciszy? Tak, ale codziennie paluszkami po volume down. U mnie trwały często przez cały kolejny dzień - sorry, że nie odbierałem telefonów. Nie bez znaczenia był fakt, że Blackberry wycofuje się z produkcji swoich pomysłów i idzie w kierunku androidów, a na dodatek niemal nie można ich słuchawek dostać. Android? Wybierzcie jakiś model słuchawki z pośród tryliona modeli. Tydzień to za mało na studiowanie ochów i achów, które skończyłyby się zapewne rootem i jakimś SlimROM-em ("bo zamula"), a Apple ograniczył mi tę liczbę raptem do trzech.

Uruchamiając "srajfon" po wyjęciu z pudełka (i oczywiście przechodząc znaną mi już procedurę aktywacji przez WIFI) odniosłem wrażenie - "it just works!". Tak, to po prostu działa. Szybki mail, notyfikacje, gps od strzała, godziny ciszy... Chwilę później Google Inbox oraz Keep "odkryte" dzięki apkom ułatwiły mi codzienne życie (zapomnijcie o apkach i usługach Google na słuchawkach Microsoftu).

Brakuje mi live tiles. To było znakomite UI. iOS jest dla mnie za bardzo "candy" czy tam "lollypop". Bateria na srajfonie? Słaba, choć bez danych komórkowych, lokalizacji i browsowania daje radę i ze dwa dni. Przyzwyczaiłem się do kabla, i to właściwie też dzięki Windows 10, na którym drenaż baterii przez Edge wespół z Outlook to podstawowy feature. Cała reszta srajfona to jednak duży komfort.

Zwracam honor marce i produktowi. Widocznie trzeba było mi dobić do dna, żeby docenić inne rozwiązania. Albo po prostu stałem się kobietą.

Colander i Django: ColanderSchemaField

Tym razem przedstawię rozwiązanie odwrotne do poprzedniego - zastosuję walidację schematem Colandera w formularzu Django.

Budując web API bardzo często istnieje potrzeba zwalidowania i odczytania nieco bardziej skomplikowanej struktury. W przypadku gdy po stronie serwera modelem jest obiekt klasy django.db.Model, to zapewne stosowany jest formularz klasy django.forms.ModelForm. To częsta i dosyć wygodna praktyka. Jednak jeśli wejście jest bardziej złożone, a dzieje się tak często podczas enkapsulacji interfejsu za pomocą RESTful API, to istnieje tendencja do tworzenia wielu formularzy, adapterów typu complex/composite forms, albo do zmiany modelu w celu odzwierciedlenia go w interfejsie 1:1.

Żadne z tych rozwiązań nie jest wystarczająco dobre, ponieważ wiele formularzy to bardziej złożona logika walidacji i brak spójności tejże (także w kontekście komunikatów o błędach), composite/complex forms narzucają tworzenie zlepek z wielu modeli (co też nie zawsze jest konieczne), a zmiany modelu mogą być zbędne i kosztowne.

Przypuśćmy, że bardziej złożona struktura to dodatkowy poziom zagnieżdżenia danych wejściowych. Naturalnym krokiem, szczególnie przy budowaniu JSON API, jest dodanie pola typu JSONField, ktore zdeserializuje część JSON-a do słownika. Byłoby to rozwiązanie dobre gdyby nie fakt, że walidacji podlega tylko syntaktyka przekazanej wartości. Nazwy kluczy, czyli ich prawidłowość oraz ich typy i wartości, pozostają nieweryfikowalne. I nie jest to winą JSON-a, tylko jego użycia (może wynikać wprost z zastosowania prostego pola typu JSONField).

Problem można rozwiązać używając pakietu Colander, za pomocą którego definiuje się dowolne schematy. Schematy Colander mogą być komponowane w złożone struktury, ale mogą być też używane pojedynczo. Colander nie narzuca tutaj żadnej konkretnej struktury oraz nie wprowadza zbędnych zależności, zatem użycie go jest bardzo wygodne.

Rozważmy bardzo prosty schemat opisujący współrzędne geograficzne:

class LocationSchema(colander.MappingSchema):
    lat = colander.SchemaNode(colander.Float())
    lng = colander.SchemaNode(colander.Float())

Powyższy schemat przyjmie słownik z kluczami lat oraz lng, których wartości muszą być liczbami zmiennoprzecinkowymi. Krótko mówiąc przyjmie on dane postaci: {"lat": 50.0, "lng": 50.0}.

Taki schemat można podpiąć bezpośrednio pod formularz Django, tak jak każde inne pole:

class LocationForm(forms.ModelForm):
    destination = ColanderSchemaField(LocationSchema, required=True)

Oczywiście potrzebny jest adapter - dedykowane pole dla formularzy Django, za pomocą którego proces walidacji zainstancjonuje schemat LocationSchema, zweryfikuje oraz oczyści dane wejściowe.

Ciało tego adaptera jest bardzo proste:

class ColanderSchemaField(forms.Field):
    def __init__(self, schema_factory, *args, **kwargs):
        self.schema_factory = schema_factory
        super(ColanderSchemaField, self).__init__(*args, **kwargs)

    def clean(self, value):
        if value is None:
            return

        schema = self.schema_factory()
        try:
            return schema.deserialize(value)
        except colander.Invalid as ex:
            raise forms.ValidationError(ex)

Od tej chwili formularz będzie oczekiwał klucza destination o strukturze określonej klasą LocationSchema.

Jednak zapis tej informacji oczywiście nie nastąpi, ponieważ ColanderSchemaField nie jest częścią modelu. Zapis trzeba przeprowadzić jawnie:

class LocationForm(forms.ModelForm):
    # ...

    def save(self, commit=True):
        instance = super(LocationForm, self).save(commit=False)
        destination = self.cleaned_data['destination']

        instance.destination_lat = destination['lat']
        instance.destination_lng = destination['lng']

        if commit:
            instance.save()
        return instance

Jeśli model miałby zdefiniowane pole destination klasy django.contrib.postgres.fields.JSONField lub analogicznej, to aktualizacja za pomocą ColanderSchemaField powinna przebiec automatycznie bez konieczności nadpisywania metody save(). Niestety jeszcze nie testowałem tego wariantu, do czego gorąco zachęcam.

Colander i Django: ModelChoice

Budując REST API mogą przydać się walidatory danych wejściowych bardziej elastyczne niż formularze Django. Do realizacji tego celu polecam i używam Colander.

Colander jest świetnym pakietem do (de)serializacji danych. Jest to odpowiednik formularzy Django dla Pyramida, lecz zdecydowanie bardziej elastyczny oraz bez związku HTML. Formularze Django są źle zaprojektowane - zbyt ściśle związane z HTML (widgety), których przecież nie używamy budując REST API, oraz są ograniczone do płaskich struktur. Jedynie jest są dyspozycji formsety, co pozwala zwalidować i zdeserializować listę obiektów jednego typu.

Colander wolny jest od tych wad, a ponieważ nie ma zależności od Pyramid (jest pakietem samodzielnym) to można go użyć z Django bez żadnego problemu. Jednak w niektórych sytuacjach brakuje mi pola typu ModelChoice, co utrudnia fabrykowanie instancji modeli. Z tego powodu zrobiłem poniższy snippet definiujący walidator ModelOneOf oraz klasę węzła ModelChoice. Jest na tyle przydatny, że postanowiłem się z nim podzielić jak najszybciej:

import colander
import types


class ModelOneOf(object):
    def __init__(self, qs):
        self._qs = qs

    def __call__(self, node, value):
        if not self._qs.filter(pk=value).exists():
            raise colander.Invalid(node, '%r is not valid choice' % value)


class ModelChoice(colander.SchemaType):
    def __init__(self, qs, *args, **kw):
        self._qs = qs
        self._model = qs.model
        self._validate = ModelOneOf(self._qs)

        super(ModelChoice, self).__init__(*args, **kw)

    def serialize(self, node, appstruct):
        if appstruct is colander.null:
            return colander.null
        if not isinstance(appstruct, self._model):
            raise colander.Invalid(
                    node, '%r is not a %s' % (appstruct, self._model))
        return appstruct.pk

    def deserialize(self, node, cstruct):
        if cstruct is colander.null:
            return colander.null
        if not isinstance(cstruct, (types.StringType, int)):
            raise colander.Invalid(
                    node, '%r is not a string nor int' % cstruct)

        self._validate(node, cstruct)
        return self._qs.get(pk=cstruct)

Przykładowe użycie (forms.py):

import colander
from .models import MyModel


class MySchema(colander.MappingSchema):
    model = colander.SchemaNode(ModelChoice(MyModel.objects.all()))
In [1]: s = MySchema()
In [2]: s.deserialize({'model': '1'})  # gdzie '1' to wartość PK
Out[2]: {'model': <MyModel pk=1>} 

Teraz zdeserializowane dane wejściowe da się wprost przekazać do fabryk lub konstruktora modeli, np:

instance = MyModel(**s.deserialize(...))

W ten sposób można całkowicie pominąć formularze django.

Lumia 830 z Windows 10 w praktyce

W lipcu 2016 posiadacze Lumii 830 w Oragne otrzymali darmową aktualizację do Windows 10. Ponieważ Windows Phone 8.1 miał kilka wad oraz Windows 10 był szeroko promowany jako coś szczególnie wyjątkowego, postanowiłem zrobić upgrade. Przede wszystkim liczyłem na lepszą przeglądarkę WWW oraz te nieszczęsne godziny ciszy. Teraz żałuję decyzji i chyba złożę reklamację (nie ma możliwości powrotu bez flashowania obrazu z WP8.1). Telefon został zniszczony przez jego destabilizację. Mam to czego sam chciałem?

Upgrade Windows 8.1 do Windows 10

Tutaj fail. Po aktualizacji nie działa backspace w Outlooku, czyli praktycznie nie da pisać się wiadomości. Sporo programów się zawiesza czy wyskakuje. Bateria jet drenowana. W aplikacjach bałagan. Jest tylko jedno rozwiązanie - hard reset.

Hard reset

Hard reset pomaga. Outlook pozwala odpowiedzieć na mail. Klawiatura jakoś działa. Jest jakby mniej zwiech. Bateria jest drenowana. Microsoft Edge kuleje ciut mniej niż ostatni Explorer. W aplikacjach robi się porządek. Bez hard reset nie ma mowy o normalnym działaniu. Straciłem nauczony słownik i parę dupereli (nie powinno się robić backupu żeby po resecie Windows 10 był czysty).

Outlook

Kuleje i zawodzi. Synchronizacja z GMail trwa wiecznie i drenuje baterię. Outlook oficjalnie nie radzi sobie z dużymi mailboxami. Poprzedni klient dawał radę by default, a tutaj trzeba uciekać się do sztuczek. Po dodaniu konta trzeba odciąć cały net, żeby Outlook przestał synchronizować. Następnie trzeba skrócić okres synchronizacji, najlepiej do 7 dni. Po tym można włączyć net z powrotem. Jakoś działa.

Kafelki słabo się odświeżają lub wcale. Start screen pokazuje ilość wiadomości "z dupy" (coś się zcacheowało i zostało).

Edge

Jak to Internet Explorer. Wykrzacza się, silnik kiepski, często muli. Brak adblocków itp. Nie ma o czym pisać

Godziny ciszy

Tylko z Cortaną.

Cortana

Niedostępna w PL.

Bateria

Trzyma ok 1 dnia. Przy używaniu (głównie zdjęcia i filmy) - parę godzin.

Uzupełnienie 24.08.2016

Wyłączając wszystkie usługi pracujące w tle poza aplikacją Telefon i Wiadomości, bateria może wytrzymać około 3 dni przy typowym użyciu. Baterię zżerają Microsoft Edge oraz Microsoft Outlook.

Ostatnio, mimo zablokowanej pracy w tle dla Edge, wyssał on w nocy całą baterię. Ot tak, mimo zablokowania pracy w tle i zablokowania telefonu. Budzik już oczywiście nie zadziałał - zaspałem.

Dodam, że zablokowanie pracy aplikacji w tle powoduje, że nie mamy już do czynienia ze smartfonem, tylko zwykłym telefonem z modułem WIFI, GPS oraz dobrym ekranem.

Zdjęcia i filmy

Nie ma już Lumia Camera. Aplkacja Microsoft Zdjęcia zacina się i jest słabo responsywna. Zrobienie zdjęcia trwa nawet kilkanaście sekund (rekcja na touch spustu migawki to nawet 5 sekund, doliczyć trzeba czas na focus).

Czasem przy odtwarzaniu filmów głośnik pierdzi, trzeba re-regulować głośność. Coś OS przesadza, boję się o rozwalenie głośniczka.

Mapy

Here Maps wywalone. Aplikacja Microsoft Mapy jest bardziej ograniczona, nie zawsze wyszukuje poprawnie ulice. Używa plików z mapami Here, to jedyna zaleta.

Krokomierz

Najpierw działał i liczył. Przez cały tydzień. Następnie aplikacja przywitała mnie informacją pożegnalną. Instaluję Microsoft Health - zamiennik. Poprzednie dane utracone. Nie chce mi się tego używać.

Pewnego dnia odzywa się stary krokomierz po przekroczeniu 10tys kroków. Klikając w powiadomienie otrzymuję starą aplikację i historię! Euforia. Przypadkowo zamknąłem aplikację - nie da się do niej wrócić. Znowu widzę "Przenieśliśmy się..".

Aby uruchomić stary krokomierz i dostać się do archiwalnych kroków trzeba przebiec 10tys kroków!

Kompas

Nie kalibruje się. Północ mam na zachodzie. Przyzwyczaiłem się.

Komunikacja

Synchronizacja zdjęć do Google Photos nie działa. Codziennie otrzymuję powiadomienie,że nie da się wgrać zdjęć. Wgrywają się po trzy dziennie. Odinstalowuję apkę, bo wkurza.

NFC czy przesył po Bluetooth z iPhone - nie działa.

Podłączenie telefonu do OSX nie pozwala odczytać zdjęć. Nie ma nic. Tylko Linux mi czyta. Windowsa nie mam.

SMS i połączenia

Nie mam pewności czy otrzymuję każdy SMS i każdy telefon. Podczas kilku rozmów miałem problemy (przerwy, przycinania), ale to może z winy sieci. Nie ufam jednak słuchawce.

Aplikacje

  • Metro Mail, sensowy zamiennik za Outlooka - nie działa z Win10.
  • Accuweather, sensowna apka do pogody - nie działa z Win10.

Ocena ogólna

Sprzedam Lumię 830, dobrze utrzymana, w atrakcyjnej cenie. Epic fail. A było tak przyzwoicie...

Szczerze radzę unikać Windowsa 10, szczególnie na urządzeniach mobilnych. Nie dajcie się przekonywać fanbojom - to sprzedawcy, fanatycy lub kłamcy. Ten system to drwiny z użytkownika i zarazem klienta. ODRADZAM