Zapis i odczyt data-attribute w DOM

Czasami trzeba zapisać coś w strukturze DOM, a potem odczytać te dane ponownie. I ponownie zapisać i odczytać i jeszcze raz. Z pomocą przychodzi wtedy nieśmiertelne jQuery. Znajdujemy wtedy interesujący nas element $(element) a następnie przy pomocy metody .data(“…”) odczytujemy wartość, lub .data(“…”,”…..”) zapisujemy wartość. jQuery lubi te dane sobie zapisać w swoim cache. Może się więc okazać, że każdy kolejny odczyt wskazywać będzie zawsze tą wartość, którą odczytaliśmy za pierwszym razem. Co wtedy? Są dwa sposoby; kiedyś udało nam się rozwiązać ten problem korzystając z innej metody, ale też jQuery; mianowicie .attr(“data-….”) i symetrycznie .attr(“data-…”, “…”) do odpisu i zaczytu (literówki!). Czasem i to nie wystarcza i wtedy należy sięgnąć po wanilie i skorzystać z getAttribute(“data-…”) oraz setAttribute(“data-…”,”…”) – te ostatnie zawsze działają. Jeden jedyny minusiczek, to taki: że jest to wyłom w spójności kodu, tzn. jeśli wszędzie używamy jQuery, a gdzieś wanilii, to dla części może to kłuć w oczy.

 

//EDIT (05.03.2016)
Przygotowałem przykład dostępny w jsfiddle jak i w giscie. Wygląda na to, w zmęczeniu przegapiłem pewne zależności lub coś działało jeszcze inaczej. Mianowicie problem z odczytem pojawia się wtedy gdy zapiszemy do data przy pomocy setAttribute a odczytamy za pomocą $.data, lub zapiszemy $.attr i odczytamy $.data. W tych przypadkach dane nie zostaną odświeżone. Sami sprawdźcie. Podziękowania dla @jarekkoziol za bycie ciekawskim.

GIST:

JSFiddle:
https://jsfiddle.net/jstadnicki/ydtry0c9/

Zamień bóla na enuma

Dlaczego zamienić? Moim zdaniem czytelniej i jasno sformułowana myśl i łatwiej zrozumieć. Nie chodzi o prosty przypadek, gdzie zamiana polegałaby na zamianie true/false na MyEnum.True/MyEnum.False – nie nie, to byłoby szaleństwem. Ale może od razu do kodu, bo czas nagli dzisiaj.

Pierwszy przypadek, wszystko działa jak należy:

Jakaś tam klasa filtrów, przyjmuje (@3) identyfikator, nazwę i bit, które oznacza że filtr będzie grupą lub nie (w zasadzie nie wiadomo, czym jest nie-grupa)

Teraz wykorzystanie:

Zasadniczo prosty kod, jest jakaś lista filtrów (@5). Następnie dodajemy filtry; pierwsze id, jakaś nazwa i true, potem id, nazwa i false. Osoba nowa w projekcie, lub nowa w tej części kodu na pierwszy rzut oka nie będzie wiedzieć co to oznacza. Następne dwie linijki są trochę bardziej rozgadane, bo jawnie mówią jak interpretować ostatniego bool’a.
Dalej szukamy tylko tych, gdzie IsGroup jest ustawione na true (@12). Jeszcze dalej wołamy metodę, która robi to samo, ale na podstawie parametru wejściowego (@18). Najczęściej gdy się tworzy takie metody nie zwraca się dużej uwagi na nazwę parametru, bo albo to R# wygenerował, a on robi dobrze, albo jest to jedna linijka i nie muszę się nad tym za bardzo skupiać. Rzeczywiście sama metoda jest prosta, trudno się pomylić do czego służy parametr i gdzie go wykorzystać.
Spójrzcie teraz na klienta tej metody, ma przesłać dwa parametry, listę filtrów (to proste), a potem flagę która oznacza group – group co?! Trzeba zajrzeć do ciała metody, żeby sprawdzić co oznacza ten parametr i jak mam go wykorzystać. Można też poprawić nazwę parametru lub (czuję obrzyd jak to piszę) napisać dokumentacja.

A co gdyby napisać kod w taki sposób:

Na scenę wchodzi tajemniczy FilterType:

Jak widać, tajemniczy bohater to enum, które jawnie definiuje jakiego rodzaju może być filtr:

  • 0 – nie został określony, nikt nie wie, nikt nie słyszał. Jeśli w ten sposób został zainicjalizowany, to najprawdopodobniej nie zostanie nigdy wykorzystany
  • 1- Typ grupowy
  • 2- Typ pojedynczy (aha, teraz to trochę jaśniejsze)

Prosty klient klasy Filtr, może wyglądać jakoś tak:

