JSON Feed Validator

Check whether your feed is valid. For more information about JSON Feed, see the specification.

{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Friedrich Ewald",
  "description": "Blog of a software engineer. Follow me along as I explore and write about Python, Ruby, and web development and random pieces of the internet. Happy to chat!",
  "language": "en",
  "home_page_url": "https://fewald.net",
  "feed_url": "https://fewald.net/feed.json",
  "items": [
    {
      "id": "https://fewald.net/ruby/rails/2022/08/08/secure-sidekiq-with-basicauth.html",
      "url": "https://fewald.net/ruby/rails/2022/08/08/secure-sidekiq-with-basicauth.html",
      "title": "Secure Sidekiq with BasicAuth",
      "content_html": "<p>I am using Sidekiq for background processing for my website Every Podcast. One of the background jobs is <a href=\"https://everypodcast.page/episodes\">loading new episodes</a> from known feeds. Because this relies on external resources, many things that are out of my control can go wrong. To isolate the individual fetch and potential errors, I automatically create a job for every single podcast update. This obviously leads to a lot of jobs. Sidekiq comes with a UI that allows me to monitor the job queue, find dead jobs and see the throughput.</p><!--more--><p>While looking on how to secure the backend interface I found many <a href=\"https://stackoverflow.com/a/46891659/636676\">articles</a>, also from the developers themselves, that recommend using the authentication framework Devise. I didn’t want to introduce a whole framework for a single use case, especially considering that there are currently no user accounts supported. Instead, something like <strong>Basic Auth</strong> is enough for my use case.</p><p>I came across this <a href=\"https://stackoverflow.com/a/13409418/636676\">Stackoverflow post</a> which suggested a solution similar to the one shown below. I added a default username and password if they’re not set to allow for easier local testing. In a production environment those values are coming from environment variables.</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"no\">Sidekiq</span><span class=\"o\">::</span><span class=\"no\">Web</span><span class=\"p\">.</span><span class=\"nf\">use</span><span class=\"p\">(</span><span class=\"no\">Rack</span><span class=\"o\">::</span><span class=\"no\">Auth</span><span class=\"o\">::</span><span class=\"no\">Basic</span><span class=\"p\">)</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">user</span><span class=\"p\">,</span> <span class=\"n\">password</span><span class=\"o\">|</span>  <span class=\"c1\"># Protect against timing attacks:</span>  <span class=\"c1\"># - See https://codahale.com/a-lesson-in-timing-attacks/</span>  <span class=\"c1\"># - See https://thisdata.com/blog/timing-attacks-against-string-comparison/</span>  <span class=\"c1\"># - Use &amp; (do not use &amp;&amp;) so that it doesn't short circuit.</span>  <span class=\"c1\"># - Use digests to stop length information leaking</span>  <span class=\"no\">Rack</span><span class=\"o\">::</span><span class=\"no\">Utils</span><span class=\"p\">.</span><span class=\"nf\">secure_compare</span><span class=\"p\">(</span><span class=\"o\">::</span><span class=\"no\">Digest</span><span class=\"o\">::</span><span class=\"no\">SHA256</span><span class=\"p\">.</span><span class=\"nf\">hexdigest</span><span class=\"p\">(</span><span class=\"n\">user</span><span class=\"p\">),</span>                             <span class=\"o\">::</span><span class=\"no\">Digest</span><span class=\"o\">::</span><span class=\"no\">SHA256</span><span class=\"p\">.</span><span class=\"nf\">hexdigest</span><span class=\"p\">(</span><span class=\"no\">ENV</span><span class=\"p\">[</span><span class=\"s1\">'SIDEKIQ_USER'</span><span class=\"p\">]</span> <span class=\"o\">||</span> <span class=\"s1\">'admin'</span><span class=\"p\">))</span> <span class=\"o\">&amp;</span>    <span class=\"no\">Rack</span><span class=\"o\">::</span><span class=\"no\">Utils</span><span class=\"p\">.</span><span class=\"nf\">secure_compare</span><span class=\"p\">(</span><span class=\"o\">::</span><span class=\"no\">Digest</span><span class=\"o\">::</span><span class=\"no\">SHA256</span><span class=\"p\">.</span><span class=\"nf\">hexdigest</span><span class=\"p\">(</span><span class=\"n\">password</span><span class=\"p\">),</span> <span class=\"o\">::</span><span class=\"no\">Digest</span><span class=\"o\">::</span><span class=\"no\">SHA256</span><span class=\"p\">.</span><span class=\"nf\">hexdigest</span><span class=\"p\">(</span><span class=\"no\">ENV</span><span class=\"p\">[</span><span class=\"s1\">'SIDEKIQ_PASSWORD'</span><span class=\"p\">]</span> <span class=\"o\">||</span> <span class=\"s1\">'password'</span><span class=\"p\">))</span><span class=\"k\">end</span></code></pre></figure>",
      "date_published": "2022-08-08T23:30:00+00:00"
    },
    {
      "id": "https://fewald.net/links/2022/07/30/aaron-koblin.html",
      "url": "https://fewald.net/links/2022/07/30/aaron-koblin.html",
      "title": "Visualizations by Aaron Koblin",
      "content_html": "<p>Beautiful visualizations and projects by <a href=\"http://www.aaronkoblin.com\">Aaron Koblin</a>. Through this I discovered the flight patterns, generated with the <a href=\"https://processing.org/\">processing framework</a> which is now on my list to try out.</p><p>I meant to post this some time ago and recently found it in my “drafts” folder.</p>",
      "date_published": "2022-07-30T05:00:00+00:00"
    },
    {
      "id": "https://fewald.net/ruby/rails/2022/07/28/fast-feed-processing-in-ruby.html",
      "url": "https://fewald.net/ruby/rails/2022/07/28/fast-feed-processing-in-ruby.html",
      "title": "Fast feed processing in Ruby and Rails",
      "content_html": "<p>While working on my current project, <a href=\"https://everypodcast.page\">everypodcast</a>, I needed to implement an RSS feed parser, mostly for iTunes RSS feeds. Initially I went with the builtin <code class=\"language-plaintext highlighter-rouge\">RSS</code> package from the standard library. When reading a lot of large XML feeds, I noticed that parsing those feeds takes anywhere from 0.5 to over 30 seconds. At the same time the process was using a 100% CPU.</p><p>After some searching I found <a href=\"https://github.com/feedjira/feedjira\">Feedjira</a>. With this, it now takes on average 0.5 seconds and at most 1.5 seconds to parse even the longest feeds. Furthermore it has dedicated support for different types of RSS feeds, I highly recommend it.</p><!--more--><p>Installation via <code class=\"language-plaintext highlighter-rouge\">Gemfile</code>:</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"n\">gem</span> <span class=\"s1\">'feedjira'</span><span class=\"p\">,</span> <span class=\"s1\">'~&gt; 3.2'</span></code></pre></figure><p>Parsing feeds is as simple as this:</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"no\">URI</span><span class=\"p\">.</span><span class=\"nf\">parse</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">).</span><span class=\"nf\">open</span><span class=\"p\">(</span><span class=\"ss\">read_timeout: </span><span class=\"mi\">10</span><span class=\"p\">)</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">rss</span><span class=\"o\">|</span>    <span class=\"n\">feed</span> <span class=\"o\">=</span> <span class=\"no\">Feedjira</span><span class=\"p\">.</span><span class=\"nf\">parse</span><span class=\"p\">(</span><span class=\"n\">rss</span><span class=\"p\">.</span><span class=\"nf\">read</span><span class=\"p\">)</span>    <span class=\"c1\"># Feed attributes can be accessed as follows</span>    <span class=\"n\">feed</span><span class=\"p\">.</span><span class=\"nf\">language</span>  <span class=\"c1\">#=&gt; 'en'</span><span class=\"k\">end</span></code></pre></figure>",
      "date_published": "2022-07-28T16:56:00+00:00"
    },
    {
      "id": "https://fewald.net/ruby/rails/2022/07/18/rails-testing-with-static-server.html",
      "url": "https://fewald.net/ruby/rails/2022/07/18/rails-testing-with-static-server.html",
      "title": "Rails testing with static server",
      "content_html": "<p>I wanted to test some part of my application that performs HTTP requests against a different website. Now, for testing, I didn’t want to perform actual outside HTTP calls because they tend to become flaky if the internnet becomes unstable or when the remote resource is not available. Another reason, why I didn’t want to use the rails server and their static files, is that I didn’t want to serve those files in production. So I decided to write my own static file server.</p><!--more--><h2 id=\"requirements\">Requirements</h2><p>The requirements are quite simple. I wanted something that I can spin up from Ruby in the current process and that is able to serve static files from a predefined folder. For this purpose, I created a <code class=\"language-plaintext highlighter-rouge\">files</code> folder under <code class=\"language-plaintext highlighter-rouge\">fixtures</code>. The other requirement is that I did not want it to clash with any other server that might be running on the target system. The reason is that I am planning to run this on my CI pipeline and I have very little control over what runs there. Furthermore, if tests run in parallel I didn’t want to spend much time on synchronization, instead spin up a temporary server for each test and shut it down immediately afterwards.</p><h2 id=\"solution\">Solution</h2><p>I came up with two methods that I added to the <code class=\"language-plaintext highlighter-rouge\">test_helper.rb</code> in my Rails application: <code class=\"language-plaintext highlighter-rouge\">fixture_server</code> and <code class=\"language-plaintext highlighter-rouge\">replace_port!</code>. The former starts a server and returns the instance of the server itself, giving full control over the server process, including shutdown. This is intended if the server should run for a whole suite instead of for a single test. The block mode allows to start a server inline, and end it immediately. This mode is intended for cases when only a single tests uses the server. See the following example for example usage.</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"k\">def</span> <span class=\"nf\">demo_explicit_shutdown</span>    <span class=\"n\">server</span> <span class=\"o\">=</span> <span class=\"n\">fixture_server</span>    <span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">config</span><span class=\"p\">[</span><span class=\"ss\">:Port</span><span class=\"p\">]</span>    <span class=\"c1\"># Do something with the server</span>    <span class=\"c1\"># ...</span>    <span class=\"c1\"># Stop server</span>    <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">shutdown</span><span class=\"k\">end</span><span class=\"k\">def</span> <span class=\"nf\">demo_block_mode</span>    <span class=\"n\">fixture_server</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">server</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">|</span>        <span class=\"c1\"># Do something with server and port</span>        <span class=\"c1\"># ...</span>        <span class=\"c1\"># No need to shutdown server</span>    <span class=\"k\">end</span><span class=\"k\">end</span></code></pre></figure><p>As the port number is dynamic, the HTTP calls also need to be constructed dynamically. I found that it is relatively easy to write <code class=\"language-plaintext highlighter-rouge\">&lt;PORT&gt;</code> in my URLs and then use the small helper method <code class=\"language-plaintext highlighter-rouge\">replace_port!</code> to replace the port via regular expression inline.</p><p>The complete code for the methods <code class=\"language-plaintext highlighter-rouge\">fixture_server</code> and <code class=\"language-plaintext highlighter-rouge\">replace_port!</code> is listed below. The dynamic port number is achieved by passing <code class=\"language-plaintext highlighter-rouge\">0</code>. This tells the system to use the first available port. The server is started in a different thread to be non-blocking and automatically shuts down upon receiving a <code class=\"language-plaintext highlighter-rouge\">SIGINT</code> signal.</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"c1\"># Start a temporary server for fixtures which gets automatically shut down.</span><span class=\"c1\"># The port number is returned</span><span class=\"k\">def</span> <span class=\"nf\">fixture_server</span>    <span class=\"n\">root</span> <span class=\"o\">=</span> <span class=\"no\">Rails</span><span class=\"p\">.</span><span class=\"nf\">root</span><span class=\"p\">.</span><span class=\"nf\">join</span><span class=\"p\">(</span><span class=\"s1\">'test'</span><span class=\"p\">,</span> <span class=\"s1\">'fixtures'</span><span class=\"p\">,</span> <span class=\"s1\">'files'</span><span class=\"p\">)</span>    <span class=\"n\">server</span> <span class=\"o\">=</span> <span class=\"no\">WEBrick</span><span class=\"o\">::</span><span class=\"no\">HTTPServer</span><span class=\"p\">.</span><span class=\"nf\">new</span> <span class=\"ss\">:Port</span> <span class=\"o\">=&gt;</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"ss\">:DocumentRoot</span> <span class=\"o\">=&gt;</span> <span class=\"n\">root</span>    <span class=\"nb\">trap</span> <span class=\"s1\">'INT'</span> <span class=\"k\">do</span> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">shutdown</span> <span class=\"k\">end</span>    <span class=\"no\">Thread</span><span class=\"p\">.</span><span class=\"nf\">new</span> <span class=\"k\">do</span>        <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">start</span>    <span class=\"k\">end</span>    <span class=\"k\">if</span> <span class=\"nb\">block_given?</span>        <span class=\"k\">yield</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">,</span> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">config</span><span class=\"p\">[</span><span class=\"ss\">:Port</span><span class=\"p\">])</span>        <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"nf\">stop</span>    <span class=\"k\">end</span>    <span class=\"n\">server</span><span class=\"k\">end</span><span class=\"c1\"># Replace port placeholder &lt;PORT&gt; with real port</span><span class=\"k\">def</span> <span class=\"nf\">replace_port!</span><span class=\"p\">(</span><span class=\"n\">s</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"p\">)</span>    <span class=\"n\">s</span><span class=\"p\">.</span><span class=\"nf\">gsub!</span> <span class=\"sr\">/&lt;PORT&gt;/</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"p\">.</span><span class=\"nf\">to_s</span><span class=\"k\">end</span></code></pre></figure><p>Besides the installation of the <code class=\"language-plaintext highlighter-rouge\">webrick</code> gem, that is all that is needed to serve static files during tests in Rails from the <code class=\"language-plaintext highlighter-rouge\">fixtures/files</code> folder.</p>",
      "date_published": "2022-07-18T04:33:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/07/17/new-york-times-story-portrait.html",
      "url": "https://fewald.net/2022/07/17/new-york-times-story-portrait.html",
      "title": "New York Times Story Portrait",
      "content_html": "<p><a href=\"/assets/images/story-portrait.jpeg\"><picture><source srcset=\"/generated/assets/images/story-portrait-200-04d658255.avif 200w, /generated/assets/images/story-portrait-400-04d658255.avif 400w, /generated/assets/images/story-portrait-800-04d658255.avif 800w, /generated/assets/images/story-portrait-1200-04d658255.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/story-portrait-200-e498de1f6.webp 200w, /generated/assets/images/story-portrait-400-e498de1f6.webp 400w, /generated/assets/images/story-portrait-800-e498de1f6.webp 800w, /generated/assets/images/story-portrait-1200-e498de1f6.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/story-portrait-200-9b7030554.jpeg 200w, /generated/assets/images/story-portrait-400-9b7030554.jpeg 400w, /generated/assets/images/story-portrait-800-9b7030554.jpeg 800w, /generated/assets/images/story-portrait-1200-9b7030554.jpeg 1200w\" type=\"image/jpeg\"><img src=\"/generated/assets/images/story-portrait-800-9b7030554.jpeg\" alt=\"New York Time Story Portrait\" width=\"2160\" height=\"2700\"></picture></a></p><p>Really clever from the New York Times. You can create your own <a href=\"http://nytimes.com/storyportrait\">here</a>.</p>",
      "date_published": "2022-07-17T03:06:00+00:00"
    },
    {
      "id": "https://fewald.net/seo/2022/07/08/improving-search-engine-optimization.html",
      "url": "https://fewald.net/seo/2022/07/08/improving-search-engine-optimization.html",
      "title": "Improving Search Engine Optimization",
      "content_html": "<p>As the final step in this very small series I tried improving the SEO (search engine optimization) score for my start page. I started out with a good score of 92. As usual, my goal was 100.</p><p><a href=\"/assets/images/lighthouse-seo-start-fewald.png\"><picture><source srcset=\"/generated/assets/images/lighthouse-seo-start-fewald-200-1cd893131.avif 200w, /generated/assets/images/lighthouse-seo-start-fewald-400-1cd893131.avif 400w, /generated/assets/images/lighthouse-seo-start-fewald-800-1cd893131.avif 800w, /generated/assets/images/lighthouse-seo-start-fewald-828-1cd893131.avif 828w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/lighthouse-seo-start-fewald-200-8f272ea12.webp 200w, /generated/assets/images/lighthouse-seo-start-fewald-400-8f272ea12.webp 400w, /generated/assets/images/lighthouse-seo-start-fewald-800-8f272ea12.webp 800w, /generated/assets/images/lighthouse-seo-start-fewald-828-8f272ea12.webp 828w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/lighthouse-seo-start-fewald-200-80af4b92d.png 200w, /generated/assets/images/lighthouse-seo-start-fewald-400-80af4b92d.png 400w, /generated/assets/images/lighthouse-seo-start-fewald-800-80af4b92d.png 800w, /generated/assets/images/lighthouse-seo-start-fewald-828-80af4b92d.png 828w\" type=\"image/png\"><img src=\"/generated/assets/images/lighthouse-seo-start-fewald-800-80af4b92d.png\" alt=\"Lighthouse score of 92 for fewald.net\" width=\"828\" height=\"436\"></picture></a></p><!--more--><h2 id=\"links\">Links</h2><p>As a first step I had to fix a broken link in one of my articles. One forgotten <code class=\"language-plaintext highlighter-rouge\">href</code> in a post needed to be fixed. Fixing this issue did not change the score.</p><h2 id=\"meta-description\">Meta description</h2><p>The start page didn’t have an HTML meta description, so I added one. I followed the best practices outlined <a href=\"https://web.dev/meta-description/\">here</a>.</p><ul>  <li>Use a unique description for each page</li>  <li>Clear and concise descriptions</li>  <li>Avoid keyword stuffing</li>  <li>No complete sentences needed, can be structured data</li></ul><p>Furthermore descriptions should be less than 160 characters to appear completely in search results.</p><figure class=\"highlight\"><pre><code class=\"language-html\" data-lang=\"html\"><span class=\"nt\">&lt;meta</span> <span class=\"na\">name=</span><span class=\"s\">\"description\"</span> <span class=\"na\">content=</span><span class=\"s\">\"...\"</span><span class=\"nt\">&gt;</span></code></pre></figure><p>For the home page I chose a static description whereas for each individual post I wanted to either use the first 160 characters or, if provided, use an individual meta description. I achieved this with the following code in the templates.</p><figure class=\"highlight\"><pre><code class=\"language-liquid\" data-lang=\"liquid\"><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">case</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">layout</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">when</span><span class=\"w\"> </span><span class=\"s2\">\"post\"</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">if</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">==</span><span class=\"w\"> </span><span class=\"kc\">nil</span><span class=\"w\"> </span><span class=\"p\">%}</span>    <span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">assign</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">excerpt</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">strip_html</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">strip_newlines</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">truncate</span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"mi\">160</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"s2\">\"... read more\"</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">else</span><span class=\"w\"> </span><span class=\"p\">%}</span>    <span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">assign</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">endif</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">when</span><span class=\"w\"> </span><span class=\"s2\">\"page\"</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">unless</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">==</span><span class=\"w\"> </span><span class=\"kc\">nil</span><span class=\"w\"> </span><span class=\"p\">%}</span>    <span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">assign</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nv\">page</span><span class=\"p\">.</span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">endunless</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">else</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">assign</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"p\">%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">endcase</span><span class=\"w\"> </span><span class=\"p\">-%}</span><span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">unless</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"o\">==</span><span class=\"w\"> </span><span class=\"kc\">nil</span><span class=\"w\"> </span><span class=\"p\">%}</span>&lt;meta name=\"description\" content=\"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">meta_description</span><span class=\"w\"> </span><span class=\"p\">}}</span>\"&gt;<span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">endunless</span><span class=\"w\"> </span><span class=\"p\">-%}</span></code></pre></figure><p>This change brought the score to 100 and concluded my improvements for now.</p><p><a href=\"/assets/images/lighthouse-seo-100-fewald.png\"><picture><source srcset=\"/generated/assets/images/lighthouse-seo-100-fewald-200-769de143f.avif 200w, /generated/assets/images/lighthouse-seo-100-fewald-400-769de143f.avif 400w, /generated/assets/images/lighthouse-seo-100-fewald-800-769de143f.avif 800w, /generated/assets/images/lighthouse-seo-100-fewald-886-769de143f.avif 886w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/lighthouse-seo-100-fewald-200-a1c570f19.webp 200w, /generated/assets/images/lighthouse-seo-100-fewald-400-a1c570f19.webp 400w, /generated/assets/images/lighthouse-seo-100-fewald-800-a1c570f19.webp 800w, /generated/assets/images/lighthouse-seo-100-fewald-886-a1c570f19.webp 886w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/lighthouse-seo-100-fewald-200-45b3d0010.png 200w, /generated/assets/images/lighthouse-seo-100-fewald-400-45b3d0010.png 400w, /generated/assets/images/lighthouse-seo-100-fewald-800-45b3d0010.png 800w, /generated/assets/images/lighthouse-seo-100-fewald-886-45b3d0010.png 886w\" type=\"image/png\"><img src=\"/generated/assets/images/lighthouse-seo-100-fewald-800-45b3d0010.png\" alt=\"Lighthouse score of 100 for blog at fewald.net\" width=\"886\" height=\"464\"></picture></a></p>",
      "date_published": "2022-07-08T06:22:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/07/08/improving-website-accessibility.html",
      "url": "https://fewald.net/2022/07/08/improving-website-accessibility.html",
      "title": "Improving Website Accessibility",
      "content_html": "<p>Continuing from before, I wanted to improve the accessibility on this website as much as possible. The initial Lighthouse run gave me a score of around 82, which is not too bad, but I definitely wanted to improve it.</p><!--more--><h2 id=\"background-and-foreground-colors-do-not-have-a-sufficient-contrast-ratio\">Background and foreground colors do not have a sufficient contrast ratio</h2><p>The first thing I did was to increase the contrast on the site. Several elements had a too low contrast against their background. Google <a href=\"https://web.dev/color-contrast/\">recommends</a> a contrast of 4.5:1 for text less than 18px and 3:1 for text equal or greater than 18px.</p><p>Luckily there is an easy way to achieve this via the Chrome dev tools. Simply select an element that violates this rule and click on the “fix” button, then copy the new color code into the CSS. After repeating this multiple times, I reran the test and everything in regards to the contrast was fixed.</p><p><a href=\"/assets/images/lighthouse-contrast-ratio.png\"><picture><source srcset=\"/generated/assets/images/lighthouse-contrast-ratio-200-370092959.avif 200w, /generated/assets/images/lighthouse-contrast-ratio-400-370092959.avif 400w, /generated/assets/images/lighthouse-contrast-ratio-582-370092959.avif 582w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/lighthouse-contrast-ratio-200-a8f01ef5e.webp 200w, /generated/assets/images/lighthouse-contrast-ratio-400-a8f01ef5e.webp 400w, /generated/assets/images/lighthouse-contrast-ratio-582-a8f01ef5e.webp 582w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/lighthouse-contrast-ratio-200-b1576a3c1.png 200w, /generated/assets/images/lighthouse-contrast-ratio-400-b1576a3c1.png 400w, /generated/assets/images/lighthouse-contrast-ratio-582-b1576a3c1.png 582w\" type=\"image/png\"><img src=\"/generated/assets/images/lighthouse-contrast-ratio-582-b1576a3c1.png\" alt=\"Google Chrome warning about too low contrast ration in developer tools\" width=\"582\" height=\"327\"></picture></a></p><h2 id=\"form-elements-do-not-have-associated-labels\">Form elements do not have associated labels</h2><p>Another easy fix with <code class=\"language-plaintext highlighter-rouge\">aria-label</code>. The checkbox that Lighthouse complained about is used to keep the state of the side menu.</p><figure class=\"highlight\"><pre><code class=\"language-html\" data-lang=\"html\"><span class=\"nt\">&lt;input</span> <span class=\"na\">aria-label=</span><span class=\"s\">\"Sidebar\"</span> <span class=\"na\">type=</span><span class=\"s\">\"checkbox\"</span> <span class=\"na\">class=</span><span class=\"s\">\"sidebar-checkbox\"</span> <span class=\"na\">id=</span><span class=\"s\">\"sidebar-checkbox\"</span><span class=\"nt\">&gt;</span></code></pre></figure><h2 id=\"user-scalable\">User scalable</h2><p>As a last step it was recommended that I change the maximum viewport scale level to 5. All I had to change was one line.</p><figure class=\"highlight\"><pre><code class=\"language-html\" data-lang=\"html\"><span class=\"nt\">&lt;meta</span> <span class=\"na\">name=</span><span class=\"s\">\"viewport\"</span> <span class=\"na\">content=</span><span class=\"s\">\"width=device-width, initial-scale=1.0, maximum-scale=5\"</span><span class=\"nt\">&gt;</span></code></pre></figure><h2 id=\"results\">Results</h2><p>After these three relatively minor tweaks, I was able to reach an accessibility level of <strong>100</strong>.</p><p><a href=\"/assets/images/lighthouse-accessibility-100-fewald.png\"><picture><source srcset=\"/generated/assets/images/lighthouse-accessibility-100-fewald-200-1fe4771f2.avif 200w, /generated/assets/images/lighthouse-accessibility-100-fewald-400-1fe4771f2.avif 400w, /generated/assets/images/lighthouse-accessibility-100-fewald-800-1fe4771f2.avif 800w, /generated/assets/images/lighthouse-accessibility-100-fewald-802-1fe4771f2.avif 802w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/lighthouse-accessibility-100-fewald-200-a7a61bd92.webp 200w, /generated/assets/images/lighthouse-accessibility-100-fewald-400-a7a61bd92.webp 400w, /generated/assets/images/lighthouse-accessibility-100-fewald-800-a7a61bd92.webp 800w, /generated/assets/images/lighthouse-accessibility-100-fewald-802-a7a61bd92.webp 802w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/lighthouse-accessibility-100-fewald-200-fc82b1784.png 200w, /generated/assets/images/lighthouse-accessibility-100-fewald-400-fc82b1784.png 400w, /generated/assets/images/lighthouse-accessibility-100-fewald-800-fc82b1784.png 800w, /generated/assets/images/lighthouse-accessibility-100-fewald-802-fc82b1784.png 802w\" type=\"image/png\"><img src=\"/generated/assets/images/lighthouse-accessibility-100-fewald-800-fc82b1784.png\" alt=\"Lighthouse showing score of 100 for Accessibility\" width=\"802\" height=\"398\"></picture></a></p>",
      "date_published": "2022-07-08T06:14:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/07/06/improving-page-speed.html",
      "url": "https://fewald.net/2022/07/06/improving-page-speed.html",
      "title": "Improving Page Speed",
      "content_html": "<p>One important thing for a good user experience is a fast website. Google offers the free tool <a href=\"https://pagespeed.web.dev/\">Page Speed Insights</a>. Running it initially on my website I already have a score of 99, which is pretty good. In the past I already spent a fair amount of time optimizing the bigger problems.</p><p><a href=\"/assets/images/page-speed-score-fewald.png\"><picture><source srcset=\"/generated/assets/images/page-speed-score-fewald-200-1e6abbe1f.avif 200w, /generated/assets/images/page-speed-score-fewald-400-1e6abbe1f.avif 400w, /generated/assets/images/page-speed-score-fewald-800-1e6abbe1f.avif 800w, /generated/assets/images/page-speed-score-fewald-1200-1e6abbe1f.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/page-speed-score-fewald-200-4f1148a9f.webp 200w, /generated/assets/images/page-speed-score-fewald-400-4f1148a9f.webp 400w, /generated/assets/images/page-speed-score-fewald-800-4f1148a9f.webp 800w, /generated/assets/images/page-speed-score-fewald-1200-4f1148a9f.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/page-speed-score-fewald-200-a6c2438e5.png 200w, /generated/assets/images/page-speed-score-fewald-400-a6c2438e5.png 400w, /generated/assets/images/page-speed-score-fewald-800-a6c2438e5.png 800w, /generated/assets/images/page-speed-score-fewald-1200-a6c2438e5.png 1200w\" type=\"image/png\"><img src=\"/generated/assets/images/page-speed-score-fewald-800-a6c2438e5.png\" alt=\"Google Page Speed score of 99\" width=\"2004\" height=\"946\"></picture></a></p><p>I looked at the tips to improve the page speed even further. There are four things that Google considers currently in need of improvement on this page.</p><!--more--><ul>  <li>Ensure text remains visible during webfont load</li>  <li>Image elements do not have explicit width and height</li>  <li>Serve static assets with an efficient cache policy</li>  <li>First Contentful Paint (3G)</li></ul><p><a href=\"/assets/images/page-speed-fewald-tips.png\"><picture><source srcset=\"/generated/assets/images/page-speed-fewald-tips-200-3d5e13c1e.avif 200w, /generated/assets/images/page-speed-fewald-tips-400-3d5e13c1e.avif 400w, /generated/assets/images/page-speed-fewald-tips-800-3d5e13c1e.avif 800w, /generated/assets/images/page-speed-fewald-tips-1200-3d5e13c1e.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/page-speed-fewald-tips-200-4d2be39fd.webp 200w, /generated/assets/images/page-speed-fewald-tips-400-4d2be39fd.webp 400w, /generated/assets/images/page-speed-fewald-tips-800-4d2be39fd.webp 800w, /generated/assets/images/page-speed-fewald-tips-1200-4d2be39fd.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/page-speed-fewald-tips-200-f78142f78.png 200w, /generated/assets/images/page-speed-fewald-tips-400-f78142f78.png 400w, /generated/assets/images/page-speed-fewald-tips-800-f78142f78.png 800w, /generated/assets/images/page-speed-fewald-tips-1200-f78142f78.png 1200w\" type=\"image/png\"><img src=\"/generated/assets/images/page-speed-fewald-tips-800-f78142f78.png\" alt=\"Google Page Speed recommendations to improve website speed\" width=\"1954\" height=\"434\"></picture></a></p><p>As a first step I made sure that the text remains visible during webfont load. It is recommended to add <code class=\"language-plaintext highlighter-rouge\">font-face: swap;</code> to the <code class=\"language-plaintext highlighter-rouge\">@font-face</code> CSS definition whenever a custom font is loaded that the browser has to fetch over the network. I am using the webfontloader library on my page. To change this, I had to slightly modify my Javascript code to the following. Note the <code class=\"language-plaintext highlighter-rouge\">&amp;display=swap</code> in the end.</p><figure class=\"highlight\"><pre><code class=\"language-javascript\" data-lang=\"javascript\"><span class=\"nx\">WebFont</span><span class=\"p\">.</span><span class=\"nx\">load</span><span class=\"p\">({</span>  <span class=\"na\">google</span><span class=\"p\">:</span> <span class=\"p\">{</span>    <span class=\"na\">families</span><span class=\"p\">:</span> <span class=\"p\">[</span><span class=\"dl\">'</span><span class=\"s1\">PT Sans:400</span><span class=\"dl\">'</span><span class=\"p\">,</span> <span class=\"dl\">'</span><span class=\"s1\">PT Serif:400&amp;display=swap</span><span class=\"dl\">'</span><span class=\"p\">]</span>  <span class=\"p\">}</span><span class=\"p\">});</span></code></pre></figure><p>After this, the largest contentful paint dropped from 3 seconds to 2.6 seconds. The score remained unchanged.</p><p>The next step was the caching of static resources. Since I am using cloudflare to serve and cache my content, it was as easy as setting the cache time to 1 year. After this, I reran the tests: Interestingly, the score for mobile dropped to 86 while the desktop score increased to 100.</p><p>The remaining issue is setting the explicit width and height for images and using next-gen formats for images. Google prefers <a href=\"https://developers.google.com/speed/webp\">webp</a>. Luckily, there is a Jekyll plugin for this purpose which does a great job: <a href=\"https://github.com/rbuchberger/jekyll_picture_tag\">jekyll_picture_tag</a>. This gem requires <code class=\"language-plaintext highlighter-rouge\">vips</code> to be installed locally as well as in the CI pipeline. I was able to do it on my Mac via <code class=\"language-plaintext highlighter-rouge\">brew install vips</code>. As a CI pipeline I use bitbucket</p><p>To automatically generate different versions of the same image, I had to place the following code in my post:</p><figure class=\"highlight\"><pre><code class=\"language-plain\" data-lang=\"plain\">{% picture jpt-webp /assets/images/page-speed-score-fewald.png --alt Google Page Speed score of 99 %}</code></pre></figure><p>This creates the following HTML of which the browser picks the best version to display:</p><figure class=\"highlight\"><pre><code class=\"language-html\" data-lang=\"html\"><span class=\"nt\">&lt;picture&gt;</span>  <span class=\"nt\">&lt;source</span> <span class=\"na\">srcset=</span><span class=\"s\">\"/generated/assets/images/page-speed-score-fewald-400-4f1148a9f.webp 400w, /generated/assets/images/page-speed-score-fewald-600-4f1148a9f.webp 600w, /generated/assets/images/page-speed-score-fewald-800-4f1148a9f.webp 800w, /generated/assets/images/page-speed-score-fewald-1000-4f1148a9f.webp 1000w\"</span> <span class=\"na\">type=</span><span class=\"s\">\"image/webp\"</span><span class=\"nt\">&gt;</span>  <span class=\"nt\">&lt;/source&gt;</span>  <span class=\"nt\">&lt;source</span> <span class=\"na\">srcset=</span><span class=\"s\">\"/generated/assets/images/page-speed-score-fewald-400-a6c2438e5.png 400w, /generated/assets/images/page-speed-score-fewald-600-a6c2438e5.png 600w, /generated/assets/images/page-speed-score-fewald-800-a6c2438e5.png 800w, /generated/assets/images/page-speed-score-fewald-1000-a6c2438e5.png 1000w\"</span> <span class=\"na\">type=</span><span class=\"s\">\"image/png\"</span><span class=\"nt\">&gt;</span>  <span class=\"nt\">&lt;/source&gt;</span>  <span class=\"nt\">&lt;img</span> <span class=\"na\">src=</span><span class=\"s\">\"/generated/assets/images/page-speed-score-fewald-800-a6c2438e5.png\"</span> <span class=\"na\">alt=</span><span class=\"s\">\"Google Page Speed score of 99\"</span> <span class=\"na\">loading=</span><span class=\"s\">\"lazy\"</span><span class=\"nt\">&gt;</span><span class=\"nt\">&lt;/picture&gt;</span></code></pre></figure><p>Serving webp images made the responses much smaller but didn’t have any significant effect on the score. The main issue remained still the first contentful paint. I <a href=\"https://github.com/typekit/webfontloader/issues/429#issuecomment-619632202\">read a tip</a> to prefetch fonts by adding the following lines to the <code class=\"language-plaintext highlighter-rouge\">&lt;head&gt;</code> section.</p><figure class=\"highlight\"><pre><code class=\"language-html\" data-lang=\"html\"><span class=\"nt\">&lt;link</span> <span class=\"na\">rel=</span><span class=\"s\">\"dns-prefetch\"</span> <span class=\"na\">href=</span><span class=\"s\">\"//fonts.googleapis.com\"</span><span class=\"nt\">&gt;</span><span class=\"nt\">&lt;link</span> <span class=\"na\">rel=</span><span class=\"s\">\"preconnect\"</span> <span class=\"na\">href=</span><span class=\"s\">\"https://fonts.gstatic.com\"</span> <span class=\"na\">crossorigin</span><span class=\"nt\">&gt;</span></code></pre></figure><p>Finally, I added <code class=\"language-plaintext highlighter-rouge\">widths</code> and <code class=\"language-plaintext highlighter-rouge\">heights</code> to image tags. This is supported directly by jekyll picture tag preset. This has to be saved in <code class=\"language-plaintext highlighter-rouge\">_data/picture.yml</code>, the important part here is <code class=\"language-plaintext highlighter-rouge\">dimension_attributes: true</code>, which is not part of the default settings.</p><figure class=\"highlight\"><pre><code class=\"language-yaml\" data-lang=\"yaml\"><span class=\"na\">presets</span><span class=\"pi\">:</span>  <span class=\"na\">default</span><span class=\"pi\">:</span>    <span class=\"na\">formats</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">webp</span><span class=\"pi\">,</span> <span class=\"nv\">original</span><span class=\"pi\">]</span>    <span class=\"na\">widths</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">200</span><span class=\"pi\">,</span> <span class=\"nv\">400</span><span class=\"pi\">,</span> <span class=\"nv\">800</span><span class=\"pi\">,</span> <span class=\"nv\">1200</span><span class=\"pi\">]</span>    <span class=\"na\">link_source</span><span class=\"pi\">:</span> <span class=\"no\">true</span>    <span class=\"na\">dimension_attributes</span><span class=\"pi\">:</span> <span class=\"no\">true</span></code></pre></figure><p>To avoid distortion of the images I had to add CSS similar to the following:</p><figure class=\"highlight\"><pre><code class=\"language-css\" data-lang=\"css\"><span class=\"nt\">img</span> <span class=\"p\">{</span>    <span class=\"nl\">width</span><span class=\"p\">:</span> <span class=\"m\">100%</span><span class=\"p\">;</span>    <span class=\"nl\">height</span><span class=\"p\">:</span> <span class=\"nb\">auto</span><span class=\"p\">;</span><span class=\"p\">}</span></code></pre></figure><p>With those improvements, I was able to reach a <strong>Google Page Speed 100</strong> with only one warning. As the next step I am planning to clean up the HTML for better accessibility (currently 62) and SEO (currently around 80).</p><p><a href=\"/assets/images/page-speed-performance-100-fewald.png\"><picture><source srcset=\"/generated/assets/images/page-speed-performance-100-fewald-200-f5eaffe94.avif 200w, /generated/assets/images/page-speed-performance-100-fewald-400-f5eaffe94.avif 400w, /generated/assets/images/page-speed-performance-100-fewald-774-f5eaffe94.avif 774w\" type=\"image/avif\"><source srcset=\"/generated/assets/images/page-speed-performance-100-fewald-200-f045618e7.webp 200w, /generated/assets/images/page-speed-performance-100-fewald-400-f045618e7.webp 400w, /generated/assets/images/page-speed-performance-100-fewald-774-f045618e7.webp 774w\" type=\"image/webp\"><source srcset=\"/generated/assets/images/page-speed-performance-100-fewald-200-4d120c6da.png 200w, /generated/assets/images/page-speed-performance-100-fewald-400-4d120c6da.png 400w, /generated/assets/images/page-speed-performance-100-fewald-774-4d120c6da.png 774w\" type=\"image/png\"><img src=\"/generated/assets/images/page-speed-performance-100-fewald-774-4d120c6da.png\" alt=\"Lighthouse report showing 100 performance for fewald.net\" width=\"774\" height=\"616\"></picture></a></p><p><strong>Update 2022-07-10</strong>: I read further on image formats and also enabled avif, which is an open standard and results in even smaller images. The browser now tries the images in the following order: AVIF, WebP, JPG/PNG.</p>",
      "date_published": "2022-07-06T22:24:00+00:00"
    },
    {
      "id": "https://fewald.net/ruby-on-rails/2022/07/06/ruby-on-rails-nil-check.html",
      "url": "https://fewald.net/ruby-on-rails/2022/07/06/ruby-on-rails-nil-check.html",
      "title": "Ruby on Rails nil check",
      "content_html": "<p>Rails provides a great way to check for <code class=\"language-plaintext highlighter-rouge\">nil</code> and empty variables in the same call: <code class=\"language-plaintext highlighter-rouge\">blank?</code>. This is especially helpful in ERB templates where a variable can be either nil or empty, depending on the object. Without it, the check would look similar to this:</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"o\">&lt;</span><span class=\"sx\">% unless </span><span class=\"n\">obj</span><span class=\"p\">.</span><span class=\"nf\">nil?</span> <span class=\"n\">and</span> <span class=\"n\">obj</span> <span class=\"o\">!=</span> <span class=\"s2\">\"\"</span> <span class=\"o\">%&gt;</span>    <span class=\"o\">&lt;</span><span class=\"sx\">%= obj %&gt;&lt;% end %&gt;</span></code></pre></figure><p>This is quite cumbersome and easy to forget. With <code class=\"language-plaintext highlighter-rouge\">blank?</code>, this can be simplified to:</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"o\">&lt;</span><span class=\"sx\">% unless </span><span class=\"n\">obj</span><span class=\"p\">.</span><span class=\"nf\">blank?</span> <span class=\"sx\">%&gt;    &lt;%= obj %&gt;</span><span class=\"o\">&lt;</span><span class=\"sx\">% end </span><span class=\"o\">%&gt;</span></code></pre></figure><p>For the following values <code class=\"language-plaintext highlighter-rouge\">blank?</code> returns <code class=\"language-plaintext highlighter-rouge\">true</code> and <code class=\"language-plaintext highlighter-rouge\">false</code> respectively:</p><figure class=\"highlight\"><pre><code class=\"language-ruby\" data-lang=\"ruby\"><span class=\"s2\">\"\"</span><span class=\"p\">.</span><span class=\"nf\">blank?</span><span class=\"o\">=&gt;</span> <span class=\"kp\">true</span><span class=\"kp\">nil</span><span class=\"p\">.</span><span class=\"nf\">blank?</span><span class=\"o\">=&gt;</span> <span class=\"kp\">true</span><span class=\"s2\">\"hello\"</span><span class=\"p\">.</span><span class=\"nf\">blank?</span><span class=\"o\">=&gt;</span> <span class=\"kp\">false</span><span class=\"mi\">5</span><span class=\"p\">.</span><span class=\"nf\">blank?</span><span class=\"o\">=&gt;</span> <span class=\"kp\">false</span><span class=\"no\">AnyClass</span><span class=\"p\">.</span><span class=\"nf\">blank?</span><span class=\"o\">=&gt;</span> <span class=\"kp\">false</span></code></pre></figure>",
      "date_published": "2022-07-06T16:57:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/07/05/jsonfeed-installed.html",
      "url": "https://fewald.net/2022/07/05/jsonfeed-installed.html",
      "title": "Jekyll JSONFeed installed",
      "content_html": "<p>I finally followed through with my plan from 2017 to install <a href=\"https://www.jsonfeed.org/\">JSONFeed</a> for this blog.</p><p><a href=\"/assets/jsonfeed-feed-validation.png\"><picture><source srcset=\"/generated/assets/jsonfeed-feed-validation-200-6e1843005.avif 200w, /generated/assets/jsonfeed-feed-validation-400-6e1843005.avif 400w, /generated/assets/jsonfeed-feed-validation-800-6e1843005.avif 800w, /generated/assets/jsonfeed-feed-validation-1200-6e1843005.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/jsonfeed-feed-validation-200-0e8869cba.webp 200w, /generated/assets/jsonfeed-feed-validation-400-0e8869cba.webp 400w, /generated/assets/jsonfeed-feed-validation-800-0e8869cba.webp 800w, /generated/assets/jsonfeed-feed-validation-1200-0e8869cba.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/jsonfeed-feed-validation-200-5668694b6.png 200w, /generated/assets/jsonfeed-feed-validation-400-5668694b6.png 400w, /generated/assets/jsonfeed-feed-validation-800-5668694b6.png 800w, /generated/assets/jsonfeed-feed-validation-1200-5668694b6.png 1200w\" type=\"image/png\"><img src=\"/generated/assets/jsonfeed-feed-validation-800-5668694b6.png\" alt=\"Valid feed validated on JSONFeed.org\" width=\"1416\" height=\"448\"></picture></a></p><p>Although I haven’t seen any breakthrough of this as a technology, I’ll keep it running next to the XML feed and will monitor all requests. This feed is <a href=\"https://validator.jsonfeed.org/?url=https%3A%2F%2Ffewald.net%2Ffeed.json\">compliant to the standard</a>.</p><p>To get it to work, I had to adjust my template a little bit and also use <code class=\"language-plaintext highlighter-rouge\">jsonify</code> from the liquid template language. Overall, it was easier to get an XML feed to work as JSON is very picky about escaping of double quotes and other special HTML characters. My template for Jekyll is saved as <code class=\"language-plaintext highlighter-rouge\">feed.json</code> and looks as follows:</p><figure class=\"highlight\"><pre><code class=\"language-liquid\" data-lang=\"liquid\">---layout: null---{    \"version\": \"https://jsonfeed.org/version/1.1\",    \"title\": <span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">title</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">jsonify</span><span class=\"w\"> </span><span class=\"p\">}}</span>,    \"description\": <span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">description</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">strip_newlines</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">jsonify</span><span class=\"w\"> </span><span class=\"p\">}}</span>,    \"language\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">language</span><span class=\"w\"> </span><span class=\"p\">}}</span>\",    \"home_page_url\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">}}{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">baseurl</span><span class=\"w\"> </span><span class=\"p\">}}</span>\",    \"feed_url\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">}}{{</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">baseurl</span><span class=\"w\"> </span><span class=\"p\">}}</span>/feed.json\",    \"items\": [        <span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">for</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"w\"> </span><span class=\"nt\">in</span><span class=\"w\"> </span><span class=\"nv\">site.posts</span><span class=\"w\"> </span><span class=\"na\">limit</span><span class=\"o\">:</span><span class=\"mi\">100</span><span class=\"w\"> </span><span class=\"p\">%}</span>        {            \"id\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">prepend</span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">baseurl</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">prepend</span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">}}</span>\",            \"url\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">prepend</span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">baseurl</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">prepend</span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"nv\">site</span><span class=\"p\">.</span><span class=\"nv\">url</span><span class=\"w\"> </span><span class=\"p\">}}</span>\",            \"title\": <span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"p\">.</span><span class=\"nv\">title</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">jsonify</span><span class=\"w\"> </span><span class=\"p\">}}</span>,            \"content_html\": <span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"p\">.</span><span class=\"nv\">content</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">strip_newlines</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">jsonify</span><span class=\"w\"> </span><span class=\"p\">}}</span>,            \"date_published\": \"<span class=\"p\">{{</span><span class=\"w\"> </span><span class=\"nv\">post</span><span class=\"p\">.</span><span class=\"nv\">date</span><span class=\"w\"> </span><span class=\"p\">|</span><span class=\"w\"> </span><span class=\"nf\">date_to_xmlschema</span><span class=\"w\"> </span><span class=\"p\">}}</span>\"        }<span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">unless</span><span class=\"w\"> </span><span class=\"nb\">forloop.last</span><span class=\"w\"> </span><span class=\"p\">%}</span>,<span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"kr\">endunless</span><span class=\"w\"> </span><span class=\"p\">%}</span>        <span class=\"p\">{%</span><span class=\"w\"> </span><span class=\"nt\">endfor</span><span class=\"w\"> </span><span class=\"p\">%}</span>    ]}</code></pre></figure>",
      "date_published": "2022-07-05T18:42:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/06/26/wisdom.html",
      "url": "https://fewald.net/2022/06/26/wisdom.html",
      "title": "Wisdom",
      "content_html": "<blockquote>  <p>Buy the nicest screwdrivers you can afford.</p></blockquote><p><a href=\"https://github.com/merlinmann/wisdom/blob/master/wisdom.md\">Source</a></p>",
      "date_published": "2022-06-26T18:37:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/06/08/how-much-precision-do-you-need.html",
      "url": "https://fewald.net/2022/06/08/how-much-precision-do-you-need.html",
      "title": "How much Precision do you need?",
      "content_html": "<p>Probably not so much, or at least less than you might think.</p><p>We often need to store dates and times in a database and retrieve them later. I have seen people choosing to store those in the highest precision available. For example, in Python the precision for a <code class=\"language-plaintext highlighter-rouge\">datetime</code> object is microseconds. That is one millionth of a second!</p><p>For a lot of use cases, this kind of precision is not needed. For manycases, second precision is enough. Examples for use-cases that probably don’t need the highest precision are:</p><ul>  <li>Blog posts</li>  <li>Comments</li>  <li>Social networks</li>  <li>Audit logs</li></ul><p>Furthermore, it makes sense to use the same precision in your code as in your database. By doing so, you get the exact same date and time when you save an object to the database and when you retrieve it later. This is especially important in tests where exact equality is tested.</p><p>One example is MySQL which has the <code class=\"language-plaintext highlighter-rouge\">DATETIME(3)</code> type where the number denotes the sub-second precision. Defining the precision explicitly makes it not only more predictable in your code, it also avoid wasting space.</p><p>As usual, it depends on the use case. It does make sense to think a little bit about the precision to avoid problems later in the development process.</p>",
      "date_published": "2022-06-08T01:10:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/06/06/jekyll-compose.html",
      "url": "https://fewald.net/2022/06/06/jekyll-compose.html",
      "title": "Jekyll Compose",
      "content_html": "<p>I was looking for an easy way to create new jekyll posts via command line and found the plugin <a href=\"https://github.com/jekyll/jekyll-compose\">Jekyll compose</a>. I am very late to the party but wanted to give this a little bit more visibility as I have used a more bare-bones Jekyll beforehand.</p><p>Installation is simple via:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">gem <span class=\"s1\">'jekyll-compose'</span>, group: <span class=\"o\">[</span>:jekyll-plugins]bundle</code></pre></figure><p>Usage:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">bundle <span class=\"nb\">exec </span>jekyll post <span class=\"s2\">\"My new post\"</span></code></pre></figure>",
      "date_published": "2022-06-06T19:12:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/06/06/full-url-google-chrome.html",
      "url": "https://fewald.net/2022/06/06/full-url-google-chrome.html",
      "title": "Show full URL in Google Chrome",
      "content_html": "<p>To show the full URL in Chrome, right click on the address bar and check “Always show full URLs”. This shows the http(s) portion of the URL in all cases and not only the domain name.</p><p><a href=\"/assets/full-url-google-chrome.png\"><picture><source srcset=\"/generated/assets/full-url-google-chrome-200-d93b35daa.avif 200w, /generated/assets/full-url-google-chrome-400-d93b35daa.avif 400w, /generated/assets/full-url-google-chrome-800-d93b35daa.avif 800w, /generated/assets/full-url-google-chrome-856-d93b35daa.avif 856w\" type=\"image/avif\"><source srcset=\"/generated/assets/full-url-google-chrome-200-7665aa35b.webp 200w, /generated/assets/full-url-google-chrome-400-7665aa35b.webp 400w, /generated/assets/full-url-google-chrome-800-7665aa35b.webp 800w, /generated/assets/full-url-google-chrome-856-7665aa35b.webp 856w\" type=\"image/webp\"><source srcset=\"/generated/assets/full-url-google-chrome-200-b068b5fe3.png 200w, /generated/assets/full-url-google-chrome-400-b068b5fe3.png 400w, /generated/assets/full-url-google-chrome-800-b068b5fe3.png 800w, /generated/assets/full-url-google-chrome-856-b068b5fe3.png 856w\" type=\"image/png\"><img src=\"/generated/assets/full-url-google-chrome-800-b068b5fe3.png\" alt=\"Show full URL in Google Chrome\" width=\"856\" height=\"744\"></picture></a></p>",
      "date_published": "2022-06-06T03:00:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/05/29/sunset-daly-city-california.html",
      "url": "https://fewald.net/2022/05/29/sunset-daly-city-california.html",
      "title": "Sunset in Daly City, California",
      "content_html": "<p><a href=\"/assets/sunset-daly-city-california.jpeg\"><picture><source srcset=\"/generated/assets/sunset-daly-city-california-200-b6585424c.avif 200w, /generated/assets/sunset-daly-city-california-400-b6585424c.avif 400w, /generated/assets/sunset-daly-city-california-640-b6585424c.avif 640w\" type=\"image/avif\"><source srcset=\"/generated/assets/sunset-daly-city-california-200-584cc215c.webp 200w, /generated/assets/sunset-daly-city-california-400-584cc215c.webp 400w, /generated/assets/sunset-daly-city-california-640-584cc215c.webp 640w\" type=\"image/webp\"><source srcset=\"/generated/assets/sunset-daly-city-california-200-84d537b27.jpeg 200w, /generated/assets/sunset-daly-city-california-400-84d537b27.jpeg 400w, /generated/assets/sunset-daly-city-california-640-84d537b27.jpeg 640w\" type=\"image/jpeg\"><img src=\"/generated/assets/sunset-daly-city-california-640-84d537b27.jpeg\" alt=\"Sunset in Daly City, California\" width=\"640\" height=\"427\"></picture></a></p>",
      "date_published": "2022-05-29T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/2022/05/28/hermes-imessage-cli.html",
      "url": "https://fewald.net/2022/05/28/hermes-imessage-cli.html",
      "title": "Introducing hermes: A CLI tool for Apple iMessage",
      "content_html": "<p>I just released the first version v0.1.0 of <a href=\"https://github.com/f-ewald/hermes\">hermes on my Github</a> account. Hermes is named after the Greek god - the messenger of Mount Olympus.</p><p>With hermes you can analyze you iMessage display statistics of your iMessage database on the command line and extract conversations. The supported formats at this time are JSON, plain text and YAML.</p><p>An example looks like this:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ hermes statisticsTotal messages:    100000Received messages: 40000Sent messages:     60000Daily Average:     45.66Monthly Average:   &lt;Not available&gt;Yearly Average:    &lt;Not available&gt;Chats:             30</code></pre></div></div><h2 id=\"installing-hermes\">Installing hermes</h2><p>To install hermes, you can follow the instructions in the <a href=\"https://github.com/f-ewald/hermes/README.md\">readme</a>.</p><p>There are two ways of installing hermes.</p><ol>  <li>Download the latest binary from the <a href=\"https://github.com/f-ewald/hermes/releases\">releases page</a>. I am providing 64 bit binaries for both Intel and ARM architectures for MacOS.</li>  <li>Build hermes from source with <code class=\"language-plaintext highlighter-rouge\">go install github.com/f-ewald/hermes@latest</code>. This will get you the most up to date version.</li></ol><p>After installing it, you can use it with either <code class=\"language-plaintext highlighter-rouge\">hermes</code> or <code class=\"language-plaintext highlighter-rouge\">./hermes</code>, depending on your installation location.</p><h2 id=\"usage\">Usage</h2><p>The simplest way to get started is to call <code class=\"language-plaintext highlighter-rouge\">hermes</code> directly and follow the instructions from the help. At the moment, there are two main commands available: <code class=\"language-plaintext highlighter-rouge\">statistics</code> and <code class=\"language-plaintext highlighter-rouge\">conversations</code>.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>% ./hermesHermes is a command-line interface for iMessage databases.You can use it to analyze and display retrieveConversations and view statistics.Usage:  hermes [command]Available Commands:  check        Validate the environment  completion   Generate the autocompletion script for the specified shell  conversation Show retrieveConversations, find participants  help         Help about any command  statistics   Display message statisticsFlags:      --config string     config file (default is $HOME/.hermes.yaml)  -d, --database string   Full path to the chat database if it is different than the default path.  -h, --help              help for hermes  -o, --output string     The output format. Can be either json, yaml or text (default \"text\")Use \"hermes [command] --help\" for more information about a command.</code></pre></div></div><p>You can view statistics with the <code class=\"language-plaintext highlighter-rouge\">statistics</code> command.</p><p>To display conversations, use the <code class=\"language-plaintext highlighter-rouge\">conversations list</code> command. This returns a list of all conversations sorted by internal id together with the participants. If this is a one-to-one conversation, there will be only one participant. Group conversations have multiple participants.</p><p>To show the conversation, use the <code class=\"language-plaintext highlighter-rouge\">conversation get &lt;id&gt;</code> command. Replace <code class=\"language-plaintext highlighter-rouge\">&lt;id&gt;</code> with the unique conversation identifier that can be obtained from the list command. The conversation will contain the participants and all messages ordered by date. Each message is prefixed with the unique participant identifier.</p><h2 id=\"troubleshooting\">Troubleshooting</h2><p>The most common issue is that there is no access to the iMessage database due to MacOS’ system restrictions. This can be solved by giving the terminal “Full Disk Access” in the system settings und the “Security Tab”.</p><p><a href=\"/assets/hermes-full-disk-access.png\"><picture><source srcset=\"/generated/assets/hermes-full-disk-access-200-d876db6f4.avif 200w, /generated/assets/hermes-full-disk-access-400-d876db6f4.avif 400w, /generated/assets/hermes-full-disk-access-800-d876db6f4.avif 800w, /generated/assets/hermes-full-disk-access-1200-d876db6f4.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/hermes-full-disk-access-200-28b27bc49.webp 200w, /generated/assets/hermes-full-disk-access-400-28b27bc49.webp 400w, /generated/assets/hermes-full-disk-access-800-28b27bc49.webp 800w, /generated/assets/hermes-full-disk-access-1200-28b27bc49.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/hermes-full-disk-access-200-4fcf129b0.png 200w, /generated/assets/hermes-full-disk-access-400-4fcf129b0.png 400w, /generated/assets/hermes-full-disk-access-800-4fcf129b0.png 800w, /generated/assets/hermes-full-disk-access-1200-4fcf129b0.png 1200w\" type=\"image/png\"><img src=\"/generated/assets/hermes-full-disk-access-800-4fcf129b0.png\" alt=\"Full Disk Access in the System security settings\" width=\"1560\" height=\"1398\"></picture></a></p><h2 id=\"future-plans\">Future plans</h2><p>I am planning to write tests for main parts of the software. Right now, there are no known bugs, but is not in an ideal state. After this, I am planning to improve the help and access to the database. Due to MacOS’ increased security features over the last years, you currently need to give your terminal full disk access or copy the database to another location, both of those approaches are not ideal.</p><h2 id=\"contributingbug-reporting\">Contributing/Bug Reporting</h2><p>If you want to contribute to hermes, feel free to get in touch with me or create a pull request on Github.</p><p>If you have any interesting ideas for statistics that you would like to display but don’t know how to implement them, you can also get in touch with me via email via hello@.</p>",
      "date_published": "2022-05-28T02:00:00+00:00"
    },
    {
      "id": "https://fewald.net/programming/2022/05/11/golang-generics.html",
      "url": "https://fewald.net/programming/2022/05/11/golang-generics.html",
      "title": "Go 1.18 supports generics",
      "content_html": "<p>Go (finally) supports generics in the version 1.18. My favorite editor, <a href=\"https://www.jetbrains.com/go/\">GoLand</a>, took a while to fully adopt generics. I have played around with them over the past days and like them so far. Although almost nothing beats the simplicity of <code class=\"language-plaintext highlighter-rouge\">interface{}</code>, I do like the warnings when the wrong type is being used. I am curious to see when the popular libraries adopt generics.</p>",
      "date_published": "2022-05-11T02:00:00+00:00"
    },
    {
      "id": "https://fewald.net/2021/04/23/coredns_config.html",
      "url": "https://fewald.net/2021/04/23/coredns_config.html",
      "title": "My minimal CoreDNS config",
      "content_html": "<p>I am using the following minimal <a href=\"https://coredns.io\">CoreDNS</a> config, savd as <code class=\"language-plaintext highlighter-rouge\">Corefile</code>. This applies a one hour cache to all requests before forwarding the to the Google DNS servers. All requests are logged to stdout. In my setup this saves me about 20ms for every DNS roundtrip and improves my internet speed by quite a bit.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>. {    cache 3600    forward . 8.8.8.8:53 8.8.4.4:53    log}</code></pre></div></div>",
      "date_published": "2021-04-23T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/webdesign/development/2021/04/22/fontshare_fonts.html",
      "url": "https://fewald.net/webdesign/development/2021/04/22/fontshare_fonts.html",
      "title": "Free fonts for private and commercial use",
      "content_html": "<p>A few weeks ago I stumbled upon <a href=\"https://www.fontshare.com/\">Fontshare</a>. This website offers free fonts for private and commercial use and is worth checking out.</p><p>From their terms:</p><blockquote>  <p>Fontshare fonts are 100% free for personal and commercial use, however, they�re not open-source and are governed by an ITF EULA. […]</p></blockquote>",
      "date_published": "2021-04-22T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/golang/development/2020/06/16/golang-private-repository.html",
      "url": "https://fewald.net/golang/development/2020/06/16/golang-private-repository.html",
      "title": "Access private repositories in Go",
      "content_html": "<p>Good <a href=\"https://medium.com/easyread/today-i-learned-fix-go-get-private-repository-return-error-terminal-prompts-disabled-8c5549d89045\">article</a> how to access a private repository via <code class=\"language-plaintext highlighter-rouge\">go get</code>.</p><p>TLDR:</p><p>For SSH access:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git config --global url.\"git@gitlab.com:\".insteadOf \"https://gitlab.com/\"$ cat ~/.gitconfig[url \"git@gitlab.com:\"] insteadOf = https://gitlab.com/</code></pre></div></div>",
      "date_published": "2020-06-16T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/photo/2020/05/19/sunday-afternoon-clouds.html",
      "url": "https://fewald.net/photo/2020/05/19/sunday-afternoon-clouds.html",
      "title": "Sunday Afternoon in the Bay Area",
      "content_html": "<p><a href=\"/assets/sunday-afternoon-cloudy.jpg\"><picture><source srcset=\"/generated/assets/sunday-afternoon-cloudy-200-91193cbbc.avif 200w, /generated/assets/sunday-afternoon-cloudy-400-91193cbbc.avif 400w, /generated/assets/sunday-afternoon-cloudy-800-91193cbbc.avif 800w, /generated/assets/sunday-afternoon-cloudy-1200-91193cbbc.avif 1200w\" type=\"image/avif\"><source srcset=\"/generated/assets/sunday-afternoon-cloudy-200-3781337f7.webp 200w, /generated/assets/sunday-afternoon-cloudy-400-3781337f7.webp 400w, /generated/assets/sunday-afternoon-cloudy-800-3781337f7.webp 800w, /generated/assets/sunday-afternoon-cloudy-1200-3781337f7.webp 1200w\" type=\"image/webp\"><source srcset=\"/generated/assets/sunday-afternoon-cloudy-200-0e0a7a9c2.jpg 200w, /generated/assets/sunday-afternoon-cloudy-400-0e0a7a9c2.jpg 400w, /generated/assets/sunday-afternoon-cloudy-800-0e0a7a9c2.jpg 800w, /generated/assets/sunday-afternoon-cloudy-1200-0e0a7a9c2.jpg 1200w\" type=\"image/jpeg\"><img src=\"/generated/assets/sunday-afternoon-cloudy-800-0e0a7a9c2.jpg\" alt=\"Sunday Afternoon in the Bay Area with clouds\" width=\"4032\" height=\"3024\"></picture></a></p><p>Last Sunday afternoon was cloudy, not very crowded but nevertheless good weather.</p>",
      "date_published": "2020-05-19T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/k8s/development/2020/05/09/kubernetes-namespace.html",
      "url": "https://fewald.net/k8s/development/2020/05/09/kubernetes-namespace.html",
      "title": "Temporary Namespace for Kubernetes",
      "content_html": "<p>To set a temporary namespace for k8s simply create an alias like so:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>alias k='kubectl -n NAMESPACE '</code></pre></div></div><p>Then use it:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>k get pods</code></pre></div></div><p>After the shell session ends it is set to default.</p>",
      "date_published": "2020-05-09T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/misc/2020/05/05/rt-live.html",
      "url": "https://fewald.net/misc/2020/05/05/rt-live.html",
      "title": "Rt.live",
      "content_html": "<p>Nice visualization of the new infections by state: rt.live.</p><p>Update 2022/05/10: The website is no longer active and I removed the link.</p>",
      "date_published": "2020-05-05T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/misc/2020/04/28/data-visualization.html",
      "url": "https://fewald.net/misc/2020/04/28/data-visualization.html",
      "title": "Apple's COVID Mobility Data",
      "content_html": "<p>Apple’s data has now been published as an <a href=\"https://kieranhealy.org/blog/archives/2020/04/23/apples-covid-mobility-data/\">R package</a>. The visualizations for the linked blog post can be found in <a href=\"https://github.com/kjhealy/apple_covid_post\">this</a> repository.</p>",
      "date_published": "2020-04-28T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/nginx/development/2020/04/19/413-nginx-ingress-k8s.html",
      "url": "https://fewald.net/nginx/development/2020/04/19/413-nginx-ingress-k8s.html",
      "title": "413 Request Entity Too Large in nginx k8s ingress",
      "content_html": "<p>I wanted to enable a file upload. Whenever I was uploading I received the error code “413 Request Entity Too Large” from the nginx ingress controller in kubernetes.</p><p>I found the solution <a href=\"https://imti.co/413-request-entity-too-large/\">here</a>.</p><p>Use the following annotation to allow unlimited upload size:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>nginx.ingress.kubernetes.io/proxy-body-size: \"0\"</code></pre></div></div><p>In my case, the ingress file definition now looks like this:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---apiVersion: networking.k8s.io/v1beta1 # for versions before 1.14 use extensions/v1beta1kind: Ingressmetadata:  name: my-name  namespace: my-namespace  annotations:    nginx.ingress.kubernetes.io/rewrite-target: /$1    nginx.ingress.kubernetes.io/proxy-body-size: \"0\"spec:  # The specs, omitted for readability</code></pre></div></div>",
      "date_published": "2020-04-19T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/misc/2020/02/22/writic-app.html",
      "url": "https://fewald.net/misc/2020/02/22/writic-app.html",
      "title": "Launching Writic.app",
      "content_html": "<p>Today, I am launching <a href=\"https://writic.app\">writic.app</a>. I was always looking for a solution to keep a journal which is online but private - something which is very hard to find across all the available social networks. The following features are essential to me:</p><ul>  <li>A quick way to write down a thought, long or short</li>  <li>Ability to upload and annotate pictures</li>  <li>Location history</li></ul><p>These kinds of data is obviously very sensitive and I believe some things should stay private. For this reason I created Writic. I wanted to create the ability to access this data from anywhere where there is internet and also at the same time offer access from the smartphone as well as the browser. Many of the apps I have tested do either one thing only or are full of features I don’t really require. With Writic, I am trying to keep it simple and avoid any clutter, especially ads and selling of any personal data.</p><p>I am planning to keep it initially free of charge and later introduce either a pay-what-you-want or a minimal fee like $1/month, depending on the user base. Another option I am considering is a “pro” version with more advanced features.</p><p>Things I am currently considering for the roadmap are:</p><ul>  <li><strong>Favorite posts</strong>, to give the ability to highlight certain entries in the future</li>  <li><strong>Throwbacks</strong>, showing favorite posts or an “on this day”-feature</li>  <li><strong>Mood Tracker</strong>, to keep track of the mood throughout the day. This can either be a manual approach with three smilies or something automated like sentiment analysis.</li></ul>",
      "date_published": "2020-02-22T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/link/2019/06/17/the-hard-way.html",
      "url": "https://fewald.net/link/2019/06/17/the-hard-way.html",
      "title": "Things learned the hard way",
      "content_html": "<p>Interesting list of things that the author learned <a href=\"https://blog.juliobiason.net/thoughts/things-i-learnt-the-hard-way/\">the hard way</a></p><blockquote>  <p>Sometimes, you’ll have to say no: No, I can’t do it; no, it can’t be made in this time; no, I don’t feel capable of doing this; no, I don’t feel comfortable writing this.</p></blockquote>",
      "date_published": "2019-06-17T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/photo/2019/05/05/getty-center.html",
      "url": "https://fewald.net/photo/2019/05/05/getty-center.html",
      "title": "The Getty Center - Los Angeles, CA",
      "content_html": "<p><a href=\"/assets/getty-center-los-angeles.jpg\"><picture><source srcset=\"/generated/assets/getty-center-los-angeles-200-b16568429.avif 200w, /generated/assets/getty-center-los-angeles-400-b16568429.avif 400w, /generated/assets/getty-center-los-angeles-800-b16568429.avif 800w, /generated/assets/getty-center-los-angeles-1024-b16568429.avif 1024w\" type=\"image/avif\"><source srcset=\"/generated/assets/getty-center-los-angeles-200-f27e4156b.webp 200w, /generated/assets/getty-center-los-angeles-400-f27e4156b.webp 400w, /generated/assets/getty-center-los-angeles-800-f27e4156b.webp 800w, /generated/assets/getty-center-los-angeles-1024-f27e4156b.webp 1024w\" type=\"image/webp\"><source srcset=\"/generated/assets/getty-center-los-angeles-200-2b716fc2c.jpg 200w, /generated/assets/getty-center-los-angeles-400-2b716fc2c.jpg 400w, /generated/assets/getty-center-los-angeles-800-2b716fc2c.jpg 800w, /generated/assets/getty-center-los-angeles-1024-2b716fc2c.jpg 1024w\" type=\"image/jpeg\"><img src=\"/generated/assets/getty-center-los-angeles-800-2b716fc2c.jpg\" alt=\"The Getty Center, Los Angeles, California, USA\" width=\"1024\" height=\"769\"></picture></a></p><p>Perfect weather and beautiful view.</p>",
      "date_published": "2019-05-05T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/links/2019/04/01/kubernetes-or-not.html",
      "url": "https://fewald.net/links/2019/04/01/kubernetes-or-not.html",
      "title": "Sometimes Kubernetes can be too much",
      "content_html": "<p>Interesting blog entry about <a href=\"https://matthias-endler.de/2019/maybe-you-dont-need-kubernetes/\">Kubernetes</a>:</p><blockquote>  <p>On top of that, the Kubernetes ecosystem is still rapidly evolving. It takes a fair amount of time and energy to stay up-to-date with the best practices and latest tooling. Kubectl, minikube, kubeadm, helm, tiller, kops, oc - the list goes on and on. Not all tools are necessary to get started with Kubernetes, but it’s hard to know which ones are, so you have to be at least aware of them. Because of that, the learning curve is quite steep.</p></blockquote>",
      "date_published": "2019-04-01T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/photo/2019/03/31/java-on-ocean.html",
      "url": "https://fewald.net/photo/2019/03/31/java-on-ocean.html",
      "title": "Java on Ocean - San Francisco, CA",
      "content_html": "<p><a href=\"/assets/java-on-ocean.jpg\"><picture><source srcset=\"/generated/assets/java-on-ocean-200-f80510b7d.avif 200w, /generated/assets/java-on-ocean-400-f80510b7d.avif 400w, /generated/assets/java-on-ocean-800-f80510b7d.avif 800w, /generated/assets/java-on-ocean-1024-f80510b7d.avif 1024w\" type=\"image/avif\"><source srcset=\"/generated/assets/java-on-ocean-200-995f95bc3.webp 200w, /generated/assets/java-on-ocean-400-995f95bc3.webp 400w, /generated/assets/java-on-ocean-800-995f95bc3.webp 800w, /generated/assets/java-on-ocean-1024-995f95bc3.webp 1024w\" type=\"image/webp\"><source srcset=\"/generated/assets/java-on-ocean-200-8e85e3381.jpg 200w, /generated/assets/java-on-ocean-400-8e85e3381.jpg 400w, /generated/assets/java-on-ocean-800-8e85e3381.jpg 800w, /generated/assets/java-on-ocean-1024-8e85e3381.jpg 1024w\" type=\"image/jpeg\"><img src=\"/generated/assets/java-on-ocean-800-8e85e3381.jpg\" alt=\"Java on Ocean, San Francisco, California\" width=\"1024\" height=\"768\"></picture></a></p>",
      "date_published": "2019-03-31T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/security/2019/03/30/decrypt-chrome-passwords-windows.html",
      "url": "https://fewald.net/security/2019/03/30/decrypt-chrome-passwords-windows.html",
      "title": "Decrypting Passwords stored in Chrome on Windows",
      "content_html": "<p>Interesting <a href=\"https://hackernoon.com/why-you-should-never-save-passwords-on-chrome-or-firefox-96b770cfd0d0\">blog post</a> about decrypting passwords stored in Chrome on Windows.</p><p>I personally switched from Chrome to the iCloud Keychain and 1Password.</p>",
      "date_published": "2019-03-30T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/python/macos/2019/03/28/setup-pip-build.html",
      "url": "https://fewald.net/python/macos/2019/03/28/setup-pip-build.html",
      "title": "Setting up PIP and PyPi on Mac OS",
      "content_html": "<h2 id=\"setting-up-pip\">Setting up PIP</h2><p>Pip can be mostly used out of the box. To configure a different repository the configuration file needs to be edited.</p><p>The configuration file should be stored in <code class=\"language-plaintext highlighter-rouge\">~/Library/Application Support/pip/pip.conf</code> according to this <a href=\"https://stackoverflow.com/a/44539968\">post</a>. If the folder and file are not existing, simply create those and then define the extra repository as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>[global]extra-index-url = http://download.example.com</code></pre></div></div><p>After this is done, <code class=\"language-plaintext highlighter-rouge\">pip install ...</code> will also look in the extra repository for the corresponding package.</p><h2 id=\"uploading-to-pypi\">Uploading to PyPi</h2><p>Uploading your package to PyPi is quite simple with <a href=\"https://pypi.org/project/twine/\">twine</a>.</p><p>First, you need to package your python application as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python setup.py sdist bdist_wheel</code></pre></div></div><p>This generates a source package in the tar.gz format and a compiled wheel package. If the source code is pure Python, this package will be platform independent.</p><p>The following step is optional and tests the upload to the PyPi test repository:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>twine upload --repository-url https://test.pypi.org/legacy/ dist/*</code></pre></div></div><p>As the next step this package needs to be uploaded which can be achieved as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>twine upload dist/*</code></pre></div></div><p>That’s it. You can now see your package on PyPi.org. It is important to note that a version number cannot exist twice and cannot be updated. To do so, you have to manually delete the old version first.</p>",
      "date_published": "2019-03-28T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/security/2019/03/25/facebook-passwords.html",
      "url": "https://fewald.net/security/2019/03/25/facebook-passwords.html",
      "title": "Facebook stored millions of passwords in plaintext",
      "content_html": "<p><a href=\"https://krebsonsecurity.com/2019/03/facebook-stored-hundreds-of-millions-of-user-passwords-in-plain-text-for-years/\">Brian Krebs</a></p><blockquote>  <p>Hundreds of millions of Facebook users had their account passwords stored in plain text and searchable by thousands of Facebook employees — in some cases going back to 2012, KrebsOnSecurity has learned. Facebook says an ongoing investigation has so far found no indication that employees have abused access to this data.</p></blockquote><p>Facebook’s statement can be found <a href=\"https://newsroom.fb.com/news/2019/03/keeping-passwords-secure/\">here</a>.</p>",
      "date_published": "2019-03-25T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/helm/2019/03/24/helm-charts.html",
      "url": "https://fewald.net/helm/2019/03/24/helm-charts.html",
      "title": "Quick introduction to using and serving Helm Charts",
      "content_html": "<p>For this posting I assume that <code class=\"language-plaintext highlighter-rouge\">kubectl</code> and <code class=\"language-plaintext highlighter-rouge\">helm</code> are installed and working. If unsure, this can be checked via <code class=\"language-plaintext highlighter-rouge\">kubectl get pods --all namespaces</code> which should return a pod with the name <code class=\"language-plaintext highlighter-rouge\">tiller-deploy-[...]</code>. For these examples I am using <a href=\"https://kubernetes.io/docs/tasks/tools/install-minikube/\">minikube</a>.</p><p>To create an initial helm chart, run the command <code class=\"language-plaintext highlighter-rouge\">helm create &lt;name&gt;</code>. This creates a new chart template with the name <code class=\"language-plaintext highlighter-rouge\">&lt;name&gt;</code>. Of course this should be replaced with the actual name.</p><p>How to create the actual chart will be part of another blog entry.</p><p>To package that chart, run the following.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ cd mychart/$ helm package .</code></pre></div></div><p>A webserver for hosting Charts is the <a href=\"https://chartmuseum.com\">Chartmuseum</a>. Generally every server which can serve static files will work fine although the <code class=\"language-plaintext highlighter-rouge\">index.yaml</code> has to be created. The quickest way for testing is to deploy the Chartmuseum via helm itself:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ helm install --set env.open.DISABLE_API=false stable/chartmuseum</code></pre></div></div><p>It is important to note that <code class=\"language-plaintext highlighter-rouge\">DISABLE_API</code> needs to be set to <code class=\"language-plaintext highlighter-rouge\">false</code>, otherwise no uploads will be possible.</p><p>To upload the packaged chart to Chartmuseum, type the following <code class=\"language-plaintext highlighter-rouge\">curl</code> command:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ curl --data-binary \"@mychart-0.1.0.tgz\" http://localhost:8080/api/charts</code></pre></div></div><p>Alternatively, the <a href=\"https://github.com/chartmuseum/helm-push\">helm-push-plugin</a> can be used with:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>helm plugin install https://github.com/chartmuseum/helm-push</code></pre></div></div><p>To use the chart in helm, the repository has to be registered with helm:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ helm repo add chartmuseum http://localhost:8080</code></pre></div></div><p>And then the chart can be pushed via:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ helm push mychart/ chartmuseum</code></pre></div></div><p>More information can be found in the <a href=\"https://chartmuseum.com/docs/\">Chartmuseum documentation</a>. Lastly, this <a href=\"https://github.com/helm/helm/blob/master/docs/chart_repository.md\">guide</a> provides step-by-step information for using a helm chart repository.</p><p>To test the newly uploaded chart the repositories need to be updated first with:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ helm repo updateHang tight while we grab the latest from your chart repositories......Skip local chart repository...Successfully got an update from the \"chartmuseum\" chart repository...Successfully got an update from the \"stable\" chart repositoryUpdate Complete. ⎈ Happy Helming!⎈</code></pre></div></div><p><strong>Note</strong>: At the time of writing, helm does not automatically update the charts before installing and thus it is required to update before a search can occur. This feature is <a href=\"https://github.com/helm/helm/issues/3249\">proposed</a> for Helm 3.</p><p>To show all charts of the helm simply run:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ helm search chartmuseum/</code></pre></div></div><p>You should now see your newly created chart.</p>",
      "date_published": "2019-03-24T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/python/2019/03/04/febase62-v1-1-0.html",
      "url": "https://fewald.net/python/2019/03/04/febase62-v1-1-0.html",
      "title": "febase62 v1.1.0 now available",
      "content_html": "<p>I just released version 1.1.0 of <code class=\"language-plaintext highlighter-rouge\">febase62</code>. With this update it is now possible to encode byte strings as base62. The method signature stays the same:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>&gt;&gt;&gt; from febase62.base62 import Base62&gt;&gt;&gt; encoder = Base62()&gt;&gt;&gt; encoder.encode(b'\\aa')'6Sd'</code></pre></div></div><p>The old way still works:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>&gt;&gt;&gt; from febase62.base62 import Base62&gt;&gt;&gt; encoder = Base62()&gt;&gt;&gt; encoder.encode(1337)'LZ'</code></pre></div></div><p>The package can be installed via <code class=\"language-plaintext highlighter-rouge\">pip install febase62</code> and the source code can be found <a href=\"https://bitbucket.org/f-ewald/febase62\">here</a>.</p>",
      "date_published": "2019-03-04T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/macos/2019/02/25/uuid-mac-os-shell.html",
      "url": "https://fewald.net/macos/2019/02/25/uuid-mac-os-shell.html",
      "title": "Creating UUID under Mac OS Shell",
      "content_html": "<p>I have the following alias in my <code class=\"language-plaintext highlighter-rouge\">.bashrc</code> to create UUID’s quickly from the shell:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>alias uuid=\"uuidgen | tr -d - | tr -d '\\n' | tr '[:upper:]' '[:lower:]' | pbcopy &amp;&amp; pbpaste &amp;&amp; echo\"</code></pre></div></div><p>This generates a lowercase uuid and copies it to the clipboard all by calling <code class=\"language-plaintext highlighter-rouge\">uuid</code>.</p>",
      "date_published": "2019-02-25T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/2019/02/17/jps-java-process.html",
      "url": "https://fewald.net/linux/2019/02/17/jps-java-process.html",
      "title": "Find all running java processes",
      "content_html": "<p>With the command <code class=\"language-plaintext highlighter-rouge\">jps</code> you can find all running java processes. This is evenfaster than the example I showed in mid 2018 here.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ jps49799683 Jps</code></pre></div></div>",
      "date_published": "2019-02-17T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/google/2019/02/17/delete-google-appengine.html",
      "url": "https://fewald.net/google/2019/02/17/delete-google-appengine.html",
      "title": "How to Delete Unused Google AppEngine Instances",
      "content_html": "<p>Google AppEngine only allows to have 15 instances maximum per project. I haveset up a build pipeline which automatically deploys a new version every time Iupdate this blog. To delete the older, no longer needed versions I am using thisone liner:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>yes | gcloud app versions delete `gcloud app versions list --filter=TRAFFIC_SPLIT=0 | cut -c 10-24 | grep '[0-9]'`</code></pre></div></div><p>This deletes all versions which are not serving traffic. The only catch is thatthose versions need to have a number in it to be recognized via regex. In my casethe versions automatically have the current date as name so that this isn’t aproblem.</p>",
      "date_published": "2019-02-17T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2018/12/25/apache-spark-dataframe-count-words.html",
      "url": "https://fewald.net/development/2018/12/25/apache-spark-dataframe-count-words.html",
      "title": "Count large amount of words with Apache Spark Dataframe",
      "content_html": "<p>One of the basic examples in Apache Spark and other big data platforms is counting words. This example reads a file into a Apache Spark <code class=\"language-plaintext highlighter-rouge\">DataFrame</code> and then performs basic tasks: Minimum, Maximum, Mean and Standard Deviation are calculated.</p><p>The CSV file I am going to read is taken from <a href=\"https://www.kaggle.com/mrisdal/fake-news/version/1\">Kaggle</a> and contains news which are either fake or not. For basic analysis, I will get some characteristics from the file. The csv file looks as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\"uuid\",\"ord_in_thread\",\"author\",\"published\",\"title\",\"text\",\"language\",\"crawled\",\"site_url\",\"country\",\"domain_rank\",\"thread_title\",\"spam_score\",\"main_img_url\",\"replies_count\",\"participants_count\",\"likes\",\"comments\",\"shares\",\"type\"\"6a175f46bcd24d39b3e962ad0f29936721db70db\",0,\"Barracuda Brigade\",\"2016-10-26T21:41:00.000+03:00\",\"Muslims BUSTED: They Stole Millions In Gov’t Benefits\",\"Print They should pay all the back all the money plus interest. The entire family and everyone who came in with them need to be deported asap. Why did it take two years to bust them? Here we go again …another group stealing from the government and taxpayers! A group of Somalis stole over four million in government benefits over just 10 months! We’ve reported on numerous cases like this one where the Muslim refugees/immigrants commit fraud by scamming our system…It’s way out of control! More Related\",\"english\",\"2016-10-27T01:49:27.168+03:00\",\"100percentfedup.com\",\"US\",25689,\"Muslims BUSTED: They Stole Millions In Gov’t Benefits\",0,\"http://bb4sp.com/wp-content/uploads/2016/10/Fullscreen-capture-10262016-83501-AM.bmp.jpg\",0,1,0,0,0,\"bias\"</code></pre></div></div><p>First, I import all necessary methods and classes and then establish the connection to Spark via <code class=\"language-plaintext highlighter-rouge\">SparkSession</code>:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>from pyspark.sql.functions import mean as _mean, stddev as _std, min as _min, max as _max, col, explodefrom pyspark.sql import SparkSessionfrom pyspark.ml.feature import Tokenizersession = SparkSession.builder.appName('fake').getOrCreate()</code></pre></div></div><p>In the next step, read the CSV file into a <code class=\"language-plaintext highlighter-rouge\">DataFrame</code>. The several options are required because the CSV file doesn’t follow a standard format. For example it has line breaks within each field which requires encapsulation with double quotes (<code class=\"language-plaintext highlighter-rouge\">\"</code>) for each field.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>df = session.read\\    .format('csv')\\    .option('header', True)\\    .option('multiline', True)\\    .option('mode', 'DROPMALFORMED')\\    .option('quote', '\"')\\    .option('escape', '\"')\\    .load('fake.csv')</code></pre></div></div><p>Then, drop all the columns except the one which is named <code class=\"language-plaintext highlighter-rouge\">text</code>:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>all_columns = df.columnsall_columns.remove('text')df = df.drop(*all_columns)</code></pre></div></div><p>Filter all rows where the text is <code class=\"language-plaintext highlighter-rouge\">None</code> because they provide no value:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>df = df.where(col('text').isNotNull())</code></pre></div></div><p>Tokenize each sentence with the default <code class=\"language-plaintext highlighter-rouge\">Tokenizer</code>. This splits the sentence into an array of words. For example, ‘Hello World.’ becomes <code class=\"language-plaintext highlighter-rouge\">[hello, world]</code>.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>tokenizer = Tokenizer(inputCol='text', outputCol='words')wordsData = tokenizer.transform(df)</code></pre></div></div><p>The output looks as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>wordsData.show()+--------------------+--------------------+|                text|               words|+--------------------+--------------------+|Print They should...|[print, they, sho...||Why Did Attorney ...|[why, did, attorn...||Red State : Fox ...|[red, state, :, ,...||Email Kayla Muell...|[email, kayla, mu...||Email HEALTHCARE ...|[email, healthcar...||Print Hillary goe...|[print, hillary, ...||BREAKING! NYPD Re...|[breaking!, nypd,...||BREAKING! NYPD Re...|[breaking!, nypd,...||Limbaugh said th...|[, limbaugh, said...||Email These peop...|[email, , these, ...||                    |                  []||Who? Comedian. |[, who?, comedian...||Students expresse...|[students, expres...||Email For Republi...|[email, for, repu...||Copyright © 2016 ...|[copyright, ©, 20...||Go to Article A T...|[go, to, article,...||Copyright © 2016 ...|[copyright, ©, 20...||Go to Article Don...|[go, to, article,...||John McNaughton i...|[john, mcnaughton...||Go to Article Dea...|[go, to, article,...|+--------------------+--------------------+only showing top 20 rows</code></pre></div></div><p>In the next step, we need to <code class=\"language-plaintext highlighter-rouge\">explode()</code> each line. This method takes items of an array and inserts one new row for each item of the array. In the example above, <code class=\"language-plaintext highlighter-rouge\">[hello, world]</code>, would become <code class=\"language-plaintext highlighter-rouge\">hello</code> and <code class=\"language-plaintext highlighter-rouge\">world</code>. This is needed as a preparation for counting the words.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>singleWords = wordsData.select(explode(wordsData.words).alias('word'))</code></pre></div></div><p>Again showing the result:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>singleWords.show()+---------+|     word|+---------+|    print||     they||   should||      pay||      all||      the||     back||      all||      the||    money||     plus||interest.||      the||   entire||   family||      and|| everyone||      who||     came||       in|+---------+only showing top 20 rows</code></pre></div></div><p>In the last step, the words need to be grouped and counted.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>counts = singleWords.groupBy('word').count().orderBy('count', ascending=False)</code></pre></div></div><p>This results in the following</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>+----+------+|word| count|+----+------+| the|468400||  of|222781||  to|222298|| and|202780||   a|159362||    |153173||  in|147685||that|104812||  is| 98624|| for| 71160||  on| 58344||  as| 52508||with| 50037||  it| 48248|| are| 47200||this| 44396||  by| 44333||  be| 42129|| was| 41164||have| 39288|+----+------+only showing top 20 rows</code></pre></div></div><p>From that it’s only a small step to getting the basic statistics:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>counts.select(_mean('count').alias('mean'),              _std('count').alias('stddev'),              _min('count').alias('min'),              _max('count').alias('max')).show()+------------------+------------------+---+------+|              mean|            stddev|min|   max|+------------------+------------------+---+------+|24.613804751125944|1217.3337287760087|  1|468400|+------------------+------------------+---+------+</code></pre></div></div>",
      "date_published": "2018-12-25T20:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2018/07/31/kafka-topic-cleanup.html",
      "url": "https://fewald.net/development/2018/07/31/kafka-topic-cleanup.html",
      "title": "Apache Kafka cleanup topics",
      "content_html": "<p>If you want to clean up data from a topic and you don’t have any intermediate storage, it is easy to set the retention time to a few seconds and then wait until the cleanup process kicks in. After that, the retention time can be set to the default value:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>./kafka-configs.sh --zookeeper localhost:2181 --alter --entity-type topics --add-config retention.ms=10000 --entity-name &lt;topic-name&gt;</code></pre></div></div>",
      "date_published": "2018-07-31T11:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2018/07/27/apache-kafka-cheat-sheet.html",
      "url": "https://fewald.net/development/2018/07/27/apache-kafka-cheat-sheet.html",
      "title": "Apache Kafka Cheat Sheet",
      "content_html": "<p>I found <a href=\"http://ronnieroller.com/kafka/cheat-sheet\">this</a> handy cheat sheet for Apache Kafka.</p>",
      "date_published": "2018-07-27T21:00:00+00:00"
    },
    {
      "id": "https://fewald.net/misc/2018/07/26/anki-flashcards.html",
      "url": "https://fewald.net/misc/2018/07/26/anki-flashcards.html",
      "title": "How to learn efficient and effective",
      "content_html": "<p>Whenever I need to memorize things of the form <code class=\"language-plaintext highlighter-rouge\">A -&gt; B</code>, I use <a href=\"https://ankiweb.net/decks/\">Anki</a>. This free software comes for many platforms: Mac OS, Windows, Linux and allows the creation of flashcards. After creating the cards you can then go and quiz yourself and rate how good your answer was. Based on that an algorithm decides how often to show you the same card again. I personally find it super effective – if you don’t cheat.</p>",
      "date_published": "2018-07-26T04:00:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/2018/07/23/ps-aux-grep-java-process.html",
      "url": "https://fewald.net/linux/2018/07/23/ps-aux-grep-java-process.html",
      "title": "Shell: Find all running java processes",
      "content_html": "<p>With the following command you can find all currently running java processes:</p><p><code class=\"language-plaintext highlighter-rouge\">ps aux | grep java</code></p><p>Of course this works for any arbitrary process.</p>",
      "date_published": "2018-07-23T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/json/development/2017/05/20/RSS-better-with-jsonfeed.html",
      "url": "https://fewald.net/json/development/2017/05/20/RSS-better-with-jsonfeed.html",
      "title": "Better feeds with JSONfeed",
      "content_html": "<p>It seems that there is finally a format which can replace the old and often ugly looking XML documents. <a href=\"https://jsonfeed.org/\">JsonFeed</a> is dead-simple and provides the same functionality as XML based feeds. This simple <a href=\"https://jsonfeed.org/version/1\">example</a> illustrates how nice and clean feeds in JSON can look like:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{\t\"version\": \"https://jsonfeed.org/version/1\",\t\"title\": \"My Example Feed\",\t\"home_page_url\": \"https://example.org/\",\t\"feed_url\": \"https://example.org/feed.json\",\t\"items\": [\t\t{\t\t\t\"id\": \"2\",\t\t\t\"content_text\": \"This is a second item.\",\t\t\t\"url\": \"https://example.org/second-item\"\t\t},\t\t{\t\t\t\"id\": \"1\",\t\t\t\"content_html\": \"&lt;p&gt;Hello, world!&lt;/p&gt;\",\t\t\t\"url\": \"https://example.org/initial-post\"\t\t}\t]}</code></pre></div></div><p>In the next days/weeks I will update this blog, which runs on Jekyll, and serve a JSONFeed for those interested.</p>",
      "date_published": "2017-05-20T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/python/2017/02/11/java-enumerations-with-values.html",
      "url": "https://fewald.net/development/python/2017/02/11/java-enumerations-with-values.html",
      "title": "Java Enum with values",
      "content_html": "<p>I wanted to be able to parse Java enums from a JSON file by not only referring to the numerical value of the enum and also the text in the JSON file should not be all uppercase. I wanted to be able to choose a different display value from the inner value in the Java code. So I came up with this code:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/** * This is an enum which has an inner value for each * enumeration value. This allows parsing not only from * numerical values but also by their inner name. */public enum MySampleEnum {    STARTING(\"starting\"),    RUNNING(\"running\");    STOPPING(\"stopping\");    /**     * Inner value of the enumeration.     */    private String text;    /**     * Private constructor.     * @param text Inner value of the enumeration.     */    private JobRunTypeEnum(String text) {        this.text = text;    }    /**     * Returns the string representation of the current enumeration.     * @return Value of the enumeration.     */    @Override    public String toString() {        return text;    }\t/**\t * Gets the right enumeration value from a given String, if it exists.\t * @param text The String to search for.\t * @return Enum value if found, null otherwise.\t */\tpublic static MySampleEnum fromString(String text) {\t    if (text != null) {\t        for (MySampleEnum val : MySampleEnum.values()) {\t            if (text.equalsIgnoreCase(val.text)) {\t                return val;\t            }\t        }\t    }\t    return null;\t}}</code></pre></div></div><p>To achieve this, it is necessary to overwrite the default constructor of the enum and give it a parameter which is a <code class=\"language-plaintext highlighter-rouge\">String</code> with the inner value. Also there needs to be a <code class=\"language-plaintext highlighter-rouge\">private</code> field to hold the value. The <code class=\"language-plaintext highlighter-rouge\">toString()</code> method should return the inner value and is therefore overridden.</p><p>The best thing about this approach is in my eyes, that I am now able to parse the enum from any <code class=\"language-plaintext highlighter-rouge\">String</code> just by calling <code class=\"language-plaintext highlighter-rouge\">MySampleEnum.fromString(\"running\")</code>. This allows me to integrate this in my JSON-parser and create more meaningful JSON objects.</p>",
      "date_published": "2017-02-11T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/python/2017/02/07/base62-encoding-decoding.html",
      "url": "https://fewald.net/development/python/2017/02/07/base62-encoding-decoding.html",
      "title": "Python Base62 Encoder & Decoder",
      "content_html": "<p>Base62 encoding is commonly used for ids where space matters. Many URL shortener service use this and also youtube calculates its video ids in Base62. The advantages are obvious. Instead of using the id <code class=\"language-plaintext highlighter-rouge\">1337</code>, you can just use <code class=\"language-plaintext highlighter-rouge\">LZ</code>. This saves 50% (in this case, usually more) of the overall traffic needed to transmit this id. However, Base62 is <strong>not</strong> an ecryption.</p><p>I created an easy to use Python module and put it on my <a href=\"https://github.com/f-ewald/febase62\">GitHub account</a>. As I try to improve code quality, this module has 100% code coverage and also passes all unit tests.</p>",
      "date_published": "2017-02-07T00:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/vim/2016/12/02/smaller-tabs-in-vim.html",
      "url": "https://fewald.net/development/vim/2016/12/02/smaller-tabs-in-vim.html",
      "title": "Less wide Tabs in VIM",
      "content_html": "<p>To use less space in vim I decided to go with only two spaces for each tab stop. This also improves readability in my eyes. I set the following in my <code class=\"language-plaintext highlighter-rouge\">.vimrc</code> which I found <a href=\"https://leohacker.wordpress.com/2011/07/05/macvim-and-vimrc/\">here</a>:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>set tabstop=4       \" The number of spaces count for a TAB.set softtabstop=2   \" The number of spaces inserted when typing TAB. If not expandtab, type TAB twice, will get one TAB.set shiftwidth=2    \" The number of spaces when auto-indent.set expandtab       \" Use the spaces only.set smarttab        \" Insert appropriate spaces in front of line according to shiftwidth, tabstop, softtabstop.set autoindentset smartindent</code></pre></div></div><p>Interesting: Now I need two tabs to have the correct indent for Markdown source code.</p>",
      "date_published": "2016-12-02T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/dotnet/2016/11/01/newtonsoft-json-dll-in-multiple-projects.html",
      "url": "https://fewald.net/development/dotnet/2016/11/01/newtonsoft-json-dll-in-multiple-projects.html",
      "title": "Using a Newtonsoft.JSON DLL in multpiple projects",
      "content_html": "<p>When building a Visual Studio solution, I had the problem that I got a <code class=\"language-plaintext highlighter-rouge\">TargetInvocationException</code> when calling a method from a referenced DLL file. When running the unit tests on the DLL file, everything seemed to work fine and I could not track down the problem. The inner exception of the exception brought me then on the right track: The Newtonsoft.JSON module was installed in different versions across each DLL file and the main project.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>' Write the exception to the console.Try        response = Await client.Login(\"a\", \"b\")    Catch ex As Exception        Console.WriteLine(ex.InnerException)    End Try</code></pre></div></div><p>The solution is <a href=\"http://stackoverflow.com/a/28291697/636676\">simple</a>. I just had to make sure, that the DLL is used in the same version in every project. This can simply be done with the console of the package manager.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>PM&gt; Update-Package Newtonsoft.JSON -reinstallPM&gt; Update-Package Newtonsoft.JSON</code></pre></div></div>",
      "date_published": "2016-11-01T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2016/10/31/experiences-with-jekyll.html",
      "url": "https://fewald.net/development/2016/10/31/experiences-with-jekyll.html",
      "title": "Experiences with Jekyll (2016)",
      "content_html": "<p>My first post on this blog was in December 2015 and since then, this blog is running with <a href=\"https://jekyllrb.com/\">Jekyll</a>. I want to take the time to summarize my experiences with Jekyll over the past year.</p><p>The first thing I did, was creating a build script which I then used within a git-hook. The build script just runs <code class=\"language-plaintext highlighter-rouge\">jekyll build</code> and then uses <code class=\"language-plaintext highlighter-rouge\">rsync</code> to synchronize the changes to my webserver. This way, I can keep the webserver very light, serving only HTML, CSS and some JavaScript. This script is located in the root folder of the site.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#!/bin/bashjekyll buildrsync --update --progress -r _site/* username@fewald.net:/.../html/</code></pre></div></div><p>Now, every time I write a new post, commit it, it will be automatically published on the web and the corresponding html sites will be updated. This also works for any change in any template file.Since I wanted to allow some dynamic content on my website, I added <a href=\"https://disqus.com/\">Disqus</a>. This adds a comment box under each blog post via JavaScript. With this technique, I am able to keep the sites static but also allow feedback. The Javascript is added just before the closing <code class=\"language-plaintext highlighter-rouge\">body</code> tag which keeps the loading times very low. <a href=\"https://developers.google.com/speed/pagespeed/insights/\">Google Page Speed Insights</a> rates this page currently with a score of 71/100 for mobile and 85/100 for desktop. This is obviously far away from the optimal solution and will be a topic in another post. What I can see so far, the changes to the code should be minor and can be easily integrated into the build script.</p><p>The theme for this site is from the page <a href=\"http://jekyllthemes.org/\">Jekyllthemes</a>. This page hosts a lot of open source themes for Jekyll which are ready to use. A theme consists of a bunch of files, which have to be places in the the main folder. An easy to understand tutorial can be found <a href=\"https://jekyllrb.com/docs/themes/\">here</a>.</p><p>Overall my experience with Jekyll is very positive and it makes creating a website very easy. Of course one loses a lot of dynamic features, but the point that I don’t have to worry about any security issue ever, justifies this decision. Also, load times are much fast, especially on a shared server.</p>",
      "date_published": "2016-10-31T18:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/python/2016/10/28/python-merge-two-lists.html",
      "url": "https://fewald.net/development/python/2016/10/28/python-merge-two-lists.html",
      "title": "Merging two ordered lists efficiently in Python",
      "content_html": "<p>To merge two lists <code class=\"language-plaintext highlighter-rouge\">l1</code>, <code class=\"language-plaintext highlighter-rouge\">l2</code> in python, one can use the following</p><ol>  <li>Create two pointers, <code class=\"language-plaintext highlighter-rouge\">p1</code>, <code class=\"language-plaintext highlighter-rouge\">p2</code></li>  <li>Use a while loop, which is terminated, when one of the lists reaches the end</li>  <li>Compare the entries of the list pairwise at the pointer position    <ul>      <li>If the entries are equal, add <code class=\"language-plaintext highlighter-rouge\">l1[p1]</code> to <code class=\"language-plaintext highlighter-rouge\">result</code> and increment <code class=\"language-plaintext highlighter-rouge\">p1</code> and <code class=\"language-plaintext highlighter-rouge\">p2</code> by 1</li>      <li>If <code class=\"language-plaintext highlighter-rouge\">l1[p1] &lt; l2[p2]</code>, then add <code class=\"language-plaintext highlighter-rouge\">l1[p1]</code> to <code class=\"language-plaintext highlighter-rouge\">result</code>, then increment <code class=\"language-plaintext highlighter-rouge\">p1</code></li>      <li>If <code class=\"language-plaintext highlighter-rouge\">l1[p1] &gt; l2[p2]</code>, then add <code class=\"language-plaintext highlighter-rouge\">l2[p2]</code> to <code class=\"language-plaintext highlighter-rouge\">result</code>, then increment <code class=\"language-plaintext highlighter-rouge\">p2</code></li>    </ul>  </li>  <li>Check, which list is not at the end and concat the tail of this list with <code class=\"language-plaintext highlighter-rouge\">result</code></li></ol><p>In Python code, this looks the following:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>l1 = [1, 3, 5, 7, 9, 11]l2 = [2, 4, 6]p1 = 0    p2 = 0    result = []    # Loop over both lists until one list is at the end    while p1 &lt; len(l1) and p2 &lt; len(l2):        # If the values at the positions are the same,        # copy the value to the result        if l1[p1] == l2[p2]:            result.append(l1[p1])            result[-1] = l1[p1] + l2[p2]            p1 += 1            p2 += 1            # One entry is smaller, increment the smaller pointer        elif l1[p1] &lt; l2[p2]:            result.append(l[p1])            p1 += 1        # One entry is smaller, increment the smaller pointer        elif l1[p1] &gt; l2[p2]:            result.append(l2[p2])            p2 += 1    rest = []    if p1 &lt; len(l1):        rest = l1[p1:len(l1)]    elif p2 &lt; len(l2):        rest = list2[p2:len(l2)]    result += rest# [1, 2, 4, 5, 6, 7, 9, 11]</code></pre></div></div>",
      "date_published": "2016-10-28T20:00:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/2016/09/12/cronjob-crontab-syntax.html",
      "url": "https://fewald.net/linux/2016/09/12/cronjob-crontab-syntax.html",
      "title": "Syntax for Crontab",
      "content_html": "<p>Just a quick reminder for the syntax of the crontab for cronjobs:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code># 1. Entry: Minute when the process will be started [0-60]# 2. Entry: Hour when the process will be started [0-23]# 3. Entry: Day of the month when the process will be started [1-28/29/30/31]# 4. Entry: Month of the year when the process will be started [1-12]# 5. Entry: Weekday when the process will be started [0-6] [0 is Sunday]## all x min = */x</code></pre></div></div><p>Found on <a href=\"http://stackoverflow.com/questions/16717930/how-to-run-crontab-job-every-week-on-sunday\">Stackoverflow</a></p>",
      "date_published": "2016-09-12T21:12:00+00:00"
    },
    {
      "id": "https://fewald.net/development/typescipt/angular2/2016/08/30/angular2-http.html",
      "url": "https://fewald.net/development/typescipt/angular2/2016/08/30/angular2-http.html",
      "title": "Angular2 - Loading indicator for Http service",
      "content_html": "<p>I wanted to notifiy the user, whenever I am doing an AJAX request in <a href=\"http://www.angular.io/\">Angular2</a>. To achieve this in an existing project, the goal was, to edit as little files as possible. The solution to this problem is very straightforward: The class <code class=\"language-plaintext highlighter-rouge\">Http</code> needs to be extended which allows own code to be used.</p><p>First, I needed to create the <code class=\"language-plaintext highlighter-rouge\">EventHttpService</code> with the following code:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>import { Http } from '@angular/http';export class EventHttpService extends Http{}</code></pre></div></div><p>So far this class does nothing, it just extends the <code class=\"language-plaintext highlighter-rouge\">Http</code> class, providing the same functionality. Therefore I needed to override all the methods which should perform a different action. Below is an example for the <code class=\"language-plaintext highlighter-rouge\">get</code> method, with all the required imports. Of course, the whole class needs to be decorated with <code class=\"language-plaintext highlighter-rouge\">Injectable()</code> so that we can inject it via Dependency Injection.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>import { Injectable } from '@angular/core';import { Http, RequestOptions, RequestOptionsArgs, Response, ConnectionBackend } from '@angular/http';import { Observable } from 'rxjs/Observable';@Injectable()export class EventHttpService extends Http {\tpublic get(url: string, options?: RequestOptionsArgs) : Observable&lt;Response&gt; {\t\t\t// Own code before the request is performed\t\t\tvar response = super.get(url, options);\t\t\tresponse.subscribe(null, error =&gt; {\t\t\t\t// Own code on error\t\t\t}, () =&gt; {\t\t\t\t// Own code on success\t\t\t});\t\t\treturn response;\t}}</code></pre></div></div><p>The comments in the source code above mark the points where the developer can implement own code. I used it, to increment and decrement a counter. When the counter starts from 0, an injected loading bar is shown and when the counter finally reaches 0, the loading bar is hidden.</p><p>So far, the class does nothing, since we are not using it. The right place to use it, is <code class=\"language-plaintext highlighter-rouge\">app.module.ts</code>:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>@NgModule({  imports:      [],  declarations: [],  providers: [    HTTP_PROVIDERS,    provide(Http, {      useFactory: (xhrBackend: XHRBackend, requestOptions: Request) =&gt; new EventHttpService(xhrBackend, requestOptions),      deps: [XHRBackend, RequestOptions]})    ],  bootstrap:    [ AppComponent ]})export class AppModule { }</code></pre></div></div><p>With this example, whenever <code class=\"language-plaintext highlighter-rouge\">Http.get</code> is used, the EventHttpService is used automatically, executing my custom code. If you want to inject other services into <code class=\"language-plaintext highlighter-rouge\">Http</code>, this is also straightforward, just add these to the constructor of the <code class=\"language-plaintext highlighter-rouge\">EventHttpService</code> so that it looks as follows. In my case, I am using the awesome ng2-slim-loading-bar to show a loading bar, when the first AJAX request is performed.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>public constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions, private loadingBar: SlimLoadingBarService) {\tsuper(_backend, _defaultOptions);}</code></pre></div></div><p>Below is the complete code of the <code class=\"language-plaintext highlighter-rouge\">event-http.service.ts</code>:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>import { Injectable } from '@angular/core';import { Http, RequestOptions, RequestOptionsArgs, Response, ConnectionBackend } from '@angular/http';import { Observable } from 'rxjs/Observable';import { SlimLoadingBarService } from 'ng2-slim-loading-bar';@Injectable()export class EventHttpService extends Http {\tprivate currentRequests: number = 0;\tpublic constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions, private loadingBar: SlimLoadingBarService) {\t\tsuper(_backend, _defaultOptions);\t}\tpublic get(url: string, options?: RequestOptionsArgs) : Observable&lt;Response&gt; {\t\tthis.incrementRequestCount();\t\tvar response = super.get(url, options);\t\tresponse.subscribe(null, error =&gt; {\t\t\tthis.decrementRequestCount();\t\t}, () =&gt; {\t\t\tthis.decrementRequestCount();\t\t});\t\treturn response;\t}\tprivate decrementRequestCount() {\t\tif (--this.currentRequests == 0) {\t\t\tthis.loadingBar.complete();\t\t}\t}\tprivate incrementRequestCount() {\t\tif (this.currentRequests++ == 0) {\t\t\tthis.loadingBar.start();\t\t}\t}}</code></pre></div></div>",
      "date_published": "2016-08-30T01:54:00+00:00"
    },
    {
      "id": "https://fewald.net/tools/2016/07/15/vim-cheatsheet.html",
      "url": "https://fewald.net/tools/2016/07/15/vim-cheatsheet.html",
      "title": "VIM cheatsheet",
      "content_html": "<p>A cheatsheet for vom can be found <a href=\"http://vim.rtorr.com\">here</a>. I find this quite useful to begin with this powerful editor.</p><p>The most important keys, if you just want to edit a file are:</p><ul>  <li><strong>i</strong> insert</li>  <li><strong>esc</strong> exit insert mode</li>  <li><strong>j</strong> move cursor down</li>  <li><strong>k</strong> move cursor up</li>  <li><strong>h</strong> move cursor left</li>  <li><strong>l</strong> move cursor right</li>  <li><strong>:w</strong> write (append the filename)</li>  <li><strong>:q</strong> quit</li>  <li><strong>:q!</strong> quit and do not save changes</li></ul>",
      "date_published": "2016-07-15T20:31:00+00:00"
    },
    {
      "id": "https://fewald.net/performance/programming/2016/05/20/big-o-notation-cheatsheet.html",
      "url": "https://fewald.net/performance/programming/2016/05/20/big-o-notation-cheatsheet.html",
      "title": "Big O notation for many datatypes",
      "content_html": "<p>An interesting site which shows the <a href=\"https://en.wikipedia.org/wiki/Big_O_notation\">Big O notation</a> for all many commonly used datatypes is the <a href=\"http://bigocheatsheet.com/\">Big O Cheatsheet</a>.</p>",
      "date_published": "2016-05-20T21:31:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/shell/2016/04/11/show-open-ports.html",
      "url": "https://fewald.net/linux/shell/2016/04/11/show-open-ports.html",
      "title": "Show open ports on the linux shell",
      "content_html": "<p>If you want to show all the currently open ports on linux, just use the following command:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>netstat -lntu</code></pre></div></div><p>This gives you the following output:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address           Foreign Address         Statetcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:3128            0.0.0.0:*               LISTENtcp6       0      0 :::22                   :::*                    LISTENudp        0      0 0.0.0.0:39790           0.0.0.0:*udp        0      0 0.0.0.0:68              0.0.0.0:*udp        0      0 0.0.0.0:41121           0.0.0.0:*udp6       0      0 :::57063                :::*udp6       0      0 :::42147                :::*</code></pre></div></div><p>Found <a href=\"http://superuser.com/a/529844\">here</a></p>",
      "date_published": "2016-04-11T21:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/git/2016/04/07/better-git-log.html",
      "url": "https://fewald.net/development/git/2016/04/07/better-git-log.html",
      "title": "Using git efficiently on the console",
      "content_html": "<p>I was looking for ways to visualize the progress of a project and also I wanted to do this with on-board tools. After a quick search I found the following command.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>git log --graph --decorate --oneline</code></pre></div></div><p>This gives a nice and colorful overview when what branch was touched and how it was merged. To display the results in a nicer way, a alias in the <code class=\"language-plaintext highlighter-rouge\">~/.giconfig</code> helps:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>[alias]lg = !\"git lg1\"lg1 = !\"git lg1-specific --all\"lg2 = !\"git lg2-specific --all\"lg3 = !\"git lg3-specific --all\"lg1-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)'lg2-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'lg3-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%</code></pre></div></div><p>The last speed trick is, to set an alias in the <code class=\"language-plaintext highlighter-rouge\">~/.bashrc</code>. This saves 2/3 of the letters.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>alias g='git'</code></pre></div></div>",
      "date_published": "2016-04-07T21:31:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/shell/2016/02/23/combining-csv-files.html",
      "url": "https://fewald.net/linux/shell/2016/02/23/combining-csv-files.html",
      "title": "Combining multiple CSV files to one",
      "content_html": "<p>If you have multiple CSV files with the same format and you want to combine them to just a single file, just do</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>cat file1.csv file2.csv &gt; output.csv</code></pre></div></div><p>Or even simpler</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>cat *.csv &gt; output.csv</code></pre></div></div>",
      "date_published": "2016-02-23T16:45:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/shell/2016/02/19/shell-count-lines.html",
      "url": "https://fewald.net/linux/shell/2016/02/19/shell-count-lines.html",
      "title": "Counting lines of a text file",
      "content_html": "<p>To count lines of one or many files on the shell I use <code class=\"language-plaintext highlighter-rouge\">wc</code> (Wordcount). If you want to count all lines of all CSV files in a dictionary, just type:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>wc -l *.csv</code></pre></div></div>",
      "date_published": "2016-02-19T19:03:00+00:00"
    },
    {
      "id": "https://fewald.net/linux/security/2016/02/12/increasing_security_ssh.html",
      "url": "https://fewald.net/linux/security/2016/02/12/increasing_security_ssh.html",
      "title": "Improving security for SSH",
      "content_html": "<p>There are two things to increase the security for ssh logins.</p><h2 id=\"change-ssh-port\">Change SSH Port</h2><p>To change the SSH port, simply edit the sshd_config file with the commmand <code class=\"language-plaintext highlighter-rouge\">sudo nano /etc/ssh/sshd_config</code> and change the line <code class=\"language-plaintext highlighter-rouge\">Port 22</code> to something higher. Make sure to stay below 65,000 and don’t use any port which is already used by another service like 80 (web). After changing and saving the file, simply restart the ssh daemon and reload the configuration with <code class=\"language-plaintext highlighter-rouge\">sudo /etc/init.d/ssh reload</code>. Done.</p><h2 id=\"disable-password-based-login-for-ssh\">Disable password based login for SSH</h2><p>To disable the password based login for all the users, you can do the following:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code># Open the sshd config filesudo nano /etc/ssh/sshd_config</code></pre></div></div><p>Disable password authentication with the following settings:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>ChallengeResponseAuthentication no</code></pre></div></div><p>Allow private public key authentication with the following:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>RSAAuthentication yesPubkeyAuthentication yes</code></pre></div></div><p>Once this is done, restart the SSH daemon to apply the settings.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/etc/init.d/sshd restart</code></pre></div></div><p>Additional information can be found <a href=\"http://www.linux.org/threads/how-to-force-ssh-login-via-public-key-authentication.4253/\">here</a>.</p>",
      "date_published": "2016-02-12T00:10:00+00:00"
    },
    {
      "id": "https://fewald.net/web/jekyll/2016/02/09/sitemap_xml-for-jekyll.html",
      "url": "https://fewald.net/web/jekyll/2016/02/09/sitemap_xml-for-jekyll.html",
      "title": "Building a sitemap.xml with jekyll",
      "content_html": "<p>While transforming this page to Jekyll I needed a sitemap.xml for the Google Webmaster Tools. A quick search showed up this <a href=\"http://davidensinger.com/2013/03/generating-a-sitemap-in-jekyll-without-a-plugin/\">result</a>. Works perfectly.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---layout: null---&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;&lt;urlset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/ruby/rails/2022/08/08/secure-sidekiq-with-basicauth.html&lt;/loc&gt;              &lt;lastmod&gt;2022-08-08T23:30:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/links/2022/07/30/aaron-koblin.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-30T05:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/ruby/rails/2022/07/28/fast-feed-processing-in-ruby.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-28T16:56:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/ruby/rails/2022/07/18/rails-testing-with-static-server.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-18T04:33:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/07/17/new-york-times-story-portrait.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-17T03:06:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/seo/2022/07/08/improving-search-engine-optimization.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-08T06:22:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/07/08/improving-website-accessibility.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-08T06:14:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/07/06/improving-page-speed.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-06T22:24:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/ruby-on-rails/2022/07/06/ruby-on-rails-nil-check.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-06T16:57:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/07/05/jsonfeed-installed.html&lt;/loc&gt;              &lt;lastmod&gt;2022-07-05T18:42:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/06/26/wisdom.html&lt;/loc&gt;              &lt;lastmod&gt;2022-06-26T18:37:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/06/08/how-much-precision-do-you-need.html&lt;/loc&gt;              &lt;lastmod&gt;2022-06-08T01:10:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/06/06/jekyll-compose.html&lt;/loc&gt;              &lt;lastmod&gt;2022-06-06T19:12:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/06/06/full-url-google-chrome.html&lt;/loc&gt;              &lt;lastmod&gt;2022-06-06T03:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/05/29/sunset-daly-city-california.html&lt;/loc&gt;              &lt;lastmod&gt;2022-05-29T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2022/05/28/hermes-imessage-cli.html&lt;/loc&gt;              &lt;lastmod&gt;2022-05-28T02:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/programming/2022/05/11/golang-generics.html&lt;/loc&gt;              &lt;lastmod&gt;2022-05-11T02:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/2021/04/23/coredns_config.html&lt;/loc&gt;              &lt;lastmod&gt;2021-04-23T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/webdesign/development/2021/04/22/fontshare_fonts.html&lt;/loc&gt;              &lt;lastmod&gt;2021-04-22T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/golang/development/2020/06/16/golang-private-repository.html&lt;/loc&gt;              &lt;lastmod&gt;2020-06-16T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/photo/2020/05/19/sunday-afternoon-clouds.html&lt;/loc&gt;              &lt;lastmod&gt;2020-05-19T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/k8s/development/2020/05/09/kubernetes-namespace.html&lt;/loc&gt;              &lt;lastmod&gt;2020-05-09T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/misc/2020/05/05/rt-live.html&lt;/loc&gt;              &lt;lastmod&gt;2020-05-05T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/misc/2020/04/28/data-visualization.html&lt;/loc&gt;              &lt;lastmod&gt;2020-04-28T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/nginx/development/2020/04/19/413-nginx-ingress-k8s.html&lt;/loc&gt;              &lt;lastmod&gt;2020-04-19T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/misc/2020/02/22/writic-app.html&lt;/loc&gt;              &lt;lastmod&gt;2020-02-22T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/link/2019/06/17/the-hard-way.html&lt;/loc&gt;              &lt;lastmod&gt;2019-06-17T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/photo/2019/05/05/getty-center.html&lt;/loc&gt;              &lt;lastmod&gt;2019-05-05T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/links/2019/04/01/kubernetes-or-not.html&lt;/loc&gt;              &lt;lastmod&gt;2019-04-01T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/photo/2019/03/31/java-on-ocean.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-31T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/security/2019/03/30/decrypt-chrome-passwords-windows.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-30T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/python/macos/2019/03/28/setup-pip-build.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-28T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/security/2019/03/25/facebook-passwords.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-25T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/helm/2019/03/24/helm-charts.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-24T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/python/2019/03/04/febase62-v1-1-0.html&lt;/loc&gt;              &lt;lastmod&gt;2019-03-04T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/macos/2019/02/25/uuid-mac-os-shell.html&lt;/loc&gt;              &lt;lastmod&gt;2019-02-25T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/2019/02/17/jps-java-process.html&lt;/loc&gt;              &lt;lastmod&gt;2019-02-17T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/google/2019/02/17/delete-google-appengine.html&lt;/loc&gt;              &lt;lastmod&gt;2019-02-17T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2018/12/25/apache-spark-dataframe-count-words.html&lt;/loc&gt;              &lt;lastmod&gt;2018-12-25T20:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2018/07/31/kafka-topic-cleanup.html&lt;/loc&gt;              &lt;lastmod&gt;2018-07-31T11:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2018/07/27/apache-kafka-cheat-sheet.html&lt;/loc&gt;              &lt;lastmod&gt;2018-07-27T21:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/misc/2018/07/26/anki-flashcards.html&lt;/loc&gt;              &lt;lastmod&gt;2018-07-26T04:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/2018/07/23/ps-aux-grep-java-process.html&lt;/loc&gt;              &lt;lastmod&gt;2018-07-23T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/json/development/2017/05/20/RSS-better-with-jsonfeed.html&lt;/loc&gt;              &lt;lastmod&gt;2017-05-20T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/python/2017/02/11/java-enumerations-with-values.html&lt;/loc&gt;              &lt;lastmod&gt;2017-02-11T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/python/2017/02/07/base62-encoding-decoding.html&lt;/loc&gt;              &lt;lastmod&gt;2017-02-07T00:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/vim/2016/12/02/smaller-tabs-in-vim.html&lt;/loc&gt;              &lt;lastmod&gt;2016-12-02T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/dotnet/2016/11/01/newtonsoft-json-dll-in-multiple-projects.html&lt;/loc&gt;              &lt;lastmod&gt;2016-11-01T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2016/10/31/experiences-with-jekyll.html&lt;/loc&gt;              &lt;lastmod&gt;2016-10-31T18:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/python/2016/10/28/python-merge-two-lists.html&lt;/loc&gt;              &lt;lastmod&gt;2016-10-28T20:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/2016/09/12/cronjob-crontab-syntax.html&lt;/loc&gt;              &lt;lastmod&gt;2016-09-12T21:12:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/typescipt/angular2/2016/08/30/angular2-http.html&lt;/loc&gt;              &lt;lastmod&gt;2016-08-30T01:54:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/tools/2016/07/15/vim-cheatsheet.html&lt;/loc&gt;              &lt;lastmod&gt;2016-07-15T20:31:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/performance/programming/2016/05/20/big-o-notation-cheatsheet.html&lt;/loc&gt;              &lt;lastmod&gt;2016-05-20T21:31:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/shell/2016/04/11/show-open-ports.html&lt;/loc&gt;              &lt;lastmod&gt;2016-04-11T21:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/git/2016/04/07/better-git-log.html&lt;/loc&gt;              &lt;lastmod&gt;2016-04-07T21:31:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/shell/2016/02/23/combining-csv-files.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-23T16:45:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/shell/2016/02/19/shell-count-lines.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-19T19:03:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/linux/security/2016/02/12/increasing_security_ssh.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-12T00:10:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/web/jekyll/2016/02/09/sitemap_xml-for-jekyll.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-09T18:22:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/c%23/json/2016/02/08/json-serialization.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-08T20:13:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/c%23/2016/02/04/list-event-handler.html&lt;/loc&gt;              &lt;lastmod&gt;2016-02-04T02:34:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/python/2016/01/07/odoo-9.html&lt;/loc&gt;              &lt;lastmod&gt;2016-01-07T17:14:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/js/2015/12/19/lunch-manager.html&lt;/loc&gt;              &lt;lastmod&gt;2015-12-19T01:28:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/js/2015/12/14/installing-node-js.html&lt;/loc&gt;              &lt;lastmod&gt;2015-12-14T04:39:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2015/12/09/git_hooks.html&lt;/loc&gt;              &lt;lastmod&gt;2015-12-09T20:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;      &lt;url&gt;      &lt;loc&gt;https://fewald.net/development/2015/12/09/jekyll-experiments.html&lt;/loc&gt;              &lt;lastmod&gt;2015-12-09T01:00:00+00:00&lt;/lastmod&gt;            &lt;changefreq&gt;weekly&lt;/changefreq&gt;      &lt;priority&gt;1.0&lt;/priority&gt;    &lt;/url&gt;              &lt;url&gt;        &lt;loc&gt;https://fewald.net/about/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;monthly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                      &lt;url&gt;        &lt;loc&gt;https://fewald.net/cv/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;monthly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                            &lt;url&gt;        &lt;loc&gt;https://fewald.net/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                      &lt;url&gt;        &lt;loc&gt;https://fewald.net/projects/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;monthly&lt;/changefreq&gt;        &lt;priority&gt;0.6&lt;/priority&gt;       &lt;/url&gt;                            &lt;url&gt;        &lt;loc&gt;https://fewald.net/page2/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page3/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page4/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page5/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page6/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page7/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page8/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page9/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page10/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page11/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page12/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page13/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;                &lt;url&gt;        &lt;loc&gt;https://fewald.net/page14/&lt;/loc&gt;        &lt;lastmod&gt;&lt;/lastmod&gt;        &lt;changefreq&gt;weekly&lt;/changefreq&gt;        &lt;priority&gt;&lt;/priority&gt;       &lt;/url&gt;      &lt;/urlset&gt;</code></pre></div></div>",
      "date_published": "2016-02-09T18:22:00+00:00"
    },
    {
      "id": "https://fewald.net/development/c%23/json/2016/02/08/json-serialization.html",
      "url": "https://fewald.net/development/c%23/json/2016/02/08/json-serialization.html",
      "title": "Serialization and Deserialization with C# & JSON",
      "content_html": "<p>For serialization and deserialization in the .NET framework, I use the <a href=\"http://www.newtonsoft.com/json\">Newtonsoft JSON Serializer</a>. This library works really fast and is very flexible. Lately I had a problem that I wanted to serialize a <code class=\"language-plaintext highlighter-rouge\">List&lt;ObjA&gt;</code> with <code class=\"language-plaintext highlighter-rouge\">ObjB</code> and <code class=\"language-plaintext highlighter-rouge\">ObjC</code>. <code class=\"language-plaintext highlighter-rouge\">ObjB</code> and <code class=\"language-plaintext highlighter-rouge\">ObjC</code> are children of <code class=\"language-plaintext highlighter-rouge\">ObjA</code>. In the code it looks as the following:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>public class ObjA{}public class ObjB : ObjA{}public class ObjC : ObjA{}public class Program{\tpublic void main()\t{\t\tList&lt;ObjA&gt; objectList = new List&lt;ObjA&gt;();\t\tstring serializedObjects = JsonConvert.SerializeObject(objectList);\t\tList&lt;ObjA&gt; objectListNew = JsonConvert.DeserializeObject&lt;List&lt;ObjA&gt;&gt;(serializedObjects);\t}}</code></pre></div></div><p>If I now serialized this with the method <code class=\"language-plaintext highlighter-rouge\">SerializeObject()</code> and later deserialized it with the method <code class=\"language-plaintext highlighter-rouge\">DeserializeObject&lt;&gt;()</code>, all the objects were of the type <code class=\"language-plaintext highlighter-rouge\">ObjA</code> instead of type <code class=\"language-plaintext highlighter-rouge\">ObjB</code> or <code class=\"language-plaintext highlighter-rouge\">ObjC</code>. After a short research, I found the solution:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>public class Program{\tpublic void main()\t{\t\tList&lt;ObjA&gt; objectList = new List&lt;ObjA&gt;();\t\tstring serializedObjects = JsonConvert.SerializeObject(objectList, Formatting.Indented, new JsonSerializerSettings()            {                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,                TypeNameHandling = TypeNameHandling.All            });\t\tList&lt;ObjA&gt; objectListNew = JsonConvert.DeserializeObject&lt;List&lt;ObjA&gt;&gt;(serializedObjects, new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto});\t}}</code></pre></div></div><p>This preserves the types of the objects during serialization.</p>",
      "date_published": "2016-02-08T20:13:00+00:00"
    },
    {
      "id": "https://fewald.net/development/c%23/2016/02/04/list-event-handler.html",
      "url": "https://fewald.net/development/c%23/2016/02/04/list-event-handler.html",
      "title": "Implementing events in List<T>",
      "content_html": "<p>If you want to subscribe for changes to a <code class=\"language-plaintext highlighter-rouge\">List&lt;T&gt;</code> in C# it is very easy. All you need to do, is subclass <code class=\"language-plaintext highlighter-rouge\">List&lt;T&gt;</code> and implement your own event handler. This could be done as following:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>using System;using System.Collections.Generic;namespace FEwald{    public class MyList&lt;T&gt; : List&lt;T&gt;    {        /// &lt;summary&gt;        /// EventHandler which is called when items are added.        /// &lt;/summary&gt;        public event EventHandler ItemAdded;        /// &lt;summary&gt;        /// EventHandler which is called when items are deleted.        /// &lt;/summary&gt;        public event EventHandler ItemRemoved;        /// &lt;summary&gt;        /// Adds an item to the list.        /// &lt;/summary&gt;        /// &lt;param name=\"item\"&gt;&lt;/param&gt;        public new void Add(T item)        {            if (ItemAdded != null)            {                ItemAdded(item, EventArgs.Empty);            }            base.Add(item);                    }        /// &lt;summary&gt;        /// Adds a range of items to the list.        /// &lt;/summary&gt;        /// &lt;param name=\"collection\"&gt;&lt;/param&gt;        public new void AddRange(IEnumerable&lt;T&gt; collection)        {            foreach (var item in collection)            {                Add(item);            }        }                /// &lt;summary&gt;        /// Removes an item from the list.        /// &lt;/summary&gt;        /// &lt;param name=\"item\"&gt;&lt;/param&gt;        public new void Remove(T item)        {            if (ItemRemoved != null)            {                ItemRemoved(item, EventArgs.Empty);            }            base.Remove(item);        }    }}</code></pre></div></div>",
      "date_published": "2016-02-04T02:34:00+00:00"
    },
    {
      "id": "https://fewald.net/development/python/2016/01/07/odoo-9.html",
      "url": "https://fewald.net/development/python/2016/01/07/odoo-9.html",
      "title": "Updating Odoo 8 to Odoo 9",
      "content_html": "<p>Currently I’m updating an odoo installation from version 8 to version 9 for a customer. In this post I will give a short introduction, on how to update the installation and most common issues I observed during the update. This post ist not complete since every odoo installation is different.</p><p>First of all, there is no updater or tutorial offered by the odoo company for the community edition. The company only provides support and updates for the paid enterprise licenses. Since the database structure in odoo changed in <a href=\"http://stackoverflow.com/questions/33336830/how-to-upgrade-odoo-8-to-odoo-9-database\">so many fields</a>, it is almost impossible, to update the odoo database “by hand” to version 9. The other solution, <a href=\"#add_link\">OpenUpgrade</a> is not yet ready for the current version of the ERP system and therefore also not a possible solution. Instead, I decided to upgrade the custom developed module in a fresh odoo 9 installation. As soon as it works, I install odoo 9 with a fresh database on the server and transfer (export, then import) all the data from 8 to 9. Below is a list with possible problems and the solutions I found for them.</p><h2 id=\"custom-module-isnt-shown-in-the-list-of-apps\">Custom module isn’t shown in the list of apps</h2><p>In odoo 8, the user has to be a technical user to be able to change the technical settings. This type of user no longer exists in odoo 9. Instead, one has to activate the developer mode of odoo. This is done by clicking on the currently logged in user name in the upper right corner, then about and then “activate developer mode”.</p><p>After this step, root / administrator users can update the list of apps. In my case I was able to search for my module using the search box in the upper right corner. Updating the list of apps is still necessary but it is more hidden than in the previous version.</p><h2 id=\"using-the-old-api\">Using the old API</h2><p>The old API is simply no longer supported in odoo 9. My module was using it in some method signatures which I had to update to the new api style, including the method decorator <code class=\"language-plaintext highlighter-rouge\">@api.one</code> and <code class=\"language-plaintext highlighter-rouge\">@api.multi</code>. All the variables, like <code class=\"language-plaintext highlighter-rouge\">cr</code>, <code class=\"language-plaintext highlighter-rouge\">context</code>, etc. still exist but have slightly different names now.</p><h2 id=\"using-variable-names-in-email-templates\">Using variable names in email templates</h2><p>Somehow the use of variable names as it was before is no longer possible. I will update this part as soon as I have a solution for this.</p><h2 id=\"problems-with-lessc\">Problems with lessc</h2><p>If lessc cannot be found it is most likely not installed on the server or, in my case, the symlink to nodejs was wrong. This is easy to fix with</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>sudo ln -s /usr/local/bin/nodejs /usr/bin/node</code></pre></div></div><h2 id=\"transfer-old-data-to-new-database\">Transfer old data to new database</h2><p>The data which is needed in the new database has to be transformed to match the new database structure. I my case this was done by copying the old database to a temporary database, then editing the tables exporting them, and importing them afterwards into the new database. It is important to import the data in the right order to not violate foreign key constraints.</p><p>This list will be updated if new difficulties occur.</p>",
      "date_published": "2016-01-07T17:14:00+00:00"
    },
    {
      "id": "https://fewald.net/development/js/2015/12/19/lunch-manager.html",
      "url": "https://fewald.net/development/js/2015/12/19/lunch-manager.html",
      "title": "Lunch manager with angularJS & nodeJS",
      "content_html": "<p>Some days ago I created a simple script just to try out some new techniques with angularJS in combination with nodeJS. The idea was to create a lunch manager where people can sign up for a specific day, that they are in for lunch at a specific time. There should be no complex signup since I was not expecting any spam or similar behavior.</p><p>I had the idea of realising this script with a minimal footprint so that it is easy to deploy and easy to run on any Linux based server. This leads to the idea of creating a one pager with most of the Javascript files hosted on a CDN and only the absolute minimum should be hosted on my server.</p><p>##Setting up the front-endAs a front-end I chose the responsive <a href=\"http://getbootstrap.com/\">bootstrap</a> framework from twitter. The dynamic part should be realized with <a href=\"http://angularjs.org/\">angular</a>. The whole website is only one controller, called <code class=\"language-plaintext highlighter-rouge\">FoodCtrl</code>.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>&lt;body ng-controller=\"FoodCtrl as f\"&gt;</code></pre></div></div><p>For this controller I wrote the following angular module which basically takes the user input, if the name is longer than two characters, submits it to the server and fetches every two seconds the list from the server. With this procedure it is ensured, that the list if the users is always up to date and the user does not have to refresh the website to see if new users have signed up.</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>angular.module('foodApp', [])  .controller('FoodCtrl', ['$http', function($http) {    var food = this;    food.alreadyIn = false;    food.pendingRequest = false;    food.name = '';    food.namesList = [];    food.signup = function(time) {      food.pendingRequest = true;      $http.post('/lunch/signup', {name: food.name, time: time})        .then(function (response) {          console.log(response);          notie.alert(1, 'Success, you\\'re in for today, ' + food.name + '!', 1.5);          food.alreadyIn = true;          food.pendingRequest = false;        }, function (response) {          notie.alert(3, 'Error ' + response.status, 5);          food.pendingRequest = false;        });    };    fetch_updates($http, food, 0, false);    refetch_updates($http, food, 0);  }])  .directive('person', function() {    return {      template: '{ {n.name}}@{ {n.time}}'    };  });</code></pre></div></div><p>With a click on the signup button, an ajax request via the integrated angular <a href=\"https://docs.angularjs.org/api/ng/service/$http\">$http</a> service is made consisting of the name and the time. For the complete source code you can check out the <a href=\"https://github.com/f-ewald/lunchmanager\">git repository</a> I set up.</p><p>##DifficultiesWhile creating the app I had some difficulties stopping the flickering effect which happens to all angularJS apps which do not use externally loaded templates. This effect happens because the browser renders first the complete HTML document with the whole DOM and after that (if you load angular just before the closing body-tag) angular, which processes the HTML document with it’s own renderer. To avoid this, one can use the angular directive <code class=\"language-plaintext highlighter-rouge\">ng-cloak</code> on any HTML tag which should be hidden at first because it contains i.e. if-expressions which are evaluated later.</p><p>Adding only <code class=\"language-plaintext highlighter-rouge\">ng-cloak</code> alone is only half of the solution because angular adds CSS tags to all elements with this directive. But since angular is loaded just before the closing <code class=\"language-plaintext highlighter-rouge\">body</code> tag, hiding of the elements is executed often too late in time. Especially, the slower the computer, the more important it is to hide these elements just from the beginning. As a workaround I chose the following approach. Just add a style in the head part which hides <code class=\"language-plaintext highlighter-rouge\">ng-cloak</code> and this style is removed later automatically by angular:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>&lt;style type=\"text/css\"&gt;  [ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {    display: none !important;  }&lt;/style&gt;</code></pre></div></div><p>Another difficulty which I had to solve was the update of the person list. There are basically three strategies on how to get an up-to-date list of the participants: long polling, WebSockets and periodic AJAX requests. Since long-polling looks more like a hack to me and WebSockets would need the set up of another server, I decided to use continuous <a href=\"https://en.wikipedia.org/wiki/Ajax_(programming)\">AJAX</a> requests, to enable a synchronization with all the connected clients. The script, which updates the list looks like this:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function fetch_updates($http, food, errorCounter, recursive) {    console.log('fetching names...');    $http.get('/lunch/people.json')      .then(function(response) {        if(food.namesList.length &lt; response.data.length &amp;&amp; recursive) {          notie.alert(4, 'Someone else signed up!', 1.5);        }        food.namesList = response.data;        if (recursive) {          refetch_updates($http, food, 0);        }      },      function(response) {        notie.alert(3, 'Error while fetching other people', 2);        refetch_updates($http, food, errorCounter + 1)      });  };  function refetch_updates($http, food, errorCounter) {    if (errorCounter &lt; 5) {      var timeout = (errorCounter + 1) * 2000;      console.log(timeout);      setTimeout(function () {        fetch_updates($http, food, errorCounter, true);      }, timeout);    }    else {      notie.confirm('Error loading data. Do you want to retry?', 'Yes', 'No', function () {        refetch_updates($http, food, 0);      });    }  }</code></pre></div></div><p>The method <code class=\"language-plaintext highlighter-rouge\">refetch_updates</code> checks the <code class=\"language-plaintext highlighter-rouge\">errorCounter</code>. If it is below 5, it calls <code class=\"language-plaintext highlighter-rouge\">fetch_updates</code> after a timeout of the error count times two seconds. The method <code class=\"language-plaintext highlighter-rouge\">fetch_updates</code> calls <code class=\"language-plaintext highlighter-rouge\">refetch_updates</code> again. If the request fails more than five times, the user is asked, if he wants to continue fetching updates or not. If yes, the <code class=\"language-plaintext highlighter-rouge\">errorCounter</code> is reset to zero and the recursive call begins again. The advantage of this method is, that I didn’t have to implement any other server than the existing nodeJS express server.</p><p>##ConclusionFrom the idea to the working script it took me about two hours, if I substract the setup time of nodeJS and Apache, which only serves as a proxy this time. If you want to download the whole project, you can check out my <a href=\"https://github.com/f-ewald/lunchmanager\">git repository</a>.</p>",
      "date_published": "2015-12-19T01:28:00+00:00"
    },
    {
      "id": "https://fewald.net/development/js/2015/12/14/installing-node-js.html",
      "url": "https://fewald.net/development/js/2015/12/14/installing-node-js.html",
      "title": "Installing nodeJS and setting up an alias",
      "content_html": "<p>Under Ubuntu 14, installing nodejs is relatively easy. At first one needs to to the obvious    sudo apt-get updatefollowed by a    sudo apt-get install nodejs</p><p>With this two easy steps you have a nodejs installation on your system which you could easily check with <code class=\"language-plaintext highlighter-rouge\">node -v</code>. If the installation was successful, this command prints the current installed nodejs version. In my case this was the version <strong>v0.10.25</strong> which is a bit outdated but could be sufficient if you do not plan to serve files or sites over the internet. In this case you should install the latest version from nodejs.org directly because of some severe security issues with the older versions.</p><p>In my case it was not done with installing node because the directory, where nodejs was installed to nor was in the <code class=\"language-plaintext highlighter-rouge\">PATH</code> neither there was a symlink to nodejs. For some reason, some programs rely on <code class=\"language-plaintext highlighter-rouge\">node</code>, others rely on <code class=\"language-plaintext highlighter-rouge\">nodejs</code>. So the goal was to use the same program for each command and also make it available in the <code class=\"language-plaintext highlighter-rouge\">PATH</code> to access it directly.</p><p>After asking <a href=\"http://stackoverflow.com/a/34257293/636676\">stackoverflow</a>, I was proposed to create an alias in the <code class=\"language-plaintext highlighter-rouge\">.bashrc</code> file like this:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>alias nodejs='/usr/bin/nodejs'</code></pre></div></div><p>Conclusion: Do not change the path, change the bash profile as the path easily can break since it depends on the order of the different folders.</p>",
      "date_published": "2015-12-14T04:39:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2015/12/09/git_hooks.html",
      "url": "https://fewald.net/development/2015/12/09/git_hooks.html",
      "title": "Playing with git hooks",
      "content_html": "<p>Git scm offers a very interesting technique, called hooks. A hook is basically a trigger which is fired after the specific event occurs. If you create a new git repository with <code class=\"language-plaintext highlighter-rouge\">git init</code> the folder .git/hooks with a couple of sample shell scripts is created. I personally use a hook to automate the deployment process of this website. After a commit is made, I automatically run a <code class=\"language-plaintext highlighter-rouge\">jekyll build</code> and then copy the newly created site to the server.</p><p>My current post-commit script looks like the following, simple but effective:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#!/bin/bashjekyll buildscp -r _site/* user@host:/home/user/html/</code></pre></div></div><p>##Update 2015-12-11</p><p>I slightly changed the deploy script to only update changed files because I realized that the time to update the homepage will increase with every posting. For this reason I had to use <code class=\"language-plaintext highlighter-rouge\">rsync</code>. Now the scripts looks as follows:</p><div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#!/bin/bashjekyll buildrsync --update --progress -r _site/* user@host:/home/user/html/</code></pre></div></div><p>The easiest way to synchronize over SSH is with a certificate.</p>",
      "date_published": "2015-12-09T20:00:00+00:00"
    },
    {
      "id": "https://fewald.net/development/2015/12/09/jekyll-experiments.html",
      "url": "https://fewald.net/development/2015/12/09/jekyll-experiments.html",
      "title": "First experiments with Jekyll",
      "content_html": "<p>Today, I’m trying out how to build this website with jekyll. With jekyll one can easily write markdown and yml files and create static pages from it. The advantages are obvious. No problems with scripts and security issues since all the files are HTML5, CSS and only a bit of Javascript. Also there is no need for complex server configuration or any high server speed. A simple webspace with FTP or SSH access is enough.</p>",
      "date_published": "2015-12-09T01:00:00+00:00"
    }
  ]
}