Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

ActiveRecord - un ORM per Ruby

Una panoramica introduttiva su un ORM che ha fatto scuola
Una panoramica introduttiva su un ORM che ha fatto scuola
Link copiato negli appunti

In questo articolo parliamo di ActiveRecord, una libreria scritta in Ruby da David Heinemeier Hansson per l'accesso alle basi di dati. Più correttamente si tratta di un design pattern originariamente pubblicato da Martin Fowler nel suo libro Pattern of Enterprise Application Architecture, edito da Addison-Wesley nel 2002, di cui il creatore di Rails ha fornito una implementazione in Ruby, rendendola poi parte integrante del core di Rails.

ActiveRecord stesso implementa al suo interno un altro pattern molto utile: il Single Table Inheritance. Come si legge dal libro di Fowler: «i database transazionali non supportano l'ereditarietà, pertanto quando si esegue il mapping da oggetti al database è necessario considerare come rappresentare l'ereditarietà delle strutture in tabelle relazionali. Il pattern STI mappa tutti i campi di tutte le classi di una struttura di eridità in una singola tabella»

L'idea alla base di ActiveRecord é quella alla base di un ORM, acronimo di Object-relational mapping, cioè una ragionevole rappresentazione in codice object-oriented che vede le tabelle del db diventare classi, le righe diventare oggetti ed i campi delle tabelle diventare attributi.

Rispetto agli ORM classici presenta dei vantaggi non indifferenti, enfatizzando l'idea di configurazione guidata da convenzioni, in modo da fare delle assunzioni di default senza la necessitá di dover specificare le convenzioni da utilizzare, o di scrivere file di configurazione o ancora dettagliare il mapping.

Riportiamo qualche esempio in tal senso:

  • tutte la tabelle presentano una chiave primaria artificiale chiamata "id", senza tralasciare, ovviamente la possibilità di sovrascriverla tramite la sequenza di istruzioni
    	:primary_key => mykey
    	:id => false                    
    
  • il nome della tabella corrisponde alla forma plurale del nome della classe modello
  • gli attributi dei nostri oggetti, che sono poi i campi delle tabelle, sono automaticamente letti dalla base di dati, senza la necessità di dover ripetere gli schemi.

ActiveRecord è stato principalmente designato per eseguire le operazioni tipiche che vanno sotto l'acronimo di CRUD (Create-Read-Update-Delete) in modo molto semplice, veloce e senza la necessità di dover scrivere codice SQL.

Elenchiamo alcuni dei vantaggi derivanti dall'uso di ActiveRecord ed usiamo l'elenco per definire la roadmap da seguire nel corso dell'articolo:

  • semplificazione della configurazione
  • mapping automatico tra tabelle e classi e tra colonne ed attributi
  • validazione dei dati
  • logging
  • migrations
  • aggregazione

C'è da considerare che ActiveRecord è scritto in Ruby, quindi tutto ció che é possibile fare con gli oggetti in Ruby, risulta fattibile con ActiveRecord e, questo risulta essere una grande potenzialità

Iniziamo ora il nostro cammino partendo dall'installazione di ActiveRecord, passando poi ad esaminare alcune caratteristiche che lo rendono realmente differente da altri ORM e, ove possibile, mostrando qualche breve esempio.

Installazione

Se si ha giá una esperienza con Rails, allora si ha già tutto l'occorrente per lavorare con ActiveRecord, altrimenti é possibile installarlo stand-alone ed usarlo al di fuori del framework di cui é parte del core.

Non dimentichiamo di dire che ActiveRecord é disponibile come adapter per i più popolari DB. Possiamo trovare la lista degli adapter disponibili partendo dalla directory di installazione di Ruby e navigando il seguente percorso:

/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/

L'installazione completa avviene in due step: installazione della gemma (attualmente alla versione 2.0.2)

gem install activerecord

installazione delle librerie per l'adapter scelto. Per questo articolo faremo riferimento a MySQL.

gem install mysql

Connessione con il database

ActiveRecord mette a disposizione un metodo unico per la connessione con la nostra base dati indipendentemente da quale dbms abbiamo deciso di usare. Il metodo in questione è establish_connection.

