· 4 min read

Leichte Einführung in mruby

Ruby 3.3.5 wurde veröffentlicht. Das Update behebt kleinere Fehler und wird allen Nutzern empfohlen. Weitere Details sind in den GitHub Release Notes verfügbar.

Ich wusste, dass mruby seit langem existiert. Ich wusste, dass es eine leichtgewichtige Version von Ruby ist, die vor allem dazu gedacht ist, in andere Programme eingebettet zu werden. Ich wusste, dass DragonRuby in irgendeiner Form mit mruby zu tun hat - sei es, dass es in mruby geschrieben ist oder eine Art Fork davon verwendet. Aber das war’s auch schon.

Angeregt durch ein paar Diskussionen hier und da in letzter Zeit, habe ich beschlossen, mir das Programm genauer anzusehen.

Installation

Wenn Sie einen Ruby-Versionsmanager verwenden - und das sollten Sie - sollte die Installation von mruby sehr einfach sein. In meinem Fall, in asdf, ist es so viel wie das Ausführen:

asdf install ruby mruby-3.3.0

Warten Sie ein paar Sekunden und wir sind bereit für die Testfahrt.

Fortbewegung

Die interaktive REPL für mruby heißt mirb und so starten Sie sie. Wenn sie gestartet ist, können Sie feststellen, dass es sich immer noch um Ruby handelt, auch wenn es ein bisschen anders aussieht als irb

$ mirb
mirb - Embeddable Interactive Ruby Shell

> Time.now.to_i
 => 1727202850
> "test".object_id
 => 95853644613216

Wir können auch ein einfaches Ruby-Skript erstellen und es ausführen.

# frozen_string_literal: true

3.times do
  puts "test".object_id
end
$ mruby test.rb
99069440489104
99069440488816
99069440488528

Daraus können wir sehen, dass ein magischer Kommentar, der String-Literale einfriert, in mruby nicht funktioniert. Nicht, dass es sehr wichtig ist, aber nützlich zu wissen.

Wo wir gerade von den Dingen sprechen, die nicht funktionieren, die Dokumentation sagt, dass es auf einer Stufe mit den Funktionen von Ruby 3.3 steht, mit Ausnahme des Mustervergleichs. Und in der Tat, selbst das einfachste Beispiel aus der Dokumentation funktioniert nicht.

case [1, 2, 3]
in [Integer, Integer]
  p "matched"
else
  p "not matched"
end
$ mruby matching.rb
matching.rb:2:2: syntax error, unexpected "'in'", expecting "'when'"
matching.rb:4:4: syntax error, unexpected "'else'", expecting end of file

Das ist ein echter Flop. Ich weiß nicht, ob es technische Schwierigkeiten sind, die dazu führen, dass der Musterabgleich in mruby nicht implementiert ist, aber er ist einfach nicht vorhanden. Andere moderne Funktionen, wie endlose Methoden, funktionieren.

> def oneline = "I'm a oneliner"
 => :oneline
> oneline
 => "I'm a oneliner"

Bytecode

So überraschend es auch klingen mag, das ist so ziemlich die Grenze dessen, was wir mit einer Ruby-Quelldatei und der ausführbaren Datei mruby machen können. Wir können den Code nicht einmal in mehrere Dateien aufteilen, weil mruby kein require hat. Wir können auch nicht wirklich Gems verwenden, zumindest habe ich keinen Weg gefunden, das zu tun.

Es scheint, dass mruby in der Tat mit dem Zweck der Einbettung im Hinterkopf gebaut ist.

Eine zusätzliche Sache, die wir tun können, ist, unser Ruby-Programm zu nehmen und es mit mrbc in Bytecode zu kompilieren.

$ echo 'puts "Hello world"' >> hello.rb
$ mrbc hello.rb
$ hexdump hello.mrb
0000000 4952 4554 3330 3030 0000 5c00 414d 5a54
0000010 3030 3030 5249 5045 0000 4000 3330 3030
0000020 0000 3400 0100 0400 0000 0000 0000 0a00
0000030 0251 2d00 0001 3801 6901 0100 0000 480b
0000040 6c65 6f6c 7720 726f 646c 0000 0001 7004
0000050 7475 0073 4e45 0044 0000 0800
000005c

Jetzt können wir es so ausführen:

$ mruby -b hello.mrb
Hello world

Wozu denn? Schauen wir mal.

Embedding

Executing Ruby code with mruby” - eine sehr nützliche Ressource auf den offiziellen mruby-Seiten - listet im Abschnitt “Bytecode (.c)” eine interessante Möglichkeit auf, mruby-Programme auszuführen:

Diese Variante ist für diejenigen interessant, die Ruby-Code direkt in ihren C-Code integrieren wollen. Es wird ein C-Array erzeugt, das den Bytecode enthält, den Sie dann selbst ausführen müssen.

Probieren wir es aus. Zuerst werden wir unsere Hallo-Welt ein wenig verkomplizieren:

class Greeter
  def initialize(name)
    @name = name
  end

  def invoke!
    "Hello, #{@name}"
  end
end

puts Greeter.new("world").invoke!

Nun kompilieren wir es in Bytecode, der in eine C-Datei eingebettet ist, indem wir mrbc -Bgreeter hello.rb verwenden. So sieht das Ergebnis (hello.c) für mich aus:

#include <stdint.h>
#ifdef __cplusplus
extern
#endif
const uint8_t greeter[] = {
0x52,0x49,0x54,0x45,0x30,0x33,0x30,0x30,0x00,0x00,0x01,0x2c,0x4d,0x41,0x54,0x5a,
0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0xf8,0x30,0x33,0x30,0x30,
0x00,0x00,0x00,0x52,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x1b,
0x11,0x01,0x11,0x02,0x5c,0x01,0x00,0x5e,0x01,0x00,0x1d,0x01,0x00,0x51,0x02,0x00,
0x2f,0x01,0x01,0x01,0x2f,0x01,0x02,0x00,0x38,0x01,0x69,0x00,0x01,0x00,0x00,0x05,
0x77,0x6f,0x72,0x6c,0x64,0x00,0x00,0x03,0x00,0x07,0x47,0x72,0x65,0x65,0x74,0x65,
0x72,0x00,0x00,0x03,0x6e,0x65,0x77,0x00,0x00,0x07,0x69,0x6e,0x76,0x6f,0x6b,0x65,
0x21,0x00,0x00,0x00,0x00,0x3d,0x00,0x01,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0x00,
0x00,0x12,0x63,0x01,0x58,0x02,0x00,0x5f,0x01,0x00,0x63,0x01,0x58,0x02,0x01,0x5f,
0x01,0x01,0x38,0x01,0x00,0x00,0x00,0x02,0x00,0x0a,0x69,0x6e,0x69,0x74,0x69,0x61,
0x6c,0x69,0x7a,0x65,0x00,0x00,0x07,0x69,0x6e,0x76,0x6f,0x6b,0x65,0x21,0x00,0x00,
0x00,0x00,0x28,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x34,
0x04,0x00,0x00,0x01,0x03,0x01,0x1a,0x03,0x00,0x38,0x03,0x00,0x00,0x00,0x01,0x00,
0x05,0x40,0x6e,0x61,0x6d,0x65,0x00,0x00,0x00,0x00,0x35,0x00,0x02,0x00,0x04,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x34,0x00,0x00,0x00,0x51,0x02,0x00,0x19,0x03,
0x00,0x52,0x02,0x38,0x02,0x00,0x01,0x00,0x00,0x07,0x48,0x65,0x6c,0x6c,0x6f,0x2c,
0x20,0x00,0x00,0x01,0x00,0x05,0x40,0x6e,0x61,0x6d,0x65,0x00,0x4c,0x56,0x41,0x52,
0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x01,0x00,0x04,0x6e,0x61,0x6d,0x65,0x00,0x00,
0xff,0xff,0xff,0xff,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
};

Bereiten wir nun eine Boilerplate-C-Datei vor, so wie es der Artikel vorschlägt:

#include <mruby.h>
#include <mruby/irep.h>
#include "hello.c"

int
main(void)
{
  mrb_state *mrb = mrb_open();
  if (!mrb) { /* handle error */ }
  mrb_load_irep(mrb, greeter);
  mrb_close(mrb);
  return 0;
}

Und nun die Zusammenstellung:

gcc -std=c99 -I ~/.asdf/installs/ruby/mruby-3.3.0/include greeter.c -o greeter ~/.asdf/installs/ruby/mruby-3.3.0/lib/libmruby.a -lm

(natürlich können Ihre genauen Pfade unterschiedlich sein)

Als Ergebnis haben wir eine vollständige eigenständige Binärdatei, die wir ausführen können:

$ ./greeter
Hello, world

Die Binärdatei hat auf meinem Rechner fast 6 MB, was darauf hindeutet, dass sie wirklich eigenständig ist.

Recap

Wir haben hier ein paar Dinge angesprochen:

  • Installation von mruby
  • Verwendung der REPL und Ausführung von Ein-Datei-Skripten
  • Lernen einiger Einschränkungen im Vergleich zu CRuby
  • Kompilieren von Skripten zu Binärdateien (sowohl eigenständig als auch eingebettet in eine C-Datei)
  • Mit ein wenig Boilerplate eine vollständig eigenständige Binärdatei erstellen

Das ist ein ziemlich guter Anfang. Die nächsten Schritte wären, einige Edelsteine zu verwenden und etwas mit etwas mehr Funktionen zu erstellen als nur ein Hallo-Welt-Programm.

Share:
Back to Blog

Related Posts

View All Posts »

Ruby on Rails World 2024

Rails 8 verfolgt einen radikal vereinfachten Ansatz, um die Komplexität moderner Webentwicklung zu reduzieren. Mit dem \#NOBUILD-Prinzip werden CSS und JavaScript ohne Build-Prozesse direkt an den Browser geliefert. Die neuen Technologien Propshaft, Solid Cable und Solid Queue ermöglichen performante Lösungen ohne den Einsatz externer Dienste. Rails will damit die Abhängigkeit von teuren PaaS-Diensten minimieren und setzt auf offene, kostengünstige Alternativen für die Bereitstellung auf eigener Hardware. Gleichzeitig werden leistungsfähige Tools wie Thruster und Kamal 2 eingeführt, die Deployment-Prozesse weiter optimieren. Rails 8 kombiniert bewährte Prinzipien mit innovativen Features, um Entwicklern maximale Flexibilität und Effizienz zu bieten.

Ruby 3.3.5 Released

Ruby 3.3.5 wurde veröffentlicht. Das Update behebt kleinere Fehler und wird allen Nutzern empfohlen. Weitere Details sind in den GitHub Release Notes verfügbar.

All things JRuby

Charles Nutter von JRuby ist zurück mit einem Update zu allen Dingen rund um JRuby, nachdem das Sponsoring von Red Hat zu Ende gegangen ist. "Die Zukunft von JRuby steht nicht in Frage", beruhigt er uns.