Usuwanie użytkownika czy blokowanie

Mam dylemat. Mianowicie nie mam ogromnego doświadczenia w pisaniu aplikacji webowych, a na forum są ludzie bardziej doświadczeni, więc chciałbym się spytać które rozwiązanie jest lepsze - totalne usuwanie(destroy) czy też zablokowanie/banowanie użytkownika.

Jeśli wybrał bym kasowanie usera to mam rozumieć że jego działalność np komentarze, strony itd mają zostać na stronie? Nie wynikną jakieś błędy, rzucanie wyjątków?

Blokowanie było by wg mnie opcją jak dla mnie łatwiejszą w implementacji. Po prostu dodało by się pole bool i zmieniało wartość. Można by też było zrobić blokowanie czasowe, ale nie wiem jak się za to zabrać. Ponadto trzeba by było logowanie zabezpieczyć. Pewnie jest jakaś metoda :before_cos, tak?

Które rozwiązanie jest lepsze?

Moim zdaniem usuwanie (destroy) uzytkownika to proszenie sie o klopoty. Jezeli komentarze lub inne dane sa powiazane z userem, to zgaduje, ze pewnie gdzies w widokach lub w innych miejsach w kodzie sa odwolania typu comment.user.name bez sprawdzania czy user istnieje. Efektem usuniecia bedzie wysyp wyjatkow w roznych miejscach aplikacji. Sadze, ze prosciej jest dopilnowac zeby tam, gdzie sa asocjacje z userami, zawsze ten user byl (czyli nie usuwac go).

Oczywiscie jezeli aplikacje masz napisana w taki sposob, ze zawsze sprawdzasz, czy user nie jest nilem, to usuwanie jest OK.

Co do blokowania to zalezy czego uzywasz do autentykacji… jezeli robisz to “recznie”, to gdzies przy logowaniu trzeba sprawdzic ta flage, czy user nie jest zablokowany. I to w sumie tyle :wink:

Blokowanie czasowe mozesz zrobic dodajac np. pole blocked_until, w ktorym bedzie data, po jakiej user bedzie mogl sie zalogowac. I teraz w miejscu, gdzie sprawdzasz flage, wystarczy dodac tez sprawdzenie daty.

Aha, jak dodasz jakiekolwiek pola odpowiadajace za blokowanie usera, to pamietaj, zeby zabezpieczyc je za pomoca attr_accessible.

ciężka sprawa.
Z jednej strony usuwanie użytkownika, to faktycznie proszenie się o kłopoty (część elementów powiązanych musi zostać usunięta(co akurat RoR nam upraszcza - wystarczy przy relacjach dodać :dependent => :destroy), część musi zostać (i wtedy wyświetlić zamiast np. loginu/imienia usera info o tym, że konto jest usunięte) - więc blokowanie jest napewno rozwiązaniem prostszym.
Problem pojawia się w momencie, gdy przechowujesz dane osobowe użytkownika - wtedy zgodnie z ustawą masz obowiązek kasować konto(niedozwolone jest właśnie blokowanie/ukrywanie) - pomijając już miliard innych obowiązków :wink:
Blokowanie czasowe jest dobre gdy dajesz czasowego bana, lub użytkownik przekroczył N niepoprawnych logowań - implementacja jest w zasadzie bardzo prosta, choć uzależniona od tego, z czego korzystasz do logowania :wink:

[quote=krzyzak]ciężka sprawa.
Problem pojawia się w momencie, gdy przechowujesz dane osobowe użytkownika - wtedy zgodnie z ustawą masz obowiązek kasować konto(niedozwolone jest właśnie blokowanie/ukrywanie) - pomijając już miliard innych obowiązków ;)[/quote]
No to mam obejście. Wymazać wszystkie pola i powpisywać np “puste”. Ale zawsze spam zostaje w bazie. ;/

Jeśli chodzi o uwierzytelnienie to używam Devise.

Lepsze jest prawdziwe usuwanie. Bo i tak musisz je zrobić!

Po pierwsze każda szanująca się aplikacja musi dawać użytkownikowi możliwość całkowitego usunięcia konta.

Po drugie dla każdego obiektu biznesowego i tak musisz zadbać, by prawidłowo działała metoda #destroy (poprzez :dependent => * lub klucze obce z klauzulą ON DELETE). Każdy obiekt wcześniej czy później przyjdzie Ci usunąć (z konsoli, w migracji, itp), więc to po prostu musi działać.

Niezależnie od powyższego blokowanie użytkownika prostą flagą i tak się przyda ale do innych celów (tymczasowość).

Nie muszę chyba dodawać, że pomysł nadpisywania pól pustymi wartościami nie powinien ujrzeć światła dziennego.

Usunięcie konta użytkownika to jedno a wpisów to drugie. Jeśli potrzebne jest nam rozwiązanie, które które np usuwa użytkownika ale zostawia jego posty na forum to może się przydać wzorzec projektowy Special Case albo jego specjalny przypadek czyli Null Object.


http://martinfowler.com/eaaCatalog/specialCase.html

to tak w ramach rekomendacji zamiast sprawdzania czy user jest nullem.

[quote=paneq]http://en.wikipedia.org/wiki/Null_Object_pattern
http://martinfowler.com/eaaCatalog/specialCase.html

to tak w ramach rekomendacji zamiast sprawdzania czy user jest nullem.[/quote]
+1

A masz może sprawdzony przykład implementacji tego w kontekście Railsów?

Mamy np.:

comment.user.name

Można by oczywiście ująć to w rescue:

( comment.user.name rescue "(użytkownik usunięty)" )

…ale zamiast dodawać wszędzie brzydkie rescue chcemy aby globalnie zajął się tym Null Object.

Trzeba by zatem zdefiniować wrapper Comment#user - tylko, że to nie jest takie trywialne, bo jak wiemy te metody wykorzystują method_missing. I właśnie o to pytam - czy masz tu może sprawdzoną implementację?

Akurat has_many/belongs_to raczej nie wykorzysutją mm tylko generują te metody. Jeśli robią to do modułu to może starczy

def user
super || EmptyUser.new
end

jeśli nie robia tego do modułu

to ja bym zrobił

class AbstractComment < AR::Base
set_table_name :comments
belongs_to :user
end

class Comment < AbstractComment
def user … end
end

lub użył brzydkiego alias_method_chain

Należy też pamiętać że czasem chcemy mieć nowego niewypełnionego użytkownika (user.build) zamiast special case więc może lepiej pójść w stronę :

def ensured_user
user || EmptyUser.new
end

Tak zarzuciłem tematem tylko, gotowej implementacji brak.

Można by też może spróbować pójść w stronę DCI (vide: ostatnie posty Andrzeja Krzywdy) i jeśli kiedyś potrzebuje by post mi zwracał usera lub special case to najpierw zrobić mu extend ? Tak sobie gdybam…

[quote=paneq]Należy też pamiętać że czasem chcemy mieć nowego niewypełnionego użytkownika (user.build) zamiast special case więc może lepiej pójść w stronę :

def ensured_user
user || EmptyUser.new
end[/quote]
…i po przemyśleniu to wydaje mi się jedynym słusznym rozwiązaniem. I to pomimo, że wymaga globalnej refaktoryzacji .user => .ensured_user.

Jeśli się nie mylę to tak np jest w cancan

def ensured_user
user || EmptyUser.new
end