Nozioni di base su AntiPatterns Visualizzazioni Rails

In questa serie per principianti, guardiamo ad AntiPattern, che - come suggerisce il nome - rappresentano praticamente l'opposto dei modelli di design. Sono scoperte di soluzioni a problemi che dovresti assolutamente evitare.

In questo articolo ci concentreremo sul principale View AntiPattern-PHPitis e lavoreremo attraverso un paio di tecniche per mantenere i tuoi punti di vista magri e cattivi. Avere tonnellate di codice Ruby nelle visualizzazioni non è solo sgradevole, ma spesso non necessario.

Temi

  • Viste delle rotaie
  • PHPitis
  • Estrarre gli helper
  • Aiutanti utili
  • Le forme
  • parziali
  • Contenuto condizionale
  • Markup semantico

Viste delle rotaie

Rails viene fornito con ERb (Embedded Ruby), fuori dalla scatola, e penso che per ora non sia necessario lanciare motori di rendering di visualizzazione come Slim per i nostri esempi. Se pensate che "convenzione sulla configurazione" si applica principalmente ai livelli del modello e del controller, state perdendo molte delle cose che rendono il lavoro con Rails così veloce e progressivo. Prendersi cura del livello di visualizzazione include non solo il modo in cui si compongono il markup, ma anche CSS / Sass, JavaScript, visualizza gli helper e i modelli di layout. Visto da quell'angolo, diventa un po 'più profondo di quanto si possa pensare all'inizio. L'enorme numero di tecnologie che possono essere coinvolte nella creazione dei tuoi punti di vista suggerisce che bisogna fare attenzione a mantenere le cose in ordine, chiare e flessibili.

Dal momento che il modo in cui scriviamo markup e stili è molto meno vincolato rispetto alla modellazione di domini, vuoi essere più cauto nel mantenere le cose il più semplici possibile. La manutenzione dovrebbe essere la priorità numero uno. Poiché le riprogettazioni o le iterazioni di progettazione possono essere più frequenti di ampie modifiche al livello del modello, la preparazione per il cambiamento assume un significato completamente nuovo quando si tratta del livello dell'utente. Il mio consiglio: non costruire necessariamente per il futuro, ma anche, con ogni mezzo, non sottovalutare il tasso di cambiamento, specialmente se si dispone di uno di quei "ragazzi idea" che conoscono le implementazioni del team. Quello che mi piace dell'approccio di Rails nei confronti delle visualizzazioni in MVC è che viene trattato come ugualmente importante e che le lezioni apprese dal dominio del modello sono state incorporate nella vista, ogni volta che è possibile e utile. Altri quadri sembrano essere d'accordo, dal momento che hanno integrato molte di queste idee introdotte da Rails.

Poiché l'ultimo articolo era un po 'più ampio, ho scelto questo argomento come un piccolo attimo di respiro. I seguenti articoli sui controller e sui test di Rails sono di nuovo più grandi. Gli AntiPatterns per le viste non sono così tanti, ma sono ugualmente importanti. Ci concentreremo su quello principale, PHPitis, e lavoreremo con un paio di tecniche per mantenere le tue opinioni magre e meschine. Dal momento che la vista è il tuo livello di presentazione, forse dovresti stare particolarmente attento a non creare un pasticcio pericoloso. Andiamo ad esso!

PHPitis

Perché abbiamo MVC in primo luogo? Sì, perché la separazione delle preoccupazioni sembrava la cosa più ragionevole da fare. Certo, le implementazioni di questa idea variano un po 'qua e là, ma il concetto generale di avere responsabilità distinte per ogni livello è la motivazione principale per costruire applicazioni robuste. Avere tonnellate di codice nel proprio livello di visualizzazione potrebbe non essere estraneo agli sviluppatori che provengono dal lato PHP delle cose, anche se i loro framework sono già stati raggiunti (pesantemente influenzati da cose come Rails?) - ma in Ruby-land queste cose hanno a lungo stato un AntiPattern ad alta voce.