Facciamo un primo esempio: eseguiamo una connessione ad un database MySQL. Supponiamo di avere una tipica tabella nel database di nome Users con gli altrettanto tipici campi firstname, lastname, email, user, pwd e di voler connetterci al db ed inserire un nuovo utente. Creiamo un semplice script Ruby come questo:

Listato 1. Connessione con il db ed inserimento di un nuovo utente

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(
      :adapter => 'mysql',
      :host => 'localhost',
      :username => 'myusername',
      :password => 'mypwd',
      :database => 'mydb')

class User < ActiveRecord::Base
end

newuser = User.new
newuser.firstname = 'Edgar Allan'
newuser.lastname = 'Poe'
newuser.email = 'eag@example.com'
newuser.username = 'eagname'
newuser.pwd = 'eagpwd'

newuser.save
    
# è equivalente alla creazione tramite il 
# metodo new ma chiama automaticamente save
seconduser = User.create(:firstname => "Galileo", :lastname => "Galilei", :email => "gg@example.com", :username => "ggalilei", :pwd => "ggpwd")

Questo semplice esempio non fa altro che richiamare gli elementi standard di un programma standard che usa AR:

  1. include la gemma AR
  2. stabilisce una connessione con il db scelto (altri argomenti, come quelli relativi ad una connesione SSL, sono accettati da establish_connection)
  3. definisce una propria classe estendendo ActiveRecord::Base
  4. esegue una delle operazioni CRUD instanziando un nuovo oggetto di tipo User, impostandone le proprietá e salvandone il contenuto nel db.

Notare come nessuna linea di codice SQL è stata scritta direttamente, anche se corrisponde ad uno statement SQL del tipo:

INSERT INTO users('firstname', 'lastname', 'email', 'username', 'pwd') VALUES('Edgar Allan', 'Poe', 'eag@example.com', 'eagname', 'eagpwd');

Dal codice si evince anche che per utilizzare la classe User basta definirla; in effetti la nostra classe User é vuota.

Interrogazione del database

Il modo più semplice per eseguire una ricerca tra i record è quello di usare il metodo find. Supponiamo di voler recuperare le informazioni su un utente con id pari a 2:

User.find(2)

Il metodo find offre una notevole flessibilitá. Il primo argomento controlla quanti risultati dovranno essere restituiti (es. :first, :all), mentre il secondo puó essere un hash di opzioni, le cui possibili chiavi, ognuna corrispondente a parti di istruzioni SQL, sono: :conditions, :include, :order, :select, :group, :from, :limit, :joins, :readonly, :offset

ActiveRecord presenta delle feature molto interessanti per quanto riguarda le operazioni di select sul db poichè fa uso dei dynamic finders (basati su method_missing di Ruby), i quali ci permettono di eseguire operazioni di ricerca tra i record a partire dai valori degli attributi.

Sempre con riferimento alla tabella Users dell'esempio, eseguiamo una ricerca per cognome. Questa si ottiene facilmente tramite un metodo creato dinamicamente, proprio a partire dal nome della colonna:

User.find_by_lastname('Galilei')

che restituisce lo stesso risultato delle due seguenti ed equivalenti istruzioni:

User.find(:all, :conditions => ["lastname"=?, "Galilei"])            
User.find(:all, :conditions => {:lastname => "Galilei"})

Tutti i dynamic finders hanno il nome che inizia per find_by o find_all_by_ e si aggiunge il nome della colonna sulla quale vogliamo eseguire la ricerca. C'è anche la possibilità di cercare su più colonne contemporaneamente separandone i nomi con _and_.

Una interessante tipologia di dynamic finders é il metodo find_or_create_by_ il quale lavora esattamente come find_by, ma con una eccezione: non restituiisce nil nel caso di matching nullo, bensí crea l'oggetto.

Finora abbiamo eseguito tutte le operazioni di ricerca senza mai scrivere una istruzione SQL, in perfetto stile ORM; questo peró non vuol dire che non sia possibile farlo. ActiveRecord mette a disposizione il metodo find_by_sql a cui si passa come argomento una stringa contenente l'istruzione SQL.

User.find_by_sql("SELECT email FROM users");

Con il metodo find_by_sql non risultano possibili le istruzioni di INSERT e UPDATE, per le quali si deve utilizzare il metodo execute.

