Mason – mniej pracy więcej programowania

Wielu z was na pewno natrafia na moment kiedy znajdujecie pomysł na nowy projekt, jednak aby go rozpocząć musicie przejść przez listę rzeczy do zrobienia:

  • dodać istotne biblioteki których używacie w każdym projekcie,

  • ustawić wszystkie parametry w MaterialApp,

  • skopiować przydatne widgety,

  • przygotować globalne ustawienia dla Theme.

 

 

Lista dla każdego może być inna, jednak raczej wszyscy możemy się zgodzić, że jest to sporo roboty. W naszych projektach potrafiło to zabrać 1-2 godzin, nawet po przygotowaniu sobie wszystkiego w osobnym folderze.

Pewnego razu, przy którymś z kolei tworzeniu projektu w czasie jednego tygodnia, wpadłem na pomysł, aby uprościć sobie życie i sprawdzić sposoby automatycznej generacji projektów. Pierwszą biblioteką jaką sprawdziłem był `Stagehand`. Niestety, brakowało mi w niej sposobu na dodawanie własnych template. Jeden z issue przekierował mnie jednak do innej paczki -`Masona`. Pozwala ona na definicje własnych preset’ów dla projektów. Wystarczy dodać wszystko do jednego folderu, zdefiniować nazwę i tyle.

Uwaga

Na urządzeniach z systemem windows, biblioteki Dartowe, które dodają własne komendy wykonują się podwójnie (Stan na październik 2021). Jest to spowodowane kolejnością wczytywania zmiennej Path w systemie. Więcej informacji m.in. w tym issue – [BUG] fvm runs commands twice on [Windows] · Issue #227 · leoafarias/fvm

Używanie tej biblioteki jest wyjątkowo proste. Najtrudniejsza część to zdefiniowanie własnych template. Trzeba przemyśleć układ plików oraz zmienne, które są nam potrzebne.

Samo używanie biblioteki jest bardzo proste. Wpisujemy komendę `mason make <nazwa>` i patrzymy jak pliki się same przenoszą.

Nasuwa się pytanie – jak to działa? Otóż, nie jest to żaden rocket science. Biblioteka używa systemu szablonów `Mustache`, który pozwala nam inject’ować tekst bezpośrednio do plików. `Mason` sam nasze pliki kopiuje, przenosi, skanuje i edytuje.

Sama biblioteka zawiera kilka funkcji oraz parę quality of life decyzji, które mają nam ułatwiać pracę.
Jak wcześniej wspomniałem, używany jest tutaj system szablonów `Mustache`. Pozwala nam on wkładać do plików “zmienne”, które potem przy generacji template są podmieniane na te, które mu podamy.

//Wszelkie nazwy naszej zmiennej są podmieniane

//Działa to także w nazwach plików!
//Działa to także w nazwach plików!

Kod posiada też możliwość generowania warunkowego. Zależnie od podanych zmiennych można decydować czy dana część kodu ma się wygenerować czy nie. Jest to przydatne w momencie, w którym chcemy tworzyć bardziej skomplikowane template, które mają obsługiwać wiele możliwych konfiguracji.

Jedną z istotniejszych rzeczy, która do mnie trafiła to fakt, że Mason nie wymaga nic od użytkownika przy generowaniu projektu. Z początku myślałem że trzeba będzie za każdym razem przenosić plik z ustawieniami template. Okazało się, że wystarczy po prostu wejść w wybraną lokalizację i wpisać jedną komendę. Zawdzięczamy to globalnym ustawieniom dla całego środowiska. Zastąpiły one wcześniejszą implementacje, w której był definiowany plik `yaml` zawierający informacje o wszystkich dostępnych `brick’ach`. Można było go zdefiniować na dole drzewa folderów. Był on jednak odrobinę nieporęczny.

