usiamo Ruby on Rails per insegnare lo sviluppo di applicazioni web presso l’EPFL Scuola di Estensione. È un ottimo framework che è solido, provato e affidabile e presenta una struttura facile da seguire quando fai i primi passi imparando a conoscere lo sviluppo web.
Ma è anche un software in continua evoluzione, con miglioramenti e nuove funzionalità rilasciate ogni anno. Una delle recenti aggiunte a Rails nella versione 5.2 è la funzione ActiveStorage, che aggiunge una facile gestione del caricamento dei file al framework core Rails per la prima volta.
Nella prima iterazione del nostro programma di sviluppo di applicazioni Web, includiamo un argomento per imparare come aggiungere caricamenti di file in un’applicazione Rails. Usiamo Rails 5.1 nel programma, tuttavia, e il soggetto di caricamento dei file utilizza una delle soluzioni di terze parti ampiamente utilizzate per l’aggiunta di caricamenti di file, chiamata CarrierWave. Questo è un solido gioiello di terze parti che ci permette di implementare il caricamento di file in un’app Rails ed è in uso in molte applicazioni Ruby on Rails del mondo reale, inclusa la nostra piattaforma per studenti!
Ma con l’introduzione di ActiveStorage, queste gemme di terze parti diventano meno attraenti di questa nuova soluzione in bundle all’interno di Rails. Infatti, Paperclip, uno degli altri popolari gemme di upload di file di terze parti, ha già annunciato che è deprecato a favore di ActiveStorage. Gli sviluppatori di Paperclip hanno riconosciuto che ActiveStorage è una scelta migliore per gli sviluppatori e hanno smesso di lavorare ulteriormente sulla propria gemma.
Quindi diamo un’occhiata in particolare a ciò che serve per passare da CarrierWave in Rails 5.1 a ActiveStorage in Rails 5.2.
L’applicazione
Nel programma di sviluppo di applicazioni web, lavoriamo attraverso la creazione di un’applicazione web chiamata My Bucket List. Questa è un’app che consente agli utenti di creare e tenere traccia delle esperienze che hanno sempre desiderato realizzare. Gli utenti possono vedere ciò che altre persone hanno creato e tenere traccia di quelli troppo. Una delle funzionalità dell’app consente a un utente di caricare il proprio avatar, e costruiamo questa funzionalità per conoscere i caricamenti di file e gli allegati utilizzando CarrierWave.
Così, in primo luogo, diamo un veloce ripasso di ciò che è coinvolto con l’aggiunta di un semplice allegato utilizzando CarrierWave.
Configurazione CarrierWave
Per includere CarrierWave in un progetto, abbiamo bisogno di:
- Aggiungi la gemma CarrierWave al
Gemfile
. - Includere l’adattatore CarrierWave per ActiveRecord all’interno di un inizializzatore di configurazione,
config/initializers/carrierwave.rb
.
e quindi per aggiungere un allegato di immagine a un modello, abbiamo bisogno di:
- Genera un Uploader usando
rails generate uploader UploaderName
. - Aggiungere un attributo sul modello per memorizzare il nome file allegato per ogni record, con la migrazione del database associato.
- Modifica i valori nell’uploader, come la posizione di archiviazione di upload e l’immagine predefinita.
- Utilizzare la macro
mount_uploader
nel modello per includere il particolare uploader. - Aggiungi un campo modulo file in cui è possibile caricare un’immagine.
- Aggiungere l’attributo model all’elenco dei parametri strong nel controller che gestisce il modulo contenente il campo file.
Consideriamo uno di questi punti ulteriormente: in CarrierWave, è necessario aggiungere attributi specifici a qualsiasi modello che ha bisogno di avere un file allegato. Ad esempio, un modello User
che richiede un’immagine avatar
ad esso collegata avrà bisogno di un attributo chiamato :avatar
che è un String
. Ciò richiede una migrazione del database per aggiungere la colonna del database sottostante, oltre a creare e montare una classe Uploader
a tale attributo nel modello.
Questo non è particolarmente laborioso ma, come vedremo, ActiveStorage richiede molto meno codice caso per caso.
Un altro punto da evidenziare è che nella mia app — l’app My Bucket List — l’uploader avatar CarrierWave è impostato con l’archiviazione di file semplice predefinita. In parole d’ordine, le immagini caricate vengono memorizzate da qualche parte all’interno della cartella public/
nell’applicazione. Quel percorso preciso è definito all’interno della classe Uploader
per l’avatar ed è impostato su "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
. Questo è un percorso relativo alla cartella public/
, in modo che le immagini caricate siano direttamente accessibili tramite il server web. Ciò consente l’accesso diretto ai file allegati, che possiamo contrastare con ActiveStorage in seguito.
Aggiornamento di Rails
Prima di esaminare ActiveStorage, è necessario eseguire l’aggiornamento a Rails 5.2. Questo è un compito che può essere molto coinvolto, a seconda della complessità dell’applicazione. L’applicazione che sto usando qui, che costruiamo nel nostro programma WAD, non è particolarmente difficile da aggiornare, ma ha ancora bisogno di qualche cura e attenzione.
Ecco cosa ho fatto per aggiornare l’applicazione My Bucket List da Rails 5.1 a Rails 5.2.
In primo luogo, ho creato un nuovo ramo nel mio repository git per isolare l’aggiornamento — è importante essere in grado di tornare alla versione di lavoro precedente di un’app se qualcosa va storto nel processo di aggiornamento!
Sul nuovo ramo, ho cambiato la versione di Rails in Gemfile
da '~> 5.1.2'
a '~> 5.2.0'
, quindi ho eseguito bin/bundle update rails
— questo comando scarica la nuova versione di rails
e tutte le gemme correlate nell’applicazione. Per la mia applicazione, non ci sono stati conflitti di gemme, ma se si utilizzano gemme di terze parti, potrebbe essere necessario risolvere i conflitti tra diverse versioni di gemme a questo punto!
Dopo aver installato il nuovo Rails gem e le sue dipendenze, è necessario aggiornare l’applicazione stessa. L’ho fatto usando bin/bundle exec rails app:update
. Questo comando aggiorna a livello di codice tutti i file necessari nell’applicazione per funzionare con Rails 5.2.
Ma… questo comando sovrascriverà le modifiche apportate all’applicazione, ad esempio config/routes.rb
. Fortunatamente, il comando chiede se sovrascrivere ogni file ed è possibile diff
uno di questi file durante questo processo per verificare cosa verrebbe sovrascritto. Il prompt per ogni sovrascrittura è , il che significa:
-
Y
per “Sì, sovrascrivi il mio file”, ed è l’opzione predefinita se premiEnter
. -
n
per “no, non sovrascrivere il mio file” -
a
per ” Sì, sovrascrivi questo file e sovrascrivi anche tutti gli altri file!”. (La mia regola generale non è mai usarea
). -
q
per “Esci da questo processo” -
d
per “Mostrami le differenze tra il mio file e la sovrascrittura” -
h
per ” Mostrami un elenco di cosa significaYnaqdh
“
Nella mia applicazione, ho accettato ogni sovrascrittura con Y
tranne config/routes.rb
e config/locales/en.yml
. Quei due file erano file che avevo cambiato e quindi volevo mantenere la mia versione, quindi ho scelto n
.
Questo processo di aggiornamento potrebbe essere più coinvolto se si dispone di un’applicazione più complessa, in particolare se si dispone di molte configurazioni personalizzate in uno qualsiasi dei file di configurazione dell’ambiente, ad esempio config/environments/production.rb
, config/environments/test.rb
e config/environments/development.rb
. L’aggiornamento aggiunge impostazioni importanti a questi file, ma vorrai anche mantenere la tua configurazione personalizzata: è qui che aiuta avere la versione originale dell’applicazione in un ramo git separato. È quindi possibile il codice in questo vecchio ramo per verificare che tutto il codice dell’applicazione e la configurazione siano ancora in vigore dopo l’aggiornamento.
Dopo aver eseguito bin/bundle exec rails app:update
, è stato necessario per me aggiungere la gemma bootsnap
a Gemfile
. Questa è una nuova gemma utilizzata in Rails 5.2 ma non viene aggiunta come parte del comando app:upgrade
. Quindi ho appena aggiunto gem 'bootsnap'
a Gemfile
e poi ho eseguito bin/bundle install
.
Dopo questo, la mia applicazione Bucket List è stata avviata come prima di utilizzare rails server
.
Sostituzione di CarrierWave con ActiveStorage
Quindi, con l’applicazione ora in esecuzione su Rails 5.2, potrei iniziare a sostituire CarrierWave con ActiveStorage.
Con ActiveStorage, non è necessario aggiungere attributi specifici del modello per memorizzare i nomi dei file degli allegati necessari con CarrierWave. ActiveStorage funziona con le stesse due tabelle associate per tutti gli allegati.
Per impostare queste due tabelle, ho eseguito bin/bundle exec rails active_storage:install
seguito da rails db:migrate
. Le due tabelle sono active_storage_blobs
e active_storage_attachments
; la prima è per i dettagli del file allegato e la seconda è per la tabella di join polimorfica che collega un file allegato a un record del modello.
Ed ecco il punto importante: una volta create queste due tabelle, non è necessario creare altre migrazioni per includere allegati nei nostri modelli! Questo rende ActiveStorage facile da lavorare dal punto di vista del database.
Successivamente, ho esaminato la sostituzione dell’implementazione dell’allegato :avatar
con ActiveStorage invece di CarrierWave. Nella mia app, ogni User
ha un :avatar
. Poiché stavo usando CarrierWave in precedenza, avevo un attributo :avatar
montato su un CarrierWave Uploader
nel modello User
, dichiarato in questo modo:
class User < ApplicationRecord
# ... various codez mount_uploader :avatar, AvatarUploader # ... other codez
end
L’allegato CarrierWave :avatar
può essere sostituito con un allegato ActiveSupport utilizzando has_one_attached :avatar
, in questo modo:
class User < ApplicationRecord
# ... various codez # commented old uploader for reference
# mount_uploader :avatar, AvatarUploader
has_one_attached :avatar # ... other codez
end
Questa è una modifica a una riga di codice. A questo punto, cos’altro deve essere fatto? Ad esempio, come viene impostata la posizione di archiviazione di caricamento? Ricorda che nella mia app, l’uploader avatar CarrierWave è impostato con l’archiviazione di file semplice predefinita e la posizione per questo è impostata esplicitamente nel file avatar_uploader.rb
. Anche l’archiviazione di file semplice è l’impostazione predefinita in ActiveStorage. La posizione è impostata all’interno del file config/storage.yml
, con un valore predefinito per i caricamenti locali.
La configurazione predefinita per ActiveStorage significa che non ho bisogno di scrivere altro codice per i miei caricamenti per funzionare per l’attributo :avatar
modificato nella mia applicazione. Ho cambiato completamente il meccanismo di caricamento da CarrierWave a ActiveStorage e non ho bisogno di fare altro: Non ho bisogno di apportare modifiche al Controller che gestisce il caricamento, perché :avatar
è già dichiarato come parametro strong. E non ho bisogno di cambiare il campo del modulo file nella vista che consente agli utenti di selezionare un’immagine da caricare, perché questo è supportato da :avatar
“attributo”.
Ma c’è un altro cambiamento che devo fare per migrare da CarrierWave a ActiveStorage, e questo è come usare l’immagine allegata nelle viste. Con CarierWave, ho usato il metodo avatar_url
per rendere il percorso completo dell’immagine. Con ActiveStorage, l’URL del file non viene reso dall’attributo allegato stesso, ma reso passando l’attributo ai metodi di supporto. Quindi l’URL dell’allegato completo può essere reso usando url_for(user.avatar)
o direttamente con image_tag
.
È anche necessario verificare esplicitamente se l’allegato è presente, quindi un utilizzo completo in una vista sarebbe simile a questo:
<% if current_user.avatar.attached? %>
<%= image_tag current_user.avatar, class: 'avatar' %>
<% else %>
<%= image_tag 'default-avatar', class: 'avatar' %>
<% end %>
Una volta aggiornati i riferimenti a user.avatar_url
nelle mie visualizzazioni con frammenti come quello sopra, mi ero trasferito completamente da CarrierWave a ActiveStorage per tutti i nuovi caricamenti.
Il vecchio codice CarrierWave deve essere riordinato, e c’è una domanda su cosa fare con qualsiasi upload esistente che è stato fatto con CarrierWave. Ma vedi sotto su questo scenarios
Scenari più complessi
Quasi tutti gli scenari di produzione saranno più complessi di quello che ho descritto sopra. Sarà necessario considerare cose come i caricamenti archiviati su CDN o server remoti come AWS S3 e il ridimensionamento delle immagini per le miniature, ecc. ActiveStorage ci permette di fare un sacco di queste cose fuori dalla scatola troppo e sembra molto ben progettato per le esigenze degli sviluppatori. Come ci si potrebbe aspettare, c’è una buona documentazione nella guida Rails.
Per quanto riguarda la migrazione dei caricamenti esistenti da CarrierWave a ActiveStorage, ciò sarebbe certamente possibile con uno script rake relativamente piccolo. Mirerò a coprire che in un post futuro.
Questo era uno scenario semplice, ma nel complesso ero davvero soddisfatto di quanto fosse facile usare ActiveStorage e sostituire CarrierWave con esso. Mi piace anche il pensiero che è andato nelle varie parti di ActiveStorage, poiché tutto sembra essere in file e cartelle che corrispondono perfettamente alla struttura del resto di Rails. Penso sicuramente che valga la pena entrare in Rails 5.2 per utilizzare ActiveStorage per i caricamenti di file nelle tue applicazioni!
Per saperne di più!
Sei interessato a saperne di più su Ruby e Ruby on Rails? Insegno Sviluppo di applicazioni Web presso la EPFL Extension School, una piattaforma di apprendimento online certificata che insegna competenze digitali nella scienza dei dati, nello sviluppo di applicazioni Web e altro ancora. EPFL è una delle principali università del mondo ed è classificato il” Numero 1 giovane Università ” dal Times Higher Education Ranking.