Na początku ponownie tworzymy listę, tym razem nie trzeba zgadywać co oznacza true/false. Nie trzeba także uciekać się do używania nazwanych parametrów aby rozjaśnić intencje.
Następnie gdy wybieramy z kolekcji filtry (@12), znowu w jasny sposób mówimy o typie, który nas interesuje. I wreszcie na koniec metoda filtrująca (@18) także w jawny sposób definiuje parametry których wymaga do działania.

Co jeszcze dobrego płynie z przyjęcia enuma? Jeśli pojawi się nowy rodzaj, nie trzeba będzie wprowadzać kolejnego bool’a który będzie mówić, czy filtr jest czy nie jest danego rodzaju. Domyślnie filtr jest nie znanego typu, więc wszystkie podczas poszukiwać za grupowym/pojedynczym takie znaleziska się nie pojawią. Nawet gdy nazwa parametru będzie do bani, sam tym parametru sprzeda intencje programisty i użytkownik metody (@18) będzie wiedzieć jakiego rodzaju wartość ma przesłać, aby otrzymać interesująco go wynik.

I jeszcze na koniec: oczywiście że ciężko tworzyć enum’y do każdego wykorzystania bool’a. Równie ciężko jest pisać kod który działa i jest czytelny, bezbłędny czy zoptymalizowany – a jednak każdy się stara.

Miłego wieczoru!

Logowanie przy pomocy Facebook, Twitter czy Google – OWIN Katana na ASP MVC 5

Następny wpis, tym razem już na nowym systemie i nowej własnej domenie. Zobaczymy czy będzie tak jak z samochodami, gdzie wizyta na myjni daje dwa konie mechaniczne więcej. Ostatnio pisałem więcej filozoficznie, dzisiaj powrót do twardej rzeczywistości.

Obejrzałem nie dawno na pluralsight fajny i krótki kurs o OWIN i Katana i częścią wiedzy chce się z wami podzielić.
Kto z was zaczynał projekt ASP MVC z włączonymi indywidualnymi kontami użytkowników? Ja na pewno! Także ja, gdy patrzyłem co się dzieje w OWINIE (w części konfiguracyjnej) i AccoutController łapałem się za głowę i zastanawiałem się WTF? Oczywiście próbowałem zrozumieć o co tam chodzi i czemu to wszystko jest tak “fajnie” napisane, niestety nie starczyło mi na to nigdy cierpliwości. Koniec końców pogodziłem się z tym faktem i korzystałem z tego tak jak jest, działało a ja nie zadawałem pytań – sprzedawać!

Teraz to wszystko może się zmienić! (dramatyczna muzyka i suspens – teraz!)

Aby to wszystko zrozumieć, zacząć należałoby od stworzenia pustego projektu MVC5 (tak, wiem staroć) – żadnych dodatkowych referencji, nic! Jak się bawić to na trylion procent. Na koniec wpisu, znajdziecie link do całego projektu. Dodajemy samodzielnie referencje do OWINowych rzeczy, tak aby moje i wasze referencje w tej kwestii się zgadzały:

  • Microsoft.Owin
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security
  • Microsoft.Owin.Security.Cookies
  • Microsoft.Owin.Security.Facebook
  • Microsoft.Owin.Security.Google
  • Microsoft.Owin.Security.Twitter

Z racji tego że projekt będzie oparty o MVC, nie można zapomnieć o:

  • System.Web.Mvc
Kompletna lista referencji w projekcie
Kompletna lista referencji w projekcie

Gdy to mamy ogarnięte, warto dodać plik z punktem wejścia do aplikacji, wykorzystany zostanie także do konfiguracji facebooka, twittera czy G+ oraz ogólnej authentykacji, u mnie wygląda on tak:

Co tam ciekawego? Na początku (@14) informujemy aplikację, o chęci korzystania z autentykacji opartej o ciastka. Ciastko przenosząca tajne dane nazywamy ThisCanBeAnythingYouLike. Następnie (@20-@41) wpinamy kolejne warstwy umożliwiające użytkownikom logowanie się przy pomocy FB, Twittera czy G+. Należy podać sekretne kody jednoznacznie identyfikujące waszą aplikacje.

Uwaga (@32) tutaj null oznacza, że nie interesuje mnie walidacja certyfikatów Twittera, oznacza to nie mniej, nie więcej, że jeśli ktoś podszyje się do Twittera i sprzeda mi swój certyfikat, ja to łyknę jak młody pelikan. Taka zagrywka przejdzie na blogu. Na żywej produkcji, należy odnaleźć aktualnie używane certyfikaty Twittera i dodać jest do walidacji. Dodatkowo zwracam uwagę, że wszystkie formy authentykacji korzystają z tego samego ciastka, znowu na blogu nie robi mi to różnicy skąd i jak i czym logują się moi użytkownicy. Produkcja rządzić może się swoimi prawami.
“/Auth/Login” (@14) ta linijka mówi, gdzie należy się przekierować gdy użytkownik nie będzie zalogowany, a aplikacje będzie tego wymagać.
Dla przypomnienia odpowiednio atrybuty AllowAnonymousAttribute oraz AuthorizeAttribute.

