piątek, 31 marca 2017

#2 Recepta na - pretty if

Druga "recepta na" będzie związana z warunkami if i skracaniem ich zapisu a zarazem poprawy ich czytelności.
Na początek przykład nad którym popracujemy:


public class MyBusinessClass
{
    AuthorizationService authorizationService = new AuthorizationService();

    public void AddOrderToInvoice(Order order, Invoice invoice, UserInfo user)
    {
        if (authorizationService.HasAuthorization(order, user, AuthorizationLevel.Read) && authorizationService.HasAuthorization(invoice, user, AuthorizationLevel.Write))
        {
            //do something
        }
    }
}


Klasa biznesowa, której metoda AddOrderToInvoice musi wykonać sprawdzenie czy użytkownik ma odpowiednie uprawnienia do wykonania tej operacji. Weryfikujemy uprawnienia do odczytu na zamówieniu i uprawnienia do zapisu na fakturze. Od razu zaznaczam, że jest to pseudo kod - tak aby pokazać istotę długich, skomplikowanych warunków if. (W sumie nie taki on pseudo bo spotykam go dość często ...)
Pierwsze co możemy zrobić bez większego wysiłku to oczywiście przerzucenie drugiego warunku do osobnej linii:


public void AddOrderToInvoice(Order order, Invoice invoice, UserInfo user)
{
    if (authorizationService.HasAuthorization(order, user, AuthorizationLevel.Read) 
        && authorizationService.HasAuthorization(invoice, user, AuthorizationLevel.Write))
    {
        //do something
    }
}

Jest o tyle lepiej, że przynajmniej mieści się już na ekranie ;) Ale to jeszcze średnio mnie zadowala. W takich sytuacjach często brakuje biznesowego opisu co dany warunek weryfikuje. A jakby tak wyciągnąć tą weryfikację przed klauzulę if przypisując wyniki do zmiennych, których nazwy dobrze je opisują?


public void AddOrderToInvoice(Order order, Invoice invoice, UserInfo user)
{
    bool hasReadPermissionOnOrder = authorizationService.HasAuthorization(order, user, AuthorizationLevel.Read);
    bool hasWritePermissionOnInvoice = authorizationService.HasAuthorization(invoice, user, AuthorizationLevel.Write);

    if (hasReadPermissionOnOrder && hasWritePermissionOnInvoice)
    {
        //do something
    }
}


Teraz śledząc taki warunek nie musimy czytać całego wywołania metod tylko wprost widzimy co jest sprawdzane. Czy możemy to tak zostawić? ... No pewnie, że nie! Przerzucając od tak wywołania metod do zmiennych pozbyliśmy się jednej bardzo istotnej właściwości operatora &&. Sprawdzenie drugiego warunku (uprawnień do zapisu na fakturze) odbywało się dopiero kiedy pierwszy warunek (uprawnienia do odczytu na zamówieniu) zwracał wartość true. Aktualnie wykonujemy dwa sprawdzenia niezależnie. W przypadku prostych metod, nie sięgających do bazy, zużywających zasobów itd. może nie byłoby to problemem. Ja jednak optuje za jeszcze innym rozwiązaniem. Nazywam to "lazy check" ;) W tym celu wykorzystuje delegat Func<T, TResult>


public void AddOrderToInvoice(Order order, Invoice invoice, UserInfo user)
{
    Func<bool> hasReadPermissionOnOrder = () => authorizationService.HasAuthorization(order, user, AuthorizationLevel.Read);
    Func<bool> hasWritePermissionOnInvoice = () => authorizationService.HasAuthorization(invoice, user, AuthorizationLevel.Write);

    if (hasReadPermissionOnOrder() && hasWritePermissionOnInvoice())
    {
        //do something
    }
}

Wprowadziłem delegat Func<bool> z deklaracją typu "inline". Jego wykonanie nastąpi dopiero w klauzuli if przy czym jeśli hasReadPermissionOnOrder() zwróci wartość false nie nastąpi wykonanie hasWritePermissionOnInvoice() - a o to ostatecznie nam chodziło.

Jeśli macie jakieś swoje "recepty na" klauzule if to podzielcie się nimi w komentarzach. Chętnie dowiem się co sądzicie o tym konkretnym rozwiązaniu.

niedziela, 26 marca 2017

#1 Recepta na - Multiple file extension

Niniejszy post rozpoczyna serię pt. "Recepta na". Każdy post będzie dotyczył, jakiegoś małego problemu programistycznego i przykładowego rozwiązania. Takie małe, pomocne snippety.