L'aggiornamento di un record, rientrando tra le operazioni CRUD, risulta essere molto semplice con ActiveRecord, poichè é una operazione simile alla creazione di uno nuovo. I metodi da usare sono update_attribute (per un singolo attributo) e update_attributes (attributi multipli). Entrambe chiamano automaticamente il metodo save.

Listato 2. Aggiornamento di un record

customer = User.find(3)
customer.update_attributes(:username => 'Pippo', :lastname => 'Pluto')

Per eliminare un record abbiamo a disposizione diversi metodi: destroy, delete, destroy_all, delete_all. La differenza tra i primi due è che il primo cancellerà dal db il record collegato alla sua chiave primaria, mentre il socondo é utilizzabile come il metodo find e quindi é possibile passare uno o più ID, i quali saranno prima istanziati e poi saranno cancellati uno per uno con una chiamata intrinseca al metodo destroy.

Listato 3. Cancellazione di un record: metodi destroy e delete

customer = User.find(5)
customer.destroy # DELETE FROM users WHERE id = 5
    
customer.delete(4, 6, 8) # DELETE FROM users WHERE id IN(4, 6, 8)

destroy_all e delete_all cancellano tutti i record contenuti nella classe ActiveRecord e prendono come secondo argomento un hash di opzioni per costruire la clausola WHERE, proprio come il metodo find.

Associazioni

Nella realtà difficilmente si troveranno tabelle del database non relazionate ad altre, ossia, in termine di ActiveRecord, degli oggetti standalone. ActiveRecord rende semplice la gestione delle relazioni tramite l'uso delle funzioni di associazione. Le associazioni o join tra tabelle hanno alla base una idea semplice. Consideriamo due o più tabelle, ognuna delle quali contiene un insieme di dati logicamente collegabile in qualche modo ad un'altra tabella. Ciò che si vuole è recuperare l'insieme di dati proveniente da tabelle collegate tra loro ma trattare tale insieme come uno unico.

ActiveRecord supporta diverse tipologie di associazioni tra tabelle oltre che modificatori di associazioni, offrendo così una libertà ed un controllo totale delle nostre classi modello ActiveRecord.
Il codice relativo alle associazioni è inserito all'interno della classe modello.

Iniziamo con la descrizione delle funzioni di associazione disponibili.

belongs_to

Si tratta di una relazione one_to_one tra oggetti. L'ID di tale associazione è memorizzata direttamente in questo modello. Un esempio può essere la relazione tra un articolo ed un autore, nel caso si supponga che un articolo è scritto da una sola persona.

has_many

Il complemento di belongs_to. Non viene tenuta traccia della chiave primaria. Questo tipo di associazione restituisce un array di oggetti. Esempio tipico è la relazione che esiste tra le categorie e gli articoli: una categoria ha più articoli.

has_one

in questo caso non abbiamo una collezione di elementi ma soltanto uno. ActiveRecord restituirà un singolo oggetto. Esempio tipico, un autore del nostro social network ha un solo editore.

has_and_belongs_to_many

in una relazione has_many una tabella memorizza la foreign key dell'altra tabella permettendo così una associazione di più record di una tabella con l'altra (una sorta di relazione many_to_one). Poichè ciò che vogliamo è mantenere sempre la base di dati normalizzata, la associazione has_and_belongs_to utilizza una tabella aggiuntiva, una join table, che non memorizza altro se non le foreign keys di ogni tabella associata. Pensiamo all'esempio dei ruoli assegnabili ad un utente. Le nostre classi modello ActiveRecord, User e Role, somiglierebbero a qualcosa del genere:

class User < ActiveRecord::Base
  ...
  has_many :articles
  has_and_belongs_to :roles
  ...
end
   
class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end             

Come detto all'inizio dell'articolo, ActiveRecord fa delle assunzioni di default riguardo la convenzione di nomenclatura di tabelle e altro. Nel caso delle tabelle di join queste avranno un nome che è composto dai nomi delle due tabelle al plurale e separate da un segno di underscore. Nel caso dell'esempio di ruoli e utenti sarà: roles_users.

has_many:through

