<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ASCIIcasts - Full Episode Feed</title>
    <description>The latest episodes from ASCIIcasts</description>
    <link>http://asciicasts.com/</link>
    <pubDate>Tue, 27 Jul 2010 11:20:46 +0000</pubDate>
    <ttl>1440</ttl>
    <item>
      <title>Sottodomini in Rails 3</title>
      <description>&lt;p&gt;Sono passati quasi due anni da quando &amp;egrave; stato trattato l&amp;rsquo;argomento &lt;a href="http://railscasts.com/episodes/123-subdomains"&gt;sottodomini&lt;/a&gt;. Rails 3 introduce alcuni supporti ai sottodomini, che vedremo in questo episodio.&lt;/p&gt;

&lt;p&gt;L&amp;rsquo;applicazione che useremo sar&amp;agrave; la solita gi&amp;agrave; usata nell&amp;rsquo;altro episodio sui sottodomini. Si tratta di una semplice applicazione di blogging che supporta pi&amp;ugrave; di un blog. Di sotto &amp;egrave; riportata la home page che elenca tutti i blog. Ciascun blog ha un insieme di articoli ad esso associati.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/408/original/E221I01.png" width="800" height="331" alt="La nostra applicazione di blogging."/&gt;
&lt;/div&gt;

&lt;p&gt;Se clicchiamo sul link di modifica di un blog, vedremo che ciascuno ha due attributi: un &lt;code&gt;name&lt;/code&gt; e un &lt;code&gt;subdomain&lt;/code&gt;. Vogliamo usare l&amp;rsquo;attributo &lt;code&gt;subdomain&lt;/code&gt; come sottodominio nell&amp;rsquo;URL per tale blog:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/409/original/E221I02.png" width="800" height="388" alt="La pagina di edit di un blog."/&gt;
&lt;/div&gt;

&lt;h3&gt;Configurazione dei sottodomini nell&amp;rsquo;ambiente di sviluppo&lt;/h3&gt;

&lt;p&gt;La prima cosa da configurare &amp;egrave; la modalit&amp;agrave; di gestione dei sottodomini nella modalit&amp;agrave; di sviluppo. La nostra applicazione ha l&amp;rsquo;URL &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; che non ci consente di avere sottodomini, per cui dobbiamo trovare un modo alternativo.&lt;/p&gt;

&lt;p&gt;Uno dei modi per farlo potrebbe essere quello di configurare la nostra applicazione per usare &lt;a href="http://www.modrails.com"&gt;Passenger&lt;/a&gt;. Potremmo quindi assegnarle un dominio tipo &lt;code&gt;http://blog.local&lt;/code&gt; e modificare il file &lt;code&gt;/etc/hosts&lt;/code&gt; per configurare i sottodomini in questo modo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/etc/hosts&lt;/p&gt;
&lt;pre class="css"&gt;
127.0.0.1 personal.blog.local company.blog.local
&lt;/pre&gt;

&lt;p&gt;Idealmente vorremmo poter usare un carattere wildcard che faccia match con ciascun sottodominio in nodo tale che non dobbiamo continuamente cambiare il file degli host quando sviluppiamo la nostra applicazione. Sfortunatamente non possiamo farlo, per cui dovremo trovare un&amp;rsquo;altra soluzione.&lt;/p&gt;

&lt;p&gt;Una buona soluzione &amp;egrave; offerta da &lt;a href="http://tbaggery.com/2010/03/04/smack-a-ho-st.html"&gt;Tim Pope sul suo blog&lt;/a&gt;. Lui ha comprato il nome di dominio &lt;code&gt;smackaho.st&lt;/code&gt; e lo ha reso un wildcard del localhost. Uno dei tizi che ha lasciato un commento sul suo blog ha fatto qualcosa del genere con un nome di dominio pi&amp;ugrave; corto, &lt;code&gt;lvh.me&lt;/code&gt;. Useremo questo approccio.&lt;/p&gt;

&lt;p&gt;Se puntiamo a &lt;a href="http://lvh.me:3000/"&gt;http://lvh.me:3000/&lt;/a&gt; vedremo la home page della nostra applicazione, poich&amp;egrave; &lt;code&gt;lvh.me&lt;/code&gt; viene risolto nell&amp;rsquo;indirizzo IP 127.0.0.1. La differenza sta nel fatto che ora possiamo anteposse un qualunque sottodominio che vogliamo all&amp;rsquo;URL e questo continuer&amp;agrave; a puntare alla stessa applicazione.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/410/original/E221I03.png" width="800" height="388" alt="Il blog &amp;egrave; ora disponibile in un sottodominio."/&gt;
&lt;/div&gt;

&lt;p&gt;Ora che siamo in grado di usare i sottodomini nella nostra applicazione, possiamo configurare i suoi instradamenti in modo tale che il sottodominio personal conduca alla action show del blog Personal. Ecco come attualmente appare il nostro file di route:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/routes.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
Bloggit::Application.routes.draw do |map|
  resources :comments
  resources :articles
  resources :blogs
  root :to =&amp;gt; &amp;quot;blogs#index&amp;quot;
end
&lt;/pre&gt;

&lt;p&gt;Per ora l&amp;rsquo;instradamento radice punta alla action &lt;code&gt;blogs/index&lt;/code&gt;. Vogliamo che questo sia vero solo nel caso in cui non sia specificato alcun sottodominio; laddove vi sia indicato un sottodominio, dovrebbe essere mostrata invece la vista associata alla action &lt;code&gt;blogs/show&lt;/code&gt;. In Rails 3 si pu&amp;ograve; fare tutto ci&amp;ograve; aggiungendo un vincolo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/routes.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
Bloggit::Application.routes.draw do |map|
  resources :comments
  resources :articles
  resources :blogs
  match &amp;#x27;/&amp;#x27; =&amp;gt; &amp;#x27;blogs#show&amp;#x27;, :constraints =&amp;gt; { :subdomain =&amp;gt; /.+/ }
  root :to =&amp;gt; &amp;quot;blogs#index&amp;quot;
end
&lt;/pre&gt;

&lt;p&gt;In un&amp;rsquo;applicazione Rails 2 avremmo dovuto usare un plugin per farlo, ma in Rails 3 tutto ci&amp;ograve; &amp;egrave; gi&amp;agrave; incluso nel framework. L&amp;rsquo;opzione &lt;code&gt;:subdomain&lt;/code&gt; accetta sia una string, sia un&amp;rsquo;espressione regolare come argomento: in questo caso abbiamo usato un&amp;rsquo;espressione regolare che facesse match con almeno un carattere, in modo tale che ogni sottodominio facesse match con l&amp;rsquo;instradamento. Si noti come sia essenziale che l&amp;rsquo;instradamento radice venga messo dopo il route di sottodominio. Se cos&amp;igrave; non fosse, l&amp;rsquo;instradamento di root intercetterebbe tutti gli indirizzi di sottodominio per primo, e gli instradamenti ai sottodomini non verrebbero mai chiamati. Come regola aurea generale, pi&amp;ugrave; il route &amp;egrave; specifico, pi&amp;ugrave; deve stare in alto nella lista.&lt;/p&gt;

&lt;p&gt;Se ora visitiamo il sottodominio &lt;code&gt;personal&lt;/code&gt;, otteniamo un errore, ma ce lo aspettavamo, dal momento che stiamo eseguendo al action &lt;code&gt;show&lt;/code&gt; del &lt;code&gt;BlogsController&lt;/code&gt; e non abbiamo fornito un &lt;code&gt;id&lt;/code&gt;:&lt;/p&gt;


&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/411/original/E221I04.png" width="800" height="312" alt="La pagina show solleva un&amp;rsquo;eccezione perch&amp;egrave; il controller si aspetta un ID che non gli abbiamo fornito."/&gt;
&lt;/div&gt;

&lt;p&gt;La cosa &amp;egrave; semplice da sistemare. Dobbiamo solo modificare la action &lt;code&gt;show&lt;/code&gt; in modo tale che trovi un blog in base al sottodominio piuttosto che per &lt;code&gt;id&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/blogs_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def show
  @blog = Blog.find_by_subdomain!(request.subdomain)
end
&lt;/pre&gt;

&lt;p&gt;Quando adesso ricarichiamo la pagina, vediamo gli articoli del nostro blog &amp;ldquo;Personal&amp;rdquo;:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/412/original/E221I05.png" width="815" height="427" alt="Il blog &amp;egrave; ora rintracciato per sottodominio."/&gt;
&lt;/div&gt;

&lt;p&gt;Se togliamo il sottodominio, vedremo la home page che avevamo in precedenza:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/413/original/E221I06.png" width="810" height="341" alt="Senza sottodomini, viene mostrata la home page."/&gt;
&lt;/div&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; ancora un piccolo problema con gli instradamenti, tuttavia. Se andiamo su &lt;a href="http://www.lvh.me:3000"&gt;http://www.lvh.me:3000&lt;/a&gt;, la nostra applicazione cercher&amp;agrave; un blog di sottodominio &lt;code&gt;www&lt;/code&gt;, mentre invece in questo caso dovrebbe puntare alla pagina index dei blog. Potremmo provare a correggere questa cosa agendo in qualche maniera furba sull&amp;rsquo;espressione regolare nei vincoli presenti nel file di routes, ma invece, al fine di concederci una maggior flessibilit&amp;agrave; e controllo sui sottodomini, faremo la stessa cosa nel codice Ruby. Tutto ci&amp;ograve; &amp;egrave; reso possibile da Rails 3, scrivendo una classe e spostando il codice coi vincoli l&amp;igrave; dentro.&lt;/p&gt;

&lt;p&gt;Per prima cosa cambieremo il file degli instradamenti affinch&amp;egrave; utilizzi una nuova classe chiamata &lt;code&gt;Subdomain&lt;/code&gt;. Facciamo ci&amp;ograve;, usando il metodo &lt;code&gt;constraints&lt;/code&gt; e passandogli la classe come argomento:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/routes.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
Bloggit::Application.routes.draw do |map|
  resources :comments
  resources :articles
  resources :blogs
  constraints(Subdomain) do
    match &amp;#x27;/&amp;#x27; =&amp;gt; &amp;#x27;blogs#show&amp;#x27;  
  end
  root :to =&amp;gt; &amp;quot;blogs#index&amp;quot;
end
&lt;/pre&gt;

&lt;p&gt;Poi dobbiamo creare la classe &lt;code&gt;Subdomain&lt;/code&gt; che andr&amp;agrave; nella cartella &lt;code&gt;/lib&lt;/code&gt;. Questa classe dovr&amp;agrave; avere un metodo di classe denominato &lt;code&gt;matches?&lt;/code&gt; che accetta un oggetto &lt;code&gt;request&lt;/code&gt; per parametro. Questo oggetto request &amp;egrave; esattamente lo stesso oggetto che accediamo nei nostri controller e nelle nostre viste, per cui su di esso possiamo usare gli stessi metodi che usiamo in quei contesti. Questo metodo deve restituire un valore booleano che indica se il route dato fa match con la richiesta o meno. Nel nostro caso vogliamo che il metodo restituisca &lt;code&gt;true&lt;/code&gt; solo se la request ha un sottodominio e tale sottodominio non &amp;egrave; &lt;code&gt;www&lt;/code&gt;, per cui la nostra classe sar&amp;agrave; cos&amp;igrave; fatta:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/subdomain.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Subdomain
  def self.matches?(request)
    request.subdomain.present? &amp;amp;&amp;amp; request.subdomain != &amp;#x27;www&amp;#x27;
  end
end
&lt;/pre&gt;

&lt;p&gt;Quando ora visitiamo &lt;a href="http://www.lvh.me:3000"&gt;http://www.lvh.me:3000&lt;/a&gt;, vediamo la home page proprio come volevamo:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/414/original/E221I07.png" width="800" height="333" alt="Il sottodominio www ora mostra la home page."/&gt;
&lt;/div&gt;

&lt;h3&gt;Correggere i link&lt;/h3&gt;

&lt;p&gt;Ora sistemeremo i collegamenti a ciascun blog nella home page in modo tale che puntino al sottodominio corretto anzich&amp;egrave; alla normale action &lt;code&gt;show&lt;/code&gt; per tale blog, dal momento che se cos&amp;igrave; fosse, verrebbe fuori un errore causato dal fatto che ora la action show si aspetta un sottodominio.&lt;/p&gt;

&lt;p&gt;Nel codice della vista per l&amp;rsquo;action &lt;code&gt;index&lt;/code&gt; abbiamo un link standard a ciascun blog fra tag &lt;code&gt;h2&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/blogs/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;Blogs&amp;quot; %&amp;gt;

&amp;lt;% for blog in @blogs %&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;h2&amp;gt;&amp;lt;%= link_to blog.name, blog %&amp;gt;&amp;lt;/h2&amp;gt;
    &amp;lt;div class=&amp;quot;actions&amp;quot;&amp;gt;
      &amp;lt;%= link_to &amp;quot;Edit&amp;quot;, edit_blog_path(blog) %&amp;gt; | 
      &amp;lt;%= link_to &amp;quot;Destroy&amp;quot;, blog, :confirm =&amp;gt; &amp;#x27;Are you sure?&amp;#x27;, :method =&amp;gt; :delete %&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Cambiamo questo link affinch&amp;egrave; punti all&amp;rsquo;URL radice con l&amp;rsquo;opportuno sottodominio. Sfortunatamente non possiamo passare una option &lt;code&gt;subdomain&lt;/code&gt; al metodo &lt;code&gt;root_url&lt;/code&gt; come potremmo fare con subdomain_fu. In questo caso dobbiamo creare il nome dell&amp;rsquo;host completo da zero e includere in quello il sottodominio. Il nome dell&amp;rsquo;host sar&amp;agrave; creato a partire dal &lt;code&gt;subdomain&lt;/code&gt; del blog, dal &lt;code&gt;domain&lt;/code&gt; corrente e dalle propriet&amp;agrave; &lt;code&gt;port_string&lt;/code&gt; dell&amp;rsquo;oggetto &lt;code&gt;request&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/blogs/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;&amp;lt;%= link_to blog.name, root_url(:host =&amp;gt; blog.subdomain + &amp;#x27;.&amp;#x27; + request.domain + request.port_string) %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Al ricaricamento della pagina principale, ora i link ai blog punteranno ai sottodomini corretti.&lt;/p&gt;

&lt;h3&gt;Ripulire il codice per cambiare il sottodominio&lt;/h3&gt;

&lt;p&gt;Ora tutto funziona bene, ma il codice riportato sopra che crea i link ad altri sottodomini potrebbe essere un po&amp;rsquo; pi&amp;ugrave; pulito, specialmente se dovessimo usarlo parecchio. Per ripulire il tutto, spostiamo la logica in un metodo helper a parte che chiamiamo &lt;code&gt;with_subdomain&lt;/code&gt; e che accetta il &lt;code&gt;subdomain&lt;/code&gt; come argomento. Innanzitutto cambiamo il codice della vista, affinch&amp;egrave; chiami il metodo che stiamo per implementare:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/blogs/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;&amp;lt;%= link_to blog.name, root_url(:host =&amp;gt; with_subdomain(blog.subdomain)) %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Mettiamo il metodo helper dentro il proprio module, in un file chiamato &lt;code&gt;url_helper.rb&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/helpers/url_helper.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
module UrlHelper
  def with_subdomain(subdomain)
    subdomain = (subdomain || &amp;quot;&amp;quot;)
    subdomain += &amp;quot;.&amp;quot; unless subdomain.empty?
    [subdomain, request.domain, request.port_string].join
  end
end
&lt;/pre&gt;

&lt;p&gt;Il codice nel module per prima cosa imposta il &lt;code&gt;subdomain&lt;/code&gt; ad una stringa vuota, se il valore passato &amp;egrave; &lt;code&gt;nil&lt;/code&gt;, poi gli aggiunge un punto, nel caso in cui &lt;code&gt;subdomain&lt;/code&gt; non sia una stringa vuota. Infine concatena &lt;code&gt;subdomain&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt; e &lt;code&gt;port_string&lt;/code&gt; e restituisce il valore.&lt;/p&gt;

&lt;p&gt;Aggiungiamo il module anche all&amp;rsquo;&lt;code&gt;ApplicationController&lt;/code&gt;, in modo tale che tutti i nostri controller possano utilizzare questo metodo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/application_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class ApplicationController &amp;lt; ActionController::Base
  include UrlHelper
  protect_from_forgery
  layout &amp;#x27;application&amp;#x27;
end
&lt;/pre&gt;

&lt;p&gt;Anche se abbiamo ripulito abbastanza il codice della vista, potrebbe esserlo ancor di pi&amp;ugrave; se potessimo semplicemente aggiungere un&amp;rsquo;opzione &lt;code&gt;:subdomain&lt;/code&gt; al metodo &lt;code&gt;root_url&lt;/code&gt; e ci&amp;ograve; &amp;egrave; possibile se ridefiniamo il metodo &lt;code&gt;url_for&lt;/code&gt;. Tutto ci&amp;ograve; che dobbiamo fare &amp;egrave; aggiungere il seguente metodo al nostro module &lt;code&gt;UrlHelper&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/helpers/url_helper.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def url_for(options = nil)
  if options.kind_of?(Hash) &amp;amp;&amp;amp; options.has_key?(:subdomain)
    options[:host] = with_subdomain(options.delete(:subdomain))
  end
  super
end
&lt;/pre&gt;

&lt;p&gt;Il metodo ridefinito &lt;code&gt;url_for&lt;/code&gt; controlla per vedere se l&amp;rsquo;hash &lt;code&gt;options&lt;/code&gt; ha una chiave denominata &lt;code&gt;:subdomain&lt;/code&gt; al suo interno e, se cos&amp;igrave;, imposta l&amp;rsquo;opzione &lt;code&gt;:host&lt;/code&gt; ad essere il valore di &lt;code&gt;with_subdomain&lt;/code&gt; per tale sottodominio. Infine chiama la &lt;code&gt;super&lt;/code&gt;, in modo tale che tutto il codice di default del metodo originale si eseguito e la parte restante dell&amp;rsquo;URL sia generata opportunamente. Non c&amp;rsquo;&amp;egrave; bisogno di usare &lt;code&gt;alias_method_chain&lt;/code&gt; in questo caso, chiamare la &lt;code&gt;super&lt;/code&gt; &amp;egrave; sufficiente.&lt;/p&gt;

&lt;p&gt;Ora possiamo aggiornare il codice della nostra vista per usare la option &lt;code&gt;:subdomain&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/blogs/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;&amp;lt;%= link_to blog.name, root_url(:subdomain =&amp;gt; blog.subdomain) %&amp;gt;&amp;lt;/h2&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Al ricaricamento della pagina index, cliccando su uno dei link ai blog il link condurra ancora all&amp;rsquo;URL corretto con il giusto sottodominio:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/415/original/E221I08.png" width="801" height="338" alt="L&amp;rsquo;opzione :subdomain ora funziona."/&gt;
&lt;/div&gt;

&lt;p&gt;Sarebbe bello avere un link su ogni blog che potesse riportare alla pagina index. Possiamo aggiungerlo invocando &lt;code&gt;root_url&lt;/code&gt; con un valore false per l&amp;rsquo;opzione &lt;code&gt;:subdomain&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/blogs/show.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;All Blogs&amp;quot;, root_url(:subdomain =&amp;gt; false) %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Ci&amp;ograve; ci dar&amp;agrave; il link alla pagina index che volevamo:&lt;/p&gt;

&lt;h3&gt;Gestire domini di primo livello diversi&lt;/h3&gt;

&lt;p&gt;Un aspetto che non abbiamo ancora affrontato &amp;egrave; come gestire nomi di dominio composti da pi&amp;ugrave; di due parti. Ebbene, anche se il nostro codice per il sottodominio funzioner&amp;agrave; per i domini .com, non funzioner&amp;agrave; invece per i domini che finiscono, per esempio, per .co.uk. Per far s&amp;igrave; che funzioni anche per questi ultimi, dobbiamo cambiare la nostra applicazione ovunque chiami &lt;code&gt;request.domain&lt;/code&gt; o &lt;code&gt;request.subdomain&lt;/code&gt; affinch&amp;egrave; possiamo indicare il numero di punti che ha il nome di dominio (al netto dei sottodomini). Rails assume un valore di default di 1, ma come appena citato per i domini del tipo .co.uk dobbiamo impostare tale valore a 2.&lt;/p&gt;

&lt;p&gt;Dobbiamo cambiare la nostra applicazione in due punti: nel metodo &lt;code&gt;UrlHelper&lt;/code&gt; e nella classe &lt;code&gt;Subdomain&lt;/code&gt; usata nei nostri instradamenti:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/helpers/url_helper.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def with_subdomain(subdomain)
  subdomain = (subdomain || &amp;quot;&amp;quot;)
  subdomain += &amp;quot;.&amp;quot; unless subdomain.empty?
  [subdomain, request.domain(2), request.port_string].join
end
&lt;/pre&gt;  

&lt;p class="codeFilePath"&gt;/lib/subdomains.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Subdomain
  def self.matches?(request)
    request.subdomain(2).present? &amp;amp;&amp;amp; request.subdomain(2) != &amp;quot;www&amp;quot;
  end
end
&lt;/pre&gt;

&lt;p&gt;Ovviamente non vogliamo veramente cablare tale valore nel codice. Lo dovremo realisticamente rendere dinamico in modo tale che in modalit&amp;agrave; di sviluppo possiamo impostare un valore di 1 (per un dominio del tipo &lt;code&gt;lvh.me&lt;/code&gt;) mentre in produzione useremo 2. Questo valore pu&amp;ograve; essere impostato in un file di configurazione esterno che carica l&amp;rsquo;ambiente in modo opportuno.&lt;/p&gt;

&lt;h3&gt;Cookie&lt;/h3&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; un&amp;rsquo;ultima questione che affronteremo in questo episodio. Se osserviamo i cookie presenti nel nostro browser e li filtriamo per il nome di dominio che abbiamo utilizzato per questa applicazione, vedremo che viene salvata una sessione diversa per ciascun sottodominio visitato. Non vogliamo che avvenga questo, perch&amp;egrave; significa che le sessioni non sono condivise fra i vari sottodomini.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/416/original/E221I09.png" width="805" height="246" alt="Ciascun sottodominio ha un cookie proprio."/&gt;
&lt;/div&gt;

&lt;p&gt;Una soluzione a questo problema &amp;egrave; stata presentata nell&amp;rsquo;episodio 123, ma ora in Rails 3 c&amp;rsquo;&amp;egrave; un modo migliore per risolvere lo stesso problema. Nel file &lt;code&gt;/config/initializers/session_store.rb&lt;/code&gt; dobbiamo semplicemente aggiungere l&amp;rsquo;opzione &lt;code&gt;:domain&lt;/code&gt; al metodo &lt;code&gt;Rails.application.config.session_store&lt;/code&gt;, dandogli come valore &lt;code&gt;:all&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/initializers/sesion_store.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
Rails.application.config.session_store :cookie_store, :key =&amp;gt; &amp;#x27;_bloggit_session&amp;#x27;, :domain =&amp;gt; :all
&lt;/pre&gt;

&lt;p&gt;Tuttavia non &amp;egrave; sufficiente questo intervento da solo. L&amp;rsquo;opzione &lt;code&gt;:domain&lt;/code&gt; &amp;egrave; stata aggiunta solo dopo l&amp;rsquo;uscita di Rails 3.0 beta 4, che nel momento in cui si sta scrivendo questo episodio &amp;egrave; anche la versione corrente. Pertanto occorre lanciare l&amp;rsquo;applicazione su Edge Rails oppure attendere la prossima release candidate per vedere il tutto funzionare correttamente. Usata con una versione di Rails che la supporta, questa opzione rende le sessioni della nostra applicazione condivise fra i sottodomini. L&amp;rsquo;opzione &lt;code&gt;:all&lt;/code&gt; assume che la nostra applicazione abbia un dominio di primo livello di dimensione 1. Se cos&amp;igrave; non fosse, potremmo definire un nome di dominio al posto di :all e quest&amp;rsquo;ultimo verrebbe utilizzato come dominio base per la sessione.&lt;/p&gt; 

&lt;p&gt;E&amp;rsquo; tutto per questo episodio. Poter gestire sottodomini senza la necessit&amp;agrave; di installare plugin di terze parti &amp;egrave; una bella novit&amp;agrave; di Rails 3 e pu&amp;ograve; essere utilizzata in vari modi nelle nostre applicazioni.&lt;/p&gt;</description>
      <pubDate>Fri, 23 Jul 2010 20:25:03 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/221-sottodomini-in-rails-3</guid>
      <link>http://it.asciicasts.com/episodes/221-sottodomini-in-rails-3</link>
    </item>
    <item>
      <title>PDFKit</title>
      <description>&lt;p&gt;Ci sono una serie di buone librerie disponibili per generare file PDF in Ruby. Una delle pi&amp;ugrave; popolari &amp;egrave; &lt;a href="http://prawn.majesticseacreature.com/"&gt;Prawn&lt;/a&gt;, che &amp;egrave; sicuramente un ottimo modo per generare PDF da zero in Ruby e che &amp;egrave; stato trattato nell&amp;rsquo;episodio 153 [&lt;a href="http://railscasts.com/episodes/153-pdfs-with-prawn"&gt;guardalo&lt;/a&gt;, &lt;a href="http://asciicasts.com/episodes/153-pdfs-with-prawn"&gt;leggilo&lt;/a&gt;]. In alcune situazioni, tuttavia, pu&amp;ograve; essere pi&amp;ugrave; semplice generare un PDF da un documento HTML gi&amp;agrave; esistente, ed &amp;egrave; ci&amp;ograve; che faremo vedere in questo episodio.&lt;/p&gt;

