Commenti

Pattern in Rails

Negli ultimi mesi sembrano essersi finalmente smosse le acque nel panorama delle pratiche per rendere le proprie applicazioni Rails più mantenibili e modulari. Avevamo già parlato della questione qualche tempo fa, dunque ci torniamo volentieri.

Tradizione vuole che le opinioni nel mondo Ruby vengano spesso espresse piuttosto vivacemente dai suoi principali esponenti, ed anche questa volta non ci sono state eccezioni. Ecco solo una piccola parte della valanga di articoli usciti:

A prescindere dalle diatribe integraliste sul Miglior Pattern®, credo che la cosa più giusta sia quella di provare ad interiorizzare quante più metodologie di decoupling possibile, in modo sfruttarle al meglio nei modi e luoghi più indicati.

Concern

Un Concern Rails non è altro che un mixin evoluto. Il mixin è uno dei capisaldi della programmazione in Ruby ed è utile per raggruppare all'interno di un modulo unitario tutti i metodi necessari a realizzare uno specifico comportamento. Lo scopo ovviamente è il riutilizzo del modulo stesso in più di un contesto.

Le classiche macro act_as_* che ci vengono messe a disposizione dalle varie gemme ActiveRecord utilizzano al loro interno questo pattern, ed il modulo ActiveSupport::Concern permette di sfruttare lo stesso meccanismo anche all'interno della nostra applicazione, per parti di codice che vogliamo utilizzare in più modelli/controller:

 1 module Taggable
 2   extend ActiveSupport::Concern
 3 
 4   included do
 5     has_many :taggings, as: :taggable, dependent: :destroy
 6     has_many :tags, through: :taggings 
 7   end
 8 
 9   def tag_names
10     tags.map(&:name)
11   end
12 end
13 
14 class Post < ActiveRecord::Base
15   # ...
16   include Taggable
17 end

Il Concern è il pattern "anti Fat Model" ufficialmente riconosciuto e supportato dal Rails core team: pare che Rails 4 potrebbe includere di default due nuove directory (app/controllers/concerns e app/models/concerns) nelle quali aggiungere i propri Concern, ed il medesimo concetto verrà sicuramente proposto anche a livello di routes.

Presenter/Decorator/Exhibit

Un oggetto Decorator prevede l'estensione di comportamento di un singolo oggetto attraverso il concetto di composition, senza influenzare le altre istanze della medesima classe presenti nel sistema.