Problem: Pobrać nazwę pliku bez jego rozszerzenia

.Net framework udostępnia metodę, która pozwala to jak najbardziej wykonać:


Path.GetExtension("sample.txt"); //-> ".txt"
Path.GetFileNameWithoutExtension("sample.txt"); //-> "sample"

Niestety nie wspiera ona wielu rozszerzeń w nazwie, np. sample.tar.gz

Path.GetExtension("sample.tar.gz"); //-> ".gz"
Path.GetFileNameWithoutExtension("sample.tar.gz"); //-> "sample.tar"

Jak spojrzymy w jej implementację zobaczymy, że poszukuję od końca przekazanej nazwy pierwszego wystąpienia kropki '.' i na tej podstawie zwraca substring od tego miejsca do końca. Dodatkowo wykonuje kilku walidacji. Link do implementacji

#1 Recepta

Najprostsze co mi przychodzi do głowy do napisać metodę, która będzie poszukiwała '.' zaczynając od początku nazwy i wszystko co następuje po nie potraktować jako rozszerzenie 😀


static string GetExtension(string filename)
{
    return filename.Substring(filename.IndexOf('.'), filename.Length - filename.IndexOf('.'));
}

Tak mogłaby wyglądać najprostsza implementacja, bez żadnych dodatkowych walidacji itp.
Ale przeczy to zasadzie a może intuicji że rozszerzenia poszukujemy od końca. Zbyt proste by mogło się sprawdzić
.

#2 Recepta

Wykorzystać istniejąca metodę Path.GetExtension ale w taki sposób by wspierała wiele rozszerzeń. Zostaniemy przy "systemowej" implementacji co będzie dodatkowym atutem. Możemy to osiągnąć poprzez rekurencyjne wołanie w/w metody lub wykonanie tego w pętli. Rekurencja była zbyt prosta 😋 więc dziś przedstawię pętlę i to nie byle jaką - mianowicie do...while.

Rezultat końcowy:

static string GetFileNameWithoutExtension(string filename)
{
    int maxCounter = 20, loopIndex = 0;
    string nameOnly, currentName = filename;
    Stack<string> extensions = new Stack<string>();
    
    do
    {
        nameOnly = currentName;
        currentName = Path.GetFileNameWithoutExtension(nameOnly);
        extensions.Push(Path.GetExtension(nameOnly));
        loopIndex++;
    }
    while (nameOnly != currentName && loopIndex <= maxCounter);
    
    return nameOnly;
}

Wejściowa nazwa pliku jest "czyszczona"  z rozszerzenia do momentu aż wywołanie metody Path.GetExtension zwraca tą samą nazwę. Wtedy wiemy, że metoda już nic więcej nie ma do roboty.
Zawarłem tutaj jeszcze kilka rzeczy:

  • maxCounter - dodatkowe zabezpieczenie pętli aby uniknąć tzw. nieskończonej pętli
  • Stack<string> extensions - stos na który odkładane są rozszerzenia.

wtorek, 21 marca 2017

Dogevents - VS2017, Nunit, Separacja modelu domenowego

Kolejny raport z pola walki 😈
Dziś kilka słów o nowym Visual Studio 2017, jak porzuciłem (chwilowo) NUnit oraz modelu domenowym, jego strukturze i separacji.

VS2017


Wczoraj instalowałem pierwszy update, który sporo poprawił w zakresie dodawania referencji do projektów. Do tej pory po dodaniu referencji do projektu musiałem robić restart solucji żeby zmiany zostały zauważone. Często VS gubił nawet referencje do System namespace - sick! Ale to zapewne takie małe uciążliwości wieku niemowlęcego 😋

NUnit


Dotychczas korzystałem z NUnit i w tym projekcie także chciałem go wykorzystać. Spędziłem 2h na próbie konfiguracji z .net core po czym ten błąd pogrzebał moje nadzieję. Does not work after migrating to csproj. Zaciągnąłem do pracy xUnit - przy okazji coś nowego się nauczę.

Separacja modelu domenowego


Mój projekt w głównej mierze polega na danych dostarczonych z Facebook API. Tak pobrane dane są  przetwarzane i zapisywane w bazie danych.



Trzy elementy - Facebook API, która zwraca swój model danych, warstwa domeny biznesowej, która je przetwarza oraz warstwa bazy danych, która je utrwala. W swoim projekcie zacząłem od zaprojektowania modelu domenowego (domain model), który jednocześnie stał się modelem bazy danych (persistence model). Co z tego wynikło i jaki są wady i zalety takiego podejścia?

