Wir verwenden Ruby on Rails, um die Entwicklung von Webanwendungen an der EPFL Extension School zu unterrichten. Es ist ein großartiges Framework, das solide, erprobt und vertrauenswürdig ist und eine einfach zu befolgende Struktur bietet, wenn Sie Ihre ersten Schritte in Bezug auf die Webentwicklung unternehmen.
Aber es ist auch eine sich ständig weiterentwickelnde Software, mit Verbesserungen und neuen Funktionen, die jedes Jahr veröffentlicht werden. Eine der jüngsten Ergänzungen zu Rails in Version 5.2 ist die ActiveStorage-Funktion, die dem Core-Rails-Framework zum ersten Mal eine einfache Datei-Upload-Verwaltung hinzufügt.
In der ersten Iteration unseres Webanwendungsprogramms enthalten wir ein Thema, in dem Sie lernen, wie Sie Datei-Uploads in einer Rails-Anwendung hinzufügen. Wir verwenden jedoch Rails 5.1 im Programm, und das Thema Datei-Upload verwendet eine der weit verbreiteten Lösungen von Drittanbietern zum Hinzufügen von Datei-Uploads, CarrierWave. Dies ist ein solides Juwel von Drittanbietern, mit dem wir Datei-Uploads in einer Rails—App implementieren können und das in vielen realen Ruby on Rails-Apps verwendet wird – einschließlich unserer Lernplattform!
Mit der Einführung von ActiveStorage werden diese Edelsteine von Drittanbietern jedoch weniger attraktiv als diese neue Lösung, die in Rails gebündelt ist. Tatsächlich hat Paperclip, einer der anderen beliebten Datei-Upload-Edelsteine von Drittanbietern, bereits angekündigt, dass er zugunsten von ActiveStorage veraltet ist. Die Entwickler von Paperclip haben erkannt, dass ActiveStorage eine bessere Wahl für Entwickler ist und haben die weitere Arbeit an ihrem eigenen Juwel eingestellt.
Schauen wir uns also genauer an, was nötig ist, um von CarrierWave in Rails 5.1 zu ActiveStorage in Rails 5.2 zu wechseln.
Die Anwendung
Im Entwicklungsprogramm für Webanwendungen erstellen wir eine Webanwendung namens My Bucket List. Dies ist eine App, mit der Benutzer Erlebnisse erstellen und verfolgen können, die sie schon immer erreichen wollten. Benutzer können sehen, was andere Personen erstellt haben, und diese auch verfolgen. Eine der Funktionen in der App ermöglicht es einem Benutzer, seinen eigenen Avatar hochzuladen, und wir erstellen diese Funktion, um mehr über Datei-Uploads und Anhänge mit CarrierWave zu erfahren.
Lassen Sie uns zunächst einen kurzen Überblick darüber geben, was mit dem Hinzufügen eines einfachen Anhangs mit CarrierWave verbunden ist.
CarrierWave-Konfiguration
Um CarrierWave in ein Projekt aufzunehmen, müssen wir:
- Fügen Sie das CarrierWave-Juwel zu
Gemfile
hinzu. - Fügen Sie den CarrierWave-Adapter für ActiveRecord in einen Konfigurationsinitialisierer ein,
config/initializers/carrierwave.rb
.
und dann, um einem Modell einen Bildanhang hinzuzufügen, müssen wir:
- Generieren Sie einen Uploader mit
rails generate uploader UploaderName
. - Fügen Sie dem Modell ein Attribut hinzu, um den angehängten Dateinamen für jeden Datensatz mit der zugehörigen Datenbankmigration zu speichern.
- Bearbeiten Sie Werte im Uploader, z. B. Upload-Speicherort und Standardbild.
- Verwenden Sie das Makro
mount_uploader
im Modell, um den jeweiligen Uploader einzuschließen. - Fügen Sie ein Dateiformularfeld hinzu, in das ein Bild hochgeladen werden kann.
- Fügen Sie das Modellattribut zur Liste der starken Parameter im Controller hinzu, der das Formular mit dem Dateifeld verarbeitet.
Betrachten wir einen dieser Punkte weiter: In CarrierWave müssen jedem Modell, das eine angehängte Datei haben muss, bestimmte Attribute hinzugefügt werden. Zum Beispiel benötigt ein User
-Modell, an das ein avatar
-Bild angehängt werden muss, ein Attribut namens :avatar
, das ein String
ist. Dies erfordert eine Datenbankmigration zum Hinzufügen der zugrunde liegenden Datenbankspalte sowie das Erstellen und Mounten einer Uploader
-Klasse für dieses Attribut im Modell.
Dies ist nicht besonders mühsam, aber wie wir sehen werden, benötigt ActiveStorage von Fall zu Fall viel weniger Code.
Ein weiterer Punkt, der hervorgehoben werden muss, ist, dass in meiner App — der My Bucket List—App – der CarrierWave Avatar Uploader mit dem standardmäßigen einfachen Dateispeicher eingerichtet ist. Mit anderen Worten, hochgeladene Bilder werden irgendwo im Ordner public/
in der Anwendung gespeichert. Dieser genaue Pfad ist in der Klasse Uploader
für den Avatar definiert und auf "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
festgelegt. Dies ist ein Pfad relativ zum Ordner public/
, so dass hochgeladene Bilder direkt über den Webserver zugänglich sind. Dies ermöglicht den direkten Zugriff auf die Anhangsdateien, die wir später mit ActiveStorage vergleichen können.
Upgrade von Rails
Bevor Sie sich ActiveStorage ansehen, müssen Sie auf Rails 5.2 aktualisieren. Dies ist eine Aufgabe, die je nach Komplexität Ihrer Anwendung sehr komplex sein kann. Die Anwendung, die ich hier verwende und die wir in unserem WAD-Programm erstellen, ist nicht besonders schwer zu aktualisieren, erfordert aber dennoch einige Sorgfalt und Aufmerksamkeit.
Hier ist, was ich getan habe, um die My Bucket List-Anwendung von Rails 5.1 auf Rails 5.2 zu aktualisieren.
Zuerst habe ich einen neuen Zweig in meinem Git—Repository erstellt, um das Upgrade zu isolieren – es ist wichtig, zur vorherigen Arbeitsversion einer App zurückkehren zu können, wenn beim Upgrade etwas schief geht!
Auf dem neuen Zweig habe ich die Version von Rails in Gemfile
von '~> 5.1.2'
auf '~> 5.2.0'
geändert und dann bin/bundle update rails
ausgeführt — dieser Befehl lädt die neue Version von rails
und alle zugehörigen Edelsteine in der Anwendung herunter. Für meine Anwendung gab es keine Edelsteinkonflikte, aber wenn Sie Edelsteine von Drittanbietern verwenden, müssen Sie möglicherweise Konflikte zwischen verschiedenen Edelsteinversionen an dieser Stelle lösen!
Nach der Installation des neuen Rails-Edelsteins und seiner Abhängigkeiten muss die Anwendung selbst aktualisiert werden. Ich habe das mit bin/bundle exec rails app:update
gemacht. Dieser Befehl aktualisiert programmgesteuert alle erforderlichen Dateien in der Anwendung, um mit Rails 5.2 zu arbeiten.
Aber… dieser Befehl überschreibt Änderungen, die Sie an Ihrer Anwendung vorgenommen haben, z. B. config/routes.rb
. Glücklicherweise fragt der Befehl, ob jede Datei überschrieben werden soll, und es ist möglich, während dieses Vorgangs eine dieser Dateien diff
, um zu überprüfen, was überschrieben werden würde. Die Eingabeaufforderung für jedes Überschreiben lautet , was bedeutet:
-
Y
für „Ja, überschreibe meine Datei“, und ist die Standardoption, wenn Sie nurEnter
drücken. -
n
für „Nein, überschreibe meine Datei nicht“ -
a
für „Ja, überschreibe diese Datei und überschreibe auch jede andere Datei!“. (Meine allgemeine Regel ist niemalsa
). -
q
für „Diesen Prozess beenden“ -
d
für „Zeige mir die Unterschiede zwischen meiner Datei und dem Überschreiben“ -
h
für „Zeig mir eine Liste dessen, wasYnaqdh
bedeutet“
In meiner Bewerbung habe ich jedes Überschreiben mit Y
außer config/routes.rb
und config/locales/en.yml
akzeptiert. Diese beiden Dateien waren Dateien, die ich geändert hatte, und so wollte ich meine Version behalten, also wählte n
.
Dieser Upgrade-Prozess kann komplizierter sein, wenn Sie eine komplexere Anwendung haben, insbesondere wenn Sie viele benutzerdefinierte Konfigurationen in einer der Umgebungskonfigurationsdateien haben, z. config/environments/production.rb
, config/environments/test.rb
und config/environments/development.rb
. Das Upgrade fügt diesen Dateien wichtige Einstellungen hinzu, aber Sie möchten auch Ihre eigene benutzerdefinierte Konfiguration beibehalten – hier hilft es, die Originalversion der Anwendung in einem separaten Git-Zweig zu haben. Es ist dann möglich, den Code in diesem alten Zweig zu überprüfen, ob der gesamte Anwendungscode und die gesamte Konfiguration nach dem Upgrade noch vorhanden sind.
Nach dem Ausführen von bin/bundle exec rails app:update
musste ich den Edelstein bootsnap
zum Edelstein Gemfile
hinzufügen. Dies ist ein neues Juwel, das in Rails 5.2 verwendet wird, aber nicht als Teil des Befehls app:upgrade
hinzugefügt wird. Also habe ich einfach gem 'bootsnap'
zu Gemfile
hinzugefügt und dann bin/bundle install
ausgeführt.
Danach wurde meine Bucket List-Anwendung wie zuvor mit rails server
gestartet.
Ersetzen von CarrierWave durch ActiveStorage
Da die Anwendung jetzt auf Rails 5.2 ausgeführt wird, könnte ich CarrierWave durch ActiveStorage ersetzen.
Bei ActiveStorage ist es nicht erforderlich, modellspezifische Attribute hinzuzufügen, um die für CarrierWave erforderlichen Dateinamen für Anhänge zu speichern. ActiveStorage arbeitet mit denselben zwei zugeordneten Tabellen für alle Anhänge.
Um diese beiden Tabellen einzurichten, habe ich bin/bundle exec rails active_storage:install
gefolgt von rails db:migrate
ausgeführt. Die beiden Tabellen sind active_storage_blobs
und active_storage_attachments
; Die erste enthält die Details der angehängten Datei und die zweite die polymorphe Verknüpfungstabelle, die eine angehängte Datei mit einem Modelldatensatz verknüpft.
Und hier ist der wichtige Punkt: Sobald wir diese beiden Tabellen erstellt haben, müssen wir keine weiteren Migrationen mehr erstellen, um Anhänge in unsere Modelle aufzunehmen! Dies macht die Arbeit mit ActiveStorage aus Datenbanksicht einfach.
Als nächstes habe ich versucht, die Implementierung des :avatar
-Anhangs durch ActiveStorage anstelle von CarrierWave zu ersetzen. In meiner App hat jeder User
einen :avatar
. Da ich zuvor CarrierWave verwendet habe, wurde ein :avatar
-Attribut an ein CarrierWave Uploader
im User
-Modell angehängt, das wie folgt deklariert ist:
class User < ApplicationRecord
# ... various codez mount_uploader :avatar, AvatarUploader # ... other codez
end
Der CarrierWave :avatar
-Anhang kann mit has_one_attached :avatar
wie folgt durch einen ActiveSupport-Anhang ersetzt werden:
class User < ApplicationRecord
# ... various codez # commented old uploader for reference
# mount_uploader :avatar, AvatarUploader
has_one_attached :avatar # ... other codez
end
Das ist eine Änderung an einer Codezeile. Was muss an dieser Stelle noch getan werden? Wie wird beispielsweise der Upload-Speicherort festgelegt? Denken Sie daran, dass in meiner App der CarrierWave-Avatar-Uploader mit dem standardmäßigen einfachen Dateispeicher eingerichtet ist und der Speicherort dafür explizit in der Datei avatar_uploader.rb
festgelegt ist. Einfacher Dateispeicher ist auch in ActiveStorage der Standard. Der Speicherort wird in der Datei config/storage.yml
mit einem Standardwert für lokale Uploads festgelegt.
Die Standardkonfiguration für ActiveStorage bedeutet, dass ich keinen Code mehr schreiben muss, damit meine Uploads für das geänderte Attribut :avatar
in meiner Anwendung funktionieren. Ich habe den Upload-Mechanismus vollständig von CarrierWave auf ActiveStorage geändert und muss nichts mehr tun: Ich muss keine Änderungen an dem Controller vornehmen, der den Upload verwaltet, da :avatar
bereits als starker Parameter deklariert ist. Und ich muss das Dateiformularfeld in der Ansicht nicht ändern, in dem Benutzer ein Bild zum Hochladen auswählen können, da dies durch das :avatar
„Attribut“ unterstützt wird.
Es gibt jedoch noch eine weitere Änderung, die ich vornehmen muss, um von CarrierWave zu ActiveStorage zu migrieren, und zwar die Verwendung des angehängten Bildes in Ansichten. Mit CarierWave habe ich die avatar_url
-Methode verwendet, um den vollständigen Bildpfad zu rendern. Bei ActiveStorage wird die Datei-URL nicht aus dem Attachment-Attribut selbst gerendert, sondern durch Übergeben des Attributs an Hilfsmethoden. Die vollständige URL des Anhangs kann also mit url_for(user.avatar)
oder direkt mit image_tag
gerendert werden.
Es muss auch explizit überprüft werden, ob der Anhang vorhanden ist, sodass eine vollständige Verwendung in einer Ansicht ungefähr so aussehen würde:
<% if current_user.avatar.attached? %>
<%= image_tag current_user.avatar, class: 'avatar' %>
<% else %>
<%= image_tag 'default-avatar', class: 'avatar' %>
<% end %>
Nachdem ich die Verweise auf user.avatar_url
in meinen Ansichten mit Snippets wie dem obigen aktualisiert hatte, war ich für alle neuen Uploads vollständig von CarrierWave zu ActiveStorage gewechselt.
Der alte CarrierWave-Code muss aufgeräumt werden, und es gibt eine Frage, was mit vorhandenen Uploads zu tun ist, die mit CarrierWave erstellt wurden. Aber siehe unten darüber …
Komplexere Szenarien
Fast alle Produktionsszenarien werden komplexer sein als die, die ich oben skizziert habe. Es wird notwendig sein, Dinge wie Uploads, die auf CDNs oder Remote-Servern wie AWS S3 gespeichert werden, und die Größenänderung von Bildern für Miniaturansichten usw. zu berücksichtigen. ActiveStorage ermöglicht es uns, viele dieser Dinge auch sofort zu erledigen, und scheint sehr gut auf die Bedürfnisse der Entwickler zugeschnitten zu sein. Wie zu erwarten, gibt es eine gute Dokumentation im Rails-Handbuch.
In Bezug auf die Migration vorhandener Uploads von CarrierWave zu ActiveStorage wäre dies sicherlich mit einem relativ kleinen Rake-Skript möglich. Ich werde versuchen, das in einem zukünftigen Beitrag zu behandeln.
Dies war ein einfaches Szenario, aber insgesamt war ich sehr zufrieden damit, wie einfach es war, ActiveStorage zu verwenden und CarrierWave durch CarrierWave zu ersetzen. Ich mag auch den Gedanken, der in die verschiedenen Teile von ActiveStorage gegangen ist, da alles in Dateien und Ordnern zu sein scheint, die gut mit der Struktur des Restes von Rails übereinstimmen. Ich denke definitiv, dass es sich lohnt, in Rails 5.2 einzusteigen, um ActiveStorage für Datei-Uploads in Ihren Anwendungen zu verwenden!
Erfahren Sie mehr!
Möchten Sie mehr über Ruby und Ruby on Rails erfahren? Ich unterrichte Web Application Development an der EPFL Extension School, einer zertifizierten Online-Lernplattform, die digitale Fähigkeiten in Data Science, Web Application Development und mehr vermittelt. Die EPFL ist eine der weltweit führenden Universitäten und wird vom Times Higher Education Ranking als „Number 1 Young University“ eingestuft.