La relazione many_to_many risulta essere molto potente anche se non é poi cosí inusuale veder nascere la necessità di aggiungere un legame relazionale maggiore rispetto a quello diretto tra tabelle. Immaginiamo uno scenario in cui vogliamo conoscere il nome di un articolo scritto da un certo autore in una data categoria. In ActiveRecord questo tipo di modello e di associazione ha una relazione del tipo has_many :through

La differenza tra has_one e belongs_to sta nella posizione in cui risiede la foregin key. Brevemente possiamo dire che belongs_to è usato quando la chiave primaria del modello a cui si riferisceè memorizzata nella classe del modello stesso, mentre has_many quando la chiave primaria è memorizzata nell'altra tabella

Validazione

La validazione dei dati con ActiveRecord è un concetto di facile implementazione e avviene applicando una serie di regole di validazione di facile comprensione. Ovviamente la validazione si ha prima dell'esecuzione delle istruzioni di INSERT e UPDATE sulla tabella.

Tecnicamente, tutti gli oggetti di ActiveRecord hanno un oggetto della classe ActiveRecord::Error associato. Quando tale oggetto risulta vuoto alla fine della fase di validazione dei dati, il mapping di quanto contenuto nell'oggetto ActiveRecord sul db é eseguito automaticamente; in caso caso di fallimento di qualche regola di validazione, un messaggio sarà inviato all'oggetto ActiveRecord::Error che conterrà i dati su cui la validazione è fallita, evitando in questo modo l'esecuzione delle istruzioni SQL durante quella stessa chiamata.

Il codice relativo alla validazione viene inserito all'interno dei file delle classi ActiveRecord (l'elemento model di un framework MVC).

ActiveRecord mette a disposizione una serie di funzioni di validazione pronte per l'uso, il cui nome é esplicativo rispetto al funzionamento. Vediamone alcune, rimandando alla documentazione ufficiale per una trattazione completa.

  • validates_confirmation_of: tipicamente usata per confermare l'uguaglianza dei valori di due attributi (campi). Un esempio potrebbe essere quello del campo password all'interno di un form di registrazione
  • validates_presence_of: il valore di un attributo non puó essere vuoto
  • validates_acceptance_of: usato per semplificare, ad esempio, nel caso in cui risulti necessario selezionare un checkbox prima dell'invio della richiesta
  • validates_length_of: semplifica la validazione di un attributo riguardo alla lunghezza del suo valore o del massimo range di caratteri
  • validates_uniqueness_of: controlla l'unicità del valore dell'attributo all'interno del database
  • validates_format_of: il valore dell'attributo deve soddisfare una espressione regolare
  • validates_inclusion_of: il valore dello specifico attributo deve essere contenuto in un oggetto aggiuntivo che va specificato, ad esempio un array. L'opzione :in ci permette di specificare quale oggetto usare per il confronto. Altre opzioni sono :message, :allow_nil.
  • validates_exclusion_of: il complementare del precedente
  • validates_numerically_of: il valore di un attributo può contenere solo caratteri numerici
  • validate_associates: gli oggetti con cui il nostro modello ha delle relazioni, devono essere validi. Questo tipo di funzione di validazione non verifica l'esistenza degli oggetti associati ma, solo la validità. La validazione tramite questa funzione deve essere inserita solo in un lato dell'associazione, in caso contrario si attiverebbe un loop infinito
  • validates_each: valuta ogni attributo in lista con il blocco associato

Ecco alcuni esempi di validazione

Esempio di validates_length_of

class User < ActiveRecord::Base
  validates_length_of :username, :within => 6..10
  validates_lenght_of :password, :is => 8
end

Esempio di validates_inclusion_of

class User < ActiveRecord::Base
  validates_inclusion_of :username, :in => ['Galileo']
end

Esempio di validate_associates

class User < ActiveRecord::Base
  has_many :articles
  validate_associates :articles
end
            
class Article < ActiveRecord::Base
  belong_to :users
end

Esempio di validates_each

class Article < ActiveRecord::Base
  validates_each :title do |rec, attr, val|
    if val != 'Test'
      rec.errors.add(':title', 'Il titolo per gli articoli di prova deve essere Test')
    end
  end
end

Oltre alle funzioni appena elencate vi è la possibilità di poter definire le proprie funzioni di validazione aggiungendo alla nostra classe modello un metodo validate in cui inviamo messaggi all'oggetto ActiveRecord::Error nel caso la validazione fallisca.