Pierwsze wrażenie


Prostota. Jedna klasa, którą operuje w na poziomie logiki biznesowej i dostępu do danych. 
Jednak po kilku testach okazało się, że ta wspólna zależność ma swoje wady. Jakakolwiek zmiana tego modelu "psuje" dostęp do danych - głównie poprzez błędy deserializacji związane z brakiem jakiejś właściwości, która została usunięta czy jej nazwa zmieniona. Musiałem czyścić kolekcje w mongodb i zaczynać od nowa wrzucać dane. W początkowym etapie nie jest to dla mnie problemem ale z czasem może być upierdliwe kiedy dokonujemy sporych zmian. Sam mongodb driver posiada pewne mechanizmy, konwencje, które pozwalają sobie z tym radzić.

Dzielić czy nie dzielić



Po tym etapie rozmyślałem nad wprowadzeniem separacji pomiędzy modelem domenowym i danych. Do mapowania użyć Automappera, czy własnych metod rozszerzających (extension methods). Pytanie czy to miałoby sens, jakie przyniosło by korzyści a jakie wady?

  • + Szybki start - jedna klasa, żadnych mapowań
  • - Model bazy danych silnie zależny od domenowego.
Ta zależność może najbardziej boleć. Model bazy danych może przechowywać wiele więcej informacji o innej strukturze niż domenowy i przypadkiem możemy udostępnić na poziomie logiki biznesowej dostęp do powiedzmy niepożądanych danych.
Polecam przeczytać post Having the domain model separated from the persistence model aby zgłębić wady i zalety różnych rozwiązań.

Ja zostałem przy "połączonym" modelu. Na chwilę obecną nie potrzebuję takiej separacji. Dodatkowe zagrożenie jest takie że moja klasa "Event" również odzwierciedla model zewnętrznego Facebook API. A jest to coś co nie jest zależne ode mnie i jeśli na tej warstwie zajdzie duża zmiana wpłynie ona bezpośrednio na mój model domenowy - a tego zdecydowanie należy uniknąć.

czwartek, 16 marca 2017

JSON online tools

Dziś podrzucam wam kilka ciekawych narzędzi "online" do przetwarzania obiektów JSON.

JSON Editor Online



Mój TOP 1 jeśli chodzi o edycję obiektów JSON. Edytor posiada wbudowany jsonlint dzięki czemu od razu mamy walidację, podgląd błędów itd.

www.jsoneditoronline.org


Prawa sekcja edytora umożliwia modyfikację obiektu. Możliwa jest m.in zmiana struktury poprzez drag&drop, dodawanie elementów, kopiowanie, sortowanie czy zmianę typu.


www.jsoneditoronline.org - menu konteksowe

JSON query tool



Narzędzie, które umożliwia wykonywanie zapytań, mapowanie do innej postaci. Taki poligon doświadczalny. Możemy pisać w czystym JS jaki i użyc lodash, underscore czy różnych odmian XPath dla JSON.

www.jsonquerytool.com

JSON2Table



Na sam koniec coś do wizualizacji danych w postaci tabelarycznej. Efekt końcowy wizualnie może nie powala ale do pewnych zastosowań wystarczy.

json2table.com


To są moje propozycję. Jeśli macie jakieś inne warte uwagi to zapraszam do komentowania.


poniedziałek, 13 marca 2017

TDD - To Do Development

Dziś o TDD ale z innej perpsektywy. I nie będzie to wpis o kolejnej aplikacji ToDo :)
Start w #dajsiepoznac wymaga "odrobiny" samodyscypliny i co ważniejsze odpowiedniego zaplanowania prac nad rozwijaniem projektu.

Godzina dla kodowania

