<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10italianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.stefanoverna.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <title>Stefano Verna</title>
  <id>http://stefanoverna.com/</id>
  <link href="http://stefanoverna.com/" />
  
  <updated>2012-02-06T00:00:00+01:00</updated>
  <author>
    <name>Stefano Verna (@steffoz)</name>
  </author>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.stefanoverna.com/stefanoverna" /><feedburner:info uri="stefanoverna" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare href="http://add.my.yahoo.com/content?lg=it&amp;url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://eur.i1.yimg.com/eur.yimg.com/i/it/my/mioya1.gif">Subscribe with Mio Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.stefanoverna.com/stefanoverna" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.stefanoverna.com%2Fstefanoverna" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry>
    <title>Faster TDD with iTerm and vim</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/vy-5NuQtFCc/faster-tdd-with-iterm-and-vim.html" rel="alternate" />
    <id>/blog/2012/02/faster-tdd-with-iterm-and-vim.html</id>
    <published>2012-02-06T00:00:00+01:00</published>
    <updated>2012-02-06T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;A couple of weeks ago &lt;a href="http://twitter.com/joshuadavey"&gt;Josh Davey&lt;/a&gt; released &lt;a href="https://github.com/jgdavey/vim-turbux"&gt;turbux&lt;/a&gt;, a great vim plugin that aims to keep your TDD feedback loop faster and less prone to unnecessary context-switches through the use of split sessions and &lt;a href="http://tmux.sourceforge.net/"&gt;tmux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before you continue reading this post, &lt;a href="http://joshuadavey.com/post/15619414829/faster-tdd-feedback-with-tmux-tslime-vim-and"&gt;check out the announcement post on his blog&lt;/a&gt;, so you know what we&amp;#39;re talking about.&lt;/p&gt;

&lt;h2&gt;iTerm anyone?&lt;/h2&gt;

&lt;p&gt;Anything that can speed up our TDD loop has to be taken very seriously, so I tried to port the same concepts to my personal development habits.&lt;/p&gt;

&lt;p&gt;In my day-to-day work I make use of &lt;a href="http://www.iterm2.com/"&gt;iTerm 2&lt;/a&gt;, a great replacement to the default Mac terminal application. iTerm provides both split and tab sessions; furthermore, it supports the use of AppleScript to automate many aspects of its behavior.&lt;/p&gt;

&lt;p&gt;I asked myself whether I really needed to add an &amp;quot;extra layer&amp;quot; to my usual stack — that is, tmux — or rather if I could achieve the same result without it. Turns out it was not the case.&lt;/p&gt;

&lt;h2&gt;How to communicate with iTerm&lt;/h2&gt;

&lt;p&gt;Man, I hate AppleScript. Approximately 90% of the entire time spent on this small project was just to learn the minimum necessary to write this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
#! /usr/bin/osascript
tell application &amp;quot;iTerm&amp;quot;
  tell the current terminal
    tell (first session Whose name contains &amp;quot;foo&amp;quot;)
      text write (&amp;quot;echo bar&amp;quot; as text)
    end tell
  end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code searches in the current iTerm window for a session named &amp;quot;foo&amp;quot;, and sends the command &lt;code&gt;echo bar&lt;/code&gt; to it. As you might have guessed, we just built a bridge between vim and an iTerm session.&lt;/p&gt;

&lt;h2&gt;Welcome iTermux!&lt;/h2&gt;

