Nell'helper o nel modello?

Chiedo lumi per sapere se c’è una soluzione elegante a un banale
problema che ricorre tante volte. Faccio un esempio tra i mille
possibili.

C’è un modello Model con un campo status. Nelle view in base allo status
si mostrerà un’icona (es: rossa per status disabilitato, verde per
status abilitato, etc).

Posso creare un helper status_for_mymodel a cui passo l’oggetto Model e
che ritorna l’image_tag appropriata. Oppure posso creare un metodo
icon_tag in Model e fargli ritornare l’immagine. Nella view poi scriverò
@oggetto.icon_tag

La prima soluzione è più MVC ed è quello che faccio di solito, ma non è
il massimo trovarsi tutti quei metodi status_for_x, quando i modelli in
questione sono parecchi. Inoltre è poco object oriented. La seconda lo è
molto di più e sarebbe più bella da leggere. Per contro è difficile
accedere ad image_tag da dentro un modello.

Magari c’è una terza e più elegante soluzione. Voi che fate normalmente?

Paolo

2009/10/8 Paolo M. [email protected]:

C’è un modello Model con un campo status. Nelle view in base allo status
si mostrerà un’icona (es: rossa per status disabilitato, verde per
status abilitato, etc).

Posso creare un helper status_for_mymodel a cui passo l’oggetto Model e
che ritorna l’image_tag appropriata. Oppure posso creare un metodo
icon_tag in Model e fargli ritornare l’immagine. Nella view poi scriverò
@oggetto.icon_tag

la tecnica che uso e’ una variazione sulla seconda strada che indichi,
in pratica separo le definizione di quale ‘icona’ usare dall’output
del tag image (o dell’url se e’ per quello), facendolo in maniera
uniforme sui diversi modelli (i.e. usando lo stesso metodo)

e.g.

class ModelA
def icon_name
case status

end
end

class ModelB
def icon_name
case this_other_status

end
end

e poi l’helper diventa lo stesso per tutti i modelli:

def icon_for(model, options)
image_tag(model.icon_name, options)
end

quindi nella pagina avrai

<%= icon_for model, options %>

insomma il metodo icon_name dei modelli mappa l’istanza del modello
nello ‘spazio’ delle possibili icone ( non e’ detto poi che l’output
debba essere direttamente il nome dell’icona sul file system, potrebbe
essere ‘un puntatore’ all’icona giusta, i.e. ci potrebbe essere di
mezzo un’altra indirezione )

che ne dici?
ciao
Luca

Il 08 ottobre 2009 10.45, Paolo M. [email protected] ha
scritto:

icon_tag in Model e fargli ritornare l’immagine. Nella view poi scriverò
@oggetto.icon_tag

La prima soluzione è più MVC ed è quello che faccio di solito, ma non è
il massimo trovarsi tutti quei metodi status_for_x, quando i modelli in
questione sono parecchi. Inoltre è poco object oriented. La seconda lo è
molto di più e sarebbe più bella da leggere. Per contro è difficile
accedere ad image_tag da dentro un modello.

Magari c’è una terza e più elegante soluzione. Voi che fate normalmente?

Il problema si può scomporre logicamente in due parti, ovvero:

  1. determinare lo stato di un modello;
  2. disegnare l’icona per un certo stato.

Il primo problema è compito del modello, il secondo è compito dell’helper.

La mia proposta è quindi che tutti i model che hanno uno stato
rappresentabile graficamente discendano da un capostipite che
definisce il metodo stato, il quale dovrebbe restituire un simbolo (se
sono pochi) o un oggetto, fai tu.

A questo punto scrivi un unico helper che riceva un ModelloConStato,
si faccia dare da questo lo stato e disegni l’icona conseguentemente.

In questo modo il codice è sia MVC che object oriented; l’informazione
sullo stato del modello sta nel modello, l’informazione sull’icona sta
nell’helper.

Piccolo appunto: se non sbaglio, stato nel senso di disabilitato,
abilitato etc. in inglese è state, non status.

pietro

Il 08 ottobre 2009 10.55, Pietro G. [email protected] ha
scritto:

La mia proposta è quindi che tutti i model che hanno uno stato
rappresentabile graficamente discendano da un capostipite che
definisce il metodo stato, il quale dovrebbe restituire un simbolo (se
sono pochi) o un oggetto, fai tu.

Rileggendomi, mi sembra di essere stato poco chiaro, qui.

Ruby è sì un linguaggio a oggetti, ma ha delle particolarità rispetto
ad altri linguaggi.
In altri linguaggi si farebbe come ho detto, oppure (forse più
propriamente) si farebbe implementare a ogni model interessato una
certa interfaccia; in Ruby ciò non è necessario, basta in genere
definire un metodo e poi usare respond_to?(:metodo), che è comodo ma
niente affatto OO, e a me

155.kind_of? Numeric

sembra più corretto di

155.respond_to? “/”

Insomma, fai un po’ tu.

pietro

volendo gli puoi far svolgere comunque l’operazione assumendo che tutto
sia corretto, e agire con “rescue” se qualcosa va storto, in questo modo
puoi applicare un duck-typing abbastanza liberal (cit.)

su “The Ruby P.ming Language” c’è un esempio di come si potrebbero
confrontare due classi Point(x,y) applicando questo tipo di duck typing:

def ==(o)
@x == o.x && @y == o.y # assume che “o” abbia i metodi “x” e “y”
rescue # se poi l’assunzione fosse sbagliata…
false
end

ciao,
A.

Pietro G. ha scritto:

Alessandro S. wrote:

Ciao!
a me è capitato spesso, la soluzione che applico di solito (se non si
tratta di un campo di un singolo modello) è estendere active_model, così
tutti i miei model hanno il metodo “icon_name” che restituisce ciò che
serve.
A quel punto può non servire neanche fare un helper, se devi mostrare un
immagine basta che usi image_tag(model.icon_name, options) direttamente

Mi sa che alla fine adotterò la soluzione di Alessandro. Pure quella di
Luca mi piaceva ma questa è più coincisa. Cambierò solo il nome del
metodo in status_icon così mi ricordo di che si tratta. Quella di Piero
invece è un po’ più complessa anche se molto OO.

Grazie a tutti!
Paolo

Ciao!
a me è capitato spesso, la soluzione che applico di solito (se non si
tratta di un campo di un singolo modello) è estendere active_model, così
tutti i miei model hanno il metodo “icon_name” che restituisce ciò che
serve.
A quel punto può non servire neanche fare un helper, se devi mostrare un
immagine basta che usi image_tag(model.icon_name, options) direttamente

class ModelA
def icon_name
case status

end
end

class ModelB
def icon_name
case this_other_status

end
end

e poi l’helper diventa lo stesso per tutti i modelli:

def icon_for(model, options)
image_tag(model.icon_name, options)
end

quindi nella pagina avrai

<%= icon_for model, options %>