I problemi evidenti, come il mescolare responsabilità e duplicazioni a parte, si sentono semplicemente cattivi e pigri, anche un po 'stupidi, per essere sinceri. Certo, capisco, quando sviluppi molto all'interno di un framework, linguaggio o qualsiasi ecosistema, è facile diventare complice o insensibile verso cose del genere. Quello che mi piace delle persone che spingono Ruby è che queste cose sembrano avere molto meno peso - questo potrebbe essere un motivo per cui l'innovazione non è mai sembrata un problema all'interno della comunità. Qualunque cosa funzioni meglio vince l'argomento, e possiamo andare avanti.

Quindi questa è un'intera sezione dedicata a bashing PHP? Affatto! In passato, le app di PHP avevano la reputazione di avere deboli separazioni tra modelli, visualizzazioni e controller (forse questo era uno dei motivi principali per cui le persone sentivano che scrivere app con Rails era molto più allettante). Avere singoli file con il codice per tutti e tre i livelli non sembrava così sexy. Quindi quando inseriamo tonnellate di codice Ruby o di dominio nelle nostre visualizzazioni, inizia ad assomigliare al terribile stile PHP di strutturare le cose: PHPitis. Solo alcune cose sono così negative quando si tratta di sviluppare applicazioni web, credo. Quando ti preoccupi degli sviluppatori felici e della tua sanità mentale futura, non riesco a capire perché qualcuno dovrebbe seguire questa strada: c'è solo dolore in futuro, sembra.

Rails offre un sacco di chicche per minimizzare il più possibile il codice nella vista. Devi imparare i modi di aiutanti, layout e preprocessori per ottenere una visione più chiara. Una semplice regola empirica è quella di mantenere la logica di dominio fuori dai tuoi punti di vista, senza scorciatoie!

Il prezzo da pagare per essere pigri su questo è difficile da sopravvalutare. Il codice Ruby che deve essere nel livello di presentazione dovrebbe essere il più piccolo e il più semplice possibile, nonché organizzato in modo intelligente. La tua ricompensa sarà un codice che è una gioia da estendere e da mantenere, e i nuovi membri del team avranno anche un tempo più facile che avvolgeranno la loro mente attorno alla nuova base di codice. Come bonus, anche i designer curiosi che codificano non si arrabbieranno e nasconderanno il cibo marcio nell'insalata se manterrai tonnellate di codice Ruby dal loro markup.

Aiutanti utili

Conoscere la miriade di metodi di supporto in Rails migliorerà in modo significativo la qualità del livello di presentazione. Non solo pulirà le cose e inietterà l'aumento di velocità occasionale in produttività, ma soprattutto ti aiuterà a combattere PHPitis.

La cosa che dovresti apprezzare di questi helper è che rappresentano estrazioni dal codice comunemente necessario. Invece di reinventare la ruota, in caso di dubbio, controlla se non c'è già un aiutante in giro che risolva il problema nella vista, lo stesso vale per i modelli e i controller, ovviamente.

Ecco un elenco di helper che dovresti esaminare subito:

  • form_for
  • Altri aiutanti per le forme.
  • fields_for
  • Collegamento a
  • content_for
  • E scrivi il tuo, ovviamente.

Le forme

Diamo un'occhiata a form_for primo. So che le forme sono un po 'noiose e non così sexy per un argomento, ma ti incoraggio fortemente a leggerle per familiarizzare con i dettagli più fini. È importante capire come funzionano. Ricordo spesso di guardarli senza dare loro molta attenzione. Certo, puoi farli lavorare abbastanza facilmente senza capire cosa sta succedendo sotto il cofano. In futuro, potrei prendere il tempo di scrivere un articolo completo su di loro. Nel frattempo, ti consiglio vivamente di dedicare un po 'di tempo a controllare la documentazione, almeno apprezzerai quanto sia conveniente Rails a occuparsi di materiale formale.

Il brutto

L'esempio seguente mostra l'HTML di un piccolo modulo di cui abbiamo bisogno per creare agenti. Accetta solo tre parametri come input: nome, numero e licence_to_kill. Un sacco di codice per questo piccolo compito, in realtà. Il authenticity_token proviene da Rails: è una cosa di sicurezza che protegge l'app da "falsificazione di richieste cross-site".