Il pattern Presenter è un caso speciale del pattern Decorator nel quale i metodi aggiunti all'oggetto sono utili a fini di presentazione dell'oggetto stesso (tipicamente all'interno di una vista).

Sono dunque un'alternativa più object-oriented all'utilizzo dei classici helper Rails. Per capirci, il metodo .to_json presente nei modelli ActiveRecord sarebbe un candidato ideale a spostarsi all'interno di un Presenter, in quanto responsabile della generazione di una rappresentazione JSON del modello.

In Ruby è possibile implementare una classe Presenter attraverso l'utilizzo della classe standard SimpleDelegator:

 1 class User < Struct.new(:first_name, :email)
 2   # ...
 3 end
 4 
 5 require 'delegate'
 6 
 7 class UserDecorator < SimpleDelegator
 8   def class
 9     __getobj__.class
10   end
11 
12   def avatar_url
13     gravatar_id = Digest::MD5.hexdigest(email.downcase)
14     "http://gravatar.com/avatar/#{gravatar_id}.png"
15   end
16 end
17 
18 user = User.new("Stefano", "stefano.verna@welaika.com")
19 user_decorator = UserDecorator.new(user)
20 puts user_decorator.class # => User
21 puts user_decorator.first_name # => "Stefano"
22 puts user_decorator.avatar_url # => "http://gravatar.com/avatar/b731002ef4fa2ee7423e4b15e177f5b3.png"

Il decoratore garantisce un comportamento in tutto e per tutto identico all'oggetto decorato, con l'aggiunta di uno o più metodi di tipo presentational da utilizzare all'interno delle nostre viste.

Per un utilizzo più avanzato di decoratori, date un'occhiata alla gemma Draper, potrebbe fare al caso vostro.

Domain Service Objects

Quando esistono logiche all'interno dell'applicazione che coinvolgono più di un modello, la responsabilità di esecuzione spesso non può essere affidata a nessuno dei singoli modelli, ne' tanto meno al controller — il cui ruolo dovrebbe essere semplicemente quello di instradare le richieste HTTP verso una particolare vista/template; Ecco che in questi casi può far comodo un Service Object, il cui compito è quello di coordinare due o più oggetti, al fine di realizzare un particolare Use Case.

Supponiamo di dover mandare una mail contestualmente alla creazione di un nuovo post:

 1 class PostsController < ApplicationController
 2   respond_to :html
 3 
 4   def create
 5     @post = PostCreationService.run(current_user, params)
 6     respond_with @post
 7   end
 8 end
 9 
10 class PostCreationService
11   def self.run(user, post_params)
12     post = Post.new(post_params.merge(user: user))
13     PostMailer.post_created(post).deliver if post.save
14     post
15   end
16 end

Certo, si potrebbe risolvere la medesima questione attraverso un hook after_create nel modello Post, ma non è compito del modello inviare una mail: se creassimo un nuovo Post all'interno della console o in un test, probabilmente non gradiremmo ricevere una mail di conferma :)

Con l'utilizzo di Service Objects i controller possono essere ultra-concisi ed è possibile testare tutte le possibili condizioni di comportamento dell'applicazione fuori da un controller, migliorando il design complessivo, il decoupling degli oggetti e i tempi di esecuzione dei test.

Il Domain Service Objects non è l'unica tipologia di service disponibile: esistono anche Application Services ed Infrastructure Services. Questo articolo di Jared Carrol esplora con maggior dettaglio la tematica.

DCI

Il DCI è il pattern più "complesso" che tratteremo (se così si può dire) ed è anche il più recente in ordine temporale ad essere stato suggerito dalla comunità all'interno di applicazioni Rails. Gim Gay, con il suo libro in-progress Clean Ruby, è il principale esponente di questa tecnica.

Grossolamente possiamo considerare il DCI come una unione più strutturata dei pattern Concern e Service Object, finalizzata alla descrizione di uno o più Use Cases espressi in termini di dominio del problema.

Il DCI è composto da 3 differenti "parti" interagenti:

  • Data: possiamo tranquillamente figurarceli come modelli Active Record "stupidi", senza logica aggiuntiva, concentrati unicamente sull'aspetto "persistenza dei dati";
  • Interaction/Roles: sono molto simili a Concern, dunque blocchi di logica applicabili a più di un modello differente. C'è un'unica differenza: vengono applicati a run-time ad un modello solo nel momento in cui questo deve assumere un determinato ruolo, e come un Presenter, non intaccano altre istanze del medesimo modello presenti nel sistema;
  • Context: è simile per molti versi ad un Service Object: dopo aver applicato un Role ad uno più più modelli, si occupa di definire le interazioni tra di loro atte ad implementare un caso d'uso.

Vediamo un classico esempio DCI:

 1 class Account < Struct.new(:owner, :amount)
 2 end
 3 
 4 class MoneyTransferContext < Struct.new(:source, :destination)
 5   def transfer(amount)
 6     # applico i ruoli ai modelli "stupidi"
 7     source.extend SourceRole
 8     destination.extend DestinationRole
 9 
10     # definisco le interazioni tra i ruoli
11     source.draw_money(amount)
12     destination.deposit(amount)
13   end
14 
15   module SourceRole
16      def draw_money(amount)
17        self.amount -= amount
18      end
19   end
20 
21   module DestinationRole
22     def deposit(amount)
23       self.amount += amount
24     end
25   end
26 end
27 
28 my_account = Account.new("Stefano", 100.0)
29 other_account = Account.new("Matteo", 50.0)
30 
31 MoneyTransferContext.new(my_account, other_account).transfer(10.0)
32 
33 puts my_account.amount    # => 90.0
34 puts other_account.amount # => 60.0

Quali vantaggi otteniamo rispetto ai precedenti pattern?

  • I Concern "sporcano" i modelli aggiungendo logica a ciascuna delle sue istanze, con potenziali conflitti tra differenti Concern. Dal punto di vista tecnico, i modelli continuano a rimanere "fat models". I ruoli DCI vengono invece applicati a run-time sui modelli, solo quando necessario.
  • Il Concern non spiega il "perchè": è difficile capire quando e come un certo Concern viene utilizzato all'interno dell'applicazione: i suoi metodi possono venire richiamati in ogni parte del codebase. Nel DCI, l'utilizzo di un determinato Role è circoscritto all'interno del suo Context, rendendo banale la comprensione del suo utilizzo.

Edit: Matteo Centenaro fa giustamente notare nei commenti che l'#extend non è l'unica tecnica che si può utilizzare per implementare i ruoli. La classe SimpleDelegator può essere utilizzata anche in questo contesto, per ottere oltretutto miglioramenti dal punto di vista delle prestazioni.

Per concludere…

A prescindere da inutili guerre di religione, ritengo estremamente positiva la proposta a livello di framework di una soluzione ufficiale al problema dei "Fat Models".

Rails è dichiaratamente ottimizzato per la fase di prototipazione iniziale di un'app e preferisce da sempre soluzioni semplici ai problemi (vedi la guerra tra Test Unit ed RSpec). Era dunque praticamente scontata la scelta del pattern più "lightweight" a disposizione, quello dei Concern. E probabilmente, è anche giusto così.

Concern e Presenters sono i primi, semplicissimi strumenti da utilizzare come primo livello di modularizzazione della logica.

Ho utilizzato spesso con successo e soddisfazione la tecnica dei Service Objects per "spostare" logiche complesse al di fuori di controller e modelli: anch'essi sono molto semplici da introdurre nei progetti Rails, e facilitano la "testabilità" del proprio dominio del problema.

Sto iniziando a sperimentare a piccoli passi il DCI in un paio di progetti, e l'impressione è quella di un pattern bene adattabile al mondo Rails, facilmente inseribile in un progetto anche di medie dimensioni.

Buon divertimento!

Commenti

Persistere sessioni di lavoro con Vim

Vim continua a stupirmi. Spesso e volentieri mi sono ritrovato a chiedermi se esistesse un modo per ripristinare lo stato delle viste così come lasciate prima dell'ultima chiusura.

Esce fuori l'esistenza del comando :mksession che permette di salvare su file lo stato corrente dell'editor sotto forma di Vimscript.

Con l'opzione sessionoptions è anche possibile specificare con un buon livello di granularità cosa salvare su file e cosa ignorare. Ovviamente è possibile caricare in un secondo momento il Vimscript con un :source session.vim.

vim-session è un plugin che estende il comportamento base di :mksession e rende più comodo gestire una serie di sessioni.

1 let g:session_directory = "."
2 let g:session_autoload = 'yes'
3 let g:session_autosave = 'yes'
4 set sessionoptions-=buffers

Configurato in questo modo, permette di dare vita ad un workflow comodissimo: per ogni progetto, è sufficiente richiamare il comando :SaveSession la prima volta. Verrà salvato un file default.vim all'interno della directory del progetto, che verrà automaticamente aggiornato ad ogni chiusura dell'editor, e ripristinato lo stato precedente ad ogni apertura dell'editor nella medesima cartella.

L'opzione set sessionoptions-=buffers specifica la volontà di non ripristinare lo stato dei buffer nascosti.

Commenti

OO Rails e Thin models: quando Rails non basta

Un paio di settimane fa ho acquistato una copia di Objects on Rails: la lettura è stata molto interessante, e ha chiarito alcuni dei dubbi "architetturali" sorti negli ultimi progetti Rails più complessi che ho incrociato.

Rails, nella sua forma base, è ottimo per bootstrappare un progetto, ma al crescere della sua complessità alcune "scelte" di default iniziano a risultare strette: la mancata separazione tra fra domain layer ed ActiveRecord, la commistione tra logica e template e la proceduralità degli helpers prima di tutte.

Questo ovviamente non vuol dire che Rails fa schifo. E' perfettamente normale partire "agili", ed aggiungere livelli "logici" object-oriented aggiuntivi sopra le normali convenzioni Rails.

Qui in fondo una buona lista di referenze per approfondire questo discorso:

Commenti

Search e replace su più files in Vim

Ritorno su un argomento a me caro, già affrontato in passato: la ricerca e sostituzione di stringhe su più files. Sebbene greplace.vim finora abbia funzionato piuttosto bene, fa sempre comodo essere a conoscenza di possibili alternative, soprattutto se non richiedono l'uso di plugin aggiuntivi.

In Vim, la modalità generica per gestire operazioni contemporanee su più file si basa sul concetto di argument list:

1 :help argument-list

L'argument list è una variabile globale che rappresenta un insieme di path su cui è possibile effettuare un determinato comando.

E' possibile vedere in ogni momento il valore della propria argument list attraverso il comando :ar[gs], aggiungere nuovi path col comando :arga[dd], e così via.

La cosa si fa interessante quando si scopre che è possibile anche impostare l'argument list pari a tutti i files con una determinata estensione con questo comando:

1 :args **/*.rb

O utilizzare un comando esterno attraverso l'uso di backticks:

1 :args `ack --ruby foobar`

Una volta impostata la lista di files da utilizzare, è possibile lanciare un comando in "batch mode" su tutti i files specificati col comando :argdo. Nel caso di search e replace, il comando sarà ovviamente un :s[ubstitute]:

1 :argdo %s/foobar/barfoo/e

Il flag e evita di lanciare un errore nel caso in cui in un certo file non dovesse venire trovata nessuna occorrenza della regular expression. Il comando in questo modo non salverà il risultato finale su file. Per ottenere questo effetto è sufficiente aggiungere il comando :update:

1 :argdo %s/foobar/barfoo/e | update

Per rendere l'operazione di ricerca più "sicura", possiamo sempre contare sul flag c, che richiederà una conferma esplicita di ogni sostituzione:

1 :argdo %s/foobar/barfoo/ec

Sarebbe brutto terminare senza un oneliner, vero? Questo ad esempio reindenta tutti i file ruby presenti nel progetto:

1 :args **/*.rb | argdo execute "normal gg=G" | update

Commenti

How CoffeeScript deprecates JavaScript

Il 23 maggio ho avuto il piacere di presentare il mio primo talk nel primo appuntamento del Florence On Ruby!

E' stata un'ottima esperienza, che sicuramente bisserò il prima possibile. CoffeeScript è un linguaggio che continua a venire visto con diffidenza e per mostrarne l'utilità si è costretti a portare esempi concreti, con del JavaScript mediamente avanzato (closure wrappers e function binding, per esempio).

Sembra che i vari concetti siano stati assorbiti piuttosto bene, cosa che mi rende felice. Condivido qui slide e il talk stesso (la qualità è quella che è, ma il parlato mi sembra abbastanza comprensibile).


Commenti

Redpomo: la mia risposta al time-tracking

Quando si tratta di gestire una piccola compagnia di sviluppo software, essere in grado di stimare i tempi di consegna dei progetti in corso, e dunque poter pianificare le disponibilità future dei (pochi) sviluppatori è fondamentale.

Uno degli strumenti più semplici ed efficaci in grado di fornirci delle metriche tali da farci arrivare a quest'obiettivo è proprio il time-tracking.

Il problema: time-trackare fa schifo

Sei uno sviluppatore? Bene, allora sai quanto sia intimamente disturbante e innaturale l'atto del time-tracking, soprattutto se imposto dall'alto. Nonostante riconosca il suo valore nel medio periodo, non c'è cosa più odiosa e facile da dimenticare.

Quando farlo? A fine giornata? Ci sono giornate così lunghe che ricordarsi con un minimo di dettaglio cosa e quanto è stato fatto la mattina è semplicemente impossibile. Ad ogni context switch? Figuriamoci.. non è raro dover alternare task ogni 5-10 minuti.. e se ogni volta si dovesse segnare il tempo..

Bisogna fare qualcosa!

Pomodoro

Una tecnica molto semplice e che consiglio a tutti per aumentare la propria capacità di concentrazione — e dunque ridurre i context-switch — è quella del Pomodoro:

  • Scegli un task da completare;
  • Imposta il Pomodoro a 25 minuti (il Pomodoro è il timer);
  • Lavora sul task senza distrazioni o interruzioni fino a che il Pomodoro non suona;
  • Prenditi un piccolo break (5 minuti vanno bene);
  • Ogni 4 pomodori prenditi una pausa un po' più lunga.

Nonostante la banalità (o proprio grazie a questa) posso garantire che funziona, e rende drammaticamente visibile fin da subito la quantità di cambi di contesto che mediamente eseguiamo senza rendercene conto.

Ogni 25 minuti il rumore di un timer da cucina parte a festeggiare il periodo di concentrazione appena concluso, ed il ticchettio continuo dopo poco tempo verrà associato a sentimenti positivi e di "buon lavoro".

Come avrete intuito, il Pomodoro è involontariamente un'ottima forma di time-tracking: non viene percepito dallo sviluppatore come una inutile imposizione dall'alto, ma come strumento di supporto attivo al proprio lavoro: da quando lo utilizzo, le mie ostilità verso il concetto di time-tracking sono pressochè terminate.

Redpomo

Dopo l'illuminazione di qualche mese fa col Pomodoro, il mio workflow nell'ultimo mese si è ulteriormente raffinato. Il risultato finale è Redpomo, una gemma che integra e amalgama insieme i 3 strumenti che utilizzo giornalmente.

Qui in fondo uno screencast per spiegarvi meglio di cosa si tratta.

Commenti

Un mese di nuove sfide!

In effetti mi sembrava che fosse tutto troppo tranquillo ultimamente. Qualcosa doveva capitare. Sta capitando.

Innanzitutto, nuova sfida lavorativa! Era da qualche tempo che seguivo le mosse di Cantiere Creativo, una delle principali realtà Ruby a Firenze, ed il caso ha voluto che fosse proprio Matteo a contattarmi per una proposta allettante: tentare di sostituire degnamente Silvio, in procinto di iniziare la sua avventura con Extendi.

In Cantiere ho ritrovato molti dei punti fermi che stiamo cercando di perseguire in weLaika: passione per la trasparenza, impegno nel mondo open source, voglia di fare community, ricerca di qualità.

Ho pensato che un po' di sana compagnia nel mio lavoro quotidiano da remote worker per weLaika non poteva che fare bene, così come il mettere il piede in una società per certi versi molto simile a quella che desideriamo costruire, ma con maggiore esperienza e contatti.

Per qualche mese tenterò quindi l'esperimento di far convivere il mio costante impegno in weLaika, con questa nuova posizione di responsabilità in Cantiere, continuando lo sforzo di Silvio nello sviluppo del CMS open Rails-based Railsyard.

Se latito, ora sapete il perchè.

Appuntamenti per coders!

A Marzo, con la bella stagione, parte il primo gruppone di eventi per nerd. Prima di tutto, il CodeMotion di Roma, il 23 e 24 marzo. Ci sarete, vero?

In weLaika, in forte astinenza del RailsRumble che sembra latitare, ci auto-organizzeremo in 48 ore di forsennato coding in compagnia, se qualcuno in Torino volesse unirsi sarà il benvenuto!

In Cantiere si sta cercando di organizzare verso fine mese il primo Ruby Social Club fiorentino, anche se è probabile che non si chiami proprio così. Non c'è ancora una data precisa, ma appena si saprà qualcosa di preciso batterò un colpo su Twitter.

Commenti

Faster TDD with iTerm and vim

A couple of weeks ago Josh Davey released turbux, 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 tmux.

Before you continue reading this post, check out the announcement post on his blog, so you know what we're talking about.

iTerm anyone?

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.

In my day-to-day work I make use of iTerm 2, 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.

I asked myself whether I really needed to add an "extra layer" 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.

How to communicate with iTerm

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:

1 #! /usr/bin/osascript
2 tell application "iTerm"
3   tell the current terminal
4     tell (first session Whose name contains "foo")
5       text write ("echo bar" as text)
6     end tell
7   end tell
8 end tell

This code searches in the current iTerm window for a session named "foo", and sends the command echo bar to it. As you might have guessed, we just built a bridge between vim and an iTerm session.

Welcome iTermux!

Next step was to fork the turbux plugin, rename it into itermux (how original, right?) and replace its Send_to_Tmux() function with a parametrized version of the snippet mentioned above:

 1 function! Send_to_iTerm(command)
 2   let app = 'iTerm'
 3   if exists("g:itermux_app_name") && g:itermux_app_name != ''
 4     let app = g:itermux_app_name
 5   endif
 6   let session = 'iTermux'
 7   if exists("g:itermux_session_name") && g:itermux_session_name != ''
 8     let session = g:itermux_session_name
 9   endif
10 
11   let commands =  [ '-e "on run argv"',
12                   \ '-e "tell application \"' . app . '\""',
13                   \ '-e "tell the current terminal"',
14                   \ '-e "tell (first session whose name contains \"' . session . '\")"',
15                   \ '-e "set AppleScript''s text item delimiters to \" \""',
16                   \ '-e "write text (argv as text)"',
17                   \ '-e "end tell"',
18                   \ '-e "end tell"',
19                   \ '-e "end tell"',
20                   \ '-e "end run"' ]
21 
22   let complete_command = "osascript " . join(commands, ' ') . " " . a:command
23   system(complete_command)
24 endfunction

As you can see, I'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.:

1 let g:itermux_session_name = 'testing'
2 let g:itermux_app_name = 'iTerm2'

Demo Time

I've prepared a small video to show how cool it is the final result. I've been using this setup for two weeks now, and it's been so rewarding.

Feedbacks appreciated!

Download it, install it, and let us know what you think of it!

Commenti

Come testare i propri controller in isolamento: un esempio reale con CanCan

TL;DR 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 stubs e mocks.

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 CanCan. Mi sembra questo un ottimo esempio concreto da sfruttare per la trattazione.

Nella Wiki del progetto su Github, 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.

Partiamo con l'analizzare meglio i problemi.

Continua a leggere

Commenti

Ottimizzare la leggibilità del proprio sito con Compass /3

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 em, arriviamo all'ultimo passaggio: come scegliere in maniera ottimale line-height e font-size per il tag <body>, ovvero le dimensioni che saranno di riferimento per l'intero sito?

La soluzione non è ovviamente farina del mio sacco, ma si rifà al concetto di Golden Ratio Typography, ideato da Chris Pearson.

L'impianto della trattazione si basa su due evidenze provabili empiricamente da chiunque:

  • Le proprietà font-size e line-height sono legate linearmente: per mantenere un testo leggibile, se una aumenta l'altra dovrà aumentare proporzionalmente;
  • Similmente, larghezza del corpo del testo (da qui in poi line-width) e line-height sono legate linearmente: più la larghezza aumenta, più difficile diventa per l'occhio umano seguire le linee di testo, e dunque diventa necessario aumentare anche l'interlinea.

Chris Pearson si è spinto oltre, cercando di legare numericamente queste grandezze, facendo uso del famoso rapporto aureo:

Line heightLine width

Quindi, è sufficiente scegliere il font-size di riferimento, per esempio 16px, e line-height e line-width verranno da sè:

1 $phi-const: 1.61803399
2 $base-font-size: 15px
3 $base-line-height: $base-font-size * $phi-const
4 $base-line-width: $base-line-height * $base-line-height

In linea teorica, il concetto termina qui. Dal punto di vista pratico però, i nostri schermi lavorano su un'unità di misura non "spezzabile": il pixel. Il calcolo riportato porterebbe ad una interlinea di 24.27px, ovviamente non realizzabile. Occorrerebbe portarlo a 24px, ma non sarebbe l'arrotondamento ottimale.

Matematicamente parlando, ciò che bisogna realizzare è un "minimizzatore" di errore, in grado di ricercare il minimo locale. Trovare dunque la combinazione di pixel "interi" per font-size, line-height e line-width che globalmente più si avvicinano ai valori aurei.

Non si tratta di calcoli piacevolissimi, e fortunatamente Chris Pearson ha creato il Golden Ratio Typography Calculator, che fa tutto il lavoro sporco per noi. Possiamo fissare sul calcolatore il font-size prescelto, o piuttosto la larghezza del contenuto, o entrambi. Il buon calcolatore ci restituirà il line-height ottimale, oltre a suggerirci eventuali cambiamenti di dimensioni che potrebbero ulteriormente migliorare la resa.

Niente. Dovrei aver finito, siete pronti per rendere i vostri blog super-leggibili. La mia parentesi tipografica si chiude temporaneamente.

Per un po' ritornerò a parlare di codice, tante cose bollono in pentola!