Listato 4. Esempio di costruzione di una funzione di validazione

class User < ActiveRecord::Base
  def validate
    if self.email == nil
      errors.add(':email', 'Specificare un indirizzo email valido')
    end
  end
        
  protected :validate
end

Sono presenti anche le funzioni validate_on_create e validate_on_update

Migrations

Passiamo ora ad analizzare una feature di ActiveRecord che rende la sua potenza e facilità d'uso senza precedenti. Parliamo di migrations. Si tratta di una soluzione in puro codice Ruby per la gestione, la creazione e l'evoluzione di uno schema database. Un set di istruzioni sotto forma di DSL create per astrarre le differenze tra i diversi database e per gestire i cambiamenti all'interno dello stesso e delle sue tabelle.
Lavorare con le migrations vuol dire pertanto lavorare con ua serie di isruzioni Ruby e una serie di chiavi e relative azioni.

I vantaggi di lavorare con le migrations sono diversi, vediamone alcuni:

  • i cambiamenti al nostro database avvengono tramite script. Ciò rende possibile la ricostruzione da zero dell'intera base di dati eseguendo gli script in sequenza, così come vi è la possibilità, sel l'engine del db lo permette, di eseguire un rollback ad una precedente configurazione
  • per tenere traccia degli aggiornamenti ad una tabella, la stessa dovrebbe avere dei campi chiamati created_at e updated_at. Quando si esegue una migration, ActiveRecord aggiorna automaticamente questi campi con un timestamp
  • i campi di una tabella vengono popolati automaticamente e, se per un campo non viene specificato un valore ActiveRecord gliassegna nil

Il pattern ActiveRecord, come detto nell'introduzione di questo articolo, è stato implementato dal creatore di Rails ed è pertanto nato per Rails. Allo stesso modo il meccanismo delle migrations è nato e cresciuto per questo framework. Questo non vuol dire che non sia utilizzabile al di fuori del framework, ma che questo richiede qualche step in più.

Rails mette a disposizione una serie di script per la generazione delle migrations, così come per i controller etc. e lo fa sfruttando rake (una sorta di make per Ruby). Tutti gli script di migration hanno una struttura di base simile alla seguente:

Listato 5. Struttura di un file di migration

class User < ActiveRecord::Migration
  def self.up
  end
        
  def self.down
  end
end

Il metodo up viene richiamato quando si esegue una migrazione ad una versione successiva del database (parametro schema_info), mentre il metodo down quando si migra ad una versione precedente. Entrambi i metodi sono invocati ad ogni transazione, garantendo così, nel caso di eccezione, l'annullamento dell'esecuzione e di conseguenza il non incremento del version number memorizzato in schema_info.

Tipi di dati
:primary_key :string :integer
:text :float :decimal
:boolean :binary :datetime
:date :timestamp :time
Opzioni
:limit
:default
:null
:precision
:scale

Creazione di una tabella

Come primo esperimento con il meccanismo delle migrations creiamouna tabella. Per farlo utilizziamo il metodo create_table

Listato 6. Script per la creazione della tabella user (prima versione)

def self.up
  create_table :users do |t|
    t.column :username, :string, :limit => 64, :null => false
    t.column :email, :string, :limit => 128, :null => false
    t.column :password, :string, :limit => 64      
    t.column :password_confirmation, :string, :limit => 64
    t.column :profile, :text
    t.column :created_at, :datetime
    t.column :updated_at, :datetime
    t.column :last_login_at, :datetime
  end
end

Possiamo cancellare la tabella appena creata ricorrendo al metodo drop_table

Listato 7. Rimozione di una tabella

def self.down
  drop_table :users        
end

Aggiungere, rimuovere e modificare le colonne

Con le migration possiamo lavorare in modo astratto con tutte le parti del nostro engine del database. Oltre che creare o cancellare tabelle, possiamo ad esempio aggiungere colonne e rimuovere colonne ed indici. Queste operazioni sono eseguite con i metodi add_column, remove_column, add_index e remove_index, così come è possibile, tramite i metodi rename_column e change_column, modificare rispettivamente, il nome della colonna ed il tipo della colonna.

Listato 8. Modifica di una tabella