Skoro już powiedziałem o przekierowaniu do “/Auth/Login” (Auth controller, Login action) zajrzyjmy co kryje się w tej klasie:

Wielkiej ilości kodu nie ma (gdzie jest AccountController?!). Zaczynając od metody [HttpGet]Login (@15) tworzymy prosty pusty model do logowania się użytkownika. Z OWINa wyciągamy informacje o zarejestrowanych providerach (dostarczycielach?) logowania i wpychamy do modułu, który potem renderujemy.

W parach pracuje się lepiej, dlatego istnieje także [HttpPost]Login (@29) który przyjmuje ten sam model(pamiętamy, że to tylko wpis na blogu i nie maiłczymy że model!=dto!=viewmodel). W klasie są informacje o nazwie i haśle, następuje skomplikowana weryfikacja i jeśli dane są poprawne tworzymy w locie użytkownika. Nadajemy mu także pewne cechy: nazwę oraz identyfikator. Tak zdefiniowanego użytkownika logujemy przy wykorzystaniu OWINa. I wracamy do domu, jeśli weryfikacja się nie powiedzie, dajemy kolejną szansę.

Dla znudzonych oferujemy opcję wylogowania (@47) jak zawsze wszystko kręci się wokół OWINa.

Wreszcie! WRESZCIE! Jak zobaczycie zaraz na przykładzie z razora, tutaj obsługujemy kliknięcie użytkownika, gdy wybierze ono (!on i !ona) opcję logowania się przy pomocy FB, Twitter czy G+. Jako parametr wejściowy przyjdzie nazwa providera. Następnie na OWINie i autentykacji wywołujemy metodę Challange z parametrem informującym, że chcemy wrócić do “/secret”. Kod tego kontrolera pokaże na końcu. Po wywołaniu zwracamy jeszcze odpowiedni kod HTTP aby wszystkim formalnościom stało się zadość. Status Unauthorized jest bardzo ważny i nie może zostać pominięty.

No dobrze, skoro tutaj tak mało kodu, to pewnie razor będzie przekombinowany, pomyślało teraz ono. Aby nie trzymać w niepewności:

Pierwsze linie wynikają z braku pełnej integracji z MVC, odpowiednich wpisów w web.config etc. Ignoruj. Linie (@15-@30) to forma gdzie można się zalogować jako aplikacyjny użytkownik. Linie (@32-@37) to pętla, która wyciąga z modelu (ono przypomina sobie teraz AuthController::Login) listę providerów, na jej podstawie tworzy proste linki z nazwą providera oraz odpowiednim id (“Facebook”, “Twitter”, “Google”), które zostanie przekazane do metody SocialLogin (AuthController::SocialLogin) i użyte to rozróżnienia providera.

Co teraz? Gdzie jest reszta, gdzie jest ACCOUNTCONTROLLER? Nie ma i nie potrzeba. Póki co, nie będę nigdzie zapisywać użytkowników. Demo pokazuje tylko jak zalogować się przy pomocy providerów wymienionych powyżej.

Co znaczy zalogowanie? Otóż mniej więcej tyle:

Będziemy w stanie przejść do metody Index, która jest klasie wymagającej autoryzacji. Czyli User.Identity.IsAuthenticated zwróci TRUE. To całkiem fajnie. Oprócz tego, mamy kilka dodatkowych informacji:
Oraz widok:

W widoku widać że interesują nas cechy nadane nam przez system. Nie ważne czy to FB, Twitter, G+ czy my sami (AuthController, Login w wersji POST). Po uruchomieniu aplikacji wynik operacji wyglądają tak:

Wersja lokalna (user:jaroslaw/pass:stadnicki):

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier : jaroslaw
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name : jaroslaw

Wersja z G+

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier : 1#6#9#9#9#2#4#7#1#2#6 (odrobina prywatności)
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname : Jarosław
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname : Stadnicki
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name : Jarosław Stadnicki
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress : jaroslaw.stadnicki@gmail.com
urn:google:profile : https://plus.google.com/+JarosławStadnicki

Itp, itd. Warto zwrócić uwagę, że z G+ (inne też), dostajemy już identyfikator użytkownika, który możemy powiązać ze swoim lokalnie tworzonym kontem. I gdy ponownie przyjdzie gościu z G+ i się zaloguje się i dostaniemy jego ID, będziemy wiedzieć, że on to on, a nie ktoś inny.