&lt;p&gt;Next step was to fork the turbux plugin, rename it into &lt;a href="https://github.com/welaika/vim-itermux"&gt;itermux&lt;/a&gt; (how original, right?) and replace its &lt;code&gt;Send_to_Tmux()&lt;/code&gt; function with a parametrized version of the snippet mentioned above:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
function! Send_to_iTerm(command)
  let app = &amp;#39;iTerm&amp;#39;
  if exists(&amp;quot;g:itermux_app_name&amp;quot;) &amp;amp;&amp;amp; g:itermux_app_name != &amp;#39;&amp;#39;
    let app = g:itermux_app_name
  endif
  let session = &amp;#39;iTermux&amp;#39;
  if exists(&amp;quot;g:itermux_session_name&amp;quot;) &amp;amp;&amp;amp; g:itermux_session_name != &amp;#39;&amp;#39;
    let session = g:itermux_session_name
  endif

  let commands =  [ &amp;#39;-e &amp;quot;on run argv&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell application \&amp;quot;&amp;#39; . app . &amp;#39;\&amp;quot;&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell the current terminal&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell (first session whose name contains \&amp;quot;&amp;#39; . session . &amp;#39;\&amp;quot;)&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;set AppleScript&amp;#39;&amp;#39;s text item delimiters to \&amp;quot; \&amp;quot;&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;write text (argv as text)&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end run&amp;quot;&amp;#39; ]

  let complete_command = &amp;quot;osascript &amp;quot; . join(commands, &amp;#39; &amp;#39;) . &amp;quot; &amp;quot; . a:command
  system(complete_command)
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, I&amp;#39;ve made it possible to change the name of the iTerm app and the name you want to give to the iTerm session dedicated to testing, i.e.:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
let g:itermux_session_name = &amp;#39;testing&amp;#39;
let g:itermux_app_name = &amp;#39;iTerm2&amp;#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Demo Time&lt;/h2&gt;

&lt;p&gt;I&amp;#39;ve prepared a &lt;a href="http://vimeo.com/welaika/itermux"&gt;small video&lt;/a&gt; to show how cool it is the final result. I&amp;#39;ve been using this setup for two weeks now, and it&amp;#39;s been so rewarding.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="http://player.vimeo.com/video/36213322?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="600" height="600" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;h2&gt;Feedbacks appreciated!&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/welaika/vim-itermux"&gt;Download it&lt;/a&gt;, install it, and let us know what you think of it!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;A couple of weeks ago &lt;a href="http://twitter.com/joshuadavey"&gt;Josh Davey&lt;/a&gt; released &lt;a href="https://github.com/jgdavey/vim-turbux"&gt;turbux&lt;/a&gt;, a great vim plugin that aims to keep your TDD feedback loop faster and less prone to unnecessary context-switches through the use of split sessions and &lt;a href="http://tmux.sourceforge.net/"&gt;tmux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before you continue reading this post, &lt;a href="http://joshuadavey.com/post/15619414829/faster-tdd-feedback-with-tmux-tslime-vim-and"&gt;check out the announcement post on his blog&lt;/a&gt;, so you know what we&amp;#39;re talking about.&lt;/p&gt;

&lt;h2&gt;iTerm anyone?&lt;/h2&gt;

&lt;p&gt;Anything that can speed up our TDD loop has to be taken very seriously, so I tried to port the same concepts to my personal development habits.&lt;/p&gt;

&lt;p&gt;In my day-to-day work I make use of &lt;a href="http://www.iterm2.com/"&gt;iTerm 2&lt;/a&gt;, a great replacement to the default Mac terminal application. iTerm provides both split and tab sessions; furthermore, it supports the use of AppleScript to automate many aspects of its behavior.&lt;/p&gt;

&lt;p&gt;I asked myself whether I really needed to add an &amp;quot;extra layer&amp;quot; to my usual stack — that is, tmux — or rather if I could achieve the same result without it. Turns out it was not the case.&lt;/p&gt;

&lt;h2&gt;How to communicate with iTerm&lt;/h2&gt;

&lt;p&gt;Man, I hate AppleScript. Approximately 90% of the entire time spent on this small project was just to learn the minimum necessary to write this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
#! /usr/bin/osascript
tell application &amp;quot;iTerm&amp;quot;
  tell the current terminal
    tell (first session Whose name contains &amp;quot;foo&amp;quot;)
      text write (&amp;quot;echo bar&amp;quot; as text)
    end tell
  end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code searches in the current iTerm window for a session named &amp;quot;foo&amp;quot;, and sends the command &lt;code&gt;echo bar&lt;/code&gt; to it. As you might have guessed, we just built a bridge between vim and an iTerm session.&lt;/p&gt;

&lt;h2&gt;Welcome iTermux!&lt;/h2&gt;

&lt;p&gt;Next step was to fork the turbux plugin, rename it into &lt;a href="https://github.com/welaika/vim-itermux"&gt;itermux&lt;/a&gt; (how original, right?) and replace its &lt;code&gt;Send_to_Tmux()&lt;/code&gt; function with a parametrized version of the snippet mentioned above:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
function! Send_to_iTerm(command)
  let app = &amp;#39;iTerm&amp;#39;
  if exists(&amp;quot;g:itermux_app_name&amp;quot;) &amp;amp;&amp;amp; g:itermux_app_name != &amp;#39;&amp;#39;
    let app = g:itermux_app_name
  endif
  let session = &amp;#39;iTermux&amp;#39;
  if exists(&amp;quot;g:itermux_session_name&amp;quot;) &amp;amp;&amp;amp; g:itermux_session_name != &amp;#39;&amp;#39;
    let session = g:itermux_session_name
  endif

  let commands =  [ &amp;#39;-e &amp;quot;on run argv&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell application \&amp;quot;&amp;#39; . app . &amp;#39;\&amp;quot;&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell the current terminal&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;tell (first session whose name contains \&amp;quot;&amp;#39; . session . &amp;#39;\&amp;quot;)&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;set AppleScript&amp;#39;&amp;#39;s text item delimiters to \&amp;quot; \&amp;quot;&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;write text (argv as text)&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end tell&amp;quot;&amp;#39;,
                  \ &amp;#39;-e &amp;quot;end run&amp;quot;&amp;#39; ]

  let complete_command = &amp;quot;osascript &amp;quot; . join(commands, &amp;#39; &amp;#39;) . &amp;quot; &amp;quot; . a:command
  system(complete_command)
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, I&amp;#39;ve made it possible to change the name of the iTerm app and the name you want to give to the iTerm session dedicated to testing, i.e.:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
let g:itermux_session_name = &amp;#39;testing&amp;#39;
let g:itermux_app_name = &amp;#39;iTerm2&amp;#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Demo Time&lt;/h2&gt;

&lt;p&gt;I&amp;#39;ve prepared a &lt;a href="http://vimeo.com/welaika/itermux"&gt;small video&lt;/a&gt; to show how cool it is the final result. I&amp;#39;ve been using this setup for two weeks now, and it&amp;#39;s been so rewarding.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="http://player.vimeo.com/video/36213322?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="600" height="600" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;h2&gt;Feedbacks appreciated!&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/welaika/vim-itermux"&gt;Download it&lt;/a&gt;, install it, and let us know what you think of it!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=eHgwjeqsQw4:8fO8kAmkJgg:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=eHgwjeqsQw4:8fO8kAmkJgg:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/vy-5NuQtFCc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/02/faster-tdd-with-iterm-and-vim.html</feedburner:origLink></entry>
  <entry>
    <title>Come testare i propri controller in isolamento: un esempio reale con CanCan</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/hO1Xz4f86-o/come-testare-i-propri-controller-in-isolamento-un-esempio-reale-con-cancan.html" rel="alternate" />
    <id>/blog/2012/01/come-testare-i-propri-controller-in-isolamento-un-esempio-reale-con-cancan.html</id>
    <published>2012-01-29T00:00:00+01:00</published>
    <updated>2012-01-29T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;div class="note"&gt;
&lt;strong&gt;TL;DR&lt;/strong&gt; Questo è un post per sviluppatori Rails di media esperienza.
L'obiettivo di questo (lungo) tutorial è quello di guidare il lettore
passo passo verso le possibili tecniche per testare i propri controller,
evidenziandone problematiche e vantaggi. Arriveremo al termine del
tutorial ad un soluzione rapida e mantenibile, che testi in completo
isolamento il controller e che farà uso di strumenti come &lt;em&gt;stubs&lt;/em&gt; e &lt;em&gt;mocks&lt;/em&gt;.
&lt;/div&gt;

&lt;p&gt;Nella stragrande maggioranza dei progetti ci si ritrova a dover
gestire autorizzazioni e ruoli per gli utenti. La gemma più popolare per
questo compito è senza dubbio &lt;a href="https://github.com/ryanb/cancan"&gt;CanCan&lt;/a&gt;. Mi sembra
questo un ottimo esempio concreto da sfruttare per la trattazione.&lt;/p&gt;

&lt;p&gt;Nella &lt;a href="https://github.com/ryanb/cancan/wiki/Testing-Abilities"&gt;Wiki del progetto su Github&lt;/a&gt;, il buon Ryan Bates elenca un paio di
possibili suggerimenti per approcciarsi al problema dei test funzionali.
Nei passati progetti ho cercato di seguirli diligentemente, ma la verità
è che sono esempi pessimi se seguiti nel mondo reale.&lt;/p&gt;

&lt;p&gt;Partiamo con l&amp;#39;analizzare meglio i problemi.&lt;/p&gt;
</summary>
    <content type="html">&lt;div class="note"&gt;
&lt;strong&gt;TL;DR&lt;/strong&gt; Questo è un post per sviluppatori Rails di media esperienza.
L'obiettivo di questo (lungo) tutorial è quello di guidare il lettore
passo passo verso le possibili tecniche per testare i propri controller,
evidenziandone problematiche e vantaggi. Arriveremo al termine del
tutorial ad un soluzione rapida e mantenibile, che testi in completo
isolamento il controller e che farà uso di strumenti come &lt;em&gt;stubs&lt;/em&gt; e &lt;em&gt;mocks&lt;/em&gt;.
&lt;/div&gt;

&lt;p&gt;Nella stragrande maggioranza dei progetti ci si ritrova a dover
gestire autorizzazioni e ruoli per gli utenti. La gemma più popolare per
questo compito è senza dubbio &lt;a href="https://github.com/ryanb/cancan"&gt;CanCan&lt;/a&gt;. Mi sembra
questo un ottimo esempio concreto da sfruttare per la trattazione.&lt;/p&gt;

&lt;p&gt;Nella &lt;a href="https://github.com/ryanb/cancan/wiki/Testing-Abilities"&gt;Wiki del progetto su Github&lt;/a&gt;, il buon Ryan Bates elenca un paio di
possibili suggerimenti per approcciarsi al problema dei test funzionali.
Nei passati progetti ho cercato di seguirli diligentemente, ma la verità
è che sono esempi pessimi se seguiti nel mondo reale.&lt;/p&gt;

&lt;p&gt;Partiamo con l&amp;#39;analizzare meglio i problemi.&lt;/p&gt;



&lt;h2&gt;Un esempio concreto&lt;/h2&gt;

&lt;p&gt;Prendiamo come esempio una azione classica azione custom che fa uso di
CanCan:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class ItemsController &amp;lt; ApplicationController
  load_and_authorize_resource :project
  load_and_authorize_resource :item, :through =&amp;gt; :project

  # /projects/:project_id/items/:id/add_tag?tag_id=XXX
  def add_tag
    @tag = Tag.find(params[:tag_id])
    authorize! :read, @tag
    @item.tags &amp;lt;&amp;lt; @tag

    respond_with @item
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il modello &lt;code&gt;Item&lt;/code&gt; appartiene ad un &lt;code&gt;Project&lt;/code&gt;. L&amp;#39;azione &lt;code&gt;add_tag&lt;/code&gt; prevede
un parametro aggiuntivo in &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;:tag_id&lt;/code&gt;. Cosa fa il metodo? Aggiunge
il tag alla collezione dei tag dell&amp;#39;item stesso. Semplice ed
indolore.&lt;/p&gt;

&lt;p&gt;Le autorizzazioni più o meno implicite che CanCan esegue nel corso di questa
azione sono le seguenti:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
can? :show, @profile
can? :add_tag, @item
can? :read, @tag
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Definiamo dunque nella classe &lt;code&gt;Ability&lt;/code&gt; le seguenti regole:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

def initialize(user)
  if user.present?
    can :show, Project, user_id: user
    can :add_tag, Item, project_id: user.projects
    can :read, Tag, public: true
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In pratica, stiamo dicendo a CanCan che:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un utente può accedere solo ai suoi progetti;&lt;/li&gt;
&lt;li&gt;che può aggiungere tag ad un item solo se 1) ne è il proprietario e 2) se il tag stesso è pubblico.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ora proviamo a scrivere il test secondo i suggerimenti nella Wiki.&lt;/p&gt;

&lt;h2&gt;Testare l&amp;#39;azione, Integration-style&lt;/h2&gt;

&lt;p&gt;La prima soluzione suggerita da CanCan per testare il controller è la seguente:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

describe ItemsController do
  describe &amp;quot;#add_tag&amp;quot; do

    before do
      @user = Factory.create(:user)
      controller.stubs(:current_user).returns(@user)
    end

    context &amp;quot;if the user passes all the authorizations&amp;quot; do
      it &amp;quot;adds the specified tag to the item if authorizatio&amp;quot; do
        @project = Factory.create(:project, user: @user)
        @item = Factory.create(:item, project: @project)
        @tag = Factory.create(:tag, public: true)

        get :add_tag, project_id: @project, id: @item, tag_id: @tag

        @item.tags.should include @tag
        response.should be_success
      end
    end

  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In pratica, ci comportiamo come nel più classico degli integration
tests: prima creiamo tutti i modelli necessari per far funzionare
l&amp;#39;azione, dopodichè la eseguiamo, e al termine controlliamo il risultato
finale, sia in termini di modello modificato che di status code della
risposta generata.&lt;/p&gt;

&lt;h3&gt;Fantastico! Anzi, no.&lt;/h3&gt;

&lt;p&gt;Certo, questo è un possibile metodo per testare l&amp;#39;azione del controller. Il vantaggio
è l&amp;#39;estrema semplicità di scrittura. Il grosso contro è che in realtà non stiamo testando
solo il controller, ma implicitamente anche una marea di altre cose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I modelli dati in pasto all&amp;#39;azione vengono salvati su DB: se per caso il DB
non fosse stato migrato correttamente, il test fallirebbe, a prescindere
dalla non colpevolezza del codice del controller;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Se tra qualche giorno dovessimo aggiungere al modello &lt;code&gt;Project&lt;/code&gt; un nuovo
attributo &lt;code&gt;:title&lt;/code&gt; con presenza obbligatoria (&lt;code&gt;validates :title, presence: true&lt;/code&gt;),
il test fallirebbe perchè non riuscirebbe più a creare un oggetto &lt;code&gt;Project&lt;/code&gt;, ma, di
nuovo, l&amp;#39;azione &lt;code&gt;add_tag&lt;/code&gt; sarebbe totalmente innocente;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Se in futuro dovessero cambiare i meccanismi all&amp;#39;interno di &lt;code&gt;Ability&lt;/code&gt; secondo i
quali l&amp;#39;autorizzazione viene concessa, il test fallirebbe, e di nuovo non sarebbe colpa del
controller.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quindi, in altre parole, &lt;strong&gt;stiamo creando un test di difficile
mantenibilità futura&lt;/strong&gt;, in quanto troppo dipendente e legato a fattori
ed oggetti esterni.&lt;/p&gt;

&lt;p&gt;Un test del genere poi da solo non sarebbe sufficiente: in questo modo &lt;strong&gt;stiamo testando unicamente
l&amp;#39;esecuzione con successo dell&amp;#39;azione&lt;/strong&gt;, ma a questo punto dovremmo anche preoccuparci
di testare il comportamento del controller nel caso in cui l&amp;#39;utente
provi a lanciare l&amp;#39;azione senza avere uno dei permessi sopra citati,
per assicurarci che venga adeguatamente bloccato. &lt;strong&gt;I test diventerebbero
allora quattro, per una sola azione!&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
context &amp;quot;if the user passes all the authorizations&amp;quot; do
  it &amp;quot;adds the specified tag to the item&amp;quot; do
    # ...
  end
end

context &amp;quot;if the user cannot :show the Project&amp;quot; do
  it &amp;quot;raises a CanCan::AccessDenied exception&amp;quot; do
    # ...
  end
end

context &amp;quot;if the user cannot :add_tag to the Item&amp;quot; do
  it &amp;quot;raises a CanCan::AccessDenied exception&amp;quot; do
    # ...
  end
end

context &amp;quot;if the user cannot :read the Tag&amp;quot; do
  it &amp;quot;raises a CanCan::AccessDenied exception&amp;quot; do
    # ...
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Consideriamo ora anche il fattore velocità. I test di integrazione
sono lenti per loro stessa natura: il loro compito è quello di riprodurre per filo e per segno
quello che è il flusso normale di utilizzo dell&amp;#39;applicazione, con tutte le innumerevoli
interazioni con la base dati e gli altri oggetti che compongono la logica dell&amp;#39;applicazione.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I quattro test del genere impiegano circa un secondo per venire eseguiti!&lt;/strong&gt; Se consideriamo una applicazione
semplice con una media di 4-5 azioni per 10 controller, arriviamo tranquillamente al minuto.&lt;/p&gt;

&lt;p&gt;Se il tempo non sembra poi così alto, ricordiamoci che stiamo parlando di controller, la componente
dell&amp;#39;applicazione che dovrebbe essere più snella in assoluto! Dev&amp;#39;esserci qualcosa di meglio per testare le &lt;em&gt;quattro dannatissime&lt;/em&gt; righe di codice dell&amp;#39;azione!&lt;/p&gt;

&lt;h2&gt;Isolarsi da &lt;code&gt;Ability&lt;/code&gt;: un primo passo verso la speranza&lt;/h2&gt;

&lt;p&gt;La &lt;a href="https://github.com/ryanb/cancan/wiki/Testing-Abilities"&gt;solita Wiki&lt;/a&gt; ci suggerisce che è possibile testare il comportamento
del controller indipendentemente da ciò che viene specificato dal file
&lt;code&gt;Ability&lt;/code&gt;. Vediamo come:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
describe &amp;quot;#add_tag&amp;quot; do

  before do
    @ability = Object.new
    @ability.extend(CanCan::Ability)
    controller.stubs(:current_ability).returns(@ability)
  end

  context &amp;quot;if the user passes all the authorizations&amp;quot; do
    it &amp;quot;adds the specified tag to the item&amp;quot; do
      @ability.can(:show, Project)
      @ability.can(:add_tag, Item)
      @ability.can(:read, Tag)

      project = Factory.create(:project)
      item = Factory.create(:item)
      tag = Factory.create(:tag)

      get :add_tag, project_id: project, id: item, tag_id: tag

      item.tags.should include tag
      response.should be_success
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il test si basa sulla consapevolezza che CanCan fa uso del metodo &lt;code&gt;current_ability&lt;/code&gt;
del controller per sapere quale dev&amp;#39;essere l&amp;#39;oggetto con modulo &lt;code&gt;CanCan::Ability&lt;/code&gt;
da utilizzare per i test di autorizzazione.&lt;/p&gt;

&lt;p&gt;Il comportamento standard del metodo &lt;code&gt;current_ability&lt;/code&gt; è quello di restituire un&amp;#39;istanza
della classe &lt;code&gt;Ability&lt;/code&gt;, ma nel blocco &lt;code&gt;before&lt;/code&gt; del test &lt;strong&gt;sostituiamo l&amp;#39;oggetto che
l&amp;#39;applicazione normalmente restituirebbe con un suo alter-ego&lt;/strong&gt;, che possiamo però
modificare a piacimento a seconda dei test. Questa è una delle tecniche
fondamentali di testing: viene chiamata &lt;a href="http://yarorb.wordpress.com/2007/11/26/mocks-and-stubs-in-ruby-on-rails-the-mocha-solution/"&gt;&lt;em&gt;stubbing&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notate come nelle righe &lt;code&gt;15-17&lt;/code&gt; non abbiamo più bisogno di specificare le relazioni
tra progetto, item e tag, perchè prima dell&amp;#39;esecuzione del test, nelle righe &lt;code&gt;11-13&lt;/code&gt;
&lt;strong&gt;stiamo forzando un successo nell&amp;#39;autenticazione&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In altre parole, abbiamo a tutti gli effetti isolato il test sul controller dalla classe
&lt;code&gt;Ability&lt;/code&gt; dell&amp;#39;app. Sarà compito dei test sulla classe &lt;code&gt;Ability&lt;/code&gt; controllare
che esso autorizzi solo nel caso in cui i modelli sono legati tra loro
in maniera corretta. Tutti i test sulle azioni dei controller danno
per assodato questo fatto, bypassano la classe &lt;code&gt;Activity&lt;/code&gt; e dunque
eviteranno di spaccarsi nel caso di suoi cambiamenti futuri.&lt;/p&gt;

&lt;div class="important"&gt;
Non è compito del test su un controller assicurarsi che con determinati modelli
venga fornita una certa autorizzazione. Il suo compito è quello di
verificare il comportamento del controller a seconda delle possibili risposte
di autorizzazione che gli vengono fornite da CanCan.
&lt;/div&gt;

&lt;p&gt;Attenzione: &lt;strong&gt;continuiamo ad aver bisogno di quattro differenti test per controllare
il diverso comportamento del controller&lt;/strong&gt; nelle varie casistiche di permesso
autorizzato/non autorizzato — e i test continuano ad essere lenti
perchè stiamo ancora scrivendo su DB — ma quantomeno abbiamo ottenuto
qualcosa di più mantenibile!&lt;/p&gt;

&lt;h2&gt;Avanti tutta! Isoliamoci dai modelli!&lt;/h2&gt;

&lt;p&gt;Iniziamo ad intravedere la via del successo. Proviamo ad applicare il
medesimo approccio di isolamento e &lt;em&gt;stubbing&lt;/em&gt; anche ai modelli coinvolti.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
describe &amp;quot;#add_tag&amp;quot; do
  before do
    @ability = Object.new
    @ability.extend(CanCan::Ability)
    controller.stubs(:current_ability).returns(@ability)
  end

  context &amp;quot;if the user passes all the authorizations&amp;quot; do
    it &amp;quot;adds the specified tag to the item&amp;quot; do
      @ability.can(:show, Project)
      @ability.can(:add_tag, Item)
      @ability.can(:read, Tag)

      project = stub_model(Project)
      item = stub_model(Item)
      tag = stub_model(Tag)

      Project.stubs(:find).with(project.to_param).returns(project)
      project.stubs(:items).returns(stub(&amp;#39;Association&amp;#39;, name: &amp;#39;items&amp;#39;, find: item))
      Tag.stubs(:find).with(tag.to_param).returns(tag)

      get :add_tag, project_id: project, id: item, tag_id: tag

      item.tags.should include tag
      response.should be_success
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nelle righe &lt;code&gt;14-16&lt;/code&gt; invece che salvare su DB dei modelli veri, creiamo
dei &lt;em&gt;model stub&lt;/em&gt;: oggetti che hanno sembianze di modelli &lt;code&gt;ActiveRecord&lt;/code&gt; correttamente
salvati su DB — hanno per esempio &lt;code&gt;id&lt;/code&gt; incrementali, fingono di essere
istanze del modello specificato e rispondono a metodi &lt;code&gt;ActiveRecord&lt;/code&gt; classici
quali &lt;code&gt;errors&lt;/code&gt; o &lt;code&gt;to_param&lt;/code&gt; — ma che sono &lt;strong&gt;totalmente sintetici e non
persistenti&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A questo punto, per portare a buon fine il test
è necessario sapere come si comporta CanCan nella fase
di caricamento delle risorse per questa azione (i comportamenti
ben documentati nella Wiki):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
@project = Project.find(params[:project_id])
@item = project.items.find(params[:id])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nelle righe &lt;code&gt;18-20&lt;/code&gt; ora siamo in grado di eseguire lo &lt;em&gt;stubbing&lt;/em&gt;
delle medesime chiamate, inducendo CanCan a ritornarci invece che modelli veri
i nostri modelli farlocchi.&lt;/p&gt;

&lt;p&gt;Di nuovo, &lt;strong&gt;abbiamo a tutti gli effetti isolato il test sul controller dai veri modelli
dell&amp;#39;app, e da ActiveRecord in generale&lt;/strong&gt;: se tra qualche giorno dovessimo
aggiungere al modello &lt;code&gt;Project&lt;/code&gt; un nuovo attributo obbligatorio, il test questa
volta non fallirebbe più.&lt;/p&gt;

&lt;div class="important"&gt;
Il controller di per sé si aspetta che esistano dei modelli salvati su DB. Non è compito del test
su un controller inizializzare e salvare modelli. E' sufficiente assicurarsi che il controller
effettivamente effettui delle chiamate per fetcharli.
&lt;/div&gt;

&lt;h2&gt;L&amp;#39;ultimo passaggio: un test che ne vale quattro (WHOA!)&lt;/h2&gt;

&lt;p&gt;Anche nell&amp;#39;ultima versione del test, continuiamo a forzare il successo
dell&amp;#39;autenticazione mediante &lt;em&gt;stubbing&lt;/em&gt;  del metodo &lt;code&gt;current_ability&lt;/code&gt;. Questo significa che se in un
test forziamo il successo, dovremo automaticamente avere altri test per forzare anche
l&amp;#39;insuccesso, di modo da poter verificare che l&amp;#39;azione effettivamente
faccia uso delle &lt;code&gt;Ability&lt;/code&gt;, e che dunque blocchi l&amp;#39;utente.&lt;/p&gt;

&lt;p&gt;La soluzione per ridurre i test ad uno, e uno solo, è questa:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
describe &amp;quot;#add_tag&amp;quot; do
  context &amp;quot;if the user passes all the authorizations&amp;quot; do
    it &amp;quot;adds the specified tag to the item&amp;quot; do
      project = stub_model(Project)
      item = stub_model(Item)
      tag = stub_model(Tag)

      should_authorize(:show, project)
      should_authorize(:add_tag, item)
      should_authorize(:read, tag)

      Project.stubs(:find).with(project.to_param).returns(project)
      project.stubs(:items).returns(stub(&amp;#39;Association&amp;#39;, name: &amp;#39;items&amp;#39;, find: item))

      Tag.stubs(:find).with(tag.to_param).returns(tag)

      get :add_tag, project_id: project, id: item, tag_id: tag

      item.tags.should include tag
      response.should be_success
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E&amp;#39; cambiato poco, in superficie: il blocco &lt;code&gt;before&lt;/code&gt; che &lt;em&gt;stubbava&lt;/em&gt; il metodo
&lt;code&gt;current_ability&lt;/code&gt; del controller se ne è andato, e le tre chiamate
ad &lt;code&gt;@ability.can&lt;/code&gt; sono state sostituite da un fantomatico metodo,
che ho chiamato &lt;code&gt;should_authorize&lt;/code&gt;, con una semantica molto simile.&lt;/p&gt;

&lt;p&gt;Cosa sta succedendo? Succede che andiamo ancora più alla fonte. CanCan, dopo aver
caricato i modelli, fa subito uso del metodo &lt;code&gt;authorize!&lt;/code&gt; del controller per
testare l&amp;#39;autorizzazione da parte dell&amp;#39;utente. E&amp;#39; &lt;code&gt;authorize!&lt;/code&gt; che fa uso del metodo
&lt;code&gt;current_ability&lt;/code&gt; finora simulato.&lt;/p&gt;

&lt;p&gt;Andiamo a vedere il codice dell&amp;#39;helper RSpec &lt;code&gt;should_authorize&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
def should_authorize(action, subject)
  controller.expects(:authorize!).with(action, subject).returns(&amp;#39;passed!&amp;#39;)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Invece che &lt;em&gt;stubbare&lt;/em&gt; &lt;code&gt;current_ability&lt;/code&gt;, ora stiamo &lt;em&gt;stubbando&lt;/em&gt; direttamente
&lt;code&gt;authorize!&lt;/code&gt;, facendolo passare sempre e comunque, ma non solo: utilizziamo il metodo
&lt;code&gt;expects&lt;/code&gt; invece che &lt;code&gt;stubs&lt;/code&gt;. La differenza è piccola ma fondamentale.
Il metodo &lt;code&gt;expects&lt;/code&gt; &lt;strong&gt;non solo modifica la risposta, ma fa fallire il
test se quel dato metodo, con quei dati parametri, non verrà effettivamente
chiamato durante il corso dell&amp;#39;azione del controller.&lt;/strong&gt; Questa variante di
&lt;em&gt;stubbing&lt;/em&gt; viene comunemente chiamata &lt;em&gt;mocking&lt;/em&gt;, e ci permette di essere
tranquilli sull&amp;#39;effettiva protezione implementata dal controller.&lt;/p&gt;

&lt;div class="important"&gt;
Non è compito del test su un controller assicurarsi che CanCan faccia
uso della classe &lt;code&gt;Ability&lt;/code&gt; per far passare o meno l'autorizzazione ad una
azione. Ne tantomeno che il metodo &lt;code&gt;authorize!&lt;/code&gt; lasci passare l'utente o
lanci un'eccezione. Questo tipo di testing è già stato fatto a livello
di gemma. Il controller deve semplicemente assicurarsi che il metodo
&lt;code&gt;authorize!&lt;/code&gt; messogli a disposizione dalla gemma stessa venga
effettivamente chiamato.
&lt;/div&gt;

&lt;h2&gt;Abbiamo finito? SRSLY?!&lt;/h2&gt;

&lt;p&gt;A questo punto possiamo — &lt;em&gt;se solo Dio volesse&lt;/em&gt; — dirci soddisfatti.
Abbiamo un solo test per il controller. Un test che totalmente isolato
dal resto dell&amp;#39;app, che non si spaccherà se non per motivi reali e
dipendenti dall&amp;#39;azione stessa, e che — non facendo uso del livello
database — verrà eseguito in pochi millisecondi.&lt;/p&gt;

&lt;p&gt;Scusate per il disagio del post. Spero che possa essere utile a
qualcuno!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=sIfK_FuXv8A:fyyfgFp39fk:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=sIfK_FuXv8A:fyyfgFp39fk:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/hO1Xz4f86-o" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/come-testare-i-propri-controller-in-isolamento-un-esempio-reale-con-cancan.html</feedburner:origLink></entry>
  <entry>
    <title>Ottimizzare la leggibilità del proprio sito con Compass /3</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/INUmZuH_WPA/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-3.html" rel="alternate" />
    <id>/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-3.html</id>
    <published>2012-01-22T00:00:00+01:00</published>
    <updated>2012-01-22T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Bene, dopo una sfarinatura su come mantenere un ritmo verticale tramite
Compass, su come impostare una scala tipografica, su come gestire in
maniera ottimale i font-size mediante &lt;code&gt;em&lt;/code&gt;, arriviamo all&amp;#39;ultimo
passaggio: come scegliere in maniera ottimale &lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;font-size&lt;/code&gt;
per il tag &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, ovvero le dimensioni che saranno di riferimento per l&amp;#39;intero sito?&lt;/p&gt;

&lt;p&gt;La soluzione non è ovviamente farina del mio sacco, ma si
rifà al concetto di &lt;a href="http://www.pearsonified.com/2011/12/golden-ratio-typography.php"&gt;Golden Ratio
Typography&lt;/a&gt;,
ideato da Chris Pearson.&lt;/p&gt;

&lt;p&gt;L&amp;#39;impianto della trattazione si basa su due evidenze provabili empiricamente da chiunque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le proprietà &lt;code&gt;font-size&lt;/code&gt; e &lt;code&gt;line-height&lt;/code&gt; sono legate
linearmente: per mantenere un testo leggibile, se una aumenta l&amp;#39;altra
dovrà aumentare proporzionalmente;&lt;/li&gt;
&lt;li&gt;Similmente, larghezza del corpo del testo (da qui in poi &lt;code&gt;line-width&lt;/code&gt;) e &lt;code&gt;line-height&lt;/code&gt; sono legate
linearmente: più la larghezza aumenta, più difficile diventa per
l&amp;#39;occhio umano seguire le linee di testo, e dunque diventa necessario
aumentare anche l&amp;#39;interlinea.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chris Pearson si è spinto oltre, cercando di legare numericamente queste
grandezze, facendo uso del famoso rapporto aureo:&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/optimal-line-height.gif" alt="Line height"&gt;
&lt;img src="/data/vertical_rythm/optimal-line-width.gif" alt="Line width"&gt;&lt;/p&gt;

&lt;p&gt;Quindi, è sufficiente scegliere il &lt;code&gt;font-size&lt;/code&gt; di riferimento, per
esempio &lt;code&gt;16px&lt;/code&gt;, e &lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;line-width&lt;/code&gt; verranno da sè:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
$phi-const: 1.61803399
$base-font-size: 15px
$base-line-height: $base-font-size * $phi-const
$base-line-width: $base-line-height * $base-line-height
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In linea teorica, il concetto termina qui. Dal punto di vista pratico
però, i nostri schermi lavorano su un&amp;#39;unità di misura non
&amp;quot;spezzabile&amp;quot;: il pixel. Il calcolo riportato
porterebbe ad una interlinea di &lt;code&gt;24.27px&lt;/code&gt;, ovviamente non realizzabile.
Occorrerebbe portarlo a &lt;code&gt;24px&lt;/code&gt;, ma non sarebbe l&amp;#39;arrotondamento
ottimale.&lt;/p&gt;

&lt;p&gt;Matematicamente parlando, ciò che bisogna realizzare è un
&amp;quot;minimizzatore&amp;quot; di errore, in grado di ricercare il minimo locale.
Trovare dunque la combinazione di pixel &amp;quot;interi&amp;quot; per &lt;code&gt;font-size&lt;/code&gt;,
&lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;line-width&lt;/code&gt; che globalmente più si avvicinano ai valori aurei.&lt;/p&gt;

&lt;p&gt;Non si tratta di calcoli piacevolissimi, e fortunatamente Chris Pearson
ha creato il &lt;a href="http://www.pearsonified.com/typography/"&gt;Golden Ratio Typography Calculator&lt;/a&gt;,
che fa tutto il lavoro sporco per noi. Possiamo fissare sul calcolatore il &lt;code&gt;font-size&lt;/code&gt;
prescelto, o piuttosto la larghezza del contenuto, o entrambi. Il buon
calcolatore ci restituirà il &lt;code&gt;line-height&lt;/code&gt; ottimale, oltre a suggerirci
eventuali cambiamenti di dimensioni che potrebbero ulteriormente
migliorare la resa.&lt;/p&gt;

&lt;p&gt;Niente. Dovrei aver finito, siete pronti per rendere i vostri blog
super-leggibili. La mia parentesi tipografica si chiude temporaneamente.&lt;/p&gt;

&lt;p&gt;Per un po&amp;#39; ritornerò a parlare di codice, tante cose bollono in
pentola!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Bene, dopo una sfarinatura su come mantenere un ritmo verticale tramite
Compass, su come impostare una scala tipografica, su come gestire in
maniera ottimale i font-size mediante &lt;code&gt;em&lt;/code&gt;, arriviamo all&amp;#39;ultimo
passaggio: come scegliere in maniera ottimale &lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;font-size&lt;/code&gt;
per il tag &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, ovvero le dimensioni che saranno di riferimento per l&amp;#39;intero sito?&lt;/p&gt;

&lt;p&gt;La soluzione non è ovviamente farina del mio sacco, ma si
rifà al concetto di &lt;a href="http://www.pearsonified.com/2011/12/golden-ratio-typography.php"&gt;Golden Ratio
Typography&lt;/a&gt;,
ideato da Chris Pearson.&lt;/p&gt;

&lt;p&gt;L&amp;#39;impianto della trattazione si basa su due evidenze provabili empiricamente da chiunque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le proprietà &lt;code&gt;font-size&lt;/code&gt; e &lt;code&gt;line-height&lt;/code&gt; sono legate
linearmente: per mantenere un testo leggibile, se una aumenta l&amp;#39;altra
dovrà aumentare proporzionalmente;&lt;/li&gt;
&lt;li&gt;Similmente, larghezza del corpo del testo (da qui in poi &lt;code&gt;line-width&lt;/code&gt;) e &lt;code&gt;line-height&lt;/code&gt; sono legate
linearmente: più la larghezza aumenta, più difficile diventa per
l&amp;#39;occhio umano seguire le linee di testo, e dunque diventa necessario
aumentare anche l&amp;#39;interlinea.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chris Pearson si è spinto oltre, cercando di legare numericamente queste
grandezze, facendo uso del famoso rapporto aureo:&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/optimal-line-height.gif" alt="Line height"&gt;
&lt;img src="/data/vertical_rythm/optimal-line-width.gif" alt="Line width"&gt;&lt;/p&gt;

&lt;p&gt;Quindi, è sufficiente scegliere il &lt;code&gt;font-size&lt;/code&gt; di riferimento, per
esempio &lt;code&gt;16px&lt;/code&gt;, e &lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;line-width&lt;/code&gt; verranno da sè:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
$phi-const: 1.61803399
$base-font-size: 15px
$base-line-height: $base-font-size * $phi-const
$base-line-width: $base-line-height * $base-line-height
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In linea teorica, il concetto termina qui. Dal punto di vista pratico
però, i nostri schermi lavorano su un&amp;#39;unità di misura non
&amp;quot;spezzabile&amp;quot;: il pixel. Il calcolo riportato
porterebbe ad una interlinea di &lt;code&gt;24.27px&lt;/code&gt;, ovviamente non realizzabile.
Occorrerebbe portarlo a &lt;code&gt;24px&lt;/code&gt;, ma non sarebbe l&amp;#39;arrotondamento
ottimale.&lt;/p&gt;

&lt;p&gt;Matematicamente parlando, ciò che bisogna realizzare è un
&amp;quot;minimizzatore&amp;quot; di errore, in grado di ricercare il minimo locale.
Trovare dunque la combinazione di pixel &amp;quot;interi&amp;quot; per &lt;code&gt;font-size&lt;/code&gt;,
&lt;code&gt;line-height&lt;/code&gt; e &lt;code&gt;line-width&lt;/code&gt; che globalmente più si avvicinano ai valori aurei.&lt;/p&gt;

&lt;p&gt;Non si tratta di calcoli piacevolissimi, e fortunatamente Chris Pearson
ha creato il &lt;a href="http://www.pearsonified.com/typography/"&gt;Golden Ratio Typography Calculator&lt;/a&gt;,
che fa tutto il lavoro sporco per noi. Possiamo fissare sul calcolatore il &lt;code&gt;font-size&lt;/code&gt;
prescelto, o piuttosto la larghezza del contenuto, o entrambi. Il buon
calcolatore ci restituirà il &lt;code&gt;line-height&lt;/code&gt; ottimale, oltre a suggerirci
eventuali cambiamenti di dimensioni che potrebbero ulteriormente
migliorare la resa.&lt;/p&gt;

&lt;p&gt;Niente. Dovrei aver finito, siete pronti per rendere i vostri blog
super-leggibili. La mia parentesi tipografica si chiude temporaneamente.&lt;/p&gt;

&lt;p&gt;Per un po&amp;#39; ritornerò a parlare di codice, tante cose bollono in
pentola!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=SMwBgXI10I8:B5XSwEiyLeY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=SMwBgXI10I8:B5XSwEiyLeY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/INUmZuH_WPA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-3.html</feedburner:origLink></entry>
  <entry>
    <title>Ottimizzare la leggibilità del proprio sito con Compass /2</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/aTE66_wT96c/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-2.html" rel="alternate" />
    <id>/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-2.html</id>
    <published>2012-01-12T00:00:00+01:00</published>
    <updated>2012-01-12T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Al termine del post precedente, abbiamo visto come mantenere
tutte le interlinee multiple tra di loro permetta una maggiore
leggibilità del testo. Per essere più chiari, ecco &lt;a href="http://24ways.org/examples/compose-to-a-vertical-rhythm/example.html"&gt;un esempio&lt;/a&gt;
di ciò di cui stiamo parlando.&lt;/p&gt;

&lt;p&gt;Nel mondo web, l&amp;#39;unità base di spazio verticale è rappresentata dalla regola CSS
&lt;code&gt;line-height&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/font-size-line-height.png" alt="Line height"&gt;&lt;/p&gt;

&lt;p&gt;Una &lt;code&gt;line-height&lt;/code&gt; rimane costante nella pagina fino a quando non
intervengono cambi di &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt; o &lt;code&gt;border&lt;/code&gt;.
A questo punto tipicamente il flusso si spezza producendo interlinee
non multiple.&lt;/p&gt;

&lt;p&gt;Riuscire a impostare dimensioni di &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt; e &lt;code&gt;border&lt;/code&gt; tali
da preservare il flusso verticale in tutta la pagina richiede una serie
di calcoli non particolarmente complessi, ma sicuramente poco divertenti.&lt;/p&gt;

&lt;p&gt;Fortunatamente Compass include un modulo poco documentato ma ottimo in grado di
alleviare di molto questo problema.&lt;/p&gt;

&lt;h2&gt;Vertical Rythm con Compass&lt;/h2&gt;

&lt;p&gt;Una volta importato il modulo tramite la direttiva&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
@import &amp;quot;compass/typography/vertical_rhythm&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;siamo pronti a partire specificando il &lt;code&gt;font-size&lt;/code&gt; e la &lt;code&gt;line-height&lt;/code&gt; che abbiamo scelto
per il corpo principale del testo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
$base-line-height: 25px
$base-font-size: 15px

+establish-baseline
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il mixin &lt;code&gt;+establish-baseline&lt;/code&gt; si occupa proprio di impostare questi due valori
sul selettore &lt;code&gt;body&lt;/code&gt; (utilizzando come unità di misura l&amp;#39;&lt;em&gt;em&lt;/em&gt; per
evitare problemi di resize su browser legacy).&lt;/p&gt;

&lt;div class="important" markdown="1"&gt;
Una volta proceduti col seguente setup del modulo, l'unica regola da ricordare
è questa: le proprietà `font-size`, `line-height`, `margin`, `padding` e `border` non vanno mai
toccate manualmente, perchè spezzerebbero il ritmo. Per modificarle è necessario passare
attraverso appositi mixin di Compass.
&lt;/div&gt;

&lt;p&gt;Supponiamo di voler stilizzare il tag &lt;code&gt;h1&lt;/code&gt;, portando il &lt;code&gt;font-size&lt;/code&gt; a 24px,
e aggiungendo del margine in basso. Non possiamo più fare:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  font-size: 24px
  margin-bottom: 15px
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ma dovremo invece scrivere qualcosa come:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  +adjust-font-size-to(24px, 2)
  +padding-trailer(3, 24px)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il primo mixin utilizzato, &lt;code&gt;+adjust-font-size-to&lt;/code&gt;, oltre ad impostare il nuovo &lt;code&gt;font-size&lt;/code&gt;,
modificherà la line-height dell&amp;#39;elemento in modo tale da preservare l&amp;#39;interlinea originaria
di &lt;code&gt;25px&lt;/code&gt;, od un suo multiplo. Il secondo parametro, impostato nell&amp;#39;esempio a 2,
imposterà un line-height effettivo di &lt;code&gt;25px * 2 = 50px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il mixin &lt;code&gt;+padding-trailer&lt;/code&gt;, similmente, imposterà un &lt;code&gt;padding-bottom&lt;/code&gt; in &lt;em&gt;em&lt;/em&gt; pari
esattamente a &lt;code&gt;26px * 3 = 75px&lt;/code&gt;. Esistono ovviamente tutta una serie di mixin analoghi
per gestire allo stesso modo margini, padding e bordi superiori e inferiori.&lt;/p&gt;

&lt;p&gt;E&amp;#39; importante ricordare di utilizzare numeri interi come parametri per
tali mixin, a meno che la somma totale non risulti comunque essere un
intero:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  +adjust-font-size-to(24px, 1.5)
  +padding-leader(0.5, 24px)
  +padding-trailer(1, 24px)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Questo esempio è comunque valido in quanto &lt;code&gt;1.5 + 0.5 + 1 = 3&lt;/code&gt;, che
continua ad essere un numero intero.&lt;/p&gt;

&lt;h2&gt;Ci fermiamo anche oggi&lt;/h2&gt;

&lt;p&gt;Nel prossimo post andremo ad analizzare l&amp;#39;ultima questione fondamentale
per la leggibilità: in base a quali parametri dovremo andare a scegliere
il &lt;code&gt;$base-line-height&lt;/code&gt; e &lt;code&gt;$base-line-height&lt;/code&gt; ottimale per la nostra pagina.&lt;/p&gt;

&lt;h2&gt;Referenze&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://24ways.org/2006/compose-to-a-vertical-rhythm"&gt;Compose to a Vertical Rhythm&lt;/a&gt;, Richard Rutter&lt;/li&gt;
&lt;li&gt;&lt;a href="http://lamb.cc/typograph/"&gt;Scale &amp;amp; Rhythm&lt;/a&gt;, Iain Lamb&lt;/li&gt;
&lt;li&gt;&lt;a href="http://compass-style.org/reference/compass/typography/vertical_rhythm/"&gt;Vertical Rythm Module&lt;/a&gt;, Compass Documentation&lt;/li&gt;
&lt;/ul&gt;
</summary>
    <content type="html">&lt;p&gt;Al termine del post precedente, abbiamo visto come mantenere
tutte le interlinee multiple tra di loro permetta una maggiore
leggibilità del testo. Per essere più chiari, ecco &lt;a href="http://24ways.org/examples/compose-to-a-vertical-rhythm/example.html"&gt;un esempio&lt;/a&gt;
di ciò di cui stiamo parlando.&lt;/p&gt;

&lt;p&gt;Nel mondo web, l&amp;#39;unità base di spazio verticale è rappresentata dalla regola CSS
&lt;code&gt;line-height&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/font-size-line-height.png" alt="Line height"&gt;&lt;/p&gt;

&lt;p&gt;Una &lt;code&gt;line-height&lt;/code&gt; rimane costante nella pagina fino a quando non
intervengono cambi di &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt; o &lt;code&gt;border&lt;/code&gt;.
A questo punto tipicamente il flusso si spezza producendo interlinee
non multiple.&lt;/p&gt;

&lt;p&gt;Riuscire a impostare dimensioni di &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt; e &lt;code&gt;border&lt;/code&gt; tali
da preservare il flusso verticale in tutta la pagina richiede una serie
di calcoli non particolarmente complessi, ma sicuramente poco divertenti.&lt;/p&gt;

&lt;p&gt;Fortunatamente Compass include un modulo poco documentato ma ottimo in grado di
alleviare di molto questo problema.&lt;/p&gt;

&lt;h2&gt;Vertical Rythm con Compass&lt;/h2&gt;

&lt;p&gt;Una volta importato il modulo tramite la direttiva&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
@import &amp;quot;compass/typography/vertical_rhythm&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;siamo pronti a partire specificando il &lt;code&gt;font-size&lt;/code&gt; e la &lt;code&gt;line-height&lt;/code&gt; che abbiamo scelto
per il corpo principale del testo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
$base-line-height: 25px
$base-font-size: 15px

+establish-baseline
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il mixin &lt;code&gt;+establish-baseline&lt;/code&gt; si occupa proprio di impostare questi due valori
sul selettore &lt;code&gt;body&lt;/code&gt; (utilizzando come unità di misura l&amp;#39;&lt;em&gt;em&lt;/em&gt; per
evitare problemi di resize su browser legacy).&lt;/p&gt;

&lt;div class="important" markdown="1"&gt;
Una volta proceduti col seguente setup del modulo, l'unica regola da ricordare
è questa: le proprietà `font-size`, `line-height`, `margin`, `padding` e `border` non vanno mai
toccate manualmente, perchè spezzerebbero il ritmo. Per modificarle è necessario passare
attraverso appositi mixin di Compass.
&lt;/div&gt;

&lt;p&gt;Supponiamo di voler stilizzare il tag &lt;code&gt;h1&lt;/code&gt;, portando il &lt;code&gt;font-size&lt;/code&gt; a 24px,
e aggiungendo del margine in basso. Non possiamo più fare:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  font-size: 24px
  margin-bottom: 15px
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ma dovremo invece scrivere qualcosa come:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  +adjust-font-size-to(24px, 2)
  +padding-trailer(3, 24px)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il primo mixin utilizzato, &lt;code&gt;+adjust-font-size-to&lt;/code&gt;, oltre ad impostare il nuovo &lt;code&gt;font-size&lt;/code&gt;,
modificherà la line-height dell&amp;#39;elemento in modo tale da preservare l&amp;#39;interlinea originaria
di &lt;code&gt;25px&lt;/code&gt;, od un suo multiplo. Il secondo parametro, impostato nell&amp;#39;esempio a 2,
imposterà un line-height effettivo di &lt;code&gt;25px * 2 = 50px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il mixin &lt;code&gt;+padding-trailer&lt;/code&gt;, similmente, imposterà un &lt;code&gt;padding-bottom&lt;/code&gt; in &lt;em&gt;em&lt;/em&gt; pari
esattamente a &lt;code&gt;26px * 3 = 75px&lt;/code&gt;. Esistono ovviamente tutta una serie di mixin analoghi
per gestire allo stesso modo margini, padding e bordi superiori e inferiori.&lt;/p&gt;

&lt;p&gt;E&amp;#39; importante ricordare di utilizzare numeri interi come parametri per
tali mixin, a meno che la somma totale non risulti comunque essere un
intero:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
h1
  +adjust-font-size-to(24px, 1.5)
  +padding-leader(0.5, 24px)
  +padding-trailer(1, 24px)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Questo esempio è comunque valido in quanto &lt;code&gt;1.5 + 0.5 + 1 = 3&lt;/code&gt;, che
continua ad essere un numero intero.&lt;/p&gt;

&lt;h2&gt;Ci fermiamo anche oggi&lt;/h2&gt;

&lt;p&gt;Nel prossimo post andremo ad analizzare l&amp;#39;ultima questione fondamentale
per la leggibilità: in base a quali parametri dovremo andare a scegliere
il &lt;code&gt;$base-line-height&lt;/code&gt; e &lt;code&gt;$base-line-height&lt;/code&gt; ottimale per la nostra pagina.&lt;/p&gt;

&lt;h2&gt;Referenze&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://24ways.org/2006/compose-to-a-vertical-rhythm"&gt;Compose to a Vertical Rhythm&lt;/a&gt;, Richard Rutter&lt;/li&gt;
&lt;li&gt;&lt;a href="http://lamb.cc/typograph/"&gt;Scale &amp;amp; Rhythm&lt;/a&gt;, Iain Lamb&lt;/li&gt;
&lt;li&gt;&lt;a href="http://compass-style.org/reference/compass/typography/vertical_rhythm/"&gt;Vertical Rythm Module&lt;/a&gt;, Compass Documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=JGGyfCpQfKY:bRYUaqOZRrQ:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=JGGyfCpQfKY:bRYUaqOZRrQ:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/aTE66_wT96c" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-2.html</feedburner:origLink></entry>
  <entry>
    <title>Ottimizzare la leggibilità del proprio sito con Compass /1</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/wKpcZfy1Fk0/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-1.html" rel="alternate" />
    <id>/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-1.html</id>
    <published>2012-01-11T00:00:00+01:00</published>
    <updated>2012-01-11T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Nel recente restyling di questo blog mi sono concentrato sul rendere
la lettura di ogni pagina il più piacevole possibile.
Era un campo inesplorato per me, e sono talmente eccitato delle scoperte
fatte e del risultato ottenuto che non posso non riassumervi qualcosa.&lt;/p&gt;

&lt;p&gt;Innanzitutto, non stiamo parlando di tematiche specifiche per il web.
I tipografi si occupano di questa materia da centinaia d&amp;#39;anni, ed è
proprio dalle loro regole d&amp;#39;oro che bisogna partire.&lt;/p&gt;

&lt;p&gt;In questa prima parte inizierò col descrivere le regole teoriche, nel
prossimo post vedremo come applicarle in assoluta semplicità grazie ai
vari mixin di Compass.&lt;/p&gt;

&lt;h2&gt;Non comporre mai una pagina senza una scala tipografica&lt;/h2&gt;

&lt;p&gt;Una delle regole fondamentali è questa: ogni variazione di &lt;code&gt;font-size&lt;/code&gt;
che si desidera introdurre nella pagina deve seguire una scala
predefinita e regolare.&lt;/p&gt;

&lt;p&gt;Non esiste una sola scala possibile; esistono differenti scale celebri
e riconosciute come &amp;quot;naturali e piacevoli alla vista&amp;quot; in campo tipografico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le &lt;a href="http://it.wikipedia.org/wiki/Successione_di_Fibonacci"&gt;successioni di Fibonacci&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Il celebre &lt;a href="http://it.wikipedia.org/wiki/Modulor"&gt;Modulor&lt;/a&gt; di Le Corbusier;&lt;/li&gt;
&lt;li&gt;Il &lt;a href="http://it.wikipedia.org/wiki/Sezione_aurea"&gt;rapporto aureo&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;La storica e tradizionale &lt;a href="http://retinart.net/typography/typographicscale/"&gt;&amp;quot;scala tipografica&amp;quot;&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La scelta di una di queste scale va fatta a seconda del contesto e del
proprio gusto personale. Per quanto mi riguarda, ho deciso di utilizzare
la scala tipografica, con un&amp;#39;unica differenza, ovvero quella di scegliere
un &lt;code&gt;font-size&lt;/code&gt; di 15px (invece che 16px come consigliato) per il contenuto
principale della pagina (il corpo del post). Sul perchè di questa scelta
ci torneremo nel prossimo post.&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/typographicscale.gif" alt="La scala tipografica"&gt;&lt;/p&gt;

&lt;p&gt;Tutti gli altri &lt;code&gt;font-size&lt;/code&gt; seguono la medesima scala: nel mio
caso ho scelto 18px per gli &lt;code&gt;h3&lt;/code&gt;, 21px per gli &lt;code&gt;h2&lt;/code&gt; e gli 24px per gli &lt;code&gt;h1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Mantieni un unico ritmo verticale nella pagina&lt;/h2&gt;

&lt;p&gt;Il concetto astratto di leggibilità si posa in gran parte sul concetto
ben più materiale di &lt;em&gt;interlinea&lt;/em&gt; (in inglese, &lt;em&gt;leading&lt;/em&gt;): è lo spazio
fra la linea di base di una riga e quella della successiva.&lt;/p&gt;

&lt;p&gt;Conservare l&amp;#39;interlinea immutata nel flusso dell&amp;#39;intera pagina aiuta
immensamente a renderla più piacevole e coerente.&lt;/p&gt;

&lt;div class="important"&gt;
Progettare una pagina con ritmo verticale significa scegliere una interlinea di
riferimento e fare in modo che tutte le interlinee presenti nella pagina
presenti siano un suo multiplo.
&lt;/div&gt;

&lt;h3&gt;Piccola digressione sull&amp;#39;&lt;em&gt;em&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;L&amp;#39;&lt;em&gt;em&lt;/em&gt; è una unità di misura relativa: il suo valore in pixel dipende
direttamente dal &lt;code&gt;font-size&lt;/code&gt; assegnato all&amp;#39;elemento stesso.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
body
  font-size: 16px
  line-height: 1.5em
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nell&amp;#39;esempio, l&amp;#39;interlinea varrà &lt;code&gt;16px * 1.5 = 24px&lt;/code&gt;, e mi permetterà
quindi di ottenere uno spazio vuoto tra le righe di testo pari a &lt;code&gt;24px -
16px = 8px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Altra cosa fondamentale da ricordare è che la direttiva &lt;code&gt;line-height&lt;/code&gt; viene
ereditata dagli elementi del DOM sottostanti:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  body
    font-size: 16px
    line-height: 1.5em

    h1
      font-size: 18px
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo caso, il tag &lt;code&gt;h1&lt;/code&gt; avrà anch&amp;#39;esso un &lt;code&gt;line-height&lt;/code&gt; pari a
&lt;code&gt;1.5em&lt;/code&gt;, ma questa si tradurrà in una interlinea pari a &lt;code&gt;18px * 1.5 =
27px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ultima cosa da ricordare riguardo all&amp;#39;&lt;em&gt;em&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
body
  font-size: 16px
  line-height: 1.5em

  h1
    font-size: 1.5em
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se è proprio la direttiva &lt;code&gt;font-size&lt;/code&gt; ad avere una misura espressa in
&lt;em&gt;em&lt;/em&gt;, allora questa sarà una misura relativa al &lt;code&gt;font-size&lt;/code&gt;
dell&amp;#39;elemento padre. Nel nostro caso, il &lt;code&gt;font-size&lt;/code&gt; per l&amp;#39;elemento &lt;code&gt;h1&lt;/code&gt;
sarà di &lt;code&gt;16px * 1.5 = 24px&lt;/code&gt;, con una interlinea pari a &lt;code&gt;24px * 1.5 = 36px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Impostare tutte le grandezze in &lt;em&gt;em&lt;/em&gt; ci permette dunque di far
discendere tutte le proporzioni direttamente da un&amp;#39;unico parametro, il
&lt;code&gt;font-size&lt;/code&gt; che si stabilisce per il &lt;code&gt;body&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Per oggi basta&lt;/h2&gt;

&lt;p&gt;Nel prossimo post vedremo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;come arrivare matematicamente al un valore di interlinea ottimale per
la propria pagina;&lt;/li&gt;
&lt;li&gt;come ruscire ad ottenere un ritmo verticale facendo uso degli &lt;em&gt;em&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;come applicare tutta questa teoria ai nostri fogli di stile Sass con
dei semplici mixin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aspetto i vostri commenti!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Nel recente restyling di questo blog mi sono concentrato sul rendere
la lettura di ogni pagina il più piacevole possibile.
Era un campo inesplorato per me, e sono talmente eccitato delle scoperte
fatte e del risultato ottenuto che non posso non riassumervi qualcosa.&lt;/p&gt;

&lt;p&gt;Innanzitutto, non stiamo parlando di tematiche specifiche per il web.
I tipografi si occupano di questa materia da centinaia d&amp;#39;anni, ed è
proprio dalle loro regole d&amp;#39;oro che bisogna partire.&lt;/p&gt;

&lt;p&gt;In questa prima parte inizierò col descrivere le regole teoriche, nel
prossimo post vedremo come applicarle in assoluta semplicità grazie ai
vari mixin di Compass.&lt;/p&gt;

&lt;h2&gt;Non comporre mai una pagina senza una scala tipografica&lt;/h2&gt;

&lt;p&gt;Una delle regole fondamentali è questa: ogni variazione di &lt;code&gt;font-size&lt;/code&gt;
che si desidera introdurre nella pagina deve seguire una scala
predefinita e regolare.&lt;/p&gt;

&lt;p&gt;Non esiste una sola scala possibile; esistono differenti scale celebri
e riconosciute come &amp;quot;naturali e piacevoli alla vista&amp;quot; in campo tipografico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le &lt;a href="http://it.wikipedia.org/wiki/Successione_di_Fibonacci"&gt;successioni di Fibonacci&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Il celebre &lt;a href="http://it.wikipedia.org/wiki/Modulor"&gt;Modulor&lt;/a&gt; di Le Corbusier;&lt;/li&gt;
&lt;li&gt;Il &lt;a href="http://it.wikipedia.org/wiki/Sezione_aurea"&gt;rapporto aureo&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;La storica e tradizionale &lt;a href="http://retinart.net/typography/typographicscale/"&gt;&amp;quot;scala tipografica&amp;quot;&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La scelta di una di queste scale va fatta a seconda del contesto e del
proprio gusto personale. Per quanto mi riguarda, ho deciso di utilizzare
la scala tipografica, con un&amp;#39;unica differenza, ovvero quella di scegliere
un &lt;code&gt;font-size&lt;/code&gt; di 15px (invece che 16px come consigliato) per il contenuto
principale della pagina (il corpo del post). Sul perchè di questa scelta
ci torneremo nel prossimo post.&lt;/p&gt;

&lt;p&gt;&lt;img src="/data/vertical_rythm/typographicscale.gif" alt="La scala tipografica"&gt;&lt;/p&gt;

&lt;p&gt;Tutti gli altri &lt;code&gt;font-size&lt;/code&gt; seguono la medesima scala: nel mio
caso ho scelto 18px per gli &lt;code&gt;h3&lt;/code&gt;, 21px per gli &lt;code&gt;h2&lt;/code&gt; e gli 24px per gli &lt;code&gt;h1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Mantieni un unico ritmo verticale nella pagina&lt;/h2&gt;

&lt;p&gt;Il concetto astratto di leggibilità si posa in gran parte sul concetto
ben più materiale di &lt;em&gt;interlinea&lt;/em&gt; (in inglese, &lt;em&gt;leading&lt;/em&gt;): è lo spazio
fra la linea di base di una riga e quella della successiva.&lt;/p&gt;

&lt;p&gt;Conservare l&amp;#39;interlinea immutata nel flusso dell&amp;#39;intera pagina aiuta
immensamente a renderla più piacevole e coerente.&lt;/p&gt;

&lt;div class="important"&gt;
Progettare una pagina con ritmo verticale significa scegliere una interlinea di
riferimento e fare in modo che tutte le interlinee presenti nella pagina
presenti siano un suo multiplo.
&lt;/div&gt;

&lt;h3&gt;Piccola digressione sull&amp;#39;&lt;em&gt;em&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;L&amp;#39;&lt;em&gt;em&lt;/em&gt; è una unità di misura relativa: il suo valore in pixel dipende
direttamente dal &lt;code&gt;font-size&lt;/code&gt; assegnato all&amp;#39;elemento stesso.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
body
  font-size: 16px
  line-height: 1.5em
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nell&amp;#39;esempio, l&amp;#39;interlinea varrà &lt;code&gt;16px * 1.5 = 24px&lt;/code&gt;, e mi permetterà
quindi di ottenere uno spazio vuoto tra le righe di testo pari a &lt;code&gt;24px -
16px = 8px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Altra cosa fondamentale da ricordare è che la direttiva &lt;code&gt;line-height&lt;/code&gt; viene
ereditata dagli elementi del DOM sottostanti:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  body
    font-size: 16px
    line-height: 1.5em

    h1
      font-size: 18px
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo caso, il tag &lt;code&gt;h1&lt;/code&gt; avrà anch&amp;#39;esso un &lt;code&gt;line-height&lt;/code&gt; pari a
&lt;code&gt;1.5em&lt;/code&gt;, ma questa si tradurrà in una interlinea pari a &lt;code&gt;18px * 1.5 =
27px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ultima cosa da ricordare riguardo all&amp;#39;&lt;em&gt;em&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
body
  font-size: 16px
  line-height: 1.5em

  h1
    font-size: 1.5em
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se è proprio la direttiva &lt;code&gt;font-size&lt;/code&gt; ad avere una misura espressa in
&lt;em&gt;em&lt;/em&gt;, allora questa sarà una misura relativa al &lt;code&gt;font-size&lt;/code&gt;
dell&amp;#39;elemento padre. Nel nostro caso, il &lt;code&gt;font-size&lt;/code&gt; per l&amp;#39;elemento &lt;code&gt;h1&lt;/code&gt;
sarà di &lt;code&gt;16px * 1.5 = 24px&lt;/code&gt;, con una interlinea pari a &lt;code&gt;24px * 1.5 = 36px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Impostare tutte le grandezze in &lt;em&gt;em&lt;/em&gt; ci permette dunque di far
discendere tutte le proporzioni direttamente da un&amp;#39;unico parametro, il
&lt;code&gt;font-size&lt;/code&gt; che si stabilisce per il &lt;code&gt;body&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Per oggi basta&lt;/h2&gt;

&lt;p&gt;Nel prossimo post vedremo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;come arrivare matematicamente al un valore di interlinea ottimale per
la propria pagina;&lt;/li&gt;
&lt;li&gt;come ruscire ad ottenere un ritmo verticale facendo uso degli &lt;em&gt;em&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;come applicare tutta questa teoria ai nostri fogli di stile Sass con
dei semplici mixin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aspetto i vostri commenti!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=eKMqdh1S4qE:kxs36AGwVRY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=eKMqdh1S4qE:kxs36AGwVRY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/wKpcZfy1Fk0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/ottimizzare-la-leggibilita-del-proprio-sito-con-compass-1.html</feedburner:origLink></entry>
  <entry>
    <title>rspec-given: e se fosse la soluzione?</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/GZqBirl7brA/rspec-given-e-se-fosse-la-soluzione.html" rel="alternate" />
    <id>/blog/2012/01/rspec-given-e-se-fosse-la-soluzione.html</id>
    <published>2012-01-10T00:00:00+01:00</published>
    <updated>2012-01-10T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Tornando a bomba &lt;a href="/blog/2011/12/alternative-a-cucumber.html"&gt;sui miei interrogativi passati&lt;/a&gt;,
potrei aver trovato una soluzione in grado di permettere una maggiore
descrittività ai test -- in stile Gherkin -- ma al tempo stesso &amp;quot;sicura&amp;quot; (ovvero che
non utilizzi librerie TDD ancora in stato alpha).&lt;/p&gt;

&lt;p&gt;La soluzione si chiama &lt;a href="https://github.com/jimweirich/rspec-given"&gt;rspec-given&lt;/a&gt; è non è altro
che una leggera DSL sopra ad RSpec, che permette di utilizzare dei
blocchi &lt;code&gt;Given&lt;/code&gt;, &lt;code&gt;When&lt;/code&gt; e &lt;code&gt;Then&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Esempio d&amp;#39;ordinanza:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
describe Stack do

  def stack_with(initial_contents)
    stack = Stack.new
    initial_contents.each { |item| stack.push(item) }
    stack
  end

  Given(:stack) { stack_with(initial_contents) }

  context &amp;quot;when empty&amp;quot; do
    Given(:initial_contents) { [] }
    Then { stack.depth.should == 0 }

    context &amp;quot;when pushing&amp;quot; do
      When { stack.push(:an_item) }

      Then { stack.depth.should == 1 }
      Then { stack.top.should == :an_item }
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Direi che la cosa migliore è provarlo per trovarne eventuali lati
negativi o brutture. Vi faccio sapere.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Tornando a bomba &lt;a href="/blog/2011/12/alternative-a-cucumber.html"&gt;sui miei interrogativi passati&lt;/a&gt;,
potrei aver trovato una soluzione in grado di permettere una maggiore
descrittività ai test -- in stile Gherkin -- ma al tempo stesso &amp;quot;sicura&amp;quot; (ovvero che
non utilizzi librerie TDD ancora in stato alpha).&lt;/p&gt;

&lt;p&gt;La soluzione si chiama &lt;a href="https://github.com/jimweirich/rspec-given"&gt;rspec-given&lt;/a&gt; è non è altro
che una leggera DSL sopra ad RSpec, che permette di utilizzare dei
blocchi &lt;code&gt;Given&lt;/code&gt;, &lt;code&gt;When&lt;/code&gt; e &lt;code&gt;Then&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Esempio d&amp;#39;ordinanza:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
describe Stack do

  def stack_with(initial_contents)
    stack = Stack.new
    initial_contents.each { |item| stack.push(item) }
    stack
  end

  Given(:stack) { stack_with(initial_contents) }

  context &amp;quot;when empty&amp;quot; do
    Given(:initial_contents) { [] }
    Then { stack.depth.should == 0 }

    context &amp;quot;when pushing&amp;quot; do
      When { stack.push(:an_item) }

      Then { stack.depth.should == 1 }
      Then { stack.top.should == :an_item }
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Direi che la cosa migliore è provarlo per trovarne eventuali lati
negativi o brutture. Vi faccio sapere.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=gWVGpfgOGI4:tIsjCv5cLlw:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=gWVGpfgOGI4:tIsjCv5cLlw:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/GZqBirl7brA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/rspec-given-e-se-fosse-la-soluzione.html</feedburner:origLink></entry>
  <entry>
    <title>TDD è meglio che fare Unit Testing a posteriori</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/3RnASPBvT_s/tdd-meglio-che-fare-unit-testing-a-posteriori.html" rel="alternate" />
    <id>/blog/2012/01/tdd-meglio-che-fare-unit-testing-a-posteriori.html</id>
    <published>2012-01-09T00:00:00+01:00</published>
    <updated>2012-01-09T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Forse non ce n&amp;#39;era il bisogno, ma se vi dovesse servire una ulteriore
prova inconfutabile che scrivere i test &lt;strong&gt;mentre&lt;/strong&gt; stai scrivendo il
codice sia meglio che testare il codice a latere, eccola.&lt;/p&gt;

&lt;p&gt;Questo è il codice del controller che avevo scritto per gestire una autenticazione
OAuth verso Twitter in un precedente progetto:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class OauthController &amp;lt; ApplicationController

  def twitter_oauth
    oauth_consumer = TwitterUser.oauth_consumer
    request_token = oauth_consumer.get_request_token(:oauth_callback =&amp;gt; team_twitter_callback_url(@team))
    session[&amp;#39;request_token&amp;#39;] = request_token.token
    session[&amp;#39;request_secret&amp;#39;] = request_token.secret
    redirect_to request_token.authorize_url
  end

  def twitter_oauth_callback
    oauth_consumer = TwitterUser.oauth_consumer
    request_token = OAuth::RequestToken.new(oauth_consumer, session[&amp;#39;request_token&amp;#39;], session[&amp;#39;request_secret&amp;#39;])
    access_token = request_token.get_access_token(:oauth_verifier =&amp;gt; params[:oauth_verifier])

    client = TwitterUser.client(access_token.token, access_token.secret)
    user = TwitterUser.initialize_with_twitter_data(client.verify_credentials)
    user.team = @team
    user.access_token = access_token.token
    user.access_secret = access_token.secret
    user.save

    flash_message :notice, &amp;quot;A new Twitter account has been successfully added.&amp;quot;
    redirect_to profile_path(@team)
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Codice che funziona, a prima vista. Ma un po&amp;#39; troppo complicato per essere un controller, o no?
E cosa succede se per caso qualcosa va come non dovrebbe? Il codice è pronto a gestire tutti i casi?&lt;/p&gt;

&lt;p&gt;Questo è il medesimo codice, ma riscritto in TDD:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class OauthController &amp;lt; ApplicationController

  def twitter_oauth
    request = TwitterProfile.oauth_authorization_request(twitter_callback_url)
    session[&amp;#39;request_token&amp;#39;] = request.token
    session[&amp;#39;request_secret&amp;#39;] = request.secret
    redirect_to request.authorize_url
  end

  def twitter_oauth_callback
    profile = TwitterProfile.new_from_oauth_authorization_response(session[&amp;#39;request_token&amp;#39;], session[&amp;#39;request_secret&amp;#39;], params[:oauth_verifier])
    profile.user = current_user
    if profile.save
      redirect_to profile_path(profile), notice: &amp;quot;A new Twitter account has been successfully added.&amp;quot;
    else
      redirect_to new_profile_path, alert: &amp;quot;We were no able to add the Twitter account: #{formatted_record_errors(profile)}&amp;quot;
    end
  rescue OAuth::Unauthorized =&amp;gt; e
    redirect_to new_profile_path, alert: &amp;quot;We were no able to add the Twitter account (#{e.message})&amp;quot;
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;La differenza si nota, o sbaglio? Controller più snello, più comprensibile,
modulare, e questo nonostante sia più complesso, dato che gestisce anche possibili errori
durante l&amp;#39;autenticazione che il primo codice ignorava con disprezzo.&lt;/p&gt;

&lt;p&gt;La cosa divertente del TDD è che la forma che prende il codice non viene
decisa a priori, ma emerge in modo naturale durante la scrittura del test.&lt;/p&gt;

&lt;p&gt;L&amp;#39;unica regola da seguire è: &amp;quot;se il test è troppo complicato da
scrivere, spezza e modularizza&amp;quot;. Il resto viene da se&amp;#39;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Forse non ce n&amp;#39;era il bisogno, ma se vi dovesse servire una ulteriore
prova inconfutabile che scrivere i test &lt;strong&gt;mentre&lt;/strong&gt; stai scrivendo il
codice sia meglio che testare il codice a latere, eccola.&lt;/p&gt;

&lt;p&gt;Questo è il codice del controller che avevo scritto per gestire una autenticazione
OAuth verso Twitter in un precedente progetto:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class OauthController &amp;lt; ApplicationController

  def twitter_oauth
    oauth_consumer = TwitterUser.oauth_consumer
    request_token = oauth_consumer.get_request_token(:oauth_callback =&amp;gt; team_twitter_callback_url(@team))
    session[&amp;#39;request_token&amp;#39;] = request_token.token
    session[&amp;#39;request_secret&amp;#39;] = request_token.secret
    redirect_to request_token.authorize_url
  end

  def twitter_oauth_callback
    oauth_consumer = TwitterUser.oauth_consumer
    request_token = OAuth::RequestToken.new(oauth_consumer, session[&amp;#39;request_token&amp;#39;], session[&amp;#39;request_secret&amp;#39;])
    access_token = request_token.get_access_token(:oauth_verifier =&amp;gt; params[:oauth_verifier])

    client = TwitterUser.client(access_token.token, access_token.secret)
    user = TwitterUser.initialize_with_twitter_data(client.verify_credentials)
    user.team = @team
    user.access_token = access_token.token
    user.access_secret = access_token.secret
    user.save

    flash_message :notice, &amp;quot;A new Twitter account has been successfully added.&amp;quot;
    redirect_to profile_path(@team)
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Codice che funziona, a prima vista. Ma un po&amp;#39; troppo complicato per essere un controller, o no?
E cosa succede se per caso qualcosa va come non dovrebbe? Il codice è pronto a gestire tutti i casi?&lt;/p&gt;

&lt;p&gt;Questo è il medesimo codice, ma riscritto in TDD:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class OauthController &amp;lt; ApplicationController

  def twitter_oauth
    request = TwitterProfile.oauth_authorization_request(twitter_callback_url)
    session[&amp;#39;request_token&amp;#39;] = request.token
    session[&amp;#39;request_secret&amp;#39;] = request.secret
    redirect_to request.authorize_url
  end

  def twitter_oauth_callback
    profile = TwitterProfile.new_from_oauth_authorization_response(session[&amp;#39;request_token&amp;#39;], session[&amp;#39;request_secret&amp;#39;], params[:oauth_verifier])
    profile.user = current_user
    if profile.save
      redirect_to profile_path(profile), notice: &amp;quot;A new Twitter account has been successfully added.&amp;quot;
    else
      redirect_to new_profile_path, alert: &amp;quot;We were no able to add the Twitter account: #{formatted_record_errors(profile)}&amp;quot;
    end
  rescue OAuth::Unauthorized =&amp;gt; e
    redirect_to new_profile_path, alert: &amp;quot;We were no able to add the Twitter account (#{e.message})&amp;quot;
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;La differenza si nota, o sbaglio? Controller più snello, più comprensibile,
modulare, e questo nonostante sia più complesso, dato che gestisce anche possibili errori
durante l&amp;#39;autenticazione che il primo codice ignorava con disprezzo.&lt;/p&gt;

&lt;p&gt;La cosa divertente del TDD è che la forma che prende il codice non viene
decisa a priori, ma emerge in modo naturale durante la scrittura del test.&lt;/p&gt;

&lt;p&gt;L&amp;#39;unica regola da seguire è: &amp;quot;se il test è troppo complicato da
scrivere, spezza e modularizza&amp;quot;. Il resto viene da se&amp;#39;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=KSDFlRo_wE0:h_H-pN0fI_Q:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=KSDFlRo_wE0:h_H-pN0fI_Q:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/3RnASPBvT_s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2012/01/tdd-meglio-che-fare-unit-testing-a-posteriori.html</feedburner:origLink></entry>
  <entry>
    <title>Vendorer: un Bundler per mantenere dipendenze esterne</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/LifDQdhiWoM/vendorer-un-bundler-per-mantenere-dipendenze-esterne.html" rel="alternate" />
    <id>/blog/2011/12/vendorer-un-bundler-per-mantenere-dipendenze-esterne.html</id>
    <published>2011-12-16T00:00:00+01:00</published>
    <updated>2011-12-16T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ottima idea (e un altro &lt;code&gt;Somethingfile&lt;/code&gt; che si aggiunge alla project root).&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Ottima idea (e un altro &lt;code&gt;Somethingfile&lt;/code&gt; che si aggiunge alla project root).&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=bJKK42a1cIM:nLctk-bwAb0:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=bJKK42a1cIM:nLctk-bwAb0:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/LifDQdhiWoM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/12/vendorer-un-bundler-per-mantenere-dipendenze-esterne.html</feedburner:origLink></entry>
  <entry>
    <title>Spinach e Turnip: alternative a Cucumber</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/BMmmjjAHiaI/alternative-a-cucumber.html" rel="alternate" />
    <id>/blog/2011/12/alternative-a-cucumber.html</id>
    <published>2011-12-16T00:00:00+01:00</published>
    <updated>2011-12-16T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Negli ultimi tempi sembra esserci stata una presa di coscienza collettiva nel mondo Rails/BDD. Se fino a sei mesi fa eravamo in piena &lt;a href="http://cukes.info"&gt;Cucumber&lt;/a&gt;-mania, la situazione pare essersi quantomeno ribilanciata. Bello Cucumber? No, il contrario. E&amp;#39; terribile, con quella miriade di steps globali ed espressioni regolari da dover domare evitando conflitti e ambiguità.&lt;/p&gt;

&lt;p&gt;Ho avuto modo di provare Cucumber su un solo progetto di media grandezza (71 classi, ~5000 LOC), scontrandomi in prima persona con questi problemi. La gestione ed il mantenimento della suite di test è stata snervante al punto da portarmi ad utilizzare sui progetti successivi soluzioni più &amp;quot;leggere&amp;quot; come &lt;a href="https://github.com/cavalle/steak"&gt;Steak&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eppure l&amp;#39;idea di un &amp;quot;contratto&amp;quot; sempre aggiornato, testato e comprensibile per tutti i soggetti coinvolti nel progetto è incredibilmente allettante. Così come l&amp;#39;idea di poter descrivere le varie feature in maniera astratta, focalizzandosi unicamente sul business value che queste portano. Il linguaggio scelto da Cucumber per questo scopo si chiama &lt;a href="https://github.com/cucumber/gherkin"&gt;Gherkin&lt;/a&gt;, ed il tempo ha confermato tutta la sua validità.&lt;/p&gt;

&lt;p&gt;Dunque eccoci di fronte ad una seconda ondata di gemme, che cercano di raddrizzare il tiro di Cucumber, mantenendo ciò che di buono c&amp;#39;è -- Gherkin appunto -- e migliorando il rimanente.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Negli ultimi tempi sembra esserci stata una presa di coscienza collettiva nel mondo Rails/BDD. Se fino a sei mesi fa eravamo in piena &lt;a href="http://cukes.info"&gt;Cucumber&lt;/a&gt;-mania, la situazione pare essersi quantomeno ribilanciata. Bello Cucumber? No, il contrario. E&amp;#39; terribile, con quella miriade di steps globali ed espressioni regolari da dover domare evitando conflitti e ambiguità.&lt;/p&gt;

&lt;p&gt;Ho avuto modo di provare Cucumber su un solo progetto di media grandezza (71 classi, ~5000 LOC), scontrandomi in prima persona con questi problemi. La gestione ed il mantenimento della suite di test è stata snervante al punto da portarmi ad utilizzare sui progetti successivi soluzioni più &amp;quot;leggere&amp;quot; come &lt;a href="https://github.com/cavalle/steak"&gt;Steak&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eppure l&amp;#39;idea di un &amp;quot;contratto&amp;quot; sempre aggiornato, testato e comprensibile per tutti i soggetti coinvolti nel progetto è incredibilmente allettante. Così come l&amp;#39;idea di poter descrivere le varie feature in maniera astratta, focalizzandosi unicamente sul business value che queste portano. Il linguaggio scelto da Cucumber per questo scopo si chiama &lt;a href="https://github.com/cucumber/gherkin"&gt;Gherkin&lt;/a&gt;, ed il tempo ha confermato tutta la sua validità.&lt;/p&gt;

&lt;p&gt;Dunque eccoci di fronte ad una seconda ondata di gemme, che cercano di raddrizzare il tiro di Cucumber, mantenendo ciò che di buono c&amp;#39;è -- Gherkin appunto -- e migliorando il rimanente.&lt;/p&gt;



&lt;h2&gt;Spinach&lt;/h2&gt;

&lt;p&gt;Ad Ottobre è uscito &lt;a href="http://codegram.github.com/spinach/"&gt;Spinach&lt;/a&gt;, frutto del lavoro della piccola realtà spagnola &lt;a href="http://codegram.com/"&gt;Codegram&lt;/a&gt;. Con Spinach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alla feature Gherkin &lt;code&gt;Feature: Test how spinach works&lt;/code&gt; corrisponde una classe Ruby chiamata &lt;code&gt;TestHowSpinachWorks&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Ad ogni step contenuto nella feature corrisponde un metodo nella classe &lt;code&gt;TestHowSpinachWorks&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;L&amp;#39;implementazione degli step non prevede l&amp;#39;uso di espressioni regolari per matchare più step in un solo colpo: uno step, una definizione;&lt;/li&gt;
&lt;li&gt;E&amp;#39; possibile generare automaticamente lo scheletro di &lt;code&gt;TestHowSpinachWorks&lt;/code&gt; tramite un generatore &lt;code&gt;spinach --generate&lt;/code&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Molto leggero e totalmente privo di ambiguità. Non esistono step globali: se si vuole riutilizzare degli steps, è sufficiente estrapolare i relativi metodi di classe in un apposito modulo Ruby, da includere dove se ne vuole fare uso. Ma sembra evidente che Spinach porti ad un basso riutilizzo degli step definitions, e dunque ad una maggiore astrazione degli step, cosa buona e giusta.&lt;/p&gt;

&lt;h2&gt;Turnip&lt;/h2&gt;

&lt;p&gt;Un mese più tardi, il buon &lt;a href="http://elabs.se/blog"&gt;Jonas Nicklas&lt;/a&gt; -- autore di &lt;a href="https://github.com/jnicklas/carrierwave"&gt;Carrierwave&lt;/a&gt; e &lt;a href="https://github.com/jnicklas/capybara"&gt;Capybara&lt;/a&gt; -- ha ufficializzato la sua proposta: &lt;a href="https://github.com/jnicklas/turnip"&gt;Turnip&lt;/a&gt;. Si tratta di un approccio sicuramente meno drastico:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le espressioni regolari sono state eliminate, ma è possibile comunque &amp;quot;parametrizzare&amp;quot; le step definitions mediante placeholders, con maggiore comprensibilità e minori possibilità di degenero (i.e. &lt;code&gt;step &amp;quot;there is/are :count monster(s)&amp;quot; do |count|&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Gli step globali continuano a poter esistere, ma è possibile anche raggrupparli in &amp;quot;scopes&amp;quot; e richiamarli sono nelle features che li necessitano (attraverso direttive &lt;code&gt;@nome_scope&lt;/code&gt; presenti nella feature Gherkin);&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Cosa ne pensate?&lt;/h2&gt;

&lt;p&gt;Sono sinceramente interessato a questi due nuovi tentativi. Sicuramente la direzione è quella giusta, ma mi domando se anche questi strumenti non nascondano delle problematiche. Siamo sicuri che Spinach non sia troppo rigido, non permettendo di parametrizzare del tutto gli step? D&amp;#39;altra parte, lo stesso Jonas in un suo &lt;a href="http://elabs.se/blog/15-you-re-cuking-it-wrong"&gt;celebre post passato&lt;/a&gt;, recitava come un mantra:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A step description should never contain regexen, CSS or XPath selectors, any kind of code or data structure. It should be easily understood just by reading the description.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ma allora perchè lui stesso ha permesso di parametrizzare gli step descriptions, dando il via a possibili nuovi &lt;a href="https://github.com/ianwhite/pickle"&gt;Pickle&lt;/a&gt;? Forse per permettere ai duri e puri di scrivere alà-Spinach, mantenendo però un ponte col passato?&lt;/p&gt;

&lt;p&gt;Avete già avuto modo di provarli sul campo? Che idea vi siete fatti?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=OvV9GEDaxdI:ftb0GxZjqVU:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=OvV9GEDaxdI:ftb0GxZjqVU:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/BMmmjjAHiaI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/12/alternative-a-cucumber.html</feedburner:origLink></entry>
  <entry>
    <title>Far convivere repositories di WP Plugins con Github</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/XEPaxCP0jts/far-convivere-il-repository-wp-plugins-con-gitub.html" rel="alternate" />
    <id>/blog/2011/12/far-convivere-il-repository-wp-plugins-con-gitub.html</id>
    <published>2011-12-13T00:00:00+01:00</published>
    <updated>2011-12-13T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Senza questa guida probabilmente non ce l&amp;#39;avrei mai fatta. Il processo descritto tra l&amp;#39;altro è generalizzabile e si applica anche fuori dal seminato &amp;quot;WP Plugins&amp;quot; e &amp;quot;Github&amp;quot;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Senza questa guida probabilmente non ce l&amp;#39;avrei mai fatta. Il processo descritto tra l&amp;#39;altro è generalizzabile e si applica anche fuori dal seminato &amp;quot;WP Plugins&amp;quot; e &amp;quot;Github&amp;quot;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=6AfpJovE3w0:h9oiRj7josI:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=6AfpJovE3w0:h9oiRj7josI:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/XEPaxCP0jts" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/12/far-convivere-il-repository-wp-plugins-con-gitub.html</feedburner:origLink></entry>
  <entry>
    <title>Search e replace di file multipli su Vim</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/DG7rmwYdzCw/search-e-replace-di-file-multipli-su-vim.html" rel="alternate" />
    <id>/blog/2011/11/search-e-replace-di-file-multipli-su-vim.html</id>
    <published>2011-11-14T00:00:00+01:00</published>
    <updated>2011-11-14T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Questo era l&amp;#39;ultimo pezzo che mi mancava da Textmate, direi. La sostituzione becera su più file in contemporanea. Io l&amp;#39;ho risolta con &lt;a href="https://github.com/vim-scripts/greplace.vim"&gt;&lt;code&gt;greplace&lt;/code&gt;&lt;/a&gt;, un simpatico plugin.&lt;/p&gt;

&lt;p&gt;Se usi &lt;a href="https://github.com/carlhuda/janus"&gt;Janus&lt;/a&gt; (lo usi, vero?), aggiungi questa riga in &lt;code&gt;~/.janus.rake&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

vim_plugin_task &amp;quot;greplace&amp;quot;, &amp;quot;git://github.com/vim-scripts/greplace.vim.git&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Perfetto, ora lancia un &lt;code&gt;rake&lt;/code&gt; dalla cartella &lt;code&gt;~/.vim&lt;/code&gt; per installare automaticamente il plugin, e aggiungi quest&amp;#39;ultima riga in &lt;code&gt;~/.gvimrc.local&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;quot; Command-Shift-R for greplace
map &amp;lt;D-R&amp;gt; :Gqfopen&amp;lt;CR&amp;gt;:ccl&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Da questo momento, potrai continuare a ricercare utilizzando Ack con lo shortcut &lt;code&gt;Cmd+Shift+F&lt;/code&gt;. Ma se, con il pannello dei risultati ancora aperto, provi a digitare &lt;code&gt;Cmd+Shift+R&lt;/code&gt;, il pannello si trasformerà in un buffer con i medesimi risultati, ma modificabile. Effettua le tue sostituzioni tranquillamente su quel file con i metodi classici. Una volta terminato il lavoro, lancia il comando &lt;code&gt;:Greplace&lt;/code&gt; -- così come suggerito in un commento nel buffer stesso -- e le modifiche effettuate sul buffer si rifletteranno sui vari file.&lt;/p&gt;

&lt;p&gt;Già che ci sono, ti consiglio anche &lt;a href="http://github.com/mutewinter/vim-indent-guides"&gt;&lt;code&gt;indentguides&lt;/code&gt;&lt;/a&gt;, io lo trovo fantastico.&lt;/p&gt;

&lt;p&gt;E voi? Usate qualcosa di particolarmente fico? Fatemi sapere, son
curioso :)&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Questo era l&amp;#39;ultimo pezzo che mi mancava da Textmate, direi. La sostituzione becera su più file in contemporanea. Io l&amp;#39;ho risolta con &lt;a href="https://github.com/vim-scripts/greplace.vim"&gt;&lt;code&gt;greplace&lt;/code&gt;&lt;/a&gt;, un simpatico plugin.&lt;/p&gt;

&lt;p&gt;Se usi &lt;a href="https://github.com/carlhuda/janus"&gt;Janus&lt;/a&gt; (lo usi, vero?), aggiungi questa riga in &lt;code&gt;~/.janus.rake&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

vim_plugin_task &amp;quot;greplace&amp;quot;, &amp;quot;git://github.com/vim-scripts/greplace.vim.git&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Perfetto, ora lancia un &lt;code&gt;rake&lt;/code&gt; dalla cartella &lt;code&gt;~/.vim&lt;/code&gt; per installare automaticamente il plugin, e aggiungi quest&amp;#39;ultima riga in &lt;code&gt;~/.gvimrc.local&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;quot; Command-Shift-R for greplace
map &amp;lt;D-R&amp;gt; :Gqfopen&amp;lt;CR&amp;gt;:ccl&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Da questo momento, potrai continuare a ricercare utilizzando Ack con lo shortcut &lt;code&gt;Cmd+Shift+F&lt;/code&gt;. Ma se, con il pannello dei risultati ancora aperto, provi a digitare &lt;code&gt;Cmd+Shift+R&lt;/code&gt;, il pannello si trasformerà in un buffer con i medesimi risultati, ma modificabile. Effettua le tue sostituzioni tranquillamente su quel file con i metodi classici. Una volta terminato il lavoro, lancia il comando &lt;code&gt;:Greplace&lt;/code&gt; -- così come suggerito in un commento nel buffer stesso -- e le modifiche effettuate sul buffer si rifletteranno sui vari file.&lt;/p&gt;

&lt;p&gt;Già che ci sono, ti consiglio anche &lt;a href="http://github.com/mutewinter/vim-indent-guides"&gt;&lt;code&gt;indentguides&lt;/code&gt;&lt;/a&gt;, io lo trovo fantastico.&lt;/p&gt;

&lt;p&gt;E voi? Usate qualcosa di particolarmente fico? Fatemi sapere, son
curioso :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=DGkAkIkLBPg:yTcHW_4Ml44:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=DGkAkIkLBPg:yTcHW_4Ml44:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/DG7rmwYdzCw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/11/search-e-replace-di-file-multipli-su-vim.html</feedburner:origLink></entry>
  <entry>
    <title>Sul fondamentale problema della stima costi/tempi</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/ZfgLMEG0Pfc/sul-fondamentale-problema-della-stima-costi-tempi.html" rel="alternate" />
    <id>/blog/2011/11/sul-fondamentale-problema-della-stima-costi-tempi.html</id>
    <published>2011-11-05T00:00:00+01:00</published>
    <updated>2011-11-05T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Uno dei maggiori ostacoli che ho personalmente vissuto approdando in un contesto professionale è sicuramente stata la definizione delle specifiche iniziali, e relativa stima di costi e tempi.&lt;/p&gt;

&lt;p&gt;Sembra banale dirlo, ma dal punto di vista pratico lo è meno. La differenza più grande nel passaggio da &amp;quot;amatori&amp;quot; a &amp;quot;professionisti&amp;quot; è proprio quella di essere in grado di garantire al 100% i risultati che vengono promessi. E per risultati non intendo soltanto l&amp;#39;output finale, ma anche le tempistiche e le modalità concordate.&lt;/p&gt;

&lt;p&gt;Non ci si può permettere di andare ad istinto, non ci si può aspettare che tutto vada nel verso giusto, o che ci possa essere  flessibilità altrui nell&amp;#39;accettare eventuali disguidi o intoppi. Al contrario, è fondamentale misurare ogni promessa fatta al cliente valutandola nella casistica peggiore che ci possa venire in mente, e avvisare fin da subito di qualsiasi situazione che possa sfuggire di mano o non dipendente dai propri mezzi/responsabilità.&lt;/p&gt;

&lt;p&gt;La stima dei tempi non è un&amp;#39;attività &amp;quot;naturale&amp;quot; per l&amp;#39;essere umano: è sorprendente vedere come gli sviluppatori durante la fase specifiche sembrano come avvolti da un&amp;#39;aurea di immotivato ottimismo, che non gli fa lontanamente scorgere le miriadi di cose che potrebbero tranquillamente andare per il verso sbagliato (e che magari sono già andate storte in passato).&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Uno dei maggiori ostacoli che ho personalmente vissuto approdando in un contesto professionale è sicuramente stata la definizione delle specifiche iniziali, e relativa stima di costi e tempi.&lt;/p&gt;

&lt;p&gt;Sembra banale dirlo, ma dal punto di vista pratico lo è meno. La differenza più grande nel passaggio da &amp;quot;amatori&amp;quot; a &amp;quot;professionisti&amp;quot; è proprio quella di essere in grado di garantire al 100% i risultati che vengono promessi. E per risultati non intendo soltanto l&amp;#39;output finale, ma anche le tempistiche e le modalità concordate.&lt;/p&gt;

&lt;p&gt;Non ci si può permettere di andare ad istinto, non ci si può aspettare che tutto vada nel verso giusto, o che ci possa essere  flessibilità altrui nell&amp;#39;accettare eventuali disguidi o intoppi. Al contrario, è fondamentale misurare ogni promessa fatta al cliente valutandola nella casistica peggiore che ci possa venire in mente, e avvisare fin da subito di qualsiasi situazione che possa sfuggire di mano o non dipendente dai propri mezzi/responsabilità.&lt;/p&gt;

&lt;p&gt;La stima dei tempi non è un&amp;#39;attività &amp;quot;naturale&amp;quot; per l&amp;#39;essere umano: è sorprendente vedere come gli sviluppatori durante la fase specifiche sembrano come avvolti da un&amp;#39;aurea di immotivato ottimismo, che non gli fa lontanamente scorgere le miriadi di cose che potrebbero tranquillamente andare per il verso sbagliato (e che magari sono già andate storte in passato).&lt;/p&gt;



&lt;p&gt;Tipicamente, l&amp;#39;errore umano di stima cresce esponenzialmente all&amp;#39;aumentare della complessità del task da stimare. E&amp;#39; difficile, se non impossibile, stimare con precisioni superiori alla &amp;quot;giornata/uomo&amp;quot; su task superiori alle 3-4 settimane. E&amp;#39; questa una delle motivazioni alla base di metodi di sviluppo agili, che incentivano uno sviluppo incrementale del progetto attraverso una serie di &amp;quot;sprint&amp;quot; da massimo una-due settimane.&lt;/p&gt;

&lt;p&gt;In lavori su commessa, un cliente particolarmente illuminato potrebbe essere concorde ad uno sviluppo incrementale, ma pretenderà ad ogni modo (comprensibilmente aggiungo) una stima di tempi e costi per il caso peggiore, dunque il problema alla base rimane. Che si fa?&lt;/p&gt;

&lt;p&gt;I consigli che posso dare sono pochi e generici. Sicuramente la stima è più un arte che una scienza perfetta, e come tale necessita di esperienza ed &amp;quot;occhio&amp;quot; per riuscire ad intuire da subito i punti &amp;quot;scricchiolanti&amp;quot; di un progetto. Cercate di suddividere ogni progetto in sotto-task elementari da 2-3 giornate massimo: su quelli il nostro cervello è in grado di stimare con precisione accettabile. Non promettetevi mai una consegna in X giornate -- come a trasformare una stima in qualcosa di scritto su pietra -- ma tenetevi su un range che sia tanto più ampio quanto sia poco conosciuto l&amp;#39;ambiente nel quale dovrete lavorare. Misuratevi sempre, e controllate a fine lavori di quanto avevato sbagliato, cercando di capire i punti che avevate sottovalutato.&lt;/p&gt;

&lt;p&gt;E infine, &lt;a href="http://www.amazon.com/Mythical-Man-Month-Software-Engineering-Anniversary/dp/0201835959"&gt;leggete&lt;/a&gt;, &lt;a href="http://www.amazon.com/Peopleware-Productive-Projects-Teams-Second/dp/0932633439"&gt;leggete&lt;/a&gt;, &lt;a href="http://www.amazon.com/Agile-Estimating-Planning-Mike-Cohn/dp/0131479415"&gt;leggete&lt;/a&gt;. Il primo passo verso delle migliori stime è essere coscienti di quanto possiamo fare schifo, e imparare da chi lo ha fatto da più tempo.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=t7OliMlP49I:yD2prcA9U-w:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=t7OliMlP49I:yD2prcA9U-w:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/ZfgLMEG0Pfc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/11/sul-fondamentale-problema-della-stima-costi-tempi.html</feedburner:origLink></entry>
  <entry>
    <title>Tabs UJS con i data-behaviour</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/L937FQtKf8s/ujs-data-behaviours.html" rel="alternate" />
    <id>/blog/2011/10/ujs-data-behaviours.html</id>
    <published>2011-10-23T00:00:00+02:00</published>
    <updated>2011-10-23T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ho scoperto l&amp;#39;UJS in concomitanza della sua introduzione massiccia su Rails: ne sono rimasto immediatamente affascinato. Un coupling ancora meno stretto tra contenuto e comportamenti dinamici della pagina, utilizzando come strumento di comunicazione gli attributi HTML5 &lt;code&gt;data-*&lt;/code&gt;? Fantastico! :)&lt;/p&gt;

&lt;p&gt;Ho cercato da quel momento di impegnarmi a generare codice JS quanto più unobtrusive possibile; il vantaggio è evidente dopo i primi tentativi: nel tempo, arrivi a comporre una comoda libreria di comportamenti riutilizzabili in altri contesti senza nessun codice JS custom da introdurre.&lt;/p&gt;

&lt;h2&gt;Esempio: una tabbed interface&lt;/h2&gt;

&lt;p&gt;Quante volte vi sarà capitato di dover impostare una vista a tab? Io sono arrivato a questa soluzione, che considero difficilmente battibile, sia per chiarezza che per brevità:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

$ -&amp;gt;

  $tab_scope_contents = {}

  $(&amp;quot;[data-behaviour=tab]&amp;quot;).each -&amp;gt;
    $tab = $(this)
    $tab_content = $($tab.attr(&amp;quot;href&amp;quot;) || $tab.find(&amp;quot;a&amp;quot;).attr(&amp;quot;href&amp;quot;)).hide()
    scope = $tab.data(&amp;quot;tab-scope&amp;quot;)
    $related_tabs = $(&amp;quot;[data-tab-scope=#{scope}]&amp;quot;)
    $tab_scope_contents[scope] = ($tab_scope_contents[scope] || $()).add $tab_content

    $tab.click -&amp;gt;
      $related_tabs.removeClass(&amp;quot;selected-tab&amp;quot;)
      $tab.addClass(&amp;quot;selected-tab&amp;quot;)
      $tab_scope_contents[scope].hide()
      $tab_content.show()
      false

    $related_tabs.eq(0).click()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come si utilizza? Semplice: supponiamo di avere un HTML del genere:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#primo&amp;quot;&amp;gt;Primo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#secondo&amp;quot;&amp;gt;Secondo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#terzo&amp;quot;&amp;gt;Terzo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;div id=&amp;quot;primo&amp;quot;&amp;gt;Primo contenuto!&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;secondo&amp;quot;&amp;gt;Secondo contenuto!&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;terzo&amp;quot;&amp;gt;Terzo contenuto!&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E&amp;#39; sufficiente aggiungere, per ogni tab handle, un attributo &lt;code&gt;data-behaviour=&amp;quot;tab&amp;quot;&lt;/code&gt; per specificare che vogliamo da quel link un comportamento di tipo &amp;quot;tab&amp;quot;, e un attributo &lt;code&gt;data-tab-scope=&amp;quot;nome-dello-scope&amp;quot;&lt;/code&gt; per specificare a quale gruppo di tab appartenga:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#primo&amp;quot;   data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Primo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#secondo&amp;quot; data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Secondo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#terzo&amp;quot;   data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Terzo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Boom. Il gioco è fatto. Ora, ovunque tu voglia tab, basta aggiungere due attributi all&amp;#39;HTML.&lt;/p&gt;

&lt;p&gt;Cosa ne pensate?&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Ho scoperto l&amp;#39;UJS in concomitanza della sua introduzione massiccia su Rails: ne sono rimasto immediatamente affascinato. Un coupling ancora meno stretto tra contenuto e comportamenti dinamici della pagina, utilizzando come strumento di comunicazione gli attributi HTML5 &lt;code&gt;data-*&lt;/code&gt;? Fantastico! :)&lt;/p&gt;

&lt;p&gt;Ho cercato da quel momento di impegnarmi a generare codice JS quanto più unobtrusive possibile; il vantaggio è evidente dopo i primi tentativi: nel tempo, arrivi a comporre una comoda libreria di comportamenti riutilizzabili in altri contesti senza nessun codice JS custom da introdurre.&lt;/p&gt;

&lt;h2&gt;Esempio: una tabbed interface&lt;/h2&gt;

&lt;p&gt;Quante volte vi sarà capitato di dover impostare una vista a tab? Io sono arrivato a questa soluzione, che considero difficilmente battibile, sia per chiarezza che per brevità:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

$ -&amp;gt;

  $tab_scope_contents = {}

  $(&amp;quot;[data-behaviour=tab]&amp;quot;).each -&amp;gt;
    $tab = $(this)
    $tab_content = $($tab.attr(&amp;quot;href&amp;quot;) || $tab.find(&amp;quot;a&amp;quot;).attr(&amp;quot;href&amp;quot;)).hide()
    scope = $tab.data(&amp;quot;tab-scope&amp;quot;)
    $related_tabs = $(&amp;quot;[data-tab-scope=#{scope}]&amp;quot;)
    $tab_scope_contents[scope] = ($tab_scope_contents[scope] || $()).add $tab_content

    $tab.click -&amp;gt;
      $related_tabs.removeClass(&amp;quot;selected-tab&amp;quot;)
      $tab.addClass(&amp;quot;selected-tab&amp;quot;)
      $tab_scope_contents[scope].hide()
      $tab_content.show()
      false

    $related_tabs.eq(0).click()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come si utilizza? Semplice: supponiamo di avere un HTML del genere:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#primo&amp;quot;&amp;gt;Primo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#secondo&amp;quot;&amp;gt;Secondo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#terzo&amp;quot;&amp;gt;Terzo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;div id=&amp;quot;primo&amp;quot;&amp;gt;Primo contenuto!&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;secondo&amp;quot;&amp;gt;Secondo contenuto!&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;terzo&amp;quot;&amp;gt;Terzo contenuto!&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E&amp;#39; sufficiente aggiungere, per ogni tab handle, un attributo &lt;code&gt;data-behaviour=&amp;quot;tab&amp;quot;&lt;/code&gt; per specificare che vogliamo da quel link un comportamento di tipo &amp;quot;tab&amp;quot;, e un attributo &lt;code&gt;data-tab-scope=&amp;quot;nome-dello-scope&amp;quot;&lt;/code&gt; per specificare a quale gruppo di tab appartenga:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#primo&amp;quot;   data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Primo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#secondo&amp;quot; data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Secondo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#terzo&amp;quot;   data-behaviour=&amp;quot;tab&amp;quot; data-tab-scope=&amp;quot;tabset1&amp;quot;&amp;gt;Terzo tab&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Boom. Il gioco è fatto. Ora, ovunque tu voglia tab, basta aggiungere due attributi all&amp;#39;HTML.&lt;/p&gt;

&lt;p&gt;Cosa ne pensate?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=OuPUzks_y8w:0AAvtk4_trk:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=OuPUzks_y8w:0AAvtk4_trk:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/L937FQtKf8s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/10/ujs-data-behaviours.html</feedburner:origLink></entry>
  <entry>
    <title>Linear gradients su IE: Sinatra alla riscossa</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/_PzG0M6z4R0/linear-gradients-ie-ed-il-rapid-prototyping.html" rel="alternate" />
    <id>/blog/2011/10/linear-gradients-ie-ed-il-rapid-prototyping.html</id>
    <published>2011-10-23T00:00:00+02:00</published>
    <updated>2011-10-23T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Con strumenti come Sass e Compass il CSS sta vivendo il suo primo vero rinascimento: la possibilità di astrarre, modularizzare e parametrizzare i fogli di stile ci permette di rendere più stimolante, rapido e mantenibile un lavoro altrimenti devastante a causa dei vari rendering engines da dover gestire.&lt;/p&gt;

&lt;p&gt;E&amp;#39; ancora recente la scelta di Compass di introdurre nel suo core l&amp;#39;ormai fu Lemonade, estensione in grado di generare automaticamente immagini sprites attraverso comodi mixins. Non tutti sono particolarmente favorevoli ad arrivare un simile livello di automazione: non sono uno di questi. Personalmente, condivido ogni strada che possa essere prese per rendere meno tedioso un lavoro banale. :)&lt;/p&gt;

&lt;p&gt;Un altro esempio di tedio, per esempio, arriva dalla direttiva &lt;code&gt;linear-gradient&lt;/code&gt;, non supportata nativamente dall&amp;#39;amato Explorer, se non a mezzo di astrusi filtri di questo tipo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

=ie-linear-gradient($from, $to)
  :filter progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=&amp;#39;#{$from}&amp;#39;, endColorstr=&amp;#39;#{$to}&amp;#39;) // IE6 &amp;amp; IE7
  :-ms-filter quote(progid:DXImageTransform.Microsoft.gradient(startColorstr=&amp;#39;#{$from}&amp;#39;, endColorstr=&amp;#39;#{$to}&amp;#39;)) // IE8 &amp;amp; IE9
  :background-image -ms-linear-gradient(top, #{$from}, #{$to}) // IE10

=cross-browser-linear-gradient($from, $to)
  +ie-linear-gradient($from, $to)
  +background(linear-gradient($from, $to))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Purtroppo la renderizzazione di gradienti realizzati con questa tecnica è orribile, dunque non c&amp;#39;è altro rimedio se non tornare nei mitici &amp;#39;90s, e utilizzare a mo&amp;#39; di gradiente la famosa immagine larga 1xNpx. E qui entra in gioco Sinatra.&lt;/p&gt;

&lt;p&gt;Prima il codice:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

=ie-linear-gradient($from, $to, $height)
  background: mix($from, $to) image-url(&amp;quot;/images/ie_gradients/#{red($from)}-#{green($from)}-#{blue($from)}-#{red($to)}-#{green($to)}-#{blue($to)}-#{$height}.png&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Questo mixin cosa fa? Prende come parametri i due colori limite del gradiente e l&amp;#39;altezza del gradiente stesso, e assume la presenza di una immagine che segua una nomenclatura predefinita, dipendente proprio dai 3 parametri di cui sopra. Imposta inoltre un colore solido di fallback, pari al mix &lt;em&gt;fifty-fifty&lt;/em&gt; tra i due colori.&lt;/p&gt;

&lt;p&gt;Ottimo, ormai siamo a cavallo, è sufficiente chiedere a Sinatra di completare il lavoro:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

get &amp;#39;/images/ie_gradients/:r1-:g1-:b1-:r2-:g2-:b2-:height.png&amp;#39; do |r1, g1, b1, r2, g2, b2, height|

  # il path ricomposto dell&amp;#39;immagine
  path = File.join(File.dirname(__FILE__), &amp;#39;public/images/ie_gradients&amp;#39;,&amp;quot;#{r1}-#{g1}-#{b1}-#{r2}-#{g2}-#{b2}-#{height}.png&amp;quot;)

  unless File.exists? path

    # ricompongo i colori esadecimali
    from = &amp;quot;#%02x%02x%02x&amp;quot; % [r1.to_i, g1.to_i, b1.to_i]
    to = &amp;quot;#%02x%02x%02x&amp;quot; % [r2.to_i, g2.to_i, b2.to_i]

    # assicuriamoci che l&amp;#39;altezza sia un intero
    height = height.to_i

    # ImageMagick, pensaci tu!
    `convert -size 1x#{height} gradient:#{from}-#{to} #{path}`
  end

  # passo l&amp;#39;immagine
  send_file path
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come potete vedere, Sinatra intercetta la richiesta del file, controlla se effettivamente il file esiste, e in caso di test negativo, la genera in automatico utilizzando brutalmente ImageMagick, estrapolando i vari parametri dal nome del file stesso.&lt;/p&gt;

&lt;p&gt;La tecnica è ancora grezza, ma vedete il potenziale? Una volta che i file sono stati generati da Sinatra, magari in fase di prototipazione iniziale del layout -- continua a funzionare anche senza Sinatra.&lt;/p&gt;

&lt;p&gt;Ovviamente, per la massima comodità, si potrebbe trasformare tutto ciò in estensione Compass. Non ne ho mai fatta una, dunque ho evitato di perdere tempo in questa fase. Ma cosa ne pensate?&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Con strumenti come Sass e Compass il CSS sta vivendo il suo primo vero rinascimento: la possibilità di astrarre, modularizzare e parametrizzare i fogli di stile ci permette di rendere più stimolante, rapido e mantenibile un lavoro altrimenti devastante a causa dei vari rendering engines da dover gestire.&lt;/p&gt;

&lt;p&gt;E&amp;#39; ancora recente la scelta di Compass di introdurre nel suo core l&amp;#39;ormai fu Lemonade, estensione in grado di generare automaticamente immagini sprites attraverso comodi mixins. Non tutti sono particolarmente favorevoli ad arrivare un simile livello di automazione: non sono uno di questi. Personalmente, condivido ogni strada che possa essere prese per rendere meno tedioso un lavoro banale. :)&lt;/p&gt;

&lt;p&gt;Un altro esempio di tedio, per esempio, arriva dalla direttiva &lt;code&gt;linear-gradient&lt;/code&gt;, non supportata nativamente dall&amp;#39;amato Explorer, se non a mezzo di astrusi filtri di questo tipo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

=ie-linear-gradient($from, $to)
  :filter progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=&amp;#39;#{$from}&amp;#39;, endColorstr=&amp;#39;#{$to}&amp;#39;) // IE6 &amp;amp; IE7
  :-ms-filter quote(progid:DXImageTransform.Microsoft.gradient(startColorstr=&amp;#39;#{$from}&amp;#39;, endColorstr=&amp;#39;#{$to}&amp;#39;)) // IE8 &amp;amp; IE9
  :background-image -ms-linear-gradient(top, #{$from}, #{$to}) // IE10

=cross-browser-linear-gradient($from, $to)
  +ie-linear-gradient($from, $to)
  +background(linear-gradient($from, $to))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Purtroppo la renderizzazione di gradienti realizzati con questa tecnica è orribile, dunque non c&amp;#39;è altro rimedio se non tornare nei mitici &amp;#39;90s, e utilizzare a mo&amp;#39; di gradiente la famosa immagine larga 1xNpx. E qui entra in gioco Sinatra.&lt;/p&gt;

&lt;p&gt;Prima il codice:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

=ie-linear-gradient($from, $to, $height)
  background: mix($from, $to) image-url(&amp;quot;/images/ie_gradients/#{red($from)}-#{green($from)}-#{blue($from)}-#{red($to)}-#{green($to)}-#{blue($to)}-#{$height}.png&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Questo mixin cosa fa? Prende come parametri i due colori limite del gradiente e l&amp;#39;altezza del gradiente stesso, e assume la presenza di una immagine che segua una nomenclatura predefinita, dipendente proprio dai 3 parametri di cui sopra. Imposta inoltre un colore solido di fallback, pari al mix &lt;em&gt;fifty-fifty&lt;/em&gt; tra i due colori.&lt;/p&gt;

&lt;p&gt;Ottimo, ormai siamo a cavallo, è sufficiente chiedere a Sinatra di completare il lavoro:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

get &amp;#39;/images/ie_gradients/:r1-:g1-:b1-:r2-:g2-:b2-:height.png&amp;#39; do |r1, g1, b1, r2, g2, b2, height|

  # il path ricomposto dell&amp;#39;immagine
  path = File.join(File.dirname(__FILE__), &amp;#39;public/images/ie_gradients&amp;#39;,&amp;quot;#{r1}-#{g1}-#{b1}-#{r2}-#{g2}-#{b2}-#{height}.png&amp;quot;)

  unless File.exists? path

    # ricompongo i colori esadecimali
    from = &amp;quot;#%02x%02x%02x&amp;quot; % [r1.to_i, g1.to_i, b1.to_i]
    to = &amp;quot;#%02x%02x%02x&amp;quot; % [r2.to_i, g2.to_i, b2.to_i]

    # assicuriamoci che l&amp;#39;altezza sia un intero
    height = height.to_i

    # ImageMagick, pensaci tu!
    `convert -size 1x#{height} gradient:#{from}-#{to} #{path}`
  end

  # passo l&amp;#39;immagine
  send_file path
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come potete vedere, Sinatra intercetta la richiesta del file, controlla se effettivamente il file esiste, e in caso di test negativo, la genera in automatico utilizzando brutalmente ImageMagick, estrapolando i vari parametri dal nome del file stesso.&lt;/p&gt;

&lt;p&gt;La tecnica è ancora grezza, ma vedete il potenziale? Una volta che i file sono stati generati da Sinatra, magari in fase di prototipazione iniziale del layout -- continua a funzionare anche senza Sinatra.&lt;/p&gt;

&lt;p&gt;Ovviamente, per la massima comodità, si potrebbe trasformare tutto ciò in estensione Compass. Non ne ho mai fatta una, dunque ho evitato di perdere tempo in questa fase. Ma cosa ne pensate?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=Mgs_xBxz68g:K6GzY4avbzE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=Mgs_xBxz68g:K6GzY4avbzE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/_PzG0M6z4R0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/10/linear-gradients-ie-ed-il-rapid-prototyping.html</feedburner:origLink></entry>
  <entry>
    <title>Posso dire di aver switchato da Textmate a Vim</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/QGD0V7wFm84/posso-dire-di-aver-switchato-da-textmate-a-vim.html" rel="alternate" />
    <id>/blog/2011/10/posso-dire-di-aver-switchato-da-textmate-a-vim.html</id>
    <published>2011-10-10T00:00:00+02:00</published>
    <updated>2011-10-10T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Alla fine ce l&amp;#39;ho fatta. Non posso dire che sia stata semplice, ma è andata. Sono ormai un paio di settimane che sviluppo unicamente con MacVim.&lt;/p&gt;

&lt;p&gt;A dirla tutta, è stato il 4° tentativo. I precedenti 3 erano andati miseramente a vuoto. C&amp;#39;era sempre qualcosa che non mi tornava, che mi faceva ritornare all&amp;#39;ovile. Questa volta, i motivi determinanti il cambio di risultato sono stati:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un&amp;#39;esperienza pregressa accumulata nei precedenti 3 tentativi. Vim, come tutte le cose belle e complesse, va digerito con calma;&lt;/li&gt;
&lt;li&gt;La totale latitanza degli &lt;a href="http://macromates.com/"&gt;autori di Textmate&lt;/a&gt;, per troppo, troppo tempo. Ok lavorare in stealth mode, ok concentrarsi sul prodotto invece che sul marketing, va bene tutto. Ma non pubblicare una bugfix release per Lion dopo 4 mesi mi sembra stupido, eccessivo e poco rispettoso del lavoro e tempo altrui;&lt;/li&gt;
&lt;li&gt;La consapevolezza di switchare verso uno strumento dall&amp;#39;enorme diffusione e supporto, ora e nei secoli a venire, da poter utilizzare a prescindere dal sistema operativo e che ultimamente si sta riscoprendo collettivamente. Per dirla in tema: &lt;em&gt;there&amp;#39;s a plugin for that&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vimcasts.org/"&gt;VimCasts&lt;/a&gt; un ottimo sito di screencast che apre mondi paralleli sull&amp;#39;utilizzo della potenza di Vim nei banali task quotidiani;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/carlhuda/janus"&gt;Janus&lt;/a&gt;, una distro MacVim che pone un rimedio a tutte le principali voglie di un utente Textmate: fuzzy file searching, ricerca di stringhe con Ack, e tutta una serie di piccole premure che rendono più intuitivo e immediato lo switch;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alloy/macvim.git"&gt;Un fork di MacVim con Project Drawer &lt;strong&gt;vero&lt;/strong&gt;&lt;/a&gt;. Personalmente, per mantenere il focus su cosa sto facendo, ho bisogno di una visione ad alto livello dell&amp;#39;alberatura del progetto, sempre visibile. Plugin come NERDTree fanno il loro sporco lavoro di gestione dei file, percarità, ma i font enormi &lt;code&gt;monospaced&lt;/code&gt; poco si adattano al ruolo di &amp;quot;mappa estesa&amp;quot; di un progetto;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ark.asengard.net/blog/"&gt;Un compagno di viaggio&lt;/a&gt; col quale confrontarmi e scambiare opinioni ed entusiasmo nei momenti difficili :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se anche uno solo di questi punti fosse mancato, probabilmente sarei ripiombato nella cupa incertezza di un software morto da anni.&lt;/p&gt;

&lt;p&gt;In questo esatto momento non credo di essere diventato particolarmente più produttivo di prima, ma posso dire con fierezza di non notare alcun rallentamento nel mio flusso di lavoro. Seguiranno a breve (spero), piccoli consigli e note sull&amp;#39;utilizzo di MacVim.&lt;/p&gt;

&lt;p&gt;PS. Per puro caso ho scoperto ora l&amp;#39;&lt;a href="http://blog.macromates.com/2011/whats-next/"&gt;ennesimo post-annuncio sul fantomatico Textmate 2.0&lt;/a&gt;. Per quanto mi riguarda, mi hanno perso per strada, per sempre. Come aspirante entrepreneur, fatico a quantificare l&amp;#39;idiozia delle mosse della Micromates. Erano avanti anni luce da qualsiasi altro editor in circolazione. Sono riusciti nell&amp;#39;impresa di dare il tempo al mondo di raggiungere quello che era lo stato dell&amp;#39;arte dell&amp;#39;editing testuale, e non solo: hanno letteralmente costretto fedeli utilizzatori a cercarsi alternative più sicure e mantenute. &lt;strong&gt;What a waste.&lt;/strong&gt;&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Alla fine ce l&amp;#39;ho fatta. Non posso dire che sia stata semplice, ma è andata. Sono ormai un paio di settimane che sviluppo unicamente con MacVim.&lt;/p&gt;

&lt;p&gt;A dirla tutta, è stato il 4° tentativo. I precedenti 3 erano andati miseramente a vuoto. C&amp;#39;era sempre qualcosa che non mi tornava, che mi faceva ritornare all&amp;#39;ovile. Questa volta, i motivi determinanti il cambio di risultato sono stati:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un&amp;#39;esperienza pregressa accumulata nei precedenti 3 tentativi. Vim, come tutte le cose belle e complesse, va digerito con calma;&lt;/li&gt;
&lt;li&gt;La totale latitanza degli &lt;a href="http://macromates.com/"&gt;autori di Textmate&lt;/a&gt;, per troppo, troppo tempo. Ok lavorare in stealth mode, ok concentrarsi sul prodotto invece che sul marketing, va bene tutto. Ma non pubblicare una bugfix release per Lion dopo 4 mesi mi sembra stupido, eccessivo e poco rispettoso del lavoro e tempo altrui;&lt;/li&gt;
&lt;li&gt;La consapevolezza di switchare verso uno strumento dall&amp;#39;enorme diffusione e supporto, ora e nei secoli a venire, da poter utilizzare a prescindere dal sistema operativo e che ultimamente si sta riscoprendo collettivamente. Per dirla in tema: &lt;em&gt;there&amp;#39;s a plugin for that&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vimcasts.org/"&gt;VimCasts&lt;/a&gt; un ottimo sito di screencast che apre mondi paralleli sull&amp;#39;utilizzo della potenza di Vim nei banali task quotidiani;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/carlhuda/janus"&gt;Janus&lt;/a&gt;, una distro MacVim che pone un rimedio a tutte le principali voglie di un utente Textmate: fuzzy file searching, ricerca di stringhe con Ack, e tutta una serie di piccole premure che rendono più intuitivo e immediato lo switch;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alloy/macvim.git"&gt;Un fork di MacVim con Project Drawer &lt;strong&gt;vero&lt;/strong&gt;&lt;/a&gt;. Personalmente, per mantenere il focus su cosa sto facendo, ho bisogno di una visione ad alto livello dell&amp;#39;alberatura del progetto, sempre visibile. Plugin come NERDTree fanno il loro sporco lavoro di gestione dei file, percarità, ma i font enormi &lt;code&gt;monospaced&lt;/code&gt; poco si adattano al ruolo di &amp;quot;mappa estesa&amp;quot; di un progetto;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ark.asengard.net/blog/"&gt;Un compagno di viaggio&lt;/a&gt; col quale confrontarmi e scambiare opinioni ed entusiasmo nei momenti difficili :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se anche uno solo di questi punti fosse mancato, probabilmente sarei ripiombato nella cupa incertezza di un software morto da anni.&lt;/p&gt;

&lt;p&gt;In questo esatto momento non credo di essere diventato particolarmente più produttivo di prima, ma posso dire con fierezza di non notare alcun rallentamento nel mio flusso di lavoro. Seguiranno a breve (spero), piccoli consigli e note sull&amp;#39;utilizzo di MacVim.&lt;/p&gt;

&lt;p&gt;PS. Per puro caso ho scoperto ora l&amp;#39;&lt;a href="http://blog.macromates.com/2011/whats-next/"&gt;ennesimo post-annuncio sul fantomatico Textmate 2.0&lt;/a&gt;. Per quanto mi riguarda, mi hanno perso per strada, per sempre. Come aspirante entrepreneur, fatico a quantificare l&amp;#39;idiozia delle mosse della Micromates. Erano avanti anni luce da qualsiasi altro editor in circolazione. Sono riusciti nell&amp;#39;impresa di dare il tempo al mondo di raggiungere quello che era lo stato dell&amp;#39;arte dell&amp;#39;editing testuale, e non solo: hanno letteralmente costretto fedeli utilizzatori a cercarsi alternative più sicure e mantenute. &lt;strong&gt;What a waste.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=OTtyKHzu7xw:Fvc4bPJnToQ:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=OTtyKHzu7xw:Fvc4bPJnToQ:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/QGD0V7wFm84" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/10/posso-dire-di-aver-switchato-da-textmate-a-vim.html</feedburner:origLink></entry>
  <entry>
    <title>Un task Capistrano per backuppare il DB di produzione</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/4c23TrKxhVA/un-task-capistrano-per-backuppare-il-db-di-produzione.html" rel="alternate" />
    <id>/blog/2011/09/un-task-capistrano-per-backuppare-il-db-di-produzione.html</id>
    <published>2011-09-13T00:00:00+02:00</published>
    <updated>2011-09-13T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Recentemente, per riprodurre un bug che si presentava solo in produzione, ho pensato bene di creare un piccolo task Capistrano per copiare in locale l&amp;#39;intero contenuto del DB remoto. Il task funziona solo se il DB di produzione è MySQL. Troverete il risultato nella cartella &lt;code&gt;backups&lt;/code&gt; del vostro progetto Rails. Eccolo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


require &amp;#39;yaml&amp;#39;

desc &amp;quot;Backup the remote production database&amp;quot;
task :backup, :roles =&amp;gt; :db, :only =&amp;gt; { :primary =&amp;gt; true } do
  filename = &amp;quot;#{application}.dump.#{Time.now.to_i}.sql.bz2&amp;quot;
  file = &amp;quot;/tmp/#{filename}&amp;quot;
  db = YAML::load(ERB.new(IO.read(File.join(File.dirname(__FILE__), &amp;#39;database.yml&amp;#39;))).result)[&amp;#39;production&amp;#39;]
  run &amp;quot;mysqldump -u #{db[&amp;#39;username&amp;#39;]} --password=#{db[&amp;#39;password&amp;#39;]} #{db[&amp;#39;database&amp;#39;]} | bzip2 -c &amp;gt; #{file}&amp;quot;  do |ch, stream, data|
    puts data
  end
  `mkdir -p #{File.dirname(__FILE__)}/../backups/`
  get file, &amp;quot;backups/#{filename}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
</summary>
    <content type="html">&lt;p&gt;Recentemente, per riprodurre un bug che si presentava solo in produzione, ho pensato bene di creare un piccolo task Capistrano per copiare in locale l&amp;#39;intero contenuto del DB remoto. Il task funziona solo se il DB di produzione è MySQL. Troverete il risultato nella cartella &lt;code&gt;backups&lt;/code&gt; del vostro progetto Rails. Eccolo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


require &amp;#39;yaml&amp;#39;

desc &amp;quot;Backup the remote production database&amp;quot;
task :backup, :roles =&amp;gt; :db, :only =&amp;gt; { :primary =&amp;gt; true } do
  filename = &amp;quot;#{application}.dump.#{Time.now.to_i}.sql.bz2&amp;quot;
  file = &amp;quot;/tmp/#{filename}&amp;quot;
  db = YAML::load(ERB.new(IO.read(File.join(File.dirname(__FILE__), &amp;#39;database.yml&amp;#39;))).result)[&amp;#39;production&amp;#39;]
  run &amp;quot;mysqldump -u #{db[&amp;#39;username&amp;#39;]} --password=#{db[&amp;#39;password&amp;#39;]} #{db[&amp;#39;database&amp;#39;]} | bzip2 -c &amp;gt; #{file}&amp;quot;  do |ch, stream, data|
    puts data
  end
  `mkdir -p #{File.dirname(__FILE__)}/../backups/`
  get file, &amp;quot;backups/#{filename}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=r6s4vafPcOA:hSEihJyKdQY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=r6s4vafPcOA:hSEihJyKdQY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/4c23TrKxhVA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/09/un-task-capistrano-per-backuppare-il-db-di-produzione.html</feedburner:origLink></entry>
  <entry>
    <title>Il costo del cambiamento nel mondo Agile</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/ahyTQrrcTbM/il-costo-del-cambiamento-nel-mondo-agile.html" rel="alternate" />
    <id>/blog/2011/08/il-costo-del-cambiamento-nel-mondo-agile.html</id>
    <published>2011-08-28T00:00:00+02:00</published>
    <updated>2011-08-28T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Una delle assunzioni universali dell’ingegneria del software è quella che vede il costo necessario per modificare un programma crescere esponenzialmente nel tempo. Chi di noi non ha mai visto qualcosa di simile su un manualone di programmazione?&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dl.dropbox.com/u/8835321/stefanoverna.com/9u.png" alt="Il costo esponenziale del cambiamento" title="Il costo esponenziale del cambiamento"&gt;&lt;/p&gt;

&lt;p&gt;Ovviamente, un grafico del genere ci spinge a premunirci. Per evitare l’insorgere di costi inimmaginabili nel futuro, si tenta di prevedere ogni problematica con il più largo anticipo, stimando ogni futura evoluzione del progetto già dall’inizio dei tempi. Questo in effetti è ciò che tutti noi — ad un certo punto della nostra crescita professionale — abbiamo imparato volenti o nolenti a fare, pur sapendo quanto a tratti sia impossibile.&lt;/p&gt;

&lt;p&gt;Durante gli ultimi decenni, enormi sforzi sono stati compiuti nella costruzione di nuovi linguaggi, paradigmi, strumenti e metodologie di lavoro in grado di alleviare il costo delle modifiche in corso d’opera.
La lettura del libro “Extreme Programming Explained” di Kent Beck è stata illuminante. Si parte da una domanda: ma se per caso tutti questi sforzi avessero veramente portato a qualcosa? Se per caso, applicando i risultati ottenuti in questo senso, il costo di una modifica ad un software non crescesse in modo esponenziale nel tempo, ma seguisse una curva molto più lenta, tendente quasi ad un asintoto?&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dl.dropbox.com/u/8835321/stefanoverna.com/9v.png" alt="Il costo asintotico del cambiamento" title="Il costo asintotico del cambiamento"&gt;&lt;/p&gt;

&lt;p&gt;È questa la premessa tecnica fondamentale dell’Extreme Programming (e di tutte le metodologie agili in generale): se i costi crescessero in questo modo, lo sviluppo SW procederebbe con modalità totalmente differenti.&lt;/p&gt;

&lt;p&gt;Le “grandi decisioni” verrebbero prese quanto più tardi possibile, per posticiparne il costo e per attendere di avere in mano il massimo quantitativo recuperabile di informazioni a convalidare l&amp;#39;investimento. Si implementerebbe solo il minimo indispensabile, e si introdurrebbero nuovi elementi al design complessivo solo quando questi semplificassero il codice già esistente, o rendessero il prossimo pezzo di codice più semplice da scrivere.&lt;/p&gt;

&lt;p&gt;Non si spenderebbero più giornate o settimane a decidere in fase di specifica iniziale se investire o meno in una componente “rischiosa”, solo per la paura che eseguire la medesima modifica in un secondo tempo possa essere enormemente più costoso; si implementerebbe solo ciò che si rivela utile all’oggi, con la fiducia e la tranquillità di sapere che un domani sarà possibile estendere l&amp;#39;esistente senza aumenti di prezzo degni di preoccupazione.&lt;/p&gt;

&lt;p&gt;Con questo cambiamento radicale di prospettiva ci sono le basi per impostare un processo di sviluppo del software nel quale — invece che attuare le grandi decisioni al “tempo zero” e limitarsi a piccole virate in seguito — siamo in grado, in ogni momento, di prendere decisioni strategiche e vederle realizzate tempi rapidi.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Una delle assunzioni universali dell’ingegneria del software è quella che vede il costo necessario per modificare un programma crescere esponenzialmente nel tempo. Chi di noi non ha mai visto qualcosa di simile su un manualone di programmazione?&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dl.dropbox.com/u/8835321/stefanoverna.com/9u.png" alt="Il costo esponenziale del cambiamento" title="Il costo esponenziale del cambiamento"&gt;&lt;/p&gt;

&lt;p&gt;Ovviamente, un grafico del genere ci spinge a premunirci. Per evitare l’insorgere di costi inimmaginabili nel futuro, si tenta di prevedere ogni problematica con il più largo anticipo, stimando ogni futura evoluzione del progetto già dall’inizio dei tempi. Questo in effetti è ciò che tutti noi — ad un certo punto della nostra crescita professionale — abbiamo imparato volenti o nolenti a fare, pur sapendo quanto a tratti sia impossibile.&lt;/p&gt;

&lt;p&gt;Durante gli ultimi decenni, enormi sforzi sono stati compiuti nella costruzione di nuovi linguaggi, paradigmi, strumenti e metodologie di lavoro in grado di alleviare il costo delle modifiche in corso d’opera.
La lettura del libro “Extreme Programming Explained” di Kent Beck è stata illuminante. Si parte da una domanda: ma se per caso tutti questi sforzi avessero veramente portato a qualcosa? Se per caso, applicando i risultati ottenuti in questo senso, il costo di una modifica ad un software non crescesse in modo esponenziale nel tempo, ma seguisse una curva molto più lenta, tendente quasi ad un asintoto?&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dl.dropbox.com/u/8835321/stefanoverna.com/9v.png" alt="Il costo asintotico del cambiamento" title="Il costo asintotico del cambiamento"&gt;&lt;/p&gt;

&lt;p&gt;È questa la premessa tecnica fondamentale dell’Extreme Programming (e di tutte le metodologie agili in generale): se i costi crescessero in questo modo, lo sviluppo SW procederebbe con modalità totalmente differenti.&lt;/p&gt;

&lt;p&gt;Le “grandi decisioni” verrebbero prese quanto più tardi possibile, per posticiparne il costo e per attendere di avere in mano il massimo quantitativo recuperabile di informazioni a convalidare l&amp;#39;investimento. Si implementerebbe solo il minimo indispensabile, e si introdurrebbero nuovi elementi al design complessivo solo quando questi semplificassero il codice già esistente, o rendessero il prossimo pezzo di codice più semplice da scrivere.&lt;/p&gt;

&lt;p&gt;Non si spenderebbero più giornate o settimane a decidere in fase di specifica iniziale se investire o meno in una componente “rischiosa”, solo per la paura che eseguire la medesima modifica in un secondo tempo possa essere enormemente più costoso; si implementerebbe solo ciò che si rivela utile all’oggi, con la fiducia e la tranquillità di sapere che un domani sarà possibile estendere l&amp;#39;esistente senza aumenti di prezzo degni di preoccupazione.&lt;/p&gt;

&lt;p&gt;Con questo cambiamento radicale di prospettiva ci sono le basi per impostare un processo di sviluppo del software nel quale — invece che attuare le grandi decisioni al “tempo zero” e limitarsi a piccole virate in seguito — siamo in grado, in ogni momento, di prendere decisioni strategiche e vederle realizzate tempi rapidi.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=yB-dVojNvb8:uTt3EJ9oqBE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=yB-dVojNvb8:uTt3EJ9oqBE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/ahyTQrrcTbM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/08/il-costo-del-cambiamento-nel-mondo-agile.html</feedburner:origLink></entry>
  <entry>
    <title>Buone vacanze!</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/rCGoH2PzCDU/buone-vacanze-.html" rel="alternate" />
    <id>/blog/2011/07/buone-vacanze-.html</id>
    <published>2011-07-27T00:00:00+02:00</published>
    <updated>2011-07-27T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Il vostro fedelissimo a partire da giovedì si ritira per un paio di settimane di riposo in famiglia. E&amp;#39; stato un anno impegnativo e pieno di sfide, sotto tutti i punti di vista. Sono felice di poter dire, come si suol dire, di aver tenuto botta a un cambio di città e al primo anno di vita di weLaika. Ora tutta la stanchezza accumulata sta iniziando a farsi sentire sul serio. Ci si risente da metà agosto circa, fate i bravi!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Il vostro fedelissimo a partire da giovedì si ritira per un paio di settimane di riposo in famiglia. E&amp;#39; stato un anno impegnativo e pieno di sfide, sotto tutti i punti di vista. Sono felice di poter dire, come si suol dire, di aver tenuto botta a un cambio di città e al primo anno di vita di weLaika. Ora tutta la stanchezza accumulata sta iniziando a farsi sentire sul serio. Ci si risente da metà agosto circa, fate i bravi!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=dmk2I6Z3Lxw:6gQhtRjyHog:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=dmk2I6Z3Lxw:6gQhtRjyHog:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/rCGoH2PzCDU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/07/buone-vacanze-.html</feedburner:origLink></entry>
  <entry>
    <title>I più bei bottoni CSS3 del mondo</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/mdbSBr72nwY/i-pi-bei-bottoni-css3-del-mondo.html" rel="alternate" />
    <id>/blog/2011/07/i-pi-bei-bottoni-css3-del-mondo.html</id>
    <published>2011-07-26T00:00:00+02:00</published>
    <updated>2011-07-26T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Chad Mazzola è riuscito a creare un set di bottoni CSS3 incredibilmente lickable, prendendo spunto dagli stili più celebri presenti nella rete. Da quando conosco questo set, ho smesso di cercare altrove.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Chad Mazzola è riuscito a creare un set di bottoni CSS3 incredibilmente lickable, prendendo spunto dagli stili più celebri presenti nella rete. Da quando conosco questo set, ho smesso di cercare altrove.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=Gv7hIJgEj-g:AwIdq5YLVFk:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=Gv7hIJgEj-g:AwIdq5YLVFk:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/mdbSBr72nwY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/07/i-pi-bei-bottoni-css3-del-mondo.html</feedburner:origLink></entry>
  <entry>
    <title>Buffer è "ramen profitable"</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/Ie-FNEBZBGY/ramen-profitable.html" rel="alternate" />
    <id>/blog/2011/07/ramen-profitable.html</id>
    <published>2011-07-07T00:00:00+02:00</published>
    <updated>2011-07-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;&lt;a href="http://joel.is/"&gt;Joel Gascoigne&lt;/a&gt; su Buffer:&lt;/p&gt;

&lt;p&gt;“Ramen profitable” is a term which is used a lot in the startup scene, and one you should get acquainted with if you haven’t already. I think Paul Graham does a good job of &lt;a href="http://www.paulgraham.com/fundraising.html"&gt;describing it&lt;/a&gt;, and he may have even invented the phrase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At YC we use the phrase “ramen profitable” to describe the situation where you’re making just enough to pay your living expenses. Once you cross into ramen profitable, everything changes. You may still need investment to make it big, but you don’t need it this month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We reached ramen profitability a couple of months ago and I can’t emphasise enough the impact it can have. I gradually dropped the number of days of contract development work I was doing as the revenue grew, and now I get to spend all my days on Buffer. We certainly have many new challenges ahead of us, but it is a very nice place to be.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;a href="http://joel.is/"&gt;Joel Gascoigne&lt;/a&gt; su Buffer:&lt;/p&gt;

&lt;p&gt;“Ramen profitable” is a term which is used a lot in the startup scene, and one you should get acquainted with if you haven’t already. I think Paul Graham does a good job of &lt;a href="http://www.paulgraham.com/fundraising.html"&gt;describing it&lt;/a&gt;, and he may have even invented the phrase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At YC we use the phrase “ramen profitable” to describe the situation where you’re making just enough to pay your living expenses. Once you cross into ramen profitable, everything changes. You may still need investment to make it big, but you don’t need it this month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We reached ramen profitability a couple of months ago and I can’t emphasise enough the impact it can have. I gradually dropped the number of days of contract development work I was doing as the revenue grew, and now I get to spend all my days on Buffer. We certainly have many new challenges ahead of us, but it is a very nice place to be.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=ThTa2Tpw55w:lOKDKoD9gEI:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=ThTa2Tpw55w:lOKDKoD9gEI:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/Ie-FNEBZBGY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/07/ramen-profitable.html</feedburner:origLink></entry>
  <entry>
    <title>Unique Value Proposition</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/ezNpACIJkpQ/unique-value-proposition.html" rel="alternate" />
    <id>/blog/2011/07/unique-value-proposition.html</id>
    <published>2011-07-06T00:00:00+02:00</published>
    <updated>2011-07-06T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;The UVP is hard to get right because you have to distill the essence of your product in a few words that can fit in the headline of your landing page. Additionally, your UVP also needs to be different and that difference needs to matter. First-time visitors spend 8 seconds on average on a landing page. Your UVP is their first interaction with your product - craft a good UVP and they might stay and view the rest of your site. Otherwise, they’ll simply leave.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ash Maurya, dal suo libro &lt;em&gt;Running Lean&lt;/em&gt;. Consigliato.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The UVP is hard to get right because you have to distill the essence of your product in a few words that can fit in the headline of your landing page. Additionally, your UVP also needs to be different and that difference needs to matter. First-time visitors spend 8 seconds on average on a landing page. Your UVP is their first interaction with your product - craft a good UVP and they might stay and view the rest of your site. Otherwise, they’ll simply leave.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ash Maurya, dal suo libro &lt;em&gt;Running Lean&lt;/em&gt;. Consigliato.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=CBrVf9I2vPY:uERWjUh1tvI:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=CBrVf9I2vPY:uERWjUh1tvI:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/ezNpACIJkpQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/07/unique-value-proposition.html</feedburner:origLink></entry>
  <entry>
    <title>"Coming soon" pages? Evitale.</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/_UNtgLvdg7w/-coming-soon-pages-evitale-.html" rel="alternate" />
    <id>/blog/2011/07/-coming-soon-pages-evitale-.html</id>
    <published>2011-07-04T00:00:00+02:00</published>
    <updated>2011-07-04T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;By skipping the “coming soon” page, you can really focus on what matters. Instead of a “coming soon” page, put up a landing page for your product. Make it look like the product exists, and then when people try and sign up, show them a page letting them know that you’re not quite ready for them yet. The effort is the same, but this tiny change can give you massive rewards. By skipping the “coming soon” page, you gain validated learning about the emails you collect: they are people who thought your product existed and showed a real interest by trying to sign up. If you have people hitting the page and no one gives you their email, you know there’s a problem with your idea or the way you’re describing it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dal blog di Joel Gascoigne, un signore che negli ultimi tempi si può dire possa essere felice.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;By skipping the “coming soon” page, you can really focus on what matters. Instead of a “coming soon” page, put up a landing page for your product. Make it look like the product exists, and then when people try and sign up, show them a page letting them know that you’re not quite ready for them yet. The effort is the same, but this tiny change can give you massive rewards. By skipping the “coming soon” page, you gain validated learning about the emails you collect: they are people who thought your product existed and showed a real interest by trying to sign up. If you have people hitting the page and no one gives you their email, you know there’s a problem with your idea or the way you’re describing it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dal blog di Joel Gascoigne, un signore che negli ultimi tempi si può dire possa essere felice.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=MrBazdZ-7Gg:DFvA88jIpDE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=MrBazdZ-7Gg:DFvA88jIpDE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/_UNtgLvdg7w" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/07/-coming-soon-pages-evitale-.html</feedburner:origLink></entry>
  <entry>
    <title>Why sharing knowledge is vital for success</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/WOwVRN_es2E/why-sharing-knowledge-is-vital-for-success.html" rel="alternate" />
    <id>/blog/2011/06/why-sharing-knowledge-is-vital-for-success.html</id>
    <published>2011-06-29T00:00:00+02:00</published>
    <updated>2011-06-29T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Sharing knowledge is one of the best things you can do to become a great team player and ultimately a leader. It is essential not only for the success of people around you, but for your own success in the first place. The more you share, the more people respect you. The more respected you feel, the more you’re willing to share. The more you give away, the more new opportunities come your way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In weLaika siamo alla continua di strumenti che ci permettano di migliorarci. Abbiamo ancora un sacco da imparare, e proprio per questo motivo diventa vitale riuscire a trovare un modo per condividere nella maniera più rapida e costruttiva possibile i propri esperimenti, pensieri e scoperte con il resto del gruppo.&lt;/p&gt;

&lt;p&gt;Non trovando nulla che ci abbia soddisfatto, stiamo lavorando da diversi mesi all&amp;#39;idea di uno strumento online che permetta di semplificare questo processo di comunicazione ed apprendimento. Nei prossimi post continuerò a stuzzicarvi sulla questione, siete già avvisati.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Sharing knowledge is one of the best things you can do to become a great team player and ultimately a leader. It is essential not only for the success of people around you, but for your own success in the first place. The more you share, the more people respect you. The more respected you feel, the more you’re willing to share. The more you give away, the more new opportunities come your way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In weLaika siamo alla continua di strumenti che ci permettano di migliorarci. Abbiamo ancora un sacco da imparare, e proprio per questo motivo diventa vitale riuscire a trovare un modo per condividere nella maniera più rapida e costruttiva possibile i propri esperimenti, pensieri e scoperte con il resto del gruppo.&lt;/p&gt;

&lt;p&gt;Non trovando nulla che ci abbia soddisfatto, stiamo lavorando da diversi mesi all&amp;#39;idea di uno strumento online che permetta di semplificare questo processo di comunicazione ed apprendimento. Nei prossimi post continuerò a stuzzicarvi sulla questione, siete già avvisati.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=KnC8WXYRVm8:VRAa-Oek80o:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=KnC8WXYRVm8:VRAa-Oek80o:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/WOwVRN_es2E" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/why-sharing-knowledge-is-vital-for-success.html</feedburner:origLink></entry>
  <entry>
    <title>From idea to paying costumers in 7 weeks</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/BzHGMUo6uW4/from-idea-to-paying-costumers-in-7-weeks.html" rel="alternate" />
    <id>/blog/2011/06/from-idea-to-paying-costumers-in-7-weeks.html</id>
    <published>2011-06-27T00:00:00+02:00</published>
    <updated>2011-06-27T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;E&amp;#39; la prima volta che ne parlo in questo blog, ma è un topic che sto seguendo ormai da alcuni mesi con attenzione ed interesse: il &lt;em&gt;minimum viable product&lt;/em&gt;. E&amp;#39; un concetto sviluppato ormai tre anni fa da Eric Ries in un più ampio contesto, quello denominato del &lt;em&gt;Lean Thinking&lt;/em&gt;, riassumibile nello sviluppare sempre il &lt;strong&gt;minimo&lt;/strong&gt; indispensabile per ottenere nuove interessanti informazioni dai propri clienti e visitatori, in modo da guidare i successivi sviluppi sulla base dei feedback registrati.&lt;/p&gt;

&lt;p&gt;Questo articolo descrive con chiarezza gli step iniziali svolti per lanciare in poco tempo Buffer, una webapp per schedulare i propri post Twitter nel tempo, partendo da una semplice pagina statica iniziale.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;E&amp;#39; la prima volta che ne parlo in questo blog, ma è un topic che sto seguendo ormai da alcuni mesi con attenzione ed interesse: il &lt;em&gt;minimum viable product&lt;/em&gt;. E&amp;#39; un concetto sviluppato ormai tre anni fa da Eric Ries in un più ampio contesto, quello denominato del &lt;em&gt;Lean Thinking&lt;/em&gt;, riassumibile nello sviluppare sempre il &lt;strong&gt;minimo&lt;/strong&gt; indispensabile per ottenere nuove interessanti informazioni dai propri clienti e visitatori, in modo da guidare i successivi sviluppi sulla base dei feedback registrati.&lt;/p&gt;

&lt;p&gt;Questo articolo descrive con chiarezza gli step iniziali svolti per lanciare in poco tempo Buffer, una webapp per schedulare i propri post Twitter nel tempo, partendo da una semplice pagina statica iniziale.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=7LgwmcyI2Rk:qbWVpEz5dNk:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=7LgwmcyI2Rk:qbWVpEz5dNk:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/BzHGMUo6uW4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/from-idea-to-paying-costumers-in-7-weeks.html</feedburner:origLink></entry>
  <entry>
    <title>Implementing DSL Blocks</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/1FwvY2vD6L8/implementing-dsl-blocks.html" rel="alternate" />
    <id>/blog/2011/06/implementing-dsl-blocks.html</id>
    <published>2011-06-19T00:00:00+02:00</published>
    <updated>2011-06-19T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Una panoramica ultra-comprensibile e dettagliata sul mondo dei DSL in Ruby e sui vantaggi e svantaggi delle varie tecniche possibili per realizzarli. Incrocerete i problemi del metodo &lt;code&gt;instance_eval&lt;/code&gt; spesso utilizzato in questi casi, scoprirete probabilmente per la prima volta &lt;code&gt;mixico&lt;/code&gt;, una gemma prodotta anni addietro dal compianto &lt;a href="http://it.wikipedia.org/wiki/Why_the_lucky_stiff"&gt;Why&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Uscirete dal tunnel di questa lettura con una gemma, &lt;code&gt;blockenspiel&lt;/code&gt;, in grado di ovviare alle problematiche dei vari metodi affrontati,
semplificando l&amp;#39;enorme complessità della materia con un banale &lt;code&gt;include&lt;/code&gt;, e col bonus aggiuntivo di poter riprodurre il comportamento di entrambe le sintassi DSL Ruby.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Una panoramica ultra-comprensibile e dettagliata sul mondo dei DSL in Ruby e sui vantaggi e svantaggi delle varie tecniche possibili per realizzarli. Incrocerete i problemi del metodo &lt;code&gt;instance_eval&lt;/code&gt; spesso utilizzato in questi casi, scoprirete probabilmente per la prima volta &lt;code&gt;mixico&lt;/code&gt;, una gemma prodotta anni addietro dal compianto &lt;a href="http://it.wikipedia.org/wiki/Why_the_lucky_stiff"&gt;Why&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Uscirete dal tunnel di questa lettura con una gemma, &lt;code&gt;blockenspiel&lt;/code&gt;, in grado di ovviare alle problematiche dei vari metodi affrontati,
semplificando l&amp;#39;enorme complessità della materia con un banale &lt;code&gt;include&lt;/code&gt;, e col bonus aggiuntivo di poter riprodurre il comportamento di entrambe le sintassi DSL Ruby.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=uNtl7AkZLcA:Jbx29G3cM_U:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=uNtl7AkZLcA:Jbx29G3cM_U:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/1FwvY2vD6L8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/implementing-dsl-blocks.html</feedburner:origLink></entry>
  <entry>
    <title>atk_icons: Agile Toolkit Icon Set su Compass</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/WDaE3W4Qob0/agile-toolkit-icon-set-compass.html" rel="alternate" />
    <id>/blog/2011/06/agile-toolkit-icon-set-compass.html</id>
    <published>2011-06-07T00:00:00+02:00</published>
    <updated>2011-06-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Da un paio di progetti a questa parte sto facendo un uso massiccio di un iconset non particolarmente conosciuto: &lt;a href="http://www.agiletech.ie/blog/128x16x16"&gt;Agile Toolkit Icon Set&lt;/a&gt;. Trattasi di 128 iconcine 16x16 da poter utilizzare in un sacco di contesti, e che con uno sforzo minimo rendono immediatamente più attraente, usabile e caratterizzante un sito.&lt;/p&gt;

&lt;p&gt;Con la solita logica DRY, ieri sera ho creato una piccola gemma che permette di sfruttare questo iconset su Compass con maggiore semplicità:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


@import &amp;quot;atk_icons&amp;quot;

.button.delete
  /* this will add an :after pseudo-selector with the specified icon
  @include atk-icon-pseudo(&amp;quot;basic-ex&amp;quot;)

.button.add span.icon
  /* you can also set the icon to a sub-element
  @include atk-icon(&amp;quot;basic-plus&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per maggiori informazioni su come installare questa estensione Compass, consultate il &lt;a href="https://github.com/welaika/atk_icons"&gt;&lt;code&gt;README&lt;/code&gt;&lt;/a&gt;. Spero vi possa essere utile!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Da un paio di progetti a questa parte sto facendo un uso massiccio di un iconset non particolarmente conosciuto: &lt;a href="http://www.agiletech.ie/blog/128x16x16"&gt;Agile Toolkit Icon Set&lt;/a&gt;. Trattasi di 128 iconcine 16x16 da poter utilizzare in un sacco di contesti, e che con uno sforzo minimo rendono immediatamente più attraente, usabile e caratterizzante un sito.&lt;/p&gt;

&lt;p&gt;Con la solita logica DRY, ieri sera ho creato una piccola gemma che permette di sfruttare questo iconset su Compass con maggiore semplicità:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


@import &amp;quot;atk_icons&amp;quot;

.button.delete
  /* this will add an :after pseudo-selector with the specified icon
  @include atk-icon-pseudo(&amp;quot;basic-ex&amp;quot;)

.button.add span.icon
  /* you can also set the icon to a sub-element
  @include atk-icon(&amp;quot;basic-plus&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per maggiori informazioni su come installare questa estensione Compass, consultate il &lt;a href="https://github.com/welaika/atk_icons"&gt;&lt;code&gt;README&lt;/code&gt;&lt;/a&gt;. Spero vi possa essere utile!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=5St9uo7TfV8:y9X3EIi3q-I:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=5St9uo7TfV8:y9X3EIi3q-I:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/WDaE3W4Qob0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/agile-toolkit-icon-set-compass.html</feedburner:origLink></entry>
  <entry>
    <title>Propositi di inizio Giugno</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/nJqu1FLjyK8/propositi-di-inizio-giugno.html" rel="alternate" />
    <id>/blog/2011/06/propositi-di-inizio-giugno.html</id>
    <published>2011-06-05T00:00:00+02:00</published>
    <updated>2011-06-05T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Dopo due mesi di vita di questo nuovo esperimento -- e assodato il fatto di essere in grado di reggere per davvero una media di 4-5 mini-post a settimana -- credo sia arrivato il momento di fare una piccola analisi sul da farsi. Gli obiettivi principali che cercherò di raggiungere nei prossimi mesi possono riassumersi in 3 punti.&lt;/p&gt;

&lt;h3&gt;Estendere il giro di nerd che visitano questo posto&lt;/h3&gt;

&lt;p&gt;Ho iniziato a scrivere con un target piuttosto specifico e ristretto: me. Sentivo il bisogno di selezionare e raggruppare in un unico luogo le montagne di link e progetti open che navigo e utilizzo settimanalmente, e mi sembrava che farlo in maniera pubblica e condivisa potesse essere potenzialmente più utile/divertente. Non nascondo la sorpresa nel leggere il counter dei lettori del feed.. A questo punto, se ci sono 20-30 lettori attivi, perchè non 50? Sarebbe sicuramente più divertente e stimolante. Non mi sembra di aver visto molti posti in cui oggi si possa discutere di Rails, web e dintorni in lingua italiana, sarebbe bello poter colmare questo vuoto!&lt;/p&gt;

&lt;h3&gt;Capire qualcosa di più su chi siete&lt;/h3&gt;

&lt;p&gt;Partiamo da un dato di fatto: non posseggo 30 amici così nerd da seguire questo posto con continuità. Ne deriva che probabilmente dev&amp;#39;esserci qualche forma di vita non meglio identificata che in silenzio legge. Non era un&amp;#39;ipotesi troppo preventivata all&amp;#39;inizio, ma dato che la cosa potrebbe essere divertente, perchè non conoscerci meglio? Questo punto porta direttamente al prossimo, ovvero...&lt;/p&gt;

&lt;h3&gt;Aumentare il coinvolgimento e la discussione&lt;/h3&gt;

&lt;p&gt;Per un blog di questo tipo, uno scambio continuo con chi legge sarebbe sicuramente cosa buona e giusta. Della serie: io consiglio a voi, voi consigliate a me. Sono sono un esperto in fidelizzazione, ma nel mio piccolo, il primo passo per cercare di convincervi a commentare è stato levare il sistema di commenti basato su Twitter e sostituirlo con Disqus, di modo da avere un modo super-rapido e poco spammoso per dialogare.&lt;/p&gt;

&lt;p&gt;Se avete consigli/suggerimenti, o volete semplicemente de-lurkizzarvi, questo è il momento!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Dopo due mesi di vita di questo nuovo esperimento -- e assodato il fatto di essere in grado di reggere per davvero una media di 4-5 mini-post a settimana -- credo sia arrivato il momento di fare una piccola analisi sul da farsi. Gli obiettivi principali che cercherò di raggiungere nei prossimi mesi possono riassumersi in 3 punti.&lt;/p&gt;

&lt;h3&gt;Estendere il giro di nerd che visitano questo posto&lt;/h3&gt;

&lt;p&gt;Ho iniziato a scrivere con un target piuttosto specifico e ristretto: me. Sentivo il bisogno di selezionare e raggruppare in un unico luogo le montagne di link e progetti open che navigo e utilizzo settimanalmente, e mi sembrava che farlo in maniera pubblica e condivisa potesse essere potenzialmente più utile/divertente. Non nascondo la sorpresa nel leggere il counter dei lettori del feed.. A questo punto, se ci sono 20-30 lettori attivi, perchè non 50? Sarebbe sicuramente più divertente e stimolante. Non mi sembra di aver visto molti posti in cui oggi si possa discutere di Rails, web e dintorni in lingua italiana, sarebbe bello poter colmare questo vuoto!&lt;/p&gt;

&lt;h3&gt;Capire qualcosa di più su chi siete&lt;/h3&gt;

&lt;p&gt;Partiamo da un dato di fatto: non posseggo 30 amici così nerd da seguire questo posto con continuità. Ne deriva che probabilmente dev&amp;#39;esserci qualche forma di vita non meglio identificata che in silenzio legge. Non era un&amp;#39;ipotesi troppo preventivata all&amp;#39;inizio, ma dato che la cosa potrebbe essere divertente, perchè non conoscerci meglio? Questo punto porta direttamente al prossimo, ovvero...&lt;/p&gt;

&lt;h3&gt;Aumentare il coinvolgimento e la discussione&lt;/h3&gt;

&lt;p&gt;Per un blog di questo tipo, uno scambio continuo con chi legge sarebbe sicuramente cosa buona e giusta. Della serie: io consiglio a voi, voi consigliate a me. Sono sono un esperto in fidelizzazione, ma nel mio piccolo, il primo passo per cercare di convincervi a commentare è stato levare il sistema di commenti basato su Twitter e sostituirlo con Disqus, di modo da avere un modo super-rapido e poco spammoso per dialogare.&lt;/p&gt;

&lt;p&gt;Se avete consigli/suggerimenti, o volete semplicemente de-lurkizzarvi, questo è il momento!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=yDZ2mv8RCr0:5-EaJapQH2k:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=yDZ2mv8RCr0:5-EaJapQH2k:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/nJqu1FLjyK8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/propositi-di-inizio-giugno.html</feedburner:origLink></entry>
  <entry>
    <title>Pow e PHP</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/Vt_zcy6oe3s/pow-e-php.html" rel="alternate" />
    <id>/blog/2011/06/pow-e-php.html</id>
    <published>2011-06-01T00:00:00+02:00</published>
    <updated>2011-06-01T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;However, there is a downside: Pow doesn’t play nicely with Apache (or any server listening on port 80). Life isn’t all greenfield, if in the course of the day you need to work on PHP or CGI legacy apps Pow is not so simple. Pow creates a firewall rule that redirects port 80 to its port; to access Apache you need to either toggle the firewall rule on and off or move Apache to a different port all together. And now you’re running two web servers. There has to be a better way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Questo è esattamente il motivo per il quale ho dovuto disinstallare Pow: lavorando spesso e volentieri su Wordpress, avere un development stack compatibile con PHP è fondamentale. Potrebbe però salvarci &lt;code&gt;rack-legacy&lt;/code&gt;, una gemma che permette di utilizzare Rack anche per far girare applicazioni PHP, sfruttando sotto il cofano il comando &lt;code&gt;php-cgi&lt;/code&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;However, there is a downside: Pow doesn’t play nicely with Apache (or any server listening on port 80). Life isn’t all greenfield, if in the course of the day you need to work on PHP or CGI legacy apps Pow is not so simple. Pow creates a firewall rule that redirects port 80 to its port; to access Apache you need to either toggle the firewall rule on and off or move Apache to a different port all together. And now you’re running two web servers. There has to be a better way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Questo è esattamente il motivo per il quale ho dovuto disinstallare Pow: lavorando spesso e volentieri su Wordpress, avere un development stack compatibile con PHP è fondamentale. Potrebbe però salvarci &lt;code&gt;rack-legacy&lt;/code&gt;, una gemma che permette di utilizzare Rack anche per far girare applicazioni PHP, sfruttando sotto il cofano il comando &lt;code&gt;php-cgi&lt;/code&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=f3vz4AiuHmE:gTKjDD2-_gE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=f3vz4AiuHmE:gTKjDD2-_gE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/Vt_zcy6oe3s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/pow-e-php.html</feedburner:origLink></entry>
  <entry>
    <title>Nuova piattaforma di Continuous Integration: Goldberg</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/h4YaO-QSQk8/nuova-piattaforma-di-continuous-integration-goldberg.html" rel="alternate" />
    <id>/blog/2011/06/nuova-piattaforma-di-continuous-integration-goldberg.html</id>
    <published>2011-06-01T00:00:00+02:00</published>
    <updated>2011-06-01T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Nuova possibilità nel mondo dei CI leggeri (per un&amp;#39;anteprima, &lt;a href="http://goldberg.c42.in/"&gt;http://goldberg.c42.in/&lt;/a&gt;). La novità è che è pensato da zero per funzionare con RVM e Bundler, quindi è in grado di supportare differenti progetti con differenti versioni di Ruby e gemme.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Nuova possibilità nel mondo dei CI leggeri (per un&amp;#39;anteprima, &lt;a href="http://goldberg.c42.in/"&gt;http://goldberg.c42.in/&lt;/a&gt;). La novità è che è pensato da zero per funzionare con RVM e Bundler, quindi è in grado di supportare differenti progetti con differenti versioni di Ruby e gemme.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=ooBGP-QL3oI:OpbkB51NJJk:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=ooBGP-QL3oI:OpbkB51NJJk:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/h4YaO-QSQk8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/06/nuova-piattaforma-di-continuous-integration-goldberg.html</feedburner:origLink></entry>
  <entry>
    <title>Learning Advanced Javascript</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/vpKMJn_T6mA/learning-advanced-javascript.html" rel="alternate" />
    <id>/blog/2011/05/learning-advanced-javascript.html</id>
    <published>2011-05-29T00:00:00+02:00</published>
    <updated>2011-05-29T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;E&amp;#39; difficile trovare buoni esempi per spiegare in poche righe di codice alcuni degli aspetti più controversi e meno intuitivi del mondo Javascript. Le famose closures, il possibile cambio di contesto all&amp;#39;interno di funzioni, i diversi metodi per ottenere l&amp;#39;ereditarietà tra (pseudo) classi..&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt; -- creatore di jQuery ed (ormai ex) Javascript Evangelist per Mozilla -- ha preparato una serie di slides interattive, dalle quali chiunque troverà qualcosina da imparare, in attesa di leggere il suo primo libro: &lt;a href="http://jsninja.com/"&gt;&lt;em&gt;Secrets of the JavaScript Ninja&lt;/em&gt;&lt;/a&gt;, in arrivo entro l&amp;#39;anno.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;E&amp;#39; difficile trovare buoni esempi per spiegare in poche righe di codice alcuni degli aspetti più controversi e meno intuitivi del mondo Javascript. Le famose closures, il possibile cambio di contesto all&amp;#39;interno di funzioni, i diversi metodi per ottenere l&amp;#39;ereditarietà tra (pseudo) classi..&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt; -- creatore di jQuery ed (ormai ex) Javascript Evangelist per Mozilla -- ha preparato una serie di slides interattive, dalle quali chiunque troverà qualcosina da imparare, in attesa di leggere il suo primo libro: &lt;a href="http://jsninja.com/"&gt;&lt;em&gt;Secrets of the JavaScript Ninja&lt;/em&gt;&lt;/a&gt;, in arrivo entro l&amp;#39;anno.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=lnusbECvvxg:3jQ1dxcnvbE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=lnusbECvvxg:3jQ1dxcnvbE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/vpKMJn_T6mA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/learning-advanced-javascript.html</feedburner:origLink></entry>
  <entry>
    <title>Aggiungere target="blank" sui link esterni con un Rack middleware</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/kbFEXiLty6E/aggiungere-target-blank-sui-link-esterni-con-un-rack-middleware.html" rel="alternate" />
    <id>/blog/2011/05/aggiungere-target-blank-sui-link-esterni-con-un-rack-middleware.html</id>
    <published>2011-05-23T00:00:00+02:00</published>
    <updated>2011-05-23T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Quante volte avete sentito, magari a termine dei lavori, la richiesta &amp;quot;tutti i link verso l&amp;#39;esterno dovrebbero aprirsi in un tab separato&amp;quot;? Questo è un tipico esempio di lavoro tremendamente noioso da fare per vie canoniche -- perchè richiederebbe un editing di tutti i link presenti in tutte le viste -- ma banale da realizzare passando per un middleware Rack.&lt;/p&gt;

&lt;p&gt;In basso il codice. Il middleware usa &lt;a href="http://nokogiri.org"&gt;Nokogiri&lt;/a&gt; per parsare tutte le pagine HTML (quelle con &lt;code&gt;Content-Type&lt;/code&gt; impostato a &lt;code&gt;text/html&lt;/code&gt;), e per ogni link trovato controlla il dominio: se non coincide con quello del server, aggiunge il fatidico attributo &lt;code&gt;target&lt;/code&gt; al link.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

require &amp;#39;nokogiri&amp;#39;

module Rack
  class TargetBlank
    include Rack::Utils

    def initialize(app)
      @app = app
    end

    def call(env)
      @request = Rack::Request.new(env)
      status, @headers, @body = @app.call(env)
      @headers = HeaderHash.new(@headers)
      if is_html_content?
        body = edit_external_links(body_to_string)
        update_response_body(body)
        update_content_length
      end
      [status, @headers, @body]
    end

    private

    def edit_external_links(body)
      doc = Nokogiri::HTML(body)
      found_links = false
      doc.css(&amp;#39;a[href]&amp;#39;).each do |link|
        uri = URI(link[&amp;#39;href&amp;#39;])
        if uri.absolute? and uri.host != @request.host
          link[&amp;#39;target&amp;#39;] = &amp;#39;blank&amp;#39;
          found_links = true
        end
      end
      found_links ? doc.to_html : body
    end

    def body_to_string
      s = &amp;quot;&amp;quot;
      @body.each { |x| s &amp;lt;&amp;lt; x }
      s
    end

    def update_content_length
      length = 0
      @body.each { |s| length += Rack::Utils.bytesize(s) }
      @headers[&amp;#39;Content-Length&amp;#39;] = length.to_s
    end

    def update_response_body(body)
      if @body.class.name == &amp;quot;ActionController::Response&amp;quot;
        @body.body = body
      else
        @body = [body]
      end
    end

    def is_html_content?
      @headers.key?(&amp;#39;Content-Type&amp;#39;) &amp;amp;&amp;amp; @headers[&amp;#39;Content-Type&amp;#39;].include?(&amp;#39;text/html&amp;#39;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
</summary>
    <content type="html">&lt;p&gt;Quante volte avete sentito, magari a termine dei lavori, la richiesta &amp;quot;tutti i link verso l&amp;#39;esterno dovrebbero aprirsi in un tab separato&amp;quot;? Questo è un tipico esempio di lavoro tremendamente noioso da fare per vie canoniche -- perchè richiederebbe un editing di tutti i link presenti in tutte le viste -- ma banale da realizzare passando per un middleware Rack.&lt;/p&gt;

&lt;p&gt;In basso il codice. Il middleware usa &lt;a href="http://nokogiri.org"&gt;Nokogiri&lt;/a&gt; per parsare tutte le pagine HTML (quelle con &lt;code&gt;Content-Type&lt;/code&gt; impostato a &lt;code&gt;text/html&lt;/code&gt;), e per ogni link trovato controlla il dominio: se non coincide con quello del server, aggiunge il fatidico attributo &lt;code&gt;target&lt;/code&gt; al link.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

require &amp;#39;nokogiri&amp;#39;

module Rack
  class TargetBlank
    include Rack::Utils

    def initialize(app)
      @app = app
    end

    def call(env)
      @request = Rack::Request.new(env)
      status, @headers, @body = @app.call(env)
      @headers = HeaderHash.new(@headers)
      if is_html_content?
        body = edit_external_links(body_to_string)
        update_response_body(body)
        update_content_length
      end
      [status, @headers, @body]
    end

    private

    def edit_external_links(body)
      doc = Nokogiri::HTML(body)
      found_links = false
      doc.css(&amp;#39;a[href]&amp;#39;).each do |link|
        uri = URI(link[&amp;#39;href&amp;#39;])
        if uri.absolute? and uri.host != @request.host
          link[&amp;#39;target&amp;#39;] = &amp;#39;blank&amp;#39;
          found_links = true
        end
      end
      found_links ? doc.to_html : body
    end

    def body_to_string
      s = &amp;quot;&amp;quot;
      @body.each { |x| s &amp;lt;&amp;lt; x }
      s
    end

    def update_content_length
      length = 0
      @body.each { |s| length += Rack::Utils.bytesize(s) }
      @headers[&amp;#39;Content-Length&amp;#39;] = length.to_s
    end

    def update_response_body(body)
      if @body.class.name == &amp;quot;ActionController::Response&amp;quot;
        @body.body = body
      else
        @body = [body]
      end
    end

    def is_html_content?
      @headers.key?(&amp;#39;Content-Type&amp;#39;) &amp;amp;&amp;amp; @headers[&amp;#39;Content-Type&amp;#39;].include?(&amp;#39;text/html&amp;#39;)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=QwPEfM6_AW0:2IEkGCT8QbY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=QwPEfM6_AW0:2IEkGCT8QbY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/kbFEXiLty6E" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/aggiungere-target-blank-sui-link-esterni-con-un-rack-middleware.html</feedburner:origLink></entry>
  <entry>
    <title>CoffeeBeans: AJAX con CoffeeScript su Rails</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/4FmjFaYn1ao/coffeebeans-ajax-con-coffeescript-su-rails.html" rel="alternate" />
    <id>/blog/2011/05/coffeebeans-ajax-con-coffeescript-su-rails.html</id>
    <published>2011-05-21T00:00:00+02:00</published>
    <updated>2011-05-21T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Il pezzo che ancora mancava a Rails 3.1: scrivere anche le risposte AJAX in CoffeeScript. Con questo plugin è sufficiente creare viste con suffisso &lt;code&gt;.coffee&lt;/code&gt;, e verranno automaticamente convertite in Javascript al momento del rendering.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


alert &amp;quot;Hello world!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;L&amp;#39;unico svantaggio da verificare è la lentezza per processare questa compilazione live.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Il pezzo che ancora mancava a Rails 3.1: scrivere anche le risposte AJAX in CoffeeScript. Con questo plugin è sufficiente creare viste con suffisso &lt;code&gt;.coffee&lt;/code&gt;, e verranno automaticamente convertite in Javascript al momento del rendering.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


alert &amp;quot;Hello world!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;L&amp;#39;unico svantaggio da verificare è la lentezza per processare questa compilazione live.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=hy9SpaednJM:Tus7s02dk68:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=hy9SpaednJM:Tus7s02dk68:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/4FmjFaYn1ao" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/coffeebeans-ajax-con-coffeescript-su-rails.html</feedburner:origLink></entry>
  <entry>
    <title>Usa oggetti Ruby nei form Rails con Informal</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/fCtalFCl0Qo/usa-oggetti-ruby-nei-form-rails-con-informal.html" rel="alternate" />
    <id>/blog/2011/05/usa-oggetti-ruby-nei-form-rails-con-informal.html</id>
    <published>2011-05-21T00:00:00+02:00</published>
    <updated>2011-05-21T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Informal is a small gem that enhances a Plain Old Ruby Object so it can be used with Rails 3 form helpers in place of an &lt;code&gt;ActiveRecord&lt;/code&gt; model. It works with the Rails &lt;code&gt;form_for&lt;/code&gt; helper, and &lt;code&gt;simple_form&lt;/code&gt; as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Non è impossibile da fare a mano (e &lt;a href="http://pragprog.com/titles/jvrails/crafting-rails-applications"&gt;Crafting Rails Applications&lt;/a&gt; ti spiega anche bene come fare), ma così è decisamente più comodo. Provata, funziona!&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Informal is a small gem that enhances a Plain Old Ruby Object so it can be used with Rails 3 form helpers in place of an &lt;code&gt;ActiveRecord&lt;/code&gt; model. It works with the Rails &lt;code&gt;form_for&lt;/code&gt; helper, and &lt;code&gt;simple_form&lt;/code&gt; as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Non è impossibile da fare a mano (e &lt;a href="http://pragprog.com/titles/jvrails/crafting-rails-applications"&gt;Crafting Rails Applications&lt;/a&gt; ti spiega anche bene come fare), ma così è decisamente più comodo. Provata, funziona!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=O4di1wDBmCw:Y8JOCAtkcTs:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=O4di1wDBmCw:Y8JOCAtkcTs:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/fCtalFCl0Qo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/usa-oggetti-ruby-nei-form-rails-con-informal.html</feedburner:origLink></entry>
  <entry>
    <title>Batman.js</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/18EF3-_0kgE/batman-js.html" rel="alternate" />
    <id>/blog/2011/05/batman-js.html</id>
    <published>2011-05-21T00:00:00+02:00</published>
    <updated>2011-05-21T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Una interessante alternativa agli MVC lato client. Opzionalmente è in grado di occuparsi anche del lato server, in Node.js, con possibilità di condividere il codice e le validazioni dei modelli.
Mi piace molto il binding automatico di comportamenti tramite attributi HTML5 &lt;code&gt;data-&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul id=&amp;quot;items&amp;quot;&amp;gt;
    &amp;lt;li data-foreach-todo=&amp;quot;Todo.all&amp;quot; data-mixin=&amp;quot;animation&amp;quot;&amp;gt;
        &amp;lt;input type=&amp;quot;checkbox&amp;quot; data-bind=&amp;quot;todo.isDone&amp;quot; /&amp;gt;
        &amp;lt;label data-bind=&amp;quot;todo.body&amp;quot; data-class-done=&amp;quot;todo.isDone&amp;quot; data-mixin=&amp;quot;editable&amp;quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;a data-event-click=&amp;quot;todo.destroy&amp;quot;&amp;gt;delete&amp;lt;/a&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se c&amp;#39;è una cosa che Backbone.js non sa fare, è farti essere rapido nelle cose banali. Qui mi sembra ci siano invece degli ottimi spunti.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Una interessante alternativa agli MVC lato client. Opzionalmente è in grado di occuparsi anche del lato server, in Node.js, con possibilità di condividere il codice e le validazioni dei modelli.
Mi piace molto il binding automatico di comportamenti tramite attributi HTML5 &lt;code&gt;data-&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;ul id=&amp;quot;items&amp;quot;&amp;gt;
    &amp;lt;li data-foreach-todo=&amp;quot;Todo.all&amp;quot; data-mixin=&amp;quot;animation&amp;quot;&amp;gt;
        &amp;lt;input type=&amp;quot;checkbox&amp;quot; data-bind=&amp;quot;todo.isDone&amp;quot; /&amp;gt;
        &amp;lt;label data-bind=&amp;quot;todo.body&amp;quot; data-class-done=&amp;quot;todo.isDone&amp;quot; data-mixin=&amp;quot;editable&amp;quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;a data-event-click=&amp;quot;todo.destroy&amp;quot;&amp;gt;delete&amp;lt;/a&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se c&amp;#39;è una cosa che Backbone.js non sa fare, è farti essere rapido nelle cose banali. Qui mi sembra ci siano invece degli ottimi spunti.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=JERNk-9Xq-Y:cRmAKSHUa4I:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=JERNk-9Xq-Y:cRmAKSHUa4I:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/18EF3-_0kgE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/batman-js.html</feedburner:origLink></entry>
  <entry>
    <title>Deploy rapidi con Capistrano</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/k47g5BA31yE/deploy-rapidi-con-capistrano.html" rel="alternate" />
    <id>/blog/2011/05/deploy-rapidi-con-capistrano.html</id>
    <published>2011-05-11T00:00:00+02:00</published>
    <updated>2011-05-11T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Niente di che, percarità, ma quando &lt;code&gt;cap deploy&lt;/code&gt; inizia a metterci cinque buoni minuti, averlo fa comodo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

namespace :deploy do
  task :fast, :roles =&amp;gt; :app do
    run &amp;quot;cd #{current_path} &amp;amp;&amp;amp; git pull origin master &amp;amp;&amp;amp; rake deployer:all_rake_tasks RAILS_ENV=production &amp;amp;&amp;amp; touch tmp/restart.txt&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ovviamente, fatelo partire con &lt;code&gt;cap deploy:fast&lt;/code&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Niente di che, percarità, ma quando &lt;code&gt;cap deploy&lt;/code&gt; inizia a metterci cinque buoni minuti, averlo fa comodo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

namespace :deploy do
  task :fast, :roles =&amp;gt; :app do
    run &amp;quot;cd #{current_path} &amp;amp;&amp;amp; git pull origin master &amp;amp;&amp;amp; rake deployer:all_rake_tasks RAILS_ENV=production &amp;amp;&amp;amp; touch tmp/restart.txt&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ovviamente, fatelo partire con &lt;code&gt;cap deploy:fast&lt;/code&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=25HbYsQhCWA:cC0-RzamDMA:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=25HbYsQhCWA:cC0-RzamDMA:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/k47g5BA31yE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/deploy-rapidi-con-capistrano.html</feedburner:origLink></entry>
  <entry>
    <title>Tapir: indicizzatore di post per siti statici</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/qaAzDLHk6f8/tapir-indicizzatore-di-post-per-siti-statici.html" rel="alternate" />
    <id>/blog/2011/05/tapir-indicizzatore-di-post-per-siti-statici.html</id>
    <published>2011-05-11T00:00:00+02:00</published>
    <updated>2011-05-11T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;I ragazzi olandesi di &lt;a href="http://www.80beans.com/"&gt;80beans&lt;/a&gt; hanno preparato &lt;a href="http://jeffkreeftmeijer.com/2011/introducing-tapir-simple-search-for-static-sites/"&gt;in una settimana circa&lt;/a&gt; Tapir, un servizio (gratuito) basato su &lt;a href="https://github.com/karmi/tire"&gt;Tire&lt;/a&gt; in grado di fetchare ed indicizzare feed esterni con una cadenza di 15 minuti (o mediante notifica push da parte dell&amp;#39;autore). Una comoda API JSON(P) ti permette poi di effettuare ricerche in un secondo tempo.&lt;/p&gt;

&lt;p&gt;Ottima idea, ben eseguita e a basso costo. Spero di trovare il tempo di utilizzarla su questo blog a breve.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;I ragazzi olandesi di &lt;a href="http://www.80beans.com/"&gt;80beans&lt;/a&gt; hanno preparato &lt;a href="http://jeffkreeftmeijer.com/2011/introducing-tapir-simple-search-for-static-sites/"&gt;in una settimana circa&lt;/a&gt; Tapir, un servizio (gratuito) basato su &lt;a href="https://github.com/karmi/tire"&gt;Tire&lt;/a&gt; in grado di fetchare ed indicizzare feed esterni con una cadenza di 15 minuti (o mediante notifica push da parte dell&amp;#39;autore). Una comoda API JSON(P) ti permette poi di effettuare ricerche in un secondo tempo.&lt;/p&gt;

&lt;p&gt;Ottima idea, ben eseguita e a basso costo. Spero di trovare il tempo di utilizzarla su questo blog a breve.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=W3hLVeED6PQ:q1lPYZKw1EE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=W3hLVeED6PQ:q1lPYZKw1EE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/qaAzDLHk6f8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/tapir-indicizzatore-di-post-per-siti-statici.html</feedburner:origLink></entry>
  <entry>
    <title>Come assumere programmatori</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/RfKLoEyFHLM/come-assumere-programmatori.html" rel="alternate" />
    <id>/blog/2011/05/come-assumere-programmatori.html</id>
    <published>2011-05-11T00:00:00+02:00</published>
    <updated>2011-05-11T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Filter out candidates who don&amp;#39;t have a github/blog/portfolio&lt;/li&gt;
&lt;li&gt;Pair with remaining candidates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You&amp;#39;ll learn 95% of what you need to know from pairing. Can they code? How do they think through problems? Can they interact with teammates? When they don&amp;#39;t know something, do they posture, or figure it out?
It&amp;#39;s pretty fucking simple, really.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortissimamente quoto.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Filter out candidates who don&amp;#39;t have a github/blog/portfolio&lt;/li&gt;
&lt;li&gt;Pair with remaining candidates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You&amp;#39;ll learn 95% of what you need to know from pairing. Can they code? How do they think through problems? Can they interact with teammates? When they don&amp;#39;t know something, do they posture, or figure it out?
It&amp;#39;s pretty fucking simple, really.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortissimamente quoto.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=u5jooUSm5XY:V4H-R-YUziE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=u5jooUSm5XY:V4H-R-YUziE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/RfKLoEyFHLM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/come-assumere-programmatori.html</feedburner:origLink></entry>
  <entry>
    <title>24 tools da terminale che forse non conoscevi</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/V7ChndmC8oY/24-tools-da-terminale-che-forse-non-conoscevi.html" rel="alternate" />
    <id>/blog/2011/05/24-tools-da-terminale-che-forse-non-conoscevi.html</id>
    <published>2011-05-10T00:00:00+02:00</published>
    <updated>2011-05-10T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ne conoscevo solo 9. Notevole &lt;code&gt;tpp&lt;/code&gt;, un precursore di &lt;a href="https://github.com/schacon/showoff"&gt;showoff&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;tpp stands for text presentation program and is an ncurses-based
presentation tool. The presentation can be written with your favorite
editor in a simple description format and then shown on any text
terminal that is supported by ncurses – ranging from an old VT100 to the
Linux framebuffer to an xterm.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
    <content type="html">&lt;p&gt;Ne conoscevo solo 9. Notevole &lt;code&gt;tpp&lt;/code&gt;, un precursore di &lt;a href="https://github.com/schacon/showoff"&gt;showoff&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;tpp stands for text presentation program and is an ncurses-based
presentation tool. The presentation can be written with your favorite
editor in a simple description format and then shown on any text
terminal that is supported by ncurses – ranging from an old VT100 to the
Linux framebuffer to an xterm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=6ggBkuu_520:E7sB56Msics:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=6ggBkuu_520:E7sB56Msics:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/V7ChndmC8oY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/24-tools-da-terminale-che-forse-non-conoscevi.html</feedburner:origLink></entry>
  <entry>
    <title>Subtle Patterns: sfondi Creative Commons per il tuo prossimo progetto web</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/RgCvX4IPD48/subtle-patterns-sfondi-creative-commons-per-il-tuo-prossimo-progetto-web.html" rel="alternate" />
    <id>/blog/2011/05/subtle-patterns-sfondi-creative-commons-per-il-tuo-prossimo-progetto-web.html</id>
    <published>2011-05-08T00:00:00+02:00</published>
    <updated>2011-05-08T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ottima collezione di sfondi poco appariscenti, con possibilità di scaricarli anche come pattern Photoshop.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Ottima collezione di sfondi poco appariscenti, con possibilità di scaricarli anche come pattern Photoshop.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=jkktZme-g9c:EyqPVx9iN6c:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=jkktZme-g9c:EyqPVx9iN6c:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/RgCvX4IPD48" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/subtle-patterns-sfondi-creative-commons-per-il-tuo-prossimo-progetto-web.html</feedburner:origLink></entry>
  <entry>
    <title>Vico: alternativa sensata a Textmate</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/-0NAQMUGPnw/vico-alternativa-sensata-a-textmate.html" rel="alternate" />
    <id>/blog/2011/05/vico-alternativa-sensata-a-textmate.html</id>
    <published>2011-05-08T00:00:00+02:00</published>
    <updated>2011-05-08T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Vico is the result of a personal itch. My eyes want a beautiful looking, modern Mac text editor. But my fingers just want vi.
Vico is inspired by some great software out there. The vi/vim command set beats anything in terms of efficiency and speed. TextMate has set a new standard in many ways, and the bundle community is still thriving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In attesa di Texmate 2 (5 anni per una major release?! ormai ho i miei fondati dubbi sul suo arrivo), ho trovato questo nuovo text editor, Vico. Ha un&amp;#39;approccio interessante: unisce i keybinding di &lt;code&gt;vi&lt;/code&gt; (insert mode, visual mode, etc.) alla compatibilità completa con i bundle di Textmate (!!). A quanto sto vedendo, funziona decisamente bene. Solo per OS X, ovviamente.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Vico is the result of a personal itch. My eyes want a beautiful looking, modern Mac text editor. But my fingers just want vi.
Vico is inspired by some great software out there. The vi/vim command set beats anything in terms of efficiency and speed. TextMate has set a new standard in many ways, and the bundle community is still thriving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In attesa di Texmate 2 (5 anni per una major release?! ormai ho i miei fondati dubbi sul suo arrivo), ho trovato questo nuovo text editor, Vico. Ha un&amp;#39;approccio interessante: unisce i keybinding di &lt;code&gt;vi&lt;/code&gt; (insert mode, visual mode, etc.) alla compatibilità completa con i bundle di Textmate (!!). A quanto sto vedendo, funziona decisamente bene. Solo per OS X, ovviamente.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=Q3VCe_1PFss:KhBl6O6ravU:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=Q3VCe_1PFss:KhBl6O6ravU:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/-0NAQMUGPnw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/vico-alternativa-sensata-a-textmate.html</feedburner:origLink></entry>
  <entry>
    <title>Un web proxy in Rack per cross-domain Ajax</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/yjzEVshYzGA/un-web-proxy-in-rack-per-cross-domain-ajax.html" rel="alternate" />
    <id>/blog/2011/05/un-web-proxy-in-rack-per-cross-domain-ajax.html</id>
    <published>2011-05-07T00:00:00+02:00</published>
    <updated>2011-05-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;In weLaika stiamo lavorando allo sviluppo di un social-network con un&amp;#39;architettura logica a due livelli: da una parte uno storage ultra-performante su Google App Engine, dall&amp;#39;altra una serie di differenti frontend per l&amp;#39;utente finale. Il primo frontend è quello web -- al quale weLaika sta lavorando. Il secondo, e per ora ultimo, sarà realizzato con tecnologia Flash.&lt;/p&gt;

&lt;p&gt;Tutti i frontend sono in grado di comunicare col backend tramite API. I metodi API sono stati suddivisi in  &amp;quot;pubblici&amp;quot; e &amp;quot;privati&amp;quot;. Quelli pubblici sono in grado di rispondere con formato JSONP e possono dunque essere utilizzati da applicazioni di terze parti, le API private sono invece pensate solo ed esclusivamente per i frontend &amp;quot;ufficiali&amp;quot; ed è possibile accedervi solo mediante richieste Ajax pure, quindi da pagine all&amp;#39;interno dello stesso dominio del backend (per maggiori info, date un&amp;#39;occhio alla &lt;a href="http://en.wikipedia.org/wiki/Same_origin_policy"&gt;same-origin policy&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Tutto bellissimo e sensato, ma come lavorare sul frontend web in locale, con la possibilità effettuare richieste Ajax verso il backend? I browsers non ce lo permettono (se non mediante hack vari, e non sempre comunque)!&lt;/p&gt;

&lt;h3&gt;Cross-domain Ajax con Rack e Net::HTTP&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://developer.yahoo.com/javascript/howto-proxy.html"&gt;Così come suggerito anche da Yahoo&lt;/a&gt;, la soluzione è semplice: il server locale deve poter intercettare tutte le richieste Ajax, capire quali sono quelle da inoltrare verso un server remoto, fingersi client con quest&amp;#39;ultimo passando i medesimi parametri ricevuti dal browser (cookie compresi), ricevere la risposta (header compresi) e inoltrare il tutto al browser. Phew.&lt;/p&gt;

&lt;p&gt;Tutto ciò in realtà è moderatamente semplice da fare. Ecco qui un middleware Rack in grado di comportarsi esattamente in questo modo, sfruttando la libreria standard &lt;code&gt;Net::HTTP&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


require &amp;quot;net/http&amp;quot;

class Rack::Proxy
  def initialize(app, &amp;amp;block)
    self.class.send(:define_method, :uri_for, &amp;amp;block)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)
    method = req.request_method.downcase
    method[0..0] = method[0..0].upcase

    return @app.call(env) unless uri = uri_for(req)

    sub_request = Net::HTTP.const_get(method).new(&amp;quot;#{uri.path}#{&amp;quot;?&amp;quot; if uri.query}#{uri.query}&amp;quot;)

    if sub_request.request_body_permitted? and req.body
      sub_request.body_stream = req.body
      sub_request.content_length = req.content_length
      sub_request.content_type = req.content_type
    end

    sub_request[&amp;quot;Cookie&amp;quot;] = req.env[&amp;quot;HTTP_COOKIE&amp;quot;]
    sub_request[&amp;quot;Accept-Encoding&amp;quot;] = req.accept_encoding
    sub_request[&amp;quot;Referer&amp;quot;] = req.referer
    sub_request.basic_auth *uri.userinfo.split(&amp;#39;:&amp;#39;) if (uri.userinfo &amp;amp;&amp;amp; uri.userinfo.index(&amp;#39;:&amp;#39;))

    http = Net::HTTP.new(uri.host, uri.port)

    sub_response = http.start { |http| http.request(sub_request) }

    headers = {}
    sub_response.each_header do |k,v|
      headers[k] = v unless k.to_s =~ /content-length|transfer-encoding/i
    end

    [sub_response.code.to_i, headers, [sub_response.read_body]]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il suo utilizzo è molto semplice, un esempio pratico (da lanciare per esempio con &lt;code&gt;thin  -R config.ru start&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


use Rack::Proxy do |req|
  if req.path =~ /api/
    URI.parse(&amp;quot;http://www.api-server.com#{req.path}#{&amp;quot;?&amp;quot; if req.query_string}#{req.query_string}&amp;quot;)
  end
end

run Rack::Directory.new(&amp;quot;.&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo caso, facciamo partire un server Rack che normalmente serve tutti i files contenuti nella directory corrente (mediante il fantastico &lt;code&gt;Rack::Directory&lt;/code&gt;), ma il middleware Rack creato, prima di passare la palla a &lt;code&gt;Rack::Directory&lt;/code&gt;, controlla se l&amp;#39;URL non contiene la stringa &lt;code&gt;&amp;quot;api&amp;quot;&lt;/code&gt;. In caso affermativo, si comporta da proxy, forwardando la richiesta HTTP ricevuta al server &lt;code&gt;www.api-server.com&lt;/code&gt;, sul medesimo path.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;In weLaika stiamo lavorando allo sviluppo di un social-network con un&amp;#39;architettura logica a due livelli: da una parte uno storage ultra-performante su Google App Engine, dall&amp;#39;altra una serie di differenti frontend per l&amp;#39;utente finale. Il primo frontend è quello web -- al quale weLaika sta lavorando. Il secondo, e per ora ultimo, sarà realizzato con tecnologia Flash.&lt;/p&gt;

&lt;p&gt;Tutti i frontend sono in grado di comunicare col backend tramite API. I metodi API sono stati suddivisi in  &amp;quot;pubblici&amp;quot; e &amp;quot;privati&amp;quot;. Quelli pubblici sono in grado di rispondere con formato JSONP e possono dunque essere utilizzati da applicazioni di terze parti, le API private sono invece pensate solo ed esclusivamente per i frontend &amp;quot;ufficiali&amp;quot; ed è possibile accedervi solo mediante richieste Ajax pure, quindi da pagine all&amp;#39;interno dello stesso dominio del backend (per maggiori info, date un&amp;#39;occhio alla &lt;a href="http://en.wikipedia.org/wiki/Same_origin_policy"&gt;same-origin policy&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Tutto bellissimo e sensato, ma come lavorare sul frontend web in locale, con la possibilità effettuare richieste Ajax verso il backend? I browsers non ce lo permettono (se non mediante hack vari, e non sempre comunque)!&lt;/p&gt;

&lt;h3&gt;Cross-domain Ajax con Rack e Net::HTTP&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://developer.yahoo.com/javascript/howto-proxy.html"&gt;Così come suggerito anche da Yahoo&lt;/a&gt;, la soluzione è semplice: il server locale deve poter intercettare tutte le richieste Ajax, capire quali sono quelle da inoltrare verso un server remoto, fingersi client con quest&amp;#39;ultimo passando i medesimi parametri ricevuti dal browser (cookie compresi), ricevere la risposta (header compresi) e inoltrare il tutto al browser. Phew.&lt;/p&gt;

&lt;p&gt;Tutto ciò in realtà è moderatamente semplice da fare. Ecco qui un middleware Rack in grado di comportarsi esattamente in questo modo, sfruttando la libreria standard &lt;code&gt;Net::HTTP&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


require &amp;quot;net/http&amp;quot;

class Rack::Proxy
  def initialize(app, &amp;amp;block)
    self.class.send(:define_method, :uri_for, &amp;amp;block)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)
    method = req.request_method.downcase
    method[0..0] = method[0..0].upcase

    return @app.call(env) unless uri = uri_for(req)

    sub_request = Net::HTTP.const_get(method).new(&amp;quot;#{uri.path}#{&amp;quot;?&amp;quot; if uri.query}#{uri.query}&amp;quot;)

    if sub_request.request_body_permitted? and req.body
      sub_request.body_stream = req.body
      sub_request.content_length = req.content_length
      sub_request.content_type = req.content_type
    end

    sub_request[&amp;quot;Cookie&amp;quot;] = req.env[&amp;quot;HTTP_COOKIE&amp;quot;]
    sub_request[&amp;quot;Accept-Encoding&amp;quot;] = req.accept_encoding
    sub_request[&amp;quot;Referer&amp;quot;] = req.referer
    sub_request.basic_auth *uri.userinfo.split(&amp;#39;:&amp;#39;) if (uri.userinfo &amp;amp;&amp;amp; uri.userinfo.index(&amp;#39;:&amp;#39;))

    http = Net::HTTP.new(uri.host, uri.port)

    sub_response = http.start { |http| http.request(sub_request) }

    headers = {}
    sub_response.each_header do |k,v|
      headers[k] = v unless k.to_s =~ /content-length|transfer-encoding/i
    end

    [sub_response.code.to_i, headers, [sub_response.read_body]]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il suo utilizzo è molto semplice, un esempio pratico (da lanciare per esempio con &lt;code&gt;thin  -R config.ru start&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


use Rack::Proxy do |req|
  if req.path =~ /api/
    URI.parse(&amp;quot;http://www.api-server.com#{req.path}#{&amp;quot;?&amp;quot; if req.query_string}#{req.query_string}&amp;quot;)
  end
end

run Rack::Directory.new(&amp;quot;.&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo caso, facciamo partire un server Rack che normalmente serve tutti i files contenuti nella directory corrente (mediante il fantastico &lt;code&gt;Rack::Directory&lt;/code&gt;), ma il middleware Rack creato, prima di passare la palla a &lt;code&gt;Rack::Directory&lt;/code&gt;, controlla se l&amp;#39;URL non contiene la stringa &lt;code&gt;&amp;quot;api&amp;quot;&lt;/code&gt;. In caso affermativo, si comporta da proxy, forwardando la richiesta HTTP ricevuta al server &lt;code&gt;www.api-server.com&lt;/code&gt;, sul medesimo path.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=wJ45tGnCRU0:cZh4eTlp3ZQ:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=wJ45tGnCRU0:cZh4eTlp3ZQ:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/yjzEVshYzGA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/un-web-proxy-in-rack-per-cross-domain-ajax.html</feedburner:origLink></entry>
  <entry>
    <title>Spine: l'erede di Backbone.js</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/GlSapliDieU/spine-l-erede-di-backbone-js.html" rel="alternate" />
    <id>/blog/2011/05/spine-l-erede-di-backbone-js.html</id>
    <published>2011-05-07T00:00:00+02:00</published>
    <updated>2011-05-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Un po&amp;#39; di sana concorrenza nel mondo dell&amp;#39;MVC client-side ci voleva. Salutiamo tutti Spine.js, un framework MVC fortemente ispirato all&amp;#39;ormai popolare Backbone.js, ma con una serie di miglioramenti molto interessanti che lo avvicinano molto ad una impostazione Rails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migliore gestione dell&amp;#39;ereditarietà tra classi JS;&lt;/li&gt;
&lt;li&gt;Ogni istanza di uno stesso record rimane sincronizzata alle altre;&lt;/li&gt;
&lt;li&gt;Eliminazione del concetto di &lt;code&gt;Collection&lt;/code&gt; presente in Backbone;&lt;/li&gt;
&lt;li&gt;Layer di persistenza Ajax o con HTML5 local storage;&lt;/li&gt;
&lt;li&gt;Aggiunta di qualche ulteriore helper nelle Viste (che in Spine diventano &lt;code&gt;Controller&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Supporto dell&amp;#39;HTML5 history nei controller (che in Spine diventano &lt;code&gt;Route&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Non vedo l&amp;#39;ora di provarlo, sembra ottimo.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Un po&amp;#39; di sana concorrenza nel mondo dell&amp;#39;MVC client-side ci voleva. Salutiamo tutti Spine.js, un framework MVC fortemente ispirato all&amp;#39;ormai popolare Backbone.js, ma con una serie di miglioramenti molto interessanti che lo avvicinano molto ad una impostazione Rails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migliore gestione dell&amp;#39;ereditarietà tra classi JS;&lt;/li&gt;
&lt;li&gt;Ogni istanza di uno stesso record rimane sincronizzata alle altre;&lt;/li&gt;
&lt;li&gt;Eliminazione del concetto di &lt;code&gt;Collection&lt;/code&gt; presente in Backbone;&lt;/li&gt;
&lt;li&gt;Layer di persistenza Ajax o con HTML5 local storage;&lt;/li&gt;
&lt;li&gt;Aggiunta di qualche ulteriore helper nelle Viste (che in Spine diventano &lt;code&gt;Controller&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Supporto dell&amp;#39;HTML5 history nei controller (che in Spine diventano &lt;code&gt;Route&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Non vedo l&amp;#39;ora di provarlo, sembra ottimo.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=ycfT4_5pQAA:NV1Tl9lxBT4:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=ycfT4_5pQAA:NV1Tl9lxBT4:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/GlSapliDieU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/spine-l-erede-di-backbone-js.html</feedburner:origLink></entry>
  <entry>
    <title>Capistrano e i tunnel SSH</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/Ohnx9-k_Ga4/capistrano-e-i-tunnel-ssh.html" rel="alternate" />
    <id>/blog/2011/05/capistrano-e-i-tunnel-ssh.html</id>
    <published>2011-05-03T00:00:00+02:00</published>
    <updated>2011-05-03T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Piccola scoperta involontaria di ieri: se avete un&amp;#39;accesso SSH al server di produzione ristretto su un solo IP pubblico, l&amp;#39;iter che fino a ieri seguivo per i deploy Capistrano era quello di creare un tunnel SSH tra me ed il server &amp;quot;gateway&amp;quot; via terminale tramite il comando:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
ssh -f -N -L 12345:server-di-produzione.com:22 server-gateway.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A questo punto era possibile connettersi alla porta &lt;code&gt;22&lt;/code&gt; di &lt;code&gt;server-di-produzione.com&lt;/code&gt; tramite la porta &lt;code&gt;12345&lt;/code&gt; di &lt;code&gt;localhost&lt;/code&gt;. Capistrano però ha già tutto predisposto: è sufficiente aggiungere la seguente variabile al proprio script di deploy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

set :gateway, &amp;quot;utente@server-gateway.com&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E in automatico creerà il tunnel al vostro posto. Urrà per il comando in meno da digitare!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Piccola scoperta involontaria di ieri: se avete un&amp;#39;accesso SSH al server di produzione ristretto su un solo IP pubblico, l&amp;#39;iter che fino a ieri seguivo per i deploy Capistrano era quello di creare un tunnel SSH tra me ed il server &amp;quot;gateway&amp;quot; via terminale tramite il comando:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
ssh -f -N -L 12345:server-di-produzione.com:22 server-gateway.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A questo punto era possibile connettersi alla porta &lt;code&gt;22&lt;/code&gt; di &lt;code&gt;server-di-produzione.com&lt;/code&gt; tramite la porta &lt;code&gt;12345&lt;/code&gt; di &lt;code&gt;localhost&lt;/code&gt;. Capistrano però ha già tutto predisposto: è sufficiente aggiungere la seguente variabile al proprio script di deploy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

set :gateway, &amp;quot;utente@server-gateway.com&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E in automatico creerà il tunnel al vostro posto. Urrà per il comando in meno da digitare!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=PeWacoCuDWI:ntGYTzTf8i0:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=PeWacoCuDWI:ntGYTzTf8i0:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/Ohnx9-k_Ga4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/capistrano-e-i-tunnel-ssh.html</feedburner:origLink></entry>
  <entry>
    <title>mogenerator: mai più rigenerare le classi dei modelli Core Data manualmente</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/Tw67xbNDR8Y/mogenerator-mai-pi-rigenerare-le-classi-dei-modelli-core-data-manualmente.html" rel="alternate" />
    <id>/blog/2011/05/mogenerator-mai-pi-rigenerare-le-classi-dei-modelli-core-data-manualmente.html</id>
    <published>2011-05-03T00:00:00+02:00</published>
    <updated>2011-05-03T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mogenerator&lt;/code&gt; is a command-line tool that, given an &lt;code&gt;.xcdatamodel&lt;/code&gt; file, will generate two classes per entity. The first class, &lt;code&gt;_MyEntity&lt;/code&gt;, is intended solely for machine consumption and will be continuously overwritten to stay in sync with your data model. The second class, &lt;code&gt;MyEntity&lt;/code&gt;, subclasses &lt;code&gt;_MyEntity&lt;/code&gt;, won&amp;#39;t ever be overwritten and is a great place to put your custom logic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dio, grazie.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mogenerator&lt;/code&gt; is a command-line tool that, given an &lt;code&gt;.xcdatamodel&lt;/code&gt; file, will generate two classes per entity. The first class, &lt;code&gt;_MyEntity&lt;/code&gt;, is intended solely for machine consumption and will be continuously overwritten to stay in sync with your data model. The second class, &lt;code&gt;MyEntity&lt;/code&gt;, subclasses &lt;code&gt;_MyEntity&lt;/code&gt;, won&amp;#39;t ever be overwritten and is a great place to put your custom logic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dio, grazie.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=WiQ2rGqKn3A:QcBSGhtji_s:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=WiQ2rGqKn3A:QcBSGhtji_s:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/Tw67xbNDR8Y" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/mogenerator-mai-pi-rigenerare-le-classi-dei-modelli-core-data-manualmente.html</feedburner:origLink></entry>
  <entry>
    <title>Slow time</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/DEAKlztNTo0/slow-time.html" rel="alternate" />
    <id>/blog/2011/05/slow-time.html</id>
    <published>2011-05-02T00:00:00+02:00</published>
    <updated>2011-05-02T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Communication doesn’t always have to be in real time. It can be in what we call &amp;quot;slow time&amp;quot;. You can post something and three hours later someone can get back to you and then four hours later someone else can get back to you. And everything will work out just fine.
Slow time is &amp;quot;Maybe it takes two or three days to have this conversation. And we do it over periods of 15 minutes here, two minutes there, four minutes there.&amp;quot; And that’s fine. It doesn’t need to happen all at once. Unless it’s really, incredibly, truly urgent. (Which most things aren’t. They’re made out to be that way, but they really aren’t that important.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Condivido totalmente.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Communication doesn’t always have to be in real time. It can be in what we call &amp;quot;slow time&amp;quot;. You can post something and three hours later someone can get back to you and then four hours later someone else can get back to you. And everything will work out just fine.
Slow time is &amp;quot;Maybe it takes two or three days to have this conversation. And we do it over periods of 15 minutes here, two minutes there, four minutes there.&amp;quot; And that’s fine. It doesn’t need to happen all at once. Unless it’s really, incredibly, truly urgent. (Which most things aren’t. They’re made out to be that way, but they really aren’t that important.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Condivido totalmente.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=tqWprwHLlgs:tnH6ceQKprM:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=tqWprwHLlgs:tnH6ceQKprM:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/DEAKlztNTo0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/slow-time.html</feedburner:origLink></entry>
  <entry>
    <title>hookup</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/72yLzD7t97I/hookup.html" rel="alternate" />
    <id>/blog/2011/05/hookup.html</id>
    <published>2011-05-02T00:00:00+02:00</published>
    <updated>2011-05-02T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Il solito Tim Pope con una nuova gemma che sfrutta gli hook Git per chiamare automaticamente &lt;code&gt;bundle install&lt;/code&gt; e &lt;code&gt;rake db:migrate&lt;/code&gt; non appena ce n&amp;#39;è bisogno.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Il solito Tim Pope con una nuova gemma che sfrutta gli hook Git per chiamare automaticamente &lt;code&gt;bundle install&lt;/code&gt; e &lt;code&gt;rake db:migrate&lt;/code&gt; non appena ce n&amp;#39;è bisogno.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=jgAYf2PoTIE:FxiMxMtx56E:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=jgAYf2PoTIE:FxiMxMtx56E:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/72yLzD7t97I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/05/hookup.html</feedburner:origLink></entry>
  <entry>
    <title>ScottBot: a "that's what she said" bot</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/4Sn3nSZVYZo/scottbot-a-that-s-what-she-said-bot.html" rel="alternate" />
    <id>/blog/2011/04/scottbot-a-that-s-what-she-said-bot.html</id>
    <published>2011-04-29T00:00:00+02:00</published>
    <updated>2011-04-29T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;To get scottbot started, I put it into an otherwise empty IRC channel and just fed it a few &amp;quot;funny&amp;quot; and &amp;quot;notfunny&amp;quot; messages, giving it feedback on each one. Within a few dozen it was getting pretty good at this. Which, coincidentally, is what she said.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Semplicemente geniale. Per un utilizzo più generico, senza necessità di training, affidatevi invece al filtro bayesiano &lt;a href="https://github.com/bvandenbos/twss"&gt;TWSS&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
&amp;gt; TWSS(&amp;quot;you&amp;#39;re not going fast enough&amp;quot;)
true
&lt;/code&gt;&lt;/pre&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;To get scottbot started, I put it into an otherwise empty IRC channel and just fed it a few &amp;quot;funny&amp;quot; and &amp;quot;notfunny&amp;quot; messages, giving it feedback on each one. Within a few dozen it was getting pretty good at this. Which, coincidentally, is what she said.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Semplicemente geniale. Per un utilizzo più generico, senza necessità di training, affidatevi invece al filtro bayesiano &lt;a href="https://github.com/bvandenbos/twss"&gt;TWSS&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
&amp;gt; TWSS(&amp;quot;you&amp;#39;re not going fast enough&amp;quot;)
true
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=Xotm5UVnxeo:zdG7zp8ZDVo:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=Xotm5UVnxeo:zdG7zp8ZDVo:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/4Sn3nSZVYZo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/scottbot-a-that-s-what-she-said-bot.html</feedburner:origLink></entry>
  <entry>
    <title>39 differenti ombre CSS3-only</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/hUEQSvGg7F8/39-differenti-ombre-css3-only.html" rel="alternate" />
    <id>/blog/2011/04/39-differenti-ombre-css3-only.html</id>
    <published>2011-04-23T00:00:00+02:00</published>
    <updated>2011-04-23T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Impressionante ed originalissimo lavoro di Doug Avery, visibile solo su Chrome. Alcuni spunti sono totalmente inediti.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Impressionante ed originalissimo lavoro di Doug Avery, visibile solo su Chrome. Alcuni spunti sono totalmente inediti.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=p96zudy3-64:r-k60RknUAQ:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=p96zudy3-64:r-k60RknUAQ:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/hUEQSvGg7F8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/39-differenti-ombre-css3-only.html</feedburner:origLink></entry>
  <entry>
    <title>Sinatra: un repository viewer web-based in.. 50 righe</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/aMLxxJhAuuk/sinatra-un-repository-viewer-web-based-in-50-righe.html" rel="alternate" />
    <id>/blog/2011/04/sinatra-un-repository-viewer-web-based-in-50-righe.html</id>
    <published>2011-04-21T00:00:00+02:00</published>
    <updated>2011-04-21T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Sinatra, per piccole webapp, è un progetto semplicemente incredibile. Riesce a condensare dentro ad un DSL composto da pochissimi comandi gran parte delle features presenti in web framework ben più complessi e pesanti.&lt;/p&gt;

&lt;p&gt;In &lt;a href="http://www.welaika.com"&gt;weLaika&lt;/a&gt; stiamo lavorando ad una Intranet scritta in Ruby per una celebre università inglese: tra le specifiche compare anche una sezione nella quale sia possibile per i dipendenti poter navigare tra i vari commit di un repository SVN e, per ogni commit, poter visualizzare l&amp;#39;alberatura delle directory, ovviamente con la possibilità di poter scaricare i file presenti nelle varie loro versioni. Un repo viewer web-based, insomma. Il primo pensiero è stato: non dovremmo metterci ad integrare Ruby con WebSVN, vero?&lt;/p&gt;

&lt;p&gt;Dopo una veloce ricerca, ho avuto la conferma di quanto SVN non sia esattamente il revision-system preferito dal popolo rubista. Poche gemme, poco utilizzate e documentate. Alchè mi sono ricordato di una interessante feature di Git (mai utilizzata dal sottoscritto fino ad ora): la possibilità di gestire localmente un repository SVN, via Git. E&amp;#39; infatti sufficiente effettuare un &lt;code&gt;git clone&lt;/code&gt; leggermente diverso:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git svn clone http://ie7-js.googlecode.com/svn ie7-js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per poter avere in locale un repo Git, in grado di committare (&lt;code&gt;git dcommit&lt;/code&gt;) e updatare dal repository SVN originario (&lt;code&gt;git rebase&lt;/code&gt;), in maniera del tutto trasparente. Non male! Ma a questo punto le possibilità di implementazione per il nostro repo-viewer si allargano incredibilmente. Siamo infatti in grado di poter usare una delle tante gemme in grado di interfacciarsi con repository Git: &lt;code&gt;Grit&lt;/code&gt;, per esempio, &lt;a href="https://github.com/schacon/grit"&gt;usata attualmente su Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ecco allora la sfida: siamo in grado di scrivere un clone di WebSVN in Sinatra, in meno di 50 righe di codice (viste comprese)? Ovvio che sì! Qui in basso avete un applicazione funzionante, in grado di soddisfare tutti i requisiti sopra elencati.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


%w(sinatra grit).each { |gem| require gem }

mime_type :binary, &amp;#39;binary/octet-stream&amp;#39;
set :repo, Grit::Repo.new(&amp;#39;/Users/steffoz/dev/richcomments&amp;#39;)

before %r{^/(\w+)} do
  commit_id = params[:captures].first[0..10]
  @commit = settings.repo.commits(commit_id).first
  halt &amp;quot;No commit exists with id #{commit_id}&amp;quot; if @commit.nil?
end

get &amp;quot;/&amp;quot; do
  @commits = settings.repo.commits
  haml :index
end

get &amp;quot;/:commit_id&amp;quot; do |commit_id|
  @tree = @commit.tree
  @path = &amp;quot;&amp;quot;
  haml :dir
end

get &amp;quot;/:commit_id/*&amp;quot; do |commit_id, path|
  @object = @commit.tree / path
  halt &amp;quot;No object exists with path #{path}&amp;quot; if @object.nil?
  if @object.is_a? Grit::Blob
    content_type :binary
    @object.data
  else
    @tree = @object
    @path = path + &amp;quot;/&amp;quot;
    haml :dir
  end
end

__END__

@@ index
%ul
  - @commits.each do |commit|
    %li
      %a{ :href =&amp;gt; &amp;quot;/#{commit.id[0..10]}&amp;quot; }= &amp;quot;#{commit.id[0..10]} (by #{commit.author}, #{commit.committed_date})&amp;quot;

@@ dir
%h1= &amp;quot;Commit #{@commit.id[0..10]} - Path: #{@path}&amp;quot;
%ul
  - @tree.contents.each do |obj|
    %li
      %a{ :href =&amp;gt; &amp;quot;/#{@commit.id}/#{@path}#{obj.name}&amp;quot; }= obj.name
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se poi consideriamo &lt;a href="https://github.com/rkh/almost-sinatra"&gt;Almost Sinatra&lt;/a&gt;, che riesce a riscrivere le 5700 righe di Sinatra in 8 righe (!!!).. capite che la cosa inizia a diventare piuttosto esilarante. Se aveste problemi a comprendere lo script, son qui.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Sinatra, per piccole webapp, è un progetto semplicemente incredibile. Riesce a condensare dentro ad un DSL composto da pochissimi comandi gran parte delle features presenti in web framework ben più complessi e pesanti.&lt;/p&gt;

&lt;p&gt;In &lt;a href="http://www.welaika.com"&gt;weLaika&lt;/a&gt; stiamo lavorando ad una Intranet scritta in Ruby per una celebre università inglese: tra le specifiche compare anche una sezione nella quale sia possibile per i dipendenti poter navigare tra i vari commit di un repository SVN e, per ogni commit, poter visualizzare l&amp;#39;alberatura delle directory, ovviamente con la possibilità di poter scaricare i file presenti nelle varie loro versioni. Un repo viewer web-based, insomma. Il primo pensiero è stato: non dovremmo metterci ad integrare Ruby con WebSVN, vero?&lt;/p&gt;

&lt;p&gt;Dopo una veloce ricerca, ho avuto la conferma di quanto SVN non sia esattamente il revision-system preferito dal popolo rubista. Poche gemme, poco utilizzate e documentate. Alchè mi sono ricordato di una interessante feature di Git (mai utilizzata dal sottoscritto fino ad ora): la possibilità di gestire localmente un repository SVN, via Git. E&amp;#39; infatti sufficiente effettuare un &lt;code&gt;git clone&lt;/code&gt; leggermente diverso:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git svn clone http://ie7-js.googlecode.com/svn ie7-js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per poter avere in locale un repo Git, in grado di committare (&lt;code&gt;git dcommit&lt;/code&gt;) e updatare dal repository SVN originario (&lt;code&gt;git rebase&lt;/code&gt;), in maniera del tutto trasparente. Non male! Ma a questo punto le possibilità di implementazione per il nostro repo-viewer si allargano incredibilmente. Siamo infatti in grado di poter usare una delle tante gemme in grado di interfacciarsi con repository Git: &lt;code&gt;Grit&lt;/code&gt;, per esempio, &lt;a href="https://github.com/schacon/grit"&gt;usata attualmente su Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ecco allora la sfida: siamo in grado di scrivere un clone di WebSVN in Sinatra, in meno di 50 righe di codice (viste comprese)? Ovvio che sì! Qui in basso avete un applicazione funzionante, in grado di soddisfare tutti i requisiti sopra elencati.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


%w(sinatra grit).each { |gem| require gem }

mime_type :binary, &amp;#39;binary/octet-stream&amp;#39;
set :repo, Grit::Repo.new(&amp;#39;/Users/steffoz/dev/richcomments&amp;#39;)

before %r{^/(\w+)} do
  commit_id = params[:captures].first[0..10]
  @commit = settings.repo.commits(commit_id).first
  halt &amp;quot;No commit exists with id #{commit_id}&amp;quot; if @commit.nil?
end

get &amp;quot;/&amp;quot; do
  @commits = settings.repo.commits
  haml :index
end

get &amp;quot;/:commit_id&amp;quot; do |commit_id|
  @tree = @commit.tree
  @path = &amp;quot;&amp;quot;
  haml :dir
end

get &amp;quot;/:commit_id/*&amp;quot; do |commit_id, path|
  @object = @commit.tree / path
  halt &amp;quot;No object exists with path #{path}&amp;quot; if @object.nil?
  if @object.is_a? Grit::Blob
    content_type :binary
    @object.data
  else
    @tree = @object
    @path = path + &amp;quot;/&amp;quot;
    haml :dir
  end
end

__END__

@@ index
%ul
  - @commits.each do |commit|
    %li
      %a{ :href =&amp;gt; &amp;quot;/#{commit.id[0..10]}&amp;quot; }= &amp;quot;#{commit.id[0..10]} (by #{commit.author}, #{commit.committed_date})&amp;quot;

@@ dir
%h1= &amp;quot;Commit #{@commit.id[0..10]} - Path: #{@path}&amp;quot;
%ul
  - @tree.contents.each do |obj|
    %li
      %a{ :href =&amp;gt; &amp;quot;/#{@commit.id}/#{@path}#{obj.name}&amp;quot; }= obj.name
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Se poi consideriamo &lt;a href="https://github.com/rkh/almost-sinatra"&gt;Almost Sinatra&lt;/a&gt;, che riesce a riscrivere le 5700 righe di Sinatra in 8 righe (!!!).. capite che la cosa inizia a diventare piuttosto esilarante. Se aveste problemi a comprendere lo script, son qui.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=-FzwPisUHu0:osHn2orI0wU:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=-FzwPisUHu0:osHn2orI0wU:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/aMLxxJhAuuk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/sinatra-un-repository-viewer-web-based-in-50-righe.html</feedburner:origLink></entry>
  <entry>
    <title>CSS3 Github buttons</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/JSu40dETXeE/css3-github-buttons.html" rel="alternate" />
    <id>/blog/2011/04/css3-github-buttons.html</id>
    <published>2011-04-20T00:00:00+02:00</published>
    <updated>2011-04-20T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Belli, stilizzati con cura e semplicità. Disponibili anche tramite &lt;a href="https://github.com/thetron/css3buttons_rails_helpers"&gt;css3buttons&lt;em&gt;rails&lt;/em&gt;helpers&lt;/a&gt;, se siete quei tipi di persone che aggiungono una gemma per degli stili CSS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; per parcondicio, ecco i &lt;a href="http://nicolasgallagher.com/lab/css3-facebook-buttons/"&gt;bottoni stile Facebook&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Belli, stilizzati con cura e semplicità. Disponibili anche tramite &lt;a href="https://github.com/thetron/css3buttons_rails_helpers"&gt;css3buttons&lt;em&gt;rails&lt;/em&gt;helpers&lt;/a&gt;, se siete quei tipi di persone che aggiungono una gemma per degli stili CSS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; per parcondicio, ecco i &lt;a href="http://nicolasgallagher.com/lab/css3-facebook-buttons/"&gt;bottoni stile Facebook&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=IckGAkEfoc0:ih2K1j1_Q9Y:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=IckGAkEfoc0:ih2K1j1_Q9Y:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/JSu40dETXeE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/css3-github-buttons.html</feedburner:origLink></entry>
  <entry>
    <title>Icone social CSS-only</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/Vx2LfAGRoMQ/icone-social-css-only.html" rel="alternate" />
    <id>/blog/2011/04/icone-social-css-only.html</id>
    <published>2011-04-20T00:00:00+02:00</published>
    <updated>2011-04-20T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Notevole esempio di cosa siamo già in grado di fare solo con del buon CSS e browser decenti (l&amp;#39;icona del feed che vedete in sidebar non è un&amp;#39;immagine). Nicolas Gallagher degenera poi nell&amp;#39;assurdo con la sua &lt;a href="http://nicolasgallagher.com/pure-css-gui-icons/demo/"&gt;libreria di 84 icone vettoriali CSS-only&lt;/a&gt;. Chapeau.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Notevole esempio di cosa siamo già in grado di fare solo con del buon CSS e browser decenti (l&amp;#39;icona del feed che vedete in sidebar non è un&amp;#39;immagine). Nicolas Gallagher degenera poi nell&amp;#39;assurdo con la sua &lt;a href="http://nicolasgallagher.com/pure-css-gui-icons/demo/"&gt;libreria di 84 icone vettoriali CSS-only&lt;/a&gt;. Chapeau.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=rhbNM6vEMJw:kPyQWVG0UaY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=rhbNM6vEMJw:kPyQWVG0UaY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/Vx2LfAGRoMQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/icone-social-css-only.html</feedburner:origLink></entry>
  <entry>
    <title>Pattener: generatore di sfondi via SASS</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/sTGoYxMtht4/pattener-generatore-di-sfondi-via-sass.html" rel="alternate" />
    <id>/blog/2011/04/pattener-generatore-di-sfondi-via-sass.html</id>
    <published>2011-04-20T00:00:00+02:00</published>
    <updated>2011-04-20T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;L&amp;#39;idea è interessante (qui ci sono degli esempi dei pattern generabili ad oggi). Spesso e volentieri mi trovo a dover generare sfondi noised, un&amp;#39;approccio del genere sarebbe perfetto. Certo, ci fossero dei pattern decenti -- gradienti, o noise, appunto -- sarebbe pure meglio. Ma si può sempre forkare.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;L&amp;#39;idea è interessante (qui ci sono degli esempi dei pattern generabili ad oggi). Spesso e volentieri mi trovo a dover generare sfondi noised, un&amp;#39;approccio del genere sarebbe perfetto. Certo, ci fossero dei pattern decenti -- gradienti, o noise, appunto -- sarebbe pure meglio. Ma si può sempre forkare.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=dw3QkV1J4qs:5lANgbw9tXc:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=dw3QkV1J4qs:5lANgbw9tXc:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/sTGoYxMtht4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/pattener-generatore-di-sfondi-via-sass.html</feedburner:origLink></entry>
  <entry>
    <title>Ruby: i tanti usi dei moduli</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/xdjodeH-u1M/ruby-i-tanti-usi-dei-moduli.html" rel="alternate" />
    <id>/blog/2011/04/ruby-i-tanti-usi-dei-moduli.html</id>
    <published>2011-04-17T00:00:00+02:00</published>
    <updated>2011-04-17T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Iper-dettagliato articolo in 4 parti di Gregory Brown sui moduli Ruby e i suoi molteplici utilizzi: namespacing, mixins, etc.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Iper-dettagliato articolo in 4 parti di Gregory Brown sui moduli Ruby e i suoi molteplici utilizzi: namespacing, mixins, etc.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=EuLjueZ34Zs:Xdo42l-p3IU:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=EuLjueZ34Zs:Xdo42l-p3IU:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/xdjodeH-u1M" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/ruby-i-tanti-usi-dei-moduli.html</feedburner:origLink></entry>
  <entry>
    <title>Nanoc, questo sito e prodotti derivati vari</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/MEuG3RLoc3g/nanoc-questo-sito-e-prodotti-derivati-vari.html" rel="alternate" />
    <id>/blog/2011/04/nanoc-questo-sito-e-prodotti-derivati-vari.html</id>
    <published>2011-04-17T00:00:00+02:00</published>
    <updated>2011-04-17T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Per un sito estremamente nerd, ci vuole una piattaforma di pubblicazione estremamente nerd, mi sembra evidente. I miei bisogni erano fondamentalmente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapidità nella scrittura;&lt;/li&gt;
&lt;li&gt;Sintassi Markdown;&lt;/li&gt;
&lt;li&gt;Possibilità di scrivere sia post &amp;quot;standard&amp;quot; che semplici link verso risorse esterne;&lt;/li&gt;
&lt;li&gt;Codice pulito in uscita (HTML5);&lt;/li&gt;
&lt;li&gt;Supporto per &lt;code&gt;Compass&lt;/code&gt;, &lt;code&gt;SASS&lt;/code&gt; e &lt;code&gt;Slim&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Compressione automatica per gli assets Javascript;&lt;/li&gt;
&lt;li&gt;Evitare totalmente qualsiasi problema di mantenimento della piattaforma (il tempo lo voglio dedicare per scrivere, grazie);&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mi è sembrato naturale dirigermi verso i vari &lt;a href="http://ruby-toolbox.com/categories/static_website_generation.html"&gt;progetti di generazione statica di siti web  &lt;/a&gt;. Con questi tools, i post possono essere scritti su semplici file Markdown e backuppati su Github. Non ci sono security holes da gestire, nessun login/logout. Il deploy equivale semplicemente a lanciare un &lt;code&gt;rsync&lt;/code&gt; remoto.&lt;/p&gt;

&lt;p&gt;Fidandomi tendenzialmente del popolo di Github sono andato diretto su &lt;a href="https://github.com/mojombo/jekyll/wiki"&gt;Jekyll&lt;/a&gt;, il pioniere nel campo. Son partito con entusiasmo, ma purtroppo non è andata come speravo: sebbene scritto bene, non è modulare a sufficienza per permettere troppe personalizzazioni. Avrei potuto forkare Jeckyll e modificarlo, ma non desiderando futuri problemi di mantenimento sono andato alla ricerca di alternative.&lt;/p&gt;

&lt;p&gt;Alla fine sono approdato su &lt;a href="http://nanoc.stoneship.org/"&gt;Nanoc&lt;/a&gt;, un progetto non così popolare, ma a mio parere decisamente valido, sicuramente molto più modulare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permette di creare i propri filtri di conversione da file testuale a codice HTML, con possibilità di concatenazione multipla;&lt;/li&gt;
&lt;li&gt;Permette di creare moduli helpers aggiuntivi per rendere più DRY le viste;&lt;/li&gt;
&lt;li&gt;Permette di implementare anche dei &lt;a href="http://projects.stoneship.org/trac/nanoc/wiki/DataSources"&gt;Data Sources alternativi&lt;/a&gt;: questo vuol dire che i post possono essere salvati su file, ma non necessariamente. E&amp;#39; ad esempio possibile creare post da Twitter, Delicious o LastFM in poche righe di codice.&lt;/li&gt;
&lt;li&gt;Permette di realizzare diverse rappresentazioni dello stesso post (i.e. JSON, XML ed HTML)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con un&amp;#39;architettura così ben pensata, è stato sufficiente creare un paio di moduli aggiuntivi per arrivare esattamente ai miei bisogni. In particolare, ho scritto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un filtro Nanoc per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/slim.rb"&gt;tradurre i file Slim&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Un filtro Nanoc per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/yui.rb"&gt;comprimere i file Javascript con YUI&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Un filtro Nanoc che &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/email_filter.rb"&gt;va in cerca di eventuali indirizzi mail e li encripta&lt;/a&gt; (date un&amp;#39;occhio alla mia mail nella pagina &amp;quot;Chi Sono&amp;quot;);&lt;/li&gt;
&lt;li&gt;Un task Rake per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/Rakefile"&gt;creare rapidamente lo scheletro per nuovi post ed aprire direttamente il file su Textmate&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il codice sorgente del sito è &lt;a href="https://github.com/stefanoverna/stefanoverna.com"&gt;disponibile su Github&lt;/a&gt;.. è immediato vedere che impostazione pulita riesce a regalare Nanoc.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Per un sito estremamente nerd, ci vuole una piattaforma di pubblicazione estremamente nerd, mi sembra evidente. I miei bisogni erano fondamentalmente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapidità nella scrittura;&lt;/li&gt;
&lt;li&gt;Sintassi Markdown;&lt;/li&gt;
&lt;li&gt;Possibilità di scrivere sia post &amp;quot;standard&amp;quot; che semplici link verso risorse esterne;&lt;/li&gt;
&lt;li&gt;Codice pulito in uscita (HTML5);&lt;/li&gt;
&lt;li&gt;Supporto per &lt;code&gt;Compass&lt;/code&gt;, &lt;code&gt;SASS&lt;/code&gt; e &lt;code&gt;Slim&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Compressione automatica per gli assets Javascript;&lt;/li&gt;
&lt;li&gt;Evitare totalmente qualsiasi problema di mantenimento della piattaforma (il tempo lo voglio dedicare per scrivere, grazie);&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mi è sembrato naturale dirigermi verso i vari &lt;a href="http://ruby-toolbox.com/categories/static_website_generation.html"&gt;progetti di generazione statica di siti web  &lt;/a&gt;. Con questi tools, i post possono essere scritti su semplici file Markdown e backuppati su Github. Non ci sono security holes da gestire, nessun login/logout. Il deploy equivale semplicemente a lanciare un &lt;code&gt;rsync&lt;/code&gt; remoto.&lt;/p&gt;

&lt;p&gt;Fidandomi tendenzialmente del popolo di Github sono andato diretto su &lt;a href="https://github.com/mojombo/jekyll/wiki"&gt;Jekyll&lt;/a&gt;, il pioniere nel campo. Son partito con entusiasmo, ma purtroppo non è andata come speravo: sebbene scritto bene, non è modulare a sufficienza per permettere troppe personalizzazioni. Avrei potuto forkare Jeckyll e modificarlo, ma non desiderando futuri problemi di mantenimento sono andato alla ricerca di alternative.&lt;/p&gt;

&lt;p&gt;Alla fine sono approdato su &lt;a href="http://nanoc.stoneship.org/"&gt;Nanoc&lt;/a&gt;, un progetto non così popolare, ma a mio parere decisamente valido, sicuramente molto più modulare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permette di creare i propri filtri di conversione da file testuale a codice HTML, con possibilità di concatenazione multipla;&lt;/li&gt;
&lt;li&gt;Permette di creare moduli helpers aggiuntivi per rendere più DRY le viste;&lt;/li&gt;
&lt;li&gt;Permette di implementare anche dei &lt;a href="http://projects.stoneship.org/trac/nanoc/wiki/DataSources"&gt;Data Sources alternativi&lt;/a&gt;: questo vuol dire che i post possono essere salvati su file, ma non necessariamente. E&amp;#39; ad esempio possibile creare post da Twitter, Delicious o LastFM in poche righe di codice.&lt;/li&gt;
&lt;li&gt;Permette di realizzare diverse rappresentazioni dello stesso post (i.e. JSON, XML ed HTML)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con un&amp;#39;architettura così ben pensata, è stato sufficiente creare un paio di moduli aggiuntivi per arrivare esattamente ai miei bisogni. In particolare, ho scritto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un filtro Nanoc per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/slim.rb"&gt;tradurre i file Slim&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Un filtro Nanoc per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/yui.rb"&gt;comprimere i file Javascript con YUI&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Un filtro Nanoc che &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/lib/email_filter.rb"&gt;va in cerca di eventuali indirizzi mail e li encripta&lt;/a&gt; (date un&amp;#39;occhio alla mia mail nella pagina &amp;quot;Chi Sono&amp;quot;);&lt;/li&gt;
&lt;li&gt;Un task Rake per &lt;a href="https://github.com/stefanoverna/stefanoverna.com/blob/master/Rakefile"&gt;creare rapidamente lo scheletro per nuovi post ed aprire direttamente il file su Textmate&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il codice sorgente del sito è &lt;a href="https://github.com/stefanoverna/stefanoverna.com"&gt;disponibile su Github&lt;/a&gt;.. è immediato vedere che impostazione pulita riesce a regalare Nanoc.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=jB0Ce3lbePQ:rfK4rq9NXhY:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=jB0Ce3lbePQ:rfK4rq9NXhY:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/MEuG3RLoc3g" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/nanoc-questo-sito-e-prodotti-derivati-vari.html</feedburner:origLink></entry>
  <entry>
    <title>Ruby: variabili di classe ereditarie e class_inheritable_accessor</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/t5g6Y9RrJPU/ruby-class-instance-variables.html" rel="alternate" />
    <id>/blog/2011/04/ruby-class-instance-variables.html</id>
    <published>2011-04-16T00:00:00+02:00</published>
    <updated>2011-04-16T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Oggi sono capitato di fronte ad un pattern interessante, immagino molto frequente nel caso di scrittura di plugin per Rails.&lt;/p&gt;

&lt;p&gt;Immaginiamo, a scopo esemplificativo, di voler scrivere un semplice plugin Rails in grado di permettere questo tipo di chiamata all&amp;#39;interno dei tuoi modelli:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


class Document &amp;lt; ActiveRecord::Base
  special_attribute :title
  special_attribute :description
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;La chiamata a &lt;code&gt;special_attribute&lt;/code&gt; supponiamo faccia miracoli ai due attributi &lt;code&gt;:title&lt;/code&gt; e &lt;code&gt;:description&lt;/code&gt; del modello, ma andiamo oltre. Supponiamo che plugin debba anche essere in grado di restituire tutti gli attributi &amp;quot;speciali&amp;quot; tramite un metodo &lt;code&gt;special_attributes&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

Document.special_attributes
# =&amp;gt; [ :title, :description ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come lo implementereste? Io farei qualcosa del genere:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @@special_attributes ||= []
    @@special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @@special_attributes ||= []
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo modo il risultato effettivamente è quello sperato! Cosa abbiamo fatto? Si è usata una variabile di classe chiamata &lt;code&gt;@@special_attributes&lt;/code&gt; per memorizzare i vari campi.&lt;/p&gt;

&lt;p&gt;Attenzione però, se complichiamo un po&amp;#39; il caso d&amp;#39;uso, ci troviamo di fronte ad comportamento inaspettato. Supponiamo che ci siano delle sottoclassi di &lt;code&gt;Document&lt;/code&gt;, per esempio &lt;code&gt;SpreadsheetDocument&lt;/code&gt; e &lt;code&gt;TextDocument&lt;/code&gt;. Saremmo in questo caso davanti ad una STI (Single Table Inheritance) --- caso tutt&amp;#39;altro che raro nella realtà. Vediamo se tutto funziona ancora come ci aspettiamo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class SpreadsheetDocument &amp;lt; Document
  special_attribute :author
end

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :title, :description, :author ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Fin qui tutto bene! Aggiungiamo &lt;code&gt;TextDocument&lt;/code&gt; ora:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class TextDocument &amp;lt; Document
  special_attribute :page_count
end

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :title, :description, :author, :page_count ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ahia, ecco il problema: &lt;code&gt;:page_count&lt;/code&gt; è ovviamente l&amp;#39;inaspettato intruso, non è un attributo di &lt;code&gt;SpreadsheetDocument&lt;/code&gt;. La spiegazione è semplice: una variabile di classe come &lt;code&gt;@@special_attributes&lt;/code&gt; viene condivisa tra la classe padre e &lt;em&gt;tutte&lt;/em&gt; le classi figlie. Non fa quindi al caso nostro. Proviamo con un&amp;#39;altra feature di Ruby: le &lt;em&gt;variabili di istanza di classe&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ruby è un linguaggio totalmente OOP, dunque tutto è un&amp;#39;oggetto, le classi non fanno eccezione. Possiamo fare in modo di aggiungere una variabile di istanza all&amp;#39;oggetto classe!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @special_attributes ||= []
    @special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @special_attributes ||= []
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come si può notare, abbiamo fatto diventare &lt;code&gt;@special_attributes&lt;/code&gt; una variabile dell&amp;#39;istanza. Ora riproviamo a testarne il funzionamento.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

Document.special_attributes
# =&amp;gt; [ :title, :description ] --&amp;gt; corretto!

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :author ] --&amp;gt; errato...

TextDocument.special_attributes
# =&amp;gt; [ :page_count ] --&amp;gt; errato...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ora effettivamente ognuna delle classi ha la sua variabile, differente da quella delle altre classi, ma le classi figlie non partono col valore della variabile della classe padre, &lt;code&gt;Document&lt;/code&gt;! Sfortunatamente Ruby non possiede nulla semplice per ovviare a questo problema.. ma in qualche modo è comunque possibile arrivare al comportamento desiderato, in poche righe di codice:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @special_attributes ||= []
    @special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @special_attributes ||= []
  end

  def inherited(subclass)
    subclass.instance_variable_set &amp;quot;@special_attributes&amp;quot;, special_attributes.dup
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il giochetto è sfruttare la callback &lt;code&gt;inherited(subclass)&lt;/code&gt;, che viene chiamata quando viene generata una classe figlia. Agganciandoci a quest&amp;#39;evento, siamo in grado di inizializzare la variabile di istanza della classe figlia con quella della classe padre. Attezione però a non passare la medesima variabile al figlio, ma una copia, altrimenti padre e figli condivideranno il medesimo oggetto, con risultati non attesi.&lt;/p&gt;

&lt;p&gt;Spero siate riusciti a seguirmi. Ora, per sentirci tutti meglio, arriviamo allo shortcut.. &lt;code&gt;ActiveSupport&lt;/code&gt; estende di default l&amp;#39;oggetto &lt;code&gt;Class&lt;/code&gt; per supportare il metodo &lt;code&gt;class_inheritable_accessor&lt;/code&gt;. Possiamo riscrivere il plugin in questo modo, ottenendo il medesimo risultato:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    class_inheritable_accessor :special_attributes
    self.special_attributes ||= []
    self.special_attributes &amp;lt;&amp;lt; attribute
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Meglio, no?&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Oggi sono capitato di fronte ad un pattern interessante, immagino molto frequente nel caso di scrittura di plugin per Rails.&lt;/p&gt;

&lt;p&gt;Immaginiamo, a scopo esemplificativo, di voler scrivere un semplice plugin Rails in grado di permettere questo tipo di chiamata all&amp;#39;interno dei tuoi modelli:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


class Document &amp;lt; ActiveRecord::Base
  special_attribute :title
  special_attribute :description
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;La chiamata a &lt;code&gt;special_attribute&lt;/code&gt; supponiamo faccia miracoli ai due attributi &lt;code&gt;:title&lt;/code&gt; e &lt;code&gt;:description&lt;/code&gt; del modello, ma andiamo oltre. Supponiamo che plugin debba anche essere in grado di restituire tutti gli attributi &amp;quot;speciali&amp;quot; tramite un metodo &lt;code&gt;special_attributes&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

Document.special_attributes
# =&amp;gt; [ :title, :description ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come lo implementereste? Io farei qualcosa del genere:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @@special_attributes ||= []
    @@special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @@special_attributes ||= []
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In questo modo il risultato effettivamente è quello sperato! Cosa abbiamo fatto? Si è usata una variabile di classe chiamata &lt;code&gt;@@special_attributes&lt;/code&gt; per memorizzare i vari campi.&lt;/p&gt;

&lt;p&gt;Attenzione però, se complichiamo un po&amp;#39; il caso d&amp;#39;uso, ci troviamo di fronte ad comportamento inaspettato. Supponiamo che ci siano delle sottoclassi di &lt;code&gt;Document&lt;/code&gt;, per esempio &lt;code&gt;SpreadsheetDocument&lt;/code&gt; e &lt;code&gt;TextDocument&lt;/code&gt;. Saremmo in questo caso davanti ad una STI (Single Table Inheritance) --- caso tutt&amp;#39;altro che raro nella realtà. Vediamo se tutto funziona ancora come ci aspettiamo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class SpreadsheetDocument &amp;lt; Document
  special_attribute :author
end

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :title, :description, :author ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Fin qui tutto bene! Aggiungiamo &lt;code&gt;TextDocument&lt;/code&gt; ora:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class TextDocument &amp;lt; Document
  special_attribute :page_count
end

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :title, :description, :author, :page_count ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ahia, ecco il problema: &lt;code&gt;:page_count&lt;/code&gt; è ovviamente l&amp;#39;inaspettato intruso, non è un attributo di &lt;code&gt;SpreadsheetDocument&lt;/code&gt;. La spiegazione è semplice: una variabile di classe come &lt;code&gt;@@special_attributes&lt;/code&gt; viene condivisa tra la classe padre e &lt;em&gt;tutte&lt;/em&gt; le classi figlie. Non fa quindi al caso nostro. Proviamo con un&amp;#39;altra feature di Ruby: le &lt;em&gt;variabili di istanza di classe&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ruby è un linguaggio totalmente OOP, dunque tutto è un&amp;#39;oggetto, le classi non fanno eccezione. Possiamo fare in modo di aggiungere una variabile di istanza all&amp;#39;oggetto classe!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @special_attributes ||= []
    @special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @special_attributes ||= []
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Come si può notare, abbiamo fatto diventare &lt;code&gt;@special_attributes&lt;/code&gt; una variabile dell&amp;#39;istanza. Ora riproviamo a testarne il funzionamento.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

Document.special_attributes
# =&amp;gt; [ :title, :description ] --&amp;gt; corretto!

SpreadsheetDocument.special_attributes
# =&amp;gt; [ :author ] --&amp;gt; errato...

TextDocument.special_attributes
# =&amp;gt; [ :page_count ] --&amp;gt; errato...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ora effettivamente ognuna delle classi ha la sua variabile, differente da quella delle altre classi, ma le classi figlie non partono col valore della variabile della classe padre, &lt;code&gt;Document&lt;/code&gt;! Sfortunatamente Ruby non possiede nulla semplice per ovviare a questo problema.. ma in qualche modo è comunque possibile arrivare al comportamento desiderato, in poche righe di codice:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    @special_attributes ||= []
    @special_attributes &amp;lt;&amp;lt; attribute
  end

  def special_attributes
    @special_attributes ||= []
  end

  def inherited(subclass)
    subclass.instance_variable_set &amp;quot;@special_attributes&amp;quot;, special_attributes.dup
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il giochetto è sfruttare la callback &lt;code&gt;inherited(subclass)&lt;/code&gt;, che viene chiamata quando viene generata una classe figlia. Agganciandoci a quest&amp;#39;evento, siamo in grado di inizializzare la variabile di istanza della classe figlia con quella della classe padre. Attezione però a non passare la medesima variabile al figlio, ma una copia, altrimenti padre e figli condivideranno il medesimo oggetto, con risultati non attesi.&lt;/p&gt;

&lt;p&gt;Spero siate riusciti a seguirmi. Ora, per sentirci tutti meglio, arriviamo allo shortcut.. &lt;code&gt;ActiveSupport&lt;/code&gt; estende di default l&amp;#39;oggetto &lt;code&gt;Class&lt;/code&gt; per supportare il metodo &lt;code&gt;class_inheritable_accessor&lt;/code&gt;. Possiamo riscrivere il plugin in questo modo, ottenendo il medesimo risultato:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;


module SpecialAttributesPlugin

  def special_attribute(attribute)
    class_inheritable_accessor :special_attributes
    self.special_attributes ||= []
    self.special_attributes &amp;lt;&amp;lt; attribute
  end

end

# aggiungo il modulo ad ActiveRecord::Base
module ActiveRecord
  class Base
    extend SpecialAttributesPlugin::ActiveRecordAdapter
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Meglio, no?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=EqB1SutLhfw:ewxwMGDX4pg:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=EqB1SutLhfw:ewxwMGDX4pg:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/t5g6Y9RrJPU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/ruby-class-instance-variables.html</feedburner:origLink></entry>
  <entry>
    <title>Do worry... be happy</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/eI97Z30R_tY/do-worry-be-happy.html" rel="alternate" />
    <id>/blog/2011/04/do-worry-be-happy.html</id>
    <published>2011-04-15T00:00:00+02:00</published>
    <updated>2011-04-15T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Un&amp;#39;ora di lezione su come gestire il proprio business dal CEO bolognese di Balsamiq, Peldi Guillizzoni. Consigliato.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Un&amp;#39;ora di lezione su come gestire il proprio business dal CEO bolognese di Balsamiq, Peldi Guillizzoni. Consigliato.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=Q9dNx3tos80:6qo3_4tZAKE:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=Q9dNx3tos80:6qo3_4tZAKE:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/eI97Z30R_tY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/do-worry-be-happy.html</feedburner:origLink></entry>
  <entry>
    <title>Adapt.js - Adaptive CSS</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/2dSRJAPADTY/adapt-js-adaptive-css.html" rel="alternate" />
    <id>/blog/2011/04/adapt-js-adaptive-css.html</id>
    <published>2011-04-15T00:00:00+02:00</published>
    <updated>2011-04-15T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ennesima proposta per gestire CSS a più resoluzioni, suggerita dei creatori del grid system 960 e composta da un leggero script JS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adapt.js is a lightweight (940 bytes minified) JavaScript file that determines which CSS file to load before the browser renders a page. It is worth noting this is a proposed, not prescribed, approach to a problem with multiple solutions. Other methods include: building a separate site for mobile, or using a media queries to adjust layouts dynamically, with a polyfill for older browser support. Also a factor is how to handle image resolutions.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
    <content type="html">&lt;p&gt;Ennesima proposta per gestire CSS a più resoluzioni, suggerita dei creatori del grid system 960 e composta da un leggero script JS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adapt.js is a lightweight (940 bytes minified) JavaScript file that determines which CSS file to load before the browser renders a page. It is worth noting this is a proposed, not prescribed, approach to a problem with multiple solutions. Other methods include: building a separate site for mobile, or using a media queries to adjust layouts dynamically, with a polyfill for older browser support. Also a factor is how to handle image resolutions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=6u5EemfpFPc:aSa2xS_bLik:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=6u5EemfpFPc:aSa2xS_bLik:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/2dSRJAPADTY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/adapt-js-adaptive-css.html</feedburner:origLink></entry>
  <entry>
    <title>Nuova pipeline per gli assets in Rails 3.1</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/trMgm05tqyk/pipeline-assets-rails-3-1.html" rel="alternate" />
    <id>/blog/2011/04/pipeline-assets-rails-3-1.html</id>
    <published>2011-04-14T00:00:00+02:00</published>
    <updated>2011-04-14T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Rails 3.1 sarà di default abilitato all&amp;#39;uso di &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;Coffeescript&lt;/a&gt; e &lt;a href="http://sass-lang.com/"&gt;SCSS&lt;/a&gt;. Nonostante il &lt;a href="https://github.com/rails/rails/commit/9f09aeb8273177fc2d09ebdafcc76ee8eb56fe33#commitcomment-340720"&gt;trolling che si sta generando su Github&lt;/a&gt;, approvo. In weLaika utilizziamo entrambi gli strumenti in ogni progetto, insieme a &lt;a href="http://slim-lang.com/"&gt;Slim&lt;/a&gt; come templating HTML. Nonostante la solita spocchiosità, mi trovo d&amp;#39;accordo con DHH e i suoi tweet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Real Men Write JavaScript Directly. I recall that being said about the JavaScript frameworks when they emerged. I also love how most of the arguments against CoffeeScript were the same we faced against Ruby back in the day. It&amp;#39;s &amp;quot;cute&amp;quot;, &amp;quot;toy&amp;quot;, &amp;quot;just syntax&amp;quot;. Here&amp;#39;s the Rails gospel: Promote good ideas and technologies. See Ajax, REST, Atom, testing. Rails is a curated set of tech choices.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
    <content type="html">&lt;p&gt;Rails 3.1 sarà di default abilitato all&amp;#39;uso di &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;Coffeescript&lt;/a&gt; e &lt;a href="http://sass-lang.com/"&gt;SCSS&lt;/a&gt;. Nonostante il &lt;a href="https://github.com/rails/rails/commit/9f09aeb8273177fc2d09ebdafcc76ee8eb56fe33#commitcomment-340720"&gt;trolling che si sta generando su Github&lt;/a&gt;, approvo. In weLaika utilizziamo entrambi gli strumenti in ogni progetto, insieme a &lt;a href="http://slim-lang.com/"&gt;Slim&lt;/a&gt; come templating HTML. Nonostante la solita spocchiosità, mi trovo d&amp;#39;accordo con DHH e i suoi tweet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Real Men Write JavaScript Directly. I recall that being said about the JavaScript frameworks when they emerged. I also love how most of the arguments against CoffeeScript were the same we faced against Ruby back in the day. It&amp;#39;s &amp;quot;cute&amp;quot;, &amp;quot;toy&amp;quot;, &amp;quot;just syntax&amp;quot;. Here&amp;#39;s the Rails gospel: Promote good ideas and technologies. See Ajax, REST, Atom, testing. Rails is a curated set of tech choices.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=dA9qsFO3HoQ:3om5IjR9sNs:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=dA9qsFO3HoQ:3om5IjR9sNs:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/trMgm05tqyk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/pipeline-assets-rails-3-1.html</feedburner:origLink></entry>
  <entry>
    <title>Placeholder Services</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/xNix6Pfky4w/placeholder-services.html" rel="alternate" />
    <id>/blog/2011/04/placeholder-services.html</id>
    <published>2011-04-13T00:00:00+02:00</published>
    <updated>2011-04-13T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Se non sono incredibilmente lenti, fanno comodo in fase di prototipazione.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Se non sono incredibilmente lenti, fanno comodo in fase di prototipazione.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=lW6_RJ5_ZJE:hXnALdvwTyA:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=lW6_RJ5_ZJE:hXnALdvwTyA:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/xNix6Pfky4w" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/placeholder-services.html</feedburner:origLink></entry>
  <entry>
    <title>PJAX</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/xGIXUXKlsuQ/pjax.html" rel="alternate" />
    <id>/blog/2011/04/pjax.html</id>
    <published>2011-04-13T00:00:00+02:00</published>
    <updated>2011-04-13T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;PJAX loads HTML from your server into the current page
without a full reload. It&amp;#39;s ajax with real permalinks,
page titles, and a working back button that fully degrades.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Piccola libreria sviluppata dal solito &lt;code&gt;defunkt&lt;/code&gt;. Viene sfruttata per il nuovo Issue manager di Github (e verrà presto propagata in altre aree del sito). Lato server è particolarmente semplice supportarlo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
def my_page
  if request.headers[&amp;#39;X-PJAX&amp;#39;]
    render :layout =&amp;gt; false
  end
end
&lt;/code&gt;&lt;/pre&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;PJAX loads HTML from your server into the current page
without a full reload. It&amp;#39;s ajax with real permalinks,
page titles, and a working back button that fully degrades.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Piccola libreria sviluppata dal solito &lt;code&gt;defunkt&lt;/code&gt;. Viene sfruttata per il nuovo Issue manager di Github (e verrà presto propagata in altre aree del sito). Lato server è particolarmente semplice supportarlo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
def my_page
  if request.headers[&amp;#39;X-PJAX&amp;#39;]
    render :layout =&amp;gt; false
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=kP8lLbuxkwI:Bb2REIsUi1s:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=kP8lLbuxkwI:Bb2REIsUi1s:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/xGIXUXKlsuQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/pjax.html</feedburner:origLink></entry>
  <entry>
    <title>Early Quora Design Notes</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/USUIdb0kD5E/early-quora-design-notes.html" rel="alternate" />
    <id>/blog/2011/04/early-quora-design-notes.html</id>
    <published>2011-04-13T00:00:00+02:00</published>
    <updated>2011-04-13T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Interessante post sulle decisioni di design prese per sviluppare Quora.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Another important decision was to be totally ruthless and make real design decisions. All too often in the past I&amp;#39;d find myself falling for some piece of UI and nurture it at the expense of the surrounding elements. Or worse, I&amp;#39;d try to appease someone who was giving feedback or pushing for specific agendas versus trying to create the best UI. This time, however, I was careful to form as few emotional bonds as necessary and really work on rational arguments for everything I designed. By eliminating all but the most important pieces, those that remained always have some concrete purpose.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
    <content type="html">&lt;p&gt;Interessante post sulle decisioni di design prese per sviluppare Quora.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Another important decision was to be totally ruthless and make real design decisions. All too often in the past I&amp;#39;d find myself falling for some piece of UI and nurture it at the expense of the surrounding elements. Or worse, I&amp;#39;d try to appease someone who was giving feedback or pushing for specific agendas versus trying to create the best UI. This time, however, I was careful to form as few emotional bonds as necessary and really work on rational arguments for everything I designed. By eliminating all but the most important pieces, those that remained always have some concrete purpose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=uBcDfs-stqY:KpCzMxnJ_BI:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=uBcDfs-stqY:KpCzMxnJ_BI:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/USUIdb0kD5E" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/early-quora-design-notes.html</feedburner:origLink></entry>
  <entry>
    <title>Chameleon Project</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/aqMrQxMjeeg/chameleon-project.html" rel="alternate" />
    <id>/blog/2011/04/chameleon-project.html</id>
    <published>2011-04-13T00:00:00+02:00</published>
    <updated>2011-04-13T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;If you&amp;#39;re an iOS developer, you&amp;#39;re already familiar with UIKit, the framework used to create apps for the iPhone, iPod and iPad. Chameleon is a drop in replacement for UIKit that runs on Mac OS X. In many cases, your iOS code doesn&amp;#39;t need to change at all in order to run on a Mac.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Progetto sicuramente con del potenziale. &lt;a href="http://twitterrific.com/"&gt;Twitterrific&lt;/a&gt; è stato sviluppato su questo Framework.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;If you&amp;#39;re an iOS developer, you&amp;#39;re already familiar with UIKit, the framework used to create apps for the iPhone, iPod and iPad. Chameleon is a drop in replacement for UIKit that runs on Mac OS X. In many cases, your iOS code doesn&amp;#39;t need to change at all in order to run on a Mac.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Progetto sicuramente con del potenziale. &lt;a href="http://twitterrific.com/"&gt;Twitterrific&lt;/a&gt; è stato sviluppato su questo Framework.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=M4S3lqWeRUU:ZSwnfQA6NrU:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=M4S3lqWeRUU:ZSwnfQA6NrU:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/aqMrQxMjeeg" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/chameleon-project.html</feedburner:origLink></entry>
  <entry>
    <title>Solarized: colorscheme per il tuo text editor preferito</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/obn4QFWO0IM/solarized-colorscheme-per-il-tuo-editor.html" rel="alternate" />
    <id>/blog/2011/04/solarized-colorscheme-per-il-tuo-editor.html</id>
    <published>2011-04-12T00:00:00+02:00</published>
    <updated>2011-04-12T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Solarized is a sixteen color palette (eight monotones, eight accent colors) designed for use with terminal and gui applications. It has several unique properties. I designed this colorscheme with both precise CIELAB lightness relationships and a refined set of hues based on fixed color wheel relationships.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mai ho visto tanto studi teorici dietro alla scelta dei colori da utilizzare all&amp;#39;interno di un editor di testo. Tendenzialmente preferisco uno sfondo nero e maggiore contrasto, ma proviamo a vedere l&amp;#39;effetto che fa. Disponibile anche per &lt;a href="https://github.com/kennethreitz/solarized/tree/master/textmate-colors-solarized"&gt;Textmate&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Solarized is a sixteen color palette (eight monotones, eight accent colors) designed for use with terminal and gui applications. It has several unique properties. I designed this colorscheme with both precise CIELAB lightness relationships and a refined set of hues based on fixed color wheel relationships.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mai ho visto tanto studi teorici dietro alla scelta dei colori da utilizzare all&amp;#39;interno di un editor di testo. Tendenzialmente preferisco uno sfondo nero e maggiore contrasto, ma proviamo a vedere l&amp;#39;effetto che fa. Disponibile anche per &lt;a href="https://github.com/kennethreitz/solarized/tree/master/textmate-colors-solarized"&gt;Textmate&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=_hqe_qfRVEA:IM5Hl-HGyoA:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=_hqe_qfRVEA:IM5Hl-HGyoA:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/obn4QFWO0IM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/solarized-colorscheme-per-il-tuo-editor.html</feedburner:origLink></entry>
  <entry>
    <title>Cinque lezioni dopo un anno di weLaika</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/z5ip7pI-TiU/5-lezioni-dopo-un-anno-di-welaika.html" rel="alternate" />
    <id>/blog/2011/04/5-lezioni-dopo-un-anno-di-welaika.html</id>
    <published>2011-04-11T00:00:00+02:00</published>
    <updated>2011-04-11T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Ebbene sì, un&amp;#39;anno è già passato. Il 2 Aprile 2010 ore 12.00, sette pischelli --- età media di 24-25 anni --- si riuniscono di fronte ad un notaio per firmare lo statuto ufficiale di &lt;a href="http://welaika.com"&gt;weLaika&lt;/a&gt;; società sognata, pensata e definita durante mesi di focose discussioni e appassionati thread mail.&lt;/p&gt;

&lt;p&gt;Sono stato, insieme a &lt;a href="http://twitter.com/mat_jack1"&gt;Matteo&lt;/a&gt; e &lt;a href="http://twitter.com/Jimmy2K"&gt;Federico&lt;/a&gt;, tra i soci operativi dal primo giorno. Posso dire, con immensa soddisfazione, di essermi riempito le tasche con palate di informazioni su cosa vuol dire lanciare una startup da zero. Zero clienti, zero esperienza di gestione manageriale alle spalle.&lt;/p&gt;

&lt;p&gt;Parecchie cose sono cambiate in un anno. Il numero di lavoratori IT full-time è passato da 3 a 6, più un paio di collaboratori saltuari che ci aiutano all&amp;#39;occorrenza. weLaika ha aperto il suo piccolo &amp;quot;dipartimento&amp;quot; di montaggio e ripresa video. &lt;a href="http://acmosadv.net/"&gt;Acmos Adv&lt;/a&gt;, agenzia pubblicitaria e di comunicazione già presente da qualche anno sulla piazza, è entrata a far parte di weLaika a tutti gli effetti, facendo lievitare il numero di stomaci da sfamare ad un totale di circa 15 e trasformando weLaika in una società multi-core in grado di coprire buona parte delle esigenze di comunicazione (web e non) di qualsiasi azienda.&lt;/p&gt;

&lt;p&gt;Siamo solo all&amp;#39;inizio, e stiamo continuando a sbagliare una valanga di cose, ma qualche consiglio mi sento di poterlo condividere a chi volesse intraprendere la nostra stessa strada.&lt;/p&gt;

&lt;h3&gt;Un passo alla volta, con la dovuta calma&lt;/h3&gt;

&lt;p&gt;Decidi di fondare una startup informatica e il tuo gruppo è composto da 3 ragazzi neo-laureati in Ingegneria Informatica, con una forte esperienza tecnica ma che non hanno idea di cosa voglia dire tenere in piedi una baracca chiamata &amp;quot;Società cooperativa&amp;quot;. Decidi di non voler tirare fuori un soldo, investendo unicamente sulle tue capacità e sul tuo tempo. Ti metti in testa di non voler evadere neanche un&amp;#39;euro (cosa che di per se&amp;#39; sarebbe superflua da dire, ma la realtà ci dice altro).&lt;/p&gt;

&lt;p&gt;È normale che finirai per prenderti un sacco di legnate sui denti per qualche mesetto. Tutto ciò che non ti uccide ti fortifica, per carità, ma se tornassi indietro, probabilmente avrei cercato di bruciare un po&amp;#39; meno tappe tutte insieme. Magari prima una semplice società di persone, o magari più loffi ancora, e preferire delle semplici partite IVA per qualche mese. Meno spese, meno burocrazia, meno regole da seguire, meno cose da imparare tutte insieme.&lt;/p&gt;

&lt;p&gt;C&amp;#39;è tutto un mondo di complicazioni là fuori che se sei un nerd con spirito ottimista e sognatore non ti immagini minimamente. Occhio a non tenerne conto, ci si fa male.&lt;/p&gt;

&lt;h3&gt;Intuisci i tuoi punti di forza&lt;/h3&gt;

&lt;p&gt;Sembra banale dirlo, ma se c&amp;#39;è un punto dopo il quale senti che tutto sta prendendo la piega giusta, è quello in cui capisci quanto vali e quali sono le carte che puoi spendere con i tuoi clienti. Putroppo, sarà molto difficile capirlo a tavolino. Dovrai incontrare qualcuno che molto semplicemente ti apra gli occhi, e te lo faccia notare. Ad ogni modo, sforzati ad intuire il prima possibile il perchè susciti curiosità/interesse, e quali sono stati i motivi per i quali un cliente abbia scelto proprio te.&lt;/p&gt;

&lt;p&gt;weLaika vive di un continuo lavoro di formazione e ricerca su tutte le tecnologie web in grado di portare al minimo tempi di sviluppo e al massimo la produttività delle proprie risorse. Il nostro modus operandi si basa sulla scelta di pochi strumenti di lavoro predefiniti per tutto il team: Wordpress per siti &amp;quot;semplici&amp;quot;, Rails per siti più complessi/gestionali/eshop, Sencha e Phonegap per applicazioni mobile cross-device, Websockets per funzionalità chat e push. Se ci si trova davanti a qualcosa di non risolvibile con gli strumenti a disposizione, si pensa sul lungo periodo, valutando con attenzione le alternative e cercando di rendere quanto più riutilizzabile e documentato il proprio lavoro. Periodicamente si rimettono in discussione le varie scelte fatte, per verificare se continuino ad essere le migliori.&lt;/p&gt;

&lt;p&gt;Col passare dei mesi, ci siamo resi conto che con le nostre scelte operative eravamo in grado di offrire tempi e costi di sviluppo dimezzati rispetto ai nostri concorrenti, con qualità spesso e volentieri superiore. Capire il mercato nel quale si opera ed essere sicuri dei propri vantaggi permette una comunicazione col potenziale cliente meno titubante, più convincente e con percentuali di successo più alte.&lt;/p&gt;

&lt;h3&gt;Pretendi un contratto&lt;/h3&gt;

&lt;p&gt;Sembrerà scontato dirlo, ma perfavore, anche se sei agli inizi e non vedi l&amp;#39;ora di iniziare a dimostrare a tutti quanto vali, esigi la firma di un contratto prima di incominciare a lavorare. Non importa se hai davanti la Nike, il Comune di Roma o una piccola azienda familiare. Non fare mai l&amp;#39;errore di cedere su questo punto, non abboccare ai vari &amp;quot;non ti preoccupare, puoi fidarti&amp;quot;. E&amp;#39; un rischio enorme, per te e per i tuoi clienti. Tra professionisti non ci deve essere bisogno di doversi fidare, è d&amp;#39;obbligo pretendere sicurezze, ricambiando con la stessa moneta.&lt;/p&gt;

&lt;p&gt;E&amp;#39; fondamentale chiarire con precisione fino a dove arrivano i diritti e doveri di entrambe le parti, perchè anche con la buona fede più sincera è probabile che verbalmente non ci si stia intendendo &lt;em&gt;sul serio&lt;/em&gt;, o che le cose cambino nel giro di qualche mese, a lavori iniziati. Non importa se il contratto sarà scritto in una forma non propriamente ineccepibile o magari presenterà qualche lacuna. I successivi saranno migliori. Ma avere un brutto contratto è meglio che non averne nessuno.&lt;/p&gt;

&lt;h3&gt;Specifiche, specifiche, specifiche&lt;/h3&gt;

&lt;p&gt;In ogni lavoro che prendete in carico, aggiungete una prima fase (pagata) di mockup e/o definizione delle specifiche del progetto. E&amp;#39; fondamentale, prima di iniziare a lavorare, avere la certezza che voi e il cliente abbiate in mente il medesimo risultato finale. Cose che per voi sono ovvie potrebbero non esserlo altrettanto per il vostro interlocutore, e viceversa. L&amp;#39;unico modo per capirsi con certezza è quello di sviscerare ogni blocco funzionale con quanto più dettaglio e realismo possibile.&lt;/p&gt;

&lt;p&gt;So cosa state pensando, ma vi fermo subito. Si tratta nel 90% dei casi di tempo assolutamente non perso. Definire i progetti con un simile livello di dettaglio ci aiuta in primo luogo ad arrivare ad un preventivo dei tempi di sviluppo più realistico, con minori possibilità di firmare contratti con costi sotto-stimati, e in secondo luogo a scoprire con un certo anticipo tutta quella serie di &amp;quot;casi particolari&amp;quot; ed eccezioni che spesso spuntano fuori nel peggiore dei momenti costringendoti a corse contro il tempo.&lt;/p&gt;

&lt;h3&gt;Fai sentire la tua voce&lt;/h3&gt;

&lt;p&gt;Essere bravi è importante, ma farlo sapere a tutti è fondamentale. L&amp;#39;ingegnere informatico non è tipicamente la figura più socievole, comunicativa e propensa all&amp;#39;auto-celebrazione che esista. Spesso non ha idea di quanto valga il proprio lavoro, e quando tenta di esporre qualcosa al mondo lo fa solitamente con estrema goffaggine, controvoglia e utilizzando terminologie poco comprensibili ed attraenti. Questo è male, e trattare con noncuranza i problemi di comunicazione verso l&amp;#39;esterno può essere potenzialmente catastrofico.&lt;/p&gt;

&lt;p&gt;In weLaika partiamo da questo punto di vista sicuramente svantaggiati, ed è per questo che gli sforzi devono essere maggiori rispetto alla norma. Abbiamo partecipato al &lt;a href="http://railsrumble.com"&gt;Rails Rumble 2010&lt;/a&gt; -- una gara mondiale di sviluppo di applicazioni web in 48 ore -- proponendo &lt;a href="http://boarrd.com/"&gt;Boarrd&lt;/a&gt;. Abbiamo ottenuto una buona copertura mediatica e siamo addirittura riusciti a convincere un&amp;#39;ottimo studente USA a passare da noi l&amp;#39;estate per  un&amp;#39;internship aziendale. Abbiamo trasformato i migliori framework e tool sviluppati internamente in progetti open source su Github (&lt;a href="http://rubygems.org/gems/incurve"&gt;incurve&lt;/a&gt;, &lt;a href="https://github.com/welaika/wp_sass_haml"&gt;wp_sass_haml&lt;/a&gt;, &lt;a href="https://github.com/endorama/rdiff-backup-BASH-script"&gt;rdiff-backup&lt;/a&gt;, &lt;a href="https://github.com/welaika/WETableViewController"&gt;WETableViewController&lt;/a&gt;) e abbiamo contribuito attivamente al miglioramento di progetti già esistenti (&lt;a href="http://socky.org/"&gt;socky&lt;/a&gt;, &lt;a href="https://github.com/welaika/guard-compass"&gt;guard-compass&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Ma possiamo fare di più. Una maggiore cura nel copyrighting del sito aziendale, una maggiore partecipazione a conferenze legate ai temi chiave di weLaika, ed un uso di twitter e blog costante saranno sicuramente i punti sui quali concentrarsi nel prossimo anno.&lt;/p&gt;

&lt;h3&gt;Siete arrivati fino a qui? Sul serio?&lt;/h3&gt;

&lt;p&gt;Grazie per avere condiviso con me questo viaggio nel tempo.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Ebbene sì, un&amp;#39;anno è già passato. Il 2 Aprile 2010 ore 12.00, sette pischelli --- età media di 24-25 anni --- si riuniscono di fronte ad un notaio per firmare lo statuto ufficiale di &lt;a href="http://welaika.com"&gt;weLaika&lt;/a&gt;; società sognata, pensata e definita durante mesi di focose discussioni e appassionati thread mail.&lt;/p&gt;

&lt;p&gt;Sono stato, insieme a &lt;a href="http://twitter.com/mat_jack1"&gt;Matteo&lt;/a&gt; e &lt;a href="http://twitter.com/Jimmy2K"&gt;Federico&lt;/a&gt;, tra i soci operativi dal primo giorno. Posso dire, con immensa soddisfazione, di essermi riempito le tasche con palate di informazioni su cosa vuol dire lanciare una startup da zero. Zero clienti, zero esperienza di gestione manageriale alle spalle.&lt;/p&gt;

&lt;p&gt;Parecchie cose sono cambiate in un anno. Il numero di lavoratori IT full-time è passato da 3 a 6, più un paio di collaboratori saltuari che ci aiutano all&amp;#39;occorrenza. weLaika ha aperto il suo piccolo &amp;quot;dipartimento&amp;quot; di montaggio e ripresa video. &lt;a href="http://acmosadv.net/"&gt;Acmos Adv&lt;/a&gt;, agenzia pubblicitaria e di comunicazione già presente da qualche anno sulla piazza, è entrata a far parte di weLaika a tutti gli effetti, facendo lievitare il numero di stomaci da sfamare ad un totale di circa 15 e trasformando weLaika in una società multi-core in grado di coprire buona parte delle esigenze di comunicazione (web e non) di qualsiasi azienda.&lt;/p&gt;

&lt;p&gt;Siamo solo all&amp;#39;inizio, e stiamo continuando a sbagliare una valanga di cose, ma qualche consiglio mi sento di poterlo condividere a chi volesse intraprendere la nostra stessa strada.&lt;/p&gt;

&lt;h3&gt;Un passo alla volta, con la dovuta calma&lt;/h3&gt;

&lt;p&gt;Decidi di fondare una startup informatica e il tuo gruppo è composto da 3 ragazzi neo-laureati in Ingegneria Informatica, con una forte esperienza tecnica ma che non hanno idea di cosa voglia dire tenere in piedi una baracca chiamata &amp;quot;Società cooperativa&amp;quot;. Decidi di non voler tirare fuori un soldo, investendo unicamente sulle tue capacità e sul tuo tempo. Ti metti in testa di non voler evadere neanche un&amp;#39;euro (cosa che di per se&amp;#39; sarebbe superflua da dire, ma la realtà ci dice altro).&lt;/p&gt;

&lt;p&gt;È normale che finirai per prenderti un sacco di legnate sui denti per qualche mesetto. Tutto ciò che non ti uccide ti fortifica, per carità, ma se tornassi indietro, probabilmente avrei cercato di bruciare un po&amp;#39; meno tappe tutte insieme. Magari prima una semplice società di persone, o magari più loffi ancora, e preferire delle semplici partite IVA per qualche mese. Meno spese, meno burocrazia, meno regole da seguire, meno cose da imparare tutte insieme.&lt;/p&gt;

&lt;p&gt;C&amp;#39;è tutto un mondo di complicazioni là fuori che se sei un nerd con spirito ottimista e sognatore non ti immagini minimamente. Occhio a non tenerne conto, ci si fa male.&lt;/p&gt;

&lt;h3&gt;Intuisci i tuoi punti di forza&lt;/h3&gt;

&lt;p&gt;Sembra banale dirlo, ma se c&amp;#39;è un punto dopo il quale senti che tutto sta prendendo la piega giusta, è quello in cui capisci quanto vali e quali sono le carte che puoi spendere con i tuoi clienti. Putroppo, sarà molto difficile capirlo a tavolino. Dovrai incontrare qualcuno che molto semplicemente ti apra gli occhi, e te lo faccia notare. Ad ogni modo, sforzati ad intuire il prima possibile il perchè susciti curiosità/interesse, e quali sono stati i motivi per i quali un cliente abbia scelto proprio te.&lt;/p&gt;

&lt;p&gt;weLaika vive di un continuo lavoro di formazione e ricerca su tutte le tecnologie web in grado di portare al minimo tempi di sviluppo e al massimo la produttività delle proprie risorse. Il nostro modus operandi si basa sulla scelta di pochi strumenti di lavoro predefiniti per tutto il team: Wordpress per siti &amp;quot;semplici&amp;quot;, Rails per siti più complessi/gestionali/eshop, Sencha e Phonegap per applicazioni mobile cross-device, Websockets per funzionalità chat e push. Se ci si trova davanti a qualcosa di non risolvibile con gli strumenti a disposizione, si pensa sul lungo periodo, valutando con attenzione le alternative e cercando di rendere quanto più riutilizzabile e documentato il proprio lavoro. Periodicamente si rimettono in discussione le varie scelte fatte, per verificare se continuino ad essere le migliori.&lt;/p&gt;

&lt;p&gt;Col passare dei mesi, ci siamo resi conto che con le nostre scelte operative eravamo in grado di offrire tempi e costi di sviluppo dimezzati rispetto ai nostri concorrenti, con qualità spesso e volentieri superiore. Capire il mercato nel quale si opera ed essere sicuri dei propri vantaggi permette una comunicazione col potenziale cliente meno titubante, più convincente e con percentuali di successo più alte.&lt;/p&gt;

&lt;h3&gt;Pretendi un contratto&lt;/h3&gt;

&lt;p&gt;Sembrerà scontato dirlo, ma perfavore, anche se sei agli inizi e non vedi l&amp;#39;ora di iniziare a dimostrare a tutti quanto vali, esigi la firma di un contratto prima di incominciare a lavorare. Non importa se hai davanti la Nike, il Comune di Roma o una piccola azienda familiare. Non fare mai l&amp;#39;errore di cedere su questo punto, non abboccare ai vari &amp;quot;non ti preoccupare, puoi fidarti&amp;quot;. E&amp;#39; un rischio enorme, per te e per i tuoi clienti. Tra professionisti non ci deve essere bisogno di doversi fidare, è d&amp;#39;obbligo pretendere sicurezze, ricambiando con la stessa moneta.&lt;/p&gt;

&lt;p&gt;E&amp;#39; fondamentale chiarire con precisione fino a dove arrivano i diritti e doveri di entrambe le parti, perchè anche con la buona fede più sincera è probabile che verbalmente non ci si stia intendendo &lt;em&gt;sul serio&lt;/em&gt;, o che le cose cambino nel giro di qualche mese, a lavori iniziati. Non importa se il contratto sarà scritto in una forma non propriamente ineccepibile o magari presenterà qualche lacuna. I successivi saranno migliori. Ma avere un brutto contratto è meglio che non averne nessuno.&lt;/p&gt;

&lt;h3&gt;Specifiche, specifiche, specifiche&lt;/h3&gt;

&lt;p&gt;In ogni lavoro che prendete in carico, aggiungete una prima fase (pagata) di mockup e/o definizione delle specifiche del progetto. E&amp;#39; fondamentale, prima di iniziare a lavorare, avere la certezza che voi e il cliente abbiate in mente il medesimo risultato finale. Cose che per voi sono ovvie potrebbero non esserlo altrettanto per il vostro interlocutore, e viceversa. L&amp;#39;unico modo per capirsi con certezza è quello di sviscerare ogni blocco funzionale con quanto più dettaglio e realismo possibile.&lt;/p&gt;

&lt;p&gt;So cosa state pensando, ma vi fermo subito. Si tratta nel 90% dei casi di tempo assolutamente non perso. Definire i progetti con un simile livello di dettaglio ci aiuta in primo luogo ad arrivare ad un preventivo dei tempi di sviluppo più realistico, con minori possibilità di firmare contratti con costi sotto-stimati, e in secondo luogo a scoprire con un certo anticipo tutta quella serie di &amp;quot;casi particolari&amp;quot; ed eccezioni che spesso spuntano fuori nel peggiore dei momenti costringendoti a corse contro il tempo.&lt;/p&gt;

&lt;h3&gt;Fai sentire la tua voce&lt;/h3&gt;

&lt;p&gt;Essere bravi è importante, ma farlo sapere a tutti è fondamentale. L&amp;#39;ingegnere informatico non è tipicamente la figura più socievole, comunicativa e propensa all&amp;#39;auto-celebrazione che esista. Spesso non ha idea di quanto valga il proprio lavoro, e quando tenta di esporre qualcosa al mondo lo fa solitamente con estrema goffaggine, controvoglia e utilizzando terminologie poco comprensibili ed attraenti. Questo è male, e trattare con noncuranza i problemi di comunicazione verso l&amp;#39;esterno può essere potenzialmente catastrofico.&lt;/p&gt;

&lt;p&gt;In weLaika partiamo da questo punto di vista sicuramente svantaggiati, ed è per questo che gli sforzi devono essere maggiori rispetto alla norma. Abbiamo partecipato al &lt;a href="http://railsrumble.com"&gt;Rails Rumble 2010&lt;/a&gt; -- una gara mondiale di sviluppo di applicazioni web in 48 ore -- proponendo &lt;a href="http://boarrd.com/"&gt;Boarrd&lt;/a&gt;. Abbiamo ottenuto una buona copertura mediatica e siamo addirittura riusciti a convincere un&amp;#39;ottimo studente USA a passare da noi l&amp;#39;estate per  un&amp;#39;internship aziendale. Abbiamo trasformato i migliori framework e tool sviluppati internamente in progetti open source su Github (&lt;a href="http://rubygems.org/gems/incurve"&gt;incurve&lt;/a&gt;, &lt;a href="https://github.com/welaika/wp_sass_haml"&gt;wp_sass_haml&lt;/a&gt;, &lt;a href="https://github.com/endorama/rdiff-backup-BASH-script"&gt;rdiff-backup&lt;/a&gt;, &lt;a href="https://github.com/welaika/WETableViewController"&gt;WETableViewController&lt;/a&gt;) e abbiamo contribuito attivamente al miglioramento di progetti già esistenti (&lt;a href="http://socky.org/"&gt;socky&lt;/a&gt;, &lt;a href="https://github.com/welaika/guard-compass"&gt;guard-compass&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Ma possiamo fare di più. Una maggiore cura nel copyrighting del sito aziendale, una maggiore partecipazione a conferenze legate ai temi chiave di weLaika, ed un uso di twitter e blog costante saranno sicuramente i punti sui quali concentrarsi nel prossimo anno.&lt;/p&gt;

&lt;h3&gt;Siete arrivati fino a qui? Sul serio?&lt;/h3&gt;

&lt;p&gt;Grazie per avere condiviso con me questo viaggio nel tempo.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=HkJh8KGwWVg:06qO3FOTR90:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=HkJh8KGwWVg:06qO3FOTR90:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/z5ip7pI-TiU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/5-lezioni-dopo-un-anno-di-welaika.html</feedburner:origLink></entry>
  <entry>
    <title>Pow: 37signals colpisce di nuovo</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/9Uy6oWZomHQ/pow-37signals.html" rel="alternate" />
    <id>/blog/2011/04/pow-37signals.html</id>
    <published>2011-04-09T00:00:00+02:00</published>
    <updated>2011-04-09T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Server e un DNS da installare sul proprio Mac per sviluppo locale di applicazioni Rack. Bundler e RVM compatibile. Installato immediatamente. Sembra abbia qualche tipo di bug relativo allo scaricamento tramite &lt;code&gt;send_data&lt;/code&gt;, ma a parte questo é incredibilmente utile. Mi aspetto un progetto similare per Linux nel giro di un paio di settimane.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Server e un DNS da installare sul proprio Mac per sviluppo locale di applicazioni Rack. Bundler e RVM compatibile. Installato immediatamente. Sembra abbia qualche tipo di bug relativo allo scaricamento tramite &lt;code&gt;send_data&lt;/code&gt;, ma a parte questo é incredibilmente utile. Mi aspetto un progetto similare per Linux nel giro di un paio di settimane.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=OmIcOSyDdng:g9vKWrXBzB8:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=OmIcOSyDdng:g9vKWrXBzB8:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/9Uy6oWZomHQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/pow-37signals.html</feedburner:origLink></entry>
  <entry>
    <title>Prime Impressioni su Sphinx</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/kf5_N1JG17s/prime-impressioni-su-sphinx.html" rel="alternate" />
    <id>/blog/2011/04/prime-impressioni-su-sphinx.html</id>
    <published>2011-04-09T00:00:00+02:00</published>
    <updated>2011-04-09T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Per la prima volta ho avuto modo di testare funzionamento di &lt;a href="http://sphinxsearch.com/"&gt;Sphinx&lt;/a&gt;, uno dei più popolari motori di ricerca full-text in circolazione. L&amp;#39;ho preferito a &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; per evitare il disagio di Java, e a  &lt;a href="http://ferret.davebalmain.com/"&gt;Ferret&lt;/a&gt; per la maggiore affidabilità che mi sembrava dare dal punto di vista del supporto e della stabilità.&lt;/p&gt;

&lt;h3&gt;Installazione&lt;/h3&gt;

&lt;p&gt;L&amp;#39;installazione è stata incredibilmente semplice e rapida con &lt;code&gt;homebrew&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;brew install sphinx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In realtà mi sono reso presto conto che la ricetta homebrew non
prevedeva il supporto per &lt;code&gt;libstemmer&lt;/code&gt; (vedremo dopo a cosa serve),
quindi ho modificato leggermente la ricetta in questo modo (&lt;code&gt;sudo brew
edit sphinx&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

require &amp;#39;formula&amp;#39;

class Sphinx &amp;lt; Formula
  url &amp;#39;http://sphinxsearch.com/downloads/sphinx-0.9.9.tar.gz&amp;#39;
  homepage &amp;#39;http://www.sphinxsearch.com&amp;#39;
  md5 &amp;#39;7b9b618cb9b378f949bb1b91ddcc4f54&amp;#39;
  head &amp;#39;http://sphinxsearch.googlecode.com/svn/trunk/&amp;#39;

  fails_with_llvm &amp;quot;fails with: ld: rel32 out of range in _GetPrivateProfileString from /usr/lib/libodbc.a(SQLGetPrivateProfileString.o)&amp;quot;

  def install
    system &amp;quot;curl -O http://snowball.tartarus.org/dist/libstemmer_c.tgz&amp;quot;
    system &amp;quot;tar zxvf libstemmer_c.tgz&amp;quot;

    args = [&amp;quot;--prefix=#{prefix}&amp;quot;, &amp;quot;--disable-debug&amp;quot;, &amp;quot;--disable-dependency-tracking&amp;quot;, &amp;quot;--with-libstemmer&amp;quot;]
    # configure script won&amp;#39;t auto-select PostgreSQL
    args &amp;lt;&amp;lt; &amp;quot;--with-pgsql&amp;quot; if `/usr/bin/which pg_config`.size &amp;gt; 0
    args &amp;lt;&amp;lt; &amp;quot;--without-mysql&amp;quot; if `/usr/bin/which mysql`.size &amp;lt;= 0

    system &amp;quot;./configure&amp;quot;, *args
    system &amp;quot;make install&amp;quot;
  end

  def caveats
    &amp;lt;&amp;lt;-EOS.undent
    Sphinx depends on either MySQL or PostreSQL as a datasource.

    You can install these with Homebrew with:
      brew install mysql
        For MySQL server.

      brew install mysql-connector-c
        For MySQL client libraries only.

      brew install postgresql
        For PostgreSQL server.

    We don&amp;#39;t install these for you when you install this formula, as
    we don&amp;#39;t know which datasource you intend to use.
    EOS
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configurazione&lt;/h3&gt;

&lt;p&gt;A questo punto, il peggio è passato. &lt;a href="http://freelancing-god.github.com/"&gt;Thinking
Sphinx&lt;/a&gt; è una gemma ottimamente
documentata e seguita dal suo sviluppatore, e ti permette di impostare
degli indici di ricerca per i tuoi modelli Rails direttamente al loro
interno, grazie ad un semplice DSL di questo tipo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class User &amp;lt; ActiveRecord::Base
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

  define_index do
    indexes first_name
    indexes last_name, :sortable =&amp;gt; true
    indexes email
    indexes company
    indexes company_role
    indexes city
    indexes website
    indexes notes
    set_property :enable_star =&amp;gt; true
    set_property :min_infix_len =&amp;gt; 1
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Avviare Sphinx&lt;/h3&gt;

&lt;p&gt;Una volta impostati gli indici (per maggiori info su come funziona
Sphinx rifatevi a &lt;a href="http://freelancing-god.github.com/ts/en/sphinx_basics.html"&gt;questa
pagina&lt;/a&gt;, per
esempio), basta semplicemente far partire un rake task per convertire il
DSL in file di configurazione Sphinx: &lt;code&gt;thinking_sphinx:configure&lt;/code&gt;.
Potete poi far partire il server Sphinx con &lt;code&gt;thinking_sphinx:start&lt;/code&gt;.
Giuro che è stato &lt;em&gt;veramente&lt;/em&gt; così semplice.&lt;/p&gt;

&lt;p&gt;Thinking Sphinx dispone di una serie di rake task aggiuntivi per coprire
tutti i vostri bisogni:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake thinking_sphinx:configure      # Generate the Sphinx configuration file using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:index          # Index data for Sphinx using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:rebuild        # Stop Sphinx (if it&amp;#39;s running), rebuild the indexes, and start Sphinx
rake thinking_sphinx:reindex        # Reindex Sphinx without regenerating the configuration file
rake thinking_sphinx:restart        # Restart Sphinx
rake thinking_sphinx:running_start  # Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:start          # Start a Sphinx searchd daemon using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:stop           # Stop Sphinx using  Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:version        # Output the current Thinking Sphinx version
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per ricercare facendo uso di Sphinx, rifatevi all&amp;#39;ottima guida, ma fondamentalmente si parla di fare cose come:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
User.search(params[:search], :star =&amp;gt; true, :order =&amp;gt; :last_name).page(params[:page]).per(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E come forse vi state chiedendo sì, è già compatibile con
will_paginate e Kaminari (dalla versione &lt;code&gt;~&amp;gt; 2.0.3&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;Word stemming&lt;/h3&gt;

&lt;p&gt;Se avete installato Sphinx con il flag &lt;code&gt;--with-libstemmer&lt;/code&gt; da me suggerito qualche riga più in alto, allora avrete anche la possibilità di configurare il &lt;a href="http://freelancing-god.github.com/ts/en/advanced_config.html"&gt;word stemming&lt;/a&gt; italiano. Questo significa che Sphinx sarà in grado di gestire correttamente le forme singolari/plurali delle ricerche, fornendo risultati più rilevanti.&lt;/p&gt;

&lt;h3&gt;Conclusioni&lt;/h3&gt;

&lt;p&gt;Quello che abbiamo visto è la base. Sphinx è incredibilmente potente e permette, ad esempio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La ricerca anche sulle relazioni delle entità;&lt;/li&gt;
&lt;li&gt;Ordinare i risultati per peso raggruppandoli in slot temporali (utile per ricerche real-time);&lt;/li&gt;
&lt;li&gt;Ricercare mediante più sintassi (da quella per operatori logici, a quella con wildcards);&lt;/li&gt;
&lt;li&gt;Effettuare ricerche geospaziali;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L&amp;#39;unico svantaggio di Sphinx rispetto ad altri motori di ricerca full-text è che gli indici devono essere totalmente rigenerati quando anche solo un&amp;#39;elemento della tabella cambia. In realtà anche questo aspetto è stato pressochè risolto mediante &lt;a href="http://freelancing-god.github.com/ts/en/deltas.html"&gt;delta indexes&lt;/a&gt;, praticamente trasparenti all&amp;#39;utente.&lt;/p&gt;

&lt;p&gt;Insomma. Sphinx mi ha convinto.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Per la prima volta ho avuto modo di testare funzionamento di &lt;a href="http://sphinxsearch.com/"&gt;Sphinx&lt;/a&gt;, uno dei più popolari motori di ricerca full-text in circolazione. L&amp;#39;ho preferito a &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; per evitare il disagio di Java, e a  &lt;a href="http://ferret.davebalmain.com/"&gt;Ferret&lt;/a&gt; per la maggiore affidabilità che mi sembrava dare dal punto di vista del supporto e della stabilità.&lt;/p&gt;

&lt;h3&gt;Installazione&lt;/h3&gt;

&lt;p&gt;L&amp;#39;installazione è stata incredibilmente semplice e rapida con &lt;code&gt;homebrew&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;brew install sphinx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In realtà mi sono reso presto conto che la ricetta homebrew non
prevedeva il supporto per &lt;code&gt;libstemmer&lt;/code&gt; (vedremo dopo a cosa serve),
quindi ho modificato leggermente la ricetta in questo modo (&lt;code&gt;sudo brew
edit sphinx&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

require &amp;#39;formula&amp;#39;

class Sphinx &amp;lt; Formula
  url &amp;#39;http://sphinxsearch.com/downloads/sphinx-0.9.9.tar.gz&amp;#39;
  homepage &amp;#39;http://www.sphinxsearch.com&amp;#39;
  md5 &amp;#39;7b9b618cb9b378f949bb1b91ddcc4f54&amp;#39;
  head &amp;#39;http://sphinxsearch.googlecode.com/svn/trunk/&amp;#39;

  fails_with_llvm &amp;quot;fails with: ld: rel32 out of range in _GetPrivateProfileString from /usr/lib/libodbc.a(SQLGetPrivateProfileString.o)&amp;quot;

  def install
    system &amp;quot;curl -O http://snowball.tartarus.org/dist/libstemmer_c.tgz&amp;quot;
    system &amp;quot;tar zxvf libstemmer_c.tgz&amp;quot;

    args = [&amp;quot;--prefix=#{prefix}&amp;quot;, &amp;quot;--disable-debug&amp;quot;, &amp;quot;--disable-dependency-tracking&amp;quot;, &amp;quot;--with-libstemmer&amp;quot;]
    # configure script won&amp;#39;t auto-select PostgreSQL
    args &amp;lt;&amp;lt; &amp;quot;--with-pgsql&amp;quot; if `/usr/bin/which pg_config`.size &amp;gt; 0
    args &amp;lt;&amp;lt; &amp;quot;--without-mysql&amp;quot; if `/usr/bin/which mysql`.size &amp;lt;= 0

    system &amp;quot;./configure&amp;quot;, *args
    system &amp;quot;make install&amp;quot;
  end

  def caveats
    &amp;lt;&amp;lt;-EOS.undent
    Sphinx depends on either MySQL or PostreSQL as a datasource.

    You can install these with Homebrew with:
      brew install mysql
        For MySQL server.

      brew install mysql-connector-c
        For MySQL client libraries only.

      brew install postgresql
        For PostgreSQL server.

    We don&amp;#39;t install these for you when you install this formula, as
    we don&amp;#39;t know which datasource you intend to use.
    EOS
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configurazione&lt;/h3&gt;

&lt;p&gt;A questo punto, il peggio è passato. &lt;a href="http://freelancing-god.github.com/"&gt;Thinking
Sphinx&lt;/a&gt; è una gemma ottimamente
documentata e seguita dal suo sviluppatore, e ti permette di impostare
degli indici di ricerca per i tuoi modelli Rails direttamente al loro
interno, grazie ad un semplice DSL di questo tipo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

class User &amp;lt; ActiveRecord::Base
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

  define_index do
    indexes first_name
    indexes last_name, :sortable =&amp;gt; true
    indexes email
    indexes company
    indexes company_role
    indexes city
    indexes website
    indexes notes
    set_property :enable_star =&amp;gt; true
    set_property :min_infix_len =&amp;gt; 1
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Avviare Sphinx&lt;/h3&gt;

&lt;p&gt;Una volta impostati gli indici (per maggiori info su come funziona
Sphinx rifatevi a &lt;a href="http://freelancing-god.github.com/ts/en/sphinx_basics.html"&gt;questa
pagina&lt;/a&gt;, per
esempio), basta semplicemente far partire un rake task per convertire il
DSL in file di configurazione Sphinx: &lt;code&gt;thinking_sphinx:configure&lt;/code&gt;.
Potete poi far partire il server Sphinx con &lt;code&gt;thinking_sphinx:start&lt;/code&gt;.
Giuro che è stato &lt;em&gt;veramente&lt;/em&gt; così semplice.&lt;/p&gt;

&lt;p&gt;Thinking Sphinx dispone di una serie di rake task aggiuntivi per coprire
tutti i vostri bisogni:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake thinking_sphinx:configure      # Generate the Sphinx configuration file using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:index          # Index data for Sphinx using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:rebuild        # Stop Sphinx (if it&amp;#39;s running), rebuild the indexes, and start Sphinx
rake thinking_sphinx:reindex        # Reindex Sphinx without regenerating the configuration file
rake thinking_sphinx:restart        # Restart Sphinx
rake thinking_sphinx:running_start  # Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:start          # Start a Sphinx searchd daemon using Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:stop           # Stop Sphinx using  Thinking Sphinx&amp;#39;s settings
rake thinking_sphinx:version        # Output the current Thinking Sphinx version
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Per ricercare facendo uso di Sphinx, rifatevi all&amp;#39;ottima guida, ma fondamentalmente si parla di fare cose come:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
User.search(params[:search], :star =&amp;gt; true, :order =&amp;gt; :last_name).page(params[:page]).per(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E come forse vi state chiedendo sì, è già compatibile con
will_paginate e Kaminari (dalla versione &lt;code&gt;~&amp;gt; 2.0.3&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;Word stemming&lt;/h3&gt;

&lt;p&gt;Se avete installato Sphinx con il flag &lt;code&gt;--with-libstemmer&lt;/code&gt; da me suggerito qualche riga più in alto, allora avrete anche la possibilità di configurare il &lt;a href="http://freelancing-god.github.com/ts/en/advanced_config.html"&gt;word stemming&lt;/a&gt; italiano. Questo significa che Sphinx sarà in grado di gestire correttamente le forme singolari/plurali delle ricerche, fornendo risultati più rilevanti.&lt;/p&gt;

&lt;h3&gt;Conclusioni&lt;/h3&gt;

&lt;p&gt;Quello che abbiamo visto è la base. Sphinx è incredibilmente potente e permette, ad esempio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La ricerca anche sulle relazioni delle entità;&lt;/li&gt;
&lt;li&gt;Ordinare i risultati per peso raggruppandoli in slot temporali (utile per ricerche real-time);&lt;/li&gt;
&lt;li&gt;Ricercare mediante più sintassi (da quella per operatori logici, a quella con wildcards);&lt;/li&gt;
&lt;li&gt;Effettuare ricerche geospaziali;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L&amp;#39;unico svantaggio di Sphinx rispetto ad altri motori di ricerca full-text è che gli indici devono essere totalmente rigenerati quando anche solo un&amp;#39;elemento della tabella cambia. In realtà anche questo aspetto è stato pressochè risolto mediante &lt;a href="http://freelancing-god.github.com/ts/en/deltas.html"&gt;delta indexes&lt;/a&gt;, praticamente trasparenti all&amp;#39;utente.&lt;/p&gt;

&lt;p&gt;Insomma. Sphinx mi ha convinto.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=OfcwjdGc07s:A8Kz9KvUfgs:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=OfcwjdGc07s:A8Kz9KvUfgs:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/kf5_N1JG17s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/prime-impressioni-su-sphinx.html</feedburner:origLink></entry>
  <entry>
    <title>Fuck you. Pay me.</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/08dIEPGS1nY/fuck-you-pay-me.html" rel="alternate" />
    <id>/blog/2011/04/fuck-you-pay-me.html</id>
    <published>2011-04-09T00:00:00+02:00</published>
    <updated>2011-04-09T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Presentazione di Mike Monteiro, Design Director di &lt;a href="www.muledesign.com"&gt;Mule Design&lt;/a&gt;, consigliata a chiunque sia alle prime armi col dover affrontare la sfida di gestire clienti, anche quando le cose non stanno andando come sperato (mi ricorda qualcuno). Spoiler: cercati un&amp;#39;avvocato.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My advice to them is usually: you need to talk to a lawyer about this stuff. There are two things I hear the most: &amp;quot;Lawyers are too expensive. I can&amp;#39;t afford them&amp;quot;. The second: &amp;quot;Are we at that pointalready do you think?&amp;quot;. You are at the point where you need a lawyer when you&amp;#39;ve decided stop being a design amateur and become a design professional. That&amp;#39;s the time when you need a legal professional to look at those contracts.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
    <content type="html">&lt;p&gt;Presentazione di Mike Monteiro, Design Director di &lt;a href="www.muledesign.com"&gt;Mule Design&lt;/a&gt;, consigliata a chiunque sia alle prime armi col dover affrontare la sfida di gestire clienti, anche quando le cose non stanno andando come sperato (mi ricorda qualcuno). Spoiler: cercati un&amp;#39;avvocato.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My advice to them is usually: you need to talk to a lawyer about this stuff. There are two things I hear the most: &amp;quot;Lawyers are too expensive. I can&amp;#39;t afford them&amp;quot;. The second: &amp;quot;Are we at that pointalready do you think?&amp;quot;. You are at the point where you need a lawyer when you&amp;#39;ve decided stop being a design amateur and become a design professional. That&amp;#39;s the time when you need a legal professional to look at those contracts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=DLinwmtMkus:bVFXn0ru9j4:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=DLinwmtMkus:bVFXn0ru9j4:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/08dIEPGS1nY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/fuck-you-pay-me.html</feedburner:origLink></entry>
  <entry>
    <title>Crash Course: Design for Startups</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/WNZm25tTp2I/design-crash-course.html" rel="alternate" />
    <id>/blog/2011/04/design-crash-course.html</id>
    <published>2011-04-07T00:00:00+02:00</published>
    <updated>2011-04-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Piccoli, semplici, ottimi suggerimenti da Paul Stamatiou per iniziare ad entrare nel mondo del web-design senza sbagliare tutto. Consigliato a tutti i programmatori duri e puri l&amp;agrave; fuori: anche noi ce la possiamo fare. Coraggioso il suo &lt;a href="http://paulstamatiou.com/portfolio"&gt;reverse portfolio&lt;/a&gt;: tutti i propri lavori, dal pi&amp;ugrave; orrendo, al pi&amp;ugrave; recente.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Piccoli, semplici, ottimi suggerimenti da Paul Stamatiou per iniziare ad entrare nel mondo del web-design senza sbagliare tutto. Consigliato a tutti i programmatori duri e puri l&amp;agrave; fuori: anche noi ce la possiamo fare. Coraggioso il suo &lt;a href="http://paulstamatiou.com/portfolio"&gt;reverse portfolio&lt;/a&gt;: tutti i propri lavori, dal pi&amp;ugrave; orrendo, al pi&amp;ugrave; recente.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=xaZhMzKQoKQ:zJIJzFqSS6o:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=xaZhMzKQoKQ:zJIJzFqSS6o:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/WNZm25tTp2I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/design-crash-course.html</feedburner:origLink></entry>
  <entry>
    <title>Top ten reasons why I won't use your open source project</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/-EPNJuzJBMc/10-reasons-i-wont-use-your-opensource-project.html" rel="alternate" />
    <id>/blog/2011/04/10-reasons-i-wont-use-your-opensource-project.html</id>
    <published>2011-04-07T00:00:00+02:00</published>
    <updated>2011-04-07T00:00:00+02:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;Nei progetti open, così come in qualsiasi altro contesto, la comunicazione verso il mondo esterno è fondamentale per avere successo, trasmettere professionalità, stabilità e propagare interesse.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Nei progetti open, così come in qualsiasi altro contesto, la comunicazione verso il mondo esterno è fondamentale per avere successo, trasmettere professionalità, stabilità e propagare interesse.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=iYd7yW0QpDM:hGrAe3AkvvA:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=iYd7yW0QpDM:hGrAe3AkvvA:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/-EPNJuzJBMc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2011/04/10-reasons-i-wont-use-your-opensource-project.html</feedburner:origLink></entry>
  <entry>
    <title>Create astonishing iCal-like calendars with jQuery</title>
    <link href="http://feeds.stefanoverna.com/~r/stefanoverna/~3/0WOUFlP1DlY/create-astonishing-ical-like-calendars-with-jquery.html" rel="alternate" />
    <id>/blog/2009/01/create-astonishing-ical-like-calendars-with-jquery.html</id>
    <published>2009-01-27T00:00:00+01:00</published>
    <updated>2009-01-27T00:00:00+01:00</updated>
    <author>
      <name>Stefano Verna</name>
    </author>
    <summary type="html">&lt;p&gt;According to my web designer experience, one of the most common requests from clients when it comes to Wordpress personalization, is to add a basic event calendar to their website.&lt;/p&gt;

&lt;p&gt;Finding a good place to position a big table like a calendar within your Wordpress template is always a taught work. In addition, the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; tag itself is often quite difficult to style in a good way.&lt;/p&gt;

&lt;p&gt;One of the calendar solution that I came out with and that I&amp;#39;m particulary proud of is the one I built inside the freshly launched &lt;a href="http://www.watsonforpresident.eu" title="Graham Watson President of European Parliament"&gt;Graham Watson for President&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;&lt;a href="/data/ical_like_calendar/demo.html"&gt;View the online Demo!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted it to be similar to the iPhone Calendar application (or, if you want, to the little calendar on the left bottom corner in iCal). And I also wanted to keep the code as little and sweet as possible (we don&amp;#39;t like maintenance, do we?).&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s the simple HTML code I used, the simplest you could ever come up with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  

  &amp;lt;table cellspacing=&amp;quot;0&amp;quot;&amp;gt;
    &amp;lt;thead&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Mon&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Tue&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Wed&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Thu&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Fri&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sat&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Sun&amp;lt;/th&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td class=&amp;quot;padding&amp;quot; colspan=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 1&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 2&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 3&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 4&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt; 5&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 6&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 7&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 8&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;today&amp;quot;&amp;gt; 9&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;10&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;11&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;12&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
          13
        &amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;14&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;15&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;16&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;17&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;18&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;19&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;20&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;21&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
          22
        &amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;23&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;24&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;25&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;26&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;27&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;28&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;29&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;30&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;31&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;padding&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/tbody&amp;gt;
    &amp;lt;tfoot&amp;gt;
      &amp;lt;th&amp;gt;Mon&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Tue&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Wed&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Thu&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Fri&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sat&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Sun&amp;lt;/th&amp;gt;
    &amp;lt;/tfoot&amp;gt;
  &amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All the magic takes place with some ninja CSS:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  table {
    border-collapse: separate;
    border: 1px solid #9DABCE;
    border-width: 0px 0px 1px 1px;
    margin: 10px auto;
    font-size: 20px;
  }
  td, th {
    width: 81px;
    height: 81px;
    text-align: center;
    vertical-align: middle;
    background: url(../img/cells.png);
    color: #444;
    position: relative;
  }
  th {
    height: 30px;
    font-weight: bold;
    font-size: 14px;
  }
  td:hover, th:hover {
    background-position: 0px -81px;
    color: #222;
  }
  td.date_has_event {
    background-position: 162px 0px;
    color: white;
  }
  td.date_has_event:hover {
    background-position: 162px -81px;
  }
  td.padding {
    background: url(../img/calpad.jpg);
  }
  td.today {
    background-position: 81px 0px;
    color: white;
  }
  td.today:hover {
    background-position: 81px -81px;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please note a couple of things here, as it&amp;#39;s the trickiest part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Make your images seamless.&lt;/strong&gt; Draw only the top and right border of the cells inside the image: neighbour cells will continue the pattern. Then add the bottom and left border to the table via CSS to complete the work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a single image for all the graphics&lt;/strong&gt; whenever is possible to decrease the download speed time (just a single TCP three-way-handshake to manage, a single Apache request to be answered by your server, a single PNG header overhead to be downloaded).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the plain calendar structure, we obviously also want the events description to show up on mouse hover. To do that, just add this block inside the calendar cells:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
  13
  &amp;lt;div class=&amp;quot;events&amp;quot;&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;
        &amp;lt;span class=&amp;quot;title&amp;quot;&amp;gt;Event 1&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;desc&amp;quot;&amp;gt;Lorem ipsum dolor sit amet, consectetu adipisicing elit.&amp;lt;/span&amp;gt;
      &amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;
        &amp;lt;span class=&amp;quot;title&amp;quot;&amp;gt;Event 2&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;desc&amp;quot;&amp;gt;Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&amp;lt;/span&amp;gt;
      &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And use the beautiful, handy, lightweight Coda-like effect for jQuery to bring it to life (how I love jQuery?)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  $(function () {
    $(&amp;#39;.date_has_event&amp;#39;).each(function () {
      // options
      var distance = 10;
      var time = 250;
      var hideDelay = 500;

      var hideDelayTimer = null;

      // tracker
      var beingShown = false;
      var shown = false;

      var trigger = $(this);
      var popup = $(&amp;#39;.events ul&amp;#39;, this).css(&amp;#39;opacity&amp;#39;, 0);

      // set the mouseover and mouseout on both element
      $([trigger.get(0), popup.get(0)]).mouseover(function () {
        // stops the hide event if we move from the trigger to the popup element
        if (hideDelayTimer) clearTimeout(hideDelayTimer);

        // don&amp;#39;t trigger the animation again if we&amp;#39;re being shown, or already visible
        if (beingShown || shown) {
          return;
        } else {
          beingShown = true;

          // reset position of popup box
          popup.css({
            bottom: 20,
            left: -76,
            display: &amp;#39;block&amp;#39; // brings the popup back in to view
          })

          // (we&amp;#39;re using chaining on the popup) now animate it&amp;#39;s opacity and position
          .animate({
            bottom: &amp;#39;+=&amp;#39; + distance + &amp;#39;px&amp;#39;,
            opacity: 1
          }, time, &amp;#39;swing&amp;#39;, function() {
            // once the animation is complete, set the tracker variables
            beingShown = false;
            shown = true;
          });
        }
      }).mouseout(function () {
        // reset the timer if we get fired again - avoids double animations
        if (hideDelayTimer) clearTimeout(hideDelayTimer);

        // store the timer so that it can be cleared in the mouseover if required
        hideDelayTimer = setTimeout(function () {
          hideDelayTimer = null;
          popup.animate({
            bottom: &amp;#39;-=&amp;#39; + distance + &amp;#39;px&amp;#39;,
            opacity: 0
          }, time, &amp;#39;swing&amp;#39;, function () {
            // once the animate is complete, set the tracker variables
            shown = false;
            // hide the popup entirely after the effect (opacity alone doesn&amp;#39;t do the job)
            popup.css(&amp;#39;display&amp;#39;, &amp;#39;none&amp;#39;);
          });
        }, hideDelay);
      });
    });
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the CSS code used to style the popup div:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  .events {
    position: relative;
  }
  .events ul {
    text-align: left;
    position: absolute;
    display: none;
    z-index: 1000;
    padding: 15px;
    background: #E7ECF2 url(../img/popup.png) no-repeat;
    color: white;
    border: 1px solid white;
    font-size: 15px;
    width: 200px;
    -moz-border-radius: 3px;
    -khtml-border-radius: 3px;
    -webkit-border-radius: 3px;
    -border-radius: 3px;
    list-style: none;
    color: #444444;
    -webkit-box-shadow: 0px 8px 8px #333;
  }
  .events li {
    padding-bottom: 5px;
  }
  .events li span {
    display: block;
    font-size: 12px;
    text-align: justify;
    color: #555;
  }
  .events li span.title {
    font-weight: bold;
    color: #222;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please note how nice the box-shadow CSS property is when applied to the popup... unfortunately, this CSS3 property is still only implemented in WebKit browsers (Safari, Google Chrome), but more of them are about to support it.&lt;/p&gt;

&lt;p&gt;&lt;a href="/data/ical_like_calendar/package.zip"&gt;Download the source files!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And... that&amp;#39;s it. Simple and sweet, as we wanted. Obviously, you&amp;#39;ll have to properly configure your preferred Wordpress plugin to output a code like the one I showed you, but that&amp;#39;s the boring part of the lesson and I&amp;#39;ll skip it :) Instead, let me just add a note...&lt;/p&gt;

&lt;h3&gt;Choosing the right Wordpress Plugin&lt;/h3&gt;

&lt;p&gt;There are plenty of pretty good Wordpress calendar plugins around to facilitate your backend work. I can tell you I tried them all, and the one that convinced me the most was &lt;a href="http://wpcal.firetree.net/" title="Event Calendar Plugin for Wordpress"&gt;Event Calendar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Event Calendar you can add a countless number of events to any post or page directly within the New and Edit page, there&amp;#39;s a lot of great functions you can use to freely tweak the event-browsing experience within your PHP template -- but some work still should be done in this direction -- it&amp;#39;s AJAX ready and the plugin itself is already localized in 19 languages.&lt;/p&gt;

&lt;p&gt;Hope you&amp;#39;ve enjoyed the tut! I&amp;#39;ll try to reply to your question in my free time :)&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;According to my web designer experience, one of the most common requests from clients when it comes to Wordpress personalization, is to add a basic event calendar to their website.&lt;/p&gt;

&lt;p&gt;Finding a good place to position a big table like a calendar within your Wordpress template is always a taught work. In addition, the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; tag itself is often quite difficult to style in a good way.&lt;/p&gt;

&lt;p&gt;One of the calendar solution that I came out with and that I&amp;#39;m particulary proud of is the one I built inside the freshly launched &lt;a href="http://www.watsonforpresident.eu" title="Graham Watson President of European Parliament"&gt;Graham Watson for President&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;&lt;a href="/data/ical_like_calendar/demo.html"&gt;View the online Demo!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted it to be similar to the iPhone Calendar application (or, if you want, to the little calendar on the left bottom corner in iCal). And I also wanted to keep the code as little and sweet as possible (we don&amp;#39;t like maintenance, do we?).&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s the simple HTML code I used, the simplest you could ever come up with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  

  &amp;lt;table cellspacing=&amp;quot;0&amp;quot;&amp;gt;
    &amp;lt;thead&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Mon&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Tue&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Wed&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Thu&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Fri&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sat&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Sun&amp;lt;/th&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td class=&amp;quot;padding&amp;quot; colspan=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 1&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 2&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 3&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 4&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt; 5&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 6&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 7&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt; 8&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;today&amp;quot;&amp;gt; 9&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;10&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;11&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;12&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
          13
        &amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;14&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;15&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;16&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;17&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;18&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;19&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;20&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;21&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
          22
        &amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;23&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;24&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;25&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;26&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;27&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;28&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;29&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;30&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;31&amp;lt;/td&amp;gt;
        &amp;lt;td class=&amp;quot;padding&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/tbody&amp;gt;
    &amp;lt;tfoot&amp;gt;
      &amp;lt;th&amp;gt;Mon&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Tue&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Wed&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Thu&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Fri&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sat&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Sun&amp;lt;/th&amp;gt;
    &amp;lt;/tfoot&amp;gt;
  &amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All the magic takes place with some ninja CSS:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  table {
    border-collapse: separate;
    border: 1px solid #9DABCE;
    border-width: 0px 0px 1px 1px;
    margin: 10px auto;
    font-size: 20px;
  }
  td, th {
    width: 81px;
    height: 81px;
    text-align: center;
    vertical-align: middle;
    background: url(../img/cells.png);
    color: #444;
    position: relative;
  }
  th {
    height: 30px;
    font-weight: bold;
    font-size: 14px;
  }
  td:hover, th:hover {
    background-position: 0px -81px;
    color: #222;
  }
  td.date_has_event {
    background-position: 162px 0px;
    color: white;
  }
  td.date_has_event:hover {
    background-position: 162px -81px;
  }
  td.padding {
    background: url(../img/calpad.jpg);
  }
  td.today {
    background-position: 81px 0px;
    color: white;
  }
  td.today:hover {
    background-position: 81px -81px;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please note a couple of things here, as it&amp;#39;s the trickiest part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Make your images seamless.&lt;/strong&gt; Draw only the top and right border of the cells inside the image: neighbour cells will continue the pattern. Then add the bottom and left border to the table via CSS to complete the work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a single image for all the graphics&lt;/strong&gt; whenever is possible to decrease the download speed time (just a single TCP three-way-handshake to manage, a single Apache request to be answered by your server, a single PNG header overhead to be downloaded).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the plain calendar structure, we obviously also want the events description to show up on mouse hover. To do that, just add this block inside the calendar cells:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;

&amp;lt;td class=&amp;quot;date_has_event&amp;quot;&amp;gt;
  13
  &amp;lt;div class=&amp;quot;events&amp;quot;&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;
        &amp;lt;span class=&amp;quot;title&amp;quot;&amp;gt;Event 1&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;desc&amp;quot;&amp;gt;Lorem ipsum dolor sit amet, consectetu adipisicing elit.&amp;lt;/span&amp;gt;
      &amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;
        &amp;lt;span class=&amp;quot;title&amp;quot;&amp;gt;Event 2&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;desc&amp;quot;&amp;gt;Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&amp;lt;/span&amp;gt;
      &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/td&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And use the beautiful, handy, lightweight Coda-like effect for jQuery to bring it to life (how I love jQuery?)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  $(function () {
    $(&amp;#39;.date_has_event&amp;#39;).each(function () {
      // options
      var distance = 10;
      var time = 250;
      var hideDelay = 500;

      var hideDelayTimer = null;

      // tracker
      var beingShown = false;
      var shown = false;

      var trigger = $(this);
      var popup = $(&amp;#39;.events ul&amp;#39;, this).css(&amp;#39;opacity&amp;#39;, 0);

      // set the mouseover and mouseout on both element
      $([trigger.get(0), popup.get(0)]).mouseover(function () {
        // stops the hide event if we move from the trigger to the popup element
        if (hideDelayTimer) clearTimeout(hideDelayTimer);

        // don&amp;#39;t trigger the animation again if we&amp;#39;re being shown, or already visible
        if (beingShown || shown) {
          return;
        } else {
          beingShown = true;

          // reset position of popup box
          popup.css({
            bottom: 20,
            left: -76,
            display: &amp;#39;block&amp;#39; // brings the popup back in to view
          })

          // (we&amp;#39;re using chaining on the popup) now animate it&amp;#39;s opacity and position
          .animate({
            bottom: &amp;#39;+=&amp;#39; + distance + &amp;#39;px&amp;#39;,
            opacity: 1
          }, time, &amp;#39;swing&amp;#39;, function() {
            // once the animation is complete, set the tracker variables
            beingShown = false;
            shown = true;
          });
        }
      }).mouseout(function () {
        // reset the timer if we get fired again - avoids double animations
        if (hideDelayTimer) clearTimeout(hideDelayTimer);

        // store the timer so that it can be cleared in the mouseover if required
        hideDelayTimer = setTimeout(function () {
          hideDelayTimer = null;
          popup.animate({
            bottom: &amp;#39;-=&amp;#39; + distance + &amp;#39;px&amp;#39;,
            opacity: 0
          }, time, &amp;#39;swing&amp;#39;, function () {
            // once the animate is complete, set the tracker variables
            shown = false;
            // hide the popup entirely after the effect (opacity alone doesn&amp;#39;t do the job)
            popup.css(&amp;#39;display&amp;#39;, &amp;#39;none&amp;#39;);
          });
        }, hideDelay);
      });
    });
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the CSS code used to style the popup div:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  
  
  .events {
    position: relative;
  }
  .events ul {
    text-align: left;
    position: absolute;
    display: none;
    z-index: 1000;
    padding: 15px;
    background: #E7ECF2 url(../img/popup.png) no-repeat;
    color: white;
    border: 1px solid white;
    font-size: 15px;
    width: 200px;
    -moz-border-radius: 3px;
    -khtml-border-radius: 3px;
    -webkit-border-radius: 3px;
    -border-radius: 3px;
    list-style: none;
    color: #444444;
    -webkit-box-shadow: 0px 8px 8px #333;
  }
  .events li {
    padding-bottom: 5px;
  }
  .events li span {
    display: block;
    font-size: 12px;
    text-align: justify;
    color: #555;
  }
  .events li span.title {
    font-weight: bold;
    color: #222;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please note how nice the box-shadow CSS property is when applied to the popup... unfortunately, this CSS3 property is still only implemented in WebKit browsers (Safari, Google Chrome), but more of them are about to support it.&lt;/p&gt;

&lt;p&gt;&lt;a href="/data/ical_like_calendar/package.zip"&gt;Download the source files!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And... that&amp;#39;s it. Simple and sweet, as we wanted. Obviously, you&amp;#39;ll have to properly configure your preferred Wordpress plugin to output a code like the one I showed you, but that&amp;#39;s the boring part of the lesson and I&amp;#39;ll skip it :) Instead, let me just add a note...&lt;/p&gt;

&lt;h3&gt;Choosing the right Wordpress Plugin&lt;/h3&gt;

&lt;p&gt;There are plenty of pretty good Wordpress calendar plugins around to facilitate your backend work. I can tell you I tried them all, and the one that convinced me the most was &lt;a href="http://wpcal.firetree.net/" title="Event Calendar Plugin for Wordpress"&gt;Event Calendar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Event Calendar you can add a countless number of events to any post or page directly within the New and Edit page, there&amp;#39;s a lot of great functions you can use to freely tweak the event-browsing experience within your PHP template -- but some work still should be done in this direction -- it&amp;#39;s AJAX ready and the plugin itself is already localized in 19 languages.&lt;/p&gt;

&lt;p&gt;Hope you&amp;#39;ve enjoyed the tut! I&amp;#39;ll try to reply to your question in my free time :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.stefanoverna.com/~ff/stefanoverna?a=tjuStvfd1IA:wRPxmgGkeps:3GzE0fYXwMM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/stefanoverna?i=tjuStvfd1IA:wRPxmgGkeps:3GzE0fYXwMM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/stefanoverna/~4/0WOUFlP1DlY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://stefanoverna.com/blog/2009/01/create-astonishing-ical-like-calendars-with-jquery.html</feedburner:origLink></entry>
</feed>