&lt;p&gt;La creazione di documenti PDF a partire dall&amp;rsquo;HTML non &amp;egrave; un&amp;rsquo;idea nuova, ma fino ad ora la maggior parte delle soluzioni disponibili era a pagamento. Di recente c&amp;rsquo;&amp;egrave; stata parecchia attivit&amp;agrave; sull&amp;rsquo;argomento, grazie ad una soluzione proposta da &lt;a href="http://thinkrelevance.com/blog/2010/06/15/rethinking-pdf-creation-in-ruby.html"&gt;Jared Pace&lt;/a&gt; e del suo nuovo &lt;a href="http://github.com/jdpace/PDFKit"&gt;PDFKit gem&lt;/a&gt;. Questo gem dipende da &lt;a href="http://code.google.com/p/wkhtmltopdf/"&gt;wkhtmltopdf&lt;/a&gt;, uno strumento che usa il motore di rendering WebKit per generare documenti PDF, per cui, per installare PDFkit, dovrete innanzitutto installare wkhtmltopdf. PDFKit &amp;egrave; distribuito in bundle con wkhtmltopdf, per cui, a seconda del vostro ambiente, ci&amp;ograve; potrebbe non essere necessario.&lt;/p&gt;

&lt;p&gt;Il gem PDFKit si installa al solito modo:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ gem install pdfkit
&lt;/pre&gt;

&lt;p&gt;Una volta installato, PDFKit &amp;egrave; in grado di generare un file PDF puntando semplicemente ad un file o ad un sito web. In alternativa, il gem viene distribuito anche come middleware rack, una soluzione che pu&amp;ograve; essere sfuttata per generare PDF di qualsiasi pagina di un sito, semplicemente aggiungendo &lt;code&gt;.pdf&lt;/code&gt; all&amp;rsquo; URL. Useremo questo approccio a middleware nella nostra applicazione dimostrativa.&lt;/p&gt;

&lt;h3&gt;Creazione di fatture in PDF&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;applicazione che useremo &amp;egrave; la stessa gi&amp;agrave; usata nell&amp;rsquo;episodio su Prawn. Di sotto &amp;egrave; mostrata la pagina dell&amp;rsquo;ordine di una semplice applicazione di e-commerce. Vogliamo aggiungere un link a questa pagina che permetta di avere una versione PDF dell&amp;rsquo;ordine da scaricare e useremo PDFKit per farlo.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/403/original/E220I01.png" width="800" height="390" alt="La pagina dell&amp;rsquo;ordine."/&gt;
&lt;/div&gt;

&lt;p&gt;La prima cosa da fare &amp;egrave; aggiungere un riferimento a PDFkit nella nostra applicazione. Dal momento che si tratta di una applicazione Rails 3, modifichiamo il &lt;code&gt;Gemfile&lt;/code&gt; per farlo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/Gemfile&lt;/p&gt;
&lt;pre class="ruby"&gt;
gem &amp;quot;pdfkit&amp;quot;
&lt;/pre&gt;

&lt;p&gt;Poi, per assicurarci che il gem venga installato, lanciamo:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ bundle install
&lt;/pre&gt;

&lt;p&gt;Poi dobbiamo aggiungere il middleware. In un&amp;rsquo;applicazione Rails 3, questo lo si fa nel file &lt;code&gt;/config/application.rb&lt;/code&gt;. (Se avessimo dovuto farlo in un&amp;rsquo;applicazione Rails 2, invece, saremmo dovuti andare nel file di configurazione dell&amp;rsquo;ambiente.) Modifichiamo il file affinch&amp;egrave; l&amp;rsquo;applicazione usi il middleware PDFKit:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
require File.expand_path(&amp;#x27;../boot&amp;#x27;, __FILE__)

require &amp;#x27;rails/all&amp;#x27;

# Auto-require default libraries and those for the current Rails environment.
Bundler.require :default, Rails.env

module Store
  class Application &amp;lt; Rails::Application
    config.secret_token = &amp;#x27;6f22fa632e18b338b4babfa5fca632f5454fc97317cb52f372fa0f0fdd7f4d5bd95a060ff412c7230627b5c17906c9762c09208624bc1ab97f8d5344d8d4f467&amp;#x27;
    config.filter_parameters &amp;lt;&amp;lt; :password
    config.middleware.use &amp;quot;PDFKit::Middleware&amp;quot;
  end
end
&lt;/pre&gt;

&lt;p&gt;Per vedere quali middleware sta usando la nostra applicazione, possiamo lanciare il comando &lt;code&gt;rake middleware&lt;/code&gt; come facciamo ora per sincerarci che il nostro PDFKit sia nella lista dei middleware:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rake middleware
(in /Users/asalicetti/rails/apps_for_asciicasts/ep220/store)
use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use PDFKit::Middleware
run Store::Application.routes
&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;PDFKit::Middleware&lt;/code&gt; &amp;egrave; presente nell&amp;rsquo;elenco, per cui possiamo andare avanti. Ora possiamo aggiungere &lt;code&gt;.pdf&lt;/code&gt; a qualunque URL della nostra applicazione per ottenere una versione PDF di quella pagina. Proviamolo nella pagina degli ordini:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/404/original/E220I02.png" width="810" height="368" alt="La versione PDF dell&amp;rsquo;ordine."/&gt;
&lt;/div&gt;

&lt;p&gt;Tutto qua. Per ora non abbiamo dovuto fare praticamente nulla per ottenere una versione PDF dell&amp;rsquo;ordine. Potrebbe essere prodotta meglio, certamente, ma comunque abbiamo gi&amp;agrave; una buona base da cui partire.&lt;/p&gt;

&lt;h3&gt;Miglioriamo l&amp;rsquo;aspetto dell&amp;rsquo;ordine in PDF&lt;/h3&gt;

&lt;p&gt;La prima modifica che faremo all&amp;rsquo;ordine sar&amp;agrave; rimuovere lo sfondo blu dalla versione PDF. Dobbiamo poter identificare una versione della risorsa in PDF, in modo tale da poter applicargli uno stile diverso. Possiamo fare tutto ci&amp;ograve; cambiando la chiamata al middleware, affinch&amp;egrave; PDFKit utilizzi il media type print:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
config.middleware.use &amp;quot;PDFKit::Middleware&amp;quot;, :print_media_type =&amp;gt; true
&lt;/pre&gt;

&lt;p&gt;Nel file di layout della nostra applicazione abbiamo un &lt;code&gt;stylesheet_link_tag&lt;/code&gt; che include un tag &lt;code&gt;application.css&lt;/code&gt;. Per default questo foglio di stile ha un media type pari a &lt;code&gt;screen&lt;/code&gt;, che indica che, viste le recenti modifiche al middleware PDFKit, nessuno degli stili presenti in tale foglio di stile verr&amp;agrave; applicato alla versione PDF. Modifichiamo il &lt;code&gt;stylesheet_link_tag&lt;/code&gt; e impostiampogli &lt;code&gt;all&lt;/code&gt; come media type, affinch&amp;egrave; tutti gli stili vengano applicati sia alla versione della pagina da monitor (HTML), sia a quella da stampa:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/application/views/layouts/application.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;%= stylesheet_link_tag &amp;quot;application&amp;quot;, :media =&amp;gt; &amp;#x27;all&amp;#x27; %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Potremmo creare (sarebbe meglio dal punto di vista dell&amp;rsquo;ordine) un foglio di stile a parte per il formato da stampa, ma in questo episodio includiamo le regole per il print media nello stesso foglio di stile contenente anche gli altri stili CSS. In fondo a questo file, possiamo aggiungere una regola media type e qualsiasi regola definita all&amp;rsquo;interno di questa sezione si applicher&amp;agrave; esclusivamente ai PDF. Per rimuovere lo sfondo blu dalla versione PDF, aggiungiamo il seguente CSS in fondo al foglio di stile della nostra applicazione:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/public/stylesheets/application.css&lt;/p&gt;
&lt;pre class="css"&gt;
@media print {
	body { background-color: #FFF; }
  #container {
		width: auto;
		margin: 0;
		padding: 0;
		border: none;
	}
}
&lt;/pre&gt;

&lt;p&gt;Al ricaricamento del PDF del nostro ordine, ora lo sfondo blu sar&amp;agrave; scomparso.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/405/original/E220I03.png" width="800" height="435" alt="Lo sfondo blu se n&amp;rsquo;&amp;egrave; andato."/&gt;
&lt;/div&gt;

&lt;p&gt;Questo PDF &amp;egrave; ora molto simile a quello creato nell&amp;rsquo;episodio su Prawn, ma abbiamo dovuto scrivere molto meno codice per ottenere lo stesso risultato che non se avessimo fatto la stessa cosa con Prawn. Prawn presenta ancora dei vantaggi, comunque, dal momento che ci permette di avere molto pi&amp;ugrave; controllo sul modo in cui viene generato il PDF, in particolare quando si prova a creare documenti multi pagina. Se la tabella degli ordini dovesse debordare su pi&amp;ugrave; pagine, cominceremmo ad avere delle difficolt&amp;agrave; a controllare il modo in cui le intestazioni e i pi&amp;egrave; di pagina vengono mostrati su ciascuna pagina.&lt;/p&gt;

&lt;h3&gt;Controllare le interruzioni di pagina&lt;/h3&gt;

&lt;p&gt;PDFkit ci fornisce un po&amp;rsquo; di controllo sulle interruzioni di pagina. Aggiungendo un paio di paragrafi di testo in cima all&amp;rsquo;ordine, in modo tale da far slittare in basso la tabella sottostante, vedremo che quest&amp;rsquo;ultima viene divisa su due pagine:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/406/original/E220I04.png" width="800" height="435" alt="La tabella &amp;egrave; stata divisa sulle due pagine."/&gt;
&lt;/div&gt;

&lt;p&gt;Possiamo evitare questo comportamento semplicemente agendo sugli stili CSS, in modo da fare aggiungere un&amp;rsquo;interruzione di pagina subito prima della tabella, cos&amp;igrave; che questa appaia sempre in cima a una nuova pagina. Usiamo la regola &lt;code&gt;page-break-before&lt;/code&gt; per ottenere questo risultato:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/public/stylesheets/application.css&lt;/p&gt;
&lt;pre class="css"&gt;
@media print {
	body { background-color: #FFF; }
  #container {
		width: auto;
		margin: 0;
		padding: 0;
		border: none;
	}
	
	#line_items {
		page-break-before: always;
	}
}
&lt;/pre&gt;

&lt;p&gt;Quando ricarichiamo il documento PDF, la tabella apparir&amp;agrave;, come volevamo, in una pagina a parte:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/407/original/E220I05.png" width="802" height="490" alt="L&amp;rsquo;interruzione di pagina ora garantisce che la tabella non venga divisa."/&gt;
&lt;/div&gt;

&lt;p&gt;Dunque, abbiamo un minimo di controllo sulle interruzioni di pagina con PDFkit, ma per la generazione di documenti pi&amp;ugrave; complessi &amp;egrave; meglio affidarsi a Prawn.&lt;/p&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; un&amp;rsquo;ultima modifica che vogliamo fare al documento dell&amp;rsquo;ordine per concludere questo episodio. Aggiungiamo un link in fondo alla pagina HTML dell&amp;rsquo;ordine che punti alla versione PDF. Per prima cosa aggiungiamo un link al file PDF in fondo alla pagina:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/show.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Download Invoice (PDF)&amp;quot;, order_path(@order, :format =&amp;gt; &amp;quot;pdf&amp;quot;) %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Questa modifica ci fornisce un link dalla maschera HTML al corrispettivo PDF, ma il link comparir&amp;agrave; erroneamente anche nel file PDF. Possiamo nasconderlo sul PDF, fornendo un id all&amp;rsquo;elemento paragrafo che contiene il link:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/show.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;p id=&amp;quot;pdf_link&amp;quot;&amp;gt;&amp;lt;%= link_to &amp;quot;Download Invoice (PDF)&amp;quot;, order_path(@order, :format =&amp;gt; &amp;quot;pdf&amp;quot;) %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;e modificando il foglio di stile dell&amp;rsquo;applicazione in modo tale da nascondere il collegamento alla versione PDF:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/public/stylesheets/application.css&lt;/p&gt;
&lt;pre class="css"&gt;
@media print {
	body { background-color: #FFF; }
  #container {
		width: auto;
		margin: 0;
		padding: 0;
		border: none;
	}
	
	#line_items {
		page-break-before: always;
	}
	
	#pdf_link {
		display: none;
	}
}
&lt;/pre&gt;

&lt;p&gt;Ora la versione HTML dell&amp;rsquo;ordine mostrer&amp;agrave; il link alla versione PDF, ma in quest&amp;rsquo;ultima non comparir&amp;agrave; pi&amp;ugrave;, come desideravamo.&lt;/p&gt;

&lt;h3&gt;Una alternativa&lt;/h3&gt;

&lt;p&gt;Prima di chiudere l&amp;rsquo;episodio, citiamo un&amp;rsquo;alternativa a PDFkit, &lt;a href="http://github.com/mileszs/wicked_pdf"&gt;Wicked PDF&lt;/a&gt;. Questo plugin usa un approccio diverso rispetto a quello adottato da PDFkit, pur usando anch&amp;rsquo;esso wkhtmltopdf. Wicked PDF non &amp;egrave; un middleware, ma usa un renderer Rails. Wicked PDF ci fornisce un maggiore controllo su quali action possano essere renderizzate in formato PDF, per cui vale la pena di tenerlo in considerazione in una eventuale scelta.&lt;/p&gt;  

&lt;p&gt;E&amp;rsquo; tutto per questo episodio. Abbiamo a questo punto trattato due metodologie piuttosto diverse fra loro per la generazione di documenti PDF all&amp;rsquo;interno di applicazioni Rails, ciascuna con i propri pro e contro. Sebbene PDFkit sia uno strumento estremamente semplice per la generazione di PDF a partire dall&amp;rsquo;HTML, Prawn ci fornisce di contro un maggior controllo al prezzo della minor semplicit&amp;agrave; d&amp;rsquo;uso. Entrambi gli approcci hanno il loro dominio applicativo e meritano pertanto di essere parimenti presi in considerazione.&lt;/p&gt;</description>
      <pubDate>Sun, 18 Jul 2010 20:29:36 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/220-pdfkit</guid>
      <link>http://it.asciicasts.com/episodes/220-pdfkit</link>
    </item>
    <item>
      <title>Active Model</title>
      <description>&lt;p&gt;L&amp;rsquo;episodio 193 [&lt;a href="http://railscasts.com/episodes/193-tableless-model"&gt;guardalo&lt;/a&gt;, &lt;a href="http://it.asciicasts.com/episodes/193-modelli-non-persistenti"&gt;leggilo&lt;/a&gt;] ha trattato i modelli completamente non persistenti. In quell&amp;rsquo;episodio abbiamo creato un modello che usava alcune delle funzionalit&amp;agrave; di ActiveRecord, ma che non aveva una tabella sul database corrispondente alle spalle. La tecnica utilizzata era un po&amp;rsquo; forzata, dal momento che si tentava di fare un qualcosa per cui ActiveRecord non era stato progettato, ma ora, in Rails 3.0, abbiamo a disposizione una nuova funzionalit&amp;agrave; chiamata ActiveModel, che rende molto pi&amp;ugrave; semplice realizzare cose del genere.&lt;/p&gt;

&lt;p&gt;Prima di entrare nei dettagli di ActiveModel, diamo una breve descrizione della parte dell&amp;rsquo;applicazione che cambieremo per usarlo.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/399/original/E219I01.png" width="801" height="465" alt="La from per la creazione di un nuovo messaggio."/&gt;
&lt;/div&gt;

&lt;p&gt;Lo screenshot di sopra mostra una form per l&amp;rsquo;inserimento di un contatto, creata usando lo scaffolding. L&amp;rsquo;applicazione ha un modello chiamato &lt;code&gt;Message&lt;/code&gt; che attualmente viene reso persistente da ActiveRecord, il che significa che stiamo gestendo i messaggi attraverso il database. Cambiamo il modo in cui funziona questa form, in modo tale che invii semplicemente le email e non salvi i messaggi su una tabella del database.&lt;/p&gt;

&lt;p&gt;Quando si pensa di fare una cosa del genere, &amp;egrave; sempre una buona idea analizzare preventivamente i requisiti ed assicurarsi che non si voglia realmente salvare alcun dato della form sul database, poch&amp;egrave; spesso ci sono degli effetti collaterali nel fare in questo modo. Un database pu&amp;ograve; fare da backup e rende anche pi&amp;ugrave; semplice lo spostamento dei messaggi inviati in una coda di un processo di background. Per gli scopi di questo esempio, tuttavia, non vogliamo nessuna di quelle funzionalit&amp;agrave;, per cui siamo liberi di andare avanti e crearci il nostro modello non persistente.&lt;/p&gt;

&lt;p&gt;Il codice della classe del messaggio appare cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/message.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Message &amp;lt; ActiveRecord::Base
 validates_presence_of :name
 validates_format_of :email, :with =&amp;gt; /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
 validates_length_of :content, :maximum =&amp;gt; 500
end
&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;Message&lt;/code&gt; eredita da &lt;code&gt;ActiveRecord::Base&lt;/code&gt; come ci aspetteremmo, ma dal momento che non vogliamo che questo modello abbia un corrispettivo sul database di backend, rimuoviamo questa discendenza. Nel momento in cui facciamo questa modifica, tuttavia, la nostra form smetter&amp;agrave; di funzionare, poich&amp;egrave; i validatori sono forniti da ActiveRecord. Fortunatamente, possiamo ripristinare questa funzionalit&amp;agrave; usando ActiveModel.&lt;/p&gt;

&lt;p&gt;Se diamo un&amp;rsquo;occhiata al &lt;a href="http://github.com/rails/rails/"&gt;codice sorgente di Rails 3&lt;/a&gt;, vedremo che ci sono le cartelle &lt;code&gt;activerecord&lt;/code&gt; e &lt;code&gt;activemodel&lt;/code&gt;. Il team di sviluppo del core di Rails ha estratto da ActiveRecord tutto ci&amp;ograve; che non fosse prettamente legato al database e l&amp;rsquo;ha spostato in ActiveModel. ActiveRecord continua ancora ad affidarsi pesantemente su ActiveModel per tutte le funzionalit&amp;agrave; che non sono specifiche del database e dal momento che ActiveModel &amp;egrave; funzionalmente completo e ben testato, &amp;egrave; adatto anche ad essere utilizzato al di fuori di ActiveRecord.&lt;/p&gt;

&lt;p&gt;Se diamo un&amp;rsquo;occhiata alla cartella che contiene il &lt;a href="http://github.com/rails/rails/tree/master/activemodel/lib/active_model/"&gt;codice di ActiveModel&lt;/a&gt;, potremo vedere i servizi che questi mette a disposizione:&lt;/p&gt; 

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/400/original/E219I02.png" width="800" height="644" alt="L&amp;rsquo;elenco dei file nella cartella ActiveModel."/&gt;
&lt;/div&gt;

&lt;p&gt;Possiamo notare dall&amp;rsquo;elenco di sopra che ActiveModel include il codice di gestione delle callback, del tracciamento delle modifiche, della serializzazione e della validazione, insieme ad altre cose. L&amp;rsquo;ultima fra queste &amp;egrave; proprio ci&amp;ograve; che stavamo cercando.&lt;/p&gt;

&lt;p&gt;Il &lt;a href="http://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations.rb"&gt;codice per le validazioni&lt;/a&gt; ha il seguente commento vicino alla cima e possiamo  vedere da quello che &amp;egrave; piuttosto semplice aggiungere le validazioni a un modello. Tutto ci&amp;ograve; di cui abbiamo bisogno &amp;egrave; di includere il module &lt;code&gt;Validations&lt;/code&gt; e fornire i metodi getter per gli attributi su cui desideriamo chiamare i validatori:&lt;/p&gt;

&lt;pre class="ruby"&gt;
&amp;nbsp;&amp;nbsp;# == Active Model Validations
&amp;nbsp;&amp;nbsp;#   
&amp;nbsp;&amp;nbsp;# Provides a full validation framework to your objects.
&amp;nbsp;&amp;nbsp;# 
&amp;nbsp;&amp;nbsp;# A minimal implementation could be:
&amp;nbsp;&amp;nbsp;# 
&amp;nbsp;&amp;nbsp;#   class Person
&amp;nbsp;&amp;nbsp;#     include ActiveModel::Validations
&amp;nbsp;&amp;nbsp;# 
&amp;nbsp;&amp;nbsp;#     attr_accessor :first_name, :last_name
&amp;nbsp;&amp;nbsp;#
&amp;nbsp;&amp;nbsp;#     validates_each :first_name, :last_name do |record, attr, value|
&amp;nbsp;&amp;nbsp;#       record.errors.add attr, &amp;#x27;starts with z.&amp;#x27; if value.to_s[0] == ?z
&amp;nbsp;&amp;nbsp;#     end
&amp;nbsp;&amp;nbsp;#   end
&amp;nbsp;&amp;nbsp;# 
&lt;/pre&gt;

&lt;p&gt;Ora che sappiamo questa cosa, possiamo applicarla al nostro modello &lt;code&gt;Message&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/message.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Message
  include ActiveModel::Validations
  
  attr_accessor :name, :email, :content
 
  validates_presence_of :name
  validates_format_of :email, :with =&amp;gt; /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
  validates_length_of :content, :maximum =&amp;gt; 500
end
&lt;/pre&gt;

&lt;p&gt;Non &amp;egrave; comunque abbastanza per far s&amp;igrave; che il nostro modello si comporti come il controller si aspetta che faccia. Ci sono due problemi nel metodo &lt;code&gt;create&lt;/code&gt;. Innanzitutto, la chiamata a &lt;code&gt;Message.new&lt;/code&gt; non funzioner&amp;agrave; poich&amp;egrave; il nostro modello &lt;code&gt;Message&lt;/code&gt; non ha pi&amp;ugrave; un initializer che accetti un hash di attributi per argomento. In secondo luogo, &lt;code&gt;save&lt;/code&gt; non funzioner&amp;agrave; dal momento che non esiste un backend sul database in cui salvare il nuovo messaggio.&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/apps/controllers/messages_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class MessagesController &amp;lt; ApplicationController
  def new
    @message = Message.new
  end

  def create
    @message = Message.new(params[:message])
    if @message.save
      # TODO qui si deve inviare il messaggio
      flash[:notice] = &amp;quot;Message sent! Thank you for contacting us.&amp;quot;
      redirect_to root_url
    else
      render :action =&amp;gt; &amp;#x27;new&amp;#x27;
    end
  end
end
&lt;/pre&gt;

&lt;p&gt;Per primo, risolviamo il secondo problema. bench&amp;egrave; non si possa salvare un messaggio, possiamo tuttavia controllarne la validit&amp;agrave;, per cui sostituiamo &lt;code&gt;@message.save&lt;/code&gt; con &lt;code&gt;@message.valid?&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/messages_controllers.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @message = Message.new(params[:message])
  if @message.valid?
    # TODO qui si deve inviare il messaggio
    flash[:notice] = &amp;quot;Message sent! Thank you for contacting us.&amp;quot;
    redirect_to root_url
  else
    render :action =&amp;gt; &amp;#x27;new&amp;#x27;
  end
end
&lt;/pre&gt;