def self.up
  add_column :users, :user_id, :integer
end
    
def self.down
  remove_column :users, :user_id
end
    
rename_column :users, :created_at, :born_at

Conclusioni

Come detto precedentemente, per poter utilizzare il meccanismo delle migrations al di fuori del framework Rails ciò che ci viene richiesto è scrivere uno script.

Prima di mostrare un semplice script per lanciare una migration, accenniamo soltanto all'importanza dei test durante le fasi di sviluppo. Anche in questo caso Rails mette a disposizione dello sviluppatore una serie di utilità che rendono i test semplici da implementare, facilitandone l'uso in modo continuo. In questo modo alcuni suggerimenti provenienti dalle metodologie Agili di sviluppo sono garantiti.

Concludiamo l'articolo con uno script che usa rake per lanciare un task di migration per creazione di una tabella nel db (la tabella di esempio finora usata, User) e un task utile per i nostri test.

Come prima cosa, creiamo una serie di cartelle che ci forniscano una struttura simile a quella creata da Rails:

  • config/db contenente un file database.yml di configurazione per la connessione al database
  • db/migrate contenente gli script di migration
  • test/fixtures contenente un file users.yml che useremo come fonte di dati per i nostri test

database.yml

development:
adapter: mysql
database: mydb
username: username
password: password

script di migration

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :username, :string, :limit => 64, :null => false
      t.column :email, :string, :limit => 128, :null => false
      t.column :hashed_password, :string, :limit => 64
      t.column :enabled, :boolean, :default => true, :null => false
      t.column :profile, :text
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
      t.column :last_login_at, :datetime
    end
  end
        
  def self.down
    drop_table :users
  end
end

users.yml

                        
validuser1:
  name: Pippo
  created_at: <%= 2.day.ago %>
validuser2:
  name: Pluto
  created_at: <%= 1.day.ago %>

Fatto ciò scriviamo il nostro Rakefile, automaticamente richiamato dal comando rake, il quale provvederà al caricamento dei file precedentemente elencati e all'esecuzione dei task descritti.

Listato 6. Esempio finale: rakefile per l'esecuzione di un task di migration ed uno di test

require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

require 'rubygems'
require 'active_record'
require 'active_record/fixtures'
require 'yaml'
require 'erb'

desc "Carico le fixtures nel database usando o rake fixtures o rake FIXTURES=primofile, secondofile"
task :fixtures => :environment do
  fixtures = ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(File.dirname(__FILE__), %w[test fixtures *.{yml, csv}]))
  fixtures.each do |fix_file|
    @logger.info("----------------------------------")
    @logger.info("lettura di #{fix_file}")
    @logger.info("----------------------------------")
    Fixtures.create_fixtures('test/fixtures', File.basename(fix_file, '.*'))
  end
end

task :default => :migrate        
desc "Migrazione nel db usando gli script nella cartella db/migrate. E' possibile specificare la versione con VERSION=x"  
task :migrate => :environment do  
  @logger.info("--------------------------------------------------")
  @logger.info("Connessione con il database in corso")
  @logger.info("In caso di errori controllare i parametri di connessione in config/database.yml")
  @logger.info("--------------------------------------------------")
  ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil )  
end  

task :environment do  
  @logger = Logger.new($stderr)
  #, File.open('database.log', 'a')
  ActiveRecord::Base.logger = @logger
  ActiveRecord::Base.colorize_logging = true
  @config = YAML.load_file(File.join(File.dirname(__FILE__), "config/database.yml")) 
  ActiveRecord::Base.establish_connection(@config["development"])        
end

Per utilizzare questo script, posizionarsi nella cartella contenente l'intera struttura sopra descritta (il rakefile è nella root) e si lancia semplicemente il comando rake per eseguire il task di migration, oppure rake fixtures per quello di test. Leggendo il codice dello script si trovano delle istruzioni desc che descrivono i modi d'impiego.

In queste pagine abbiamo mostrato alcune delle caratteristiche di ActiveRecord, evidenziandone alcuni punti di forza. Naturalmente avremmo potuto dilungarci ancora di più sull'argomento, soffermandoci magari sui tecnicismi, eseguendo paragoni con altri ORM esistenti o fornendo semplicemente esempi reali d'uso.

Ti consigliamo anche