Czyli mój pomysł na przetrwanie w DSP2017. Jedyna jednostka czasu dostępna w tygodniu poza weekendem. Czas po pracy poświęcam rodzinie - głównie dzieciom (to są te jedne z najbardziej zajmujących dzieci - czyli 8mc'y i 2 lata). Ten post zaczynam pisać o godz. 21:45. Max 23:00 muszę wylądować w wyrku aby ładnie o 06:00 wstać do pracy. Więc tego czasu jest naprawdę niewiele! A work-life balance musi być zachowany.

TDD - To Do Development


Siadając do mojej "godziny" mam już w głowie gotowy plan co dzisiaj wykonam. Są to nie duże rzeczy takie, które jestem w stanie wykonać w tym czasie. A to dorzucę jakiś nowy serwis, coś skonfiguruje, przeczytanie artykułu czy tutoriala. W tym czasie nie ma slack'a, wykopu i innych odskoczni - ma być maksymalne skupienie. Lista TDD powstaje automatycznie. Jest wynikiem tego co udało się zrealizować jak również idei, pomysłów o których myślę w drodze do/z pracy.

22:10 - koniec redagowania postu

Czas na herbatkę, kanapeczki i jakiś serialik ...


sobota, 11 marca 2017

Dogevents - podsumowanie pierwszych prac

Dziś małe podsumowanie tego co dotychczas wykonałem w projekcie. Myślę, że raz na dwa tygodnie jeden post będzie właśnie taki.
git log - pierwsze dwa tygodnie
Założyłem, że w pierwszym okresie rozpoczęcia prac głównie skupie się na planowaniu kolejnych milestone, rozpoznawaniu narzędzi, bibliotek, które zamierzam wykorzystać. Dlatego też nie wyprodukowałem jako tako dużo kodu. Póki co większa jego część siedzi mi w głowie ;)

Tak się wciągnąłem w ten projekt i całe #dajsiepoznac, że każdą wolną chwilę poświęcam na przemyślenie czegoś, przeczytanie artykułu na medium.com czy też przeglądanie projektów na github. Zauważyłem, że minusem tego wszystkiego jest slack i kanały związane z konkursem - zabierają za dużo czasu. Więc postanowiłem ograniczyć przebywanie tam i skupić się na pracy nad projektem :) Apropo aby nasz móżdżek był w stanie sam wrzucać pomysły, idee musi być czysty. Tutaj polecam obejrzeć prezentację Macieja Aniserowicz

Konfiguracja projektu,  zmiany zmiany i migracja


Aktualnie mam przygotowaną strukturę projektu z podziałem na warstwy. Pojawiło się trochę kodu związanego z infrastrukturą. Zacząłem od backend'u - to jest moja mocniejsza strona.  W międzyczasie podczas przygotowywania skryptu, który wykonywał by deploy na lokalny IIS natknąłem się na dwa błędy komendy `dotnet publish`. Pierwszy związany był z pomijaniem widoków z areas o których pisałem w poprzednim poście, drugi dotyczył pomijania plików gdy zostały użyte ścieżki relatywne w parametrach. Praca nad rozwiązaniem tego problemu pokryła się z publikacją Visual Studio 2017 dodatkowo zniechęciłem się do Visual Studio Code (co poszło nie tak postaram się opisać w innym poście) więc postanowiłem wykonać migrację całego projektu do VS'a.
Przeszło bezproblemowo! Komenda `dotnet migrate` wykonuję za nas całą robotę.

Infrastruktura MongoDB

 Jest to mój pierwszy projektu z użyciem tej bazy i na aktualnym etapie skupiam się głównie jak przygotować odpowiednią infrastrukturę pod nią. Wzorzec Repository to do każdego źródła można zastosować ale poszukałem na github innych projektów żeby zobaczyć jak został tam rozwiązany ten problem. Pierwszy to projekt Warden zeszłorocznego zwycięzcy konkursu #dajsiepoznac a drugi to netdeveloperspoland.pl. Jak widać obydwa z naszego podwórka. Osobiście bardziej przypadło mi do gustu rozwiązanie użyte w Warden. Jest to coś w rodzaju fluent api, które zostało zrealizowane poprzez extension methods na interfejsach driver'a mongo.

Za co już lubię asp.net core


Co mi osobiście najbardziej przypadło do gustu w dotychczasowej pracy z asp.net core to sposób konfiguracji middleware i dostępny od ręki mechanizm DI. Próg wejścia w użycie DI jest tak niski, że nie powinno już być żadnych wymówek do jego użycia. Co więcej cały framework załatwia nam dostęp do skonfigurowanych serwisów w każdej z warst - od middleware, poprzez model, kontroler, widok, filtry. Krótka notka o tym na blogu Scott'a Alen'a

Plan na kolejny tydzień


1. Dodać nowy powód do aplikacji "Dlaczego nie weźmiesz udziału" ;)
2. Zamodelować obiekty domenowe. To będzie wymagało wgryzienia się w Facebook API
3. Utworzyć infrastrukturę pod integrację z Facebook


wtorek, 7 marca 2017

asp.net core mvc - areas

