EveryDocs – Entwicklung einer Ruby on Rails Anwendung

In diesem Blog habe ich mich bereits an einer Reihe verschiedener Programmiersprachen und Frameworks ausprobiert. Als nächste Technologie auf meiner Liste stand Ruby mit dem Web-Framework Ruby on Rails. Dafür habe ich mir als Projekt die Entwicklung eines DMS (Document Management System) überlegt und möchte im Folgenden meinen Einstieg in Ruby on Rails schildern.

Warum ein DMS entwickeln?

Aktuell benutzte ich ecoDMS, um alle meine eingescannten Dokumente digital zu organisieren. In der kostenfreien Version gibt es aber zum Beispiel keinen Web-Client oder eine App, sodass ich auf meine Dokumente nur von meinem Laptop aus zugreifen kann. Zudem umfasst das Tool auch viele Funktionen, die für ein Unternehmen wahrscheinlich von Bedeutung, für meine private Nutzung aber zu umfangreich sind. Da ich auch keine andere zufriendestellende Lösung gefunden habe, habe ich mich dazu entschlossen, ein eigene Software dafür zu programmieren.

Ein neues Rails-Projekt erstellen

Kurz vorweg: Das Projekt habe ich auf GitHub veröffentlicht und kann hier gefunden werden: EveryDocs Github-Repository.

Die Entwicklung des Projektes führe ich meinem Linux-Server durch. Wenn Ruby installiert ist, kann man mit folgendem Befehl ein neues Projekt erstellen. In diesem Fall möchte ich mit der Anwendung nur eine REST-API anbieten, weswegen ich hier noch den Parameter ––api ergänze:

Wechselt man anschließend in den Ordner, wurde ein bereits lauffähiges Rails-Projekt angelegt. Nachdem alle notwendigen Pakete installiert worden sind, kann man den in Rails integrierten Webserver starten und im Browser aufrufen. Dieser läuft standardmäßig auf Port 3000, kann aber über den Parameter ––port geändert werden.

Die Standard-Landingpage eines neu erstellten Rails-Projektes im Browser
Die Standard-Landingpage eines neu erstellten Rails-Projektes im Browser

Datenmodelle erstellen

Dabei möchte ich es natürlich nicht belassen. Beginnen möchte ich damit, die Models zu erstellen, also die Klassen, die für die Datenhaltung zuständig sein sollen. Davon muss es mehrere geben, wie dem nachfolgenden ER-Modell zu entnehmen ist. Diese lassen sich sehr bequem über die Kommandozeile generieren.

Die Syntax lautet also rails generate model #MODEL_NAME# und anschließend die Attributnamen jeweils mit einem Doppelpunkt getrennt von ihrem Datentyp. Über references lässt sich zudem eine Beziehung zu einem anderen Model abbilden. So hat ein Ordner unter Umständen einen Oberordner und ist einem Benutzer zugeordnet.

Durch die aufgeführten Befehle werden im Ordner app/models/ die Klassen-Dateien angelegt. Einige kleine Anpassungen müssen hier im Folgenden noch vorgenommen werden. Zudem werden unter db/migrate/ Dateien generiert, die dafür dienen, die Datenbank-Tabellen anzulegen, sobald diese konfiguriert ist. Die jeweiligen IDs werden später automatisch hinzugefügt und müssen hier nicht aufgeführt werden. Zudem werden automatisch zwei Spalten created_at und updated_at angelegt.

Generierte Datenmodelle anpassen

In /app/models/user.rb wird has_secure_password ergänzt, wodurch Methoden zum Setzen und Authentifizieren mit einem BCrypt-Passwort bereitgestellt werden (siehe auch has_secure_password Rails Dokumentation). Dazu ist es noch wichtig, „gem ‚bcrypt‘, ‚~> 3.1.7′“ – sowie vorbereitend schon einmal „gem ‚jwt'“ (später für JSON Web Token) – in das Gemfile einzutragen.

Zudem können hier die 1:n-Beziehungen angegeben werden, auf die ein User Zugriff haben soll. In diesem Fall handelt es sich um has_many :documents. Diese Beziehungen müssen an entsprechenden anderen Stellen ergänzt werden. Außerdem kann über validates_presence_of festgelegt werden, welche Attribute beim Anlegen eines neuen Objektes vorhanden sein müssen.

Datenbankverbindung einrichten

Standardmäßig verwendet Ruby on Rails eine SQLite-Datenbank. In meinem Fall möchte ich dies ändern und eine MariaDB einsetzen, die auf dem gleichen Server läuft. Dafür muss im Gemfile eine neues Paket eingetragen und anschließend über den Befehl bundle install heruntergeladen werden: gem ‚mysql2‘, ‚~> 0.3.18‘

Die Datenbank-Verbindung wird in der Datei /config/database.yml konfiguriert. Dort ist es möglich, verschiedene Datenbanken für die Entwicklungs-, Test- und Produktionsumgebung einzurichten. Für die Entwicklungsumgebung wähle ich folgende Konfiguration:

Anschließend kann man in der Datenbank anhand der generieten Dateien über folgenden Befehl die gewünschten Tabellen und Relationen anlegen lassen:

An der API authentifizieren