Wisienką na torcie jest dobre wsparcie dla `git’a`. Pozwala nam ono zdefiniować nasze template i przechowywać np. na GitHubie. Wystarczy użyć komendy `mason get`, aby pobrać wszystkie zmiany, które pojawiły się na serwerze.

Jednak pomimo tych wszystkich zalet, `Mason` posiada jedną, bardzo istotną, wadę. Nie można używać go do dodawania częściowych plików. Albo to tylko ja nie wpadłem jak to robić. Jest to dość istotna funkcja np. w momencie, gdybyśmy chcieli dodać cały feature do naszego projektu. Przykładowo notyfikacje – nie moglibyśmy edytować częściowo pliku `pubspec.yaml` i musielibyśmy pamiętać, aby za każdym razem dokleić wymagane biblioteki. Albo musielibyśmy zdefiniować ten feature, jako część głównego template. Nie jest to kompletny deal breaker dla `Masona`. W mojej opinii jest jednak największym problemem aktualnej wersji. (Nie wygląda też na to aby miało to zostać zmienione.)

Uważam dodatkowo, że z dostępem do narzędzia generującego za nas większość pracy, dostajemy dostęp do większej wolności przy tworzeniu projektów. Słyszałem na przykład już propozycje, aby użyć dość interesującej biblioteki `storyboard`, która pozwalałaby nam testować widgety w lepszy sposób. Zawdzięczamy to temu, że nie wpływa na nas większy nakład czasu przy tworzeniu tych feature. Tworzenie projektu z wbudowanymi, skomplikowanymi narzędziami diagnostycznymi oraz testującymi trwa w teorii tak samo długo, jak podstawowego “hello word”.

Przykłady użycia:

Wszystkie poniższe przykłady są zawarte w repo na githubie: GitHub – Fasuh/mason_testing_ground.
Brick’i są zdefiniowane w folderze `bricks`, po wywołaniu komend:

  • `Mason get`

  • `Mason list`

Powinny wyświetlić się nam 3 bricki:

  • authorization

  • feature

  • flutter_clean_architecture

Test case 1 – Inicjalizacja całego projektu:

Najbardziej podstawowy case – definiujemy wszystko, co chcemy mieć utworzone w projekcie. W naszym repo ten przykład jest nazwany `flutter_clean_architecture`.
Po wywołaniu komendy możemy zobaczyć jedną, z początku może dziwną, rzecz. Mason pyta nas o zmienną o nazwie `project_name`. Jest to spowodowane tym, że biblioteka nie posiada informacji o projekcie. W dwóch miejscach używamy tej nazwy:

  1. Linki importów są zdefiniowane po `package`, więc każdy import wewnętrzny w projekcie posiada nazwę projektu. Można pominąć ten krok używając importowania relatywnego

  2. Przy generowaniu `pubspec.yaml` musimy w nim zdefiniować nazwę projektu. Raczej ciężkie jest to do pominięcia w sensowny sposób

Poza tym, biblioteka działa dość sprawnie. Generuje wszystko, przy wywołaniu komendy `flutter pub get`. Nie powinniśmy mieć żadnych błędów. Przeniosły się nasze bloc’e, widgety, podstawowe biblioteki oraz flavory.

Test case 2 – Inicjalizacja pojedynczego modułu

Jedną z najbardziej pożądanych feature w naszej firmie jest możliwość generowania całego modułu na raz. W naszej architekturze mamy standardowy podział na `data`, `domain`, oraz `presentation`. Tworzenie wszystkich plików zabiera trochę czasu. Nie jest to jakaś bardzo skomplikowana praca, ale nie mamy za dużo chętnych do jej manualnego robienia. Z początku używaliśmy plugina do AndroidStudio, który robił to za nas. Szybko jednak natrafiliśmy na problem przy zmianie wersji AS, nasz plugin przestawał działać. Postanowiliśmy użyć w jego miejsce nowo nabytego Mason’a. W projekcie na githubie jest to brick o nazwie `feature`. Po jego wywołaniu dostajemy prośbę o wypełnienie zmiennej odpowiadającej za nazwę modułu. W tym przypadku zastosowaliśmy relatywne importy. Jak widać działają one dobrze. Jest to jednak kwestia osobistej preferencji (ja osobiście uważam że nie wyglądają elegancko).

