homeASCIIcasts

208: Blocchi ERB in Rails 3 

(view original Railscast)

Other translations: En Es

Other formats:

Written by Andrea Salicetti

La seconda versione beta di Rails 3.0 è stata appena rilasciata. Una delle modifiche più significative di questa release è il modo in cui i blocchi vengono gestiti a livello di viste. In questo episodio daremo uno sguardo a cosa è cambiato.

Aggiornare Ruby e Rails

Prima di aggiornarci alla nuova versione beta di Rails , aggiorniamo la nostra versione di Ruby alla 1.9.2. Nell’episodio 200 [guardalo, leggilo] abbiamo usato rvm per installare Ruby 1.9.1, ma Ruby 1.9.1 è un po’ bacato, mentre la versione 1.9.2 sembra più solida, benchè sia ancora in fase di sviluppo.

Per installare Ruby 1.9.2 con rvm, dobbiamo lanciare questo comando:

rvm install ruby-head

Dal momento che Ruby 1.9.2 non è ancora uscito (nel momento in cui scriviamo), prendiamo l’ultima versione di head. Così facendo, lavoriamo con la versione di sviluppo di Ruby, ma Ruby 1.9.2 è prossimo al rilascio, per cui siamo abbastanza tranquilli sul fatto di usare una versione comunque piuttosto stabile. Una volta che rvm ha installato la nuova versione, possiamo passare a quella:

rvm ruby-head --default

Si noti che è stata usata l’opzione --default per rendere la versione 1.9.2 quella di default. Questo significa che ogni nuovo terminale che apriremo userà Ruby 1.9.2 come interprete Ruby.

Rails 3.0 beta 2 ha bisogno di RubyGems in versione 1.3.6, per cui, prima di installarlo, lanciamo un:

gem -v

per vedere quale versione stiamo usando al momento. Se abbiamo una versione antecedente alla 1.3.6, possiamo lanciare:

gem update --system

per aggiornarla. Ora possiamo installare Rails 3.0 beta 2 con:

gem install rails --pre

Aggiornare le applicazioni

Tutte le applicazioni sviluppate con la prima versione beta di Rails 3.0 possono essere aggiornate semplicemente cambiando il numero di versione all’interno del Gemfile di applicazione da 3.0.0.beta a 3.0.0.beta2:

/Gemfile

gem "rails", "3.0.0.beta2"

Poi possiamo lanciare bundle install per assicurarci che tutte le dipendenze siano risolte.

Cos’è cambiato in erb

Ora che abbiamo installato Rails 3.0 beta 2, cominciamo a guardare cosa è cambiato. Ci sono due tipi di tag usati nei file erb. Se il codice è racchiuso in tag <%= %>, allora qualsiasi cosa sia restituita da tale codice Ruby viene messo in output nella vista. Se si omette il segno uguale, allora il codice è interpretato, ma l’output viene scartato. Questo è un concetto comune in erb e in Rails 3 è stato esteso per funzionare in questo modo all’interno di blocchi.

L’esempio più comune dove ritroviamo un simile comportamento è enlle form. Sotto è riportato un template erb per una form per un modello di tipo Product:

/app/views/products/_form.html.erb

<% form_for @product do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :category_id %><br />
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </p>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :price %><br />
    <%= f.text_field :price %>
  </p>
  <p>
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </p>
  <p><%= f.submit "Submit" %></p>
<% end %>

Nella prima linea di questo codice si usa il form_for. Questo comando inserisce i tag form nella vista attorno al contenuto presente le blocco, ma non c’è alcun segno uguale nel tag erb. Questo stile infrange la regola secondo cui i blocchi erb che emettono dell’output nella vista debbano usare il tag <%= %> ed ha reso difficile lavorare con le parti interne a form_for nelle precedenti versioni di Rails. Dalla nuova versione beta in avanti, comunque, useremo un segno uguale qui, come faremmo in qualunque altro codice erb che genera output:

/app/views/products/_form.html.erb

<%= form_for @product do |f| %>
  <!-- rest of form -->
<% end %>

Anche se stiamo passando un blocco, usiamo ora il tag con l’uguale con il form_for, anche se il tag di chiusura end rimane come prima (senza). Questo codice inserirà il tag form ed il contenuto correttamente. Questa sintassi pulirà anche un po’ il codice all’interno del blocco, come vi mostreremo fra poco.

Potreste domandarvi quando sia necessario usare il segno uguale e quando no. Guardiamo un paio di esempi. Per primo, il div_for:

<%= div_for @product do %>

<% end %>

Nelle prime versioni di Rails non avremmo usato il segno = in questo caso, ma ora è richiesto, in quanto stiamo producendo del contenuto introno al blocco, in questo caso un tag div. Praticamente, la maggior parte degli helpers di Rails ora necessita del segno uguale, perchè producono un qualcosa attorno al blocco che accettano.

Ci sono ancora casi in cui non va messo il segno uguale, comunque. Un esempio è il caso di tutto ciò che usa il metodo each:

<% @comments.each do |c|%>

<% end %>

Il metodo each non produce nulla, di per sè, che si deve mostrare nella vista, per cui questo non necessiterà, come di fatto era già nelle precedenti versioni, del segno di uguale nel tag erb.

Un altro esempio è il metodo content_for:

<% content_for :side do %>

<% end %>

Anche in questo caso il segno uguale non si usa, perchè la chiamata content_for salva il contenuto del blocco in una variabile da usare in un secondo tempo, nel layout. Nulla è prodotto nella vista da questo metodo di per sè, per cui il segno uguale non ci vuole.

Sfortunatamente c’è un’eccezione a questa regola: il metodo cache. Teoricamente cache dovrebbe richiedere il segno uguale dal momento che potrebbe restituire del contenuto alla vista, proveniente dalla cache, ma a causa di come funziona internamente, non va usato:

<% cache do %>

<% end %>

Utilizzo dei blocchi nei metodi helper

In un primo momento tutto ciò può sembrare molto complicato, ma una volta che ci si è abituati al concetto di fondo, fare le modifiche assume un senso ben chiaro. La ragione principale di queste modifiche è che ripuliscono le parti interne del codice. L’episodio 40 ci ha mostrato come usare i blocchi nelle viste nelle prime versioni di Rails. Era poi necessario usare il metodo concat per mettere del testo prima o dopo il blocco con cui era un po’ difficile lavorare:

def admin_area(&block)
  concat('<div class="admin">', block.binding)
  block.call
  concat("</div>", block.binding)
end

Per farvi vedere come sia più semplice usare i blocchi coi metodi helper, ora useremo una semplice applicazione store che ha una serie di collegamenti che dovrebbero essere visibili solo agli amministratori di sistema:

La pagina di dettaglio per un prodotto che mostra i link amministrativi.

Vogliamo che i link “Edit”, “Destroy” e “View All” siano visibili solo agli amministratori e vogliamo anche racchiudere i collegamenti in un div. I link vengono creati nella vista dal seguente codice:

/app/views/products/show.html.erb

<p>
  <%= link_to "Edit", edit_product_path(@product) %> | 
  <%= link_to "Destroy", @product, :confirm => "Are you sure?", :method => :delete %> | 
  <%= link_to "View All", products_path %>
</p>

Cambiamo il tag di paragrafo in cui sono racchiusi i link con un nuovo metodo helper chiamato admin_area tipo questo:

/app/views/products/show.html.erb

<%= admin_area do %>
  <%= link_to "Edit", edit_product_path(@product) %> | 
  <%= link_to "Destroy", @product, :confirm => "Are you sure?", :method => :delete %> | 
  <%= link_to "View All", products_path %>
<% end %>