Die Datenbank ist eingerichtet und die Models sind vorhanden. Nun können wir damit beginnen, die Schnittstelle zu implementieren. Beginnen möchte ich dabei mit der Authentifizierung. Um zu verhindern, dass nicht eingeloggte Benutzer diese Anwendung benutzen können, muss die Datei app/controllers/ApplicationController.rb erweitert werden.

Über diesen vorher automatisch angelegten Controller wird nun über before_action festgelegt, dass die Methode authorize_request vor jeder anderen Methode eines Controllers ausgeführt wird, um den aktuell angemeldeten Benutzer zu ermitteln. Das include-Statement bindet noch zwei Module für die Fehlerbehandlung und Formatierung der Antwort als JSON ein.

Um die Authentifizierung aber überhaupt durchführen zu können, ist noch ein weiterer Controller notwendig, der diese Aufgabe übernimmt. An dieser Stelle darf die authorize_request-Methode jedoch nicht ausgeführt werden und wird über skip_before_action deaktiviert.

In den beiden Controllern werden zwei weitere Module verwendet, die ich im zu erstellenden Ordner app/auth/ abgelegt habe. Hier wiederum wird ein Modul zur De- und Encodierung von JSON verwendet, das ich im Ordner app/lib/ angelegt habe, da dies später auch noch an anderer Stelle zum Einsatz kommt.

Die Methoden zum Login sind nun vorhanden. Anschließend muss noch festgelegt werden, unter welchem Pfad diese Methoden zu erreichen sein sollen. Dafür ist die Datei config/routes.rb zuständig.

Über die erste Zeile wird festgelegt, dass bei einem POST-Request auf den Pfad ‚auth/login‘ die Methode authenticate aus dem AuthenticationController aufgerufen wird. Die zweite Zeile habe ich bereits hinzugefügt, um auch die Möglichkeit zu bieten, einen neuen Benutzer anzulegen. Dafür ist ein neuer UsersController erforderlich, der mit dem obigen Befehl wieder generiert werden kann.

Auch hier wird die vorherige Authentifizierung wieder deaktiviert. Es wird ein neuer User erstellt, wobei die Parameter aus dem POST-Request vorher überprüft werden. Anschließend wird ein Token generiert, der dann zur Authentifizierung genutzt werden kann.

Auf die Daten der Anwendung zugreifen

Für die Ressourcen werden ebenfalls jeweils eigene Controller benötigt. Jede davon besitzt Methoden, um alle und anhand der ID jeweils einen einzelnen Satz auszuliefern und einen Satz anzulegen, upzudaten und zu löschen. Ein solcher Controller könnte dann folgendermaßen aussehen:

Wieder muss die Datei config/routes.rb angepasst werden. Jedoch muss nicht für für jede einzelne der oben genannten Operationen eine einzelne Zeile eingetragen werden.

Über das resources-Keyword werden automatisch die notwendigen Routen erzeugt. Über folgenden Befehl kann dies überprüft werden, indem alle konfigurierten Routen ausgegeben werden.

Anwendung testen

Alle Routen sind nun konfiguriert, sodass es an der Zeit ist, die Anwendung zu testen. Dazu habe ich mir Postman installiert, um verschiedene Requests an die API zu senden. Natürlich ist dies auch mit cURL oder ähnlichen Programmen möglich. Im Browser lassen sich die GET-Requests einfach ausführen, jedoch wird es bei POST-Requests schwieriger. Zuerst einmal muss aber wieder der Server gestartet werden.

Führe ich nun ein GET auf http://localhost:5678/documents durch, erhalte ich einen JSON String mit der Nachricht „Missing token“. Um mich anzumelden, muss ich mir zuerst einen neuen Benutzer einrichten. Dazu mache ich ein POST auf http://localhost:5678/signup durch und gebe im Body folgende Parameter mit:

  • name: Jonas Hellmann
  • email: test@test.com
  • password: Test123
  • password_digest: Test123

Als Antwort erhalte ich einen JSON-String mit der Nachricht, dass der Account erfolgreich angelegt wurde und einem Token. Führe ich nun erneut ein GET auf die Dokumente durch und gebe als „Authorization“-Header diesen Token an, ist die Fehlermeldung weg und ich erhalte einen leeren JSON-Array, was natürlich Sinn macht, da noch keine Dokumente vorhanden sind. Es zeigt aber, dass das Anmelden grundsätzlich funktioniert. Ein praktischer Nebeneffekt von der Nutzung von has_secure_password ist, dass das Passwort auch automatisch verschlüsselt in der Datenbank gespeichert wird und nicht im Klartext.

Fazit

Einige Anpassungen werden später noch notwendig sein, wenn ich noch einige andere Funktionalitäten ergänzen möchte (z.B. Import und Export der Dokumente). Fürs Erste sollte der Artikel aber einen kurzen Einblick in Rails geben und wie man mit relativ geringem Aufwand eine REST-API bereitstellen kann. Es wäre auch möglich, mit dieser Technologie die Oberfläche bereitzustellen, aber da habe ich mir schon etwas neues ausgedacht. Wahrscheinlich folgt also demnächst ein weiterer Artikel! 🙂

P.S.: Falls mir ein Fehler unterlaufen sein sollte oder ich noch Verbesserungen vornehmen kann, bin ich über jeden Kommentar dankbar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.