<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Webseiten erstellen &#8211; webmatze.de</title>
	<atom:link href="https://webmatze.de/category/webseiten-erstellen/feed/" rel="self" type="application/rss+xml" />
	<link>https://webmatze.de</link>
	<description>Profi Tipps für einen erfolgreichen Internetauftritt</description>
	<lastBuildDate>Wed, 11 Feb 2026 19:18:31 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>AI Coding-Agents sicher ausführen mit sandbox-shell (sx)</title>
		<link>https://webmatze.de/ai-coding-agents-sicher-ausfuehren-mit-sandbox-shell-sx/</link>
					<comments>https://webmatze.de/ai-coding-agents-sicher-ausfuehren-mit-sandbox-shell-sx/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Wed, 11 Feb 2026 19:18:31 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[claude]]></category>
		<category><![CDATA[codex]]></category>
		<category><![CDATA[sandbox]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1142</guid>

					<description><![CDATA[Tools wie Claude Code oder Codex sind unglaublich produktiv. Sie schreiben Code, führen Tests aus, installieren Dependencies und arbeiten eigenständig an Features. Aber: Dafür brauchen sie Zugriff auf dein Terminal. Und genau da wird es spannend. Denn ein AI-Agent mit Shell-Zugriff kann im Prinzip alles, was du auch kannst. Deine SSH-Keys lesen, AWS-Credentials zugreifen, Dateien [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Tools wie Claude Code oder Codex sind unglaublich produktiv. Sie schreiben Code, führen Tests aus, installieren Dependencies und arbeiten eigenständig an Features. Aber: Dafür brauchen sie Zugriff auf dein Terminal. Und genau da wird es spannend.</p>
<p>Denn ein AI-Agent mit Shell-Zugriff kann im Prinzip alles, was du auch kannst. Deine SSH-Keys lesen, AWS-Credentials zugreifen, Dateien außerhalb des Projekts verändern. Nicht weil die Tools bösartig sind, sondern weil ein <code>npm install</code> mit einem manipulierten Paket oder ein falsch interpretierter Prompt reichen kann.</p>
<p>Die Frage ist also: Wie gibst du einem AI-Agent genug Rechte zum Arbeiten, ohne ihm gleich die Schlüssel zu deinem ganzen System zu geben?</p>
<h2>sandbox-shell (sx): macOS Seatbelt für Entwickler</h2>
<p>macOS hat mit Seatbelt ein mächtiges Sandboxing-System eingebaut. Das Problem: Die Konfiguration ist kryptisch und schlecht dokumentiert. Genau hier setzt <code>sx</code> an. Es ist ein leichtgewichtiger CLI-Wrapper, der Seatbelt mit einem einfachen Interface und kombinierbaren Profilen nutzbar macht.</p>
<p>Das Prinzip ist Deny-by-Default:</p>
<ul>
<li>Dateisystem: Nur das Projektverzeichnis + <code>/tmp</code> sind beschreibbar</li>
<li>Netzwerk: Standardmäßig komplett blockiert</li>
<li>Sensible Pfade: <code>~/.ssh</code>, <code>~/.aws</code>, <code>~/Documents</code> usw. sind gesperrt</li>
</ul>
<h3>Installation</h3>
<pre><code class="language-bash">brew tap agentic-dev3o/sx
brew install sx</code></pre>
<p>Optional: Shell-Integration für Prompt-Indikatoren und Aliases:</p>
<pre><code class="language-bash"># In ~/.zshrc einfügen
source $(brew --prefix)/share/sx/sx.zsh</code></pre>
<p>Damit bekommt ihr im Terminal einen Indikator, ob ihr gerade in einer Sandbox arbeitet (<code>[sx:offline]</code>, <code>[sx:localhost]</code>, <code>[sx:online]</code>), und praktische Aliases wie <code>sxo</code> (online) und <code>sxl</code> (localhost).</p>
<h2>Globale Konfiguration</h2>
<p>In <code>~/.config/sx/config.toml</code> definiert ihr, welche Pfade eure Tools generell lesen und schreiben dürfen. Das hängt davon ab, welchen Ruby-Version-Manager ihr nutzt. Hier ein Beispiel für <code>mise</code>:</p>
<pre><code class="language-toml">[filesystem]
allow_read = [
    &quot;~/.gitconfig&quot;,
    &quot;~/.config/git/&quot;,
    &quot;~/.bundle/&quot;,
    &quot;~/.gem/&quot;,
    &quot;~/.local/share/mise/&quot;,
    &quot;~/.config/mise/&quot;,
]

allow_write = [
    &quot;~/.bundle/&quot;,
    &quot;~/.gem/&quot;,
    &quot;~/.cache/&quot;,
]</code></pre>
<p>Nutzt ihr <code>rbenv</code>, ersetzt die <code>mise</code>-Pfade durch <code>~/.rbenv/</code>. Bei <code>asdf</code> entsprechend <code>~/.asdf/</code>.</p>
<h2>Projekt-Konfiguration</h2>
<p>Im Projektverzeichnis könnt ihr mit <code>sx --init</code> eine <code>.sandbox.toml</code> anlegen:</p>
<pre><code class="language-bash">cd mein-projekt
sx --init</code></pre>
<p>Für ein typisches Rails-Projekt mit MySQL, Redis und Memcache in Docker sieht die Konfiguration so aus:</p>
<pre><code class="language-toml">[sandbox]
inherit_global = true

# Localhost für Datenbankzugriff über Docker
network = &quot;localhost&quot;

[shell]
pass_env = [&quot;RUBY_DEBUG_ENABLE&quot;]</code></pre>
<p>Die <code>pass_env</code>-Einstellung ist wichtig: Der Ruby-Debugger (<code>rdbg</code>) versucht beim Start automatisch einen Unix-Socket zu öffnen, was Seatbelt im Localhost-Modus blockiert. Mit <code>RUBY_DEBUG_ENABLE=0</code> als Environment-Variable umgeht ihr das Problem.</p>
<h2>Was blockiert die Sandbox konkret?</h2>
<p>Ein <code>sx --explain</code> zeigt euch genau, was erlaubt und was gesperrt ist:</p>
<pre><code class="language-bash">=== Sandbox Configuration ===

Network Mode: Localhost

Working Directory (full access):
  /Users/mathias/Workspace/mein-projekt

Denied Read Paths:
  - ~/.ssh
  - ~/.aws
  - ~/.docker/config.json
  - ~/Documents
  - ~/Desktop
  - ~/Downloads</code></pre>
<p>Und das könnt ihr leicht verifizieren:</p>
<pre><code class="language-bash"># Funktioniert – Projektdateien sind lesbar
sx -- cat README.md

# Blockiert – SSH-Keys sind geschützt
sx -- cat ~/.ssh/id_rsa
# =&gt; No such file or directory

# Blockiert – kein Schreibzugriff außerhalb des Projekts
sx -- touch ~/Desktop/test
# =&gt; Operation not permitted</code></pre>
<p>Seatbelt macht die gesperrten Dateien quasi unsichtbar. Ein Prozess in der Sandbox weiß nicht einmal, dass <code>~/.ssh/id_rsa</code> existiert.</p>
<h2>Netzwerk-Modi</h2>
<p><code>sx</code> bietet drei Netzwerk-Modi, die ihr je nach Aufgabe kombiniert:</p>
<table>
<thead>
<tr>
<th>Modus</th>
<th>Befehl</th>
<th>Erlaubt</th>
</tr>
</thead>
<tbody>
<tr>
<td>Offline</td>
<td><code>sx --</code></td>
<td>Kein Netzwerk</td>
</tr>
<tr>
<td>Localhost</td>
<td><code>sx localhost --</code></td>
<td>Nur 127.0.0.1 (Docker-Services)</td>
</tr>
<tr>
<td>Online</td>
<td><code>sx online --</code></td>
<td>Voller Netzwerkzugriff</td>
</tr>
</tbody>
</table>
<p>In der Praxis heißt das:</p>
<pre><code class="language-bash"># Linting braucht kein Netzwerk
sx -- bundle exec rubocop

# Tests brauchen die lokale Datenbank
RUBY_DEBUG_ENABLE=0 sx localhost -- bundle exec rspec

# Gems installieren braucht Internet
sx online -- bundle install</code></pre>
<h2>Einsatz mit AI Coding-Agents</h2>
<p>Jetzt zum eigentlichen Punkt: Warum das Ganze besonders relevant für AI-Tools ist.</p>
<h3>Claude Code</h3>
<p>Claude Code hat einen eingebauten <code>--dangerously-skip-permissions</code>-Modus, der alle Bestätigungsdialoge überspringt. Praktisch, aber riskant. Mit <code>sx</code> könnt ihr diesen Modus nutzen und trotzdem sicher bleiben:</p>
<pre><code class="language-bash">sx claude -- claude --dangerously-skip-permissions</code></pre>
<p>Das <code>claude</code>-Profil gibt Claude Zugriff auf <code>~/.claude</code>, während alles andere gesperrt bleibt. Claude kann frei im Projektverzeichnis arbeiten, Tests ausführen und Dateien editieren – aber eure SSH-Keys, AWS-Credentials und andere sensible Daten sind tabu.</p>
<h3>OpenAI Codex</h3>
<p>Für Codex gilt das gleiche Prinzip:</p>
<pre><code class="language-bash">sx localhost -- codex</code></pre>
<h3>Warum nicht einfach Docker?</h3>
<p>Docker ist natürlich auch eine Option, aber <code>sx</code> hat ein paar Vorteile für den täglichen Einsatz:</p>
<ul>
<li><strong>Kein Overhead</strong>: Kein Container-Build, kein Volume-Mounting, keine Port-Forwards</li>
<li><strong>Native Performance</strong>: Läuft direkt auf dem Host, keine Virtualisierung</li>
<li><strong>Einfache Integration</strong>: Ein Prefix vor dem Befehl, fertig</li>
<li><strong>Zugriff auf lokale Tools</strong>: Eure installierte Ruby-Version, eure Shell-Config – alles da</li>
</ul>
<h2>Mein Alltags-Setup</h2>
<p>So sieht mein typischer Workflow aus:</p>
<table>
<thead>
<tr>
<th>Aufgabe</th>
<th>Befehl</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tests ausführen</td>
<td><code>RUBY_DEBUG_ENABLE=0 sx localhost -- bundle exec rspec</code></td>
</tr>
<tr>
<td>Einzelnen Test</td>
<td><code>RUBY_DEBUG_ENABLE=0 sx localhost -- bundle exec rspec spec/models/user_spec.rb</code></td>
</tr>
<tr>
<td>Linting</td>
<td><code>sx -- bundle exec rubocop</code></td>
</tr>
<tr>
<td>Gems installieren</td>
<td><code>sx online -- bundle install</code></td>
</tr>
<tr>
<td>Rails Server</td>
<td><code>RUBY_DEBUG_ENABLE=0 sx localhost -- bin/rails server</code></td>
</tr>
<tr>
<td>Rails Console</td>
<td><code>RUBY_DEBUG_ENABLE=0 sx localhost -- bin/rails console</code></td>
</tr>
<tr>
<td>AI-Agent starten</td>
<td><code>sx claude -- claude</code></td>
</tr>
<tr>
<td>Git Push</td>
<td>Außerhalb der Sandbox (SSH-Keys blockiert)</td>
</tr>
</tbody>
</table>
<h2>Fazit</h2>
<p>AI Coding-Agents sind gekommen, um zu bleiben. Die Produktivitätsgewinne sind real. Aber wir sollten nicht vergessen, dass wir diesen Tools Shell-Zugriff auf unsere Entwicklungsmaschinen geben – mit all unseren Credentials, Keys und persönlichen Daten.</p>
<p><code>sx</code> löst das elegant: Ein Befehlsprefix, ein paar Zeilen Config, und eure AI-Agents arbeiten in einer Sandbox, die sie alles tun lässt, was sie für die Entwicklung brauchen – und nichts darüber hinaus.</p>
<p>Viel Spaß beim Ausprobieren!</p>
<p><strong>Links:</strong></p>
<ul>
<li><a href="https://github.com/agentic-dev3o/sandbox-shell">sandbox-shell auf GitHub</a></li>
<li><a href="https://claude.ai/claude-code">Claude Code</a></li>
<li><a href="https://developer.apple.com/documentation/security">macOS Seatbelt Dokumentation</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/ai-coding-agents-sicher-ausfuehren-mit-sandbox-shell-sx/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Recordables in Rails: Delegated Types praxisnah erklärt</title>
		<link>https://webmatze.de/recordables-in-rails-delegated-types-praxisnah-erklaert/</link>
					<comments>https://webmatze.de/recordables-in-rails-delegated-types-praxisnah-erklaert/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Fri, 19 Dec 2025 19:15:37 +0000</pubDate>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Recordable Pattern]]></category>
		<category><![CDATA[ruby]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1122</guid>

					<description><![CDATA[In diesem Beitrag zeige ich dir das Recordable Pattern, das 37signals (Basecamp, HEY) einsetzt, und erkläre es Schritt für Schritt anhand von lauffähigen Rails-Code-Beispielen. Zielgruppe sind Anfänger bis Fortgeschrittene Ruby on Rails Entwickler, die polymorphe Datenmodelle sauber, skalierbar und gut paginierbar bauen möchten. Warum das Ganze? Klassische Ansätze wie Single Table Inheritance (STI) oder „nackte“ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>In diesem Beitrag zeige ich dir das Recordable Pattern, das 37signals (Basecamp, HEY) einsetzt, und erkläre es Schritt für Schritt anhand von lauffähigen Rails-Code-Beispielen. Zielgruppe sind Anfänger bis Fortgeschrittene Ruby on Rails Entwickler, die polymorphe Datenmodelle sauber, skalierbar und gut paginierbar bauen möchten.</p>
<p><iframe title="The Rails Delegated Type Pattern with Jeffrey Hardy" width="500" height="281" src="https://www.youtube.com/embed/m90sl-Uvu0Y?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p>
<p>Warum das Ganze? Klassische Ansätze wie Single Table Inheritance (STI) oder „nackte“ polymorphe Assoziationen stoßen schnell an Grenzen: STI bläht Tabellen auf, polymorphes CRUD bleibt oft mühsam zu paginieren. Das Recordable Pattern nutzt Rails’ <a href="http://https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html" title="Delegated Types">Delegated Types</a>, um eine dünne, performante „Superklasse“-Tabelle (Recording) mit schlanken „Subklasse“-Tabellen (Recordables wie Message, Document, Comment) zu kombinieren.</p>
<hr />
<h2>Das Grundprinzip</h2>
<ul>
<li>Eine zentrale Tabelle <strong>„recordings“</strong> hält alle gemeinsamen Metadaten (z. B. Bucket, Creator, Timestamps) und verweist polymorph auf den „konkreten Inhalt“ (recordable).</li>
<li>Jede konkrete Recordable (Message, Document, Comment) hat eine eigene Tabelle mit genau ihren Feldern.</li>
<li>Abfragen, Pagination und gemeinsame Logik passieren über Recording; konkretes Verhalten (z. B. <code>export_html</code>) lebt bei den Recordables.</li>
</ul>
<p>So bleibst du:</p>
<ul>
<li>flexibel (neue Typen ohne Recording-Migration),</li>
<li>performant (einheitliche Timeline auf der dünnen Recording-Tabelle),</li>
<li>sauber getrennt (Metadaten vs. Inhalte).</li>
</ul>
<hr />
<h2>Minimales Datenmodell</h2>
<p>Migration (gekürzt auf das Wesentliche):</p>
<pre><code class="language-ruby">create_table :recordings do |t|
  t.references :bucket,  null: false, foreign_key: true
  t.references :creator, null: false, foreign_key: { to_table: :users }
  t.string  :recordable_type, null: false
  t.bigint  :recordable_id,   null: false
  t.index [:recordable_type, :recordable_id]
  t.bigint :parent_id # optional: Recording-Baum (Unterelemente)
  t.index :parent_id
  t.timestamps
end
add_foreign_key :recordings, :recordings, column: :parent_id

create_table :messages do |t|
  t.string :title,   null: false
  t.text   :content, null: false
  t.timestamps
end

create_table :documents do |t|
  t.string :title, null: false
  t.text   :body
  t.string :external_url
  t.timestamps
end

create_table :comments do |t|
  t.text :body, null: false
  t.timestamps
end</code></pre>
<hr />
<h2>Models und Concerns</h2>
<p>Recording: die „Superklasse“ mit Delegation</p>
<pre><code class="language-ruby">class Recording &lt; ApplicationRecord
  belongs_to :bucket
  belongs_to :creator, class_name: &quot;User&quot;

  delegated_type :recordable, types: %w[Message Document Comment], dependent: :destroy

  # optional: Baumstruktur
  belongs_to :parent, class_name: &quot;Recording&quot;, optional: true
  has_many   :children, class_name: &quot;Recording&quot;, foreign_key: :parent_id, dependent: :nullify

  # Bequeme Filter-Scopes kommen mit delegated_type:
  # Recording.messages, Recording.documents, Recording.comments
end</code></pre>
<p>Recordable-Concern: gemeinsame API für Inhalte</p>
<pre><code class="language-ruby">module Recordable
  extend ActiveSupport::Concern

  included do
    has_many :recordings, as: :recordable, inverse_of: :recordable
  end

  def display_title
    respond_to?(:title) ? title : self.class.name
  end

  def export_html
    &quot;#{display_title}&quot;
  end
end</code></pre>
<p>Konkrete Typen:</p>
<pre><code class="language-ruby">class Message &lt; ApplicationRecord
  include Recordable
  validates :title, :content, presence: true

  def export_html
    &quot;#{title}#{content}&quot;
  end
end

class Document &lt; ApplicationRecord
  include Recordable
  validates :title, presence: true

  def export_html
    if external_url.present?
      &quot;#{title}External: #{external_url}&quot;
    else
      &quot;#{title}#{body}&quot;
    end
  end
end

class Comment &lt; ApplicationRecord
  include Recordable
  validates :body, presence: true

  def display_title
    body.truncate(40)
  end

  def export_html
    &quot;#{body}&quot;
  end
end</code></pre>
<hr />
<h2>Erstellen und Paginieren einer Timeline</h2>
<p>Bucket kapselt die Aufnahme neuer Inhalte („record“) und die Timeline. Bucket ist hier eher abstrakt und könnte genausogut auch ein Projekt sein, oder ein Kunde:</p>
<pre><code class="language-ruby">class Bucket &lt; ApplicationRecord
  has_many :recordings, dependent: :destroy

  def timeline(limit: 50)
    recordings.order(created_at: :desc).limit(limit).includes(:recordable)
  end

  def record(recordable, creator:, parent: nil, color: nil)
    recordings.create!(recordable: recordable, creator: creator, parent: parent, color: color)
  end
end</code></pre>
<p>Typische Abfragen:</p>
<pre><code class="language-ruby">bucket = Bucket.find(1)

# Gesamte Timeline effizient paginieren
bucket.timeline(limit: 50).each do |rec|
  puts &quot;[#{rec.recordable_type}] #{rec.recordable.display_title}&quot;
end

# Nur bestimmte Typen (Scopes via delegated_type)
Recording.messages.where(bucket: bucket).limit(20)
Recording.documents.where(bucket: bucket).order(created_at: :desc)</code></pre>
<hr />
<h2>Versionierung per Repointing (immutable Recordables)</h2>
<p>Statt Inhalte zu „updaten“, erzeugst du neue Recordables und verweist die Recording-Zeile auf die neue Version. Das hält Historie sauber und macht Kopieren effizient.</p>
<pre><code class="language-ruby">class Recording &lt; ApplicationRecord
  # ...

  def repoint_to!(new_recordable)
    update!(recordable: new_recordable)
  end
end

# Beispiel: Message ? neue Document-Version
msg_rec = bucket.record(Message.create!(title: &quot;Kickoff&quot;, content: &quot;Welcome!&quot;), creator: user)
new_doc = Document.create!(title: &quot;Specs v2&quot;, body: &quot;Updated requirements&quot;)
msg_rec.repoint_to!(new_doc) # Recording zeigt nun auf Document</code></pre>
<p>Wenn du eine echte Ereignis-Historie brauchst, ergänze ein <code>Event</code>-Model und schreibe beim <code>record</code>/<code>repoint_to!</code> Einträge (z. B. „created“, „repointed“, inkl. vorher/nachher <code>recordable_type/_id</code>). Das hält Audit-Trails und erlaubt Timeline-Features (siehe Video/37signals-Artikel).</p>
<hr />
<h2>Copy &amp; Move: Subtrees kopieren</h2>
<p>Ein Vorteil des Patterns: Kopieren und Verschieben ganzer Teilbäume wird planbar und schnell, weil Recordings leichtgewichtig sind und Recordables immutable dupliziert werden. Eine einfache Kopier-Serviceklasse:</p>
<pre><code class="language-ruby">class Copier
  def self.copy!(source_recording:, destination_bucket:, creator:)
    new(source_recording, destination_bucket, creator).copy!
  end

  def initialize(source_recording, destination_bucket, creator)
    @source_recording   = source_recording
    @destination_bucket = destination_bucket
    @creator            = creator
  end

  def copy!
    ActiveRecord::Base.transaction do
      copied_root = copy_recording(@source_recording, parent: nil)
      @source_recording.children.each { |child| copy_branch(child, parent: copied_root) }
      copied_root
    end
  end

  private

  def copy_branch(node, parent:)
    copied = copy_recording(node, parent: parent)
    node.children.each { |child| copy_branch(child, parent: copied) }
    copied
  end

  def copy_recording(original, parent:)
    new_recordable = duplicate_recordable(original.recordable)
    @destination_bucket.record(new_recordable, creator: @creator, parent: parent, color: original.color)
  end

  def duplicate_recordable(recordable)
    case recordable
    when Message
      Message.create!(title: recordable.title, content: recordable.content)
    when Document
      Document.create!(title: recordable.title, body: recordable.body, external_url: recordable.external_url)
    when Comment
      Comment.create!(body: recordable.body)
    else
      raise ArgumentError, &quot;Unsupported recordable: #{recordable.class.name}&quot;
    end
  end
end</code></pre>
<hr />
<h2>Vergleich mit STI und „plain“ Polymorphismus</h2>
<ul>
<li><strong>STI</strong>: Einfache Fälle ok. Bei divergenten Typen entsteht Tabellen-Bloat, viele NULL-Spalten, Migrationen werden groß, Erweiterbarkeit leidet.</li>
<li><strong>Polymorphismus ohne <code>delegated_type</code></strong>: Funktioniert, aber es fehlen bequeme Scopes/Convenience-Methoden; unified Pagination ist oft hakelig.</li>
<li><strong>Recordables + <code>delegated_type</code></strong>: Einheitliche Timeline über eine dünne Tabelle, klare Trennung von Meta vs. Inhalt, leichte Erweiterbarkeit (neuen Typ hinzufügen statt zentrale Tabelle migrieren), gute Performance bei Abfragen.</li>
</ul>
<hr />
<h2>Wann solltest du das Pattern nutzen?</h2>
<ul>
<li>Du brauchst eine gemischte, paginierbare Timeline (z. B. Aktivitätsfeed über Messages, Documents, Comments).</li>
<li>Deine Typen unterscheiden sich stark in ihren Attributen.</li>
<li>Du willst Polymorphismus am Parent (Recording) und saubere Delegation an die Inhalte.</li>
</ul>
<p>Wann eher nicht?</p>
<ul>
<li>Deine Subtypen sind fast identisch (STI könnte reichen).</li>
<li>Du hast keine gemeinsamen Abfragen über Typgrenzen hinweg.</li>
</ul>
<hr />
<h2>Best Practices</h2>
<ul>
<li>Halte Recording schlank: nur Metadaten und die polymorphe Referenz. Keine großen Textfelder.</li>
<li>Packe typenübergreifende Logik in Recording (oder Concerns), typenspezifische Logik in die Recordables.</li>
<li>Nutze <code>delegate</code> am Recording für gemeinsame Schnittstellen, z. B. <code>delegate :export_html, to: :recordable</code>.</li>
<li>Wenn Auditing/History wichtig ist, ergänze Events und schreibe Änderungen mit.</li>
<li>Autorisierung: Recording bündelt viele Aktionen – prüfe Berechtigungen konsequent.</li>
</ul>
<hr />
<h2>Fazit</h2>
<p>Das Recordable Pattern mit <code>delegated_type</code> ist eine elegante, praxiserprobte Lösung für polymorphe Inhalte in Rails. Es bringt dir:</p>
<ul>
<li>eine einheitliche, performante Timeline,</li>
<li>klare Verantwortlichkeiten zwischen Metadaten und Inhaltsobjekten,</li>
<li>einfache Erweiterbarkeit ohne große Migrationen,</li>
<li>saubere APIs durch Delegation.</li>
</ul>
<p>Wenn du Feed-ähnliche Strukturen, Versionierung und Kopier-Features brauchst, wirst du mit Recordings/Recordables sehr schnell produktiv.</p>
<p>Viel Spaß beim Ausprobieren – und schreib mir gern, wenn du Fragen hast oder Beispiele aus deinem Projekt teilen willst!</p>
<p>— Mathias (webmatze.de)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/recordables-in-rails-delegated-types-praxisnah-erklaert/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>React, Svelte oder Astro? Ein Leitfaden für Einsteiger</title>
		<link>https://webmatze.de/react-svelte-oder-astro-ein-leitfaden-fuer-einsteiger/</link>
					<comments>https://webmatze.de/react-svelte-oder-astro-ein-leitfaden-fuer-einsteiger/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Wed, 03 Dec 2025 21:17:46 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Astro]]></category>
		<category><![CDATA[Einsteiger]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Svelte]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1109</guid>

					<description><![CDATA[Vergleich der drei wichtigsten JavaScript-Frameworks für Einsteiger. Mit praktischen Hello World Beispielen und Entscheidungshilfen für React, Svelte und Astro.]]></description>
										<content:encoded><![CDATA[<p>Wer heute mit der Webentwicklung beginnt, steht vor einer schier endlosen Auswahl an JavaScript-Frameworks. React dominiert mit einem Marktanteil von über 80%, aber immer mehr Entwickler entdecken Alternativen wie Svelte und Astro. Welches Framework ist das richtige für dich als Einsteiger?</p>
<p>In diesem Artikel vergleiche ich die drei wichtigsten JavaScript-Frameworks des Jahres 2025: React, Svelte und Astro. Du bekommst praktische &quot;Hello World&quot;-Beispiele für jedes Framework und klare Entscheidungshilfen, die dir bei der Auswahl helfen. Spoiler: Es gibt kein &quot;bestes&quot; Framework – es kommt auf deinen Use Case an.</p>
<h2>Was sind JavaScript-Frameworks?</h2>
<p>Bevor wir in den Vergleich einsteigen, lass uns kurz klären, was JavaScript-Frameworks eigentlich sind und warum wir sie brauchen.</p>
<p>Ein <strong>JavaScript-Framework</strong> ist eine Sammlung von vorgefertigten Code-Bausteinen, die dir helfen, moderne Webanwendungen effizienter zu entwickeln. Statt jedes Mal das Rad neu zu erfinden, bieten Frameworks Lösungen für wiederkehrende Probleme wie:</p>
<ul>
<li><strong>Komponentenbasierte Entwicklung:</strong> Teile deine Benutzeroberfläche in wiederverwendbare Bausteine auf</li>
<li><strong>Reaktivität:</strong> Die Benutzeroberfläche aktualisiert sich automatisch, wenn sich Daten ändern</li>
<li><strong>State Management:</strong> Verwalte den Zustand deiner Anwendung zentral</li>
<li><strong>Routing:</strong> Navigation zwischen verschiedenen Seiten ohne volle Seitenladezeiten</li>
</ul>
<p>Frameworks sparen dir Zeit, helfen dir dabei, sauberen Code zu schreiben, und bieten bewährte Lösungsansätze, die in der Community getestet wurden.</p>
<h2>React: Der etablierte Standard</h2>
<h3>Was ist React?</h3>
<p>React wurde 2013 von Facebook (heute Meta) entwickelt und ist heute das mit Abstand populärste JavaScript-Framework. Genau genommen ist React eine <strong>Bibliothek</strong> (Library) für User Interfaces, wird aber oft als Framework bezeichnet. React setzt auf das Konzept des <strong>Virtual DOM</strong> – einer virtuellen Darstellung des DOM, die React nutzt, um Updates effizient durchzuführen.</p>
<h3>Die Stärken von React</h3>
<p><strong>Riesiges Ökosystem:</strong> React hat die größte Community aller JavaScript-Frameworks. Das bedeutet:</p>
<ul>
<li>Unzählige Third-Party-Bibliotheken für fast jeden Use Case</li>
<li>Viele Tutorials, Kurse und Lernressourcen</li>
<li>Schnelle Antworten auf Stack Overflow und in Foren</li>
</ul>
<p><strong>Job-Markt:</strong> Wenn du als Entwickler arbeiten möchtest, ist React die sicherste Wahl. Die meisten Job-Angebote setzen React-Kenntnisse voraus.</p>
<p><strong>Mature und stabil:</strong> Nach über 10 Jahren Entwicklung ist React ausgereift. Breaking Changes sind selten, und Meta investiert kontinuierlich in die Weiterentwicklung.</p>
<h3>Die Lernkurve</h3>
<p>React hat eine <strong>mittelschwere Lernkurve</strong> für Einsteiger:</p>
<ul>
<li><strong>JSX-Syntax:</strong> Du schreibst HTML-ähnlichen Code direkt in JavaScript. Das wirkt anfangs ungewohnt, wird aber schnell zur zweiten Natur.</li>
<li><strong>Hooks:</strong> Seit React 16.8 gibt es Hooks wie <code>useState</code> und <code>useEffect</code>. Sie ersetzen die alten Class Components und sind einfacher zu verstehen.</li>
<li><strong>Konzepte:</strong> Du musst Konzepte wie Props, State und Component Lifecycle verstehen.</li>
</ul>
<h3>Praktisches React-Beispiel</h3>
<p>Hier ist eine einfache Counter-Komponente in React:</p>
<pre><code class="language-jsx">// Einfache Counter-Komponente mit React Hooks
import { useState } from &#039;react&#039;;

function Counter() {
  // useState Hook für reaktiven State
  // count ist die aktuelle Zahl, setCount ist die Funktion zum Ändern
  const [count, setCount] = useState(0);

  return (
    &lt;div&gt;
      &lt;h1&gt;React Counter&lt;/h1&gt;
      &lt;p&gt;Aktuelle Zahl: {count}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
        Erhöhen
      &lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;
        Verringern
      &lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(0)}&gt;
        Zurücksetzen
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

export default Counter;</code></pre>
<p><strong>Was passiert hier?</strong></p>
<ul>
<li><code>useState(0)</code> erstellt eine State-Variable <code>count</code> mit Startwert 0</li>
<li>Bei jedem Klick auf &quot;Erhöhen&quot; wird <code>setCount</code> aufgerufen</li>
<li>React erkennt die Änderung und aktualisiert automatisch die Anzeige</li>
<li>JSX mischt HTML-ähnliche Syntax mit JavaScript (<code>{count}</code> ist JavaScript)</li>
</ul>
<h2>Svelte: Der aufsteigende Stern</h2>
<h3>Was ist Svelte?</h3>
<p>Svelte, entwickelt von Rich Harris, ist das jüngste der drei Frameworks (erstes Release 2016, große Überarbeitung 2019 mit Svelte 3). Der große Unterschied zu React: Svelte ist ein <strong>Compiler</strong>, kein Framework, das zur Laufzeit im Browser läuft.</p>
<p>Das bedeutet: Svelte verwandelt deinen Code beim Build-Prozess in hochoptimiertes Vanilla JavaScript. Es gibt keine Framework-Runtime, die im Browser geladen werden muss – Svelte &quot;verschwindet&quot; im finalen Bundle.</p>
<h3>Die Stärken von Svelte</h3>
<p><strong>Einfache, intuitive Syntax:</strong> Svelte fühlt sich näher an HTML, CSS und JavaScript an als React. Es gibt weniger Boilerplate-Code.</p>
<p><strong>Performance:</strong> Da Svelte zur Build-Zeit kompiliert wird, ist es extrem schnell. Keine Virtual DOM-Berechnungen zur Laufzeit.</p>
<p><strong>Weniger Code:</strong> Svelte-Komponenten brauchen im Schnitt 40% weniger Code als vergleichbare React-Komponenten.</p>
<p><strong>Reaktivität &quot;einfach da&quot;:</strong> In Svelte ist Reaktivität eingebaut – du musst keine speziellen Hooks oder State-Management-Tools lernen.</p>
<h3>Die Lernkurve</h3>
<p>Svelte hat die <strong>niedrigste Lernkurve</strong> der drei Frameworks:</p>
<ul>
<li><strong>HTML-ähnlicher als JSX:</strong> Svelte-Templates sehen aus wie HTML mit einigen Erweiterungen</li>
<li><strong>Reaktivität ist intuitiv:</strong> <code>let count = 0; count += 1</code> funktioniert einfach</li>
<li><strong>Scoped CSS:</strong> Styles sind automatisch auf die Komponente beschränkt</li>
</ul>
<h3>Praktisches Svelte-Beispiel</h3>
<p>Hier ist die gleiche Counter-Komponente in Svelte:</p>
<pre><code class="language-html">&lt;script&gt;
  // Reaktive Variable - automatisch aktualisiert
  let count = 0;

  // Reaktive Berechnungen mit $:
  // Wird automatisch neu berechnet, wenn count sich ändert
  $: doubled = count * 2;

  // Funktionen für Button-Clicks
  function increment() {
    count += 1; // So einfach ist Reaktivität in Svelte!
  }

  function decrement() {
    count -= 1;
  }

  function reset() {
    count = 0;
  }
&lt;/script&gt;

&lt;div&gt;
  &lt;h1&gt;Svelte Counter&lt;/h1&gt;
  &lt;p&gt;Aktuelle Zahl: {count}&lt;/p&gt;
  &lt;p&gt;Verdoppelt: {doubled}&lt;/p&gt;

  &lt;button on:click={increment}&gt;
    Erhöhen
  &lt;/button&gt;
  &lt;button on:click={decrement}&gt;
    Verringern
  &lt;/button&gt;
  &lt;button on:click={reset}&gt;
    Zurücksetzen
  &lt;/button&gt;
&lt;/div&gt;

&lt;style&gt;
  /* Scoped CSS - gilt nur für diese Komponente */
  div {
    padding: 20px;
    background-color: #f0f0f0;
    border-radius: 8px;
  }

  button {
    margin: 5px;
    padding: 10px 15px;
    background-color: #ff3e00;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }

  button:hover {
    background-color: #cc3100;
  }
&lt;/style&gt;</code></pre>
<p><strong>Was passiert hier?</strong></p>
<ul>
<li><code>let count = 0</code> ist eine normale Variable – Svelte macht sie automatisch reaktiv</li>
<li><code>$: doubled = count * 2</code> ist eine reaktive Berechnung</li>
<li><code>on:click={increment}</code> bindet die Funktion an den Button</li>
<li>CSS im <code>&lt;style&gt;</code> Block gilt nur für diese Komponente</li>
<li>Deutlich weniger Code als React, aber gleiche Funktionalität!</li>
</ul>
<h2>Astro: Der Performance-Champion</h2>
<h3>Was ist Astro?</h3>
<p>Astro ist das neueste Framework in diesem Vergleich (erstes Release 2021) und verfolgt einen radikal anderen Ansatz: <strong>Zero JavaScript by Default</strong>. Astro ist primär ein <strong>Static Site Generator</strong> mit einer innovativen Architektur namens &quot;Islands Architecture&quot;.</p>
<p>Die Grundidee: Deine Website ist standardmäßig statisches HTML und CSS – komplett ohne JavaScript. Nur dort, wo du Interaktivität brauchst, fügst du &quot;Islands&quot; (Inseln) von JavaScript hinzu.</p>
<h3>Die Stärken von Astro</h3>
<p><strong>Extrem schnell:</strong> Da Astro standardmäßig kein JavaScript ausliefert, sind Astro-Sites blitzschnell. Perfekte Lighthouse-Scores sind die Regel, nicht die Ausnahme.</p>
<p><strong>Content-fokussiert:</strong> Astro wurde für Content-Sites wie Blogs, Marketing-Websites und Dokumentationen entwickelt. Weniger für hochinteraktive Web-Apps.</p>
<p><strong>Multi-Framework Support:</strong> Das Besondere an Astro: Du kannst React, Svelte, Vue und andere Frameworks <em>gleichzeitig</em> in einem Projekt nutzen. Jede Komponente kann in einem anderen Framework geschrieben sein.</p>
<p><strong>Developer Experience:</strong> Astro bietet eine hervorragende Entwickler-Erfahrung mit schnellem Hot Reload und klarer Fehlermeldungen.</p>
<h3>Die Lernkurve</h3>
<p>Astro hat eine <strong>mittlere Lernkurve</strong>:</p>
<ul>
<li><strong>Ähnlich wie andere Komponenten-Frameworks:</strong> Wenn du React oder Svelte kennst, findest du dich schnell zurecht</li>
<li><strong>Neues Konzept:</strong> &quot;Islands Architecture&quot; ist ein neues Paradigma, das du verstehen musst</li>
<li><strong>Zwei &quot;Modi&quot;:</strong> Code im Frontmatter (<code>---</code> Bereich) läuft beim Build, Code in <code>&lt;script&gt;</code> Tags läuft im Browser</li>
</ul>
<h3>Praktisches Astro-Beispiel</h3>
<p>Hier ist die Counter-Komponente in Astro:</p>
<pre><code class="language-html">---
// Component Script (läuft nur beim Build)
const title = &quot;Astro Counter&quot;;
const currentDate = new Date().toLocaleDateString(&#039;de-DE&#039;);

// Dieser Code läuft auf dem Server, nicht im Browser!
console.log(&#039;Diese Komponente wird gerade gebaut...&#039;);
---

&lt;div class=&quot;counter-container&quot;&gt;
  &lt;h1&gt;{title}&lt;/h1&gt;
  &lt;p class=&quot;date&quot;&gt;Erstellt am: {currentDate}&lt;/p&gt;

  &lt;!-- Interaktive Island mit Client-JavaScript --&gt;
  &lt;!-- client:load bedeutet: JavaScript beim Laden ausführen --&gt;
  &lt;div id=&quot;counter&quot;&gt;
    &lt;p&gt;Aktuelle Zahl: &lt;span id=&quot;count&quot;&gt;0&lt;/span&gt;&lt;/p&gt;
    &lt;button id=&quot;increment&quot;&gt;Erhöhen&lt;/button&gt;
    &lt;button id=&quot;decrement&quot;&gt;Verringern&lt;/button&gt;
    &lt;button id=&quot;reset&quot;&gt;Zurücksetzen&lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
  // Dieser Code läuft im Browser (Client-Side)
  let count = 0;

  // DOM-Elemente holen
  const incrementBtn = document.getElementById(&#039;increment&#039;);
  const decrementBtn = document.getElementById(&#039;decrement&#039;);
  const resetBtn = document.getElementById(&#039;reset&#039;);
  const countDisplay = document.getElementById(&#039;count&#039;);

  // Event Listeners hinzufügen
  incrementBtn?.addEventListener(&#039;click&#039;, () =&gt; {
    count++;
    updateDisplay();
  });

  decrementBtn?.addEventListener(&#039;click&#039;, () =&gt; {
    count--;
    updateDisplay();
  });

  resetBtn?.addEventListener(&#039;click&#039;, () =&gt; {
    count = 0;
    updateDisplay();
  });

  // Display aktualisieren
  function updateDisplay() {
    if (countDisplay) {
      countDisplay.textContent = count.toString();
    }
  }
&lt;/script&gt;

&lt;style&gt;
  .counter-container {
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 8px;
    max-width: 400px;
    margin: 20px auto;
  }

  .date {
    color: #666;
    font-size: 0.9em;
  }

  #counter {
    margin-top: 20px;
    padding: 15px;
    background-color: white;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  }

  button {
    margin: 5px;
    padding: 10px 15px;
    background-color: #5b21b6;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: 500;
  }

  button:hover {
    background-color: #4c1d95;
  }

  #count {
    font-weight: bold;
    font-size: 1.2em;
    color: #5b21b6;
  }
&lt;/style&gt;</code></pre>
<p><strong>Was passiert hier?</strong></p>
<ul>
<li>Code im <code>---</code> Bereich läuft beim Build (Server-Side)</li>
<li><code>currentDate</code> wird zur Build-Zeit erstellt, nicht beim Seitenaufruf</li>
<li>HTML wird als statischer Content ausgeliefert</li>
<li>Nur der Code im <code>&lt;script&gt;</code> Tag läuft im Browser</li>
<li>Weniger &quot;magisch&quot; als React/Svelte, aber volle Kontrolle</li>
</ul>
<p><strong>Hinweis:</strong> In der Praxis würdest du für interaktive Komponenten in Astro oft React, Svelte oder Vue nutzen. Das obige Beispiel zeigt Vanilla JavaScript, um die Islands-Idee zu verdeutlichen.</p>
<h2>Direkter Vergleich: Die wichtigsten Unterschiede</h2>
<p>Hier ist eine Übersicht, die dir bei der Entscheidung hilft:</p>
<table>
<thead>
<tr>
<th>Kriterium</th>
<th>React</th>
<th>Svelte</th>
<th>Astro</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Lernkurve</strong></td>
<td>Mittel</td>
<td>Niedrig</td>
<td>Mittel</td>
</tr>
<tr>
<td><strong>Performance</strong></td>
<td>Gut</td>
<td>Sehr gut</td>
<td>Hervorragend</td>
</tr>
<tr>
<td><strong>Bundle Size</strong></td>
<td>~40-50 KB (gzipped)</td>
<td>~2-3 KB (gzipped)</td>
<td>0 KB (Standard)*</td>
</tr>
<tr>
<td><strong>Ökosystem</strong></td>
<td>Riesig</td>
<td>Wachsend</td>
<td>Klein, aber wachsend</td>
</tr>
<tr>
<td><strong>Job-Markt</strong></td>
<td>Sehr groß</td>
<td>Klein, aber wachsend</td>
<td>Sehr klein</td>
</tr>
<tr>
<td><strong>Beste für</strong></td>
<td>SPAs, komplexe Apps</td>
<td>Moderne Web-Apps</td>
<td>Content-Sites, Blogs</td>
</tr>
<tr>
<td><strong>Community</strong></td>
<td>Sehr groß</td>
<td>Groß</td>
<td>Mittel</td>
</tr>
<tr>
<td><strong>Learning Resources</strong></td>
<td>Sehr viele</td>
<td>Viele</td>
<td>Wachsend</td>
</tr>
<tr>
<td><strong>Entwicklungsgeschwindigkeit</strong></td>
<td>Gut</td>
<td>Sehr gut</td>
<td>Gut</td>
</tr>
<tr>
<td><strong>Reaktivität</strong></td>
<td>Hooks</td>
<td>Eingebaut</td>
<td>Manuell (oder via Framework-Integration)</td>
</tr>
</tbody>
</table>
<p><em>*Astro liefert standardmäßig kein JavaScript aus. JavaScript wird nur für interaktive &quot;Islands&quot; geladen.</em></p>
<h3>Bundle Size erklärt</h3>
<p>Die Zahlen bedeuten, wie viel Framework-Code im Browser geladen werden muss:</p>
<ul>
<li><strong>React:</strong> ~42 KB (React + ReactDOM, gzipped)</li>
<li><strong>Svelte:</strong> ~2 KB – fast nichts, da Svelte beim Build kompiliert wird</li>
<li><strong>Astro:</strong> 0 KB – statische Sites brauchen kein Framework im Browser</li>
</ul>
<p>Das ist besonders wichtig für:</p>
<ul>
<li>Mobile Nutzer mit langsamen Verbindungen</li>
<li>SEO (Google bevorzugt schnelle Websites)</li>
<li>User Experience (schnellere Ladezeiten)</li>
</ul>
<h2>Welches Framework für wen?</h2>
<p>Hier sind meine Empfehlungen, basierend auf verschiedenen Szenarien:</p>
<h3>Wähle React, wenn du...</h3>
<p><span class="dashicons dashicons-saved green"></span> <strong>Jobs als Frontend-Entwickler suchst:</strong> React ist mit Abstand am gefragtesten<br />
<span class="dashicons dashicons-saved green"></span> <strong>Ein großes Ökosystem brauchst:</strong> Für jedes Problem gibt es eine React-Library<br />
<span class="dashicons dashicons-saved green"></span> <strong>Komplexe Single-Page Applications (SPAs) baust:</strong> React ist perfekt für Web-Apps wie Dashboards, SaaS-Tools<br />
<span class="dashicons dashicons-saved green"></span> <strong>Mit einem etablierten, ausgereiften Tool arbeiten möchtest:</strong> React ist &quot;battle-tested&quot;<br />
<span class="dashicons dashicons-saved green"></span> <strong>Teil einer großen Community sein willst:</strong> Hilfe ist überall verfügbar</p>
<p><span class="dashicons dashicons-no red"></span> <strong>Nicht ideal für:</strong> Einfache Websites, Performance-kritische Content-Sites</p>
<h3>Wähle Svelte, wenn du...</h3>
<p><span class="dashicons dashicons-saved green"></span> <strong>Einfache, intuitive Syntax magst:</strong> Svelte ist am schnellsten zu lernen<br />
<span class="dashicons dashicons-saved green"></span> <strong>Performance wichtig ist:</strong> Svelte ist extrem schnell<br />
<span class="dashicons dashicons-saved green"></span> <strong>Weniger Code schreiben willst:</strong> Svelte ist prägnant und ausdrucksstark<br />
<span class="dashicons dashicons-saved green"></span> <strong>Moderne Entwicklung magst:</strong> Svelte fühlt sich &quot;2025&quot; an<br />
<span class="dashicons dashicons-saved green"></span> <strong>Kleinere bis mittlere Web-Apps baust:</strong> Svelte glänzt hier</p>
<p><span class="dashicons dashicons-no red"></span> <strong>Nicht ideal für:</strong> Wenn du sofort einen Job brauchst (noch nicht so verbreitet)</p>
<h3>Wähle Astro, wenn du...</h3>
<p><span class="dashicons dashicons-saved green"></span> <strong>Content-Sites baust:</strong> Blogs, Marketing-Websites, Dokumentationen, Portfolios<br />
<span class="dashicons dashicons-saved green"></span> <strong>Performance oberste Priorität ist:</strong> Astro ist unschlagbar schnell<br />
<span class="dashicons dashicons-saved green"></span> <strong>Wenig JavaScript brauchst:</strong> Die meiste Site ist statisch<br />
<span class="dashicons dashicons-saved green"></span> <strong>SEO wichtig ist:</strong> Statische Sites sind perfekt für Suchmaschinen<br />
<span class="dashicons dashicons-saved green"></span> <strong>Flexibilität willst:</strong> Du kannst React, Svelte, Vue mischen</p>
<p><span class="dashicons dashicons-no red"></span> <strong>Nicht ideal für:</strong> Hochinteraktive SPAs, Echtzeit-Apps, komplexe Web-Apps</p>
<h3>Mein persönlicher Tipp für Einsteiger</h3>
<p>Wenn du gerade erst anfängst, würde ich <strong>in dieser Reihenfolge</strong> lernen:</p>
<ol>
<li><strong>Starte mit Svelte</strong> – Die niedrigste Lernkurve, und du verstehst Grundkonzepte schnell</li>
<li><strong>Lerne dann React</strong> – Für Job-Chancen und das große Ökosystem</li>
<li><strong>Entdecke Astro</strong> – Wenn du Content-Sites bauen willst</li>
</ol>
<p>Du musst nicht alle drei können! Wähle eines und werde richtig gut darin.</p>
<h2>Erste Schritte: So startest du</h2>
<p>Hier ist, wie du mit jedem Framework ein erstes Projekt erstellst:</p>
<h3>React: Projekt starten</h3>
<pre><code class="language-bash"># Mit Vite (empfohlen – schneller als Create React App)
npm create vite@latest mein-react-projekt -- --template react

# In das Projektverzeichnis wechseln
cd mein-react-projekt

# Dependencies installieren
npm install

# Entwicklungsserver starten
npm run dev</code></pre>
<p>Der Dev-Server läuft dann auf <code>http://localhost:5173</code></p>
<h3>Svelte: Projekt starten</h3>
<pre><code class="language-bash"># Mit SvelteKit (empfohlen)
npm create svelte@latest mein-svelte-projekt

# Wähle: Skeleton project ? Yes, using TypeScript syntax (optional) ? Add Prettier

# In das Projektverzeichnis wechseln
cd mein-svelte-projekt

# Dependencies installieren
npm install

# Entwicklungsserver starten
npm run dev</code></pre>
<p>Der Dev-Server läuft dann auf <code>http://localhost:5173</code></p>
<h3>Astro: Projekt starten</h3>
<pre><code class="language-bash"># Mit dem Astro CLI
npm create astro@latest mein-astro-projekt

# Wähle: Empty ? Yes (TypeScript optional) ? Install dependencies: Yes

# In das Projektverzeichnis wechseln
cd mein-astro-projekt

# Entwicklungsserver starten
npm run dev</code></pre>
<p>Der Dev-Server läuft dann auf <code>http://localhost:4321</code></p>
<h3>VS Code Extensions</h3>
<p>Installiere diese Extensions für bessere Developer Experience:</p>
<p><strong>Für React:</strong></p>
<ul>
<li>ES7+ React/Redux/React-Native snippets</li>
<li>Prettier – Code formatter</li>
</ul>
<p><strong>Für Svelte:</strong></p>
<ul>
<li>Svelte for VS Code (offiziell)</li>
<li>Svelte Intellisense</li>
</ul>
<p><strong>Für Astro:</strong></p>
<ul>
<li>Astro (offiziell)</li>
<li>Prettier (mit Astro Plugin)</li>
</ul>
<h2>Browser-Kompatibilität &amp; Voraussetzungen</h2>
<h3>Node.js Version</h3>
<p>Alle drei Frameworks benötigen Node.js:</p>
<ul>
<li><strong>Minimal:</strong> Node.js 18.x</li>
<li><strong>Empfohlen:</strong> Node.js 20.x oder 22.x (LTS)</li>
</ul>
<p>Download: <a href="https://nodejs.org/">nodejs.org</a></p>
<h3>Browser-Support</h3>
<p><strong>React:</strong></p>
<ul>
<li>Alle modernen Browser (Chrome, Firefox, Safari, Edge)</li>
<li>Internet Explorer 11 wird nicht mehr unterstützt</li>
</ul>
<p><strong>Svelte:</strong></p>
<ul>
<li>Alle modernen Browser</li>
<li>Sehr guter Support bis zurück zu Safari 12</li>
</ul>
<p><strong>Astro:</strong></p>
<ul>
<li>Alle modernen Browser</li>
<li>Da Astro statisches HTML generiert, funktioniert die Basis-Site sogar ohne JavaScript</li>
</ul>
<h3>Empfohlene Entwicklungsumgebung</h3>
<ul>
<li><strong>Editor:</strong> VS Code, WebStorm oder Cursor</li>
<li><strong>Terminal:</strong> Integriertes Terminal oder iTerm2 (Mac) / Windows Terminal</li>
<li><strong>Browser:</strong> Chrome oder Firefox (mit DevTools)</li>
<li><strong>Git:</strong> Für Versionskontrolle</li>
</ul>
<h2>Zusammenfassung</h2>
<p>Hier sind die wichtigsten Punkte im Überblick:</p>
<ul>
<li><strong>Es gibt kein &quot;bestes&quot; Framework</strong> – die richtige Wahl hängt von deinem Use Case ab</li>
<li><strong>React</strong> ist der etablierte Standard mit riesigem Ökosystem und den meisten Job-Möglichkeiten</li>
<li><strong>Svelte</strong> ist modern, einfach zu lernen und extrem performant – perfekt für Einsteiger</li>
<li><strong>Astro</strong> ist ideal für Content-Sites und bietet unschlagbare Performance durch statische HTML-Generierung</li>
<li><strong>Alle drei sind 2025 gute Wahlen</strong> und werden aktiv weiterentwickelt</li>
<li><strong>Fokussiere dich auf eines</strong> und lerne es richtig, statt alle oberflächlich anzukratzen</li>
<li><strong>Die Grundlagen sind wichtiger als das Framework</strong> – HTML, CSS und JavaScript sind das Fundament</li>
</ul>
<h3>Meine Empfehlung</h3>
<p>Für deinen ersten Einstieg: <strong>Probiere Svelte aus!</strong> Es hat die niedrigste Lernkurve und zeigt dir moderne Konzepte ohne viel Komplexität.</p>
<p>Wenn du als Entwickler arbeiten willst: <strong>Investiere Zeit in React.</strong> Es öffnet die meisten Türen.</p>
<p>Wenn du einen Blog oder eine Marketing-Site baust: <strong>Schau dir Astro an.</strong> Du wirst die Performance lieben.</p>
<h2>Weiterführende Ressourcen</h2>
<h3>Offizielle Dokumentationen</h3>
<ul>
<li><strong>React:</strong> <a href="https://react.dev/">react.dev</a> – Komplett überarbeitete Docs mit interaktiven Tutorials</li>
<li><strong>Svelte:</strong> <a href="https://svelte.dev/tutorial">svelte.dev/tutorial</a> – Interaktives Tutorial direkt im Browser</li>
<li><strong>Astro:</strong> <a href="https://docs.astro.build/">docs.astro.build</a> – Sehr gut strukturierte Dokumentation</li>
</ul>
<h3>Interactive Learning</h3>
<ul>
<li><strong>React:</strong> <a href="https://react.dev/learn">react.dev/learn</a> – Offizieller Learn-Path</li>
<li><strong>Svelte:</strong> <a href="https://learn.svelte.dev/">learn.svelte.dev</a> – Interaktives Tutorial</li>
<li><strong>Astro:</strong> <a href="https://astro.new/">astro.new</a> – Starter Templates zum Ausprobieren</li>
</ul>
<h3>Community &amp; News</h3>
<ul>
<li><strong>State of JavaScript Survey:</strong> <a href="https://stateofjs.com/">stateofjs.com</a> – Jährliche Entwickler-Umfrage</li>
<li><strong>JavaScript Weekly:</strong> <a href="https://javascriptweekly.com/">javascriptweekly.com</a> – Newsletter mit News</li>
<li><strong>Dev.to:</strong> Viele Tutorials und Erfahrungsberichte zu allen drei Frameworks</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/react-svelte-oder-astro-ein-leitfaden-fuer-einsteiger/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Eine smarte ZSH-Funktion: cat für Dateien und URLs</title>
		<link>https://webmatze.de/eine-smarte-zsh-funktion-cat-fuer-dateien-und-urls/</link>
					<comments>https://webmatze.de/eine-smarte-zsh-funktion-cat-fuer-dateien-und-urls/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Thu, 07 Nov 2024 13:16:42 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[HTML / XHTML]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1072</guid>

					<description><![CDATA[Als Unix/Linux-Nutzer kennen wir alle den beliebten 'cat' Befehl, um Dateiinhalte anzuzeigen. Aber was, wenn wir den gleichen, vertrauten Befehl auch für URLs verwenden möchten? Hier ist eine elegante Lösung in Form einer ZSH-Funktion. Die folgende Funktion erweitert den klassischen 'cat' Befehl so, dass er sowohl mit lokalen Dateien als auch mit URLs umgehen kann: [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Als Unix/Linux-Nutzer kennen wir alle den beliebten 'cat' Befehl, um Dateiinhalte anzuzeigen. Aber was, wenn wir den gleichen, vertrauten Befehl auch für URLs verwenden möchten? Hier ist eine elegante Lösung in Form einer ZSH-Funktion.</p>
<p>Die folgende Funktion erweitert den klassischen 'cat' Befehl so, dass er sowohl mit lokalen Dateien als auch mit URLs umgehen kann:</p>
<p><script src="https://gist.github.com/webmatze/64e94b1df232febed51a36d2932f4807.js"></script></p>
<p>Die Funktion prüft mittels eines regulären Ausdrucks, ob das übergebene Argument eine URL ist. Falls ja, wird <strong>curl</strong> verwendet, um den Inhalt herunterzuladen und anzuzeigen. Bei normalen Dateien wird der Standard-<strong>cat</strong>-Befehl verwendet.</p>
<p>Beispiele für die Verwendung:</p>
<ul>
<li><code>cat datei.txt</code> -&gt; Zeigt den Inhalt einer lokalen Datei</li>
<li><code>cat https://github.com/beispiel</code> -&gt; Zeigt den HTML-Inhalt der Website</li>
</ul>
<p>Die Funktion könnt ihr einfach in eure <code>~/.zshrc</code> einfügen und nach dem Neuladen der Shell sofort nutzen.</p>
<p>Ein kleiner, aber feiner Hack, der den Workflow beim Arbeiten mit Dateien und Webinhalten vereinfacht. Das ist Unix-Philosophie at its best: Bestehende Werkzeuge erweitern, ohne ihre ursprüngliche Funktionalität zu beeinträchtigen.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/eine-smarte-zsh-funktion-cat-fuer-dateien-und-urls/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mein neues Zufalls-&#8222;Dad Joke&#8220; Ruby Gem: ICanHazDadJoke</title>
		<link>https://webmatze.de/mein-neues-zufalls-dad-joke-ruby-gem-icanhazdadjoke/</link>
					<comments>https://webmatze.de/mein-neues-zufalls-dad-joke-ruby-gem-icanhazdadjoke/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Tue, 14 May 2024 23:07:04 +0000</pubDate>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[dad jokes]]></category>
		<category><![CDATA[ruby gem]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1040</guid>

					<description><![CDATA[Hallo zusammen! Ich freue mich, euch heute mein neuestes Ruby Gem vorzustellen: icanhazdadjoke. Wie der Name schon verrät, ermöglicht dieses Gem euch, auf einfache Weise zufällige "Dad Jokes" aus der icanhazdadjoke API abzurufen. Wer liebt nicht einen guten, alten, schlechten Witz? Was ist ein "Dad Joke"? "Dad Jokes" sind einfache, oft vorhersehbare und klischeehafte Witze, [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Hallo zusammen!</p>



<p>Ich freue mich, euch heute mein neuestes Ruby Gem vorzustellen: <code>icanhazdadjoke</code>. Wie der Name schon verrät, ermöglicht dieses Gem euch, auf einfache Weise zufällige "Dad Jokes" aus der <a title="icanhazdadjoke API" href="https://icanhazdadjoke.com/api">icanhazdadjoke API</a> abzurufen. Wer liebt nicht einen guten, alten, schlechten Witz?</p>



<h2 class="wp-block-heading">Was ist ein "Dad Joke"?</h2>



<p>"Dad Jokes" sind einfache, oft vorhersehbare und klischeehafte Witze, die typischerweise von Vätern erzählt werden. Sie zeichnen sich durch ihre Wortspiele und ihren harmlosen Humor aus. Beispiele gefällig? Hier sind ein paar Klassiker:</p>



<ul class="wp-block-list">
<li>Warum können Geister so schlecht lügen? Weil man sie durchschauen kann!</li>



<li>Wie nennt man einen Bumerang, der nicht zurückkommt? Einen Stock!</li>
</ul>



<p>Ha! Ich weiß.</p>



<h2 class="wp-block-heading">Funktionen des Gems</h2>



<p>Mit <code>icanhazdadjoke</code> könnt ihr ganz einfach zufällige Witze abrufen und in euren Ruby-Anwendungen verwenden oder direkt im Terminal aufrufen. Das Gem ist leicht zu installieren und noch einfacher zu benutzen. Hier sind einige der Hauptfunktionen:</p>



<ul class="wp-block-list">
<li><strong>Zufällige Witze abrufen</strong>: Holt euch einen zufälligen Witz mit einer einzigen Zeile Code.</li>



<li><strong>Leicht erweiterbar</strong>: Das Gem ist so konzipiert, dass es leicht erweitert und in bestehende Projekte integriert werden kann.</li>
</ul>



<h2 class="wp-block-heading">Installation</h2>



<p>Die Installation des Gems ist einfach und dauert nur einen Moment. Führt einfach folgenden Befehl in eurem Terminal aus:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">gem install icanhazdadjoke</code></pre>



<h2 class="wp-block-heading">Nutzung</h2>



<p>Nachdem ihr das Gem installiert habt, könnt ihr es in eurem Projekt verwenden. Hier ist ein kurzes Beispiel, wie ihr einen zufälligen Witz abrufen könnt:</p>



<pre class="wp-block-code"><code lang="ruby" class="language-ruby">require 'icanhazdadjoke'

joke = ICanHazDadJoke.fetch_joke
puts joke</code></pre>



<p>So einfach ist das! Ihr könnt den abgerufenen Witz dann überall in eurem Code verwenden, wo ihr ihn braucht.</p>



<h2 class="wp-block-heading">Beispielprojekt</h2>



<p>Hier ist ein kleines Beispielprojekt, das zeigt, wie man das Gem in einer einfachen Ruby-Anwendung verwenden kann:</p>



<pre class="wp-block-code"><code lang="ruby" class="language-ruby">require 'sinatra'
require 'icanhazdadjoke'

get <span class="hljs-string">'/'</span> <span class="hljs-keyword">do</span>
  joke = <span class="hljs-title class_">ICanHazDadJoke</span>.fetch_joke
  <span class="hljs-string">"&lt;h1>Dad Joke&lt;/h1>&lt;p><span class="hljs-subst">#{joke}</span>&lt;/p>"</span>
<span class="hljs-keyword">end</span>
</code></pre>



<p>Dieses einfache Sinatra-Projekt erstellt einen kleinen Webserver, der bei jedem Aufruf der Startseite einen neuen Witz anzeigt.</p>



<h2 class="wp-block-heading">Fazit</h2>



<p>Ich hoffe, euch gefällt mein neues Gem <code>icanhazdadjoke</code>. Es war ein lustiges Projekt und ich freue mich darauf zu sehen, wie ihr es in euren eigenen Projekten verwendet. Wenn ihr Fragen oder Anregungen habt, lasst es mich gerne wissen. Den Code und weitere Informationen findet ihr auf <a href="https://github.com/webmatze/icanhazdadjoke">GitHub</a>.</p>



<p>Viel Spaß beim Programmieren und vergesst nicht, hin und wieder einen guten "Dad Joke" zu erzählen!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/mein-neues-zufalls-dad-joke-ruby-gem-icanhazdadjoke/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Scripte regelmäßig per cronjob ausführen lassen</title>
		<link>https://webmatze.de/php-scripte-regelmasig-per-cronjob-ausfuhren-lassen/</link>
					<comments>https://webmatze.de/php-scripte-regelmasig-per-cronjob-ausfuhren-lassen/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Tue, 12 Jun 2012 15:46:26 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[cronjobs]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=727</guid>

					<description><![CDATA[Heute möchte ich euch kurz zeigen, wie man PHP Scripte, die über einen Browser ausgeführt werden müssen, auch per Cronjob z.B. täglich ausführen lassen kann. Nehmen wir an, wir haben ein PHP Script, dass über die folgende URL aufgerufen wird. Nehmen wir beispielsweise an, dieses Script leert den Cache unserer Anwendung. Die URL für das [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Heute möchte ich euch kurz zeigen, wie man PHP Scripte, die über einen Browser ausgeführt werden müssen, auch per Cronjob z.B. täglich ausführen lassen kann.</p>
<p>Nehmen wir an, wir haben ein PHP Script, dass über die folgende URL aufgerufen wird. Nehmen wir beispielsweise an, dieses Script leert den Cache unserer Anwendung. Die URL für das Script könnte demnach folgendermaßen aussehen:</p>
<pre>http://www.meineseite.de/scripte/delete_cache.php</pre>
<p>Um also den Cache zu leeren, können wir diese URL einfach im Browser aufrufen. Doch was machen wir, wenn wir das Script nun jeden Tag um 12:00 Uhr ausführen wollen?</p>
<h2>Den Cron-Daemon nutzen</h2>
<p>Wenn wir das Glück haben, per SSH auf den Server zugreifen zu können und möglicherweise sogar noch root Rechte besitzen, können wir hierfür einen Cronjob für den Cron-Daemon erstellen. Was genau der Cron-Daemon ist, erfahrt ihr hier:</p>
<blockquote><p>"Der Cron-Daemon ist ein Dienst, der automatisch Skripte und Programme zu vorgegebenen Zeiten, also chronologisch, starten kann. Der auszuführende Befehl wird in einer Tabelle, der <strong>crontab</strong>, gespeichert. Es gibt systemweite Crontabs, die nur mit root-Rechten bearbeitet werden können, zusätzlich kann jeder Benutzer seine eigene Crontab erstellen."<br />
<em>Source: <a href="http://wiki.ubuntuusers.de/Cron">http://wiki.ubuntuusers.de/Cron</a></em></p></blockquote>
<p>Das bedeutet also, dass wir die Tabelle mit den Cronjob Befehlen bearbeiten müssen und unser Script dort eintragen. Unter Ubuntu funktioniert dies so.</p>
<pre>crontab -e</pre>
<p><span id="more-727"></span>Wenn wir diesen Befehl auf der Kommandozeile ausführen, gelangen wir in einen Editor, wo uns alle bisher vorhanden Cronjobs des aktuellen Nutzers angezeigt werden, sofern welche vorhanden sind. Bei mir unter Ubuntu sieht dies z.B. so aus:</p>
<pre># Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command</pre>
<p>Wir können also genau festlegen, welcher Befehl zu welchen Zeitpunkt bzw. in welchen Intervall ausgeführt werden soll. Da wir wollen, dass unser Befehl den Cache zu leeren jeden Tag um 12 Uhr ausgeführt werden soll, müssen wir also folgende Zeile am Ende der Datei einfügen:</p>
<pre>0 12 * * * /usr/bin/wget -q -O /var/log/delete_cache.log http://www.meineseite.de/scripte/delete_cache.php</pre>
<p>Wie ihr sehen könnt, verwende ich einfach das Linux Programm <strong>wget</strong>, um die Seite zu laden und gleichzeitig die Ausgabe des Scriptes in eine Log-Datei zu schreiben, welche über den Parameter<strong> -O</strong> angegeben werden kann.</p>
<p>Weitere Möglichkeiten das Script auszuführen, wären <strong>Curl</strong> oder <strong>Lynx</strong>:</p>
<pre>#mit curl
0 12 * * * /usr/bin/curl -o /var/log/delete_cache.log http://www.meineseite.de/scripte/delete_cache.php
#oder mit lynx
0 12 * * * /usr/bin/curl -dump http://www.meineseite.de/scripte/delete_cache.php</pre>
<h2>Mehrere Parameter übergeben</h2>
<p>Eine Besonderheit die ihr noch beachten solltet sind Parameter, die mit der URL zum Script mitgeschickt werden sollen. Nehmen wir an, wir haben zwei Parameter um den Cache für Artikel und das Gästebuch zu löschen.</p>
<pre>http://www.meineseite.de/scripte/delete_cache.php?articles=true&amp;guestbook=true</pre>
<p>Dann müssen wir in diesem Falle die URL in der Cron-Tabelle in Anführungszeichen schreiben, oder das &amp; Zeichen mit \ escapen:</p>
<pre>0 12 * * * /usr/bin/wget -q -O /var/log/delete_cache.log "http://www.meineseite.de/scripte/delete_cache.php?articles=true&amp;guestbook=true"
0 12 * * * /usr/bin/wget -q -O /var/log/delete_cache.log http://www.meineseite.de/scripte/delete_cache.php?articles=true\&amp;guestbook=true</pre>
<p>So, damit sollte es nun kein Problem mehr sein, euch täglich die Arbeit zu sparen um bestimmte URLs aufrufen zu müssen.</p>
<p>Im Allgemeinen sollte es jedoch immer besser sein, solche Script so zu programmieren, so dass sie direkt von der Kommandozeile ausgeführt werden können und nicht per Browser über eine URL aufgerufen werden. Die könnte schließlich auch ein Sicherheitsproblem sein.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/php-scripte-regelmasig-per-cronjob-ausfuhren-lassen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ruby on Rails auf Ubuntu 11.10 installieren</title>
		<link>https://webmatze.de/ruby-on-rails-auf-ubuntu-11-10-installieren/</link>
					<comments>https://webmatze.de/ruby-on-rails-auf-ubuntu-11-10-installieren/#comments</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Sat, 15 Oct 2011 14:17:06 +0000</pubDate>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=689</guid>

					<description><![CDATA[Seit einigen Tagen (Oktober 2011) ist die neuste Version von Ubuntu zum Download freigegeben. Ubuntu 11.10 oder auch Oneiric Ocelot genannt, ist ein wirklich ausgereiftes und leicht zu bedienendes Linux Betriebssystem, welches kostenlos zum Download bereit steht. Da Linux schon von Natur aus viele Funktionen und Helfer für Programmierer bereit hält, eignet es sich auch [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Seit einigen Tagen (Oktober 2011) ist die neuste Version von <a title="Ubuntu Linux" href="http://www.ubuntu.com/">Ubuntu</a> zum <a title="Download Ubuntu hier" href="http://www.ubuntu.com/download">Download</a> freigegeben. <strong>Ubuntu 11.10</strong> oder auch <strong>Oneiric Ocelot</strong> genannt, ist ein wirklich ausgereiftes und leicht zu bedienendes Linux Betriebssystem, welches kostenlos zum Download bereit steht. Da Linux schon von Natur aus viele Funktionen und Helfer für Programmierer bereit hält, eignet es sich auch hervorragend zur Entwicklung von <strong>Ruby on Rails</strong> Anwendungen.</p>
<p><a title="Ruby on Rails" href="http://rubyonrails.org/">Ruby on Rails</a> hingegen ist derzeit eines der populärsten und produktivsten OpenSource Frameworks, mit dem sich sehr schnell umfangreiche Webanwendungen erstellen lassen. Wer schon damit gearbeitet hat, weiß wieviel Spaß es machen kann.</p>
<p><a title="Ruby Homepage" href="http://www.ruby-lang.org">Ruby</a> selbst ist eine Scriptsprache, wie z.B. auch PHP eine Scriptsprache ist. Ruby wurde jedoch von Grund auf mit dem Ziel entwickelt, leicht verständlich zu sein und eine schnelle Entwicklung damit zu ermöglichen.</p>
<h3>Ubuntu für Rails vorbereiten</h3>
<p>Damit wir das Rails Framework unter <strong>Ubuntu 11.10</strong> verwenden können, müssen wir zuerst <strong>Ruby</strong> installieren. Von Haus aus ist Ubuntu noch nicht mit Ruby ausgestattet, jedoch läßt sich dieses (und auch andere Programmiersprachen) sehr leicht nachträglich installieren.</p>
<p>Hierzu muss man wissen, dass es verschiedene Versionen von Ruby gibt. Ältere Versionen von Ruby on Rails haben hauptsächlich auf die <strong>Ruby Version 1.8.7</strong> gesetzt. Die neusten <strong>Ruby on Rails Versionen ab 3.0</strong> verwenden jedoch bevorzugt <strong>Ruby Version 1.9.2</strong>. Um unser System also für die Zukunft vorzubereiten, werden wir auf den <a title="Ruby Version Manager Homepage" href="http://beginrescueend.com/">Ruby Versions Manager</a> (RVM) setzen, mit dem es möglich ist jede beliebige Ruby Version zu installieren und auch bequem zwischen diesen zu wechseln.</p>
<p><span id="more-689"></span>Damit wir <strong>RVM</strong> installieren können, müssen wir zuerst ein paar Abhängigkeiten installieren:</p>
<pre>sudo apt-get install git
sudo apt-get install curl</pre>
<p>Git benötigen wir, weil die meisten <strong>Ruby on Rails</strong> Projekte und Erweiterungen ihre Sourcen mit <a title="Git SCM Homepage" href="http://git-scm.com/">Git</a> verwalten. Und curl brauchen wir, um mit dem folgenden Befehl <strong>RVM</strong> zu installieren:</p>
<pre>bash &lt; &lt;(curl -s https://rvm.beginrescueend.com/install/rvm)</pre>
<p>Nachdem dies ausgeführt wurde, bekommen wir noch ein paar Hinweise vom <strong>RVM</strong> Entwickler, wie wir damit umgehen können. Unter anderem wird auch noch darauf hingewiesen, dass folgende Abhängigkeiten installiert werden sollten, bevor man damit beginnen kann, ein oder mehrere Ruby Versionen zu installieren:</p>
<pre>sudo apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion</pre>
<p>Nun sollte man das Terminal einmal schließen und wieder ein neues Terminal öffnen und mit folgenden Befehl überprüfen, ob <strong>RVM</strong> richtig installiert wurde:</p>
<pre>type rvm | head -1</pre>
<p>Wird hier jetzt '<strong>rvm is a function</strong>' zurückgegeben, ist alles in Ordnung und wir können damit beginnen, unsere benötigte Ruby Version zu installieren.</p>
<h3>Ruby 1.9.2 auf Ubuntu installieren</h3>
<p>Abhängig davon, welche Ruby on Rails Version wir nutzen wollen, können wir nun die entsprechende Ruby Version installieren. Dies ist mit dem <strong>RVM</strong> Befehlt mehr als einfach. Gehen wir davon aus, dass wir <strong>Ruby on Rails 3.1</strong> (also die aktuelle Version) installieren wollen, können wir so <strong>Ruby 1.9.2</strong> installieren:</p>
<pre>rvm install 1.9.2</pre>
<p>Nach ein paar Minuten ist dann Ruby installieret. Beachten sollte man hierbei jedoch unbedingt, dass alles was man mit <strong>RVM</strong> macht ohne den <strong>sudo</strong> Befehlt ausgefürt werden muss! Warum dies so ist und weitere wichtige Nutzungshinweise erhaltet ihr in der <a title="RVM Dokumentation" href="http://beginrescueend.com/rvm/basics/"><strong>RVM</strong> Dokumentation</a>.</p>
<p>Um nun die installierte Ruby Version nutzen zu können, muss man sie mit dem folgenden Befehl aktivieren:</p>
<pre>rvm use 1.9.2</pre>
<p>Jetzt kann man überprüfen, welche Ruby Version installiert ist:</p>
<pre>ruby -v</pre>
<h3>Ruby on Rails auf Ubuntu installieren</h3>
<p>Nachdem nun alle notwendigen Vorbereitungen abgeschlossen sind, können wir damit beginnen die aktuelle Rails Version zu installieren. Rails und andere benötigte Packete werden mit dem <a title="RubyGems Package Manager" href="http://en.wikipedia.org/wiki/RubyGems">RubyGems Package Manager</a> installiert. Dieser wurde zum Glück auch schon automatisch von <strong>RVM</strong> mit installiert. Wir brauchen also nur noch folgendes auszuführen:</p>
<pre>gem install rails</pre>
<p>und schon wurde <strong>Ruby on Rails</strong> erfolgreich installiert. Auch hier ist wieder zu beachten, dass alle <strong>RubyGems</strong> ohne den <strong>sudo</strong> Befehl installiert werden.</p>
<h3>JavaScript Runtime installieren</h3>
<p>In der aktuellen <strong>Rails Version 3.1.1</strong> gibt es noch eine weitere Abhängigkeit, welche man beachten muss. Rails benötigt hier nämlich eine im System installierte <strong>JavaScript Runtime</strong> um bestimmte Funktionen ausführen zu können. Am einfachsten und problemlosesten konnte ich dieses Problem bei mir beheben, indem ich <a title="Node.js Homepage" href="http://nodejs.org/">Node.js</a> installiert habe:</p>
<pre>sudo apt-get install nodejs</pre>
<h3>Ruby on Rails testen</h3>
<p>Jetzt wo alles installiert ist, können wir schnell noch ein Testprojekt mit Rails erstellen:</p>
<pre>rails new test_app
cd test_app
rails server</pre>
<p>und uns dies im Browser aufrufen:</p>
<pre>http://localhost:3000</pre>
<p>Fertig ist unsere <strong>Ruby on Rails installation auf Ubuntu</strong>.</p>
<p>Die hier genannten Installationsschritte mögen sich mit denen auf anderen Ubuntu Versionen oder anderen Linux System unterscheiden.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/ruby-on-rails-auf-ubuntu-11-10-installieren/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Schatten mit CSS Pseudo-Elementen hinzufügen</title>
		<link>https://webmatze.de/schatten-mit-css-pseudo-elementen-hinzufugen/</link>
					<comments>https://webmatze.de/schatten-mit-css-pseudo-elementen-hinzufugen/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Tue, 02 Aug 2011 21:05:04 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML / XHTML]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[pseudo elements]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=665</guid>

					<description><![CDATA[In diesem Beitrag möchte ich euch zeigen, wie man mit ganz einfachen Mitteln einen Schatteneffekt an beliebige Block-Elemente hinzufügen kann. Der Clou dabei ist jedoch, dass dafür kein HTML angefasst werden muss, sondern dies ausschließlich über normale CSS Mittel funktioniert. Ja klar, werden jetzt einige rufen. Dafür kann man ja einfach die CSS Deklaration box-shadow verwenden! Richtig. [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><strong>In diesem Beitrag möchte ich euch zeigen, wie man mit ganz einfachen Mitteln einen Schatteneffekt an beliebige Block-Elemente hinzufügen kann. Der Clou dabei ist jedoch, dass dafür kein HTML angefasst werden muss, sondern dies ausschließlich über normale CSS Mittel funktioniert.</strong></p>
<p>Ja klar, werden jetzt einige rufen. Dafür kann man ja einfach die CSS Deklaration <a title="CSS3 box-shadow" href="http://www.w3.org/TR/css3-background/#the-box-shadow">box-shadow</a> verwenden! Richtig. CSS3 erlaubt es seit einiger Zeit, mit <strong>box-shadow</strong> schöne Schatteneffekte an beliebige HTML Elemente zu zaubern und ist dabei ziemlich flexibel.</p>
<p>Aber leider ist es wie mit vielen neuen Dingen - einige Browser unterstützen es noch nicht, oder nur mit speziellen Browserprefixen wie <strong>-moz-</strong>, <strong>-o-</strong> oder<strong> -webkit-</strong>. Dies bedeutet, man muss diese Deklaration für jeden Browser in seiner speziellen Schreibweise wiederholen und bläst damit sein Stylesheet unnötig auf. Und selbst dann werden ältere Browser wie IE8 (und niedriger) oder Opera 10.10 (oder niedriger) <a href="http://www.quirksmode.org/css/contents.html#link5">nicht unterstützt</a>.</p>
<p>Aus diesem Grund möchte ich euch eine weitere Möglichkeit vorstellen, wie man noch einen Schatten erstellen kann.</p>
<h3>Gestatten, Pseudo-Elemente :before und :after</h3>
<p>CSS2 bietet schon seit einiger Zeit die Möglichkeit mit Hilfe der <a href="http://www.w3.org/TR/CSS2/selector.html#before-and-after">Pseudo-Elemente :before und :after</a> Inhalte vor bzw. nach Elementen per CSS hinzuzufügen. So lässt sich zum Beispiel mit folgender Anweisung vor allen Link-Elementen ein Text hinzufügen:</p>
<pre lang="css">a:before {content: "Ich bin ein Link: "}</pre>
<p>Und das alles, ohne etwas am HTML selbst ändern zu müssen. Und ein weiterer Vorteil für uns ist, dass diese Pseudo-Elemente auch schon von einigen älteren Browserversionen unterstützt werden und zudem noch ohne spezielle Prefixe auskommen.</p>
<p><span id="more-665"></span></p>
<h3>Wie entsteht nun der Schatteneffekt?</h3>
<p>Wir nutzen den Umstand, dass der so hinzugefügte Text vom Browser als Block-Element behandelt wird, wenn dieser auch auf ein Block-Element angewandt wird. Er verhält sich also genau so, als wenn wir ein neues DIV-Element hinzugefügt hätten.</p>
<p>Wenn wir dieses Element nun noch mit einer Hintergrundfarbe versehen und entsprechend positionieren, können wir so einen passablen Schatteneffekt erzeugen. Wie genau dies aussieht, könnt ihr nun (endlich) hier sehen:</p>
<div id="box" class="shadowme">Diese Box hat einen schönen Schatten. Wenn Ihr mit der Maus darüberfahrt, seht ihr auch, dass sich der Schatten der Größe der Box anpasst.</div>
<p>Da habt ihr es. Ist der Schatten nicht schön?</p>
<p>Jetzt werde ich euch noch schnell den dazu notwendigen Code zeigen und schon steht euch nichts mehr im Wege, um solche Schatten selbst in euren Seiten zu verwenden.</p>
<p>Hier das HTML:</p>
<pre lang="html4strict">
<div id="box" class="shadowme">Diese Box hat einen schönen Schatten. 
Wenn Ihr mit der Maus darüberfahrt, seht ihr auch, 
dass sich der Schatten der Größe der Box anpasst.</div>
</pre>
<p>Und hier das entsprechende CSS:</p>
<pre lang="css">      
#box {
  background-color: green;
  position: relative;
  width: 50%;
  text-align: center;
  margin: 0px auto;
  padding: 10px;
  color: #fff;
  font-family: sans-serif;
  margin-bottom: 20px;
}
#box:hover {
  font-size: 150%;
}
.shadowme:after {
  content: "";
  background-color: lightGrey;
  position: absolute;
  bottom: -5px;
  right: -5px;
  width: 100%;
  height: 5px;
}
.shadowme:before {
  content: "";
  background-color: lightGrey;
  position: absolute;
  top: 5px;
  right: -5px;
  width: 5px;
  height: 100%;
}</pre>
<p>Ich wünsche Euch viel Spaß damit und bei Fragen oder Hinweisen könnt ihr gerne die Kommentarfunktion verwenden. :)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/schatten-mit-css-pseudo-elementen-hinzufugen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Kittyfier Bookmarklet &#8211; Süße Kätzchen überall</title>
		<link>https://webmatze.de/kittyfier-bookmarklet-suse-katzchen-uberall/</link>
					<comments>https://webmatze.de/kittyfier-bookmarklet-suse-katzchen-uberall/#comments</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Thu, 21 Apr 2011 17:10:34 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmierung]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=652</guid>

					<description><![CDATA[Was passiert, wenn man sich als Programmierer mal etwas vom normalen Arbeitsstress ablenken will? Man programmiert etwas, dass Spaß macht. Auf genau diese Weise ist auch dieses Bookmarklet entstanden. Es wandelt einfach alle per IMG HTML-Tag eingebundene Bilder einer Seite in Bilder mit niedlichen kleinen Katzen um. Um zu sehen, was es macht, einfach den [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><strong>Was passiert, wenn man sich als Programmierer mal etwas vom normalen Arbeitsstress ablenken will? Man programmiert etwas, dass Spaß macht.</strong></p>
<p>Auf genau diese Weise ist auch dieses <a href="http://en.wikipedia.org/wiki/Bookmarklet">Bookmarklet</a> entstanden. Es wandelt einfach alle per <strong>IMG</strong> HTML-Tag eingebundene Bilder einer Seite in Bilder mit niedlichen kleinen Katzen um. Um zu sehen, was es macht, einfach den folgenden Link in die Bookmark-Leiste eures Browsers ziehen und auf einer beliebigen Seite mit Bildern ausprobieren!</p>
<div style="text-align: center;margin-bottom:10px;"><a style="border: 1px solid #666666; background-color: #e4e4e4; padding: 10px; line-height: 30px;font-weight:bold;" href="javascript:(function(){var%20images=document.images;var%20icount=0;for(var%20i=0;i%20%3C%20images.length;i++){var%20img=images.item(i);if(img.width%20%3E%2020%20%26%26%20img.height%20%3E%2020){img.src=%22http://placekitten.com/%22+img.width+%22/%22+img.height+%22%3Fimage=%22+icount;icount++;if(icount%20%3E%2016){icount=0;}}}})();">Kittyfier Bookmarklet</a></div>
<p>Wer es gleich hier auf dieser Seite testen möchte, kann auch einfach nur den Link anklicken.</p>
<p>Hier sind ein paar zufällige Bilder zum Testen.</p>
<p><img decoding="async" style="float: left; margin-right: 10px; margin-bottom: 10px;" src="http://farm5.static.flickr.com/4090/5006132868_50980e8b91_m.jpg" alt="Flowers" width="200" /><img decoding="async" style="float: left; margin-right: 10px; margin-bottom: 10px;" src="http://farm5.static.flickr.com/4087/5101125270_77244f7c0f_m.jpg" alt="flowers and bokeh" width="200" /><img decoding="async" style="float: left; margin-right: 10px; margin-bottom: 10px;" src="http://farm3.static.flickr.com/2504/3757385629_b58653d59b_m.jpg" alt="Macro flower experiment" width="200" /><img decoding="async" style="float: left; margin-right: 10px; margin-bottom: 10px;" src="http://farm4.static.flickr.com/3047/3319679433_b4771513a3_m.jpg" width="200" alt="Fire-tree flower oppening, Tulipa africana, bisnagueira (Spathodea campanulata). Ceret park São Paulo Brasil. African native"></p>
<p style="clear: both; margin-top: 10px;">Wer wissen möchte, wie das Kittyfier Bookmarklet funktioniert, kann sich den kompletten Code hier ansehen. Eigentlich nichts besonderes:</p>
<pre lang="javascript">var images = document.images; 
var icount = 0; 
for(var i = 0; i < images.length; i++) { 
var img = images.item(i);
if (img.width > 20 && img.height > 20) { 
img.src = "http://placekitten.com/"+img.width+"/"+img.height+"?image="+icount; 
icount++;if(icount > 16){icount=0;}
} 
}</pre>
<p>Wie man sehen kann, verwende ich den <a href="http://placekitten.com">Placekitten</a> Service, welcher es einem erlaubt durch Angabe von Höhe und Breite beliebig große Katzenbilder zu generieren um diese als Platzhalter in die eigenen Entwürfe einzubauen. Dies ist vor allem dann praktisch, wenn man nur mal schnell ein HTML Layout testen möchte, aber noch keine passenden Bilder hat.</p>
<p>Ich hoffe ihr habt Spaß an diesem kleinen Script und ich freue mich über eure Kommentare.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/kittyfier-bookmarklet-suse-katzchen-uberall/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>A small update to my CSS3 Rotating Image Gallery</title>
		<link>https://webmatze.de/a-small-update-to-my-css3-rotating-image-gallery/</link>
					<comments>https://webmatze.de/a-small-update-to-my-css3-rotating-image-gallery/#comments</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Sat, 18 Dec 2010 16:04:57 +0000</pubDate>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmierung]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=642</guid>

					<description><![CDATA[A couple of months ago I wrote a small image gallery script using some of the new CSS3 functions like transitions and transforms of the Webkit Nightly Browser. It is a simple gallery with only four pictures in it which rotates on mouse click to show the next picture. That thing became a little famous [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>A couple of months ago I wrote <a href="http://webmatze.de/css-3-rotating-image-gallery/">a small image gallery script using some of the new CSS3 functions</a> like transitions and transforms of the <strong>Webkit Nightly Browser</strong>. It is a simple gallery with only four pictures in it which rotates on mouse click to show the next picture.</p>
<p>That thing <strong>became a little famous </strong>and was shown on many other websites as an example to what can be done with <strong>CSS3</strong> and <strong>a modern browser</strong>.</p>
<p>Now, a year later, I updated this script so it works now in many other modern browsers such as Firefox, Chrome, Opera and Internet Explorer.</p>
<p>Just <a href="http://samples.webmatze.de/rotating_image_gallery.htm">browse to the example page</a> and see it for yourself.</p>
<p>And if you have an older browser you can watch this little screen capture:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="344" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="src" value="http://www.youtube.com/v/rPH1G41Nu2U&amp;hl=de&amp;fs=1" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/rPH1G41Nu2U&amp;hl=de&amp;fs=1" allowfullscreen="true"></embed></object></p>
<p>I hope you like it and I am eager to see what you can come up with using those CSS3 transitions and transforms.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/a-small-update-to-my-css3-rotating-image-gallery/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