Il metodo admin_area aggiungerà il tag div e mostrerà o nasconderà i link a seconda del ruolo dell’utente correntemente autenticato. Si noti che dal momento che il nostro metodo helper genera codice HTML sulla vista, abbiamo usato il segno uguale nel suo tag di apertura.

Definiamo il metodo all’interno dell’helper di applicazione. Ciò che è bello di questo nuovo modo di lavorare coi blocchi nei metodi helper è che il tutto si comporta esattamente come ci si aspetterebbe per cui se il metodo restituisce semplicemente una stringa, quella stringa verrà messa così com’è nella vista:

/app/helpers/application_helper.rb

module ApplicationHelper
  def admin_area(&block)
    "OH HAI!"
  end
end

Se ricarichiamo la pagina ora, il contenuto del blocco sarà sostituito dalla stringa che ha restituito il metodo admin_area. I link non vengono mostrati siccome non stiamo eseguendo il blocco da nessuna parte nel metodo:

Il contenuto del blocco è sostituito dalla stringa.

Per eseguire il blocco e restituire il contenuto, dobbiamo chiamare un nuovo metodo chiamato with_output_buffer e passargli il blocco. Questo chiamerà il blocco in un buffer di output separato in modo che il contenuto non sia renderizzato direttamente nella vista. Possiamo assegnare l’output ad una variabile e poi farne ciò che vogliamo. Noi vogliamo racchiudere il contenuto in un elemento div con class di admin, per cui cambieremo il nostro metodo admin_area nel modo seguente:

/app/helpers/application_helper.rb

def admin_area(&block)
  content = with_output_buffer(&block)
  content_tag(:div, content, :class => 'admin')
end

Quando ora ricarichiamo la pagina vediamo i link di amministrazione racchiusi in un div. Poichè abbiamo già creato uno stile per la classe admin nel foglio di stile dell’applicazione, il div avrà quello stile applicato.

Il blocco è ora contenuto in un div.

Guardando al sorgente HTML, possiamo vedere il div intorno ai link.

<div class="admin">
  <a href="/products/1/edit">Edit</a> | 
  <a href="/products/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a> | 
  <a href="/products">View All</a>
</div>

Per cui, se mai avessimo bisogno di caricare i contenuti di un blocco in una vista, potremmo usare il metodo with_output_buffer e passargli il blocco e quello restituirebbe il contenuto del blocco. Detto ciò, il codice che abbiamo scritto nel nostro metodo helper era principalmente dimostrativo ed esiste un modo piùefficiente per fare la stessa cosa.

Il metodo content_tag può ricevere un blocco come argomento e quello gestirà il buffer di output cambiando automaticamente. Questo significa che il nostro metodo helper può essere riscritto così:

/app/helpers/application_helper.rb

def admin_area(&block)
  content_tag(:div, :class => 'admin', &block)
end

Naturalmente, per far sì che il metodo helper funzioni appieno, vorremmo che il tag fosse prodotto sulla vista solo se l’utente è un amministratore, e lo possiamo fare usando un metodo che restituisce un valore booleano a seconda del fatto che l’utente corrente sia amministratore:

/app/helpers/application_helper.rb

def admin_area(&block)
  content_tag(:div, :class => 'admin', &block) if admin?
end

Tutto quello che è stato mostrato è più che sufficiente per questo episodio. Usando il segno uguale con i blocchi nelle viste può inizialmente essere difficile da digerire, ma alla lunga si rivela più sensato, poichè questi blocchi generano dei contenuti. Con una più pulita implementazione dietro le quinte, poi, tutto ciò rappresenta un moglioramento del codice delle viste di Rails.

Come bonus, eccovi un suggerimento extra. Nelle prime versioni di Rails si poteva metter un segno meno all’inizio o alla fine di un tag erb per aiutare a togliere gli spazi in modo che il markup risultasse un po’ più pulito. In Rails 3 questo non è più necessario. Se un tag erb non genera output, viene comunque automaticamente tolto, per cui non ci sono spazi non necessari al posto del contenuto del tag erb.