Rails Best Practices 5: Ottimizzare le Migration

Le migration sono a mio avviso una delle cose migliori presenti in rails poichè permettono la creazione ed il popolamento del database tramite codice ruby e senza doversi preoccupare del tipo di db sottostante.
Detto questo, anche nello scrivere le migration ci sono alcune best practices che è utile seguire.


1. Indici
La prima pratica che consiglio caldamente è la definizione degli indici per le chiavi esterne e per tutte quelle colonne su cui si andranno a fare ordinamenti, ricerche e raggruppamenti.
Prendiamo una migration di esempio:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class CreateInvoices < ActiveRecord::Migration
def self.up
create_table :invoices do |t|
t.integer :number
t.integer :year
t.decimal :total_amount
t.date :invoice_date
t.integer :company_id
t.integer :client_id
t.timestamps
end
end

def self.down
drop_table :invoices
end
end

Questa è la classica migration che possiamo trovare generata da rails dopo l’esecuzione di comandi quali generate Model o generate Scaffold.
Andiamo ora ad aggiungere gli indici per le chiavi esterne e per i campi di ordinamento.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class CreateInvoices < ActiveRecord::Migration
def self.up
create_table :invoices do |t|
t.integer :number
t.integer :year
t.decimal :total_amount
t.date :invoice_date
t.integer :company_id
t.integer :client_id
t.timestamps
end

add_index :invoices, :company_id
add_index :invoices, :client_id
add_index :invoices, :number
add_index :invoices, :year
end

def self.down
drop_table :invoices
end
end

La definizione degli indici è importante al fine delle prestazioni. Una tabella indicizzata aumenta infatti le performance delle query sql. Il mio consiglio dunque è: indicizzate!
Esistono anche alcuni plugin che aiutano ad individuare i campi da indicizzare.
Effettuando una breve ricerca su github ne potete trovate diversi, io ve ne segnalo solo alcuni:

2. Seed dei dati
Dalla versione 2.3.4 rails ha introdotto il concetto di seed.
In realtà l’idea era presente anche nelle versioni precedenti ma dalla 2.3.4 è stato creato un apposito file ed un apposito comando per eseguire il seed (ovvero il popolamento) dei dati.
Ciò che spesso accade quando si inizia a sviluppare web application con Ruby on Rails è di scrivere nelle migration sia le istruzioni di creazione delle tabelle sia quelle per popolarle con dei valori di default.
Una migration di questo tipo è riportata nell’esempio seguente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

class CreateCompanies < ActiveRecord::Migration
def self.up
create_table :companies do |t|
t.string :name
t.string :street
t.string :city
t.string :country
t.string :email
t.timestamps
end

Company.create(:name => "DevInterface",
:street => "Via Postale Vecchia",
:city => "Verona",
:country => "Italy",
:email => "info@devinterface.com",
:fax => "045/1234567")
Company.create(:name => "CDF Tech Solutions",
:street => "Via XX Settembre",
:city => "Verona",
:country => "Italy",
:email => "info@cdf.com",
:fax => "045/1234567")
end

def self.down
drop_table :companies
end
end

Una migration scritta in questo modo viene eseguita correttamente ma è sempre consigliabile tenere separata la struttura del db dai dati. Questo per poter ripopolare il database con dati di test piuttosto che con dati reali a seconda delle esigenze senza dover ogni volta ricrearlo.
Vediamo di seguito come scrivere la migration ed il file di seed per separare i dati dalla struttura.
Migration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class CreateCompanies < ActiveRecord::Migration
def self.up
create_table :companies do |t|
t.string :name
t.string :street
t.string :city
t.string :country
t.string :email
t.string :fax
t.timestamps
end
end

def self.down
drop_table :companies
end
end

seeds.rb

1
2
3
4
5
6
7
8
9
10
11
12
13

Company.create(:name => "DevInterface",
:street => "Via Postale Vecchia",
:city => "Verona",
:country => "Italy",
:email => "info@devinterface.com",
:fax => "045/1234567")

Company.create(:name => "CDF Tech Solutions",
:street => "Via XX Settembre",
:city => "Verona",
:country => "Italy",
:email => "info@cdf.com",
:fax => "045/1234567")

A questo punto una volta creato il db sarà sufficiente eseguire il comando rake db:seed per popolarlo.

Utilizzare il file di seed è utile anche per popolare il database con grandi quantità di dati generati in maniera casuale o con dati specifici per simulare particolari condizioni.
A tal scopo esistono anche dei plugin per la generazione di dati pseudo-casuali.
Vi elenco quelli che utilizzo maggiormente:

Concludo questo post sulle migration con un esempio di file seed con dati generati in modo pseudo-casuale che popola il database con 5 company ed utilizza i tre plugin elencati sopra:

1
2
3
4
5
6
7
8
9
10
11
12

require 'populator'
require 'faker'
require 'random_data'

[Company].each(& :delete_all)

Company.populate(5) do |comp|
comp.name= Faker::Lorem.words
comp.street= Faker::Address.street_address
comp.city= "Verona"
comp.fax = '045 / ' + Random.number(20).to_s + Random.number(450).to_s
comp.email = Faker::Internet.email