<?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>Programmierung &#8211; webmatze.de</title>
	<atom:link href="https://webmatze.de/category/uncategorized/programmierung/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>Dateien lokal von Git ausschließen – ohne .gitignore</title>
		<link>https://webmatze.de/dateien-lokal-von-git-ausschliessen-ohne-gitignore/</link>
					<comments>https://webmatze.de/dateien-lokal-von-git-ausschliessen-ohne-gitignore/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Wed, 14 Jan 2026 19:39:16 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[Programmierung]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1128</guid>

					<description><![CDATA[Manchmal möchte man Dateien von Git ignorieren lassen, die aber nicht in der .gitignore auftauchen sollen. Vielleicht handelt es sich um persönliche Konfigurationsdateien, lokale Notizen oder IDE-spezifische Ordner, die nur dich betreffen und nicht das gesamte Team. Die Lösung: .git/info/exclude Was ist .git/info/exclude? Diese Datei funktioniert genau wie .gitignore, wird aber nicht ins Repository committed [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Manchmal möchte man Dateien von Git ignorieren lassen, die aber nicht in der <code>.gitignore</code> auftauchen sollen. Vielleicht handelt es sich um persönliche Konfigurationsdateien, lokale Notizen oder IDE-spezifische Ordner, die nur dich betreffen und nicht das gesamte Team.</p>
<p>Die Lösung: <code>.git/info/exclude</code></p>
<p><span id="more-1128"></span></p>
<h2>Was ist .git/info/exclude?</h2>
<p>Diese Datei funktioniert genau wie <code>.gitignore</code>, wird aber nicht ins Repository committed und bleibt lokal auf deinem Rechner. Jedes Git-Repository hat diese Datei bereits – du musst sie nur mit Einträgen füllen.</p>
<table>
<thead>
<tr>
<th>Datei</th>
<th>Zweck</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.gitignore</code></td>
<td>Ausschlüsse, die für alle Entwickler gelten (wird committed)</td>
</tr>
<tr>
<td><code>.git/info/exclude</code></td>
<td>Persönliche/lokale Ausschlüsse nur für dich</td>
</tr>
<tr>
<td><code>~/.config/git/ignore</code></td>
<td>Globale Ausschlüsse für alle deine Repositories</td>
</tr>
</tbody>
</table>
<h2>Praktische Git-Aliases für den Alltag</h2>
<p>Um die Arbeit mit lokalen Excludes zu vereinfachen, habe ich mir einige Git-Aliases erstellt, die ich hier teilen möchte.</p>
<h3>1. Dateien zum Exclude hinzufügen</h3>
<pre><code class="language-bash">git config --global alias.exclude &#039;!f() { echo &quot;$1&quot; &gt;&gt; .git/info/exclude; }; f&#039;</code></pre>
<p><strong>Verwendung:</strong></p>
<pre><code class="language-bash">git exclude &quot;meine-lokale-datei.txt&quot;
git exclude &quot;tmp/&quot;
git exclude &quot;*.local&quot;</code></pre>
<h3>2. Alle Exclude-Einträge anzeigen</h3>
<pre><code class="language-bash">git config --global alias.excluded &#039;!grep -v &quot;^#&quot; .git/info/exclude | grep -v &quot;^$&quot;&#039;</code></pre>
<p><strong>Verwendung:</strong></p>
<pre><code class="language-bash">git excluded</code></pre>
<p>Dieser Befehl zeigt alle Einträge aus <code>.git/info/exclude</code> an und filtert dabei Kommentare und Leerzeilen heraus.</p>
<h3>3. Betroffene Dateien auflisten</h3>
<pre><code class="language-bash">git config --global alias.excluded-files &#039;!git ls-files -o --ignored --exclude-from=.git/info/exclude&#039;</code></pre>
<p><strong>Verwendung:</strong></p>
<pre><code class="language-bash">git excluded-files</code></pre>
<p>Zeigt alle ungetrackten Dateien, die durch <code>.git/info/exclude</code> ignoriert werden.</p>
<h3>4. Bonus: Alle ignorierten Dateien anzeigen</h3>
<p>Falls du alle ignorierten Dateien sehen willst – aus <code>.gitignore</code>, <code>.git/info/exclude</code> und der globalen Ignore-Datei kombiniert:</p>
<pre><code class="language-bash">git config --global alias.ignored &#039;!git ls-files -o --ignored --exclude-standard&#039;</code></pre>
<p><strong>Verwendung:</strong></p>
<pre><code class="language-bash">git ignored</code></pre>
<h2>Hinweis zu Git-Aliases mit Argumenten</h2>
<p>Bei der Erstellung des <code>exclude</code>-Aliases bin ich auf eine Besonderheit gestoßen: Git hängt Argumente automatisch ans Ende eines Aliases an. Verwendet man zusätzlich <code>$1</code>, erscheint das Argument doppelt.</p>
<p>Die Lösung ist das Wrapping in eine Shell-Funktion:</p>
<pre><code class="language-bash"># Falsch – Argument erscheint doppelt:
git config --global alias.exclude &#039;!echo &quot;$1&quot; &gt;&gt; .git/info/exclude&#039;

# Richtig – Funktion verhindert das Problem:
git config --global alias.exclude &#039;!f() { echo &quot;$1&quot; &gt;&gt; .git/info/exclude; }; f&#039;</code></pre>
<h2>Fazit</h2>
<p>Mit diesen einfachen Aliases wird die Arbeit mit lokalen Git-Excludes zum Kinderspiel. Du kannst Dateien schnell ausschließen, dir einen Überblick verschaffen und behältst die Kontrolle darüber, was in deinem Repository getrackt wird – ohne die <code>.gitignore</code> für alle anderen zu verändern.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/dateien-lokal-von-git-ausschliessen-ohne-gitignore/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>Git Dashboard: Dein täglicher Überblick über Team-Aktivitäten</title>
		<link>https://webmatze.de/git-dashboard-dein-taeglicher-ueberblick-ueber-team-aktivitaeten/</link>
					<comments>https://webmatze.de/git-dashboard-dein-taeglicher-ueberblick-ueber-team-aktivitaeten/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Fri, 20 Dec 2024 19:11:02 +0000</pubDate>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[tools]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1084</guid>

					<description><![CDATA[Hey Developer-Community! Heute möchte ich euch ein cooles Tool vorstellen, das mir und meinem Team das Leben deutlich einfacher macht: Das Git Dashboard! Was ist das Git Dashboard? Stellt euch vor, ihr kommt morgens ins Büro und wollt schnell wissen: Was habt ihr gestern alles committed? Was haben eure Teammitglieder gemacht? Wer war besonders aktiv? [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Hey Developer-Community!</p>
<p>Heute möchte ich euch ein cooles Tool vorstellen, das mir und meinem Team das Leben deutlich einfacher macht: Das <a href="http://https://github.com/webmatze/git-dashboard" title="Git Dashboard">Git Dashboard</a>!</p>
<h2>Was ist das Git Dashboard?</h2>
<p>Stellt euch vor, ihr kommt morgens ins Büro und wollt schnell wissen:</p>
<ul>
<li>Was habt ihr gestern alles committed?</li>
<li>Was haben eure Teammitglieder gemacht?</li>
<li>Wer war besonders aktiv?</li>
</ul>
<p>Genau dafür haben wir das Git Dashboard entwickelt! Mit einem einzigen Befehl bekommt ihr einen übersichtlichen Report über alle Aktivitäten in eurem Repository.</p>
<h2>Wie funktioniert's?</h2>
<p>Super einfach! Nach der Installation könnt ihr zum Beispiel eingeben:</p>
<pre><code class="language-bash">git dashboard              # Zeigt den Report von gestern
git dashboard -d today     # Zeigt den heutigen Report
git dashboard --date 2024-12-18  # Zeigt den Report für ein bestimmtes Datum</code></pre>
<h2>Was macht es so besonders?</h2>
<ul>
<li>Zeigt eure persönlichen Commits übersichtlich an</li>
<li>Listet die Commits eurer Teammitglieder auf</li>
<li>Erstellt eine Rangliste mit Commit-Zahlen und geänderten Zeilen</li>
<li>Berücksichtigt alle Branches</li>
<li>Flexibel im Datum - heute, gestern oder jedes andere Datum</li>
</ul>
<h2>Perfekt für Daily Standups!</h2>
<p>Keine Panik mehr vor der Frage &quot;Was hast du gestern gemacht?&quot;. Ein schnelles <code>git dashboard</code> und ihr habt alle Infos parat!</p>
<h2>Probiert es aus!</h2>
<p>Das Tool ist Open Source und super einfach zu installieren. Schaut euch das Projekt auf GitHub an und gebt ihm einen Stern, wenn es euch gefällt!</p>
<p>Installation in zwei Schritten:</p>
<pre><code class="language-bash">git clone https://github.com/webmatze/git-dashboard.git
./install.sh</code></pre>
<p>Viel Spaß beim Ausprobieren!</p>
<p>P.S.: Feedback und Contributions sind immer willkommen!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/git-dashboard-dein-taeglicher-ueberblick-ueber-team-aktivitaeten/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Der Reiz und die Gefahren von KI-Werkzeugen in der Entwicklung</title>
		<link>https://webmatze.de/der-reiz-und-die-gefahren-von-ki-werkzeugen-in-der-entwicklung/</link>
					<comments>https://webmatze.de/der-reiz-und-die-gefahren-von-ki-werkzeugen-in-der-entwicklung/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Mon, 22 Jul 2024 18:27:39 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[Programmierung]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1064</guid>

					<description><![CDATA[Im Bereich der Softwareentwicklung ist das Aufkommen von KI-Werkzeugen vergleichbar mit dem Öffnen der Büchse der Pandora – eine Mischung aus grenzenlosem Potenzial und unvorhersehbaren Konsequenzen. Als Entwickler stehen wir an der Schwelle zu einer neuen Ära, in der die Grenzen zwischen menschlicher Kreativität und maschineller Effizienz verschwimmen. Der Reiz von KI-Werkzeugen in der Entwicklung [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Im Bereich der Softwareentwicklung ist das Aufkommen von KI-Werkzeugen vergleichbar mit dem Öffnen der Büchse der Pandora – eine Mischung aus grenzenlosem Potenzial und unvorhersehbaren Konsequenzen. Als Entwickler stehen wir an der Schwelle zu einer neuen Ära, in der die Grenzen zwischen menschlicher Kreativität und maschineller Effizienz verschwimmen. Der Reiz von KI-Werkzeugen in der Entwicklung ist unbestreitbar, aber ebenso die Fallstricke, die sie mit sich bringen. Lasst uns dieses Terrain erkunden und sowohl die strahlenden Aussichten als auch die dunklen Ecken betrachten.</p>
<p><span id="more-1064"></span></p>
<p>Der unmittelbare Reiz von KI-Werkzeugen in der Entwicklung liegt in ihrem Versprechen von Effizienz. Stellen wir uns eine Welt vor, in der die mühsame Arbeit des Programmierens von KI übernommen wird und Entwickler sich auf höherwertige Problemlösungen und kreatives Denken konzentrieren können. Dies ist kein Wunschtraum. KI-Werkzeuge können in Sekundenschnelle große Mengen an Code analysieren und Fehler entdecken, die menschliche Augen Stunden kosten könnten. Sie können Optimierungen vorschlagen, Code umstrukturieren und sogar routinemäßige Funktionen autonom schreiben. Die potenziellen Zeitersparnisse sind enorm und bieten einen Einblick in eine Zukunft, in der Entwicklungszyklen drastisch verkürzt und die Produktivität enorm gesteigert wird.</p>
<p>Diese Effizienz kommt jedoch mit einem Vorbehalt: dem Risiko der Überabhängigkeit. Wenn Entwickler beginnen, sich stärker auf KI für alltägliche Aufgaben zu verlassen, besteht die Gefahr, dass grundlegende Fähigkeiten verkümmern. Programmieren ist ebenso eine Kunst wie eine Wissenschaft und erfordert nicht nur ein Verständnis der Syntax, sondern auch eine tiefgehende Intuition für Problemlösungen. Eine Überabhängigkeit von KI könnte Entwickler zu Aufsehern statt zu Schöpfern machen und möglicherweise die Fähigkeiten abstumpfen, die ihre Expertise definieren.</p>
<p>Neben der Effizienz versprechen KI-Werkzeuge eine Demokratisierung der Entwicklung. Mit der Unterstützung von KI können auch Personen mit begrenzten Programmierkenntnissen funktionale Anwendungen erstellen, was die Eintrittsbarriere für technologische Unternehmungen und Innovationen senkt. Dies könnte zu einer Welle von Kreativität und Vielfalt in der Technologielandschaft führen, da mehr Stimmen die Mittel finden, ihre Ideen zum Leben zu erwecken. Doch diese Demokratisierung ist nicht ohne Schattenseiten. Da die Entwicklung zugänglicher wird, könnte der Markt mit Anwendungen unterschiedlicher Qualität übersättigt werden, was es schwieriger macht, wirklich innovative Ideen hervorzuheben. Zudem könnte die Leichtigkeit der Erstellung ethische Dilemmata aufwerfen, da böswillige Akteure diese Werkzeuge für schädliche Zwecke nutzen.</p>
<p>Die Integration von KI in die Entwicklung wirft auch Fragen zur Arbeitsplatzverdrängung auf. Während KI die Produktivität steigern kann, weckt sie auch Ängste vor der Automatisierung, die menschliche Arbeitsplätze ersetzen könnte. Diese Sorge ist nicht unbegründet; die Geschichte ist voller Beispiele, in denen Technologie bestimmte Fähigkeiten überflüssig gemacht hat. Es ist jedoch auch zu beachten, dass Technologie stets neue Möglichkeiten geschaffen hat, auch wenn sie andere schließt. Die Herausforderung besteht darin, sich an diese Veränderungen anzupassen und sicherzustellen, dass Entwickler die Fähigkeiten und das Wissen haben, um mit KI zu arbeiten, anstatt von ihr verdrängt zu werden.</p>
<p>Schließlich gibt es das Thema Kreativität. KI kann Code schreiben, aber kann sie auch innovieren? Entwicklung bedeutet nicht nur, Lösungen für bekannte Probleme zu finden; es geht darum, Möglichkeiten zu sehen, die noch niemand anderes gesehen hat. Das menschliche Element – unsere Fähigkeit zu träumen, außerhalb der Algorithmen zu denken – ist es, was wahre Innovation antreibt. Während KI-Werkzeuge den kreativen Prozess unterstützen können, können sie nicht den Funken menschlicher Genialität ersetzen.</p>
<p>Zusammenfassend lässt sich sagen, dass die Entwicklungslandschaft zweifellos durch KI-Werkzeuge transformiert wird. Sie bieten eine Welt der Effizienz, Zugänglichkeit und Möglichkeiten, die vor wenigen Jahrzehnten noch unvorstellbar war. Doch während wir diese Werkzeuge annehmen, müssen wir auch die Herausforderungen beachten, die sie mit sich bringen. Das Gleichgewicht zwischen Effizienz und Fähigkeitserwerb, Demokratisierung und Qualitätskontrolle sowie Automatisierung und Arbeitsplatzschaffung wird der Schlüssel sein, um diese neue Ära zu navigieren. Vor allem müssen wir uns daran erinnern, dass KI ein Werkzeug zur Verbesserung der menschlichen Kreativität ist, nicht zu deren Ersatz. Die Zukunft der Entwicklung liegt nicht in der Wahl zwischen Mensch oder Maschine, sondern in der Nutzung der Stärken beider.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/der-reiz-und-die-gefahren-von-ki-werkzeugen-in-der-entwicklung/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Git-Tipp: So richtest du eine globale `.gitignore`-Datei ein</title>
		<link>https://webmatze.de/git-tipp-so-richtest-du-eine-globale-gitignore-datei-ein/</link>
					<comments>https://webmatze.de/git-tipp-so-richtest-du-eine-globale-gitignore-datei-ein/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Mon, 15 Jul 2024 17:46:25 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[shell]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=1054</guid>

					<description><![CDATA[Erfahre, wie du deine Git-Konfiguration optimieren kannst, indem du eine globale .gitignore-Datei einrichtest. Spare Zeit und Aufwand, indem du gängige Ignore-Muster einmalig definierst und in all deinen Projekten verwendest. Folge meiner Schritt-für-Schritt-Anleitung und verbessere deinen Git-Workflow effizient und unkompliziert.]]></description>
										<content:encoded><![CDATA[<p>Hallo zusammen!</p>
<p>Heute möchte ich euch einen nützlichen Tipp für eure Git-Konfiguration zeigen: das Einrichten einer globalen <code>.gitignore</code>-Datei. Diese Methode ist super praktisch, wenn ihr bestimmte Dateien oder Dateitypen habt, die ihr in all euren Projekten ignorieren möchtet, ohne die lokale <code>.gitignore</code>-Datei in jedem Repository manuell anzupassen.<br />
Das ist bei mir zum Beispiel dann der Fall, wenn ich lokale Tools nutze, die ihre Konfigurationsdateien im Repository-Verzeichnis ablegen, die ich aber nie einchecken möchte.</p>
<h2>Warum eine globale <code>.gitignore</code>-Datei?</h2>
<p>Eine globale <code>.gitignore</code>-Datei ermöglicht es euch, Ignore-Muster zu definieren, die in allen euren Git-Repositories gelten. Das ist besonders nützlich, wenn ihr häufig mit Dateien arbeitet, die ihr in jedem Projekt ignorieren möchtet, wie z.B. Log-Dateien oder temporäre Dateien.</p>
<h2>Schritt-für-Schritt-Anleitung</h2>
<h3>1. Erstelle die globale <code>.gitignore</code>-Datei</h3>
<p>Zuerst müsst ihr eine globale <code>.gitignore</code>-Datei erstellen. Diese Datei kann irgendwo auf eurem System liegen, aber ein üblicher Ort ist das Home-Verzeichnis. Ihr könnt sie beispielsweise <code>.gitignore_global</code> nennen.</p>
<pre><code class="language-bash">touch ~/.gitignore_global</code></pre>
<h3>2. Füge Muster zur globalen <code>.gitignore</code>-Datei hinzu</h3>
<p>Jetzt könnt ihr die <code>~/.gitignore_global</code>-Datei bearbeiten und die Muster hinzufügen, die ihr global ignorieren möchtet. Dafür könnt ihr jeden beliebigen Texteditor verwenden. Hier ein Beispiel:</p>
<pre><code class="language-bash"># Beispielhafte globale .gitignore-Datei
*.log
*.tmp</code></pre>
<h3>3. Konfiguriere Git, um die globale <code>.gitignore</code>-Datei zu verwenden</h3>
<p>Als nächstes müsst ihr Git mitteilen, dass es diese globale <code>.gitignore</code>-Datei verwenden soll. Dazu verwendet ihr den <code>git config</code> Befehl:</p>
<pre><code class="language-bash">git config --global core.excludesfile ~/.gitignore_global</code></pre>
<h3>4. Überprüfe die Konfiguration</h3>
<p>Um sicherzustellen, dass Git eure globale <code>.gitignore</code>-Datei verwendet, könnt ihr die Konfiguration überprüfen:</p>
<pre><code class="language-bash">git config --get core.excludesfile</code></pre>
<p>Dieser Befehl sollte den Pfad zu eurer globalen <code>.gitignore</code>-Datei ausgeben (z.B. <code>/home/euerbenutzername/.gitignore_global</code>).</p>
<h2>Wie funktioniert das?</h2>
<ul>
<li>Die globale <code>.gitignore</code>-Datei ergänzt die Ignore-Muster, die in der lokalen <code>.gitignore</code>-Datei eures Repositories definiert sind.</li>
<li>Muster, die in der globalen <code>.gitignore</code>-Datei definiert sind, werden in allen euren Git-Repositories ignoriert.</li>
<li>Die lokale <code>.gitignore</code>-Datei in einem spezifischen Repository hat Vorrang vor der globalen Datei. Das bedeutet, dass bei einem Konflikt die Regeln der lokalen <code>.gitignore</code>-Datei angewendet werden.</li>
</ul>
<p>Mit diesen Schritten könnt ihr sowohl globale als auch lokale Ignore-Regeln effektiv in eurem Git-Workflow verwalten.</p>
<p>Ich hoffe, dieser Tipp hilft euch dabei, eure Git-Konfiguration ein bisschen effizienter zu gestalten. Viel Spaß beim Codieren!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/git-tipp-so-richtest-du-eine-globale-gitignore-datei-ein/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>DragonRuby Sokoban &#8211; Mein erstes Game auf itch.io</title>
		<link>https://webmatze.de/dragonruby-sokoban-mein-erstes-game-auf-itch-io/</link>
					<comments>https://webmatze.de/dragonruby-sokoban-mein-erstes-game-auf-itch-io/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Thu, 12 Jan 2023 20:24:38 +0000</pubDate>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[DragonRuby]]></category>
		<category><![CDATA[ruby]]></category>
		<guid isPermaLink="false">https://webmatze.de/?p=966</guid>

					<description><![CDATA[Wenn man etwas Neues lernen möchte, ist es häufig am einfachsten, mit einem richtigen Projekt zu starten und dann bei dessen Umsetzung zu lernen. So habe ich es schon immer gemacht. Als ich Ruby lernen wollte, habe ich mir als Idee eine Event Veranstaltungsseite genommen und angefangen diese mit Ruby on Rails umzusetzen. Entstanden ist [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Wenn man etwas Neues lernen möchte, ist es häufig am einfachsten, mit einem richtigen Projekt zu starten und dann bei dessen Umsetzung zu lernen. So habe ich es schon immer gemacht. Als ich Ruby lernen wollte, habe ich mir als Idee eine Event Veranstaltungsseite genommen und angefangen diese mit <a href="https://rubyonrails.org/" title="Ruby on Rails">Ruby on Rails</a> umzusetzen. Entstanden ist Eventicus (leider offline) aber noch <a href="http://https://github.com/webmatze/eventicus.de" title="zu finden auf github.com">zu finden auf github.com</a>.</p>
<p>Als Jugentlicher wollte ich unbedingt <a href="http://https://winworldpc.com/product/turbo-pascal/7x" title="Turbo Pascal">Turbo Pascal</a> lernen und habe mir als Projekt ein einfaches Sokoban Spiel genommen und es umgesetzt. Im Laufe der Umsetzung habe ich vieles über Turbo Pascal gelernt und auch dessen Grenzen entdeckt. So musste ich z.B. einige der Grafikroutinen dann doch mit <a href="http://https://de.wikibooks.org/wiki/Assembler-Programmierung_f%C3%BCr_x86-Prozessoren/_Einleitung" title="Assembler">Assembler</a> umsetzen.</p>
<p>Da mich das Programmieren von grafischen Spielereien (Demos) oder eben auch gleich richtigen Spielen schon immer faziniert hat, habe ich mich auch gleich für das <a href="http://https://dragonruby.org/toolkit/game" title="DragonRuby Game Toolkit">DragonRuby Game Toolkit</a> interessiert. Dieses erlaubt es euch, mit Hilfe der Ruby Programmiersprache 2D oder auch 3D Spiele zu programmieren.</p>
<p>Und da ich wieder nach einer Idee für ein Spiel suchte, dachte ich mir: &quot;Hey, warum versucht du nicht einfach das gleiche Spiel wie damals umzusetzen?&quot;.</p>
<p>Gesagt – getan.</p>
<p>Also habe ich angefangen und entstanden ist dabei <a href="http://https://webmatze.itch.io/dragonruby-sokoban" title="DragonRuby Sokoban">DragonRuby Sokoban</a>. </p>
<p><iframe src="https://itch.io/embed/1873848?border_width=2" width="554" height="169" frameborder="0"><a href="https://webmatze.itch.io/dragonruby-sokoban">Dragonruby Sokoban by webmatze</a></iframe><br />
<span id="more-966"></span></p>
<p>Den Code findet ihr wie immer <a href="http://https://github.com/webmatze/dragonruby-sokoban-game" title="auf meinen GitHub Account">auf meinen GitHub Account</a>.</p>
<p>Und wer es nicht erwarten kann, kann das Spiel auch gleich hier ausprobieren:</p>
<p><iframe src="https://itch.io/embed-upload/7147494?color=333333" allowfullscreen="" width="1280" height="740" frameborder="0"><a href="https://webmatze.itch.io/dragonruby-sokoban">Play Dragonruby Sokoban on itch.io</a></iframe></p>
<p>Viel Spaß!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/dragonruby-sokoban-mein-erstes-game-auf-itch-io/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Schneller Verbinden &#8211; SSH Config Tricks</title>
		<link>https://webmatze.de/schneller-verbinden-ssh-config-tricks/</link>
					<comments>https://webmatze.de/schneller-verbinden-ssh-config-tricks/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Thu, 28 Sep 2017 21:30:56 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[dev-ops]]></category>
		<category><![CDATA[ssh]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=799</guid>

					<description><![CDATA[Oft ist es umständlich, sich per SSH mit entfernten Servern zu verbinden. Der Befehl dafür kann schnell sehr lang werden und ist häufig schwer zu merken. Host Alias Wollen wir nicht immer den vollen Hostnamen eingeben müssen, um uns per SSH mit einem Server zu verbinden, lohnt es sich eine SSH Konfiguration ~/.ssh/config zu erstellen. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Oft ist es umständlich, sich per <strong>SSH</strong> mit entfernten Servern zu verbinden. Der Befehl dafür kann schnell sehr lang werden und ist häufig schwer zu merken.</p>



<h2 class="wp-block-heading">Host Alias</h2>



<p>Wollen wir nicht immer den vollen Hostnamen eingeben müssen, um uns per SSH mit einem Server zu verbinden, lohnt es sich eine SSH Konfiguration <code>~/.ssh/config</code> zu erstellen.</p>



<p>Dort definieren wir dann einen Alias Namen für den <strong>Ziel Host</strong>. In unserem Fall „myserver“ und legen mit <code>HostName</code> fest, welcher Host sich dahinter verbergen soll. Außerdem können wir uns sparen, immer einen User angeben zu müssen, indem wir diesen per <code>User</code> Eintrag festlegen.</p>



<pre title="~/.ssh/config" class="wp-block-code"><code lang="bash" class="language-bash">Host myserver
	HostName remote.server.com
	User ouruser</code></pre>



<p>Nun können wir uns ganz einfach verbinden:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ ssh myserver</code></pre>



<span id="more-799"></span>



<h2 class="wp-block-heading">Über einen Jump Host mit einem anderen Host verbinden</h2>



<p>Manchmal kann man sich nur über einen dritten Server (Jump Host) mit einem anderen Server verbinden. Die SSH Syntax dafür ist jedoch nicht einfach zu merken. Nehmen wir an wir wollen uns über den <strong>Jump Host</strong>&nbsp;<a href="http://jump.host.com">jump.host.com</a> mit unserem <strong>Ziel Host</strong>&nbsp;<a href="http://remote.server.com">remote.server.com</a> verbinden, dann müssen wir normalerweise folgendes eingeben:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ ssh -J ouruser@jump.host.com:22 remote.server.com</code></pre>



<p>Es ist jedoch deutlich einfacher, dies alles ebenfalls in der SSH Konfiguration <code>~/.ssh/config</code>zu definieren.</p>



<p>In unserem Fall können wir <code>ProxyJump</code> verwenden, um den <strong>Jump Host</strong> zu definieren, über den wir uns mit dem <strong>Ziel Host</strong> verbinden wollen.</p>



<p>Den <strong>Jump Host</strong> selber definieren wir ebenfalls über einen <code>Host</code> Eintrag:</p>



<pre title="~/.ssh/config" class="wp-block-code"><code lang="bash" class="language-bash">Host jumphost
	HostName jump.host.com
	User ouruser
	ProxyCommand none

Host myserver
	HostName remote.server.com
	User ouruser
	ProxyJump jumphost</code></pre>



<p>Nun können wir uns mit dem <strong>Ziel Host</strong> verbinden und werden automatisch über den <strong>Jump Host</strong> geschickt:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ ssh myserver</code></pre>



<p>In der SSH Konfiguration läßt sich allerdings noch viel mehr konfigurieren. Eine ausführlichere Anleitung findet sich hier <a href="http://man.openbsd.org/ssh_config.5">ssh_config(5) - OpenBSD manual pages</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/schneller-verbinden-ssh-config-tricks/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nutze dein Chromebook zur Webentwicklung</title>
		<link>https://webmatze.de/nutze-dein-chromebook-zur-webentwicklung/</link>
					<comments>https://webmatze.de/nutze-dein-chromebook-zur-webentwicklung/#respond</comments>
		
		<dc:creator><![CDATA[Mathias Karstädt]]></dc:creator>
		<pubDate>Fri, 30 Dec 2016 16:22:45 +0000</pubDate>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[chromebook]]></category>
		<category><![CDATA[i3]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[ubuntu]]></category>
		<guid isPermaLink="false">http://webmatze.de/?p=781</guid>

					<description><![CDATA[Wer ein Chromebook besitzt und sich zudem mit der Entwicklung von Webanwendungen beschäftigt, wird sich irgendwann Gedanken dazu machen, wie man das Chromebook dafür nutzen könnte. Von Hause aus ist ein Chromebook nicht mehr als ein Chrome Browser mit ein paar erweiterten Funktionen. Jedoch eignet es sich kaum für entwicklungstechnische Aufgaben. Es ist zwar nicht [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Wer ein Chromebook besitzt und sich zudem mit der Entwicklung von Webanwendungen beschäftigt, wird sich irgendwann Gedanken dazu machen, wie man das Chromebook dafür nutzen könnte.</p>
<p>Von Hause aus ist ein Chromebook nicht mehr als ein Chrome Browser mit ein paar erweiterten Funktionen. Jedoch eignet es sich kaum für entwicklungstechnische Aufgaben. Es ist zwar nicht unmöglich, auf einem Chromebook zu programmieren, aber man stößt schnell an die Grenzen.<span id="more-781"></span></p>
<p>Auch ich habe mir dazu längere Zeit Gedanken gemacht und einiges ausprobiert. So habe ich zum Beispiel den (jetzt leider eingestellten) Service von <a href="https://www.nitrous.io/">Nitrous.io</a> genutzt, um auf einer externen Virtuellen Maschine und mit einem Editor in einem Browser zu arbeiten. Aber man ist von den externen Anbietern abhängig, es kommen regelmäßige Kosten auf einen zu und wenn man Pech hat, wird der Service irgendwann einfach eingestellt - wie in diesem Fall geschehen.</p>
<p>Es musste also eine bessere Möglichkeit gefunden werden. Zum Glück ist es mittlerweile relativ einfach möglich, Linux parallel zu Chrome OS laufen lassen. Und unter Linux kann man dann leicht eine Entwicklungsumgebung einrichten und nutzen.</p>
<p>Hier stelle ich euch vor, welche Schritte man durchführen muss und auf welche Schwierigkeiten man stoßen kann.</p>
<h2>Chromebook in den Developer Modus schalten</h2>
<p>Damit man Linux auf einem Chromebook nutzen kann, muss dieses zuerst in den Developer Modus geschaltet werden. Wie dies geschieht, kann von Chromebook zu Chromebook unterschiedlich sein.</p>
<p>Auf <a href="http://www.chromium.org/">chromium.org</a> gibt es hierfür für die meisten Modelle eine entsprechende <a href="http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices">Anleitung</a>.</p>
<p>Ich selber verwende das <strong>Acer C740</strong> welches sich durch Drücken der Tasten <strong>Escape</strong> und <strong>Refresh</strong> und dann auf <strong>Power</strong> in den Developer Mode versetzten lässt. (Achtung! Nur <strong>Refresh</strong> und <strong>Power</strong> setzt das Chromebook in den Werkszustand zurück!) Bitte beachtet auch, dass dieser Vorgang alle Daten auf eurem Chromebook löscht und das System komplett neu einrichtet!</p>
<h2>Crouton herunterladen</h2>
<p>Im nächsten Schritt müssen wir <a href="https://github.com/dnschneid/crouton">Crouton</a> herunterladen. Hierbei handelt es sich um ein Programm, mit dem sich verschiedene Linux Chroot Umgebungen installieren lassen. Es ist somit also auch möglich, mehrere verschiedene Linux Systeme nebeneinander betreiben zu können.</p>
<p>Dazu einfach <a href="https://goo.gl/fd3zc">die Datei</a> herunterladen oder auf der Seite von Crouton den entsprechenden Link suchen.</p>
<h2>Ubuntu Trusty mit XFCE Windowmanager installieren</h2>
<p>Nun müssen wir mit der Tastenkombination<strong> Strg + Alt + T</strong> ein Terminal öffnen. In diesem Terminal wird die Chrome OS Developer Shell (crosh) ausgeführt. Hier geben wir dann folgenden Befehl ein:</p>
<pre>crosh&gt; shell
chronos@localhost / $</pre>
<p>Nun können wir Crouton aus dem Downloads Ordner heraus ausführen und Ubuntu Trusty mit dem Window Manager XFCE installieren:</p>
<pre>sudo sh ~/Downloads/crouton -r trusty -t xfce</pre>
<p>Dieser Vorgang dauert etwas länger. Es werden alle benötigten Linux Bibliotheken heruntergeladen und installiert. Wenn alles fertig ist, können wir die Chroot Umgebung starten.</p>
<h2>Ubuntu starten</h2>
<p>Mit folgendem Befehl starten wir Ubuntu:</p>
<pre>sudo enter-chroot</pre>
<p>Damit kommen wir in die Shell von Ubuntu. Auch wenn <a href="https://www.xfce.org/">XFCE</a> ein netter Window Manager ist, möchte ich doch lieber einen einfach zu bedienenden und performanten <a href="https://i3wm.org/">Tiling Window Manager wie i3</a> nutzen. Diesen können wir direkt gleich installieren:</p>
<pre>sudo apt-get install i3</pre>
<h2>XFCE starten und i3 einrichten</h2>
<p>Wir können an dieser Stelle die Shell mit <strong>Strg + D</strong> wieder schließen und XFCE starten:</p>
<pre><code>sudo startxfce4</code></pre>
<p>Wir rufen dann im Menü<strong> 'Session and Startup'</strong> die Einstellungen<strong> 'Application Autostart'</strong> auf und fügen mit <strong>'+ Add'</strong> einen neues Kommando mit folgenden Werten hinzu:</p>
<ul>
<li>Name: i3</li>
<li>Description: i3</li>
<li>Command: /usr/bin/i3</li>
</ul>
<p>Dann gehen wir zu <strong>'Session and Startup &gt; Session'</strong> wählen alle Einträge die <strong>xfce</strong> enthalten aus und schließen diese indem wir auf<strong> 'Quit program'</strong> klicken. Danach speichern wir die Session und starten die Chroot Umgebung neu.</p>
<p><strong>i3</strong> sollte jetzt automatisch starten und funktionsfähig sein.</p>
<h2>Warum ein Window Manager wie i3?</h2>
<p>Warum nutzen wir nicht einfach die normale Shell von Ubuntu, sondern installieren einen Window Manager? Aber der Vorteil liegt klar auf der Hand. Mit i3 können sehr leicht mehrere Shells geöffnet und positioniert werden, auch in verschiedenen Workspaces. Außerdem können grafische Programme wie <a href="https://www.mozilla.org/de/firefox/new/">Firefox</a> oder auch der <a href="https://atom.io/">Atom Editor</a> geöffnet werden. Gerade dies ist wichtig, wenn wir komfortabel unseren Code eingeben und bearbeiten wollen.</p>
<p>Das System lässt sich einfach individuell anpassen und erweitern. Ihr wollte ein anderes Hintergrundbild? Oder Ruby, Node oder ähnliche Dinge installieren? Die Möglichkeiten sind praktisch unendlich.</p>
<p><a href="https://www.youtube.com/playlist?list=PL5ze0DjYv5DbCv9vNEzFmP6sU7ZmkGzcf">Hier findet ihr drei sehr gute Einführungsvideos zu i3</a>.</p>
<p><iframe loading="lazy" src="https://www.youtube.com/embed/videoseries?list=PL5ze0DjYv5DbCv9vNEzFmP6sU7ZmkGzcf" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p>Ich hoffe ich konnte euch zeigen, welche Möglichkeiten in eurem Chromebook stecken. Wenn Ihr Fragen habt, stellt diese einfach in den Kommentaren.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://webmatze.de/nutze-dein-chromebook-zur-webentwicklung/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