some.html.erb
 

Scrivere un modulo a mano non è solo lungo, ma anche incline all'errore. Inoltre, se ci apprestassimo in questo modo, dovremmo anche risolvere il problema con i vari percorsi e le classi CSS che potremmo aver bisogno per creare un nuovo oggetto e aggiornare un effetto one-in esistente, avremmo bisogno di duplicare i moduli per creare e modificare i record. Come vedrai presto, Rails ti incontra più di metà. Verdetto: comunque tu lo metti, l'approccio manuale è sgradevole e privo di comodità.

Il cattivo

Potremmo scendere lungo la strada seguente, che non fa un uso perfetto delle convenzioni in Rails. Controlla, non farlo. In pratica mostra che non stai gestendo gli strumenti disponibili a tuo vantaggio e stai duplicando il modulo per nuovo e modificare Azioni.

some.html.erb
 <%= form_for :agent, url: agents_path(@agent), html: method: :post do |form_object| %> <%= form_object.label :name %> <%= form_object.text_field :name %> <%= form_object.label :number %> <%= form_object.text_field :number %> <%= form_object.label :licence_to_kill %> <%= form_object.check_box :licence_to_kill %> <%= form_object.submit 'Create New Agent' %> <% end %> 

Quello che succede qui è che il form builder porta il modello che ti serve per il modulo.

 <%= form_object.text_field :name %> 

Dietro le quinte, la riga sopra viene espansa in quanto segue:

 <%= text_field :agent, :name %> 

Il form_for il metodo richiede un paio di argomenti:

  • un simbolo o una stringa per specificare l'oggetto
  • un url hash
  • un html hash
  • un namespace hash

L'url dell'URL serve a specificare le opzioni di routing. Ciò significa che è possibile specificare manualmente a quale percorso di instradamento si inviano i percorsi con nome e forma utile. Questo stile è chiamato "modo generico" perché è necessario configurare manualmente il file form_for chiamata.

Perché questa soluzione non è ottimale? Perché vogliamo mantenere la logica aziendale fuori dalle nostre viste e controllori il più possibile. Un effetto collaterale di questo è che abbiamo bisogno di cambiare un minor numero di parti quando necessario.

HTML
 

Nel caso lo avessi perso, questo approccio non ci ha fornito ID e classi per il modulo tag automaticamente. Quelli per ingresso i tag, tuttavia, sono stati generati per te. Lo risolveremo in un minuto. Basta sapere cosa si può ottenere gratuitamente e che probabilmente dovresti usarlo a tuo vantaggio. Se hai bisogno di qualcosa di diverso o di uno spazio dei nomi aggiuntivo, puoi usare il html hash o il namespace hash per specificare le cose un po 'di più.

some.html.erb
 <%= form_for :agent, url: agents_path(@agent), html: method: :post, class: 'create_agent', id: 'unique_agent', namespace: 'mi6' do |form_object| %> 
HTML
 

Non male! Ora il modulo il tag ha la classe e l'id specificati, qualunque cosa faccia fluire il tuo sangue, e il ingresso i tag sono nominati con MI6. Quasi lì.

Il bene

Questo è chiamato "stile orientato alle risorse" e ha la minima quantità di Ruby che è necessario scrivere nelle viste. Con questo approccio, vogliamo fare affidamento sull'identificazione automatica delle risorse. Rails individua i percorsi di cui ha bisogno in base all'oggetto stesso. Non solo, ti dà un output HTML diverso per creare un nuovo oggetto o per modificarne uno esistente. Dietro le quinte, Rails chiede all'oggetto se esiste già e agisce di conseguenza.

La creazione di moduli in questo modo è un uso intelligente delle convenzioni ed aiuta ad evitare la duplicazione. Una linea e tutto il sollevamento pesante è fatto per te.

some.html.erb
 <%= form_for @agent do |form_object| %> <%= form_object.label :name %> <%= form_object.text_field :name %> <%= form_object.label :number %> <%= form_object.text_field :number %> <%= form_object.label :licence_to_kill %> <%= form_object.check_box :licence_to_kill %> <%= form_object.submit %> <% end %> 