&lt;p&gt;Possiamo risolvere il primo problema, invece, scrivendo un metodo &lt;code&gt;initialize&lt;/code&gt; nella classe &lt;code&gt;Message&lt;/code&gt; che accetti un hash come parametro. Questo metodo iterer&amp;agrave; su ciascun elemento nell&amp;rsquo;hash e ne assegner&amp;agrave; il valore all&amp;rsquo;opportuno attributo del messaggio, usando il metodo &lt;code&gt;send&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/message.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Message
  include ActiveModel::Validations
  
  attr_accessor :name, :email, :content
 
  validates_presence_of :name
  validates_format_of :email, :with =&amp;gt; /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
  validates_length_of :content, :maximum =&amp;gt; 500
  
  def initialize(attributes = {})
    attributes.each do |name, value|
      send(&amp;quot;#{name}=&amp;quot;, value)
    end
  end
end
&lt;/pre&gt;

&lt;p&gt;Se ora ricarichiamo la form, tuttavia, vedremo che non ci siamo ancora:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/401/original/E219I03.png" width="796" height="510" alt="La form mostra un errore."/&gt;
&lt;/div&gt;
 
&lt;p&gt;Questa volta l&amp;rsquo;errore &amp;egrave; causato dalla mancanza del metodo &lt;code&gt;to_key&lt;/code&gt; nel modello &lt;code&gt;Message&lt;/code&gt;. L&amp;rsquo;errore viene lanciato dal metodo &lt;code&gt;form_for&lt;/code&gt;, per cui sembra che Rails stesso si aspetti che il nostro modello abbia una funzionalit&amp;agrave; che ancora non supporta. Aggiungiamogliela ora.&lt;/p&gt;

&lt;p&gt;Anzich&amp;egrave; provare a indovinare tutto ci&amp;ograve; che Rails si aspetter&amp;agrave; di vedere implementato dal nostro modello, c&amp;rsquo;&amp;egrave; un simpatico &lt;a href="http://github.com/rails/rails/blob/master/activemodel/lib/active_model/lint.rb"&gt;lint test&lt;/a&gt; incluso in ActiveModel che ci permette di controllare se il nostro modello personalizzato si comporti o meno come Rails si aspetta che faccia. Se includiamo il module &lt;code&gt;ActiveModel::Lint::Tests&lt;/code&gt; in un test per il modello, esso controller&amp;agrave; che il modello abbia tutte le funzionalit&amp;agrave; necessarie.&lt;/p&gt;

&lt;p&gt;Il codice sorgente per il module &lt;code&gt;Lint::Tests&lt;/code&gt; mostra i metodi ai quali il modello deve poter rispondere al fine di funzionare come si deve, incluso il &lt;code&gt;to_key&lt;/code&gt;. Possiamo fare in modo che il nostro modello funzioni includendo un paio di module ActiveRecord. Il primo di questi &amp;egrave; &lt;a href="http://github.com/rails/rails/blob/master/activemodel/lib/active_model/conversion.rb"&gt;&lt;code&gt;Conversion&lt;/code&gt;&lt;/a&gt;, che fornisce il metodo to_key ed alcuni altri. L&amp;rsquo;altro module &amp;egrave; &lt;a href="http://github.com/rails/rails/blob/master/activemodel/lib/active_model/naming.rb"&gt;&lt;code&gt;Naming&lt;/code&gt;&lt;/a&gt;, ma in questo caso lo estendiamo nella nostra classe anzich&amp;egrave; includerlo, poich&amp;egrave; include alcuni metodi di classe.&lt;/p&gt;

&lt;p&gt;Oltre a includere il module &lt;code&gt;Conversion&lt;/code&gt;, dobbiamo anche definire un metodo &lt;code&gt;persisted?&lt;/code&gt;, che dovr&amp;agrave; restituire sempre &lt;code&gt;false&lt;/code&gt;, dato che il nostro modello non viene mai reso persistente sul database. Con queste modifiche, ora la nostra classe &lt;code&gt;Message&lt;/code&gt; appare cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/message.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Message
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming
  
  attr_accessor :name, :email, :content
 
  validates_presence_of :name
  validates_format_of :email, :with =&amp;gt; /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
  validates_length_of :content, :maximum =&amp;gt; 500
  
  def initialize(attributes = {})
    attributes.each do |name, value|
      send(&amp;quot;#{name}=&amp;quot;, value)
    end
  end
  
  def persisted?
    false
  end
end
&lt;/pre&gt;

&lt;p&gt;Se ricarichiamo la form, ora, riprender&amp;agrave; a funzionare, ad indicare che ora il modello &lt;code&gt;Message&lt;/code&gt; soddisfa tutti i requisiti richiesti da Rails 3. Se proviamo a fare il submit della form, vedremo che anche i validatori funzionano:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/402/original/E219I04.png" width="800" height="631" alt="La form ora funziona, compresi i validatori."/&gt;
&lt;/div&gt;

&lt;p&gt;Abbiamo trattato solo una piccola parte di ci&amp;ograve; che mette a disposizione ActiveModel in questo episodio, ma dovrebbe essere abbastanza per stimolare il vostro appetito e darvi una buona ragione per approfondire sul codice sorgente per vedere cosa si pu&amp;ograve; fare. Il codice &amp;egrave; ben documentato e strutturato, per cui se troverete qualcosa di utile, ci dovrebbero essere anche le informazioni sufficienti nei commenti per mettervi in grado di utilizzare la funzionalit&amp;agrave; trovata.&lt;/p&gt;</description>
      <pubDate>Sun, 27 Jun 2010 21:36:44 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/219-active-model</guid>
      <link>http://it.asciicasts.com/episodes/219-active-model</link>
    </item>
    <item>
      <title>Creare generatori in Rails 3</title>
      <description>&lt;p&gt;Come gi&amp;agrave; fatto vedere nell&amp;rsquo;episodio 216 [&lt;a href="http://railscasts.com/episodes/216-generators-in-rails-3"&gt;guardalo&lt;/a&gt;, &lt;a href="http://it.asciicasts.com/episodes/216-generatori-in-rails-3"&gt;leggilo&lt;/a&gt;] i generatori in Rails 3 sono molto pi&amp;ugrave; modulari e possono essere personalizzati in modi che non erano pensabili con Rails 2. Ciononostante ci sono ancora delle volte in cui si ha ugualmente la necessit&amp;agrave; di creare un generatore completamente nuovo, perch&amp;egrave; magari quelli forniti non vengono incontro appieno alle vostre necessit&amp;agrave;. La creazione di un generatore personalizzato &amp;egrave; stata discussa nell&amp;rsquo;&lt;a href="http://railscasts.com/episodes/58-how-to-make-a-generator"&gt;episodio 58&lt;/a&gt; ma la tecnica &amp;egrave; cambiata sostanzialmente da allora, per cui riprendiamo il discorso e mostriamo come creare un generatore analogo a quello di tale episodio in Rails 3.&lt;/p&gt;

&lt;h3&gt;Incominciamo&lt;/h3&gt;

&lt;p&gt;Il modo pi&amp;ugrave; semplice per creare un generatore &amp;egrave; farlo da una nuova applicazione Rails 3, er cui partiamo creandone una nuova. Rails 3.0 beta 4 &amp;egrave; appena stato rilasciato e la sintassi per la creazione di nuove applicazioni &amp;egrave; cambiata, per cui, per creare l&amp;rsquo;applicazione todo che useremo per scrivere il nostro generatore, anzich&amp;egrave; lanciare il comando:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails todo
&lt;/pre&gt;

&lt;p&gt;dovremo lanciare:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails new todo
&lt;/pre&gt;

&lt;p&gt;Dopo che abbiamo creato la nostra nuova applicazione, possiamo partire con la creazione di del nostro generatore, usando uno dei generatori forniti. Il comando &lt;code&gt;rails g generator&lt;/code&gt; crea i file di base di cui avremo bisogno per il nostro nuovo generatore. Possiamo esaminare i parametri che il comando accetta, lanciando:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g generator --help
&lt;/pre&gt;

&lt;p&gt;Il generatore &amp;egrave; piuttosto semplice e ha bisogno solo del nome del generatore che si vuole creare. Vogliamo che il nostro generatore generi un file di layout applicativo, per cui lo chiamiamo &lt;code&gt;layout&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g generator layout
      &lt;span class="passed"&gt;create&lt;/span&gt;  lib/generators/layout
      &lt;span class="passed"&gt;create&lt;/span&gt;  lib/generators/layout/layout_generator.rb
      &lt;span class="passed"&gt;create&lt;/span&gt;  lib/generators/layout/USAGE
      &lt;span class="passed"&gt;create&lt;/span&gt;  lib/generators/layout/templates
&lt;/pre&gt;      
      
&lt;p&gt;Il generatore crea i file sotto la cartella &lt;code&gt;/lib&lt;/code&gt; dell&amp;rsquo;applicazione, il che significa che il generatore sar&amp;agrave; disponibile esclusivamente per questa applicazione. Per renderlo disponibile a qualunque applicazione, dovremo fare un po&amp;rsquo; di lavoro extra; vedremo come fare anche questa cosa pi&amp;ugrave; avanti nel corso dell&amp;rsquo;episodio.&lt;/p&gt;

&lt;p&gt;La struttura creata dal generatore &amp;egrave; piuttosto semplice e contiene esclusivamente una classe. Nella classe c&amp;rsquo;&amp;egrave; una linea di codice che dice al generatore di ispezionare nella cartella dei template alla ricerca di ulteriori file:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/layout_generator.rb&lt;/p&gt;
&lt;pre class="rails"&gt;
class LayoutGenerator &amp;lt; Rails::Generators::NamedBase
  source_root File.expand_path(&amp;#x27;../templates&amp;#x27;, __FILE__)
end
&lt;/pre&gt;

&lt;p&gt;Il genratore &lt;code&gt;generator&lt;/code&gt; crea anche un file &lt;code&gt;USAGE&lt;/code&gt; che serve a descrivere la documentazione per il nostro generatore. Questa documentazione verr&amp;agrave; mostrata se si lancier&amp;agrave; il nostro generatore con l&amp;rsquo;opzione &lt;code&gt;--help&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La classe creata dal generatore eredita da &lt;code&gt;Rails::Generators::NamedBase&lt;/code&gt;. Questo implica che si dovr&amp;agrave; fornire un nome come parametro al lancio del nostro generatore, allo stesso modo di come si &amp;egrave; fatto al lancio del generatore &lt;code&gt;generator&lt;/code&gt;. Possiamo averne conferma, lanciando il nostro nuovo generatore con l&amp;rsquo;opzione &lt;code&gt;--help&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout --help
Usage:
  rails generate layout NAME [options]

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Supress status output

Description:
    Explain the generator

Example:
    rails generate layout Thing

    This will create:
        what/will/it/create
&lt;/pre&gt;  

&lt;p&gt;Si note che il contenuto del file &lt;code&gt;USAGE&lt;/code&gt; viene mostrato in fondo all&amp;rsquo;output.&lt;/p&gt;

&lt;p&gt;Vogliamo rendere opzionale il parametro &lt;code&gt;NAME&lt;/code&gt; impostando un valore di default pari a &lt;code&gt;application&lt;/code&gt;. Possiamo farlo rendendo la nostra classe figlia di &lt;code&gt;Rails::Generators::Base&lt;/code&gt;, anzich&amp;egrave; di &lt;code&gt;NamedBase&lt;/code&gt;. Ci&amp;ograve; render&amp;agrave; opzionali tutti gli argomenti e di conseguenza ci dar&amp;agrave; pi&amp;ugrave; flessibilit&amp;agrave; nell&amp;rsquo;adattare il generatore alle nostra esigenze.&lt;/p&gt;

&lt;p&gt;Possiamo dichiarare gli argomenti accettati dal nostro generatore mediante il metodo &lt;code&gt;argument&lt;/code&gt;. Definire gli argomenti per conto nostro anzich&amp;egrave; usare l&amp;rsquo;opzione di default &lt;code&gt;NAME&lt;/code&gt; significa che possiamo stabilire un valore di default per ciascuno argomento. Aggiungiamo un argomento &lt;code&gt;layout_name&lt;/code&gt; con un valore di default pari a &lt;code&gt;application&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/layout_generator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class LayoutGenerator &amp;lt; Rails::Generators::Base
  source_root File.expand_path(&amp;#x27;../templates&amp;#x27;, __FILE__)
  argument :layout_name, :type =&amp;gt; :string, :default =&amp;gt; &amp;quot;application&amp;quot;
end
&lt;/pre&gt;

&lt;p&gt;Se lanciamo nuovamente il generatore con l&amp;rsquo;opzione &lt;code&gt;--help&lt;/code&gt;, vedremo il nostro nuovo argomento in lista. E&amp;rsquo; mostrato fra parentesi quadre, ad indicare che &amp;egrave; un parametro opzionale.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout --help
Usage:
  rails generate layout [LAYOUT_NAME] [options]
&lt;/pre&gt;  

&lt;p&gt;I generatori in Rails 3 si basano sulla libreria &lt;a href="http://github.com/wycats/thor"&gt;Thor&lt;/a&gt;, che somiglia a rake. Molti dei metodi che chiamiamo nella classe del nostro generatore sono metodi definiti all&amp;rsquo;interno dello stesso Thor, per cui se vorrete saperne di pi&amp;ugrave; potrete consultare il codice sorgente di Thor e la documentazione relativa.&lt;/p&gt;

&lt;h3&gt;Creiamo i file che verranno usati dal generatore&lt;/h3&gt;

&lt;p&gt;Vogliamo che il nostro generatore crei due file: un file di layout di applicazione e un foglio di stile. Per fare ci&amp;ograve;, creiamo due file nella cartella &lt;code&gt;templates&lt;/code&gt; del nostro generatore e facciamo in modo che quest&amp;rsquo;ultimo copi questi file nelle cartelle corrette dell&amp;rsquo;applicazione su cui verr&amp;agrave; lanciato.&lt;/p&gt;

&lt;p&gt;Come definiamo il comportamento del generatore? Ebbene, il modo in cui lo si fa &amp;egrave; piuttosto anomalo. Tutti i metodi pubblici definiti nella classe del generatore verranno eseguiti al lancio del generatore. Quindi possiamo definire un metodo chiamato &lt;code&gt;generate_layout&lt;/code&gt; e questo sar&amp;agrave; automaticamente eseguito al lancio del generatore. E&amp;rsquo; un concetto piuttosto strano inizialmente, ma alla fine si rivela un buon sistema per poter organizzare il codice all&amp;rsquo;interno del generatore.&lt;/p&gt;

&lt;p&gt;La prima cosa che facciamo fare al nostro generatore &amp;egrave; copiare il foglio di stile dalla cartella &lt;code&gt;templates&lt;/code&gt; alla cartella &lt;code&gt;/public/stylesheets&lt;/code&gt; dell&amp;rsquo;applicazione. Possiamo usare il metodo &lt;code&gt;copy_file&lt;/code&gt; per questo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/layout_generator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class LayoutGenerator &amp;lt; Rails::Generators::Base
  source_root File.expand_path(&amp;#x27;../templates&amp;#x27;, __FILE__)
  argument :layout_name, :type =&amp;gt; :string, :default =&amp;gt; &amp;quot;application&amp;quot;
  
  def generate_layout
    copy_file &amp;quot;stylesheet.css&amp;quot;, &amp;quot;public/stylesheets/#{layout_name.underscore}.css&amp;quot;
  end
  
end
&lt;/pre&gt;

&lt;p&gt;Il metodo &lt;code&gt;copy_file&lt;/code&gt; richiede due parametri. Il primo &amp;egrave; il nome del file nella cartella &lt;code&gt;templates&lt;/code&gt; da copiare e il secondo &amp;egrave; il percorso di destinazione. Si noti che il nome del file di destinazione &amp;egrave; basato sul nome dell&amp;rsquo;argomento &lt;code&gt;layout_name&lt;/code&gt; che viene passato al generatore. Il metodo &lt;code&gt;argument&lt;/code&gt; crea un metodo &lt;code&gt;layout_name&lt;/code&gt; che possiamo utilizzare, ma siccome il nome del layout passato potrebbe essere in camel-case, invochiamo sul quest&amp;rsquo;ultimo il metodo &lt;code&gt;underscore&lt;/code&gt; per essere certi che sia in un formato adatto ai nomi di file.&lt;/p&gt;

&lt;p&gt;Infine dobbiamo creare il file &lt;code&gt;stylesheet.css&lt;/code&gt; nella cartella &lt;code&gt;templates&lt;/code&gt; e incollarvi il contenuto del &lt;a href="http://github.com/ryanb/railscasts-episodes/blob/master/episode-218/todo/lib/generators/layout/templates/stylesheet.css"&gt;default CSS&lt;/a&gt; che vogliamo per la nostra applicazione.&lt;/p&gt;

&lt;p&gt;Se ora invochiamo il nostro generatore senza argomenti, dovrebbe creare un file &lt;code&gt;application.css&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout
      &lt;span class="passed"&gt;create&lt;/span&gt;  public/stylesheets/application.css
&lt;/pre&gt;

&lt;p&gt;Poi dobbiamo fare la stessa cosa per il file di layout. Useremo la versione con gli underscore del nome del layout un paio di volte, per cui &amp;egrave; meglio fattorizzare la conversione in un metodo che chiamiamo &lt;code&gt;file_name&lt;/code&gt;. Come detto poco fa, ogni metodo pubblico definito nel generatore sar&amp;agrave; invocato al lancio del generatore, per cui dobbiamo dichiarare il metodo &lt;code&gt;file_name&lt;/code&gt;, che &amp;egrave; di servizio, privato.&lt;/p&gt;

&lt;p&gt;La prossima cosa che dobbiamo fare nel nostro generatore &amp;egrave; creare il file di layout stesso. Potremmo usare nuovamente il metodo &lt;code&gt;copy_file&lt;/code&gt;, ma in questo modo avremmo semplicemente una copia esatta del file di template, mentre invece vorremmo far girare un qualche codice erb sul template, in modo che venga modificato al lancio del generatore.&lt;/p&gt;

&lt;p&gt;Per fare questo, possiamo usare il metodo &lt;code&gt;template&lt;/code&gt;, che accetta argomenti simili a quelli di &lt;code&gt;copy_file&lt;/code&gt;, ma che interpreta tutto l&amp;rsquo;erb che trova all&amp;rsquo;interno del template prima di copiarlo nella cartella di destinazione:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/layout_generator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class LayoutGenerator &amp;lt; Rails::Generators::Base
  source_root File.expand_path(&amp;#x27;../templates&amp;#x27;, __FILE__)
  argument :layout_name, :type =&amp;gt; :string, :default =&amp;gt; &amp;quot;application&amp;quot;
  
  def generate_layout
    copy_file &amp;quot;stylesheet.css&amp;quot;, &amp;quot;public/stylesheets/#{file_name}.css&amp;quot;
    template &amp;quot;layout.html.erb&amp;quot;, &amp;quot;app/views/layouts/#{file_name}.html.erb&amp;quot;
  end
  
  private
  def file_name
    layout_name.underscore
  end
  
end
&lt;/pre&gt;

&lt;p&gt;Poi creiamo il vero e proprio file di template nella cartella &lt;code&gt;templates&lt;/code&gt;. Il codice contenutovi sar&amp;agrave; il seguente:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/templates/layout.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Untitled&amp;lt;/title&amp;gt;
    &amp;lt;%%= stylesheet_link_tag &amp;quot;&amp;lt;%= file_name %&amp;gt;&amp;quot; %&amp;gt;
    &amp;lt;%%= javascript_include_tag :defaults %&amp;gt;
    &amp;lt;%%= csrf_meta_tag %&amp;gt;
    &amp;lt;%%= yield(:head) %&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&amp;quot;container&amp;quot;&amp;gt;
      &amp;lt;%% flash.each do |name, msg| %&amp;gt;
        &amp;lt;%%= content_tag :div, msg, :id =&amp;gt; &amp;quot;flash_#{name}&amp;quot; %&amp;gt;
      &amp;lt;%% end %&amp;gt;
      &amp;lt;%%= yield %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;La prima cosa degna di nota &amp;egrave; che dal momento che stiamo usando il metodo &lt;code&gt;template&lt;/code&gt;, tutti i tag erb nel codice verranno eseguiti al lancio del generatore. Se vogliamo includere dell&amp;rsquo;erb nel file generato, dobbiamo farne l&amp;rsquo;escape col simbolo percentuale all&amp;rsquo;inizio di ciascun tag erb e ci&amp;ograve; &amp;egrave; quanto abbiamo fatto per la maggior parte del codice erb qui sopra.&lt;/p&gt;

&lt;p&gt;Per includere del contenuto dinamico nel template, possiamo invece usare del normale codice erb. Possiamo accedere ai metodi del generatore, anche privati, per cui chiamiamo &lt;code&gt;file_name&lt;/code&gt; per inserire il nome corretto del foglio di stile nella chiamata a &lt;code&gt;stylesheet_link_tag&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Per vedere se tutto funziona, rilanciamo il nostro generatore, questa volta passandogli un nome per il layout:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout admin
      &lt;span class="passed"&gt;create&lt;/span&gt;  public/stylesheets/admin.css
      &lt;span class="passed"&gt;create&lt;/span&gt;  app/views/layouts/admin.html.erb
&lt;/pre&gt;

&lt;p&gt;Questa volta il generatore ha creato due file, ciascuno col nome basato sul nome di layout passato come parametro al lancio, per cui tutto sembra funzionare. Se diamo uno sguardo al file di layout generato, notiamo che il metodo &lt;code&gt;file_name&lt;/code&gt; &amp;egrave; stato chiamato quando il file &amp;egrave; stato generato, per cui il metodo &lt;code&gt;stylesheet_link_tag&lt;/code&gt; ha l&amp;rsquo;argomento corretto. Per gli altri tag erb era stato applicato l&amp;rsquo;escape, per cui ora li ritroviamo correttamente nel file di layout:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/layouts/admin.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Untitled&amp;lt;/title&amp;gt;
  &amp;lt;%= stylesheet_link_tag &amp;quot;admin&amp;quot; %&amp;gt;
  &amp;lt;%= javascript_include_tag :defaults %&amp;gt;
  &amp;lt;%= csrf_meta_tag %&amp;gt;
  &amp;lt;%= yield(:head) %&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Rendere le opzioni facoltative&lt;/h3&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; ancora una piccola feature che vorremmo aggiungere al nostro generatore: la possibilit&amp;agrave; di passare un&amp;rsquo;opzione che prevenga la generazione del foglio di stile. Possiamo fare questa cosa, usando il metodo &lt;code&gt;class_option&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/layout_generator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class LayoutGenerator &amp;lt; Rails::Generators::Base
  source_root File.expand_path(&amp;#x27;../templates&amp;#x27;, __FILE__)
  argument :layout_name, :type =&amp;gt; :string, :default =&amp;gt; &amp;quot;application&amp;quot;
  class_option :stylesheet, :type =&amp;gt; :boolean, :default =&amp;gt; true, :description =&amp;gt; &amp;quot;Include stylesheet file&amp;quot;
  
  def generate_layout
    copy_file &amp;quot;stylesheet.css&amp;quot;, &amp;quot;public/stylesheets/#{file_name}.css&amp;quot; if options.stylesheet?
    template &amp;quot;layout.html.erb&amp;quot;, &amp;quot;app/views/layouts/#{file_name}.html.erb&amp;quot;
  end
  
  private
  def file_name
    layout_name.underscore
  end
  
end
&lt;/pre&gt;

&lt;p&gt;Abbiamo chiamato la nostra option &lt;code&gt;stylesheet&lt;/code&gt;, dandole il tipo &lt;code&gt;boolean&lt;/code&gt; con un valore di default pari a &lt;code&gt;true&lt;/code&gt; e fornendole una descrizione. Nella linea &lt;code&gt;copy_file&lt;/code&gt; possiamo ora aggiungere una condizione &lt;code&gt;if&lt;/code&gt; per cui il foglio di stile venga copiato solo se l&amp;rsquo;opzione &lt;code&gt;stylesheet&lt;/code&gt; vale &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Analogamente, nel template di layout, vogliamo includere la linea &lt;code&gt;stylesheet_link_tag&lt;/code&gt; solo se l&amp;rsquo;opzione &lt;code&gt;stylesheet&lt;/code&gt; &amp;egrave; &lt;code&gt;true&lt;/code&gt;. Racchiudiamo dunque quella riga di codice in un &lt;code&gt;if&lt;/code&gt; affinch&amp;egrave; tutto ci&amp;ograve; avvenga:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/generators/layout/templates/layout.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Untitled&amp;lt;/title&amp;gt;
    &amp;lt;%- if options.stylesheet? -%&amp;gt;
    &amp;lt;%%= stylesheet_link_tag &amp;quot;&amp;lt;%= file_name %&amp;gt;&amp;quot; %&amp;gt;
    &amp;lt;%- end -%&amp;gt;
    &amp;lt;%%= javascript_include_tag :defaults %&amp;gt;
    &amp;lt;%%= csrf_meta_tag %&amp;gt;
    &amp;lt;%%= yield(:head) %&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&amp;quot;container&amp;quot;&amp;gt;
      &amp;lt;%% flash.each do |name, msg| %&amp;gt;
        &amp;lt;%%= content_tag :div, msg, :id =&amp;gt; &amp;quot;flash_#{name}&amp;quot; %&amp;gt;
      &amp;lt;%% end %&amp;gt;
      &amp;lt;%%= yield %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Se lanciamo la documentazione di help per il nostro generatore nuovamente, ora vedremo che abbiamo una nuova opzione (facoltativa) stylesheet.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout --help
Usage:
  rails generate layout [LAYOUT_NAME] [options]

Options:
  [--stylesheet]  # Indicates when to generate stylesheet
                  # Default: true
&lt;/pre&gt;            
                  
&lt;p&gt;Ora siamo in grado di generare un layout privo di foglio di stile, usando l&amp;rsquo;opzione &lt;code&gt;--skip-stylesheet&lt;/code&gt; (o &lt;code&gt;--no-stylesheet&lt;/code&gt;). Cos&amp;igrave; facendo, verr&amp;agrave; creato il solo file di layout:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g layout foo --skip-stylesheet
      &lt;span class="passed"&gt;create&lt;/span&gt;  app/views/layouts/foo.html.erb
&lt;/pre&gt;      

&lt;p&gt;E&amp;rsquo; tutto per questo episodio sulla creazione di generatori in Rails 3. Siete invitati a provare per conto vostro la creazione di nuovi generatori. Ogniqualvolta dobbiate riprodurre del codice uguale (o molto simile) nelle vostre applicazioni Rails, questo processo pu&amp;ograve; essere convertito e reso pi&amp;ugrave; rapido e meno noioso dall&amp;rsquo;uso di un generatore. Per condividere il vostro generatore con altri, tutto quello che dovrete fare sar&amp;agrave; di creare un plugin Rails o un gem e mettere il vostro generatore in una cartella &lt;code&gt;lib/generators&lt;/code&gt;. Cos&amp;igrave; facendo, chiunque includa tale gem o plugin nella propria applicazione, avr&amp;agrave;, a disposizione anche il vostro generatore.&lt;/p&gt;</description>
      <pubDate>Fri, 25 Jun 2010 19:59:58 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/213-creare-generatori-in-rails-3</guid>
      <link>http://it.asciicasts.com/episodes/213-creare-generatori-in-rails-3</link>
    </item>
    <item>
      <title>Wizard Form</title>
      <description>&lt;p&gt;Una form multistep, anche nota come wizard, &amp;egrave; una form molto grande suddivisa in pi&amp;ugrave; pagine in sequenza che l&amp;rsquo;utente pu&amp;ograve; navigare per completare l&amp;rsquo;inserimento dei dati. Non tutti amano questo tipo di approccio, alcuni preferiscono far vedere un&amp;rsquo;unica immensa form suddividendo piuttosto i dati fra pi&amp;ugrave; risorse e modelli. Esistono in ogni modo alcuni casi in cui il wizard rappresenta il miglior approccio, per esempio il caso di un sito per la dichiarazione dei redditi online, che richiede molti dati all&amp;rsquo;utente e che prevede molte ramificazioni a seconda dei dati immessi e che perci&amp;ograve; rende sensato l&amp;rsquo;utilizzo di una form multistep.&lt;/p&gt;

&lt;p&gt;Una form multistep ben fatta ricorda i dati inseriti fra i vari passi, permettendo agli utenti di andare avanti e indietro fra le pagine senza perdere nessuna delle informazioni inserite. Se il vostro obiettivo &amp;egrave; semplicemente quello di suddividere una form grande per renderla pi&amp;ugrave; semplice agli occhi dell&amp;rsquo;utente, potreste considerare anche il solo utilizzo di JavaScript e CSS per mostrare e nascondere le varie parti della form quando vengono premuti i pulsanti &amp;ldquo;previous&amp;rdquo; e &amp;ldquo;next&amp;rdquo;. Un &lt;a href="http://developer.apple.com/internet/webcontent/examples/wizard.html"&gt;esempio di questo approccio&lt;/a&gt; lo si pu&amp;ograve; ritrovare nell&amp;rsquo;Apple&amp;rsquo;s developer site. Se avete bisogno di qualcosa di pi&amp;ugrave; e orientato al lato server, allora dovrete passare necessariamente da Rails stesso e in questo episodio vi mostreremo come farlo.&lt;/p&gt;

&lt;h3&gt;Una form multistep di ordine&lt;/h3&gt;

&lt;p&gt;Per spiegare come comporre una form multistep, aggiungeremo un processo di checkout all&amp;rsquo;applicazione negozio online. Gli utenti dovranno per prima cosa inserire le loro informazioni di spedizione e poi quelle relative al pagamento ed infine, al terzo passo, dovranno vedere un riassunto del loro ordine in cui potranno dare conferma definitiva dei dati e dell&amp;rsquo;ordine stesso.&lt;/p&gt;

&lt;p&gt;Non abbiamo ancora un modello per l&amp;rsquo;ordine e nemmeno un controller, per cui usiamo uno dei &lt;a href="http://github.com/ryanb/nifty-generators"&gt;nifty generator&lt;/a&gt; di Ryan Bates per creare uno scaffold per la form degli ordini. Si noti come, essendo questa applicazione gi&amp;agrave; stata scritta con Rails 2, si debba usare il comando &lt;code&gt;script/generate&lt;/code&gt;. Un modello di ordine vero e proprio dovrebbe avere molti attibuti, ma per i nostri scopi semplifichiamo, dandogliene solamente due. Creiamo anche le action &lt;code&gt;index&lt;/code&gt;, &lt;code&gt;show&lt;/code&gt; e &lt;code&gt;new&lt;/code&gt; per il controller:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ script/generate nifty_scaffold order shipping_name:string billing_name:string index show new
&lt;/pre&gt;

&lt;p&gt;Poi dobbiamo migrare il database per creare le nuove tabelle sul database:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rake db:migrate
&lt;/pre&gt;

&lt;p&gt;La form dell&amp;rsquo;ordine, cos&amp;igrave; come &amp;egrave; stata generata dallo scaffold, ha tutti i campi insiemesu un unica pagina, per cui la prima cosa che dobbiamo fare &amp;egrave; suddividere l&amp;rsquo;inserimento dei dati su pi&amp;ugrave; passaggi:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/391/original/E217I01.png" width="800" height="387" alt="La form di inserimento di un nuovo ordine generata dallo scaffold."/&gt;
&lt;/div&gt;

&lt;p&gt;Il codice per la form del nuovo ordine ha tutti i campi riuniti insieme. Per cominciare a realizzare il nostro wizard, spostiamo ciascuno dei tre elementi paragrafo e i campi della form che questi racchiudono in partial distinti:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= f.label :shipping_name %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.text_field :shipping_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= f.label :billing_name %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.text_field :billing_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Submit&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Prima di fare ci&amp;ograve;, tuttavia, aggiungiamo un&amp;rsquo;intestazione a ciascuna sezione e modifichiamo la sezione finale in modo tale che mostri un riassunto dell&amp;rsquo;ordine:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;h2&amp;gt;Shipping Information&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= f.label :shipping_name %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.text_field :shipping_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;h2&amp;gt;Billing Information&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;%= f.label :billing_name %&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%= f.text_field :billing_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;h2&amp;gt;Confirm Information&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Shipping Name:&amp;lt;/strong&amp;gt;
    &amp;lt;%= h @order.shipping_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Billing Name:&amp;lt;/strong&amp;gt;
    &amp;lt;%= h @order.billing_name %&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Submit&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Una volta fatto questo, possiamo spostare ogni sezione in un proprio partial. Creiamo tre nuovi partial chiamati &lt;code&gt;shipping_step&lt;/code&gt;, &lt;code&gt;billing_step&lt;/code&gt; e &lt;code&gt;confirmation_step&lt;/code&gt; e copiamo le rispettive parti della form all&amp;rsquo;interno di essi. Dopo aver fatto ci&amp;ograve;, la nostra form di nuovi ordini dovrebbe apparire cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="terminal"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;%= render &amp;#x27;shipping_step&amp;#x27;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;%= render &amp;#x27;billing_step&amp;#x27;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;%= render &amp;#x27;confirmation_step&amp;#x27;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Submit&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;e i tre partial dovrebbero essere cos&amp;igrave; fatti:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/_billing_step.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;Billing Information&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
  &amp;lt;%= f.label :billing_name %&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;%= f.text_field :billing_name %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/_shipping_step.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;Shipping Information&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
  &amp;lt;%= f.label :shipping_name %&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;%= f.text_field :shipping_name %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/confirmation_step.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h2&amp;gt;Confirm Information&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Shipping Name:&amp;lt;/strong&amp;gt;
  &amp;lt;%= h @order.shipping_name %&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
  &amp;lt;strong&amp;gt;Billing Name:&amp;lt;/strong&amp;gt;
  &amp;lt;%= h @order.billing_name %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Dobbiamo modificare anche la classe di modello &lt;code&gt;Order&lt;/code&gt; in modo tale che sia consapevole dell&amp;rsquo;esistenza dei tre passaggi e possa riconoscere quale fra questi sia il corrente. Potremmo usare il plugin state machine per questo scopo, ma dal momento che questo esempio &amp;egrave; piuttosto semplice, scriveremo il codice da zero.&lt;/p&gt;

&lt;p&gt;Il modello &lt;code&gt;Order&lt;/code&gt; dovr&amp;agrave; sapere quali sono i vari passi e l&amp;rsquo;ordine in cui si devono susseguire, per cui scriviamo un nuovo metodo &lt;code&gt;steps&lt;/code&gt; per restituire una lista di passi. La nostra lista sar&amp;agrave; un semplice array, ma per form pi&amp;ugrave; complesse questo metodo pu&amp;ograve; facilmente essere reso pi&amp;ugrave; dinamico e gestire i casi in cui la lista di passi dipenda dallo storico delle scelte gi&amp;agrave; fatte nei passi precedenti.&lt;/p&gt;

&lt;p&gt;Per ottenere ed impostare il passo corrente per un ordine, creiamo un writer chiamato &lt;code&gt;current_step&lt;/code&gt; e un metodo accessor corrispondente che restituir&amp;agrave; il passo corrente nel caso in cui sia stato impostato, o altrimenti il primo passo. Fatte queste modifiche, il modello &lt;code&gt;Order&lt;/code&gt; dovrebbe apparire cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Order &amp;lt; ActiveRecord::Base
  attr_accessible :shipping_name, :billing_name
  attr_writer :current_step
  
  def current_step
    @current_step || steps.first
  end
  
  def steps
    %w[shipping billing confirmation]
  end
end
&lt;/pre&gt;

&lt;p&gt;Ora che il modello &lt;code&gt;Order&lt;/code&gt; a che passo corrisponde il passo corrente, possiamo usare questa informazione per cambiare dinamicamente il partial da renderizzare, modificando leggermente la form del nuovo ordine, in modo che mostri solo il passo corrente:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;%= render &amp;quot;#{@order.current_step}_step&amp;quot;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Submit&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Al ricaricamento della pagina con la form del nuovo ordine, ora vedremo mostrata solo la sezione relativa al primo passaggio:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/392/original/E217I02.png" width="800" height="373" alt="La form del nuovo ordine mostra ora solo il primo passo."/&gt;
&lt;/div&gt;

&lt;h3&gt;Muoversi fra i vari passi&lt;/h3&gt;

&lt;p&gt;Se clicchiamo il pulsante di submit sulla form, verr&amp;agrave; chiamata la action &lt;code&gt;create&lt;/code&gt; sul controller e verr&amp;agrave; creato un nuovo ordine, ma questo non &amp;egrave; ci&amp;ograve; che vogliamo che accada. Vorremmo piuttosto che venisse mostrata la pagina col passo successivo al corrente: modifichiamo dunque la action sul controller per riflettere il comportamento desiderato.&lt;/p&gt;

&lt;p&gt;Ci sono una serie di modi per approcciarsi al problema specifico. Potremmo creare una action separata per ogni passo; potremmo usare semplicemente la action &lt;code&gt;create&lt;/code&gt; per il passo iniziale e le action &lt;code&gt;edit&lt;/code&gt; e &lt;code&gt;update&lt;/code&gt; per gli altri passi, che vorrebbe dire che abbiamo un modello parzialmente completato sul database, oppure potremmo rimanere nelle action &lt;code&gt;new&lt;/code&gt; e &lt;code&gt;create&lt;/code&gt; e salvare i dettagli inseriti fino ad ora in sessione. Ci sono una serie di caveat a quest&amp;rsquo;utlimo approccio che verranno citati di volta in volta man mano che emergeranno, mostrandone le alternative.&lt;/p&gt;

&lt;p&gt;La cosa pi&amp;ugrave; semplice che possiamo fare &amp;egrave; modificare la action &lt;code&gt;create&lt;/code&gt;. Il primo passo che faremo sar&amp;agrave; cambiarla in modo tale che non salvi l&amp;rsquo;ordine, ma che piuttosto mostri il prossimo passo della form.&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @order = Order.new(params[:order])
  @order.next_step
  render &amp;#x27;new&amp;#x27;
end
&lt;/pre&gt;

&lt;p&gt;Sul controller, come si vede, chiamiamo il metodo &lt;code&gt;next_step&lt;/code&gt; della classe &lt;code&gt;Order&lt;/code&gt; che abbiamo scritto nella classe di modello:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def next_step
  self.current_step = steps[steps.index(current_step)+1]
end
&lt;/pre&gt;

&lt;p&gt;Ora verremo portati dalla maschera del primo passo a quella del secondo al click del pulsante di submit sulla form, ma al successivo click rimarremo sul secondo passo. Ciograve; a causa del fatto che non stiamo salvando lo stato relativo al passo corrente quando facciamo il submit della form. Nell&amp;rsquo;action &lt;code&gt;create&lt;/code&gt; dobbiamo impostare il passo corrente sul modello &lt;code&gt;Order&lt;/code&gt; e poi salvare tale valore. Possiamo fare tutto questo recuperando il passo corrente dalla sessione, muovendoci al passo successivo e poi salvando quest&amp;rsquo;ultimo nuovo passo in sessione:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @order = Order.new(params[:order])
  @order.current_step = session[:order_step]
  @order.next_step
  session[:order_step] = @order.current_step
  render &amp;#x27;new&amp;#x27;
end
&lt;/pre&gt;

&lt;p&gt;Se proviamo ora la form, si potr&amp;agrave; navigare in avanti da una pagina alla successiva come volevamo, ma si ritorna nuovamente alla prima pagina al submit dell&amp;rsquo;ultima. Quest&amp;rsquo;ultima cosa non riflette esattamente ci&amp;ograve; che avevamo in mente per l&amp;rsquo;ultimo passo, ma sistemeremo la cosa pi&amp;ugrave; tardi.&lt;/p&gt;

&lt;p&gt;Prima per&amp;ograve;, implementiamo il modo per tornare indietro nei passi precedenti. Al momento la form ci permette di spostarci in avanti, ma non possiamo tornare indietro e fare delle modifiche ai campi che abbiamo gi&amp;agrave; compilato. Aggiungiamo un altro pulsante alla form per fare questo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;%= render &amp;quot;#{@order.current_step}_step&amp;quot;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Continue&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Back&amp;quot;, :name =&amp;gt; &amp;quot;previous_button&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Si noti che abbiamo dato al pulsate "Back" un attributo denominato &lt;code&gt;name&lt;/code&gt;, in modo tale che si possa capire quale pulsante &amp;egrave; stato utilizzato per fare il submit della form. Ora possiamo cambiare il controller per farlo andare coerentemente, a seconda di quale pulsante &amp;egrave; stato premuto:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @order = Order.new(params[:order])
  @order.current_step = session[:order_step]
  if params[:back_button]
    @order.previous_step
  else
    @order.next_step
  end
  session[:order_step] = @order.current_step
  render &amp;#x27;new&amp;#x27;
end
&lt;/pre&gt;

&lt;p&gt;Affinch&amp;egrave; tutto questo funzioni, dobbiamo aggiungere un metodo &lt;code&gt;previous_step&lt;/code&gt; alla classe di modello &lt;code&gt;Order&lt;/code&gt; che sia simile al metodo &lt;code&gt;next_step&lt;/code&gt; scritto poco fa:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def previous_step
  self.current_step = steps[steps.index(current_step)-1]
end
&lt;/pre&gt;

&lt;p&gt;Ora ci sono entrambi i pulsanti nella form per permettere di spostarsi avanti e indietro fra i vari passi del wizard:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/393/original/E217I03.png" width="800" height="410" alt="La form ora ha i pulsanti per spostarsi avanti e indietro."/&gt;
&lt;/div&gt;

&lt;p&gt;Non ha senso tuttavia che ci sia un pulsante per andare indietro anche sulla prima pagina, per cui imponiamo che venga nascosto se l&amp;rsquo;ordine &amp;egrave; al primo passo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/orders/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;New Order&amp;quot; %&amp;gt;

&amp;lt;% form_for @order do |f| %&amp;gt;
  &amp;lt;%= f.error_messages %&amp;gt;
  &amp;lt;%= render &amp;quot;#{@order.current_step}_step&amp;quot;, :f =&amp;gt; f %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Continue&amp;quot; %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit &amp;quot;Back&amp;quot;, :name =&amp;gt; &amp;quot;previous_button&amp;quot; unless @order.first_step? %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;Back to List&amp;quot;, orders_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;e ovviamente aggiungiamo il metodo &lt;code&gt;first_step?&lt;/code&gt; che viene riferito nel partial alla classe di modello &lt;code&gt;Order&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def first_step?
  current_step == steps.first
end
&lt;/pre&gt;

&lt;p&gt;Quando ricarichiamo la form ora, non vedremo il pulsante per andare indietro sulla prima pagina:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/394/original/E217I04.png" width="800" height="375" alt="Il primo passo non ha pi&amp;ugrave; il pulsante 'Back'"/&gt;
&lt;/div&gt;

&lt;h3&gt;Fare in modo che i campi ricordino i propri valori&lt;/h3&gt;

&lt;p&gt;Ora possiamo andare indietro e avanti fra le pagine della form, ma se inseriamo un valore in uno dei campi, questo non viene ricordato se si ritorna su tale passo pi&amp;ugrave; tardi. Possiamo risolvere questo problema usando nuovamente la sessione. Sebbene non sia raccomandabile memorizzare oggetti complessi in variabili di sessione, per oggetti semplici come hash o array pu&amp;ograve; andare.&lt;/p&gt; 

&lt;p&gt;Il primo passo per fare ci&amp;ograve; &amp;egrave; creare una nuova variabile di sessione nella action &lt;code&gt;new&lt;/code&gt; chiamata &lt;code&gt;order_params&lt;/code&gt; e impostarne il valore ad un hash vuoto a meno che non abbia gi&amp;agrave; un valore:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def new
  session[:order_params] ||= {}
  @order = Order.new
end
&lt;/pre&gt;

&lt;p&gt;Nell&amp;rsquo;action &lt;code&gt;create&lt;/code&gt; raggruppiamo i valori dei parametri dell&amp;rsquo;ordine con i valori della variabile di sessione. Si noti che usiamo una deep merge nel caso ci siano valori innestati fra i parametri dell&amp;rsquo;ordine mentre faremo un semplice merge se esistono parametri di ordine. Possiamo creare un nuovo oggetto &lt;code&gt;Order&lt;/code&gt; da questo hash di merge:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  session[:order_params].deep_merge!(params[:order]) if params[:order]
  @order = Order.new(session[:order_params])
  @order.current_step = session[:order_step]
  if params[:back_button]
    @order.previous_step
  else
    @order.next_step
  end
  session[:order_step] = @order.current_step
  render &amp;#x27;new&amp;#x27;
end
&lt;/pre&gt;

&lt;p&gt;Ora, al riempimento dei dati in form, vedremo i valori immessi nel passo finale, dato che sono stati salvati fra i vari passi:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/395/original/E217I05.png" width="804" height="419" alt="Il passo conclusivo della form che mostra le informazioni salvate."/&gt;
&lt;/div&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; ancora un piccolo problema, comunque. Se ci spostiamo dalla form mentre &amp;egrave; solo parzialmente compilata e subito dopo ritorniamo indietro, le informazioni inserite ed il passo sono persi. Possiamo risolvere questa cosa copiando le due linee che recuperano le informazioni sull&amp;rsquo;ordine e sul passo corrente dalla variabile di sessione che abbiamo scritto per la action create nella action new:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def new
  session[:order_params] ||= {}
  @order = Order.new(session[:order_params])
  @order.current_step = session[:order_step]
end
&lt;/pre&gt;

&lt;p&gt;Sistemato questo codice, ora possiamo ritornare alla form ed ogni dato inserito sar&amp;agrave; ancora l&amp;agrave;. Non solo, ma saremo anche ricondotti all&amp;rsquo;ultimo passo in cui eravamo arrivati.&lt;/p&gt;

&lt;h3&gt;Salvataggio di un ordine&lt;/h3&gt;

&lt;p&gt;Ora renderemo completamente funzionante la form, consentendole di salvare l&amp;rsquo;ordine al termine dell&amp;rsquo;ultimo passo. Possiamo fare ci&amp;ograve;, cambiando il controller in modo tale che salvi l&amp;rsquo;ordine solo se il passo corrente &amp;egrave; l&amp;rsquo;ultimo e se il pulsante premuto per il submit non &amp;egrave; quello per ritornare alla pagina precedente. Dobbiamo anche cambiare il comportamento del rendering in modo che se l&amp;rsquo;ordine viene correttamente salvato, l&amp;rsquo;applicazione alla action show per tale ordine e mostri un messaggio flash:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class OrdersController &amp;lt; ApplicationController
  def index
    @orders = Order.all
  end
  
  def show
    @order = Order.find(params[:id])
  end
  
  def new
    session[:order_params] ||= {}
    @order = Order.new(session[:order_params])
    @order.current_step = session[:order_step]
  end
  
  def create
    session[:order_params].deep_merge!(params[:order]) if params[:order]
    @order = Order.new(session[:order_params])
    @order.current_step = session[:order_step]
    if params[:back_button]
      @order.previous_step
    elsif @order.last_step?
      @order.save
    else
      @order.next_step
    end
    session[:order_step] = @order.current_step
    
    if @order.new_record?
      render &amp;#x27;new&amp;#x27;
    else
      flash[:notice] = &amp;quot;Order saved.&amp;quot;
      redirect_to @order
    end
  end
end
&lt;/pre&gt;

&lt;p&gt;Per fare funzionare il tutto, dobbiamo anche aggiungere il metodo &lt;code&gt;last_step&lt;/code&gt; al modello &lt;code&gt;Order&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def last_step?
  current_step == steps.last
end
&lt;/pre&gt;

&lt;p&gt;Quando andiamo nella form di inserimento di un nuovo ordine, ora, e compiliamo tutte le informazioni per tutti i passi e salviamo, l&amp;rsquo;ordine verr&amp;agrave; salvato e saremo ridiretti alla pagina di dettaglio di tale ordine:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/396/original/E217I06.png" width="801" height="343" alt="L&amp;rsquo;ordine &amp;egrave; stato salvato."/&gt;
&lt;/div&gt;

&lt;p&gt;Non abbiamo ancora finito, per&amp;ograve;. Se proviamo a creare un nuovo ordine, la form andr&amp;agrave; direttamente all&amp;rsquo;ultimo passo e vedremo il dettaglio dell&amp;rsquo;ordine precedente. Dobbiamo cambiare nuovamente il controller in modo tale che le informazioni dell&amp;rsquo;ordine siano cancellate dalla sessione una volta che l&amp;rsquo;ordine &amp;egrave; stato salvato correttamente. Possiamo fare ci&amp;ograve; svuotando le informazioni di sessione una volta salvato l&amp;rsquo;ordine, impostando entrambe le variabili di sessione, &lt;code&gt;order_step&lt;/code&gt; e &lt;code&gt;order_params&lt;/code&gt;, a &lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  session[:order_params].deep_merge!(params[:order]) if params[:order]
  @order = Order.new(session[:order_params])
  @order.current_step = session[:order_step]
  if params[:back_button]
    @order.previous_step
  elsif @order.last_step?
    @order.save
  else
    @order.next_step
  end
  session[:order_step] = @order.current_step
    
  if @order.new_record?
    render &amp;#x27;new&amp;#x27;
  else
    session[:order_step] = session[:order_params] = nil
    flash[:notice] = &amp;quot;Order saved.&amp;quot;
    redirect_to @order
  end
end
&lt;/pre&gt;

&lt;h3&gt;Aggiungere la validazione&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;ultima cosa che trattiamo &amp;egrave; come gestire le validazioni nella form. Imponiamo una validazione per il modello &lt;code&gt;Order&lt;/code&gt; per gli attributi &lt;code&gt;shipping_name&lt;/code&gt; e &lt;code&gt;billing_name&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
validates_presence_of :shipping_name
validates_presence_of :billing_name
&lt;/pre&gt;

&lt;p&gt;Vorremmo che l&amp;rsquo;errore di validazione comparisse solo nel passo opportuno. Aggiungiamo, per questa ragione, una condizione &lt;code&gt;if&lt;/code&gt; ai validatori:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
validates_presence_of :shipping_name, :if =&amp;gt; lambda { |o| o.current_step == &amp;quot;shipping&amp;quot; }
validates_presence_of :billing_name, :if =&amp;gt; lambda { |o| o.current_step == &amp;quot;billing&amp;quot; }
&lt;/pre&gt;  

&lt;p&gt;Vogliamo che la form transiti al passo successivo (o precedente) se l&amp;rsquo;ordine &amp;egrave; valido, per cui dobbiamo cambiare di nuovo la action &lt;code&gt;create&lt;/code&gt; sul controller in modo che controlli che l&amp;rsquo;ordine sia valido. Per fare ci&amp;ograve;, racchiudiamo il codice che cambia il passo e salva il record in un blocco &lt;code&gt;if&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  session[:order_params].deep_merge!(params[:order]) if params[:order]
  @order = Order.new(session[:order_params])
  @order.current_step = session[:order_step]
  if @order.valid?
    if params[:back_button]
      @order.previous_step
    elsif @order.last_step?
      @order.save
    else
      @order.next_step
    end
    session[:order_step] = @order.current_step
  end
  if @order.new_record?
    render &amp;#x27;new&amp;#x27;
  else
    session[:order_step] = session[:order_params] = nil
    flash[:notice] = &amp;quot;Order saved.&amp;quot;
    redirect_to @order
  end
end
&lt;/pre&gt;

&lt;p&gt;Se proviamo ora a creare un nuovo ordine e proviamo a fare il submit, nel primo passo vedremo l&amp;rsquo;errore di validazione per il campo shipping, ma non per il billing:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/397/original/E217I07.png" width="800" height="500" alt="Viene mostrato solo l&amp;rsquo;errore per il passo shipping."/&gt;
&lt;/div&gt;

&lt;p&gt;Analogamente, se andiamo avanti fino al passo della fatturazione e proviamo a lasciare il campo vuoto e a fare il submit, vedremo solo l&amp;rsquo;errore per tale passo:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/398/original/E217I08.png" width="800" height="536" alt="Analogamente a prima, nel passo di fatturazione viene mostrato il solo errore per il campo billing non compilato."/&gt;
&lt;/div&gt;

&lt;p&gt;Possiamo ripulire un po&amp;rsquo; la validazione, spostando le espressioni lambda nei metodi denominati &lt;code&gt;shipping?&lt;/code&gt; e &lt;code&gt;billing?&lt;/code&gt;, cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Order &amp;lt; ActiveRecord::Base
  attr_accessible :shipping_name, :billing_name
  attr_writer :current_step

  validates_presence_of :shipping_name, :if =&amp;gt; :shipping?
  validates_presence_of :billing_name, :if =&amp;gt; :billing?

  # other methods omittted.
    
  def shipping?
    current_step == &amp;quot;shipping&amp;quot;
  end
  
  def billing?
    current_step == &amp;quot;billing&amp;quot;
  end
end
&lt;/pre&gt;

&lt;p&gt;E&amp;rsquo;anche utile avere un metodo che validi tutti i passi in un colpo solo. Per fare ci&amp;ograve;, possiamo scrivere un metodo &lt;code&gt;all_valid?&lt;/code&gt; che iteri su ogni passo e controlli che sia valido:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/order.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def all_valid?
  steps.all? do |step|
    self.current_step = step
    valid?
  end
end
&lt;/pre&gt;

&lt;p&gt;In questo modo, se mai facessimo delle modifiche alle validazioni o al modo in cui funzionano i passi, potremmo essere tranquilli del fatto che nessun passo &amp;egrave; stato invalidato nel processo. Possiamo usare questo nuovo metodo nel controller per assicurarci che l&amp;rsquo;ordine sia salvato solo se tutti i passi sono validi:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/orders_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  session[:order_params].deep_merge!(params[:order]) if params[:order]
  @order = Order.new(session[:order_params])
  @order.current_step = session[:order_step]
  if @order.valid?
    if params[:back_button]
      @order.previous_step
    elsif @order.last_step?
      @order.save if @order.all_valid?
    else
      @order.next_step
    end
    session[:order_step] = @order.current_step
  end
  if @order.new_record?
    render &amp;#x27;new&amp;#x27;
  else
    session[:order_step] = session[:order_params] = nil
    flash[:notice] = &amp;quot;Order saved.&amp;quot;
    redirect_to @order
  end
end
&lt;/pre&gt;

&lt;p&gt;L&amp;rsquo;aspetto pratico del metodo &lt;code&gt;all_valid?&lt;/code&gt; &amp;egrave; che cambia il passo su cui si trova in modo che se uno dei passi non &amp;egrave; valido, quello diventi anche il passo corrente, in modo tale che all&amp;rsquo;utente sia mostrato il primo passo non valido.&lt;/p&gt;

&lt;p&gt;E&amp;rsquo; tutto per questo episodio sulle form multipasso. Una cosa da tenere a mente &amp;egrave; che stiamo salvando i parametri dell&amp;rsquo;ordine in sessione, il che significa che se un utente apre pi&amp;ugrave; finestre o tab del browser, le stesse informazioni saranno condivise fra tali finestre. Se si vuole che pi&amp;ugrave; tab o finestre di un medesimo browser siano trattati in modo indipendente, allora potreste voler salvare i parametri dell&amp;rsquo;ordine sul database e salvare prima il modello dell&amp;rsquo;ordine, usando la action update per salvare le informazioni aggiuntive provenienti dai vari passi. In alternativa, i parametri potrebbero essere salvati in campi nascosti della form.&lt;/p&gt;

&lt;p&gt;Infine, un rapido sguardo alla action create evidenzia che &amp;egrave; diventata pi&amp;ugrave; corposa e complessa di quello che dovrebbe essere. Anche se per gli scopi della lezione questo pu&amp;ograve; anche andare, se questa tecnica fosse applicata in produzione, necessiterebbe di un po&amp;rsquo; di ulteriore refactoring di pulizia.&lt;/p&gt;</description>
      <pubDate>Thu, 17 Jun 2010 18:17:03 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/217-wizard-form</guid>
      <link>http://it.asciicasts.com/episodes/217-wizard-form</link>
    </item>
    <item>
      <title>Generatori in Rails 3</title>
      <description>&lt;p&gt;Se avete usato a fondo Rails, sarete avvezzi all&amp;rsquo;uso dei generatori e avrete sicuramente utilizzato il comando &lt;code&gt;script/generate&lt;/code&gt; per creare modelli, controller, scaffold e roba simile. I generatori in Rails 3 sono stati riscritti e sono completamente diversi da quelli che c&amp;rsquo;erano in Rails 2. Da un lato, ora sono molto pi&amp;ugrave; modulari, il che significa che potete personalizzarli per adattarli alle vostre esigenze.&lt;/p&gt; 

&lt;p&gt;Se lanciamo &lt;code&gt;rails g&lt;/code&gt; dalla cartella radice di un applicazione Rails 3, vediamo l&amp;rsquo;elenco dei generatori disponibili per tale applicazione. Se non abbiamo ancora creato alcun generatore per conto nostro, vedremo solo i generatori inclusi in Rails 3: &lt;code&gt;controller&lt;/code&gt;, &lt;code&gt;generator&lt;/code&gt;, &lt;code&gt;helper&lt;/code&gt;, &lt;code&gt;integration_test&lt;/code&gt;, &lt;code&gt;mailer&lt;/code&gt;, &lt;code&gt;metal&lt;/code&gt;, &lt;code&gt;migration&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;observer&lt;/code&gt;, &lt;code&gt;performance_test&lt;/code&gt;, &lt;code&gt;plugin&lt;/code&gt;, &lt;code&gt;resource&lt;/code&gt;, &lt;code&gt;scaffold&lt;/code&gt;, &lt;code&gt;scaffold_controller&lt;/code&gt;, &lt;code&gt;session_migration&lt;/code&gt; e &lt;code&gt;stylesheets&lt;/code&gt;. Per la maggior parte dei casi, questi generatori funzionano allo stesso modo in cui funzionavano le rispettive controparti Rails 2.&lt;/p&gt;

&lt;p&gt;Se lanciamo un generatore con l&amp;rsquo;opzione &lt;code&gt;--help&lt;/code&gt;, ci vengono mostrate le informazioni di supporto all&amp;rsquo;uso di tale generatore, unitamente ad un elenco di opzioni. Se proviamo questo comando con il generatore scaffold, vediamo che ci sono molte opzioni a disposizione e che queste ci danno parecchia flessibilit&amp;agrave; nella personalizzazione del comportamento del generatore.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g scaffold --help
Usage:
  rails generate scaffold NAME [field:type field:type] [options]

Options:
  -c, --scaffold-controller=NAME  # Scaffold controller to be invoked
                                  # Default: scaffold_controller
      [--singleton]               # Supply to create a singleton controller
      [--force-plural]            # Forces the use of a plural ModelName
  -y, [--stylesheets]             # Indicates when to generate stylesheets
                                  # Default: true
  -o, --orm=NAME                  # Orm to be invoked
                                  # Default: active_record

ScaffoldController options:
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb
      [--helper]                # Indicates when to generate helper
                                # Default: true
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -q, [--quiet]    # Supress status output
  -s, [--skip]     # Skip files that already exist

TestUnit options:
  -r, [--fixture-replacement=NAME]  # Fixture replacement to be invoked
      [--fixture]                   # Indicates when to generate fixture
                                    # Default: true

ActiveRecord options:
  [--parent=PARENT]  # The parent class for the generated model
  [--timestamps]     # Indicates when to generate timestamps
                     # Default: true
  [--migration]      # Indicates when to generate migration
                     # Default: true
&lt;/pre&gt;

&lt;p&gt;Si noti che certe opzioni hanno un valore di default. Per esempio l&amp;rsquo;opzione &lt;code&gt;--stylesheets&lt;/code&gt; di default vale true. Ma che succede se non vogliamo il foglio di stile creato alla generazione dello scaffold? Beh, essendoci un default booleano a &lt;code&gt;true&lt;/code&gt;, possiamo prefissare l&amp;rsquo;opzione con un &lt;code&gt;--no&lt;/code&gt; per disabilitare la stessa. Per farvi vedere il tutto con un esempio, creiamo un nuovo scaffold chiamato &lt;code&gt;project&lt;/code&gt; privo di foglio di stile.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g scaffold project name:string --no-stylesheets
      invoke  active_record
      &lt;span class="passed"&gt;create&lt;/span&gt;    db/migrate/20100602201538_create_projects.rb
      &lt;span class="passed"&gt;create&lt;/span&gt;    app/models/project.rb
      invoke    test_unit
      &lt;span class="passed"&gt;create&lt;/span&gt;      test/unit/project_test.rb
      &lt;span class="passed"&gt;create&lt;/span&gt;      test/fixtures/projects.yml
       route  resources :projects
      invoke  scaffold_controller
      &lt;span class="passed"&gt;create&lt;/span&gt;    app/controllers/projects_controller.rb
      invoke    erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects/index.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects/edit.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects/show.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects/new.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/projects/_form.html.erb
      invoke    test_unit
      &lt;span class="passed"&gt;create&lt;/span&gt;      test/functional/projects_controller_test.rb
      invoke    helper
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/helpers/projects_helper.rb
      invoke      test_unit
      &lt;span class="passed"&gt;create&lt;/span&gt;        test/unit/helpers/projects_helper_test.rb
&lt;/pre&gt;      

&lt;p&gt;Se guardiamo l&amp;rsquo;output del comando, vediamo che non sono stati generati fogli di stile dal momento che abbiamo disabilitato l&amp;rsquo;opzione relativa.&lt;/p&gt;

&lt;p&gt;Oltre all&amp;rsquo;elenco dei file creati, l&amp;rsquo;output generato dal lancio del generatore mostra una serie di chiamate ad &lt;code&gt;invoke&lt;/code&gt;: queste linee indicano l&amp;rsquo;invocazione di altri generatori. Ci&amp;ograve; significa che il generatore dello scaffold non fa in pratica un granch&amp;egrave; di per s&amp;egrave;, ma piuttosto delega ad altri generatori come &lt;code&gt;active_record&lt;/code&gt;, che crea il file di modello e il file di magration. Il generatore  &lt;code&gt;active_record&lt;/code&gt; a sua volta chiama un altro generatore, &lt;code&gt;test_unit&lt;/code&gt;, per creare i file dei test unitari e una fixture. Questo pattern si ripete anche oltre nel generatore &lt;code&gt;scaffold_controller&lt;/code&gt;, che invoca i generatori erb, &lt;code&gt;test_unit&lt;/code&gt; e &lt;code&gt;helper&lt;/code&gt; per creare tutti i file associati al controller e alle viste. Il pattern rende il sistema molto modulare, con la conseguenza che potremmo sostituire alcuni generatori con altri. Se volessimo usare Haml, anzich&amp;egrave; erb, nelle viste, o Shoulda o RSpec al posto di Test::Unit per i test, potremmo agganciare questi generatori ed essere gi&amp;agrave; a posto con tutto.&lt;/p&gt;

&lt;p&gt;Ora che sappiamo tutto questo, possiamo riguardare l&amp;rsquo;elenco di opzioni fornite dall&amp;rsquo;help del generatore scaffold: dovrebbero apparire pi&amp;ugrave; sensate. Per esempio, l&amp;rsquo;opzione &lt;code&gt;--orm&lt;/code&gt; ci consente di cambiare l&amp;rsquo;ORM che genera i modelli, in modo tale da poter usare, ad esempio, DataMapper al posto del default ActiveRecord. Pi&amp;ugrave; in gi&amp;ugrave; nella lista delle opzioni c&amp;rsquo;&amp;egrave; una lista di opzioni specifiche per ActiveRecord, che ovviamente riguardano la versione ActiveRecord del generatore, unitamente alle opzioni per i generatori TestUnit e ScaffoldController.&lt;/p&gt;

&lt;h3&gt;Cambiamo le opzioni di default&lt;/h3&gt;

&lt;p&gt;Anche se &amp;egrave; utile poter passare opzioni tipo &lt;code&gt;--no-stylesheets&lt;/code&gt; ai generatori, lo sarebbe ancor di pi&amp;ugrave; se potessimo cambiare proprio il comportamento di default delle opzioni, e in Rails 3 ci&amp;ograve; &amp;egrave; possibile, a livello di singola applicazione, modificando il file &lt;code&gt;application.rb&lt;/code&gt;. Il file di default &lt;code&gt;application.rb&lt;/code&gt; generato alla creazione dell&amp;rsquo;applicazione mostra le seguenti sezioni al suo interno, commentate:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
# Configure generators values. Many other options are available, be sure to check the documentation.
# config.generators do |g|
#   g.orm             :active_record
#   g.template_engine :erb
#   g.test_framework  :test_unit, :fixture =&amp;gt; true
# end
&lt;/pre&gt;

&lt;p&gt;Se de-commentiamo questa sezione, possiamo ridefinire il comportamento di default delle opzioni per tutti i generatori dell&amp;rsquo;applicazione. Le opzioni fornite a titolo di esempio rispecchiano anche i valori di default, ma possiamo sostituire i valori con quelli che vogliamo noi. Per rendere l&amp;rsquo;opzione stylesheets &lt;code&gt;false&lt;/code&gt; per default, possiamo scrivere:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
# Configure generators values. Many other options are available, be sure to check the documentation.
config.generators do |g|
  g.stylesheets false
end
&lt;/pre&gt;

&lt;p&gt;Se lanciamo nuovamente il comando help per il generatore scaffold, saranno riflessi i nuovi valori di default e l&amp;rsquo;opzione &lt;code&gt;--stylesheets&lt;/code&gt; non comparir&amp;agrave; pi&amp;ugrave; con un default a &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g scaffold --help
Usage:
  rails generate scaffold NAME [field:type field:type] [options]

Options:
  -y, [--stylesheets]             # Indicates when to generate stylesheets
  -c, --scaffold-controller=NAME  # Scaffold controller to be invoked
                                  # Default: scaffold_controller
      [--singleton]               # Supply to create a singleton controller
      [--force-plural]            # Forces the use of a plural ModelName
  -o, --orm=NAME                  # Orm to be invoked
                                  # Default: active_record
&lt;/pre&gt;

&lt;p&gt;Ora proviamo qualcosa di pi&amp;ugrave; ardito: cambiamo il framework di test in modo che sia Shoulda anzich&amp;egrave; Test::Unit e sostituiamo le fixture con Factory Girl, cambiando l&amp;rsquo;opzione di default di &lt;code&gt;fixture-replacement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Per fare ci&amp;ograve;, per prima cosa dobbiamo dichiarare i rispettivi gem nel &lt;code&gt;Gemfile&lt;/code&gt; dell&amp;rsquo;applicazione. Abbiamo bisogno di questi gem solamente nell&amp;rsquo;ambiente di test, per cui li aggiungiamo all&amp;rsquo;interno del gruppo specifico:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/Gemfile&lt;/p&gt;
&lt;pre class="ruby"&gt;
group :test do
  gem &amp;quot;shoulda&amp;quot;
  gem &amp;quot;factory_girl&amp;quot;
end
&lt;/pre&gt;

&lt;p&gt;Sfortunatamente questi due gem non includono i generatori per Rails 3, ma possiamo usare un altro gem chiamato &lt;a href="http://github.com/indirect/rails3-generators"&gt;rails3-generators&lt;/a&gt; che include i generatori per una serie di plugin e gem di Rails 3, incluso Factory Girl e Shoulda. Aggiungeremo anche questi gem al Gemfile, ma solo per il gruppo development, in modo tale che siano disponibili solo in tale ambiente:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/Gemfile&lt;/p&gt;
&lt;pre class="ruby"&gt;
gem &amp;quot;rails3-generators&amp;quot;, :group =&amp;gt; :development
&lt;/pre&gt;

&lt;p&gt;Fatto ci&amp;ograve; lanciamo:&lt;/p&gt;

&lt;pre class="terminal"&gt;
bundle install
&lt;/pre&gt;

&lt;p&gt;per installare i gem. Una volta che i gem sono stati installati, possiamo lanciare &lt;code&gt;rails g&lt;/code&gt; per vedere un elenco dei generatori disponibili. In fondo all&amp;rsquo;elenco, ci dovrebbero ora essere tutti i generatori che sono stati creati dal gem rails3-generators.&lt;/p&gt;

&lt;p&gt;Ora che abbiamo questi nuovi geneatori, possiamo modificare la configurazione dei generatori nell&amp;rsquo;&lt;code&gt;application.rb&lt;/code&gt; e aggiungere i default che vogliamo per le opzioni &lt;code&gt;test_framework&lt;/code&gt; e &lt;code&gt;fixture_replacement&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
# Configure generators values. Many other options are available, be sure to check the documentation.
config.generators do |g|
  g.stylesheets false
  g.test_framework :shoulda
  g.fixture_replacement :factory_girl
end
&lt;/pre&gt;

&lt;p&gt;Al lancio del comando di help per il generatore di scaffold, ora, vedremo le nostre modifiche ai valori di default.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g scaffold --help
Usage:
  rails generate scaffold NAME [field:type field:type] [options]
  ...

ActiveRecord options:
  [--parent=PARENT]      # The parent class for the generated model
  [--migration]          # Indicates when to generate migration
                         # Default: true
  [--timestamps]         # Indicates when to generate timestamps
                         # Default: true
  [--textframework=NAME] # Test framework to be invoked
                         # Default: shoulda

Shoulda options:
  [--fixture-replacement=NAME]  # Fixture replacement to be invoked
                                # Default: factory_girl
  [--dir=DIR]                   # The directory where the model tests should go
                                # Default: test/unit
&lt;/pre&gt;                              

&lt;p&gt;Possiamo testare il tutto generando un nuovo scaffold per un modello &lt;code&gt;task&lt;/code&gt;.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g scaffold task project_id:integer name:string
      invoke  active_record
      &lt;span class="passed"&gt;create&lt;/span&gt;    db/migrate/20100604202823_create_tasks.rb
      &lt;span class="passed"&gt;create&lt;/span&gt;    app/models/task.rb
      invoke    shoulda
      &lt;span class="passed"&gt;create&lt;/span&gt;      test/unit/task_test.rb
      invoke      factory_girl
      &lt;span class="passed"&gt;create&lt;/span&gt;        test/factories/tasks.rb
       route  resources :tasks
      invoke  scaffold_controller
      &lt;span class="passed"&gt;create&lt;/span&gt;    app/controllers/tasks_controller.rb
      invoke    erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks/index.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks/edit.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks/show.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks/new.html.erb
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/views/tasks/_form.html.erb
       &lt;span class="failed"&gt;error&lt;/span&gt;    shoulda [not found]
      invoke    helper
      &lt;span class="passed"&gt;create&lt;/span&gt;      app/helpers/tasks_helper.rb
       &lt;span class="failed"&gt;error&lt;/span&gt;      shoulda [not found]
&lt;/pre&gt; 

&lt;p&gt;In cima all&amp;rsquo;output vediamo che i generatori &lt;code&gt;shoulda&lt;/code&gt; e &lt;code&gt;factory_girl&lt;/code&gt; sono stati invocati correttamente, ma che un po&amp;rsquo; dopo, nel generatore &lt;code&gt;scaffold_controller&lt;/code&gt;, non si trova un generatore shoulda per il file erb o per l&amp;rsquo;helper. Se diamo nuovamente un&amp;rsquo;occhiata all&amp;rsquo;elenco degli helper, possiamo notare come di fatto abbiamo a disposizione i generatori shoulda solamente per i modelli e i controller:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ rails g
  ...
Shoulda:
  shoulda:controller
  shoulda:model
&lt;/pre&gt;  

&lt;p&gt;Come dobbiamo procedere, dunque? Beh, leggendo la &lt;a href="http://guides.rails.info/generators.html#adding-generators-fallbacks"&gt;pagina delle Rails Guides per i generatori&lt;/a&gt; vediamo che &amp;egrave; possibile aggiungere dei generatori in cascata in modo tale che se un determinato generatore non viene trovato, ne venga usato uno alternativo. Tutto ci&amp;ograve; che dobbiamo fare, dunque, &amp;egrave; modificare nuovamente il blocco config in &lt;code&gt;application.rb&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/application.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
# Configure generators values. Many other options are available, be sure to check the documentation.
config.generators do |g|
  g.stylesheets false
  g.test_framework :shoulda
  g.fallbacks[:shoulda] = :test_unit 
  g.fixture_replacement :factory_girl
end
&lt;/pre&gt;

&lt;p&gt;Ora potremo finalmente generare senza errori gli scaffold con Shoulda e i generatori usati saranno quelli di Test::Unit, nel caso in cui quelli propri di Shoulda non vengano rintracciati.&lt;/p&gt;

&lt;h3&gt;Personalizzazione dei template&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;ultima cosa che trattiamo in questo episodio &amp;egrave; il modo per personalizzare i template generati. Di sotto &amp;egrave; riportata il codice di default della vista per l&amp;rsquo;action &lt;code&gt;index&lt;/code&gt; per il controller dei task che abbiamo appena generato.&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/tasks/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h1&amp;gt;Listing tasks&amp;lt;/h1&amp;gt;

&amp;lt;table&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
  &amp;lt;/tr&amp;gt;

&amp;lt;% @tasks.each do |task| %&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Show&amp;#x27;, task %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Edit&amp;#x27;, edit_task_path(task) %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Destroy&amp;#x27;, task, :confirm =&amp;gt; &amp;#x27;Are you sure?&amp;#x27;, :method =&amp;gt; :delete %&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/table&amp;gt;

&amp;lt;br /&amp;gt;

&amp;lt;%= link_to &amp;#x27;New Task&amp;#x27;, new_task_path %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Poniamo di voler togliere il tag di ritorno a capo presente alla fine del template e di voler anche racchiudere l&amp;rsquo;ultimo link all&amp;rsquo;interno di un elemento di paragrafo. Come possiamo cambiare il template affinch&amp;egrave; ogni vista index generata sia personalizzata in questo modo?&lt;/p&gt;

&lt;p&gt;Per far ci&amp;ograve;, possiamo creare un nuovo template personalizzato all&amp;rsquo;interno di una nuova cartella &lt;code&gt;templates&lt;/code&gt;, sotto la cartella &lt;code&gt;lib&lt;/code&gt; dell&amp;rsquo;applicazione. I generatori andranno a vedere qui dentro alla ricerca dei file di template prima di utilizzare i loro default. Al fine di capire quale template sia da ridefinire, dovremo dare un&amp;rsquo;occhiata al &lt;a href="http://github.com/rails/rails"&gt;codice sorgente di Rails&lt;/a&gt;. Il codice per i generatori &amp;egrave; contenuto nella cartella &lt;a href="http://github.com/rails/rails/tree/master/railties/lib/rails/generators/"&gt;railties/lib/rails/generators&lt;/a&gt; ed i template di default delle viste sono in una sottocartella &lt;a href="http://github.com/rails/rails/tree/master/railties/lib/rails/generators/erb/scaffold/templates/"&gt;erb/scaffold/templates/&lt;/a&gt;. Possiamo copiare il contenuto del file  &lt;code&gt;index.html.erb&lt;/code&gt; in questa cartella e personalizzarlo secondo il nostro volere.&lt;/p&gt;

&lt;p&gt;Dobbiamo creare una strutturas di cartelle analoga sotto alla cartella dei generatori e perci&amp;ograve; il nostro template personalizzato index deve trovarsi in &lt;code&gt;/lib/templates/erb/scaffold/index.html.erb&lt;/code&gt;. Dopodich&amp;egrave; possiamo incollargli dentro il contenuto del file di template di default, modificato come volevamo. Un volta fatto ci&amp;ograve;, alla creazione di un nuovo scaffold, per esempio per un modello chiamato &lt;code&gt;Category&lt;/code&gt;, vedremo la vista index generata a partire dal nostro template personalizzato.&lt;/p&gt;

&lt;pre class="terminal"&gt;
rails g scaffold category name:string
&lt;/pre&gt;

&lt;p class="codeFilePath"&gt;/app/views/categories/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;h1&amp;gt;Listing categories&amp;lt;/h1&amp;gt;

&amp;lt;table&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;
  &amp;lt;/tr&amp;gt;

&amp;lt;% @categories.each do |category| %&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= category.name %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Show&amp;#x27;, category %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Edit&amp;#x27;, edit_category_path(category) %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to &amp;#x27;Destroy&amp;#x27;, category, :confirm =&amp;gt; &amp;#x27;Are you sure?&amp;#x27;, :method =&amp;gt; :delete %&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/table&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;#x27;New Category&amp;#x27;, new_category_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;E&amp;rsquo; tutto per questo episodio. I generatori in Rails 3 si sono nettamente evoluti rispetto a quelli presenti in Rails 2 e sono anche molto pi&amp;ugrave; semplici da personalizzare.&lt;/p&gt;</description>
      <pubDate>Fri, 11 Jun 2010 20:39:43 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/216-generatori-in-rails-3</guid>
      <link>http://it.asciicasts.com/episodes/216-generatori-in-rails-3</link>
    </item>
    <item>
      <title>Query avanzate in Rails 3</title>
      <description>&lt;p&gt;Questo episodio tratter&amp;agrave; le query avanzate di Rails 3. Nell&amp;rsquo;episodio 202 [&lt;a href="http://railscasts.com/episodes/202-active-record-queries-in-rails-3"&gt;guardalo&lt;/a&gt;, &lt;a href="http://it.asciicasts.com/episodes/203-routing-in-rails-3"&gt;leggilo&lt;/a&gt;] abbiamo parlato delle novit&amp;agrave; alle query di ActiveRecord di Rails 3; oggi proseguiremo il discorso da l&amp;igrave; e mostreremo alcune altre feature avanzate.&lt;/p&gt;

&lt;h3&gt;Utilizzo dei metodi di classe al posto degli scope&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;applicazione che useremo ha due modelli: &lt;code&gt;Product&lt;/code&gt; e &lt;code&gt;Category&lt;/code&gt; con il prodotto che appartiene ad una categoria. Il modello prodotto ha due named scope: &lt;code&gt;discontinued&lt;/code&gt;, che restituisce tutti i prodotti per i quali il valore del campo discontinued &amp;egrave; true e price, che restituisce i prodotti pi&amp;ugrave; economici del prezzo passato come argomento:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  scope :discontinued, where(:discontinued =&amp;gt; true)
  scope :cheaper_than, lambda { |price| where(&amp;quot;price &amp;lt; ?&amp;quot;, price) }
end
&lt;/pre&gt;

&lt;p&gt;Nel secondo named scope si usa un lambda. Se non ne avete mai usato uno di questi in un named scope, potreste valutare piuttosto l&amp;rsquo;uso di un metodo di classe, specialmente se state passando un consistente numero di parametri o se il contenuto dello scope &amp;egrave; complesso. Il nostro invece &amp;egrave; piuttosto semplice, ma lo cambiamo ugualmente con un metodo di classe:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  scope :discontinued, where(:discontinued =&amp;gt; true)
  
  def self.cheaper_than(price)
    where(&amp;quot;price &amp;lt; ?&amp;quot;, price)
  end
end
&lt;/pre&gt;

&lt;p&gt;Il metodo di classe si comporter&amp;agrave; alla stessa maniera dello scope. Possiamo fare la stessa cosa anche in Rails 2, ma si comporta molto meglio in Rails 3. Possiamo perfino riusare questo metodo in un altro named scope. Se vogliamo creare uno scope chiamato &lt;code&gt;cheap&lt;/code&gt; che restituisce i prodotti che costano meno di cinque euro, possiamo scrivere:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/products.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
scope :cheap, cheaper_than(5)
&lt;/pre&gt;

&lt;p&gt;Tuttavia potenzialmente ci pu&amp;ograve; essere un&amp;rsquo;insidia in tutto ci&amp;ograve;. Se usiamo un metodo di classe in uno scope, dobbiamo definire tale scope, all&amp;rsquo;interno della classe, dopo che &amp;egrave; stato definito il metodo di classe, il che significa che lo scope dovr&amp;agrave; essere messo pi&amp;ugrave; in basso nella classe rispetto al solito.&lt;/p&gt;

&lt;p&gt;Usando la console Rails, possiamo vedere l&amp;rsquo;SQL generato dallo scope:&lt;/p&gt;

&lt;pre class="terminal"&gt;
ruby-1.8.7-p249 &amp;gt; Product.cheap.to_sql
 =&amp;gt; &amp;quot;SELECT    \&amp;quot;products\&amp;quot;.* FROM      \&amp;quot;products\&amp;quot; WHERE    (price &amp;lt; 5)&amp;quot;
&lt;/pre&gt; 

&lt;p&gt;Se chiamiamo semplicemente lo scope, vedremo la lista dei prodotti pi&amp;ugrave; economici:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt;Product.cheap
 =&amp;gt; [#&amp;lt;Product id: 1, name: &amp;quot;Top&amp;quot;, price: 4.99, discontinued: nil, category_id: 3, created_at: &amp;quot;2010-05-24 21:01:59&amp;quot;, updated_at: &amp;quot;2010-05-24 21:01:59&amp;quot;&amp;gt;, #&amp;lt;Product id: 2, name: &amp;quot;Milk&amp;quot;, price: 2.99, discontinued: nil, category_id: 2, created_at: &amp;quot;2010-05-24 21:02:38&amp;quot;, updated_at: &amp;quot;2010-05-24 21:02:38&amp;quot;&amp;gt;]
&lt;/pre&gt;

&lt;h3&gt;Associazioni&lt;/h3&gt;

&lt;p&gt;Finch&amp;egrave; siamo in console, vi mostriamo un altro trucco, che coinvolge l&amp;rsquo;utilizzo degli scope mediante le associazioni. Come abbiamo citato in precedenza, nella nostra applicazione un prodotto appartiene ad una categoria. Ci&amp;ograve; significa che possiamo usare il metodo &lt;code&gt;joins&lt;/code&gt; per ottenere una query SQL che esegua una inner join fra le tabelle prodotti e categorie:&lt;/p&gt;
&lt;pre class="terminal"&gt;
ruby-1.8.7-p249 &amp;gt; Category.joins(:products).to_sql
 =&amp;gt; &amp;quot;SELECT     \&amp;quot;categories\&amp;quot;.* FROM       \&amp;quot;categories\&amp;quot; INNER JOIN \&amp;quot;products\&amp;quot; ON \&amp;quot;products\&amp;quot;.\&amp;quot;category_id\&amp;quot; = \&amp;quot;categories\&amp;quot;.\&amp;quot;id\&amp;quot;&amp;quot;
&lt;/pre&gt; 

&lt;p&gt;Vogliamo trovare tutte le categorie che hanno un prodotto che faccia match con un certo scope; tanto per fissare le idee, vogliamo trovare tutte le categorie che hanno almeno un prodotto che costi meno di cinque euro. Ci sono due metodi che possiamo usare a questo scopo. Possiamo usare il metodo &lt;code&gt;merge&lt;/code&gt; in questa maniera:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; Category.joins(:products).merge(Product.cheap)
 =&amp;gt; [#&amp;lt;Category id: 3, name: &amp;quot;Games&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:57&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:18&amp;quot;&amp;gt;, #&amp;lt;Category id: 2, name: &amp;quot;Groceries&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:50&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:39&amp;quot;&amp;gt;]
&lt;/pre&gt; 

&lt;p&gt;Oppure in alternativa possiamo usare una e commerciale che &amp;egrave; un alias alla stessa cosa, per cui restituir&amp;agrave; lo stesso risultato:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; Category.joins(:products) &amp;amp; Product.cheap
 =&amp;gt; [#&amp;lt;Category id: 3, name: &amp;quot;Games&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:57&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:18&amp;quot;&amp;gt;, #&amp;lt;Category id: 2, name: &amp;quot;Groceries&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:50&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:39&amp;quot;&amp;gt;]
&lt;/pre&gt; 

&lt;p&gt;Usando questi metodi, possiamo mettere in join qualsiasi altra query, anche se non si trova sullo stesso modello, consentendoci di trovare tutte le categorie che hanno dei prodotti che costano meno di cinque euro. Se invochiamo la &lt;code&gt;to_sql&lt;/code&gt; sulle query in join vedremo che &amp;egrave; stata usata la inner join e che la clausola di WHERE del named scope &amp;egrave; stata aggiunta in fondo all&amp;rsquo;istruzione:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; (Category.joins(:products) &amp;amp; Product.cheap).to_sql
 =&amp;gt; &amp;quot;SELECT     \&amp;quot;categories\&amp;quot;.* FROM       \&amp;quot;categories\&amp;quot; INNER JOIN \&amp;quot;products\&amp;quot; ON \&amp;quot;products\&amp;quot;.\&amp;quot;category_id\&amp;quot; = \&amp;quot;categories\&amp;quot;.\&amp;quot;id\&amp;quot; WHERE     (price &amp;lt; 5)&amp;quot;
&lt;/pre&gt; 
 
&lt;p&gt;Questa pu&amp;ograve; rivelarsi una tecnica potente, allorch&amp;egrave; usata all&amp;rsquo;interno della definizione di un named scope. La possiamo usare, per esempio, per creare uno scope nel modello &lt;code&gt;Category&lt;/code&gt;, che trovi le categorie aventi prodotti economici:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/category.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Category &amp;lt; ActiveRecord::Base
  has_many :products
  scope :with_cheap_products, joins(:products) &amp;amp; Product.cheap
end
&lt;/pre&gt;

&lt;p&gt;Questo nuovo named scope restituir&amp;agrave; le stesse categorie della query in join che abbiamo scritto poco fa:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; Category.with_cheap_products
 =&amp;gt; [#&amp;lt;Category id: 3, name: &amp;quot;Games&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:57&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:18&amp;quot;&amp;gt;, #&amp;lt;Category id: 2, name: &amp;quot;Groceries&amp;quot;, created_at: &amp;quot;2010-05-24 21:00:50&amp;quot;, updated_at: &amp;quot;2010-05-25 18:30:39&amp;quot;&amp;gt;]
&lt;/pre&gt; 

&lt;p&gt;Una cosa di cui stare attenti quando si tratta con condizioni fra associazioni &amp;egrave; il nome delle tabelle. Se diamo un&amp;rsquo;occhiata all&amp;rsquo;SQL prodotto alla chiamata del named scope, possiamo vedere che non c&amp;rsquo;&amp;egrave; alcun nome di tabella che qualifichi il campo nella clausola di WHERE:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; Category.with_cheap_products.to_sql
 =&amp;gt; &amp;quot;SELECT     \&amp;quot;categories\&amp;quot;.* FROM       \&amp;quot;categories\&amp;quot; INNER JOIN \&amp;quot;products\&amp;quot; ON \&amp;quot;products\&amp;quot;.\&amp;quot;category_id\&amp;quot; = \&amp;quot;categories\&amp;quot;.\&amp;quot;id\&amp;quot; WHERE     (price &amp;lt; 5)&amp;quot;
&lt;/pre&gt; 

&lt;p&gt;Questa cosa sarebbe un problema se entrambe le tabelle avessero una colonna chiamata price, perch&amp;egrave; si creerebbe un&amp;rsquo;ambiguit&amp;agrave; sul nome della colonna nella query SQL. Si pu&amp;ograve; superare questa ambiguit&amp;agrave; definendo esplicitamente il nome della tabella nel modello &lt;code&gt;Product&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def self.cheaper_than(price) 
  where(&amp;quot;products.price &amp;lt; ?&amp;quot;, price)
end
&lt;/pre&gt;

&lt;p&gt;Ora non c&amp;rsquo;&amp;egrave; pi&amp;ugrave; alcun pericolo di ambiguit&amp;agrave; sul nome della colonna. Dovreste sempre specificare il nome della tabella quando usate stringhe SQL nelle condizioni di find. Comunque, se state usando un hash, come negli scope, non c&amp;rsquo;&amp;egrave; bisogno di preoccuparsi del nome della tabella, dal momento che Rails lo aggiunge in automatico per noi.&lt;/p&gt;

&lt;h3&gt;Costruire record mediante named scope&lt;/h3&gt;

&lt;p&gt;La prossima cosa che vi mostriamo &amp;egrave; il modo di costruire record usando i named scope. Abbiamo un named scope definito nella nostra classe di modello &lt;code&gt;Product&lt;/code&gt;, chiamato discontinued, che trova tutti i prodotti dismessi:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; Product.discontinued
 =&amp;gt; [#&amp;lt;Product id: 3, name: &amp;quot;Some DVD&amp;quot;, price: 13.49, discontinued: true, category_id: 1, created_at: &amp;quot;2010-05-25 19:45:05&amp;quot;, updated_at: &amp;quot;2010-05-25 19:45:05&amp;quot;&amp;gt;]
&lt;/pre&gt; 

&lt;p&gt;Poich&amp;egrave; il named scope usa un hash, possiamo chiamare il metodo &lt;code&gt;build&lt;/code&gt; questo per costruire un nuovo prodotto con l&amp;rsquo;attributo &lt;code&gt;discontinued&lt;/code&gt; gi&amp;agrave; impostato a true:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; p = Product.discontinued.build
 =&amp;gt; #&amp;lt;Product id: nil, name: nil, price: nil, discontinued: true, category_id: nil, created_at: nil, updated_at: nil&amp;gt;
&lt;/pre&gt; 

&lt;p&gt;Il nuovo prodotto cos&amp;igrave; creato risulter&amp;agrave; gi&amp;agrave; dismesso in quanto questo attributo fa parte della condizione di &lt;code&gt;where&lt;/code&gt;. Tutto ci&amp;ograve; funziona in maniera simile ad una associazione, in quanto quando si chiama il metodo build sull&amp;rsquo;associazione, automaticamente viene impostata la foreign key associata a quel record. In questo caso, tuttavia, stiamo pi&amp;ugrave; semplicemente assegnando il valore agli attributi che sono inclusi nella condizione di &lt;code&gt;where&lt;/code&gt;. Questa &amp;egrave; una cosa utile da sapere se si ha la necessit&amp;agrave; di creare dei record che rispettino un determinato named scope.&lt;/p&gt;

&lt;h3&gt;Arel&lt;/h3&gt;

&lt;p&gt;Concludiamo questo episodio presentando &lt;a href="http://github.com/rails/arel"&gt;Arel&lt;/a&gt;. Arel gestisce le query ActiveRecord dietro le quinte. Probabilmente non avrete molto spesso il bisogno di interfacciarvi direttamente con questo livello, ma &amp;egrave; utile capire come il tutto funzioni e comprendere ci&amp;ograve; di cui questo framework &amp;egrave; capace, nel caso in cui ci sia mai il bisogno di doverlo usare direttamente.&lt;/p&gt;

&lt;p&gt;Per accedere direttamente ad Arel in Rails 3, si pu&amp;ograve; richiedere al modello una &lt;code&gt;arel_table&lt;/code&gt;, per cui prendiamo l&amp;rsquo;&lt;code&gt;arel_table&lt;/code&gt; per il modello Product dalla console e assegnamola ad una variabile:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; t = Product.arel_table
&lt;/pre&gt;

&lt;p&gt;Questo oggetto &amp;egrave; una rappresentazione della tabella dei prodotti. Possiamo avere accesso alle colonne nella tabella in questo modo:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt;t[:price]
 =&amp;gt; &amp;lt;Attribute price&amp;gt;
&lt;/pre&gt; 

&lt;p&gt;Possiamo chiamare metodi sugli attributi per eseguire delle condizioni di find. Per esempio, se vogliamo trovare tutti i prodotti che costano 2.99 euro, possiamo lanciare:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; t[:price].eq(2.99)
 =&amp;gt; #&amp;lt;Arel::Predicates::Equality:0x1040dd9f0 @operand1=&amp;lt;Attribute price&amp;gt;, @operand2=2.99&amp;gt;
&lt;/pre&gt; 

&lt;p&gt;Questa istruzione restituisce un predicato che fondamentalmente &amp;egrave; la condizione di find. Ci sono anche altri metodi che possiamo chiamare, come il metodo &lt;code&gt;matches&lt;/code&gt;, che esegue una query di tipo LIKE. Come per le altre query, possiamo invocare il &lt;code&gt;to_sql&lt;/code&gt; per vedere l&amp;rsquo;effettivo codice SQL prodotto dalla query.&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; t[:name].matches(&amp;#x27;%lore&amp;#x27;).to_sql
 =&amp;gt; &amp;quot;\&amp;quot;products\&amp;quot;.\&amp;quot;name\&amp;quot; LIKE &amp;#x27;%lore&amp;#x27;&amp;quot;
&lt;/pre&gt; 

&lt;p&gt;Possiamo usare il metodo &lt;code&gt;or&lt;/code&gt; per concatenare i predicati, per cui, per esempio, possiamo cercare i prodotti che costano 2.99 euro o che hanno un nome che termina per &amp;lsquo;lore&amp;rsquo;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
t[:price].eq(2.99).or(t[:name].matches(&amp;#x27;%lore&amp;#x27;))
&lt;/pre&gt;

&lt;p&gt;Questa istruzione restituir&amp;agrave; una combinazione dei due predicati: possiamo vedere l&amp;rsquo;SQL prodotto chiamando &lt;code&gt;to_sql&lt;/code&gt; come abbiamo fatto anche prima:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt; t[:price].eq(2.99).or(t[:name].matches(&amp;#x27;%lore&amp;#x27;)).to_sql
 =&amp;gt; &amp;quot;(\&amp;quot;products\&amp;quot;.\&amp;quot;price\&amp;quot; = 2.99 OR \&amp;quot;products\&amp;quot;.\&amp;quot;name\&amp;quot; LIKE &amp;#x27;%lore&amp;#x27;)&amp;quot;
&lt;/pre&gt; 

&lt;p&gt;Possiamo passare i predicati come argomenti al metodo &lt;code&gt;where&lt;/code&gt; di un oggetto ActiveRecord. Ci&amp;ograve; significa che possiamo passare il predicato che abbiamo creato alla chiamata &lt;code&gt;Product.where&lt;/code&gt; al fine di restituire tutti i prodotti il cui prezzo sia 2.99 euro, o il cui nome termini per &amp;lsquo;lore&amp;rsquo;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
&amp;gt;   Product.where(t[:price].eq(2.99).or(t[:name].matches(&amp;#x27;%lore&amp;#x27;)))
 =&amp;gt; [#&amp;lt;Product id: 2, name: &amp;quot;Milk&amp;quot;, price: 2.99, discontinued: nil, category_id: 2, created_at: &amp;quot;2010-05-24 21:02:38&amp;quot;, updated_at: &amp;quot;2010-05-24 21:02:38&amp;quot;&amp;gt;, #&amp;lt;Product id: 4, name: &amp;quot;Knight Lore&amp;quot;, price: 2.99, discontinued: nil, category_id: nil, created_at: &amp;quot;2010-05-26 19:36:02&amp;quot;, updated_at: &amp;quot;2010-05-26 19:36:02&amp;quot;&amp;gt;]
&lt;/pre&gt;

&lt;p&gt;Abbiamo solo dato uno sguardo rapido ad Arel in queste poche righe; Arel &amp;egrave; in grado di fare molte pi&amp;ugrave; cose e ActiveRecord espone solo una piccola parte di queste. Ci sono una serie di plugin che sfruttano tutta la potenza di Arel e la rendono disonibile in una interfaccia pi&amp;ugrave; semplice, come ad esempio &lt;a href="http://metautonomo.us/projects/metawhere/"&gt;MetaWhere&lt;/a&gt;. Questo plugin da accesso ai metodi di Arel come il &lt;code&gt;matches&lt;/code&gt; e l&amp;rsquo;&lt;code&gt;eq&lt;/code&gt;  dall&amp;rsquo;interno dell&amp;rsquo;hash delle condizioni, in questo modo:&lt;/p&gt;

&lt;pre class="ruby"&gt;
Product.where(:price.eq =&amp;gt; 2.99, :name.matches =&amp;gt; &amp;#x27;%lore&amp;#x27;)
&lt;/pre&gt;

&lt;p&gt;Questo vi da molta pi&amp;ugrave; flessibilit&amp;agrave; sulla definizione dell&amp;rsquo;hash delle condizioni e vale la pena darci un&amp;rsquo;occhiata se avete la necessit&amp;agrave; di eseguire query pi&amp;ugrave; complesse sui vostri modelli.&lt;/p&gt;</description>
      <pubDate>Sat, 29 May 2010 08:45:26 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/215-query-avanzate-in-rails-3</guid>
      <link>http://it.asciicasts.com/episodes/215-query-avanzate-in-rails-3</link>
    </item>
    <item>
      <title>A/B Testing con A/Bingo</title>
      <description>&lt;p&gt;L&amp;rsquo; A/B testing, anche noto come split testing, &amp;egrave; un grand metodo per testare versioni alternative di un&amp;rsquo;applicazione e capire quali di queste sia la pi&amp;ugrave; efficace. Di sotto &amp;egrave; riportata la maschera di registrazione di una applicazione. In cima alla form c&amp;rsquo;&amp;egrave; un paragrafo che spiega ci&amp;ograve; che l&amp;rsquo;utente deve fare. Vogliamo determinare quanto sia efficace questo paragrafo nella persuasione degli utenti a registrarsi e possiamo farlo creando un A/B test.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/386/original/E214I01.png" width="808" height="495" alt="La pagina di registrazione della nostra applicazione."/&gt;
&lt;/div&gt;

&lt;p&gt;Il modo in cui tutto ci&amp;ograve; funziona &amp;egrave; il seguente: ogni utente che visita la pagina vedr&amp;agrave; o meno il parametro in cima alla pagina: possiamo usare l&amp;rsquo;A/B testing per determinare quale opzione porta ai migliori risultati. Per fare ci&amp;ograve; abbiamo bisogno di un evento che, quando scatta, marchi l&amp;rsquo;opzione come vincente. In questo caso, l&amp;rsquo;evento sar&amp;agrave; sollevato alla compilazione e conseguente submit della form.&lt;/p&gt;

&lt;h3&gt;Scegliere lo strumento di test opportuno&lt;/h3&gt;

&lt;p&gt;Ci sono una serie di diversi strumenti per l&amp;rsquo;A/B testing disponibili. Uno che potremmo usare &amp;egrave; il &lt;a href="http://www.google.com/websiteoptimizer/b/index.html"&gt;Website Optimizer&lt;/a&gt;di Google. Non &amp;egrave; specifico di Rails, pu&amp;ograve; essere usato con qualunque applicazione web, persino con siti web statici. Questo tool ci consente di fornire diversi URL per il sito o usare del JavaScript per tracciare le conversioni per voi. E&amp;rsquo; una soluzione carina, ma se volete testare un&amp;rsquo;applicazione Rails, allora potreste voler usare qualcosa che magari si integri anche nell&amp;rsquo;applicazione stessa e che gestisca da s&amp;egrave; il tracciamento dei comportamenti degli utenti.&lt;/p&gt;

&lt;p&gt;Una delle soluzioni pi&amp;ugrave; popolari specifiche per Rails &amp;egrave; &lt;a href="http://vanity.labnotes.org/"&gt;Vanity&lt;/a&gt;. Questo strumento fornisce risultati meravigliosi ed &amp;egrave; anche piuttosto completo a livello di funzionalit&amp;agrave;. Non lo tratteremo in questo episodio, ma lo faremo probabilmente in uno dei prossimi. Pu&amp;ograve; risultare un po&amp;rsquo; ostico da configurare, in quanto richiede un database &lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; per ragioni di performance, ma vale la pena darci un&amp;rsquo;occhiata per vedere se fa al caso vostro.&lt;/p&gt;

&lt;p&gt;Il plugin che invece useremo in questo episodio &amp;egrave; &lt;a href="http://www.bingocardcreator.com/abingo/"&gt;A/Bingo&lt;/a&gt;. Ha un buon DSL per la definizione degli esperimenti ed &amp;egrave; semplice da mettere su. L&amp;rsquo;applicazione su cui eseguiremo i test &amp;egrave; scritta in Rails 2, per cui possiamo installare questo plugin eseguendo:&lt;/p&gt;

&lt;pre class="terminal"&gt;
	script/plugin install git://git.bingocardcreator.com/abingo.git
&lt;/pre&gt;

&lt;p&gt;dalla cartella della nostra applicazione. Quando termina l&amp;rsquo;esecuzione, dovremo generare una migrazione con:&lt;/p&gt;

&lt;pre class="terminal"&gt;
  script/generate abingo_migration
&lt;/pre&gt;

&lt;p&gt;e poi applicarla con:&lt;/p&gt;

&lt;pre class="terminal"&gt;
	rake db:migrate
&lt;/pre&gt;

&lt;p&gt;Questa migrazione generer&amp;agrave; due nuove tabelle sul database: &lt;code&gt;experiments&lt;/code&gt; e &lt;code&gt;alternatives&lt;/code&gt; ed &amp;egrave; in queste tabelle che verranno memorizzati i risultati dei nostri test.&lt;/p&gt;

&lt;p&gt;In un ambiente di produzione &amp;egrave; meglio configurare il caching di A/Bingo, ma in questo contesto possiamo saltare questa configurazione, visto che siamo in sviluppo.&lt;/p&gt;

&lt;h3&gt;Scrivere il primo test&lt;/h3&gt;

&lt;p&gt;Ora che abbiamo installato A/Bingo, possiamo scrivere il primo test. Vogliamo mostrare o meno il paragrafo in cima alla form di registrazione in modo tale che si veda per alcuni utenti, mentre per altri resti nascosto. Possiamo farlo usando il metodo &lt;code&gt;ab_test&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/users/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title (&amp;quot;Sign up&amp;quot;) %&amp;gt;

&amp;lt;% if ab_test &amp;quot;signup_intro&amp;quot; %&amp;gt;
&amp;lt;p&amp;gt;Complete the form below to create a new user account. You will then be able 
to create projects and tasks, and mark them as complete when finishing them.&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;p&amp;gt;Already have an account? &amp;lt;%= link_to &amp;quot;Log in&amp;quot;, new_user_session_path %&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;% form_for @user do |form| %&amp;gt;
 &amp;lt;!-- form omitted --&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Il primo argomento passato al metodo &lt;code&gt;ab_test&lt;/code&gt; &amp;egrave; il nome del test, che in questo caso &amp;egrave; &amp;ldquo;signup-intro&amp;rdquo;. Se non forniamo ulteriori argomenti, questo metodo restituir&amp;agrave; in modo casuale &lt;code&gt;true&lt;/code&gt; o &lt;code&gt;false&lt;/code&gt; ai vari utenti che via via chiederanno la pagina, di conseguenza il paragrafo sar&amp;agrave; mostrato o nascosto a second di chi carica la pagina.&lt;/p&gt; 

&lt;p&gt;Poi aggiungiamo un trigger di evento in modo tale che possiamo tracciare i risultati. Vogliamo che l&amp;rsquo;evento sia tracciato quando l&amp;rsquo;utente viene salvato con successo. Per questa ragione, modifichiamo la action &lt;code&gt;create&lt;/code&gt; sul controller &lt;code&gt;UsersController&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/users_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @user = User.new(params[:user])
  if @user.save
    bingo! &amp;quot;signup_intro&amp;quot;
    session[:user_id] = @user.id
    flash[:notice] = &amp;quot;Thank you for signing up. You are now logged in.&amp;quot;
    redirect_to root_url
  else
    render :action =&amp;gt; &amp;#x27;new&amp;#x27;
  end
end
&lt;/pre&gt;

&lt;p&gt;Nell&amp;rsquo;action &lt;code&gt;create&lt;/code&gt; tutto ci&amp;ograve; che dobbiamo fare &amp;egrave; chiamare il metodo &lt;code&gt;bingo!&lt;/code&gt; e passargli il nome del test quando l&amp;rsquo;utente viene correttamente salvato.&lt;/p&gt;

&lt;p&gt;Se ricarichiamo la pagina di registrazione ora, potremo vedere che per noi &lt;code&gt;ab_test&lt;/code&gt; ha restituito &lt;code&gt;false&lt;/code&gt;, per cui non vediamo il paragrafo in cima alla pagina. Possiamo provare a ricaricare la pagina nuovamente, ma il paragrafo continuer&amp;agrave; ad essere nascosto, dal momento che la nostra identit&amp;agrave; viene ricordata (come, ve lo faremo vedere fra poco).&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/387/original/E214I02.png" width="808" height="495" alt="Il paragrafo in cima alla pagina non &amp;egrave; mostrato quando ci registriamo."/&gt;
&lt;/div&gt;

&lt;p&gt;Se completiamo la compilazione della form e facciamo il submit, ci registreremo con successo e per questo scateneremo un evento.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/388/original/E214I03.png" width="808" height="280" alt="L&amp;rsquo;evento sar&amp;agrave; lanciato ora che ci siamo registrati."/&gt;
&lt;/div&gt;


&lt;h3&gt;Vedere i risultati&lt;/h3&gt;

&lt;p&gt;Al momento &amp;egrave; difficile vedere i risultati del test, ma possiamo comunque farlo creando un controller per vederli. Per fare ci&amp;ograve;, generiamo un controller che chiamiamo &lt;code&gt;abingo_dashboard&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
	script/generate controller abingo_dashboard
&lt;/pre&gt;

&lt;p&gt;Dentro a questo controller dobbiamo includere il module dashboard di A/Bingo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/abingo_dashboard_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class AbingoDashboardController &amp;lt; ApplicationController
  # TODO aggiungere logica di autorizzazione.
  include Abingo::Controller::Dashboard
end
&lt;/pre&gt;

&lt;p&gt;Ovviamente, se questa applicazione andasse in produzione, non vorremmo che chiunque potesse vedere la dashboard, per cui dovremmo implementare un qualche meccanismo di autorizzazione sul controller. Per ora comunque lasciamo solo un commento per ricordarcelo in futuro.&lt;/p&gt;

&lt;p&gt;Dobbiamo anche aggiungere un nuovo instradamento in modo che possiamo accedere alla dashboard.&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/config/routes.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
	map.abingo_dashboard &amp;quot;/abingo/:action/:id&amp;quot;, :controller =&amp;gt; :abingo_dashboard
&lt;/pre&gt;

&lt;p&gt;Sistemato tutto ci&amp;ograve;, possiamo andare all&amp;rsquo;indirizzo &lt;a href="http://localhost:3000/abingo"&gt;http://localhost:3000/abingo&lt;/a&gt; e vedremo la dashboard:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/389/original/E214I04.png" width="823" height="433" alt="La pagina di dashboard di A/Bingo."/&gt;
&lt;/div&gt;

&lt;p&gt;Al momento non ci sono stili applicati alla dashboard, ma &amp;egrave; abbastanza semplice cambiare il template della vista e darle uno stile per venire incontro all&amp;rsquo;aspetto del resto dell&amp;rsquo;applicazione. Con lo stile di default possiamo gi&amp;agrave; vedere i resultati degli esperimenti,  per cui non c&amp;rsquo;&amp;egrave; bisogno di occuparsi degli stili per ora. Dando uno sguardo ai risultati, possiamo vedere che c&amp;rsquo;&amp;egrave; un esperimento con un partecipante (si tratta della mia visita alla pagina di registrazione) ed una conversione che &amp;egrave; avvenuta quando ho fatto correttamente il submit dei dati della form. Il paragrafo in cima alla form non era visibile all&amp;rsquo;atto della registrazione, per cui il participante e la conversione sono mostrati fra i risultati false (f).&lt;/p&gt;

&lt;h3&gt;Riconoscere gli utenti&lt;/h3&gt;

&lt;p&gt;Se rivisitassimo la form di registrazione nuovamente, continueremmo a non vedere il paragrafo sopra alla form, fintanto che A/Bingo sapr&amp;agrave; che la nostra identit&amp;agrave; &amp;egrave; costante. Dobbiamo istruirlo per gestire le identit&amp;agrave; utente e dargli un modo per distinguere un utente da un altro. Tutto ci&amp;ograve; viene fatto dentro l&amp;rsquo;application controller scrivendo un &lt;code&gt;before_filter&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/application_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController &amp;lt; ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details
  before_filter :set_abingo_identity

  private
  def set_abingo_identity
    session[:abingo_identity] ||= rand(10 ** 10)
    Abingo.identity = session[:abingo_identity]
  end
end
&lt;/pre&gt;

&lt;p&gt;Nell&amp;rsquo;application controller abbiamo aggiunto un before filter chiamato &lt;code&gt;set_abingo_identity&lt;/code&gt; e nel metodo  &lt;code&gt;set_abingo_identity&lt;/code&gt; c&amp;rsquo;&amp;egrave; il codice che determina l&amp;rsquo;identit&amp;agrave; di ogni utente. Il metodo controlla per prima cosa l&amp;rsquo;esistenza di una variabile di sessione chiamata &lt;code&gt;abingo_identity&lt;/code&gt; e se non ne trova una, la crea con un valore numerico casuale. Ci&amp;ograve; significa che, fintanto che gli utenti mantengono attive le loro sessioni, saranno sempre trattati allo stesso modo da A/Bingo e vedranno sempre la maschera come l&amp;rsquo;hanno vista la prima volta.&lt;/p&gt;

&lt;p&gt;Se avessimo l&amp;rsquo;autenticazione nella nostra applicazione, vorremmo che la dipendenza fosse rispetto all&amp;rsquo;utenza piuttosto che alla sessione. Possiamo cambiare il metodo &lt;code&gt;set_abingo_identity&lt;/code&gt; in modo che usi l&amp;rsquo;id univoco di utente se l&amp;rsquo;utente corrente &amp;egrave; autenticato, ricadendo nella gestione basata su sessione per i soli utenti anonimi:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/application_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def set_abingo_identity
  if current_user
    Abingo.identity = current_user.id
  else
    session[:abingo_identity] ||= rand(10 ** 10)
    Abingo.identity = session[:abingo_identity]
  end
end
&lt;/pre&gt;      

&lt;p&gt;Possiamo anche controllare per vedere se il sito &amp;egrave; stato visitato da un web crawler o un bot e dare a ciascun bot la stessa identit&amp;agrave;, in modo tale che i risultati non siano falsati da questi visitatori non umani:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/application_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def set_abingo_identity
  if request.user_agent =~ /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
     Abingo.identity = &amp;quot;robot&amp;quot;
   elsif current_user
     Abingo.identity = current_user.id
   else
     session[:abingo_identity] ||= rand(10 ** 10)
     Abingo.identity = session[:abingo_identity]
   end
end
&lt;/pre&gt;

&lt;p&gt;Identifichiamo i crawler e i bot confrontandone il loro user agent contro una lista di nomi noti corrispondenti a questo genere di navigatori e se riscontriamo un match impostiamo l&amp;rsquo;identit&amp;agrave; a &amp;ldquo;robot&amp;rdquo;. In questo modo tutti i bot che visitano la pagina saranno considerati come un unico utente.&lt;/p&gt;

&lt;p&gt;Mentre scrivevo questa applicazione, ho visitato la pagina di registrazione con una serie di differenti identit&amp;agrave; portando a termine la registrazione un paio di volte. Questo mio comportamento &amp;egrave; ora riflesso nella dashboard di A/Bingo. Ci sono ora infatti otto partecipanti, due dei quali che hanno visto il paragrafo nella pagina di registrazione e sei che non l&amp;rsquo;hanno visto:&lt;/p&gt; 

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/390/original/E214I05.png" width="823" height="545" alt="Ora sono visualizzati pi&amp;ugrave; risultati."/&gt;
&lt;/div&gt;

&lt;h3&gt;Test pi&amp;ugrave; complessi&lt;/h3&gt;

&lt;p&gt;Concludiamo l&amp;rsquo;episodio mostrandovi come fornire pi&amp;ugrave; alternative per singolo test. Il test &lt;code&gt;signup_into&lt;/code&gt; &amp;egrave; un semplice test booleano, ma se volessimo fornire pi&amp;ugrave; di due diverse alternative, per esempio mostrando una serie di devrsi titoli di pagina, potremmo fare anche questo.&lt;/p&gt;

&lt;p&gt;Vale la pena ricordarsi che avere test multipli su una singola pagina pu&amp;ograve; incasinare i risultati, dal momento che questi possono derivare da quelle opzioni che vengono mostrate pi&amp;ugrave; frequentemente. Per un&amp;rsquo;applicazione di produzione tutto ci&amp;ograve; &amp;egrave; importante, ma dal momento che questa &amp;egrave; solo un&amp;rsquo;applicazione di esempio, in realt&amp;agrave; non conta molto.&lt;/p&gt;

&lt;p&gt;Per creare un test con opzioni multiple, possiamo chiamare la &lt;code&gt;ab_test&lt;/code&gt; con due argomenti, il secondo dei quali &amp;egrave; un array delle diverse opzioni che vogliamo testare. Nello specifico, vogliamo testare tre diversi titoli di pagina. Possiamo cambiare la pagina per mostrarne uno dei tre, usando:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/users/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title ab_test(&amp;quot;signup_title&amp;quot;, [&amp;quot;Sign up&amp;quot;, &amp;quot;Registration&amp;quot;, &amp;quot;Free Sign up&amp;quot;]) %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Messo a posto ci&amp;ograve;, ogni utente che visiter&amp;agrave; la pagina vedr&amp;agrave; a caso uno di questi tre titoli. Questo &amp;egrave; sufficientemente accettabile per qualcosa di semplice come il titolo di una pagina, ma se volessimo usare gli stessi valori casuali pi&amp;ugrave; di una volta sulla pagina, lo potremmo fare utilizzando il metodo &lt;code&gt;ab_test&lt;/code&gt; con un blocco:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/users/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% ab_test(&amp;quot;signup_title&amp;quot;, [&amp;quot;Sign up&amp;quot;, &amp;quot;Registration&amp;quot;, &amp;quot;Free Sign up&amp;quot;]) do |signup_title| %&amp;gt;
&amp;lt;% title signup_title %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Provando questo codice, potreste vedere un errore che dice &amp;ldquo;can&amp;rsquo;t modify frozen array&amp;rdquo; nel caso in cui non si stia usando l&amp;rsquo;ultima versione di A/Bingo. Ryan Bates ha reso disponibile una patch per questo problema che sembra sia stata inclusa direttamente nel codice sorgente del progetto, per cui se vedete questo errore, provate a fare l&amp;rsquo;upgrade del plugin e a riprovare.&lt;/p&gt;

&lt;p&gt;Visitando ora la pagina di registrazione, vedremo uno dei titoli relativi ad una delle tre opzioni e la pagina mostrer&amp;agrave; o meno il paragrafo in testata. C&amp;rsquo;&amp;egrave; ancora una cosa da sistemare, tuttavia. Anche se abbiamo configurato i test nella vista, non ne stiamo registrando i successi sul controller. Lo possiamo fare semplicemente aggiungendo un&amp;rsquo;altra chiamata a &lt;code&gt;bingo!&lt;/code&gt; nella action create:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/users_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @user = User.new(params[:user])
  if @user.save
    bingo! &amp;quot;signup_intro&amp;quot;
    bingo! &amp;quot;signup_title&amp;quot;
    session[:user_id] = @user.id
    flash[:notice] = &amp;quot;Thank you for signing up. You are now logged in.&amp;quot;
    redirect_to root_url
  else
    render :action =&amp;gt; &amp;#x27;new&amp;#x27;
  end
end
&lt;/pre&gt;      

&lt;p&gt;Se abbiamo molti test in una determinata action, tutto ci&amp;ograve; pu&amp;ograve;, diventare poco manutenibile. Piuttosto, dunque, potremmo fare una sola chiamata a &lt;code&gt;bingo!&lt;/code&gt;, col nome di una conversione e usare il nome di tale conversione come argomento extra da passare al metodo &lt;code&gt;ab_test&lt;/code&gt; nella vista.&lt;/p&gt;

&lt;p&gt;Per cui, possiamo sostituire nel controller le due invocazioni a bingo! con una sola:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/users_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def create
  @user = User.new(params[:user])
  if @user.save
    bingo! &amp;quot;signup&amp;quot;
    session[:user_id] = @user.id
    flash[:notice] = &amp;quot;Thank you for signing up. You are now logged in.&amp;quot;
    redirect_to root_url
  else
    render :action =&amp;gt; &amp;#x27;new&amp;#x27;
  end
end
&lt;/pre&gt;

&lt;p&gt;Mentre nella vista possiamo passare il nome della conversione come parte di un hash di argomenti:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/users/new.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% ab_test(&amp;quot;signup_title&amp;quot;, [&amp;quot;Sign up&amp;quot;, &amp;quot;Registration&amp;quot;, &amp;quot;Free Sign up&amp;quot;], :conversion =&amp;gt; &amp;quot;signup&amp;quot;) do |signup_title| %&amp;gt;
  &amp;lt;% title signup_title %&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;% if ab_test &amp;quot;signup_intro&amp;quot;, nil, :conversion =&amp;gt; &amp;quot;signup&amp;quot; %&amp;gt;
&amp;lt;!-- resto della vista --&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Si noti che, dal momento che il secondo test &amp;egrave; un semplice booleano, gli abbiamo passato &lt;code&gt;nil&lt;/code&gt; come secondo argomento.&lt;/p&gt;

&lt;p&gt;E&amp;rsquo; tutto per questo episodio. L&amp;rsquo;A/B testing fornisce un ottimo metodo per sperimentare versioni diverse di maschere in un&amp;rsquo;applicazione web e tracciare il tasso di successo dei risultati. Vi esorto a provarlo nelle vostre applicazioni, o usando Google Website Optimizer o Vanity o A/Bingo.&lt;/p&gt;
</description>
      <pubDate>Thu, 27 May 2010 15:33:12 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/214-a-b-testing-con-a-bingo</guid>
      <link>http://it.asciicasts.com/episodes/214-a-b-testing-con-a-bingo</link>
    </item>
    <item>
      <title>Calendari</title>
      <description>&lt;p&gt;Ogni volta che si gestiscono delle date in un&amp;rsquo;applicazione &amp;egrave; spesso utile mettere a disposizione un calendario nell&amp;rsquo;interfaccia utente. Se l&amp;rsquo;utente deve infatti scegliere una data per un certo campo, sar&amp;agrave; sicuramente pi&amp;ugrave; semplice per lui farlo da una popup che mostra un calendario, piuttosto che doverne inserire una scegliendo da una serie di tendine (giorno, mese e anno) o peggio ancora inserendo testualmente la data in un campo di testo. Pu&amp;ograve; anche capitare che si abbia una tabella di modello nell&amp;rsquo;applicazione che abbia un campo data e che quindi serva un calendario per permettere di scegliere per data fra i record di tale tabella. Vi mostreremo in questo episodio come poter fare entrembe le cose.&lt;/p&gt;

&lt;p&gt;L&amp;rsquo;applicazione con cui lavoreremo &amp;egrave; una semplice applicazione di blogging che ha un certo numero di articoli a database, ma quasi tutte le applicazioni si aspettano che i campi di input per le date beneficino di questa funzionalit&amp;agrave; di selezione. Quando creiamo o modifichiamo un articolo, possiamo scegliere una data di pubblicazione usando un classico date picker di Rails. Cambieremo questo sistema per poter usare un calendario in popup come metodo alternativo per scegliere tale data.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/379/original/E213I01.png" width="804" height="539" alt="La pagina di modifica di un articolo con le tendine di scelta della data."/&gt;
&lt;/div&gt;

&lt;p&gt;I calendari sono complicati da creare e gestire da zero, ma per fortuna ci sono parecchie soluzioni di terze parti disponibili, che ci permetteranno di fare praticamente tutto ci&amp;ograve; che vorremmo fare con i calendari e le date. Uno di questi &amp;egrave; il plugin &lt;a href="http://github.com/timcharper/calendar_date_select"&gt;calendar_date_select&lt;/a&gt;. Questo plugin ci fornisce un metodo helper che funziona con le librerie Prototype e che invoca un calendario attraverso del JavaScript. Non useremo questo plugin, tuttavia, ma useremo piuttosto una soluzione diversa che fa uso di unobtrusive JavaScript e che si basa su jQuery. Non si tratta di una soluzione specifica per Rails, ma l&amp;rsquo;abbiamo comunque scelta in quanto fornisce un metodo facile da capire per aggiungere un calendario ad un campo di data.&lt;/p&gt;

&lt;p&gt;La libreria &lt;a href="http://jqueryui.com/"&gt;jQuery UI&lt;/a&gt; fornisce un &lt;a href="http://jqueryui.com/demos/datepicker/"&gt;Datepicker&lt;/a&gt; che permette l&amp;rsquo;inclusione pulita di un calendario ad un qualunque campo di testo di una pagina. Quando il campo di testo ottiene il focus, o perch&amp;egrave; viene selezionato con click, o perch&amp;egrave; viene selezionato mediante tab da tastiera, una popup mostrante un calendario appare per consentire la selezione di una data. Una volta scelta in questo modo la data, il campo di testo si ritrova il valore selezionato impostato. Questo calendario pu&amp;ograve; essere personalizzato mediante temi, usando il &lt;a href="http://jqueryui.com/themeroller/"&gt;Themeroller&lt;/a&gt; di jQuery: in questo modo ci viene data la possibilit&amp;agrave; di scegliere l&amp;rsquo;aspetto che pi&amp;ugrave; ci piace. Se vogliamo ottenere rapidamente ci&amp;ograve; che vogliamo, possiamo usare i file JavaScript e gli stili presenti sui server di Google per includere il codice jQueryUI ed uno dei temi di default. Dobbiamo solo referenziare i corretti file nella sezione HEAD della pagina di layout:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/layouts/application.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot;/&amp;gt;
  &amp;lt;%= stylesheet_link_tag &amp;quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/redmond/jquery-ui.css&amp;quot;, &amp;quot;application&amp;quot; %&amp;gt;
  &amp;lt;%= javascript_include_tag &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js&amp;quot;, &amp;quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/jquery-ui.min.js&amp;quot;, &amp;quot;application&amp;quot; %&amp;gt;
  &amp;lt;title&amp;gt;&amp;lt;%= yield :title %&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Si noti che l&amp;rsquo;applicazione con cui lavoriamo qui &amp;egrave; scritta in Rails 2. Se stessimo usando Rails 3, avremmo bisogno di aggiungere una versione compatibile con jQuery del file rails.js, come gi&amp;agrave; trattato nell&amp;rsquo;episodio 205 [&lt;a href="http://railscasts.com/episodes/205-unobtrusive-javascript"&gt;guardalo&lt;/a&gt;, &lt;a href="http://it.asciicasts.com/episodes/205-javascript-non-appariscente"&gt;leggilo&lt;/a&gt;].&lt;/p&gt;

&lt;p&gt;Ora che abbiamo incluso le librerie jQuery e jQueryUI, possiamo cminciare a modificare la nostra applicazione per farla funzionare con il Datepicker. La prima cosa da fare &amp;egrave; modificare il campo published_on nella form dell&amp;rsquo;articolo, in modo tale che usi un &lt;code&gt;text_field&lt;/code&gt; al posto di un &lt;code&gt;date_select&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/articles/_form.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;%= f.label :published_on %&amp;gt;
&amp;lt;%= f.text_field :published_on %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Poi dobbiamo aggiungere un calendario in maniera non invasiva e lo possiamo fare aggiungendo del JavaScript al file &lt;code&gt;application.js&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/public/javascripts/application.js&lt;/p&gt;
&lt;pre class="javascript"&gt;
$(function (){
	$(&amp;#x27;#article_published_on&amp;#x27;).datepicker();
});
&lt;/pre&gt;

&lt;p&gt;Questo codice eseguir&amp;agrave; al termine del caricamento del DOM, aggiungendo un date-picker ad ogni elemento che abbia &lt;code&gt;id&lt;/code&gt; ugauale a &lt;code&gt;article_published_at&lt;/code&gt;. Se ricarichiamo la pagina di modifica dell&amp;rsquo;articoloe clicchiamo nel campo di testo &lt;code&gt;published_on&lt;/code&gt;, vedremo la popup del calendario e potremo scegliere una data da quella popup:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/380/original/E213I02.png" width="804" height="539" alt="I menu a tendina sono stati sostituiti da campi di testo con annesso calendario in popup."/&gt;
&lt;/div&gt;

&lt;p&gt;Al submit della form, Rails interpreter&amp;agrave; la data dal valore presente nel campo di testo e aggiorner&amp;agrave; la data relativa al campo &lt;code&gt;published_on&lt;/code&gt; nella tabella articles sul database.&lt;/p&gt;

&lt;h3&gt;Una vista calendario&lt;/h3&gt;

&lt;p&gt;Ora che abbiamo il nostro selettore di date funzionante, vediamo gli altri usi dei calendari di cui abbiamo discusso: fornire una vista basata su calendario degli articoli nella nostra appicazione. Questo potrebbe non essere il modo pi&amp;ugrave; efficiente di elencare gli articoli in un blog, ma lo forniremo comunque come modo alternativo per sfogliare gli articoli presenti.&lt;/p&gt;

&lt;p&gt;Ci sono molteplici soluzioni a questo problema e trovare quella corretta dipende dalle necessit&amp;agrave; dell&amp;rsquo;applicazione. Se c&amp;rsquo;&amp;egrave; bisogno di mostrare dei record che si sviluppano su pi&amp;ugrave; giorni, allora ha senso dare un&amp;rsquo;occhiata al plugin &lt;a href="http://github.com/elevation/event_calendar"&gt;event_calendar&lt;/a&gt; che pu&amp;ograve; mostrare eventi che spaziano su di un range di date. Se tale funzionalit&amp;agrave; non &amp;egrave; richiesta, una buona alternativa &amp;egrave; il plugin &lt;a href="http://github.com/p8/table_builder"&gt;table_builder&lt;/a&gt;. Questo plugin offre un metodo helper chiamato &lt;code&gt;calendar_for&lt;/code&gt; che rende semplice raggruppare un dato insieme di record in giorni su di un calendario. Quest&amp;rsquo;ultimo corrisponde meglio ai nostri bisogni, per cui lo useremo nella nostra applicazione.&lt;/p&gt;

&lt;p&gt;Possiamo installare table_builder lanciando il seguente comando:&lt;/p&gt;
&lt;pre class="terminal"&gt;
script/plugin install git://github.com/p8/table_builder.git
&lt;/pre&gt;

&lt;p&gt;Dopo che si sar&amp;agrave; installato, potremo cominciare a lavorare sulla nostra vista a calendario cambiando la vista &lt;code&gt;index&lt;/code&gt; nel nostro controller degli articoli. Attualmente appare cos&amp;igrave;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/articles/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;Articles&amp;quot; %&amp;gt;
&amp;lt;div id=&amp;quot;articles&amp;quot;&amp;gt;
&amp;lt;% @articles.each do |article| %&amp;gt;
  &amp;lt;h2&amp;gt;
    &amp;lt;%= link_to h(article.title), article %&amp;gt;
    &amp;lt;span class=&amp;quot;comments&amp;quot;&amp;gt;(&amp;lt;%= pluralize(article.comments.count, &amp;#x27;comment&amp;#x27;) %&amp;gt;)&amp;lt;/span&amp;gt;
  &amp;lt;/h2&amp;gt;
  &amp;lt;div class=&amp;quot;author&amp;quot;&amp;gt;from &amp;lt;%=h article.author %&amp;gt; on &amp;lt;%= article.written_date.strftime(&amp;#x27;%b %d, %Y&amp;#x27;) %&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;&amp;lt;%= h(article.content) %&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to &amp;quot;New Article&amp;quot;, new_article_path %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Sostituiremo il codice che itera su ogni articolo e lo mostreremo con una chiamata al metodo &lt;code&gt;calendar_for&lt;/code&gt; per mostrare gli articoli in una vista a calendario:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/articles/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;Articles&amp;quot; %&amp;gt;

&amp;lt;div id=&amp;quot;calendar&amp;quot;&amp;gt;
  &amp;lt;% calendar_for @articles do |calendar| %&amp;gt;
    &amp;lt;% calendar.day(:day_method =&amp;gt; :published_on) do |date, articles| %&amp;gt;
      &amp;lt;%= date.day %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Chiamiamo il &lt;code&gt;calendar_for&lt;/code&gt; passandogli la nostra collezione di articoli. Il blocco viene eseguito una volta e accetta un oggetto di tipo &lt;code&gt;calendar&lt;/code&gt;, per cui possiamo chiamare il &lt;code&gt;calendar.day&lt;/code&gt; per iterare su ogni giorno. Dobbiamo specificare una property dell&amp;rsquo;articolo che restituisca una data, per poter raggruppare gli articoli per giorno, per cui gli passiamo un parametro &lt;code&gt;:day_method&lt;/code&gt; col valore di &lt;code&gt;:published_on&lt;/code&gt;. Il blocco in questione ha due parameteri: una data e una lista di articoli che sono stati pubblicati per giorno. Possiamo mettere il codice che ci pare dentro a questo blocco, ma per ora restituiremo semplicemente in output il giorno del mese:&lt;/p&gt;

&lt;p&gt;Se ricarichiamo la pagina ora, non vedremo una lista di articoli, quanto piuttosto una lista di date che assomigliano un po&amp;rsquo; ad un calendario:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/381/original/E213I03.png" width="800" height="289" alt="Un calendario molto basilare generato da table_builder."/&gt;
&lt;/div&gt;

&lt;p&gt;Non sembra ancora molto carino, ma possiamo aggiungere un po&amp;rsquo; di &lt;a href="http://github.com/ryanb/railscasts-episodes/blob/master/episode-213/blog/public/stylesheets/application.css"&gt;CSS&lt;/a&gt; per migliorarne l&amp;rsquo;aspetto:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/382/original/E213I04.png" width="842" height="460" alt="Il calendario dopo l&amp;rsquo;aggiunta degli stili."/&gt;
&lt;/div&gt;

&lt;p&gt;Possiamo migliorare l&amp;rsquo;usabilit&amp;agrave; del calendario, aggiungendo i nomi dei giorni in testata, usando il metodo &lt;code&gt;calendar.head&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/articles/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;Articles&amp;quot; %&amp;gt;

&amp;lt;div id=&amp;quot;calendar&amp;quot;&amp;gt;
  &amp;lt;% calendar_for @articles do |calendar| %&amp;gt;
    &amp;lt;%= calendar.head(&amp;#x27;Sunday&amp;#x27;, &amp;#x27;Monday&amp;#x27;, &amp;#x27;Tuesday&amp;#x27;, &amp;#x27;Wednesday&amp;#x27;, &amp;#x27;Thursday&amp;#x27;, &amp;#x27;Friday&amp;#x27;, &amp;#x27;Saturday&amp;#x27;)%&amp;gt;
    &amp;lt;% calendar.day(:day_method =&amp;gt; :published_on) do |date, articles| %&amp;gt;
      &amp;lt;%= date.day %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Quando ricarichiamo la pagina nuovamente, i giorni della settimana saranno stati aggiunti in cima al calendario:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/383/original/E213I05.png" width="842" height="460" alt="Il calendario ora mostra anche i giorni della settimana."/&gt;
&lt;/div&gt;

&lt;p&gt;Cos&amp;igrave; com&amp;rsquo;&amp;egrave;, il nostro calendario pu&amp;ograve; solo mostrare delle date del mese corrente, per cui il prossimo passo sar&amp;agrave; di aggiungere un seletore del mese che mostri il mese corrente e che abbia dei link su entrambi i lati per permetterci di cambiare il mese mostrato.&lt;/p&gt;

&lt;p&gt;Per fare questo, partiamo dalla action &lt;code&gt;index&lt;/code&gt; del controller degli articoli, dove creeremo una variabile che manterr&amp;agrave; la data del mese che vogliamo sia mostrata. Per ora impostiamo semplicemente la data di oggi:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/articles_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def index
  @articles = Article.all
  @date = Date.today
end
&lt;/pre&gt;

&lt;p&gt;Poi modifichiamo la vista in modo tale che siano mostrati il mese e i link:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/articles_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% title &amp;quot;Articles&amp;quot; %&amp;gt;

&amp;lt;div id=&amp;quot;calendar&amp;quot;&amp;gt;
  &amp;lt;h2 id=&amp;quot;month&amp;quot;&amp;gt;
    &amp;lt;%= link_to &amp;quot;&amp;lt;&amp;quot;, :month =&amp;gt; (@date.beginning_of_month-1).strftime(&amp;quot;%Y-%m-01&amp;quot;) %&amp;gt;
    &amp;lt;%= h @date.strftime(&amp;quot;%B %Y&amp;quot;) %&amp;gt;
    &amp;lt;%= link_to &amp;quot;&amp;gt;&amp;quot;, :month =&amp;gt; (@date.end_of_month+1).strftime(&amp;quot;%Y-%m-01&amp;quot;) %&amp;gt;
  &amp;lt;/h2&amp;gt;
  &amp;lt;% calendar_for @articles, :year =&amp;gt; @date.year, :month =&amp;gt; @date.month do |calendar| %&amp;gt;
    &amp;lt;%= calendar.head(&amp;#x27;Sunday&amp;#x27;, &amp;#x27;Monday&amp;#x27;, &amp;#x27;Tuesday&amp;#x27;, &amp;#x27;Wednesday&amp;#x27;, &amp;#x27;Thursday&amp;#x27;, &amp;#x27;Friday&amp;#x27;, &amp;#x27;Saturday&amp;#x27;) %&amp;gt;
    &amp;lt;% calendar.day(:day_method =&amp;gt; :published_on) do |date, articles| %&amp;gt;
      &amp;lt;%= date.day %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;

&lt;p&gt;I link puntano entrambi alla action &lt;code&gt;index&lt;/code&gt; che &amp;egrave; mostrata attualmente, ma con l&amp;rsquo;aggiunta di un parametro alla stringa di query, chiamato &lt;code&gt;month&lt;/code&gt;, che indica il mese precedente o il mese successivo. Ora possiamo ritornare al codice del controller, controllare l&amp;rsquo;esistenza di quel parametro &lt;code&gt;month&lt;/code&gt; e usarlo per impostare il valore della variabile &lt;code&gt;@date&lt;/code&gt; se esiste:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/articles_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
def index
  @articles = Article.all
  @date = params[:month] ? Date.parse(params[:month]) : Date.today
end
&lt;/pre&gt;

&lt;p&gt;Al ricaricamento della pagina, vedremo il nome del mese sopra il calendario e le freccie su entrambi i lati di questo, che ci permetteranno di scorrere fra i vari mesi:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/384/original/E213I06.png" width="842" height="460" alt="I link per cambiare il mese ora sono stati aggiunti."/&gt;
&lt;/div&gt;

&lt;p&gt;La nostra vista a calendario ora appare carina, ma non mostra gli articoli che ci sono per un dato giorno. Abbiamo gi&amp;agrave; accesso agli articoli di un determinato giorno nel blocco &lt;code&gt;calendar.head&lt;/code&gt;, per cui possiamo iterare attraverso questa collection e mettere un link per ciascun articolo di quel giorno in una lista:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/views/articles/index.html.erb&lt;/p&gt;
&lt;pre class="ruby"&gt;
&amp;lt;% calendar_for @articles, :year =&amp;gt; @date.year, :month =&amp;gt; @date.month do |calendar| %&amp;gt;
  &amp;lt;%= calendar.head(&amp;#x27;Sunday&amp;#x27;, &amp;#x27;Monday&amp;#x27;, &amp;#x27;Tuesday&amp;#x27;, &amp;#x27;Wednesday&amp;#x27;, &amp;#x27;Thursday&amp;#x27;, &amp;#x27;Friday&amp;#x27;, &amp;#x27;Saturday&amp;#x27;)%&amp;gt;
  &amp;lt;% calendar.day(:day_method =&amp;gt; :published_on) do |date, articles| %&amp;gt;
    &amp;lt;%= date.day %&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;% for article in articles %&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;%= link_to h(article.title), article %&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Quando ricarichiamo la pagina un&amp;rsquo;ultima volta, vedremo elencati gli articoli per ciascun giorno e potremo cliccare su ciascuno per essere portati al dettaglio di quell&amp;rsquo;articolo:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/385/original/E213I07.png" width="842" height="488" alt="Gli articoli ora sono elencati nel calendario."/&gt;
&lt;/div&gt;

&lt;p&gt;Per questo episodio &amp;egrave; tutto. Come abbiamo mostrato, &amp;egrave; semplice usare i calendari per scegliere date o per mostrare una lista di record a seconda di una determinata property di tipo data. Siete invitati a considerare l&amp;rsquo;utilizzo di una vista a calendario tipo questa ogni volta che possa rivelarsi utile rispetto ai dati delle vostre applicazioni.&lt;/p&gt;
</description>
      <pubDate>Thu, 27 May 2010 15:28:30 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/213-calendari</guid>
      <link>http://it.asciicasts.com/episodes/213-calendari</link>
    </item>
    <item>
      <title>Refactoring e Dynamic Delegator</title>
      <description>&lt;p&gt;L&amp;rsquo;episodio di questa settimana &amp;egrave; un po&amp;rsquo; diverso dal solito. E&amp;rsquo; un esercizio di refactoring che ci consentir&amp;agrave; di scoprire una simpatica tecnica Ruby che chiameremo Dynamic Delegator.&lt;/p&gt;

&lt;p&gt;Per mostrarvi il tutto, ci avvarremo di una semplice applicazione di negozio on-line che ha un modello &lt;code&gt;Product&lt;/code&gt; con associato un &lt;code&gt;ProductsController&lt;/code&gt;. La action del controller &lt;code&gt;index&lt;/code&gt; permette che la lista di prodotti sia filtrata per nome o per prezzo. Fornendo uno o pi&amp;ugrave; parametri fra &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price_lt&lt;/code&gt; e &lt;code&gt;price_gt&lt;/code&gt; alla stringa di query, possiamo cercare prodotti che corrispondano a tali criteri di ricerca, per trovare, ad esempio, i soli prodotti il cui nome contenga &amp;ldquo;video&amp;rdquo; e che costino pi&amp;ugrave; di &amp;pound;50:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/377/original/E212I01.png" width="800" height="315" alt="Filtering the list of products."/&gt;
&lt;/div&gt;

&lt;p&gt;Prima di iniziare il il refactoring della action index, diamo un&amp;rsquo;occhiata a ci&amp;ograve; che fa attualmente:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/products_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class ProductsController &amp;lt; ApplicationController
  def index
    @products = Product.scoped
    @products = @products.where(&amp;quot;name like ?&amp;quot;, &amp;quot;%&amp;quot; + params[:name] + &amp;quot;%&amp;quot;) if params[:name]
    @products = @products.where(&amp;quot;price &amp;gt;= ?&amp;quot;, params[:price_gt]) if params[:price_gt]
    @products = @products.where(&amp;quot;price &amp;lt;= ?&amp;quot;, params[:price_lt]) if params[:price_lt]
  end

  # Altre action
end
&lt;/pre&gt;

&lt;p&gt;Si tratta di un&amp;rsquo;applicazione Rails 3, dunque si usa il metodo &lt;code&gt;where&lt;/code&gt; per aggiungere condizioni alla query se &amp;egrave; stato passato il parametro corrispondente. Prima di fare ci&amp;ograve;, tuttavia, usiamo &lt;code&gt;Product.scoped&lt;/code&gt; per ottenere l&amp;rsquo;insieme di tutti i prodotti. Potreste non conoscere ancora questo metodo, ma essenzialmente &amp;egrave; un ulteriore modo per dire &lt;code&gt;Product.all&lt;/code&gt;. La differenza rispetto al primo &amp;egrave; che il metodo &lt;code&gt;all&lt;/code&gt; determina istantaneamente una query sul database, restituendo un array di prodotti. Siccome non vogliamo intraprendere una chiamata al database fintanto che non abbiamo applicato i nostri filtri, allora usiamo il metodo &lt;code&gt;scoped&lt;/code&gt; al posto di &lt;code&gt;all&lt;/code&gt;, che ci permette di aggiungere condizioni alla query prima che questa sia eseguita.&lt;/p&gt; 

&lt;p&gt;Ora pensiamo al refactoring dell&amp;rsquo;action. Il primo passo che faremo sar&amp;agrave; di rimuovere parte della logica dal controller, dal momento che quello non &amp;egrave; il posto pi&amp;ugrave; indicato per quel genere di codice. In qualunque linguaggio object-orientated, se siete nella situazione in cui un oggetto di un certo tipo sta chiamando molti metodi su un altro oggetto di un altro tipo, probabilmente significa che dovreste spostare tutte quelle chiamate e quella logica all&amp;rsquo;interno di un metodo dell&amp;rsquo;altro oggetto. In questo caso,nella action index della classe di &lt;code&gt;ProductController&lt;/code&gt;, stiamo chiamando ben quattro metodi della classe di modello &lt;code&gt;Product&lt;/code&gt; per creare la nostra query e ci&amp;ograve; ci suggerisce che questo codice debba appartenere al modello stesso piuttosto che al controller.&lt;/p&gt;

&lt;p&gt;Sostituiamo dunque il codice all&amp;rsquo;interno della action &lt;code&gt;index&lt;/code&gt; con una chiamata al nuovo metodo di classe nel modello &lt;code&gt;Product&lt;/code&gt; chiamato &lt;code&gt;search&lt;/code&gt;, a cui passiamo un hash &lt;code&gt;params&lt;/code&gt; in modo che quest&amp;rsquo;ultimo sappia su cosa deve essere fatta la query.&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/controllers/products_controller.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class ProductsController &amp;lt; ApplicationController
  def index
    @products = Product.search(params)
  end

  # Altre actions
end
&lt;/pre&gt;

&lt;p&gt;Poi definiamo il metodo in questione nella classe &lt;code&gt;Product&lt;/code&gt;. Il metodo deve essere di classe, per cui lo definiamo come &lt;code&gt;self.search&lt;/code&gt;. Il codice nel metodo sar&amp;agrave; lo stesso che avevamo prima all&amp;rsquo;interno del controller, ma con una variabile locale (products) che sostituisce la variabile di istanza che avevamo prima: questa variabile &amp;egrave; restituita alla fine del metodo:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  
  def self.search(params)
    products = scoped
    products = products.where(&amp;quot;name like ?&amp;quot;, &amp;quot;%&amp;quot; + params[:name] + &amp;quot;%&amp;quot;) if params[:name]
    products = products.where(&amp;quot;price &amp;gt;= ?&amp;quot;, params[:price_gt]) if params[:price_gt]
    products = products.where(&amp;quot;price &amp;lt;= ?&amp;quot;, params[:price_lt]) if params[:price_lt]    
    products
  end 

end
&lt;/pre&gt;

&lt;p&gt;Se ora ricarichiamo la pagina, vedremo che funziona tutto esattamente come prima.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/377/original/E212I01.png" width="800" height="315" alt="La pagina funziona ancora."/&gt;
&lt;/div&gt;

&lt;p&gt;Naturalmente, ricaricando la pagina abbiamo solo constatato che le modifiche al codice funzionano per quegli specifici parametri; &amp;egrave; solo in scenari di sviluppo Test-Driven che si pu&amp;ograve; veramente avere una riprova effettiva del corretto funzionamento della pagina post-refactoring. Ricaricare la pagina diventa rapidamente piuttosto tedioso e non controlla comunque ogni ramo del codice dal momento che la query di ricerca &amp;egrave; costruita in modo diverso a seconda dei parametri che vengono passati. E&amp;rsquo; una buona idea, specialmente quando si fa del refactoring sul codice, quella di mettere su un sistema di test per assicurarsi di non introdurre regressioni con le modifiche.&lt;/p&gt;

&lt;p&gt;Spostare questo codice nel modello presenta l&amp;rsquo;ulteriore beneficio di rendere pi&amp;ugrave; semplice anche il test, dal momento che dovremmo solo scrivere un test unitario sulla classe di modello, anzich&amp;egrave; un test completo sull&amp;rsquo;intero stack.&lt;/p&gt;

&lt;h3&gt;Introduzione al Dynamic Delegator&lt;/h3&gt;

&lt;p&gt;Abbiamo un po&amp;rsquo; rivisto il codice spostando la logica di ricerca nel modello ed ora andremo oltre, togliendo la necessit&amp;agrave; di reimpostare la variabile &lt;code&gt;products&lt;/code&gt; ogni volta che aggiungiamo una condizione di ricerca. E&amp;rsquo; un pattern ricorrente quando si tratta con le opzioni di ricerca e se ne vedete molti nella vostra applicazione, potreste considerare la tecnica che vi stiamo per mostrare, che abbiamo chiamato dynamic delegator.&lt;/p&gt;

&lt;p&gt;Piuttosto che spiegare come funziona un dynamic delegator, ve lo mostreremo usandone uno per il refactoring del nostro codice di ricerca. Cominciamo creando la classe del dynamic delegator nella cartella &lt;code&gt;/lib&lt;/code&gt; della nostra applicazione:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/dynamic_delegator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class DynamicDelegator
  def initialize(target)
    @target = target
  end  
  
  def method_missing(*args, &amp;amp;block)
    @target.send(*args, &amp;amp;block)
  end
end
&lt;/pre&gt;

&lt;p&gt;La classe &lt;code&gt;DynamicDelegator&lt;/code&gt; accetta un argomento nel suo costruttore, un oggetto target, e imposta una variabile di istanza a quell&amp;rsquo;oggetto. Fa anche l&amp;rsquo;override del metodo &lt;code&gt;&lt;a href="http://ruby-doc.org/core/classes/Kernel.html#M005925"&gt;method_missing&lt;/a&gt;&lt;/code&gt;, in modo tale che qualsiasi chiamata a questo oggetto che non sia supportata sia catturata e passata all&amp;rsquo;oggetto insieme allo stesso blocco e argomenti.&lt;/p&gt;

&lt;p&gt;Possiamo pensare al nostro &lt;code&gt;DynamicDelegator&lt;/code&gt; come ad un oggetto proxy che passa ogni chiamata al proprio oggetto target: questo significa che possiamo usarlo ovunque vogliamo al posto dell&amp;rsquo;oggetto target. Se lo creiamo su un certo oggetto target, si comporter&amp;agrave; come se fosse quel tipo di oggetto. Ci&amp;ograve; significa che possiamo sostituire l&amp;rsquo;oggetto &lt;code&gt;scoped&lt;/code&gt; nel nostro metodo di ricerca di &lt;code&gt;Product&lt;/code&gt; con un nuovo &lt;code&gt;DynamicDelegator&lt;/code&gt; che prenda quell&amp;rsquo;oggetto come argomento:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  
  def self.search(params)
    products = DynamicDelegator(scoped)
    products = products.where(&amp;quot;name like ?&amp;quot;, &amp;quot;%&amp;quot; + params[:name] + &amp;quot;%&amp;quot;) if params[:name]
    products = products.where(&amp;quot;price &amp;gt;= ?&amp;quot;, params[:price_gt]) if params[:price_gt]
    products = products.where(&amp;quot;price &amp;lt;= ?&amp;quot;, params[:price_lt]) if params[:price_lt]    
    products
  end 

end
&lt;/pre&gt;

&lt;p&gt;Possiamo verificare che tutto ci&amp;ograve; funzione, ricaricando nuovamente la pagina: dovremmo vedere lo stesso insieme di risultati.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/377/original/E212I01.png" width="800" height="315" alt="La pagina continua a funzionare."/&gt;
&lt;/div&gt;

&lt;p&gt;Ha funzionato, ma a questo punto vi starete probabilmente domandando quale sia il vantaggio nell&amp;rsquo;usare un &lt;code&gt;DynamicDelegator&lt;/code&gt; al posto dell&amp;rsquo;oggetto originale &lt;code&gt;scoped&lt;/code&gt;. Il vantaggio del delegator &amp;egrave; che possiamo fare qualsiasi cosa vogliamo all&amp;rsquo;interno del metodo &lt;code&gt;method_missing&lt;/code&gt;. Anzich&amp;egrave; delegare sempre passivamente la stessa cosa all&amp;rsquo;oggetto target, possiamo modificare il nostro target e renderlo pi&amp;ugrave; dinamico.&lt;/p&gt;

&lt;p&gt;Per esempio, vogliamo catturare il risultato della chiamata al metodo &lt;code&gt;method_missing&lt;/code&gt; e, se restituisce un oggetto della stessa classe del target, impostare il target stesso come il risultato:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/dynamic_delegator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class DynamicDelegator
  def initialize(target)
    @target = target
  end  
  
  def method_missing(*args, &amp;amp;block)
    result = @target.send(*args, &amp;amp;block)
    @target = result if result.kind_of? @target.class
    result
  end
end
&lt;/pre&gt;

&lt;p&gt;Ora possiamo rimuovere il codice che resetta la variabile &lt;code&gt;products&lt;/code&gt; in ogni linea del metodo &lt;code&gt;search&lt;/code&gt; nel modello &lt;code&gt;Product&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  
  def self.search(params)
    products = DynamicDelegator.new(scoped)
    products.where(&amp;quot;name like ?&amp;quot;, &amp;quot;%&amp;quot; + params[:name] + &amp;quot;%&amp;quot;) if params[:name]
    products.where(&amp;quot;price &amp;gt;= ?&amp;quot;, params[:price_gt]) if params[:price_gt]
    products.where(&amp;quot;price &amp;lt;= ?&amp;quot;, params[:price_lt]) if params[:price_lt]    
    products
  end 

end
&lt;/pre&gt;

&lt;p&gt;Possiamo farlo perch&amp;egrave; la chiamata alla where restituir&amp;agrave; lo stesso tipo di oggetto dello &lt;code&gt;scoped&lt;/code&gt; e in questo modo il target verr&amp;agrave; sostituito ogni volta. Ricarichiamo di nuovo la pagina e vediamo se funziona ancora tutto:&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/378/original/E212I02.png" width="800" height="315" alt="Il dynamic delegator restituisce se stesso anzich&amp;egrave; il suo oggetto target."/&gt;
&lt;/div&gt;

&lt;p&gt;Non funziona pi&amp;ugrave;, e la ragione &amp;egrave; che non stiamo delegando esattamente tutti i metodo dell&amp;rsquo;oggetto target. In questo caso la fonte dei problemi &amp;egrave; il metodo &lt;code&gt;class&lt;/code&gt;: possiamo usare la console per mostrare il perch&amp;egrave;. Se chiamiamo &lt;code&gt;Product.search&lt;/code&gt; con un hash vuoto e chiamiamo &lt;code&gt;class&lt;/code&gt; sul risultato, vedremo che questo &amp;egrave; di tipo &lt;code&gt;DynamicDelegator&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
ruby-head &amp;gt; Product.search({}).class
 =&amp;gt; DynamicDelegator
&lt;/pre&gt;

&lt;p&gt;Ci&amp;ograve; indica che il nostro dynamic delegator non sta delegando tutto all&amp;rsquo;oggetto target dal momento che ha alcuni metodi gi&amp;agrave; definiti di per s&amp;egrave;. Il motivo per cui ce li ha, &amp;egrave; dovuto semplicemente al fatto che la classa &lt;code&gt;DynamicDelegator&lt;/code&gt; li eredita da &lt;code&gt;Object&lt;/code&gt; e &lt;code&gt;Object&lt;/code&gt; ha molti metodi definiti al suo interno, incluso &lt;code&gt;class&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="terminal"&gt;
ruby-head &amp;gt; Object.instance_methods.count
 =&amp;gt; 108 
ruby-head &amp;gt; Object.instance_methods.grep /class/
 =&amp;gt; [:subclasses_of, :class_eval, :class, :singleton_class]
&lt;/pre&gt;

&lt;p&gt;Se abbiamo bisogno di una classe pi&amp;ugrave; semplice da cui derivare, da Ruby 1.9 esiste un&amp;rsquo;altra classe che possiamo usare, chiamata BasicObject, che ha definiti molti meno metodi:&lt;/p&gt;

&lt;pre class="terminal"&gt;
ruby-head &amp;gt; BasicObject.instance_methods
 =&amp;gt; [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
&lt;/pre&gt; 

&lt;p&gt;Questo tipo di classe serve meglio a fare da base per un oggetto delegator o un proxy che si basano sul &lt;code&gt;method_missing&lt;/code&gt; per ridefinire il comportamento dei metodi. Possiamo cambiare il &lt;code&gt;DynamicDelegator&lt;/code&gt; per estendere da &lt;code&gt;BasicObject&lt;/code&gt; in modo tale che il metodo &lt;code&gt;class&lt;/code&gt; non sia ereditato (perch&amp;egrave; non definito nell&amp;rsquo;ancestor) e di conseguenza la chiamata a tale metodo sia ricondotta alla gestione mediante &lt;code&gt;method_missing&lt;/code&gt; e quindi delegata effettivamente al target object:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/lib/dynamic_delegator.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class DynamicDelegator &amp;lt; BasicObject
  def initialize(target)
    @target = target
  end  
  
  def method_missing(*args, &amp;amp;block)
    result = @target.send(*args, &amp;amp;block)
    @target = result if result.kind_of? @target.class
    result
  end
end
&lt;/pre&gt;

&lt;p&gt;Se ora ricarichiamo la pagina, funzioner&amp;agrave; nuovamente tutto.&lt;/p&gt;

&lt;div class="imageWrapper"&gt;
  &lt;img src="/system/photos/377/original/E212I01.png" width="800" height="315" alt="La pagina funziona nuovamente."/&gt;
&lt;/div&gt;

&lt;p&gt;C&amp;rsquo;&amp;egrave; un altro po&amp;rsquo; di refactoring che potremmo considerare di fare nella classe di modello &lt;code&gt;Product&lt;/code&gt;. Il &lt;code&gt;DynamicDelegator&lt;/code&gt; non esprime le proprie intenzioni in modo molto chiaro, per cui potremmo scrivere un metodo nella classe &lt;code&gt;Product&lt;/code&gt;, chiamato &lt;code&gt;scope_builder&lt;/code&gt; e creare l&amp;igrave; il &lt;code&gt;DynamicDelegator&lt;/code&gt;:&lt;/p&gt;

&lt;p class="codeFilePath"&gt;/app/models/product.rb&lt;/p&gt;
&lt;pre class="ruby"&gt;
class Product &amp;lt; ActiveRecord::Base
  belongs_to :category
  
  def self.search(params)
    products = scope_builder
    products.where(&amp;quot;name like ?&amp;quot;, &amp;quot;%&amp;quot; + params[:name] + &amp;quot;%&amp;quot;) if params[:name]
    products.where(&amp;quot;price &amp;gt;= ?&amp;quot;, params[:price_gt]) if params[:price_gt]
    products.where(&amp;quot;price &amp;lt;= ?&amp;quot;, params[:price_lt]) if params[:price_lt]    
    products
  end 
  
  def self.scope_builder
    DynamicDelegator.new(scoped)
  end

end
&lt;/pre&gt;

&lt;p&gt;Ora &amp;egrave; pi&amp;ugrave; chiaro capire che stiamo trattando con uno scope che abbiamo costruito dinamicamente. Se usiamo questa tecnica su pi&amp;ugrave; modelli di record, allora potremmo fattorizzare questo metodo &lt;code&gt;scope_builder&lt;/code&gt; in &lt;code&gt;ActiveRecord::Base&lt;/code&gt;, in modo tale da averlo disponibile su tutti i modelli. Quest&amp;rsquo;ultima cosa la potremmo realizzare propriamente in un initializer.&lt;/p&gt;
</description>
      <pubDate>Thu, 27 May 2010 15:23:21 +0000</pubDate>
      <guid>http://it.asciicasts.com/episodes/212-refactoring-e-dynamic-delegator</guid>
      <link>http://it.asciicasts.com/episodes/212-refactoring-e-dynamic-delegator</link>
    </item>
  </channel>
</rss>