Blog prowadzony po polsku a tytuł po angielsku. Czasem ciężko jest dobrać polski odpowiednik :)
Dziś będzie o strefach (area = strefa), które wykorzystam także w projekcie dogevents.

Ale o co chodzi


Dla zobrazowania powiedzmy, że projektujemy rozbudowaną aplikację składa się z kilku sekcji (nie do końca ze sobą powiązanych). Pierwsze co mi przyszło do głowy do taki system ERP. Możemy w nim wyróżnić m.in takie moduły:
  • Kontrahenci 
  • Sprzedaż
  • Zamówienia
  • Księgowość
  • Administracja
Gdyby przenieść to na prostą strukturę kontrolerów w mvc mogłoby to wyglądać następująco:

Z czasem może okazać się, że jeden kontroler zacznie obsługiwać wiele funkcjonalności (w końcu planujemy system ERP ;) ) i najzwyczajniej puchnąć. Zaczną powstawać nowe, struktura mocno się rozbuduje i poruszanie się po projekcie stanie się uciążliwe.

Areas dla ratunku


Co nam da wprowadzenie stref? Po pierwsze separacje nie tylko na poziomie kontrolerów ale również modeli i widoków! Każda strefa może posiadać także odrębny layout. Tak przedstawia się struktura projektu po wprowadzeniu stref:

Projekt został rozdzielony na obszary nad którymi można pracować oddzielnie.

Dwa kroki do użycia

Aby skorzystać z podstawowej funkcjonalności stref należy:

#1 zdefiniować nową mapę routingu:


app.UseMvc(routes =>
{
    routes.MapRoute(
        name:"areaRoute",
        template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Pierwsza deklaracja dotyczy stref. Parametr adresu url {area:exists} to nazwa strefy, następnie kontroler, akcja i opcjonalnie parametr.

#2 Na każdym kontrolerze w strefie nadać atrybut Area z nazwą strefy:


[Area("Orders")]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Specjalnie podkreśliłem słowo "każdy" gdyż jego brak będzie prowadził do problemów dopasowania wielu kontrolerów.

#3 Miały być dwa kroki

Tak skonfigurowany projekt zadziała ale pod warunkiem, że nasze strefy umieściliśmy w katalogu o nazwie "Areas". To właśnie tam domyślnie nasz view engine (no nie mogę  z tym ang.) będzie poszukiwał naszych stref. Jest możliwość zmiany tego stanu poprzez zmianę jego konfiguracji:


services.Configure<RazorViewEngineOptions>(options =>
{
    options.AreaViewLocationFormats.Clear();
    options.AreaViewLocationFormats.Add("/Modules/{2}/Views/{1}/{0}.cshtml");
    options.AreaViewLocationFormats.Add("/Modules/{2}/Views/Shared/{0}.cshtml");
    options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
});

Dla powyższego przykładu nasze strefy będą znajdować się w katalogu "Modules". Parametry oznaczają:
{2} - nazwę strefy
{1} - nazwę kontrolera
{0} - nazwę akcji

To tyle jeśli chodzi o ideę stref. W moim projekcie dogevents wydzieliłem jedną strefę -administracyjną, która będzie panelem administracyjnym dla mnie ;)

niedziela, 5 marca 2017

Refaktoryzacja - Bool To Enum

Dziś mały przykład refaktoryzacji polegającej na zamianie flag bool na enum'a. Zdecydowałem się na ten krok gdyż nie lubię złożonych warunków if i tam gdzie można staram się je upraszczać. Poniżej klasa zawierająca dwa pola typu bool, którę odpowiadają za to jaka wersja pliku ma zostać wyeksportowana.


public class FileExport
    {
        public bool Origin { get; set; }
        public bool Modified { get; set; }

        public void Export()
        {
            if (Origin && !Modified)
            {
                WriteLine("Exporting origin file version");
            }
            else if (!Origin && Modified)
            {
                WriteLine("Exporting modified file version");
            }
            else if (Origin && Modified)
            {
                WriteLine("Exporting origin&modified file version");
            }
            else
            {
                WriteLine("Nothing to export ....");
            }
        }
    }


Co mi tutaj nie odpowiadało to klauzule if. Może zbyt mocno uprościłem ten przykład ale w kodzie produkcyjnym te klauzule były dość długie i mało czytelne. Zastanawiałem się także co będzie jak nastąpi wprowadzenie kolejnej flagi do warunku?
I tutaj przyszedł pomysł wprowadzenia typu enum, który uwzględniał by już kombinację tych flag i odpowiednio by je opisywał.

Krok #1 - Utworzenie typu enum


[Flags]
public enum VersionToExport
{
    None = 0,
    Origin = 1 << 1,
    Modified = 1 << 2,
    Both = Origin | Modified
}
 Atrybut Flags umożliwia traktowanie takiego typu enum jako pola bitowego, które może być zbiorem kombinacji pól. Kolejne pola otrzymują wartość kolejnych potęg dwójki 0, 2, 4, 8, 16 ...

Krok #2 - Przypisanie wartości


Kolejnym etapem jest "zapalenie" odpowiednich bitów. Ja użyłem takiego rozwiązania:

public VersionToExport ExportVersion {get; set; }

public SetExportVersion()
{
    ExportVersion = Origin ? VersionToExport.Origin : 0;
    ExportVersion |= Modified ? VersionToExport.Modified : 0;
}

To tyle. Opcji None oraz Both nie musimy tutaj przypisywać. None jest domyślną wartością a Both jest z kolei kombinacją dwóch flag Origin i Modified. Zwracam tutaj uwagę na operator |=, który wykonuje operację bitową OR. x |=y jest odpowiednikiem x = x | y

Krok #3 - Użycie


I na koniec przykład jak uległa zmianie klauzula if. Można przy okazji zamienić ją na switch'a (Jakoś tak mam, że przy typach enum częściej używam instrukcji switch).


public void Export()
{
    switch (ExportVersion)
    {
        case VersionToExport.Origin:
            WriteLine("Exporting origin file version");
            break;
        case VersionToExport.Modified:
            WriteLine("Exporting modified file version");
            break;
        case VersionToExport.Both:
            WriteLine("Exporting origin&modified file version");
            break;
        default:
            WriteLine("Nothing to export ....");
            break;
    }
}

W całym tym rozwiązaniu chciałem pozbyć się różnych kombinacji true&false z klauzul if. Dodatkowo enum lepiej opisuje dany warunek przez co kod lepiej się czyta.


czwartek, 2 marca 2017

Dogevents - Geneza

Jestem psiarzem


Wszystko zaczęło się w maju 2011 a dokładniej po weekendzie majowym. W moim życiu pojawił się taki oto jegomość (prawdopodobnie ktoś porzucił go na majówkę). Od tamtej chwili sporo zacząłem się kręcić wokół psich tematów. Szkolenia, psie sporty, behawiorystyka, tropienie użytkowe. Dziś trochę się zmieniło, rodzina się powiększyła, czasu zdecydowanie mniej ale zainteresowanie tą dziedziną pozostało.
Dexter kiedyś i dziś

Dogevents

Zamysł projektu Dogevents - czyli czegoś co ma dostarczać informacji o psich wydarzeniach pojawił się 2-3 lata temu. Założenie było proste, zebrać wydarzenia udostępnianie na grupach facebook'owych w przystępnej do przeglądania formie. Powstała nawet szybka implementacja tego pomysłu, jednak nie przetrwała próby czasu. Pierwsze rozwiązanie to była aplikacja konsolowa, która zbierała wydarzenia, wrzucała do bazy i następnie produkowała plik xml, który z pomocą jQuery + HTML wyświetlał je w postaci kafelek. Nie było tam żadnego backendu. Na hostingu znalazło się dokładnie kilka statycznych plików.

Dogevents, pierwotna wersja 2014r

Było to bardzo proste rozwiązanie. Byłem zdziwiony, że jQuery jest w stanie przetwarzać plik xml, filtrować węzły itp. Poniżej część kodu realizująca pobieranie danych z pliku xml, który pobierany był asynchronicznie $.ajax.get


var events = $(xml).find("Event").filter(function () {
 var evtStartTime = new Date($('StartTime', this).text());
 var evtEndTime = new Date($('EndTime', this).text());
 return (evtStartTime.getFullYear() == year && evtStartTime.getMonth() + 1 == month);
});


Comeback 2017

W raz z kolejną edycją DSP powrócił pomysł ponownej realizacji tego projektu. Posiedziałem, pomyślałem co wtedy poszło nie tak i mam w głowie nowy plan działania. Zebrałem również opinie, wymagania od potencjalnych użytkowników. Aspekt technologiczny i biznesowy opiszę w kolejnym poście. Już teraz mogę powiedzieć (żadna to tajemnica gdyż trzeba było podać w formularzu rejestracji do DSP2017), że projekt zostanie wykonany z użyciem asp.net core oraz Vue.js