Na koniec jeszcze ważna sprawa, oczywiście należy pamiętać aby samodzielnie stworzyć swoje aplikacje odpowiednio na stronach:
https://developers.facebook.com/
https://apps.twitter.com/
https://console.developers.google.com/home/dashboard

Należy także ustawić poprawnie callbacki w tych systemach, u mnie wartość które działały poprawnie to: http://www.xoxox.com

Ej, ale co to za dziwny adres? No tak tak, bo Twitter nie lubi localhost (FB i G+ sobie z tym radzą). Dlatego dla Twittera robi się wyjątek i należy zaatakować windows\sytem32\drivers\etc\host dopisać taką linijkę:

127.0.0.1         www.xoxox.com

Od tej pory www.xoxox.com będzie wskazywać na wasz komputer. Ale jeszcze! Należy na normalnym IIS dodać stronę i pokazać jej, że binarki leżą u was w źródłach. Dodać binding w IIS. I na końcu w Visual Studio przełączyć się z IIS express na normalnego i też korzystać z www.xoxox.com. Można też zignorować twittera i dla testów sprawdzić tylko FB i G+ i korzystać z IIS express na 127.0.0.1

Tyle, od tej pory ja i wy (jaka jest liczna mnoga od ono?) będziemy mogli w prosty sposób logować się społecznościami. Aplikacja dostępna jest pod linkiem:
https://bitbucket.org/jstadnicki/simplesociallogin

Kod do aplikacji zostały zmienione przed publikacją, także aby zadziałały należy wygenerować własne oraz podmienić. Projekt działa pod kontrolą pełnego IIS, to także należy/można zmienić.

To tyle. Życzę miłej zabawy.

Nie bój się powiedzieć: nie wiem

Pomyślcie sobie gdy idziecie do lekarza i strasznie was coś boli. Macie wizytę, doktor bada i opukuje i finał finałów: chcielibyście dostać informacje że lekarz nie wie i zaleca wizytę u specjalisty, czy lepiej żebyście dostali witaminę C, podczas gdy np. wasze oko wygląda jak u osób zarażonych wirusem H1Z1? Ja wybieram opcje 1. Oszczędzam swój czas, swoją nadzieję że to co dostałem mnie wyleczy i oszczędzam reputację lekarza u którego byłem, bo na pewno wrażeniami z leczenia podzielę się z innymi.
Ostatnio powiedziałem: “nie wiem, nie mogę tego zrobić, to jest poza moim poziomem wiedzy” – i wiecie co, świat się nie skończył. Zadanie dotyczyło wyznaczania jakichś standardów technicznych w miejscu gdzie pracuje. Oczywiście mogłem dorzucić swoje trzy grosze, ale pomyślałem że lepiej nie wtykać nosa tam gdzie nie mam nic mądrego do powiedzenia. Gdybym się wymądrzał, mówił rzeczy które mogą nie mieć sensu, kręcił, etc. wtedy robota którą miałem dostarczyć byłaby nie fajna. Standardy, które wyznaczyłem byłyby nie fajne. wrażenia innych  po przeczytaniu standardów byłyby nie fajne.
Jasne, każdy z nas chciałbym zrobić coś ciekawego i ważnego – ja też. Czasami jednak przychodzi moment, że coś mnie (nas wszystkich) przerasta. Możemy wtedy zrobić dwie rzeczy: wziąć na siebie odpowiedzialność i spierdolić sprawę śpiewająco; budżet, czas, reputacja, nerwy, zdrowie, co tam jeszcze można. Lub! Lub zagrać w otwarte karty: nie dam rady, potrzebuje kogoś innego do pomocy, albo w ogóle się na tym nie znam, musicie znaleźć kogoś innego.
Sami zgadnijcie która postawa jest bardzie pro.
Następnym razem postaram się bardziej technicznie napisać.

Posłuchaj klienta

Jeśli klient chce statycznego HTML za kilka minut, nie dostarczaj mu dynamicznego kodu, z pętlami, partialami czy różnymi warstwami za godzinę – za późno.
Jeśli klient chce Abyś wygenerował te notatki w kontrolerze za kilka minut – nie produkuj repozytorium, nie zaprzęgaj dependency injection za godzinę – za późno.
Jeśli klient chce edycji tylko swoich wpisów i możliwości edytowania tylko tytułu czy tekstu, to dostarcz tylko tyle, nie podmieniaj całego modelu – za późno, źle i za dużo.
Pamiętaj, lepiej mieć coś co spełnia wymagania klienta powoli, niż coś co nie spełnia ich szybko.
Posłuchaj swojego klienta, zapytaj jeśli czegoś nie rozumiesz, zaproponuj jeśli uważasz że coś może być zrobione lepiej. Ale pamiętaj, że to jego biznes i to on wie jak robić na tym pieniądze.