Test case 3 – Inicjalizacja specyficznego modułu – Autoryzacja

Prawdopodobnie to, co interesuje większość z nas to wyobrażenie tworzenia aplikacji “z klocków” wywołując tylko komendy Mason’a w celu dodania modułów i dorabiania jedynie ekranów. Nie jest to jednak tak oczywisty case. Zapraszam do spojrzenia na Brick o nazwie `authorization`. Przy jego generowaniu używamy ponownie nazwy projektu. Poza tym nie wymagamy nic.

Od razu możemy zauważyć, że widzimy kilka błędów w projekcie. W tym miejscu odrobinę uwypuklają się problemy z biblioteką. Nie jesteśmy w stanie edytować plików tak aby, nie stracić ich aktualnego stanu, błędy, które widzimy wchodzą z naszego sposobu error handlingu. Dodajemy błędy w osobnym pliku. Nie jest to dużo nadmiarowej roboty. Problem ten może jednak bardzo szybko eskalować.

Mimo tego, jest to szybszy sposób na implementację popularnych modułów np. Autoryzacji lub Push Notifications.

Możliwe pytania

  1. Czy jest możliwe budowanie aplikacji, “jak z klocków”? – Uważam że jest to wykonalne na trzy sposoby:

    1. Zdefiniowanie brick’ów w taki sposób, że nie wymagają one zewnętrznych zależności (np błędów). Jest to dość limitujące rozwiązanie. Wymaga konkretnego podejścia i może ostatecznie bardzo nas limitować.

    2. Użycie dodatkowego narzędzia. W teorii możliwe jest, aby użyć dodatkowej biblioteki (np `Grinder`), która wywoływała by komendy masona, edytowała pliki,  generowała model, oraz instalowała biblioteki w `pubspec.yaml`. Ciężko mi określić poziom skomplikowania takiej implementacji. Zdaje się ona jednak możliwa.

    3. Definiowanie modułów w głównym template całego projektu. Tworzylibyśmy wszystko za pomocą bloków warunkowych. Pozwoliło by nam to dodać wszystko na raz. Minusem tego rozwiązania jest limitacja do dodawania modułów tylko przy tworzeniu projektu.

      Poza tymi sposobami możemy oczywiście też po prostu wykorzystać częściową generacje, i manualnie dodawać wszystkie zależności. Jest to w mojej opinii najlepsze z obu światów.

  2. Ile roboty można pominąć w ten sposób? – Na naszym przykładzie mogę powiedzieć, że od połowy do całego dnia, w zależności od projektu i architektury, może skrócić się nawet do 30 minut.

  3. W jaki sposób można by użyć Mason’a z innymi narzędziami? – Powyżej wspomniałem, że można by utworzyć sieć narzędzi z Grinder’a oraz Mason’a. Jest to tylko jeden przykład jak można by użyć tej biblioteki do przyspieszenia naszej pracy. Wszystko dzięki prostej budowie tej biblioteki.

Ostatnią sekcję chciałem przeznaczyć na lekki shout out dla autora biblioteki. Jej żywotność wydaje się bardzo dobra, dostajemy częste update. Bardzo spodobała mi się sytuacja, kiedy zadałem pytanie w `issue` odnośnie niedziałającej integracji z windowsem. Nie dość, że dostałem odpowiedź w przeciągu kilku godzin, to jeszcze tego samego dnia integracja została przerobiona na nową, działającą.

Wydaje mi się, że Mason może w przyszłości zostać jednym z podstawowych narzędzi programistów Fluttera, zaraz obok FVM czy build_runner’a. Polecam więc dać jej szansę i zdefiniować własne template!

This site is registered on wpml.org as a development site.