Molto meglio, non è vero? Ora otteniamo esattamente ciò di cui abbiamo bisogno sia per gli oggetti esistenti che per quelli nuovi. Inoltre, non è stato necessario aggiungere testo al pulsante di invio. Rails si è preso cura di questo e si adatta anche a oggetti nuovi o esistenti.

HTML per un nuovo oggetto
 
HTML per la modifica degli oggetti
...

Quando si modificano gli oggetti, questo si riflette anche nell'output HTML aggiungendo l'id all'id del modulo e la rotta o l'azione necessaria per l'aggiornamento di un oggetto specifico.

Una parola sulla magia di Rails. Quando le persone sostengono esattamente in queste situazioni che Rails è troppo magico per i loro gusti, spesso penso che ciò potrebbe significare semplicemente che non hanno speso abbastanza tempo per imparare gli strumenti del loro mestiere. Una volta preso il tempo necessario per padroneggiare questi strumenti, capirai spesso perché è stata effettuata una semplificazione o un'estrazione, e appaiono anche molto più sobri e diretti.

Attenzione!

Gli esempi di codice sopra utilizzati form_object come parametro di blocco. Questa procedura non è raccomandata, ma è stata fatta per ricordare all'utente cosa rappresenta questo oggetto e da cosa viene ottenuto form_for. La maggior parte delle persone usa solo un piano | F | o | Modulo |, che sembra molto più bello e più conciso.

A proposito, cose del genere etichetta, campo di testo, check_box e simili sono solo metodi di supporto che vengono richiamati nell'oggetto generatore di moduli. Ce ne sono un sacco che coprono praticamente ogni possibile esigenza che potresti incontrare.

some.html.erb
 <%= form_for @agent do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :number %> <%= f.text_field :number %> <%= f.label :licence_to_kill %> <%= f.check_box :licence_to_kill %> <%= f.submit %> <% end %> 

Conciso e legge bene, giusto?

parziali

Le collezioni sono un'altra cosa di cui non vogliamo essere troppo prolissi. Il rendering di partial per singoli oggetti di collezioni è così conciso e diretto, se fatto bene, che ritengo che tu abbia scuse molto scarse per non utilizzare le convenzioni di Rails per ridurre il codice di visualizzazione Ruby.

Giriamo le cose con questo e iniziamo con un esempio che mostra come sei incoraggiato ad avvicinarti a questo. Lungo la strada, ti spiegherò cosa puoi lasciare fuori.

Il bene

app / views / agenti / index.html.erb
 <%= render @agents %> 

Il rendere il metodo è abbastanza intelligente. La riga sopra è tutto ciò che devi scrivere per iterare su una collezione. Se hai bisogno di cambiare qualcosa in questa vista, sarà una piccola modifica e quindi una piccola causa di errore.

Quello che succede qui è che il framework è in grado di determinare di quale parziale ha bisogno. Attraverso il nome dell'oggetto, sa dove cercare il dato parziale dato che aderisci alla denominazione convenzionale delle cose. Per come la vedo io, questo è un buon esempio di come Rails non stia cercando di impressionarti con la magia. Il team di Rails lavora duramente per semplificarti la vita tagliando ripetizioni burocratiche di sorta.

app / views / agenti / _agent.erb
 

Nome dell'agente: <%= agent.name %>

Licenza per uccidere: <%= agent.licence_to_kill %>

Numero: <%= agent.number %>

Giocatore: <%= agent.gambler %>

Donnaiolo: <%= agent.womanizer %>

L'unica altra cosa necessaria per fare questo lavoro è posizionare un modello parziale nel percorso appropriato nella directory dell'oggetto ed estrarre gli attributi necessari dall'oggetto. Non è necessario scrivere loop per conto tuo. È veloce, facile, maneggevole e pragmatico, direi.

Questa estrazione era stata originariamente eseguita perché il nome del partial era la maggior parte delle volte il nome dell'oggetto iterated comunque, e quindi era facile creare una convenzione che gestisse questa attività comune in modo più efficace.

