<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>webcodr</title>
    <link>https://webcodr.io/series/building-codrpress/</link>
    <description>Recent content on webcodr</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 27 Jul 2013 22:30:36 +0100</lastBuildDate>
    <atom:link href="https://webcodr.io/series/building-codrpress/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Amplify 2.0</title>
      <link>https://webcodr.io/2013/07/amplify-2.0/</link>
      <pubDate>Sat, 27 Jul 2013 22:30:36 +0100</pubDate>
      <guid>https://webcodr.io/2013/07/amplify-2.0/</guid>
      <description>Ich habe Amplify grundlegend überarbeitet und mit ein paar neuen Features ausgestattet:
Redcarpet wurde durch kramdown ersetzt und unterstützt damit auch die Markdown-Erweiterungen, die kramdown anbietet. Automatisches Verlinken von URLs HTML-Sanitation Das Syntax-Highlighting übernimmt nun das Gem ‘pygments.rb’. Der Umweg über Pygmentizr fällt damit intern weg und verbessert die Reaktionszeiten deutlich. Das Frontend basiert nun auf AngularJS. Komplett überarbeiteter Quelltext. JSON-basierte API kramdown, das automatische Verlinken und die HTML-Sanitation werden über das Gem ‘slodown’ von Hendrik Mans erledigt.
</description>
      <content:encoded><![CDATA[<p>Ich habe Amplify grundlegend überarbeitet und mit ein paar neuen Features ausgestattet:</p>
<ul>
<li>Redcarpet wurde durch kramdown ersetzt und unterstützt damit auch die Markdown-Erweiterungen, die kramdown anbietet.</li>
<li>Automatisches Verlinken von URLs</li>
<li>HTML-Sanitation</li>
<li>Das Syntax-Highlighting übernimmt nun das Gem &lsquo;pygments.rb&rsquo;. Der Umweg über Pygmentizr fällt damit intern weg und verbessert die Reaktionszeiten deutlich.</li>
<li>Das Frontend basiert nun auf AngularJS.</li>
<li>Komplett überarbeiteter Quelltext.</li>
<li>JSON-basierte API</li>
</ul>
<p>kramdown, das automatische Verlinken und die HTML-Sanitation werden über das Gem &lsquo;<a href="https://github.com/hmans/slodown">slodown</a>&rsquo; von Hendrik Mans erledigt.</p>
<p>Die neue JSON-basierte API ist über eine andere URL erreichbar:</p>
<p><code>http://amplify.webcodr.de/api/2.0/transform</code></p>
<p>Ein Beispiel-Request via POST:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;# Hello World!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Und die entsprechende Antwort von Amplify:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;html&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;h1 id=\&#34;hello-world\&#34;&gt;Hello World!&lt;/h1&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;# Hello World!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Wer, wie früher, ohne JSON auskommen möchte, nutzt bitte folgende URL:</p>
<p><code>http://amplify.webcodr.de/api/1.0/transform</code></p>
<p>Die bisherige Möglichkeit einfach einen POST-Request auf die Amplify-URL abzusetzen ist nur noch aus Gründen der Kompatibilität zu bestehenden Anwendungen aktiv, wird aber langfristig deaktiviert.</p>
<p>Zukünftige Features werde ich außerdem nur für die JSON-basierte API 2.0 implementieren.</p>
<p>Des weiteren plane ich Amplify langfristig nicht mehr auf Heroku laufen zu lassen, da die App einfach zu häufig in den Schlafmodus versetzt wird und anschließend sehr lange braucht, bis sie auf Anfragen reagiert.</p>
<p>Ich könnte zwar einfach einen Dyno hinzubuchen, aber das ist nicht gerade billig und es gibt wirklich kostengünstigere Möglichkeiten, Ruby-Web-Applikationen zu hosten &ndash; beispielsweise bei <a href="https://uberspace.de/">Uberspace.de</a>.</p>
<p>Aus technischer Sicht dürfte noch interessant sein, dass Amplify nun mit Capybara automatisiert getestet wird und auf <a href="https://travis-ci.org/WebCodr/Amplify">Travis CI</a> sowie testweise <a href="https://circleci.com/">Circle CI</a> (mit automatischem Heroku-Deployment) läuft.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Say Hello to Mango</title>
      <link>https://webcodr.io/2013/04/say-hello-to-mango/</link>
      <pubDate>Sat, 06 Apr 2013 22:29:53 +0100</pubDate>
      <guid>https://webcodr.io/2013/04/say-hello-to-mango/</guid>
      <description>Was’n das? Finger weg! Diese Mango schmeckt nicht.
Mango ist ein Object Document Mapper für MongoDB und PHP.
Und MongoAppKit? MongoAppKit hat ein Problem, denn es ist nicht nur ein ODM und kann noch so einiges mehr. Theoretisch kann man zwar die ODM-Komponente auch ohne den ganzen anderen Krempel nutzen, aber es bleibt eine große Abhängigkeit zu Silex, die man auch nicht so einfach wieder los wird.
Dies und das im Vergleich schlechte Handling bzw. der geringe Komfort von MongoAppKit, haben mich dazu bewogen mit Mango einen universell einsatzbaren und leicht handzuhabenden ODM zu entwickeln.
</description>
      <content:encoded><![CDATA[<h2 id="wasn-das"><a href="#wasn-das" class="heading-anchor">Was&rsquo;n das?</a></h2>
<p>Finger weg! Diese Mango schmeckt nicht.</p>
<p>Mango ist ein Object Document Mapper für MongoDB und PHP.</p>
<h3 id="und-mongoappkit"><a href="#und-mongoappkit" class="heading-anchor">Und MongoAppKit?</a></h3>
<p>MongoAppKit hat ein Problem, denn es ist nicht nur ein ODM und kann noch so einiges mehr. Theoretisch kann man zwar die ODM-Komponente auch ohne den ganzen anderen Krempel nutzen, aber es bleibt eine große Abhängigkeit zu Silex, die man auch nicht so einfach wieder los wird.</p>
<p>Dies und das im Vergleich schlechte Handling bzw. der geringe Komfort von MongoAppKit, haben mich dazu bewogen mit Mango einen universell einsatzbaren und leicht handzuhabenden ODM zu entwickeln.</p>
<p>Mango wurde stark von Mongoid für Ruby inspiriert und soll dessen Funktionalität zumindest teilweise in PHP abbilden. Das ist einfacher gesagt als getan, denn Ruby bietet wesentlich elegantere Möglichkeiten diverse Probleme zu lösen, als es mit PHP derzeit machbar ist.</p>
<h2 id="los-gehts-"><a href="#los-gehts-" class="heading-anchor">Los geht&rsquo;s …</a></h2>
<h3 id="installation-via-composer"><a href="#installation-via-composer" class="heading-anchor">Installation via Composer</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar require webcodr/mango:*
</span></span></code></pre></div><h3 id="ein-dokument-anzulegen-ist-ein-kinderspiel"><a href="#ein-dokument-anzulegen-ist-ein-kinderspiel" class="heading-anchor">Ein Dokument anzulegen ist ein Kinderspiel</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">MyProject\Model</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Mango\Document</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Mango\DocumentInterface</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="k">implements</span> <span class="nx">DocumentInterface</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">use</span> <span class="nx">Document</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">function</span> <span class="nf">addFields</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">addField</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;String&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">addField</span><span class="p">(</span><span class="s1">&#39;email&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;String&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">addField</span><span class="p">(</span><span class="s1">&#39;created_at&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;DateTime&#39;</span><span class="p">,</span> <span class="s1">&#39;index&#39;</span> <span class="o">=&gt;</span> <span class="k">true</span><span class="p">,</span> <span class="s1">&#39;default&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;now&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">addField</span><span class="p">(</span><span class="s1">&#39;updated_at&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;DateTime&#39;</span><span class="p">,</span> <span class="s1">&#39;index&#39;</span> <span class="o">=&gt;</span> <span class="k">true</span><span class="p">,</span> <span class="s1">&#39;default&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;now&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Es ist lediglich nötig, dass die Model-Klasse das Interface <code>DocumentInterface</code> implementiert und den Trait <code>Document</code> einbindet. In der Hook-Methode <code>addFields()</code> werden anschließend noch die Felder des Dokuments deklariert.</p>
<p>Mango nutzt etwas Magic: Der Klassenname des Models ist gleichzeitig auch der Name der Collection (klein geschrieben). Soll die Collection anders heißen bzw. das Model eine vorhandene nutzen, muss lediglich die Methode <code>getCollectionName()</code> überschrieben werden.</p>
<h3 id="go-mango-go"><a href="#go-mango-go" class="heading-anchor">Go, Mango, go</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Mango\Mango</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Mango\DocumentManager</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Document\User</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$mango</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Mango</span><span class="p">(</span><span class="s1">&#39;mongodb://localhost/galactica&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$dm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DocumentManager</span><span class="p">(</span><span class="nv">$mango</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="s1">&#39;William Adama&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">email</span> <span class="o">=</span> <span class="s1">&#39;william.adama@bsg-75.mil&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">store</span><span class="p">();</span>
</span></span></code></pre></div><p>Das Mango-Object erwartet eine gültige MongoDB URI, falls notwendig inkl. Benutzer, Passwort, Port usw.</p>
<p>Dem Document Manager kommt eine vergleichbare Aufgabe zu, wie dem Entity Manager in Doctrine2.</p>
<p>Für mehr Komfort holt sich eine Model-Klasse den Document Manager über eine statische Methode ab. Daher können Methoden wie <code>store()</code> direkt über die Model-Klasse abgewickelt werden.</p>
<h3 id="dokumente-abfragen"><a href="#dokumente-abfragen" class="heading-anchor">Dokumente abfragen</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">where</span><span class="p">([</span><span class="s1">&#39;name&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;William Adama&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">count</span><span class="p">();</span> <span class="c1">// = 1
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">first</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">email</span><span class="p">;</span> <span class="c1">// = william.adama@bsg-75.mil
</span></span></span></code></pre></div><p>Eine Abfrage kann einfach über die statische Methode <code>where()</code> ausgeführt werden. Die Syntax der Abfragen entspricht derzeit noch der normalen MongoDB Query API. Für die Zukunft plane ich aber eine Abstraktionsebene für die Abfragen, vergleichbar mit Mongoid.</p>
<p>Eine Abfrage mit <code>where()</code> oder <code>find()</code> gibt immer ein Cursor-Objekt zurück, das anhand der aufgerufenen Methode entscheiden kann, ob der Zugriff auf den MongoCursor oder das Abfrageergebnis in Form einer Instanz von MutableMap erfolgt.</p>
<p>In obigem Beispiel ist <code>count()</code> eine Methode des Cursors, während <code>first()</code> schon auf das Ergebnis zugreift. Wie MongoCursor kann auch die Cursor-Klasse von Mango einfach über das Ergebnis iterieren.</p>
<p>Durch die dynamische Unterscheidung zwischen MongoCursor- und Datenzugriff, können auf eine Instanz der Cursor-Klasse auch alle Methoden von MutableMap angewandt werden.</p>
<p>Beispielsweise:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">User</span><span class="o">::</span><span class="na">where</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">reverse</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">each</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$document</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="nv">$document</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Natürlich macht dieser Code wenig Sinn, da man das wesentlich effizienter über die Cursor-Methoden erledigen kann. Das Beispiel soll lediglich zeigen, was möglich wäre.</p>
<h3 id="hydration"><a href="#hydration" class="heading-anchor">Hydration</a></h3>
<p>Mango sorgt automatisch dafür, dass die Dokumente im Ergebnis immer Instanzen ihrer jeweiligen Model-Klasse sind.</p>
<p>Die Hydration-Automatik sorgt außerdem dafür, dass die Daten intern als jeweilige Typ-Klasse von Mango gehalten werden.</p>
<p>Typ-Klassen halten die Daten und können sie in zwei Formaten zurückgeben. Konfiguriert man ein Feld als <code>DateTime</code> bekommt Mango intern beim Speichern automatisch ein MongoDate-Objekt. Greift man hingegen außerhalb von Mango auf den Wert zu, bekäme man in diesem Fall eine Instanz der Klasse DateTime zurück.</p>
<p>Soweit zum aktuellen Funktionsumfang von Mango. Es ist bei weitem noch nicht fertig, kann aber für kleine Projekte schon eingesetzt werden. Ich verwende es selbst in der aktuellsten Version von CodrPress und es macht wesentlich mehr Spaß als MongoAppKit, ohne ein monströses Schlachtschiff wie Doctrine zu sein.</p>
<p>Natürlich gibt&rsquo;s Mango auch bei <a href="https://github.com/WebCodr/Mango">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Services ftw!</title>
      <link>https://webcodr.io/2013/02/services-ftw/</link>
      <pubDate>Sat, 02 Feb 2013 22:29:07 +0100</pubDate>
      <guid>https://webcodr.io/2013/02/services-ftw/</guid>
      <description>Wer kennt das nicht? Man findet eine nette Software-Bibliothek in einer bestimmten Sprache, die vom Server der eigenen Web-Applikation nicht unterstützt wird oder nur sehr umständlich auf andere Weise genutzt werden kann.
So erging es mir mit Tools für Markdown-Rendering und server-basiertem Syntax-Highlighting. Zwar habe ich dafür ja im Oktober die Composer-Pakete SilexMarkdown und Radiant geschrieben, die beide auf bereits vorhandenen Bibliotheken fußen.
Ich war mit beiden nie recht glücklich. Für Ruby und Python gibt es viel schönere, wesentlich umfangreichere Lösungen:
</description>
      <content:encoded><![CDATA[<p>Wer kennt das nicht? Man findet eine nette Software-Bibliothek in einer bestimmten Sprache, die vom Server der eigenen Web-Applikation nicht unterstützt wird oder nur sehr umständlich auf andere Weise genutzt werden kann.</p>
<p>So erging es mir mit Tools für Markdown-Rendering und server-basiertem Syntax-Highlighting. Zwar habe ich dafür ja im Oktober die Composer-Pakete SilexMarkdown und Radiant geschrieben, die beide auf bereits vorhandenen Bibliotheken fußen.</p>
<p>Ich war mit beiden nie recht glücklich. Für Ruby und Python gibt es viel schönere, wesentlich umfangreichere Lösungen:</p>
<ul>
<li>
<p>Pygments ist ein in Python geschriebener Syntax-Highlighter, der nahezu jede relevante Sprache unterstützt &ndash; selbst esoterische Merkwürdigkeiten wie Brainfuck.</p>
</li>
<li>
<p>Redcarpet wurde in Ruby verfasst und bietet einen sehr leicht erweiter- und modifizierbaren Markdown-Renderer.</p>
</li>
</ul>
<p>Gerade für ein Blog-System wie CodrPress liegt es nahe, beide zu kombinieren und damit zumindest teilweise GitHub flavoured Markdown zu unterstützen.</p>
<p>Wie bekomme ich also drei Programmiersprachen unter einen Hut, ohne dass CodrPress nur auf angepassten Server-Konfigurationen läuft? Ganz einfach: Services!</p>
<h2 id="pygmentizr"><a href="#pygmentizr" class="heading-anchor">Pygmentizr</a></h2>
<p><a href="https://web.archive.org/web/20140516204904/http://pygmentizr.herokuapp.com/">Pygmentizr</a> ist logischerweise in Python geschrieben, um Pygments nutzen zu können.</p>
<p>Per POST-Anfrage auf die verlinkte URL erreicht man den eigentlichen Service, der als Parameter die Sprache und den Quelltext erwartet. Zurück kommt HTML, das nur noch per CSS hübsch gemacht werden muss.</p>
<p>Ein entsprechendes Stylesheet für den bekannten Monokai-Stil ist auf der Seite verlinkt oder im GitHub-Repository zu finden.</p>
<p><a href="https://web.archive.org/web/20131210082228/https://github.com/MadCatme/Pygmentizr">Pygmentizr bei GitHub</a></p>
<h2 id="amplifyr"><a href="#amplifyr" class="heading-anchor">Amplifyr</a></h2>
<p>Amplifyr nutzt Redcarpet und bindet Pygmentizr als Syntax-Highlighter ein.</p>
<p>Wie Pygmentizr lässt sich Amplifyr per POST-Anfrage ansprechen und gibt den in HTML konvertierten Markdown-Quelltext zurück.</p>
<p>Beide Dienste laufen auf der Cloud-Plattform Heroku.</p>
<h2 id="codrpress-integration"><a href="#codrpress-integration" class="heading-anchor">CodrPress-Integration</a></h2>
<p>Um beide Services in CodrPress nutzen zu können, habe ich in SilexMarkdown ein paar Umbauten vorgenommen. Beim Registrieren des Service-Providers in einer Silex-Applikation, lässt sich nun ganz einfach übergeben, ob der eingebaute Renderer samt Radiant oder Amplifyr genutzt werden soll. Eine entsprechende Anleitung findet sich in der ReadMe-Datei des SilexMarkdown-Repositories bei GitHub.</p>
<p>Beide Dienste sind bei Heroku untergebracht und kosten mich keinen Cent. Daher gebe ich die Nutzung für jeden frei. Viel Spaß!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>PythonPress</title>
      <link>https://webcodr.io/2012/12/pythonpress/</link>
      <pubDate>Sun, 02 Dec 2012 22:27:13 +0100</pubDate>
      <guid>https://webcodr.io/2012/12/pythonpress/</guid>
      <description>Da eine neue Sprache nicht genug ist, beschäftige ich mich neben Ruby neuerdings auch noch mit Python.
Als Lernprojekt setze ich aktuell CodrPress als Python-Version um. Natürlich will ich nicht das Rad neu erfinden, daher setze ich auf zwei Frameworks, dank denen ich sehr schnell ein vorzeigbares Ergebnis zusammenbauen konnte:
Flask ist ein Micro-Framework vergleichbar mit Sinatra (Ruby) oder Silex (PHP). Es kümmert sich also um alles, was man braucht, um eine Website zu bauen. Vom Routing bis hin zur Template Engine (Jinja2).
</description>
      <content:encoded><![CDATA[<p>Da eine neue Sprache nicht genug ist, beschäftige ich mich neben Ruby neuerdings auch noch mit Python.</p>
<p>Als Lernprojekt setze ich aktuell CodrPress als Python-Version um. Natürlich will ich nicht das Rad neu erfinden, daher setze ich auf zwei Frameworks, dank denen ich sehr schnell ein vorzeigbares Ergebnis zusammenbauen konnte:</p>
<ul>
<li>
<p><a href="http://flask.pocoo.org/">Flask</a> ist ein Micro-Framework vergleichbar mit Sinatra (Ruby) oder Silex (PHP). Es kümmert sich also um alles, was man braucht, um eine Website zu bauen. Vom Routing bis hin zur Template Engine (Jinja2).</p>
</li>
<li>
<p><a href="http://mongoengine.org/">MongoEngine</a> bietet einen ODM (Object Document Mapper) vergleichbar mit Mongoid (Ruby) oder meinem eigenen Projekt MongoAppKit in PHP.</p>
</li>
</ul>
<h2 id="routen-definitionen-mit-flask"><a href="#routen-definitionen-mit-flask" class="heading-anchor">Routen-Definitionen mit Flask</a></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@app.route</span><span class="p">(</span><span class="s1">&#39;/hello/&lt;name&gt;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s1">&#39;Hello </span><span class="si">%s</span><span class="s1">!&#39;</span> <span class="o">%</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</span></span></code></pre></div><p>Zu Beginn wird die Klasse <code>Flask</code> aus dem Package <code>flask</code> importiert und anschließend eine Instanz erstellt.</p>
<p>Im Gegensatz zu PHP oder anderen C-Syntax-Sprachen kennt Python das Schlüsselwort new nicht. Um ein neues Objekt zu instanziieren reicht es den Klassennamen samt den Klammern und ggf. den Constructor-Argumenten zu schreiben.</p>
<p>Es folgt die Routen-Definition. Variable Werte werden in spitze Klammern gesetzt und der anschließenden Methode mit gleichem Namen als Parameter übergeben.</p>
<p>Wie auch in Silex oder Sinatra wird der Rückgabewert einer Routen-Methode zurück an den Browser geschickt. In diesem Fall ist das nur ein simpler String-Wert.</p>
<h2 id="templates-in-flask"><a href="#templates-in-flask" class="heading-anchor">Templates in Flask</a></h2>
<p>Flask nutzt die Template Engine Jinja2. Wer aus der PHP-Welt Twig kennt fühlt sich sofort heimisch. Die Sprachelemente sind nahezu identisch.</p>
<p>Datei: <code>./templates/hello.html</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Hello {{ name }}!<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>  
</span></span></code></pre></div><p>In obiger Route müsste die Methode nun so aussehen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">&#39;hello.html&#39;</span><span class="p">,</span> <span class="n">name</span> <span class="o">=</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span></code></pre></div><p>Nicht vergessen: <code>render_template</code> muss zusätzlich importiert werden!</p>
<h2 id="mongoengine"><a href="#mongoengine" class="heading-anchor">MongoEngine</a></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">mongoengine</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Post</span><span class="p">(</span><span class="n">Document</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">_id</span> <span class="o">=</span> <span class="n">ObjectIdField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">created_at</span> <span class="o">=</span> <span class="n">DateTimeField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">published</span> <span class="o">=</span> <span class="n">BooleanField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span> <span class="o">=</span> <span class="n">StringField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">body</span> <span class="o">=</span> <span class="n">StringField</span><span class="p">()</span>
</span></span></code></pre></div><p>Der Aufruf von <code>connect</code> stellt eine Verbindung zur Datenbank <code>test</code> her. Da keine Verbindungsdaten angegeben werden, geht MongoEngine automatisch von einem lokalen MongoDB-Server auf dem Standard-Port aus.</p>
<p>Die Klasse <code>Post</code> ist eine Sub-Klasse von <code>Document</code> aus MongoEngine. Anschließend werden die Felder der Klasse definiert. MongoEngine stellt für jeden von MongoDB unterstützten Datentyp entsprechende Klassen zur Verfügung.</p>
<p>Sofern nicht über das Attribut <code>meta</code> eine andere Collection definiert wird, greift MongoEngine auf den Klassennamen in Kleinbuchstaben als Collection zu.</p>
<p>Um ein neues Dokument von Post zu erstellen und zu speichern, reicht schon folgender Code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">post</span> <span class="o">=</span> <span class="n">Post</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">post</span><span class="o">.</span><span class="n">published</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"><span class="n">post</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s1">&#39;Hello World!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">post</span><span class="o">.</span><span class="n">body</span> <span class="o">=</span> <span class="s1">&#39;Hallo, ich bin ein Test.&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">post</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
</span></span></code></pre></div><h2 id="abfragen-mit-mongoengine"><a href="#abfragen-mit-mongoengine" class="heading-anchor">Abfragen mit MongoEngine</a></h2>
<p>Als vollständiger ODM bietet MongoEngine natürlich auch die Möglichkeit vorhandene Daten abzufragen. In folgendem Beispiel werden die letzten zehn veröffentlichten Einträge absteigend nach dem Erstelldatum sortiert, in ein Array geschrieben.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">posts</span> <span class="o">=</span> <span class="n">Post</span><span class="o">.</span><span class="n">objects</span><span class="p">(</span><span class="n">published</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;-created_at&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</span></span></code></pre></div><p>Abfragen erfolgen statisch, daher ist keine Instanz nötig. Die Methode <code>objects()</code> enthält die Bedingungen, also in diesem Fall, dass ein Eintrag veröffentlicht wurde. <code>order_by()</code> erwartet den Feldnamen mit der Sortierrichtung als Präfix. Hierbei steht <code>+</code> für aufsteigend und <code>-</code> für absteigend. Zu guter Letzt wird das Ergebnis mit <code>limit()</code> auf 10 Dokumente eingeschränkt.</p>
<p>Mit diesem Wissen lässt sich nun ganz schnell eine Basis-Applikation bauen, die aus der vorhandenen CodrPress-Collection Einträge ausliest und anzeigt.</p>
<p>Der bisherige Stand ist natürlich bei <a href="https://web.archive.org/web/20131210082353/https://github.com/MadCatme/CodrPressP">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>CodrPress</title>
      <link>https://webcodr.io/2012/10/codrpress/</link>
      <pubDate>Sun, 21 Oct 2012 22:26:30 +0100</pubDate>
      <guid>https://webcodr.io/2012/10/codrpress/</guid>
      <description>Ich bin mal wieder so wahnsinnig und arbeite an einem Blog-System. Diesmal will ich das Rad aber nicht neu erfinden und ein zweites Wordpress bauen. Stattdessen orientiert sich CodrPress an Schnitzelpress.
Da Schnitzelpress auf Ruby basiert und primär für den Einsatz auf Heroku ausgelegt ist, habe ich mich dazu entschieden mit CodrPress quasi eine PHP-Version von Schnitzelpress zu entwickeln.
Natürlich ist das wieder mal einfacher gesagt als getan, vor allem da es für diverse Ruby Gems, die Schnitzelpress nutzt, in der PHP-Welt kaum brauchbaren Ersatz gibt.
</description>
      <content:encoded><![CDATA[<p>Ich bin mal wieder so wahnsinnig und arbeite an einem Blog-System. Diesmal will ich das Rad aber nicht neu erfinden und ein zweites Wordpress bauen. Stattdessen orientiert sich CodrPress an <a href="https://github.com/hmans/schnitzelpress">Schnitzelpress</a>.</p>
<p>Da Schnitzelpress auf Ruby basiert und primär für den Einsatz auf Heroku ausgelegt ist, habe ich mich dazu entschieden mit <a href="https://web.archive.org/web/20131210084517/https://github.com/MadCatme/CodrPress">CodrPress</a> quasi eine PHP-Version von Schnitzelpress zu entwickeln.</p>
<p>Natürlich ist das wieder mal einfacher gesagt als getan, vor allem da es für diverse Ruby Gems, die Schnitzelpress nutzt, in der PHP-Welt kaum brauchbaren Ersatz gibt.</p>
<p>Mit <a href="https://github.com/vmg/redcarpet">Redcarpet</a> und <a href="https://github.com/rubychan/coderay">CodeRay</a> hat Ruby zwei wundervolle Gems, die sich um Markdown-Rendering bzw. Syntax-Highlighting kümmern.</p>
<p>CodrPress basiert auf meinem Projekt <a href="https://web.archive.org/web/20131210082258/https://github.com/MadCatme/mongoappkit">MongoAppKit</a>, das wiederum auf <a href="https://github.com/silexphp/Silex">Silex</a> sowie <a href="https://twig.symfony.com/">Twig</a> setzt und seine Abhängigkeiten mit <a href="http://getcomposer.org/">Composer</a> regelt. Keine der PHP-basierten Lösungen, um diese zwei Ruby Gems zu ersetzen, bietet Composer-Unterstützung an und die Strukturen sind z.T. auch nicht <a href="/2012/06/php-autoloader-nach-dem-psr-0-standard/">PSR-0-kompatibel</a>, so dass ein Autoloading der Klassen nicht möglich ist.</p>
<p>Daher habe ich zwei neue Projekte aus der Taufe gehoben, die genau diesen Mangel beseitigen:</p>
<h3 id="silexmarkdown"><a href="#silexmarkdown" class="heading-anchor">SilexMarkdown</a></h3>
<p>Da ich keine Lust und Zeit habe, selbst einen Markdown-Renderer zu schreiben, basiert <a href="https://web.archive.org/web/20131210084926/https://github.com/MadCatme/SilexMarkdown">SilexMarkdown</a> auf <a href="https://github.com/michelf/php-markdown/">php-markdown</a> von Michel Fortin.</p>
<p>Ich musste es erstmal in eine brauchbare Struktur bringen, da das Original leider keine Namespaces nutzt und sogar zwei Klassen in einer Datei besitzt.</p>
<p>SilexMarkdown stellt nun eine Service-Provider-Klasse für Silex und eine entsprechende Twig-Extension zur Verfügung. Dazu wurde es noch mit einer Unterstützung für Code-Blöcke angereichert, um Syntax Highlighting wie in GitHub nutzen zu können.</p>
<h3 id="radiant"><a href="#radiant" class="heading-anchor">Radiant</a></h3>
<p>Die Kern-Komponente von <a href="https://web.archive.org/web/20131210083959/https://github.com/MadCatme/Radiant">Radiant</a> ist ebenfalls nicht auf meinem Mist gewachsen und stammt aus dem Projekt <a href="https://github.com/ccampbell/nijikodo">Nijikodo</a> von Clint Campbell.</p>
<p>Immerhin war die Grundlage schon mal PSR-0-kompatibel und damit auch relativ leicht Composer-tauglich zu machen.</p>
<p>Meine Arbeit bestand zum Großteil darin, entsprechende Unit-Tests zu schreiben und einige Fehler zu beseitigen und es in SilexMarkdown einzubinden.</p>
<h2 id="qualität"><a href="#qualit%c3%a4t" class="heading-anchor">Qualität</a></h2>
<p>Alle genannten Projekte, also MongoAppKit, SilexMarkdown, Radiant und CodrPress werden mittels <a href="https://github.com/sebastianbergmann/phpunit/">PHPUnit</a> laufend von mir und automatisiert via <a href="https://travis-ci.org/">Travis CI</a> getestet. Abgesehen von SilexMarkdown beträgt die Code-Coverage zwischen 70 - 90%.</p>
<h2 id="style"><a href="#style" class="heading-anchor">Style</a></h2>
<p>Aktuell ist CodrPress mit dem vollständigen <a href="https://getbootstrap.com/">Twitter Bootstrap</a> versehen, um auch in der Entwicklungsphase ein halbwegs ansehnliches Design zu haben. Später werde ich nur ein paar Komponenten aus Bootstrap nutzen, z.B. das Grid und die responsiven Fähigkeiten.</p>
<p>Für das Syntax-Highlighting habe ich ein Farb-Theme basierend auf meinem Farbschema von PhpStorm geschrieben, das auch Radiant beiliegt. Dank einer recht einfachen Struktur kann man sich auch sehr schnell ein eigenes Theme zusammenstellen.</p>
<h2 id="ausblick"><a href="#ausblick" class="heading-anchor">Ausblick</a></h2>
<p>Die Frontend-Funktionen von CodrPress sind mit einer gefüllten Datenbank (Schnitzelpress-kompatibel) schon nutzbar. Homepage, Einzeldarstellung von Einträgen, eigene Seiten und das Markdown-Rendering mit Syntax-Highlighting funktionieren soweit einwandfrei.</p>
<p>Als nächstes werde ich mich einem Admin-Bereich und anschließend einem ansprechenden Design widmen.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
