<?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/topics/web-development/</link>
    <description>Recent content on webcodr</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 31 Oct 2020 22:54:24 +0000</lastBuildDate>
    <atom:link href="https://webcodr.io/topics/web-development/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>webcodr goes Netlify CMS</title>
      <link>https://webcodr.io/2020/10/webcodr-goes-netlify-cms/</link>
      <pubDate>Sat, 31 Oct 2020 22:54:24 +0000</pubDate>
      <guid>https://webcodr.io/2020/10/webcodr-goes-netlify-cms/</guid>
      <description>Until today posts on webcodr were published via a simple git-based workflow. If I wanted to create a new post, I had to open the repository in Visual Studio Code and create a markdown file. After pushing the commit with the new file, a GitHub hook notified Netlify to pull the repo and build and publish the site.
It was quite simple and effective, but lacks comfort and does not work on iOS/iPadOS devices. After buying a new iPad Air and Magic Keyboard, I wanted a pragmatic way to write posts without my MacBook or PC.
</description>
      <content:encoded><![CDATA[<p>Until today posts on webcodr were published via a simple git-based workflow. If I wanted to create a new post, I had to open the repository in Visual Studio Code and create a markdown file. After pushing the commit with the new file, a GitHub hook notified Netlify to pull the repo and build and publish the site.</p>
<p>It was quite simple and effective, but lacks comfort and does not work on iOS/iPadOS devices. After buying a new iPad Air and Magic Keyboard, I wanted a pragmatic way to write posts without my MacBook or PC.</p>
<p>I always wanted to try Netlify CMS, so this was my chance. The transition was really simple. I followed the guide for Hugo-based sites and adjusted the config to work with Hugo. That’s it. Netlify CMS works just fine with the already existing markdown files. Even custom frontmatter fields within the markdown files are no problem, just set them up in the config file.</p>
<p>A little more configuration in the Netlify admin panel is necessary, but the guide explains everything very well. It was just a matter of 15 minutes to get everything going.</p>
<p>If you use a static site generator and want to have a little more comfort, Netlify CMS makes it really simple. They provide guides for every major player like Gatsby, Jekyll or Nuxt and of course Hugo. You don’t even have to use GitHub. GitLab and BitBucket are supported as well. As are more complex workflows for more than one editor, custom authentication with OAuth or custom media libraries.</p>
<h2 id="editor"><a href="#editor" class="heading-anchor">Editor</a></h2>
<p>Netlify CMS supports markdown and has a basic, but decent editor with rich-text mode. But I wanted a little bit more, so I decided to write my posts in Ulysses — a specialized writing app with support of GitHub-flavored markdown, including syntax highlighting preview. It’s available for macOS and iOS/iPadOS. All files and settings are synched via iCloud. So, once you have setup everything, you’re good to go on any of your Apple devices.</p>
<p>Since Ulysses requires a subscription, I will use this to „force“ me writing more posts. 😁</p>
<p>I wrote this post entirely on my iPad Air and so far, I’m quite happy with the new workflow. Of course I will not write every post this way. New posts with code examples will be easier to handle on a Mac or PC. (Hey Apple, how about IntelliJ on an iPad?)</p>
<p>btw: even as an enthusiast of mechanical keyboards I have to say, it’s quite nice to type on a Magic Keyboard. I just have to get used to the smaller size. The Magic Keyboard for the 10.9“ iPad Air or 11“ iPad Pro is a compromise in size and some keys like tab, shift, backspace, enter and the umlaut keys (bracket keys on English keyboard layouts) are way smaller compared to normal-sized keyboards. Its bigger brother for the 13“ iPad Pro has a normal layout, but that monster of an iPad seems a bit excessive for my use case.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hello, Dark Mode</title>
      <link>https://webcodr.io/2019/09/hello-dark-mode/</link>
      <pubDate>Wed, 25 Sep 2019 22:20:12 +0200</pubDate>
      <guid>https://webcodr.io/2019/09/hello-dark-mode/</guid>
      <description>Dark mode for Android and iOS? Hold my beer …
It’s quite simple to implement. Every modern browser can evaluate media queries in JavaScript with window.matchMedia() and supports CSS variables.
I added the following to my application JavaScript file:
const preferColorSchemeResult = window.matchMedia(&#39;(prefers-color-scheme: dark)&#39;) if (preferColorSchemeResult &amp;&amp; preferColorSchemeResult.matches === true) { document.documentElement.setAttribute(&#39;data-theme&#39;, &#39;dark&#39;) } else { document.documentElement.setAttribute(&#39;data-theme&#39;, &#39;light&#39;) } The script will set the data attribute theme on the document element (html) with the possible values dark or light depending on the result of the media query.
</description>
      <content:encoded><![CDATA[<p>Dark mode for Android and iOS? Hold my beer &hellip;</p>
<p>It&rsquo;s quite simple to implement. Every modern browser can evaluate media queries in JavaScript with
<code>window.matchMedia()</code> and supports CSS variables.</p>
<p>I added the following to my application JavaScript file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">preferColorSchemeResult</span>
</span></span><span class="line"><span class="cl">  <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span><span class="p">(</span><span class="s1">&#39;(prefers-color-scheme: dark)&#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">if</span> <span class="p">(</span><span class="nx">preferColorSchemeResult</span> <span class="o">&amp;&amp;</span> <span class="nx">preferColorSchemeResult</span><span class="p">.</span><span class="nx">matches</span> <span class="o">===</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">&#39;data-theme&#39;</span><span class="p">,</span> <span class="s1">&#39;dark&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">&#39;data-theme&#39;</span><span class="p">,</span> <span class="s1">&#39;light&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The script will set the data attribute <code>theme</code> on the document element (html) with the possible values
<code>dark</code> or <code>light</code> depending on the result of the media query.</p>
<p>There&rsquo;s no need for a polyfill, even IE 10 supports <code>window.matchMedia()</code>.</p>
<p>The stylesheet changes are even simpler, since I already had introduced SCSS color variables a while ago. I just
had to replace them with CSS variables.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="c1">// colors
</span></span></span><span class="line"><span class="cl"><span class="nv">$c_white</span><span class="o">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$c_dark-grey</span><span class="o">:</span> <span class="mh">#4A4A4A</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">:root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="na">--container-background-color</span><span class="o">:</span> <span class="si">#{</span><span class="nv">$c_white</span><span class="si">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nc">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">&#34;dark&#34;</span><span class="o">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="na">--container-background-color</span><span class="o">:</span> <span class="si">#{</span><span class="nf">darken</span><span class="p">(</span><span class="nv">$c_dark-grey</span><span class="o">,</span> <span class="mi">20</span><span class="kt">%</span><span class="p">)</span><span class="si">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nc">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s basically it. If you use SCSS, please take notice to use interpolations to map the SCSS
variables to CSS variables. This change in SassScript expressions was necessary to provide full
compatibility with plain CSS.</p>
<p>Since the theme selection is fully automated, I will provide a toggle possibility in a future
release for those of you who prefer the light mode. This can be easily achieved with a flag in local
storage and some minor changes in the JavaScript part.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing DeliveryGuy</title>
      <link>https://webcodr.io/2018/04/introducing-deliveryguy/</link>
      <pubDate>Fri, 27 Apr 2018 23:10:16 +0200</pubDate>
      <guid>https://webcodr.io/2018/04/introducing-deliveryguy/</guid>
      <description>I like the Fetch API. It’s supported by all modern browsers, easy to use and has some really good polyfills for older devices. But Fetch has one major flaw: it will only throw errors if there is a network problem.
That’s a really odd decision from my point of view. Nearly any HTTP library out there throws errors or rejects the promise in case of a HTTP error.
A Fetch response object has the property ok to determine if the server responded with an error, but that’s not very comfortable to use.
</description>
      <content:encoded><![CDATA[<p>I like the Fetch API. It&rsquo;s supported by all modern browsers, easy to use and has some really good polyfills for older devices. But Fetch has one major flaw: it will only throw errors if there is a network problem.</p>
<p>That&rsquo;s a really odd decision from my point of view. Nearly any HTTP library out there throws errors or rejects the promise in case of a HTTP error.</p>
<p>A Fetch response object has the property <code>ok</code> to determine if the server responded with an error, but that&rsquo;s not very comfortable to use.</p>
<p>Since my team and I have decided to use Fetch in a Vue-based web app, I decided to create a little wrapper for much more convenience. Say hello to DeliveryGuy.</p>
<h2 id="usage"><a href="#usage" class="heading-anchor">Usage</a></h2>
<p>Well, surprise, it&rsquo;s a Node module, so just use any package manager you like. My personal choice is yarn.</p>
<p><code>yarn install delivery-guy</code></p>
<h3 id="example"><a href="#example" class="heading-anchor">Example</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">deliverJson</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;delivery-guy&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">getItems</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">items</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">deliverJson</span><span class="p">(</span><span class="s1">&#39;/api/items&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">items</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;HTTP Status&#39;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Response Body&#39;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">responseBody</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><h3 id="whats-going-on-here"><a href="#whats-going-on-here" class="heading-anchor">What&rsquo;s going on here?</a></h3>
<p>DeliveryGuy exports two main functions:</p>
<ul>
<li><code>deliver()</code> will return a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">response promise</a> like <code>fetch()</code> does.</li>
<li><code>deliverJson()</code> presumes your response body contains JSON. It&rsquo;s basically a shortcut and returns the promise of <code>Response.json()</code>.</li>
</ul>
<p>Both will accept the same two parameters as <code>fetch()</code> does and pass them along.</p>
<p>If the server responds with a HTTP error, DeliveryGuy will throw an error.</p>
<p>Due to the inheritance limitations of built-in classes with ES5 I mentioned in my <a href="/2018/04/why-custom-errors-in-javascript-with-babel-are-broken/">last post</a>, it&rsquo;s only possible to set custom properties of a custom error class.</p>
<p>DeliveryGuy provides two additional properties on an error object:</p>
<ul>
<li><code>response</code> has the original response object of a Fetch call.</li>
<li><code>responseBody</code> contains the response body and will try to parse it as JSON. If <code>JSON.parse</code> fails, it will return the response body in its original state.</li>
</ul>
<h2 id="tldr"><a href="#tldr" class="heading-anchor">TL;DR</a></h2>
<p>DeliveryGuy allows you to comfortably call the Fetch API without a hassle on HTTP errors. Just use <code>try/catch</code> and you&rsquo;re done.</p>
<p>Please let me know on <a href="https://github.com/WebCodr/delivery-guy">GitHub</a> if you have feedback, a feature request or found a bug. Thank you!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Vue Loader Setup in Webpack</title>
      <link>https://webcodr.io/2018/04/vue-loader-setup-in-webpack/</link>
      <pubDate>Tue, 24 Apr 2018 16:21:49 +0200</pubDate>
      <guid>https://webcodr.io/2018/04/vue-loader-setup-in-webpack/</guid>
      <description>Since it’s no option to use Vue CLI in my current project, I had to manually add vue-loader to the Webpack config. Well, I was positively surprised how simple it was. Especially compared to a manual set-up of Angular 2 shortly after its launch in late 2016, while Angular CLI was an alpha version and buggy as hell.
Shall we begin? Modules Let’s add the necessary modules to the package:
</description>
      <content:encoded><![CDATA[<p>Since it&rsquo;s no option to use Vue CLI in my current project, I had to manually add <code>vue-loader</code> to the Webpack
config. Well, I was positively surprised how simple it was. Especially compared to a manual set-up of Angular 2 shortly
after its launch in late 2016, while Angular CLI was an alpha version and buggy as hell.</p>
<h2 id="shall-we-begin"><a href="#shall-we-begin" class="heading-anchor">Shall we begin?</a></h2>
<h3 id="modules"><a href="#modules" class="heading-anchor">Modules</a></h3>
<p>Let&rsquo;s add the necessary modules to the package:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">yarn add --dev vue-loader vue-template-compiler
</span></span><span class="line"><span class="cl">yarn add vue
</span></span></code></pre></div><p>If you want to use a template engine like Pug or prefer TypeScript over JavaScript, you can add the respective
Webpack loader package, <code>pug-loader</code> for example. Webpack will also tell you in detail, if modules are missing.</p>
<h3 id="webpack"><a href="#webpack" class="heading-anchor">Webpack</a></h3>
<p>Just add the following rule to your Webpack config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.vue$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">use</span><span class="o">:</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="nx">loader</span><span class="o">:</span> <span class="s1">&#39;vue-loader&#39;</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><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Webpack will now be able to use import statements with Vue single file components.</p>
<p>If you want to have a separate JavaScript file with your Vue application, you can add a new entry point:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">entry</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;current-application&#39;</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;js/current-application.js&#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="s1">&#39;vue-application&#39;</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;js/vue-application.js&#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><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Not fancy enough? How about vendor chunks?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">optimization</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">splitChunks</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">chunks</span><span class="o">:</span> <span class="s1">&#39;all&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">cacheGroups</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">vendor</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">chunks</span><span class="o">:</span> <span class="s1">&#39;initial&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">test</span><span class="o">:</span> <span class="sr">/node_modules\/(?!(vue|vue-resource)\/).*/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;vendor&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">enforce</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;vue-vendor&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">chunks</span><span class="o">:</span> <span class="s1">&#39;initial&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">test</span><span class="o">:</span> <span class="sr">/node_modules\/(vue|vue-resource)\/.*/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;vue-vendor&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">enforce</span><span class="o">:</span> <span class="kc">true</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><span class="line"><span class="cl">    <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>This will separate your vendor files from <code>node_modules</code> into <code>vendor.js</code> and <code>vue-vendor.js</code>. The property <code>test</code> contains
a regex to determine which modules should go into the vendor chunks.</p>
<p>Of course, this comes not even close to what Vue CLI can do. I highly recommend to use Vue CLI when it&rsquo;s feasible. It&rsquo;s
quite easy to configure while not being dumbed down, has an excellent documentation and is very well maintained. At least
for our customer&rsquo;s set-up it would be difficult to use Vue CLI at the moment, but we are eager to migrate to Vue CLI asap.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Let&#39;s Encrypt Wildcard Certificates with acme.sh and CloudFlare</title>
      <link>https://webcodr.io/2018/04/lets-encrypt-wildcard-certificates-with-acme.sh-and-cloudflare/</link>
      <pubDate>Thu, 19 Apr 2018 20:00:00 +0200</pubDate>
      <guid>https://webcodr.io/2018/04/lets-encrypt-wildcard-certificates-with-acme.sh-and-cloudflare/</guid>
      <description>A few weeks ago Let’s Encrypt finally launched ACME 2.0 with support of wildcard certificates. Woohoo!
Wait, what are wildcard certificates? Wildcard certificates allow you to use multiple hostnames of your domain with one certificate. Without them you need a separate certificate for each host like foo.webcodr.io and bar.webcodr.io. A wildcard certificate can be issued for *.webcodr.io and that’s it. One certificate to rule them all.
Get started My nginx example used certbot to issue certificates from Let’s Encrypt, but there’s a better tool: acme.sh
</description>
      <content:encoded><![CDATA[<p>A few weeks ago Let&rsquo;s Encrypt finally launched ACME 2.0 with support of wildcard certificates. Woohoo!</p>
<h2 id="wait-what-are-wildcard-certificates"><a href="#wait-what-are-wildcard-certificates" class="heading-anchor">Wait, what are wildcard certificates?</a></h2>
<p>Wildcard certificates allow you to use multiple hostnames of your domain with one certificate. Without them you need a
separate certificate for each host like <code>foo.webcodr.io</code> and <code>bar.webcodr.io</code>. A wildcard certificate can be issued for
<code>*.webcodr.io</code> and that&rsquo;s it. One certificate to rule them all.</p>
<h2 id="get-started"><a href="#get-started" class="heading-anchor">Get started</a></h2>
<p>My nginx example used certbot to issue certificates from Let&rsquo;s Encrypt, but there&rsquo;s a better tool: <a href="https://github.com/Neilpang/acme.sh">acme.sh</a></p>
<p>Acme.sh is written in Shell and can run on any unix-like OS. Since it&rsquo;s also installed with a Shell script, there&rsquo;s no need for a maintained package to get the latest features. Just run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">curl https://get.acme.sh <span class="p">|</span> sh
</span></span></code></pre></div><p>That&rsquo;s it. The install script will copy acme.sh to your home directory, create an alias for terminal use and create a cron job to automatically renew certificates.</p>
<h2 id="dns-challenge"><a href="#dns-challenge" class="heading-anchor">DNS challenge</a></h2>
<p>To issue a wildcard certificate ACME 2.0 allows only DNS-based challenges to verify your domain ownership. You can manage this manually, but challenge tokens will only work for 60 days, so you have to renew it every time a certificate expires.</p>
<p>Well, that sucks. But acme.sh has you covered. It supports the APIs of many DNS providers like CloudFlare, GoDaddy etc.</p>
<p>The following guide will show you how to use the CloudFlare API to automatically update the DNS challenge token. No CloudFlare? No problem, you can find examples for all supported DNS providers within the acme.sh docs.</p>
<h2 id="set-up-cloudflare"><a href="#set-up-cloudflare" class="heading-anchor">Set-up CloudFlare</a></h2>
<p>Login to CloudFlare and go to your profile. You&rsquo;ll need the global API key.</p>
<p>Set your CloudFlare API key and your account email address as environment variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CF_Key</span><span class="o">=</span><span class="s2">&#34;sdfsdfsdfljlbjkljlkjsdfoiwje&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CF_Email</span><span class="o">=</span><span class="s2">&#34;you@example.com&#34;</span>
</span></span></code></pre></div><p>I recommend to put these environment variables into your <code>.bashrc</code>, <code>.zshrc</code> or in the respective file of your favorite shell.</p>
<h2 id="issue-a-wildcard-certificate"><a href="#issue-a-wildcard-certificate" class="heading-anchor">Issue a wildcard certificate</a></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">acme.sh --issue --dns dns_cf -d <span class="s2">&#34;*.webcodr.io&#34;</span> -w <span class="s2">&#34;/what/ever/dir/you/like/*.webcodr.io&#34;</span>
</span></span></code></pre></div><p>Your new certificate will be ready soon and acme.sh will automatically renew it every 60 days. Just update your web-server configuration to the new path. I recommend also to create a cron-job reloading the web-server every night to load a renewed certificate.</p>
<h2 id="unclutter-your-nginx-config"><a href="#unclutter-your-nginx-config" class="heading-anchor">Unclutter your nginx config</a></h2>
<p>If you manage multiple hosts within the same nginx, you can use <code>include</code> to put your TLS configuration in a separate file to avoid duplicates.</p>
<h3 id="create-a-separate-file-for-your-tls-configuration"><a href="#create-a-separate-file-for-your-tls-configuration" class="heading-anchor">Create a separate file for your TLS configuration</a></h3>
<p>File: <code>/etc/nginx/tls-webcodr.io</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">ssl_certificate</span> <span class="s">/home/webcodr/.acme.sh/*.webcodr.io/*.webcodr.io.cer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_certificate_key</span> <span class="s">/home/webcodr/.acme.sh/*.webcodr.io/*.webcodr.io.key</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_trusted_certificate</span> <span class="s">/home/webcodr/.acme.sh/*.webcodr.io/ca.cer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_session_timeout</span> <span class="s">1d</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_session_cache</span> <span class="s">shared:SSL:50m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_session_tickets</span> <span class="no">off</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_protocols</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_ciphers</span> <span class="s">ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_ecdh_curve</span> <span class="s">secp384r1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_prefer_server_ciphers</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">add_header</span> <span class="s">Strict-Transport-Security</span> <span class="s">max-age=15768000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_stapling</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">ssl_stapling_verify</span> <span class="no">on</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="update-your-site-configuration"><a href="#update-your-site-configuration" class="heading-anchor">Update your site configuration</a></h3>
<p>File: <code>/etc/nginx/sites-enabled/webcodr.io</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kn">server_name</span> <span class="s">webcodr.io</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">proxy_pass</span> <span class="s">http://10.0.0.2:80</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><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span> <span class="s">http2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kn">server_name</span> <span class="s">webcodr.io</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kn">ssl</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">include</span> <span class="s">tls-webcodr.io</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">proxy_pass</span> <span class="s">https://10.0.0.2:443</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>Repeat this for every site on a host on this domain and reload nginx. And you&rsquo;re done. Just one certificate and TLS config for all your sites. Pretty neat, huh?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Webpack 2 with Pug and SASS</title>
      <link>https://webcodr.io/2017/01/webpack-2-with-pug-and-sass/</link>
      <pubDate>Sun, 08 Jan 2017 20:00:00 +0100</pubDate>
      <guid>https://webcodr.io/2017/01/webpack-2-with-pug-and-sass/</guid>
      <description>Until now I used some Gulp tasks with Jade and SASS to create the HTML and CSS files as a base for the webcodr blog theme. A crude little setup, but it worked.
I decided to write a replacement with Webpack 2 and to share it with you: WebCodr/design GitHub repo
First steps The original setup used the indented SASS syntax and Jade templates. Since the indented syntax is more trouble than it’s worth, I had to convert to the SCSS syntax, a superset of the CSS syntax.
</description>
      <content:encoded><![CDATA[<p>Until now I used some Gulp tasks with Jade and SASS to create the HTML and CSS files as a base for the webcodr blog theme. A crude little setup, but it worked.</p>
<p>I decided to write a replacement with Webpack 2 and to share it with you: <a href="https://github.com/WebCodr/design">WebCodr/design GitHub repo</a></p>
<h2 id="first-steps"><a href="#first-steps" class="heading-anchor">First steps</a></h2>
<p>The original setup used the indented SASS syntax and Jade templates. Since the indented syntax is more trouble than it&rsquo;s worth, I had to convert to the SCSS syntax, a superset of the CSS syntax.</p>
<p>Unfortunately there was a copyright claim for &ldquo;Jade&rdquo; and they had to rename the project to Pug. The Jade Node module is still available but unmaintained. It&rsquo;s time to use Pug.</p>
<h3 id="sass-to-scss"><a href="#sass-to-scss" class="heading-anchor">SASS to SCSS</a></h3>
<p>Don&rsquo;t worry, that&rsquo;s quite simple. The SASS people provide a tool called <code>sass-convert</code> and it&rsquo;s really easy to use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sass-convert --from<span class="o">=</span>sass --to<span class="o">=</span>scss -R assets/styles/
</span></span></code></pre></div><p>Just pass the tool the format you want to convert, the new format, <code>-R</code> for recursive conversion and add the directory name. After <code>sass-convert</code> finished, you find the new SCSS files along with their SASS predecessors. You can now delete the old SASS files, adjust your build tool and you&rsquo;re done.</p>
<h3 id="jade-to-pug"><a href="#jade-to-pug" class="heading-anchor">Jade to Pug</a></h3>
<p>It depends on your templates how easy or complicated this step is. My template files are rather simple, so just renaming the file extensions from <code>.jade</code> to <code>.pug</code> and using Pug instead of Jade did the job. If you are having trouble, there is a <a href="https://pugjs.org/api/migration-v2.html">migration guide from Jade to Pug</a>.</p>
<h2 id="using-webpack-2"><a href="#using-webpack-2" class="heading-anchor">Using Webpack 2</a></h2>
<p>Webpack 2 isn&rsquo;t finished yet. The current version is 2.2.0 RC3, but even the Beta versions were stable enough to use it for production purposes.</p>
<p>Here&rsquo;s my <code>package.json</code> file with all necessary modules:</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;name&#34;</span><span class="p">:</span> <span class="s2">&#34;WebCodr_Design&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;server&#34;</span><span class="p">:</span> <span class="s2">&#34;webpack-dev-server&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;webpack --config webpack.config.prod.babel.js&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;autoprefixer&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.6.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;babel-core&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.21.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;babel-preset-es2015&#34;</span><span class="p">:</span> <span class="s2">&#34;^6.18.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;css-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^0.26.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;csso-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^0.2.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;extract-text-webpack-plugin&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.0.0-beta.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;html-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^0.4.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;node-sass&#34;</span><span class="p">:</span> <span class="s2">&#34;^4.1.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;postcss-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.2.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pug&#34;</span><span class="p">:</span> <span class="s2">&#34;^2.0.0-beta6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pug-html-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.0.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sanitize.css&#34;</span><span class="p">:</span> <span class="s2">&#34;^4.1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sass-loader&#34;</span><span class="p">:</span> <span class="s2">&#34;^4.1.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;webpack&#34;</span><span class="p">:</span> <span class="s2">&#34;2.2.0-rc.3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;webpack-dev-server&#34;</span><span class="p">:</span> <span class="s2">&#34;2.2.0-rc.0&#34;</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>I wrote two little helper scripts to run a dev server on port <code>8080</code> and to build a production version of the assets, mainly the CSS file. Currently there is no JavaScript file for webcodr. But it would be really easy to add JavaScript to the Webpack config.</p>
<h3 id="the-webpack-config-or-where-the-magic-happens"><a href="#the-webpack-config-or-where-the-magic-happens" class="heading-anchor">The Webpack config or where the magic happens</a></h3>
<p>I can&rsquo;t stand the old JavaScript syntax anymore, so I write all my stuff in ES2015 and that&rsquo;s why Babel is present in the dev dependencies.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">webpack</span> <span class="nx">from</span> <span class="s1">&#39;webpack&#39;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">path</span> <span class="nx">from</span> <span class="s1">&#39;path&#39;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">autoprefixer</span> <span class="nx">from</span> <span class="s1">&#39;autoprefixer&#39;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">ExtractTextPlugin</span> <span class="nx">from</span> <span class="s1">&#39;extract-text-webpack-plugin&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">extractStyles</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ExtractTextPlugin</span><span class="p">(</span><span class="s1">&#39;[name].css&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">extractHtml</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ExtractTextPlugin</span><span class="p">(</span><span class="s1">&#39;[name].html&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">stats</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">assets</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">colors</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">version</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">hash</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">timings</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chunks</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chunkModules</span><span class="o">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">entry</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">index</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;templates/index.pug&#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="nx">post</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;templates/post.pug&#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="s1">&#39;css/application&#39;</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;assets/styles/application.scss&#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><span class="line"><span class="cl">  <span class="nx">output</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;build&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">filename</span><span class="o">:</span> <span class="s1">&#39;[name].js&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">module</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">rules</span><span class="o">:</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="nx">test</span><span class="o">:</span> <span class="sr">/\.pug$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loader</span><span class="o">:</span> <span class="nx">extractHtml</span><span class="p">.</span><span class="nx">extract</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">loader</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;html-loader&#39;</span><span class="p">,</span> <span class="s1">&#39;pug-html-loader?pretty&amp;exports=false&#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><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.scss$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loader</span><span class="o">:</span> <span class="nx">extractStyles</span><span class="p">.</span><span class="nx">extract</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">loader</span><span class="o">:</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="nx">loader</span><span class="o">:</span> <span class="s1">&#39;css-loader&#39;</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><span class="line"><span class="cl">              <span class="nx">loader</span><span class="o">:</span> <span class="s1">&#39;postcss-loader&#39;</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><span class="line"><span class="cl">              <span class="nx">loader</span><span class="o">:</span> <span class="s1">&#39;sass-loader&#39;</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><span class="line"><span class="cl">        <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><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="nx">webpack</span><span class="p">.</span><span class="nx">LoaderOptionsPlugin</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">minimize</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">debug</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">postcss</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="nx">autoprefixer</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">browsers</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;last 2 version&#39;</span><span class="p">,</span> <span class="s1">&#39;Explorer &gt;= 10&#39;</span><span class="p">,</span> <span class="s1">&#39;Android &gt;= 4&#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><span class="line"><span class="cl">        <span class="nx">sassLoader</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">includePaths</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">&#39;node_modules/sanitize.css/&#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><span class="line"><span class="cl">      <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="nx">extractStyles</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">extractHtml</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><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">config</span>
</span></span></code></pre></div><p>Explanations:</p>
<ul>
<li>
<p><code>stats</code>: Webpack 2 is really verbose, this options will help to make it shut up. ;)</p>
</li>
<li>
<p><code>entry</code>: I defined three entry points for Webpack. Two for Pug files and one for my SCSS file. The property name is the filename for saving the finished file and the array item points to the source file.</p>
</li>
<li>
<p><code>output</code>: Defines where to put the files and how to name them.</p>
</li>
<li>
<p><code>module</code>: The module rules tell Webpack what to do with different file extensions. Since Webpack 2 is a JavaScript module bundler it creates JavaScript files and we have to use a plug-in to get HTML and CSS files. The extract text plug-in looks for the content of the defined loaders and extracts it into the file you want.</p>
<p>To convert Pug into HTML I had to use the Pug HTML loader and the HTML loader. The extract text plug-in now knows what to extract of the generated JavaScript files.</p>
<p>The same goes for SASS. The SASS loader converts SASS into CSS, after that the PostCSS loader applies the Autoprefixer and finally the CSS loader tells the extract text plug-in what to do.</p>
</li>
<li>
<p><code>plugins</code>: <code>webpack.LoaderOptionsPlugin</code> contains the loader config for Webpack 1 loaders. Webpack 2 compatible loaders can be configured directly with the loader definition. As you can see, PostCSS loader and SASS loader are not compatible with Webpack 2 yet.</p>
<p>After the configuration of the legacy loaders, I just had to add the extraction plug-in instances and that&rsquo;s it.</p>
</li>
</ul>
<p>When Webpack is started, it will iterate through the entry points and apply the appropriate rules based on the regular expressions in the property <code>test</code>. Each rule applies the loaders in reverse order, so the last loader will be applied first. The loaders themselves obtain their configs from the query as the Pug HTML loader does or from the loader options plug-in, only the extract text plug-in is an exception as it needs two configurations: the loaders and where to put the extracted content.</p>
<p>After Webpack is finished, the files are located in the output directory. There are two files for each entry point: a HTML or CSS file and a JavaScript file. As mentioned above, Webpack 2 is a JavaScript module bundler and can only create JavaScript files without help from plug-ins. Without extraction, these JavaScript files would contain the HTML or CSS code as a Webpack runtime module and could be used within JavaScript.</p>
<p>You could now open one of the HTML files in your browser, but there&rsquo;s a better way. You would have to manually start Webpack for each change to make &hellip; meh.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn run server
</span></span></code></pre></div><p>This will start the Webpack Dev Server on Port <code>8080</code>. It watches for changes and re-builds all affected files. You just have to reload the browser tab. Not comfy enough? There is a possibility of auto-reloading and even hot JavaScript module replacement which will be explored in a future blog post.</p>
<h3 id="adding-javascript-support"><a href="#adding-javascript-support" class="heading-anchor">Adding JavaScript support</a></h3>
<p>What do you think? Is it complicated to create JavaScript files with a JavaScript module bundler? Of course not!</p>
<p>Add the following rule to your config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.js$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">exclude</span><span class="o">:</span> <span class="sr">/node_modules\/*/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">use</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;babel-loader&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">presets</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">[</span><span class="s1">&#39;es2015&#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><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Any entry point with JavaScript files will be processed with Babel for ES2015 support. Pretty neat, huh?</p>
<p>Need ES2016 or ES2017? No problem, just add a suitable Babel preset with npm or yarn to your project and adjust the config.</p>
<p>You&rsquo;re using TypeScript or CoffeeScript? No problem, just look for a compatible loader.</p>
<h2 id="conclusion"><a href="#conclusion" class="heading-anchor">Conclusion</a></h2>
<p>Some say Webpack is too complicated. Well, it can be quite confusing if you&rsquo;re just using it without knowledge what it really does, but it&rsquo;s no rocket science.</p>
<p>I used Webpack to replace a rather large and complex collection of Gulp tasks to build the assets for the website of a customer. For certain things like SVG sprite maps I still use Gulp, but all other tasks are done by Webpack faster and way more easy, especially things like hashes in filenames for production purposes or automatically splitting the main JavaScript file into an application file and a vendor file with certain libraries/frameworks like AngularJS.</p>
<p>The same goes for Karma to run the Jasmine tests. Just add the Webpack plug-in for Karma, make some small adjustments to the Webpack config, let Webpack handle the rest and you&rsquo;re done.</p>
<p>Compared to the old Gulp tasks, the Webpack setup is easier to understand, faster and much more fun to use. New team members do not have to dig into a bunch of Gulp tasks and related helper methods. A quick introduction to the Webpack config is mostly enough to understand how it works.</p>
<p>If you have about 1,000 lines of code with Gulp tasks and helper functions or some really small tasks and about 150 lines of Webpack config, what do you prefer?</p>
<h2 id="tldr"><a href="#tldr" class="heading-anchor">TL;DR</a></h2>
<p>Use Webpack 2, it&rsquo;s awesome. :P</p>
]]></content:encoded>
    </item>
    
    <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>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>
    
    <item>
      <title>Responsive Bilder mit WordPress</title>
      <link>https://webcodr.io/2012/08/responsive-bilder-mit-wordpress/</link>
      <pubDate>Mon, 13 Aug 2012 22:23:38 +0100</pubDate>
      <guid>https://webcodr.io/2012/08/responsive-bilder-mit-wordpress/</guid>
      <description>Aktuell wage ich erste Gehversuche mit responsiven Layouts in meinem WordPress-Theme. Ziel der Übung ist ein smartphone-taugliches Layout. Leider macht einem WordPress die Arbeit bei Bildern unnötig schwer.
Automatische Bildskalierung mit CSS Bilder müssen in responsiven Layouts automatisch mit der Breite des Anzeigegeräts skalieren. Feste Breiten würden hier zwangsläufig zu Darstellungsproblemen führen. Das klingt nun komplizierter als es ist. Mit drei Zeilen CSS lassen sich Bilder abhängig von der Breite ihres Eltern-Elements automatisch skalieren.
</description>
      <content:encoded><![CDATA[<p>Aktuell wage ich erste Gehversuche mit responsiven Layouts in meinem <a href="https://web.archive.org/web/20131210084100/https://github.com/MadCatme/mcw-blue">WordPress-Theme</a>. Ziel der Übung ist ein smartphone-taugliches Layout. Leider macht einem WordPress die Arbeit bei Bildern unnötig schwer.</p>
<h2 id="automatische-bildskalierung-mit-css"><a href="#automatische-bildskalierung-mit-css" class="heading-anchor">Automatische Bildskalierung mit CSS</a></h2>
<p>Bilder müssen in responsiven Layouts automatisch mit der Breite des Anzeigegeräts skalieren. Feste Breiten würden hier zwangsläufig zu Darstellungsproblemen führen. Das klingt nun komplizierter als es ist. Mit drei Zeilen CSS lassen sich Bilder abhängig von der Breite ihres Eltern-Elements automatisch skalieren.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">max-width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Ein Bild darf also maximal so breit werden, wie seine vorgesehene Weite. Schrumpft das Eltern-Element durch Verkleinern des Viewports, wird das Bild automatisch mitskaliert. Wir müssen uns also um nichts weiter kümmern, da die Browser den Rest erledigen.</p>
<h2 id="problemfall-wordpress"><a href="#problemfall-wordpress" class="heading-anchor">Problemfall WordPress</a></h2>
<p>Leider klappt die automatische Skalierung in WordPress nicht. Wenn man in einem Beitrag Bilder einfügt, setzt WordPress automatisch das <code>width</code>- bzw. <code>height</code>-Attribut auf das <code>img</code>-Element. Sobald auch nur eines von beiden gesetzt ist, wird eine automatische Größenanpassung verhindert. Die Attribute müssen also weg.</p>
<p>Wie immer, gibt es dafür zig verschiedene Möglichkeiten. Beispielsweise könnte man die überflüssigen Element-Eigenschaften per jQuery-Einzeiler entfernen. Wirklich schön ist das aber nicht. Es wäre doch viel besser, wenn man WordPress dazu bringen könnte, den Quelltext gleich ohne <code>width</code> und <code>height</code> auszuliefern.</p>
<p>Dank des Hook-Systems in WordPress geht das mit ein paar Zeilen Code in der Datei <code>functions.php</code> des Themes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">add_filter</span><span class="p">(</span><span class="s1">&#39;the_content&#39;</span><span class="p">,</span> <span class="s1">&#39;removeImageDimensions&#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">function</span> <span class="nf">removeImageDimensions</span><span class="p">(</span><span class="nv">$html</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">preg_replace</span><span class="p">(</span><span class="s1">&#39;/(width|height)=\&#34;\d*\&#34;\s/&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$html</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Die Funktion <code>removeImageDimensions()</code> entfernt per regulärem Ausdruck unsere unerwünschten Gäste <code>width</code> und <code>height</code>. Mittels <code>add_filter()</code> wird der WordPress-Funktion <code>the_content()</code> (gibt den Inhalt eines Beitrags aus) unsere neue Funktion als Ausgabefilter zugewiesen. WordPress führt nun bei jedem Aufruf von <code>the_content()</code> unsere neue Funktion <code>removeImageDimensions()</code> aus, die den Rückgabewert von <code>the_content()</code> entsprechend verändert.</p>
<p>Damit steht responsiven Bildbreiten nun nichts mehr im Weg.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Retina-Display-taugliche Icons mit CSS-Hintergrundbildern</title>
      <link>https://webcodr.io/2012/08/retina-display-taugliche-icons-mit-css-hintergrundbildern/</link>
      <pubDate>Sun, 12 Aug 2012 22:22:40 +0100</pubDate>
      <guid>https://webcodr.io/2012/08/retina-display-taugliche-icons-mit-css-hintergrundbildern/</guid>
      <description>Besitzer von Apple-Geräten mit Retina-Displays kennen das Dilemma: auf vielen Seiten sehen Hintergrundbilder, insbesondere Icons, reichlich unscharf aus. So erging es mir gleich doppelt mit meinem privaten Blog. Dank neuem iPad und MacBook Pro, vermatschen die Icons für externe Links.
Es gibt verschiedene Lösungsansätze für dieses Problem, beispielsweise Icons in vektorbasierten Formaten (SVG), die beliebig in jeder Pixeldichte skalieren können.
Da ich die verwendeten Icons nur als Bilder vorliegen habe, kommt diese Lösung nicht in Betracht. Dank CSS-Media-Queries ist das aber kein großes Problem.
</description>
      <content:encoded><![CDATA[<p>Besitzer von Apple-Geräten mit Retina-Displays kennen das Dilemma: auf vielen Seiten sehen Hintergrundbilder, insbesondere Icons, reichlich unscharf aus. So erging es mir gleich doppelt mit meinem privaten Blog. Dank neuem iPad und MacBook Pro, vermatschen die Icons für externe Links.</p>
<p>Es gibt verschiedene Lösungsansätze für dieses Problem, beispielsweise Icons in vektorbasierten Formaten (SVG), die beliebig in jeder Pixeldichte skalieren können.</p>
<p>Da ich die <a href="http://www.iconarchive.com/show/farm-fresh-icons-by-fatcow.html">verwendeten Icons</a> nur als Bilder vorliegen habe, kommt diese Lösung nicht in Betracht. Dank CSS-Media-Queries ist das aber kein großes Problem.</p>
<h2 id="media-queries"><a href="#media-queries" class="heading-anchor">Media-Queries?</a></h2>
<p>Ein Media-Query ermöglicht es, innerhalb eines Stylesheets diverse Informationen zum aktuellen Anzeigegerät abzufragen. Dazu gehören unter anderem die Minimal-Breite, die Ausrichtung (Portrait, Landscape), der Gerätetyp (Screen, Projection etc.), das Bildverhältnis oder in diesem Fall besonders wichtig, das Verhältnis zwischen vorhandenen und tatsächlich dargestellten Pixeln.</p>
<h2 id="was-machen-eigentlich-retina-displays"><a href="#was-machen-eigentlich-retina-displays" class="heading-anchor">Was machen eigentlich Retina-Displays?</a></h2>
<p>Retina-Displays verdoppeln die Auflösung, während die dargestellten Elemente gleich groß bleiben. Bei normalen Displays ist dieses Verhältnis 1:1. Eine Grafik mit 100 Pixeln Breite wird also mit 100 Pixeln auf dem Monitor dargestellt. Auf Apple-Geräten mit Retina-Displays verdoppeln sich die 100 Pixel auf 200 Pixel &ndash; das Verhältnis beträgt also 2:1.</p>
<p>Ist eine Grafik in der notwendigen Auflösung nicht verfügbar, wird das vorhandene Bild hochgerechnet und wirkt unscharf. Mit einem Media-Query können wir dem Browser eine höher aufgelöste Version zur Verfügung stellen, die genau das verhindert.</p>
<h2 id="an-die-arbeit"><a href="#an-die-arbeit" class="heading-anchor">An die Arbeit</a></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">min--moz-device-pixel-ratio</span><span class="o">:</span> <span class="nt">2</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"><span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">-o-min-device-pixel-ratio</span><span class="o">:</span> <span class="nt">2</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"><span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">-webkit-min-device-pixel-ratio</span><span class="o">:</span> <span class="nt">2</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"><span class="nt">only</span> <span class="nt">screen</span> <span class="nt">and</span> <span class="o">(</span><span class="nt">min-device-pixel-ratio</span><span class="o">:</span> <span class="nt">2</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="p">#</span><span class="nn">content</span> <span class="nt">article</span> <span class="p">.</span><span class="nc">content</span> <span class="nt">p</span> <span class="nt">a</span><span class="p">.</span><span class="nc">external</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">background-image</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="s2">&#34;icons/external-url-32.png&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">background-size</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span> <span class="kc">auto</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>Da die Media-Query-Eigenschaft <code>min-device-pixel-ratio</code> noch nicht vollständig in allen Browsern implementiert ist, setze ich die entsprechenden Präfix-Versionen vorher ein. So kann man sichergehen, dass die proprietäre als auch die standardisierte Eigenschaft greifen &ndash; je nach aktuellem Stand der Browser-Implementation.</p>
<p>Mozilla greift hier zu einer recht seltsamen Präfix-Syntax, während Opera und Webkit sich an das bewährte Schema halten. Ob und wann der Internet Explorer die Eigenschaft unterstützt, konnte ich bisher nicht herausfinden.</p>
<p>Wie oben schon beschrieben, ist das Verhältnis von tatsächlichen Pixeln zu dargestellten Pixeln 2:1, daher wird es in der Bedingung mit einer <code>2</code> angegeben.</p>
<p>Innerhalb des Media-Queries kann ganz normales CSS verwendet werden. Ich tausche nun einfach das bisherige Hintergrundbild (16 x 16 Pixel) durch eine größere Version (32 x 32 Pixel) aus und setze die Größe des Hintergrundbildes auf 16 Pixel. Ansonsten würde der Browser eine falsche Annahme treffen und das Bild auf 64 x 64 Pixel hochrechnen. Damit wäre alles für die sprichwörtliche Katz.</p>
<p>Das war&rsquo;s schon. Sofern man ein passendes Gerät hat und der Browser die Media-Query-Eigenschaft unterstützt, bekommt man nun ein schön hoch aufgelöstes Icon zu sehen, das um Welten besser aussieht als der hochgerechnete Pixelmatsch.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>PHP-Tip: Limonade</title>
      <link>https://webcodr.io/2012/04/php-tip-limonade/</link>
      <pubDate>Wed, 11 Apr 2012 22:17:38 +0100</pubDate>
      <guid>https://webcodr.io/2012/04/php-tip-limonade/</guid>
      <description>Wer sich schon mal mit Symfony oder ähnlichen PHP-Frameworks beschäftigt hat, kam sicher schnell zur Erkenntnis, dass die Dinger zwar viel können und generell eine tolle Sache sind, aber hohe Einstiegshürden haben bzw. viel Einarbeitungszeit benötigen, sowie für viele Projekte einfach überdimensioniert sind.
In Ruby hätte man für solche Fälle z.B. Sinatra: übersichtlich, klein, schnell und flexibel. Mit Limonade gibt es so ein Micro-Framework nun endlich für PHP, mit dem sich auch ähnlich elegant entwickeln lässt.
</description>
      <content:encoded><![CDATA[<p>Wer sich schon mal mit <a href="http://symfony.com/">Symfony</a> oder ähnlichen PHP-Frameworks beschäftigt hat, kam sicher schnell zur Erkenntnis, dass die Dinger zwar viel können und generell eine tolle Sache sind, aber hohe Einstiegshürden haben bzw. viel Einarbeitungszeit benötigen, sowie für viele Projekte einfach überdimensioniert sind.</p>
<p>In Ruby hätte man für solche Fälle z.B. <a href="http://www.sinatrarb.com/">Sinatra</a>: übersichtlich, klein, schnell und flexibel. Mit <a href="https://limonade-php.github.io/">Limonade</a> gibt es so ein Micro-Framework nun endlich für PHP, mit dem sich auch ähnlich elegant entwickeln lässt.</p>
<p>Es reicht eine Datei in ein Script einzubinden, die .htaccess-Datei anzupassen und schon kann man loslegen:</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="k">require_once</span> <span class="s1">&#39;vendors/limonade.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// lambda function (&gt;= PHP 5.3)
</span></span></span><span class="line"><span class="cl"><span class="nx">dispatch</span><span class="p">(</span><span class="s1">&#39;/hello/:name&#39;</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$name</span> <span class="o">=</span> <span class="nx">params</span><span class="p">(</span><span class="s1">&#39;name&#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">return</span> <span class="s2">&#34;Hello, </span><span class="si">{</span><span class="nv">$name</span><span class="si">}</span><span class="s2">&#34;</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></span><span class="line"><span class="cl"><span class="nx">run</span><span class="p">();</span>
</span></span></code></pre></div><p>Das war es schon. Den ganzen Rest erledigt Limonade und das war noch lange nicht alles, was es kann. Die Routen-Definitionen können beispielsweise auch Wildcards oder reguläre Ausdrücke enthalten. Als Callback lassen sich selbstverständlich auch Objekte bzw. deren Methoden aufrufen (auch statisch) oder man gibt in klassischer Manier einen Funktionsnamen als String an.</p>
<p>Dazu gibt es eine Template Engine mit partiellen Templates, Capture-Möglichkeiten, JSON-Unterstützung, Hooks und Filtern.</p>
<p>Alles davon lässt sich ohne große Einarbeitung sofort nutzen. Natürlich muss man die integrierte Template Engine nicht nutzen und kann auch stattdessen einfach Twig oder Smarty verwenden.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Schnellere Websites mit RequireJS</title>
      <link>https://webcodr.io/2012/04/schnellere-websites-mit-requirejs/</link>
      <pubDate>Fri, 06 Apr 2012 22:16:42 +0100</pubDate>
      <guid>https://webcodr.io/2012/04/schnellere-websites-mit-requirejs/</guid>
      <description>Script-Elemente sind Blocker im Rendering-Prozess. Browser arbeiten den Quelltext einer Seite von oben nach unten durch. Wenn ein script-Element auftaucht, muss es erst ausgeführt werden, bevor der Browser sich um den nachfolgenden Quelltext kümmern kann. Externe JavaScripts können aus diesem Grund massiven Einfluss auf die Ladegeschwindigkeit einer Seite haben, auch wenn die eigentliche Website schon längst vom Web-Server an den Browser ausgeliefert wurde.
Falls ein externes Script auf einem langsamen Server liegt, muss der Browser warten, bis er es komplett abgerufen und ausgeführt hat. Sollte das aufgerufene Script gar nicht mehr vorhanden sein, wartet der Browser seine Timeout-Einstellung ab, bis er weitermacht. Sicherlich hat jeder schon mal gesehen, dass eine Website nur bis zu einem gewissen Teil angezeigt wird und erst nach ein paar Sekunden der Rest dargestellt wird. Mit sehr hoher Wahrscheinlichkeit, war dies ein nicht mehr vorhandenes oder nur sehr langsam ladendes JavaScript.
</description>
      <content:encoded><![CDATA[<p>Script-Elemente sind Blocker im Rendering-Prozess. Browser arbeiten den Quelltext einer Seite von oben nach unten durch. Wenn ein script-Element auftaucht, muss es erst ausgeführt werden, bevor der Browser sich um den nachfolgenden Quelltext kümmern kann. Externe JavaScripts können aus diesem Grund massiven Einfluss auf die Ladegeschwindigkeit einer Seite haben, auch wenn die eigentliche Website schon längst vom Web-Server an den Browser ausgeliefert wurde.</p>
<p>Falls ein externes Script auf einem langsamen Server liegt, muss der Browser warten, bis er es komplett abgerufen und ausgeführt hat. Sollte das aufgerufene Script gar nicht mehr vorhanden sein, wartet der Browser seine Timeout-Einstellung ab, bis er weitermacht. Sicherlich hat jeder schon mal gesehen, dass eine Website nur bis zu einem gewissen Teil angezeigt wird und erst nach ein paar Sekunden der Rest dargestellt wird. Mit sehr hoher Wahrscheinlichkeit, war dies ein nicht mehr vorhandenes oder nur sehr langsam ladendes JavaScript.</p>
<p>Asynchrones Abholen findet erst nach dem Rendern der Seite statt und blockiert daher nichts. Dazu lässt sich bei konsequentem Nutzen von RequireJS der Einsatz von script-Elementen weitgehend vermeiden. Im Idealfall gibt es nur noch ein script-Element, das RequireJS lädt und ein weiteres Script startet, um die gesamte JavaScript-Funktionalität einer Seite zu initialisieren.</p>
<h3 id="konfiguration"><a href="#konfiguration" class="heading-anchor">Konfiguration</a></h3>
<p>Hier ein simples Beispiel einer Require-JS-Konfiguration mit jQuery:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">.</span><span class="nx">config</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="nx">paths</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;jquery&#39;</span><span class="o">:</span> <span class="s1">&#39;libs/jquery/jquery-1.7.1&#39;</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><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">([</span><span class="s1">&#39;jquery&#39;</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">jQuery</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">jQuery</span><span class="p">.</span><span class="nx">noConflict</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>Zuerst wird RequireJS so konfiguriert, dass <a href="http://jquery.com/">jQuery</a> mittels des Keywords &ldquo;jquery&rdquo; geladen werden kann. Im Konfigurations-Objekt &ldquo;paths&rdquo; wird dafür als Attributsname &ldquo;jquery&rdquo; und als Wert der Pfad (ausgehend vom Verzeichnis des Scripts) zu jQuery gesetzt. Anschließend wird require() aufgerufen und als erster Parameter wird ein Array mit den aufzulösenden Abhängigkeiten erwartet &ndash; hier jQuery. Man kann hier ein unter &ldquo;paths&rdquo; gesetztes Keyword, den Pfad oder auch externe JavaScripts über die URL angeben. Falls es sich um ein Modul handelt, wird auf die Dateiendung &ldquo;.js&rdquo; verzichtet.</p>
<p>Im zweiten Parameter wird eine Callback-Funktion definiert, deren Aufruf unmittelbar nach dem Laden der Abhängigkeiten stattfindet. Als Parameter folgen hier die Rückgaben der angeforderten JavaScript-Dateien. In diesem Fall ist es ein jQuery-Objekt, das sogleich in den No-Conflict-Modus versetzt wird, um nicht mit evtl. anderen geladenen Bibliotheken zu kollidieren. Dieses Verfahren ist besser bekannt als <a href="http://de.wikipedia.org/wiki/Dependency_Injection">Dependency Injection</a>.</p>
<h3 id="module"><a href="#module" class="heading-anchor">Module</a></h3>
<p>RequireJS bietet über die Modul-Definition eine elegante Lösung, Programmbestandteile zu kapseln und Abhängigkeiten (z.B. jQuery) aufzulösen. Aktuell setze ich hier drei Module ein, die Funktionen für einzelne Beiträge (Syntax Highlighting, Lightbox etc.) übernehmen sowie das Tracking über Piwik bzw. Google Analytics starten.</p>
<p>Alle Module sind so aufgebaut, dass sie Abhängigkeiten in Form von Bibliotheken oder anderen Modulen erst auflösen, wenn sie gebraucht werden. Gleichzeitig stellt RequireJS sicher, dass eine Abhängigkeit nur einmal geladen wird und für jedes andere Modul zur Verfügung steht.</p>
<p>Ein Beispiel-Modul:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">define</span><span class="p">([</span><span class="s1">&#39;jquery&#39;</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">exports</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">exports</span><span class="p">.</span><span class="nx">init</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="s1">&#39;&lt;h1&gt;Hello World!&lt;/h1&gt;&#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></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">exports</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Mit define() wird die Moduldefinition gestartet. Anschließend passiert das gleiche, wie in der Funktion require(). Zuerst werden die Abhängigkeiten als Array definiert, danach startet eine Dependency Injection in die Callback-Funktion. An dieser Stelle wird ein Dollarzeichen als Parameter für die Abhängigkeit verwendet, um jQuery wie gewohnt einsetzen zu können. Anschließend wird das Objekt &ldquo;exports&rdquo; mit der Methode init() erstellt und zurückgegeben. Wird das Modul an anderer Stelle über require() geladen und das exports-Objekt in die Callback-Funktion injiziert, kann man in ihr die init-Methode aufrufen und damit das Modul starten. Natürlich es ist aber auch möglich, darauf zu verzichten und innerhalb der Modul-Callback-Funktion direkt Code auszuführen.</p>
<p>Um ein Modul an anderer Stelle mit require() zu laden, gibt man den Pfad, ausgehend vom Verzeichnis der RequireJS-Konfiguration, zum Modul an. Wie vorhin schon beschrieben, muss man hierbei auf die Dateiendung &ldquo;.js&rdquo; verzichten.</p>
<p>Zu guter Letzt muss RequireJS selbst samt Konfiguration geladen werden:</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">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;js/libs/require/require.js&#34;</span> <span class="na">data-main</span><span class="o">=</span><span class="s">&#34;js/config&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Ein simples Script-Element hierzu genügt &ndash; am besten unmittelbar vor dem schließenden body-Element. Zusätzlich wird mit dem Daten-Attribut &ldquo;main&rdquo; noch der Pfad zur Konfiguration mitgegeben. RequireJS bindet das entsprechende JavaScript automatisch ein und startet es.</p>
<h3 id="fazit"><a href="#fazit" class="heading-anchor">Fazit</a></h3>
<p>RequireJS ist ein mächtiges Tool, das alleine schon durch die Kapselung in verschiedene Module dem Entwickler sehr viele Vorteile bringt. Durch die immer größer werdende Komplexität von Web-Sites bzw. Web-Applikationen sind andere Formen der Organisation notwendig geworden, die in anderen Programmiersprachen schon von Anfang an vorhanden waren.</p>
<p>Dank der Module mit ihren von RequireJS automatisch aufgelösten Abhängigkeiten durch Dependency Injection, lassen sich schnell und einfach, neue Funktionalitäten in eine Website implementieren, ohne weitere script-Elemente in den HTML-Quelltext einfügen zu müssen.</p>
<p>Bis auf RequireJS selbst werden alle definierten Abhängigkeiten bzw. Module asynchron geladen, ohne das Rendern der Seite zu blockieren. Da Browser asynchrone Anfragen parallel abarbeiten können, werden insgesamt alle Scripts schneller geladen und früher ausgeführt, als es bei einer traditionellen Verwendung von JavaScript möglich wäre.</p>
<p>Das Ergebnis ist ein großer Vorteil für alle Beteiligten. Für den Nutzer wird eine Website mit viel JavaScript deutlich schneller dargestellt, während die Funktionalitäten im Hintergrund nachgeladen werden, ohne dass man etwas davon bemerkt. Aus Sicht der Entwickler bietet RequireJS eine elegante Möglichkeit, schnelle neue Funktionen zu entwickeln, Abhängigkeiten einfach aufzulösen und durch die Module strukturierter zu arbeiten.</p>
<p>Abgesehen von TypeKit und Disqus laufen alle JavaScript-Bestandteile dieser Seite als Modul. Zwar lässt sich TypeKit problemlos als Modul umsetzen, nur werden die Schriften erst nach dem Rendern der Seite geladen. Für etwa eine halbe Sekunde sind die Fallback-Schriften zu sehen, erst dann werden die über TypeKit geladenen Schriften dargestellt. Da das nicht Sinn und Zweck von TypeKit ist und Euch Leser irritiert, wird TypeKit klassisch im head-Element vor den Stylesheets geladen.</p>
<p>Disqus ist dagegen als Wordpress-Plug-In eingebunden und darauf würde ich auch nur sehr ungern verzichten. Ich könnte das Plug-In zwar anpassen, aber damit wäre die Update-Fähigkeit dahin. Da Disqus seine Scripts selbst asynchron lädt, sehe ich auch keine Notwendigkeit, den ganzen Aufwand zu betreiben und Module zu schreiben. Allerdings habe ich andere Plug-Ins wie die Lightbox und das Syntax Highlighting rausgeworfen und durch RequireJS-Module ersetzt, die nun fester Bestandteil des Themes sind. Geladen werden sie aber nur, wenn auch Beiträge vorhanden sind, die eine der Funktionen brauchen.</p>
<p>Falls ich Euer Interesse an RequireJS geweckt habe, könnt Ihr meine Implementierung inkl. eines Ladesystems für Module aus dem HTML-Quelltext heraus im GitHub-Repository meines <a href="https://web.archive.org/web/20131210084100/https://github.com/MadCatme/mcw-blue">Wordpress-Themes</a> anschauen oder auch einen Fork erstellen, um damit selbst entwickeln zu können. Falls jemand Ideen oder Verbesserungen hat, ich freue mich über jede Anregung und jeden Pull-Request in GitHub.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Upload-Probleme mit PHP via FastCGI</title>
      <link>https://webcodr.io/2012/04/upload-probleme-mit-php-via-fastcgi/</link>
      <pubDate>Thu, 05 Apr 2012 22:15:00 +0100</pubDate>
      <guid>https://webcodr.io/2012/04/upload-probleme-mit-php-via-fastcgi/</guid>
      <description>Als ich eben eine neue Galerie in mein privates Weblog hochladen wollte, begrüßte mich bei jedem Versuch ein HTTP 500, besser bekannt als Internal Server Error. Die Meldung ist absolut nichtssagend und es lässt sich nur über Log-Dateien rausfinden, was eigentlich passiert.
Das Problem besteht offenbar seit dem Umzug auf einen virtuellen Server bei Host Europe mit Ubuntu 10.04 LTS und Plesk zur Verwaltung. In Plesk wird PHP standardmäßig via mod_php in den Apache eingebunden. Da das aber u.U. Rechteprobleme zwischen dem Apache-User und dem FTP-User bei von PHP angelegten Dateien geben kann, lasse ich PHP via FastCGI laufen. Das braucht zwar mehr RAM, hat aber den Vorteil, dass der PHP-Prozess und FTP-Zugang über den gleichen Nutzer laufen. Im Gegensatz zu suPHP funktionieren damit auch Opcode Caches wie APC und es muss nicht für jede Anfrage auf ein Script ein neuer PHP-Prozess gestartet werden.
</description>
      <content:encoded><![CDATA[<p>Als ich eben eine neue Galerie in mein privates Weblog hochladen wollte, begrüßte mich bei jedem Versuch ein HTTP 500, besser bekannt als Internal Server Error. Die Meldung ist absolut nichtssagend und es lässt sich nur über Log-Dateien rausfinden, was eigentlich passiert.</p>
<p>Das Problem besteht offenbar seit dem Umzug auf einen virtuellen Server bei <a href="http://www.hosteurope.de/">Host Europe</a> mit Ubuntu 10.04 LTS und Plesk zur Verwaltung. In Plesk wird PHP standardmäßig via mod_php in den Apache eingebunden. Da das aber u.U. Rechteprobleme zwischen dem Apache-User und dem FTP-User bei von PHP angelegten Dateien geben kann, lasse ich PHP via FastCGI laufen. Das braucht zwar mehr RAM, hat aber den Vorteil, dass der PHP-Prozess und FTP-Zugang über den gleichen Nutzer laufen. Im Gegensatz zu suPHP funktionieren damit auch Opcode Caches wie APC und es muss nicht für jede Anfrage auf ein Script ein neuer PHP-Prozess gestartet werden.</p>
<p>Nach etwas Recherche, war die Ursache aber schnell klar. Um mit FastCGI arbeiten zu können, verwendet der Apache das Modul mod_fcgid, das folgenden Fehler auslöst:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">mod_fcgid: HTTP request length 1019250 (so far) exceeds MaxRequestLen
</span></span><span class="line"><span class="cl">(131072)
</span></span></code></pre></div><p>Sprich: sämtliche HTTP-Anfragen, deren Länge mehr als 128 KB beträgt, werden durch FastCGI nicht zugelassen. Wie man sieht, war der Request knapp 1 MB groß, was bei größeren Bildern in ordentlicher Qualität schnell passiert.</p>
<p>Um das Limit zu erhöhen, muss man in die Modul-Konfiguration unter /etc/apache2/mods-available/fcgid.conf eingreifen und folgenden Eintrag hinzufügen bzw. entsprechend verändern:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">MaxRequestLen 2097152
</span></span></code></pre></div><p>Damit wird die Beschränkung auf 2 MB erhöht. Sollte für die meisten Zwecke mehr als ausreichen. Anschließend muss der Apache neu gestartet werden, damit die Änderung wirksam wird.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