Il cattivo

OK, ora che sappiamo come gestirlo, diamo un'occhiata a ciò che potresti e dovresti evitare. L'esempio qui sotto è solo un cattivo utilizzo di Rails, ma non lo chiamerei brutto questa volta.

app / views / agenti / index.html.erb
 <% @agents.each do |agent| %> 

Nome dell'agente: <%= agent.name %>

Licenza per uccidere: <%= agent.licence_to_kill %>

Numero: <%= agent.number %>

Giocatore: <%= agent.gambler %>

Donnaiolo: <%= agent.womanizer %>

<% end %>

Ottieni lo stesso risultato di sopra, ma è decisamente più dettagliato. L'iterazione sulla collezione nella vista non è più necessaria. Quando usi rendere come sopra, il parametro di blocco agente è implicito, e puoi semplicemente usarlo senza il ogni ciclo continuo. Quindi stai lontano dal codice come questo: non ti fa sembrare particolarmente buono (ma nessuno riuscirà a prenderti la testa neanche per questo). Non è solo elegante e aggiunge al PHPitis.

Estrarre gli helper

La soluzione più ovvia per la pulizia del codice dalle visualizzazioni è di evitare di scriverne o di estrarle in modo intelligente. Diciamo che vogliamo scramble i nomi dei nostri agenti nella lista degli indici. Non dovremmo inserire questo codice direttamente nelle nostre opinioni. Se decidiamo che anche il modello non è il livello appropriato per posizionarlo, allora un helper personalizzato nel app / aiutanti directory potrebbe essere la scelta giusta.

