to_s w modelu

Hej,
zastanawiam się jakie jest wasze zdanie w kwestii nadpisywania to_s w modelach. Banalny przykład:

class User def full_name [firstname, lastname].compact.join(' ') end alias :to_s :full_name end
zaciemnia czy rozjaśnia kod? Ew. tworzenie to_s z parametrami typu

to_s(:with_age)

Interesują mnie zalety i wady korzystania z tego (może jest jakaś ogólnie przyjęta praktyka?).

Z zupełnie innej beczki i nie na temat. Czemu nie?:

"#{firstname} #{lastname}"

[quote=paneq]Z zupełnie innej beczki i nie na temat. Czemu nie?:

"#{firstname} #{lastname}"

[/quote]
To akurat rozumiem - jeśli brakuje pierwszego, to będzie niepotrzebna spacja z przodu. Choć lepiej chyba byłoby “#{f} #{l}”.strip.

to_s może przyjmować parametry - jest tak np. w wbudowanych klasach Rubiego:

10.to_s #=> "10" 10.to_s(16) #=> "a"
Nie widzę więc przeciwwskazań z zastosowaniem parametru (przynajmniej w kontekście podążania za standardami. Natomiast zastanawiam się, czy faktycznie jest sens tworzyć fullname i aliasować go przez to_s. Czy samo to_s nie wystarczy?

Ja rozumiem dlaczego to jest tak napisane i jaka jest różnica, tylko jeśli twoja aplikacja nie wymaga firstname (lub lastname) to kto by się tym przejmował :slight_smile:

Dzięki za odpowiedzi. Powiem szczerze, że bardziej zależy mi na opinii o samym używaniu to_s niż aliasowaniu go :wink: Tzn. ilu z was używa tego w swoich aplikacjach i czy nie uważacie tego za zaciemnianie kodu (ostatnio miałem dyskusję na ten temat, dlatego mnie to ciekawi)?

W jednym projekcie, który rozwijałem było coś takiego zastosowane. Ciężko powiedzieć czy zaciemnia. Z jednej strony jest fajnie, bo zamiast np. pisać:

[code]<%= user.full_name %>

piszemy po prostu

<%= user %>[/code]
ale z drugiej strony trzeba wtedy wiedzieć co tak naprawdę to_s zwraca. Ja chyba jednak wolałbym użyć full_name.

ja uważam to za super praktykę - zysk jest zwłaszcza przy selectach - podając jako kolekcję User.all masz automagicznie opcje z imieniem i nazwiskiem - bez jakiejś żonglerki .map czy innymi cudami.

Z drugiej strony w formtastic też masz to za darmo: https://github.com/justinfrench/formtastic/blob/d94ee2f5a7a3378f17dd122a6580fbbbd201ad5e/lib/formtastic/form_builder.rb#L19 . Ja się zgadzam z drogusem. Jest jeszcze kwestia tego, że nazwe kolumny i tak wstawisz przez User.human_attribute_name(:full_name) więc dla mnie wtedy wartośc kolumny najlepiej też jednoznacznie outputować przez user.full_name

w większości przypadków, wydaje mi się, że jesteś w stanie zgadnąć. Widząc modele Restaurant, User, Article sądzę, że bez patrzenia w kod jesteś w stanie się domyśleć co .to_s zwraca. A gdy masz wątpliwości (lub model, w którym ciężko zgadnąć) - sprawdzenie tego w modelu zajmuje ułamek sekundy *

jasne, przy czym ja nie jestem wielkim fanem formtastica

    • Podsumowując: uważam, że .to_s daje zupełnie gratis dużo plusów. Jedyne, czego trzeba się trzymać, to żeby ta metoda była możliwie prosta. o ile parametryzacja metody w pewnych przypadkach wydaje mi się dopuszczalna, tak robienie jakiejś większej magii (nawet nie bardzo jestem w stanie wymyślić jakiś use-case), taki, że domyślenie się, co faktycznie metoda zwraca w więcej niż 10 sekund to zło.

Restauracja - nazwa, ale z miejscowością czy już nie
Użytkownik - login, email, full name - nie mam pojęcia.
Article - pewnie nazwe ale równie dobrze z jakiegoś powodu mogłaby to być część nazwy (by nie za długi) lub odpowiednia wersja bez jakiś znaków specjalnych czy coś. Może też z autorem.

Powiedziałbym, że pewnie jak pracujesz w projekcie to wiesz co zwraca (nauczyłeś się na pamięć lub znasz strukturę aplikajci na tyle by się domyślić) natomiast jak z doskoku siadasz to już nie byłbym taki pewien. Wydaje mi się, że w dłuższym czasie nie ma to wszystko szczególnego znaczenia. Ani dla czytelności ani dla wydajności pisania.

Moim zdaniem o wiele sensowniej do takich celów używać dekoratorów, np.: https://github.com/jcasimir/draper
W modelu nie powinno być logiki prezentacji, bo co jeśli np. będziemy chcieli dać tam i18n lub lokalizacje?

Przykład:

class Product def name_with_price "" end end

ile osób, tyle podejść - ja mimo wszystko uważam, że wszystko powinno być tak proste, jak to tylko możliwe(a .to_s jest chyba najprostszym rozwiązaniem)

rozmawialiśmy o w miarę prostych przypadkach (np. User), gdzie imienia i nazwiska raczej nie wpychasz do i18n. Jeśli natomiast znalazłbyś przypadek, gdy faktycznie trzeba, to można przecież to załatwić np. przez globalize(lub np. mongo)

Jak dla mnie to najprostsze do odczytania(a kod ma być prosty do czytania) jest zrobienie nowej metody z odpowiednią nazwą.

@client.full_name

jest proste, czytelne i grepowalne

Niezależnie od tego czego się później używa, warto zdefiniować to_s w taki sposób, żeby był krótki i dawał jakiś obraz obiektu, z którym mamy do czynienia. Jest to szczególnie przydatne przy pracy w konsoli.

W Django na przykład to jest normalna praktyka i pojawia się już w pierwszym tutorialu (Wait a minute. <Poll: Poll object> is, utterly, an unhelpful representation of this object.) - właśnie do ułatwienia pracy z konsolą (ale nie tylko - Django Admin też z tego korzysta np. do wyświetlania listy rekordów).

Tylko że w Ruby by default dostajesz w takim wypisaniu wartości wszystkich zmiennych instancyjnych, co jest bardzo prostą, czytelną i pomocną reprezentacją.

Z punktu widzenia ood to_s jest na zbyt niskim poziomie abstrakcji (powiedziałbym nawet, że na takim jak id) i raczej nie powinien byc używany w logice aplikacji. Jeżeli chcesz dostać full_name to napisz metodę, która to zrobi. to_s tak jak już ktoś wspomniał może być przydatny na konsoli albo przy debugowaniu.
Widziałem ostatnio kod (javowy), który w testach porównywał dwie daty bazując na tym co zwróci toString(). Problem w tym, że na polskich localach toString() zwraca coś innego niż na angielskich przez co oczywiście test w głupi sposób failował.

Nie po to mamy puts i p (lub puts zmienna.inspect ;))?

[code=ruby]class Offer < ActiveRecord::Base
def to_s
title
end
end

puts Offer.find(1) #=> “Tytuł oferty”
p Offer.find(1) #=> #<Offer id: 1, title: “Tytuł oferty”>[/code]

Nie po to mamy puts i p (lub puts zmienna.inspect ;))?[/quote]
Dokładnie. @Tomasz - prostą i pomocną, owszem, ale czytelną? Jak wypisuję 50 obiektów z 10 zmiennymi instancyjnymi, to nigdy to nie jest czytelne.