app / aiutanti / agents_helper.rb
 modulo AgentsHelper def scramble (agent) agent.name.split ("). shuffle.join end end 

Imballando questo in un modulo all'interno della directory helpers, ora abbiamo accesso a questo metodo nelle nostre viste. Si prega di dare agli aiutanti specifici la propria casa e non mettere tutto su ApplicationHelper (app / aiutanti / application_helper.rb), che è davvero pensato per cose più "globali".

Ora posso accedere a questo piccolo individuo nel mio modello parziale, o in qualsiasi vista, per rendere la mia collezione di agenti.

app / views / agenti / _agent.erb
 

Nome dell'agente: <%= scramble(agent) %>

Licenza per uccidere: <%= agent.licence_to_kill %>

Numero: <%= agent.number %>

Giocatore: <%= agent.gambler %>

Donnaiolo : <%= agent.womanizer %>

I tuoi aiutanti personalizzati sono un ottimo modo per mantenere le tue opinioni pulite e in salute. E come hai visto, è così semplice e veloce che ci sono poche scuse per essere troppo pigri e non estrarli per lottare contro PHPitis.

Contenuto condizionale

Il metodo di supporto content_for è uno strumento utile per estrarre contenuti che non si adattano veramente al conto per un parziale ma che richiedono un po 'di incapsulamento. È un modo per archiviare un po 'di markup che è possibile applicare in base a una pagina per pagina, rendendola nel layout dove necessario. In termini di dimensioni, dovrebbe essere molto più piccolo di partial o persino layout.

Questa tecnica può anche farti risparmiare il passo di creare il tuo metodo per questo. I menu di navigazione o le barre laterali sono spesso esempi in cui questo aiuto diventa utile. Diciamo che vuoi avere un posto nel tuo menu che è solo per gli amministratori, ma non è necessario modificare l'intero layout. O hai pagine in cui la barra laterale non è necessaria. Con content_for, iniettate ciò di cui avete bisogno nel punto in cui vi serve su una pagina per pagina. Duplicazione non di più!

app / views / agenti / index.html.erb
 <% content_for :double_o_navbar do %> 
  • <%= link_to 'Operations', operations_path %>
  • <%= link_to 'Agents', agents_path %>
  • <%= link_to 'Messages', messages_path %>
  • <% end %> <%= render @agents %>
    app / views / layout / application.html.erb
    ...   
    • <%= link_to 'Home', root_path %>
    • <%= link_to 'About', '#' %>
    • <%= yield :double_o_navbar %>
    <%= yield %>

    A parte il fatto che questo intestazione è un buon candidato per l'estrazione in un parziale, guarda il yield: double_o_navbar sezione. Questa è una regione produttiva che inserisce il codice da a content_for bloccare. Inserirà il codice solo se i nomi dei simboli corrispondono. Qui vogliamo che solo gli agenti double-o abbiano accesso a determinati collegamenti nella barra di navigazione. Tutti gli altri vedono solo Casa e Di. Pensa ai link speciali di cui un amministratore ha bisogno per vedere che non dovrebbe mai affrontare un'interfaccia pubblica.

    Puoi anche usare questo helper per inserire id o classe attributi su tag HTML, se necessario. Ogni tanto è utile.

    Un altro uso comune è il popolamento del </code> di una pagina dinamicamente con a <code>content_for</code> bloccare.</p> <h6>app / views / layout / application.html.erb</h6> <pre> <title> Spettro - <%= yield(:title).presence || "Default" %>

    some.html.erb
     <% content_for :title do %> Qualche titolo funky <% end %> 

    Devi solo inserire il titolo desiderato in a content_for blocco e il layout dell'applicazione lo inserirà per te. Puoi diventare più intelligente con esso, ma per ora dovrebbe bastare. Se non hai bisogno di un titolo o dimentichi di aggiungerne uno, allora il logico || prenderà il via e darà un valore predefinito alla tua scelta. Nell'esempio sopra, dobbiamo verificare la presenza di un titolo o il default non funzionerà.

    Quello che non vuoi assolutamente fare è creare variabili di istanza per quel tipo di cose. Singole responsabilità, ricorda?

     def show @title = Fine del titolo di alcune pagine 

    Un'altra cosa: puoi chiedere se le pagine hanno un content_for bloccare.

    app / views / layout / application.html.erb
     <% if content_for?(:q_navbar) %> <%= yield :q_navbar %> <% end %> 

    Questo può aiutarti a evitare la duplicazione del markup che è rilevante per lo styling di una pagina che si adatta se gli elementi sono presenti in una pagina o meno.

    Markup semantico

    Questa è roba che vorresti assolutamente evitare.

    Il markup qui sopra è tratto dalla documentazione di bootstrap e specifica in che modo le colonne dovrebbero "sembrare" - informazioni che non hanno alcun significato semantico e che in realtà appartengono ai fogli di stile. Questa è la roba di cui gli stilisti hanno incubi.

    Allora, qual è il problema con quello? Questo è importante perché - oltre alla discutibile denominazione delle classi - il markup non convenzionale viola separazione degli interessi. Il tuo markup non dovrebbe essere infastidito dalle informazioni sullo stile; invece, entrambi dovrebbero stare in piedi da soli e permettere di farlo cambiare stili senza fatica-senza toccare il tuo HTML. All'inizio non è così difficile come potrebbe sembrare. Ci vuole un po 'di disciplina, però.

    Quando sei in grado di mantenere le informazioni sullo styling fuori dal tuo markup, hai effettivamente ridotto PHPitis su un altro fronte: per i designer è essenziale! Inoltre, l'uso di div generici senza significato intrinseco è un altro esempio di scarso markup. HTML5 ti offre molti elementi utili che forniscono più informazioni al tuo sé futuro, agli altri sviluppatori e ai motori di ricerca. La denominazione è presumibilmente difficile, ma HTML5 ti offre tantissimi elementi semantici che rendono le tue opzioni molto più semplici a tale riguardo.

    Pensieri finali

    Spero tu abbia visto che Rails Views non ha bisogno di molto amore per brillare. Gli sviluppatori possono essere un po 'snob sul livello front-end. Trattare con il markup a volte sembra essere un po 'inferiore a loro - scrivere HTML, DUH! Beh, non dovrei lanciare pietre, ma ho imparato ad apprezzare uno strato di presentazione ben affinato e ben rifinito. Rende molto più divertente lavorare e, se fatto bene, molto più velocemente per apportare le inevitabili modifiche. Analizzare tonnellate di codice Ruby mescolato con un markup scritto male non è un'esperienza divertente nel mio libro.