<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Published Notes</title><link href="https://wooptoo.com/" rel="alternate"/><link href="https://wooptoo.com/feed.xml" rel="self"/><id>https://wooptoo.com/</id><updated>2020-05-29T00:00:00+00:00</updated><entry><title>Publishing with Github Actions</title><link href="https://wooptoo.com/blog/github-actions/" rel="alternate"/><published>2020-05-29T00:00:00+00:00</published><updated>2020-05-29T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2020-05-29:/blog/github-actions/</id><summary type="html">&lt;p&gt;Continuous integration and deployment has now become a well established practice in software companies of all sizes.
With the advent of platforms like Gitlab &lt;span class="caps"&gt;CI&lt;/span&gt; and more recently Github Actions setting up &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; even for small personal projects has become
quick and&amp;nbsp;easy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Actions&amp;#8221;&lt;/em&gt; is the name of the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Continuous integration and deployment has now become a well established practice in software companies of all sizes.
With the advent of platforms like Gitlab &lt;span class="caps"&gt;CI&lt;/span&gt; and more recently Github Actions setting up &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; even for small personal projects has become
quick and&amp;nbsp;easy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Actions&amp;#8221;&lt;/em&gt; is the name of the platform now actively developed and promoted by Github.
It&amp;#8217;s lightweight and remarkably simple to work with, requiring no infrastructure of our&amp;nbsp;own.&lt;/p&gt;
&lt;p&gt;In this post we&amp;#8217;re going to create a setup which allows us to publish a static blog using Python, Actions, an &lt;span class="caps"&gt;AWS&lt;/span&gt; S3 bucket, and finally serve the contents of this bucket with Cloudflare&amp;#8217;s &lt;span class="caps"&gt;CDN&lt;/span&gt;.&lt;br&gt;
This post assumes you&amp;#8217;re already using a static site generator such as &lt;a href="https://blog.getpelican.com/"&gt;Pelican&lt;/a&gt; to build your website.
There are plenty of tutorials floating around the Internet covering this topic so we&amp;#8217;re going to skip that bit&amp;nbsp;here.&lt;/p&gt;
&lt;h2&gt;The Actions job&amp;nbsp;setup&lt;/h2&gt;
&lt;p&gt;The entire job description resides in a single &lt;span class="caps"&gt;YAML&lt;/span&gt; file.
In our case this will live in our git repo&amp;nbsp;under &lt;code&gt;.github/workflows/python-app.yml&lt;/code&gt;.
This might look intimidating at first but will become obvious once we dive&amp;nbsp;in.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Publish blog&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;[ master ]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="na"&gt;... Continued ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This starts with the git events (or hooks) which will trigger the job run.
Another useful event, if you&amp;#8217;re working within a team,&amp;nbsp;is &lt;code&gt;pull_request&lt;/code&gt;.
Here we&amp;#8217;re only dealing with pushing to the master&amp;nbsp;branch.&lt;/p&gt;
&lt;p&gt;The job is called simply &lt;em&gt;&amp;#8220;build&amp;#8221;&lt;/em&gt; and specifies which Linux distribution to run on.
Here I pinned Ubuntu to version 18.04 &lt;span class="caps"&gt;LTS&lt;/span&gt;&amp;nbsp;but &lt;code&gt;ubuntu-latest&lt;/code&gt; is a good default.
Running jobs on Windows and Mac is also an option with Github, one which I haven&amp;#8217;t tried&amp;nbsp;yet.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;- uses&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
&lt;span class="na"&gt;- name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Setup Python 3.8&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;3.8&lt;/span&gt;
&lt;span class="na"&gt;- name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Install Dependencies with Pip&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;python -m pip install --upgrade pip&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;if [ -f requirements.txt ]; then pip install -r requirements.txt; fi&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;pip install awscli&lt;/span&gt;
&lt;span class="na"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This clones the git repository into a fresh Ubuntu environment and sets up Python 3.8.
All actions that look&amp;nbsp;like &lt;code&gt;actions/*&lt;/code&gt; are first-party scripts offered by Github to ease our&amp;nbsp;work.&lt;/p&gt;
&lt;p&gt;Next it installs our application&amp;#8217;s dependencies&amp;nbsp;with &lt;code&gt;pip&lt;/code&gt;. Additionally it&amp;#8217;s going to pull&amp;nbsp;in &lt;code&gt;awscli&lt;/code&gt; as well. We&amp;#8217;re going to use it&amp;nbsp;shortly.&lt;/p&gt;
&lt;p&gt;And on to the final part of the spec&amp;nbsp;file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;- name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Make Publish&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;make publish&lt;/span&gt;
&lt;span class="na"&gt;- name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Sync S3&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${{ secrets.AWS_S3_BUCKET }}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;us-east-1&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;output&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;aws s3 sync --acl public-read --delete $SOURCE_DIR s3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s"&gt;//$AWS_S3_BUCKET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Make Publish&amp;#8221;&lt;/em&gt; is the default command used by Pelican to build the website. The result will be written to&amp;nbsp;the &lt;code&gt;output/&lt;/code&gt; folder as a series of &lt;span class="caps"&gt;HTML&lt;/span&gt; files and other assets put together by the building&amp;nbsp;process.&lt;/p&gt;
&lt;p&gt;The S3 Sync is the interesting bit which synchronises the contents of our output folder to an &lt;span class="caps"&gt;AWS&lt;/span&gt; S3 bucket.
A bunch of environment variables are defined here which are read from the repo&amp;#8217;s secrets section. This can be found in your repository&amp;#8217;s Settings &amp;rarr; Secrets&amp;nbsp;section.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Github secrets" src="https://wooptoo.com/images/github-secrets.png"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s easy to configure and ensures we don&amp;#8217;t leak any secrets into our&amp;nbsp;code.&lt;/p&gt;
&lt;p&gt;The run command does the synchronisation itself between the local output folder and the remote S3 bucket. It also makes sure that the files are set to be publicly readable and that any extraneous files found on the remote are&amp;nbsp;deleted.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;AWS&lt;/span&gt; has &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html"&gt;good documentation&lt;/a&gt; for all this stuff which I&amp;#8217;d recommend looking&amp;nbsp;over.&lt;/p&gt;
&lt;p&gt;The complete Github workflow file can be found here &amp;#8212; &lt;a href="https://gist.github.com/radupotop/7d8b4a4a7894b9c72b19571c1b6cc707"&gt;build-blog.yml&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The S3&amp;nbsp;bucket&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m not going to cover the S3 bucket setup in detail, but what I would recommend is having a separate &lt;span class="caps"&gt;IAM&lt;/span&gt; Role with access restricted to buckets only.
You&amp;#8217;ll then have to feed the access-key-id and the secret-access-key obtained during the creation of this role into the Github secrets&amp;nbsp;page.&lt;/p&gt;
&lt;p&gt;The bucket also needs to be configured to serve a static website, this can be easily done with the &lt;span class="caps"&gt;AWS&lt;/span&gt; &lt;span class="caps"&gt;CLI&lt;/span&gt;&amp;nbsp;tools:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;aws s3 website s3://my-bucket/ --index-document index.html \
    --error-document 404.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It only has to be done once so it can be left out of the job spec.&lt;br&gt;
You will need to know the public &lt;span class="caps"&gt;URL&lt;/span&gt; of your bucket for the next step. It will look similar&amp;nbsp;to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;http://bucket-name.s3-website-region.amazonaws.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The bucket needs to have the same name as your domain, including the &lt;span class="caps"&gt;TLD&lt;/span&gt;, e.g. &lt;em&gt;wooptoo.com&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Cloudflare&lt;/h2&gt;
&lt;p&gt;Cloudflare offers a robust &lt;span class="caps"&gt;CDN&lt;/span&gt; and &lt;span class="caps"&gt;DNS&lt;/span&gt; service which will serve these files to a large audience with ease.
The service has a free tier for personal use &amp;#8212; which is more than enough for our application.
This post assumes your site&amp;#8217;s &lt;span class="caps"&gt;DNS&lt;/span&gt; is with&amp;nbsp;Cloudflare.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re hosting the website on the root domain like I do then you&amp;#8217;ll need a &lt;span class="caps"&gt;DNS&lt;/span&gt; record pointing from that root domain to the bucket&amp;#8217;s public &lt;span class="caps"&gt;URL&lt;/span&gt;. 
In Cloudflare&amp;#8217;s web &lt;span class="caps"&gt;UI&lt;/span&gt; this can easily be done with a &lt;span class="caps"&gt;CNAME&lt;/span&gt;&amp;nbsp;record.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cloudflare" src="https://wooptoo.com/images/github-cloudflare.png"&gt;&lt;/p&gt;
&lt;p&gt;For this to work Cloudflare employs a trick. It knows that this is a special record and it applies &lt;a href="https://support.cloudflare.com/hc/en-us/articles/200169056-Understand-and-configure-CNAME-Flattening"&gt;&lt;span class="caps"&gt;CNAME&lt;/span&gt; flattening&lt;/a&gt;
&amp;#8212; which basically means resolving the bucket&amp;#8217;s A/&lt;span class="caps"&gt;AAAA&lt;/span&gt; records first and then pointing your own domain&amp;#8217;s records to the resulting &lt;span class="caps"&gt;IP&lt;/span&gt;.
It might sound complicated but it works very well in practice.
Other &lt;span class="caps"&gt;DNS&lt;/span&gt; services such as &lt;span class="caps"&gt;AWS&lt;/span&gt; Route53 behave similarly with Aliases.
This can then be easily tested from the command&amp;nbsp;line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;host -t A -v wooptoo.com

;; ANSWER SECTION:
wooptoo.com.        96  IN  A   172.67.133.56
wooptoo.com.        96  IN  A   104.21.5.86
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that those &lt;span class="caps"&gt;IP&lt;/span&gt; addresses actually belong to Cloudflare.
I assume &lt;span class="caps"&gt;CF&lt;/span&gt; proxies the requests through its own servers in order to provide caching and &lt;abbr title="Denial of Service"&gt;DoS&lt;/abbr&gt;
protection for the underlying&amp;nbsp;resource.&lt;/p&gt;
&lt;h2&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Every push to the repository master branch will now trigger a build with Github Actions.
The output of that build is the static website which will sync to an S3 bucket and
then get served with Cloudflare&amp;#8217;s &lt;span class="caps"&gt;CDN&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This is an easy and flexible setup which allows writing blog posts even from your mobile phone, pushing to the repository and letting Github handle the rest.
All this without having to manage server instances yourself and the associated&amp;nbsp;trouble.&lt;/p&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;Since posting this, the blog has been moved to Cloudflare Pages
which simplifies deployment significantly for static websites.
&lt;a href="https://pages.cloudflare.com/"&gt;Give it a try&lt;/a&gt;.&lt;/p&gt;</content><category term="articles"/><category term="AWS"/><category term="Cloudflare"/><category term="Devops"/><category term="Python"/><category term="Github"/></entry><entry><title>Docker multistage builds</title><link href="https://wooptoo.com/blog/docker-multistage-builds/" rel="alternate"/><published>2020-02-25T00:00:00+00:00</published><updated>2020-02-25T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2020-02-25:/blog/docker-multistage-builds/</id><summary type="html">&lt;p&gt;The multistage pattern splits the Docker image building process into two or more individual
steps. These are described in a&amp;nbsp;single &lt;code&gt;Dockerfile&lt;/code&gt; and will run sequentially.
The main advantage is a smaller final output image and a clear separation between&amp;nbsp;concerns.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a minimal example to help us get …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The multistage pattern splits the Docker image building process into two or more individual
steps. These are described in a&amp;nbsp;single &lt;code&gt;Dockerfile&lt;/code&gt; and will run sequentially.
The main advantage is a smaller final output image and a clear separation between&amp;nbsp;concerns.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a minimal example to help us get&amp;nbsp;started:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.8-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--upgrade&lt;span class="w"&gt; &lt;/span&gt;alpine-sdk
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;/app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;wheel&lt;span class="w"&gt; &lt;/span&gt;setuptools
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;wheel&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/wheels/

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.8-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--from&lt;span class="o"&gt;=&lt;/span&gt;builder&lt;span class="w"&gt; &lt;/span&gt;/wheels&lt;span class="w"&gt; &lt;/span&gt;/wheels
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;/wheels/*
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;/app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;runapp.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first stage, the &lt;em&gt;builder&lt;/em&gt;, pulls a meta-package which in turn installs
quite a lot of system dependencies needed for compilation.
The Python wheels are then built over the next steps, including our own&amp;nbsp;application.&lt;/p&gt;
&lt;p&gt;In my example I&amp;#8217;ve used the same base Docker image for both, but
this stage could be built from a completely different base image than the one
we use next in the &lt;em&gt;runner&lt;/em&gt;.
In general you might not want to combine Alpine environments with Debian ones for example.&lt;br&gt;
They use different C standard libraries and there&amp;#8217;s a good chance that
compiled packages will not function correctly across&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;runner&lt;/em&gt; builds our final image.
The process is lightweight and simply installs the previously built packages and boots the app.
The trick here is&amp;nbsp;the &lt;code&gt;COPY&lt;/code&gt; directive which will allow us to fetch content from the previous
stage into the new clean environment.
This can potentially mean significant image size savings and a simplified runner&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;builder&lt;/em&gt; stage is then discarded and only the runner is presented as the final product
without the extra baggage associated with&amp;nbsp;compiling.&lt;/p&gt;
&lt;p&gt;Additional saving in build time can be achieved if for example we push the built wheels to
a private PyPI repository, or even cache the whole Docker image whenever&amp;nbsp;possible.&lt;/p&gt;
&lt;p&gt;Naming each stage using&amp;nbsp;the &lt;code&gt;AS&lt;/code&gt; directive makes this process nice and tidy,
even if the naming of terminal images is not really&amp;nbsp;required.&lt;/p&gt;
&lt;h2&gt;Read&amp;nbsp;more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;Docker docs multi-stage&amp;nbsp;builds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="Docker"/></entry><entry><title>Quick IPTables filtering</title><link href="https://wooptoo.com/blog/quick-iptables-setup/" rel="alternate"/><published>2019-03-03T00:00:00+00:00</published><updated>2019-03-03T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2019-03-03:/blog/quick-iptables-setup/</id><summary type="html">&lt;p&gt;The Linux kernel has a great firewall built-in commonly referred to as &lt;em&gt;iptables&lt;/em&gt;.
Actually iptables is only the user-space tool that you as the user interact with,
while the kernel module doing all the work is actually called &lt;em&gt;Netfilter&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here are a few tips on how to quickly set up …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The Linux kernel has a great firewall built-in commonly referred to as &lt;em&gt;iptables&lt;/em&gt;.
Actually iptables is only the user-space tool that you as the user interact with,
while the kernel module doing all the work is actually called &lt;em&gt;Netfilter&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here are a few tips on how to quickly set up a filtering firewall on a Linux&amp;nbsp;box.&lt;/p&gt;
&lt;p&gt;The filtering mechanism uses a series of groups called &lt;em&gt;&amp;#8220;chains&amp;#8221;&lt;/em&gt; in the literature.
The main predefined chains&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="caps"&gt;INPUT&lt;/span&gt; &amp;#8212; All the packets reaching the host&amp;nbsp;computer&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;OUTPUT&lt;/span&gt; &amp;#8212; Packets originating from the host&amp;nbsp;computer&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;FORWARD&lt;/span&gt; &amp;#8212; Packets routed / passing&amp;nbsp;through.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these chains can have a default policy such as accepting all packets
by default or dropping&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Individual rules are then added to a List-like structure within each of these chains.
Rules are then read from the top to the bottom for each intercepted &lt;span class="caps"&gt;IP&lt;/span&gt;&amp;nbsp;packet.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s go on and add a few rules. These commands are all run as the&amp;nbsp;superuser.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The iptables command can append&amp;nbsp;(&lt;code&gt;-A&lt;/code&gt;), insert&amp;nbsp;(&lt;code&gt;-I&lt;/code&gt;), delete&amp;nbsp;(&lt;code&gt;-D&lt;/code&gt;) rules from the list.
In this case we append to the &lt;span class="caps"&gt;INPUT&lt;/span&gt;&amp;nbsp;chain.&lt;/p&gt;
&lt;p&gt;The first one will allow all requests to&amp;nbsp;the &lt;code&gt;lo&lt;/code&gt; interface: a shorthand for the localhost.&lt;br&gt;&amp;nbsp;The &lt;code&gt;-j&lt;/code&gt; flag indicates which action should be applied when the rule is matched,
such&amp;nbsp;as &lt;code&gt;ACCEPT&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;,&amp;nbsp;etc.&lt;/p&gt;
&lt;p&gt;The second rule will allow all inbound &lt;span class="caps"&gt;TCP&lt;/span&gt; packets on ports 80 and 443.
IPTables uses the concept of extensions. These are &lt;em&gt;loaded&lt;/em&gt; using&amp;nbsp;the &lt;code&gt;-m&lt;/code&gt; flag.&lt;br&gt;
For the second rule we use&amp;nbsp;the &lt;code&gt;multiport&lt;/code&gt; extension which allows to specify two
ports at&amp;nbsp;once.&lt;/p&gt;
&lt;p&gt;You probably also want to allow &lt;span class="caps"&gt;UDP&lt;/span&gt; traffic on the same ports to account for newer
protocols such as &lt;span class="caps"&gt;QUIC&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We don&amp;#8217;t necessarily have to use extensions, a simple rule will also work such as this one,
where all inbound traffic on &lt;span class="caps"&gt;TCP&lt;/span&gt; port 22 is&amp;nbsp;accepted.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -A INPUT -p tcp --dport 22 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However a cautious user will move their &lt;span class="caps"&gt;SSH&lt;/span&gt; daemon from port 22 to something
a bit more obscure, and turn on public key based&amp;nbsp;authentication.&lt;/p&gt;
&lt;p&gt;All rules can then be visualised using a command such&amp;nbsp;as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -nL --line-numbers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will display each rule preceded by its &lt;em&gt;line number&lt;/em&gt;.
This is significant as we&amp;#8217;ll see in the following&amp;nbsp;example.&lt;/p&gt;
&lt;p&gt;Due to how some protocols work we will also have to allow inbound traffic &lt;em&gt;in reply&lt;/em&gt; to
outbound traffic that was initiated from the host&amp;nbsp;machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -I INPUT 2 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We want this rule to be inserted near the top of the &lt;span class="caps"&gt;INPUT&lt;/span&gt; chain on the 2&lt;sup&gt;nd&lt;/sup&gt; position in the list,
because it will be used quite a lot.
We then use&amp;nbsp;the &lt;code&gt;state&lt;/code&gt; extension here, which enables us to specify that for already &lt;em&gt;established&lt;/em&gt; connections
the firewall should allow all inbound traffic.
This rule is important as without it many protocols will break
(such as outbound &lt;span class="caps"&gt;HTTP&lt;/span&gt; from the host&amp;nbsp;machine).&lt;/p&gt;
&lt;p&gt;If we now list the chain rules again we can see that this was added to the 2&lt;sup&gt;nd&lt;/sup&gt; position:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Chain INPUT (policy ACCEPT)
num  target     prot opt source        destination
1    ACCEPT     all  --  0.0.0.0/0     0.0.0.0/0 
2    ACCEPT     all  --  0.0.0.0/0     0.0.0.0/0     state ESTABLISHED
3    ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     multiport dports 80,443
4    ACCEPT     udp  --  0.0.0.0/0     0.0.0.0/0     multiport dports 80,443
5    ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     tcp dpt:22
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Don&amp;#8217;t worry if you make a mistake, rules can always be deleted by their &lt;em&gt;index&lt;/em&gt; in the&amp;nbsp;chain.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -D INPUT 6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or even &lt;em&gt;flush&lt;/em&gt; the entire chain&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -F INPUT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally we could drop all the remaining traffic with something&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -A INPUT -j DROP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But then the drop rule will always have to stay at the bottom of the chain.
A better practice is to change the Policy of the entire chain to &lt;span class="caps"&gt;DROP&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -P INPUT DROP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will then be the default catch-all action for each packet which doesn&amp;#8217;t match one of the rules we&amp;nbsp;defined.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Chain INPUT (policy DROP)
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As for the &lt;span class="caps"&gt;OUTPUT&lt;/span&gt; chain, this usually has a default &lt;span class="caps"&gt;ACCEPT&lt;/span&gt; policy, as we generally trust all 
outbound traffic from our&amp;nbsp;machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -P OUTPUT ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally persist the state of the chains to the disk,
and make sure that the iptables service is enabled by your daemon manager &amp;#8212; such as&amp;nbsp;systemd.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables-save &amp;gt; /etc/iptables/iptables.rules
systemctl enable iptables
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will ensure that the rules are actually kept and loaded between reboots.
And we are done, you have now configured a few simple filtering rules for your Linux firewall.
Below are a few good resources to further your knowledge with&amp;nbsp;iptables.&lt;/p&gt;
&lt;h2&gt;Read&amp;nbsp;More&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.centos.org/HowTos/Network/IPTables"&gt;CentOS IPTables&amp;nbsp;Howto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/index.php/Iptables#Basic_concepts"&gt;IPTables Basic&amp;nbsp;Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ma.ttias.be/googles-quic-protocol-moving-web-tcp-udp/"&gt;Google&amp;#8217;s &lt;span class="caps"&gt;QUIC&lt;/span&gt; protocol: moving the web from &lt;span class="caps"&gt;TCP&lt;/span&gt; to &lt;span class="caps"&gt;UDP&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man iptables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man iptables-extensions&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="Devops"/><category term="Linux"/></entry><entry><title>Modding the Inspiron 7570</title><link href="https://wooptoo.com/blog/inspiron-7570-plus-plus/" rel="alternate"/><published>2018-03-26T00:00:00+00:00</published><updated>2018-03-26T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2018-03-26:/blog/inspiron-7570-plus-plus/</id><summary type="html">&lt;p&gt;Not too long ago I purchased the Inspiron 15 7570 laptop from Dell&amp;#8217;s outlet website.
Having owned the amazing Inspiron 7537 for the past four years I had very high expectations from this&amp;nbsp;machine.&lt;/p&gt;
&lt;p&gt;While it&amp;#8217;s a really good product in many ways with great specs, I was …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Not too long ago I purchased the Inspiron 15 7570 laptop from Dell&amp;#8217;s outlet website.
Having owned the amazing Inspiron 7537 for the past four years I had very high expectations from this&amp;nbsp;machine.&lt;/p&gt;
&lt;p&gt;While it&amp;#8217;s a really good product in many ways with great specs, I was bitterly disappointed by its remarkably poor screen. 
The unpleasant blue tint and the &lt;a href="https://www.notebookcheck.net/Dell-Inspiron-15-7570-i7-8550U-940MX-Laptop-Review.263654.0.html#toc-display"&gt;low colour gamut&lt;/a&gt;
are a complete eyesore, enough to put off even casual users. 
The colour mangling and the banding that can be seen in gradients make this completely unusable for any sort of design.&lt;br&gt;
I&amp;#8217;m not sure how Dell even decided on this panel in the first&amp;nbsp;place.&lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;intrigue&lt;/h2&gt;
&lt;p&gt;I initially wanted to send it back due to this &lt;em&gt;defect&lt;/em&gt; but decided to keep the machine at the last&amp;nbsp;minute.  &lt;/p&gt;
&lt;p&gt;After a bit of googling around I figured I could replace the &lt;span class="caps"&gt;LCD&lt;/span&gt; panel myself with a &lt;em&gt;much&lt;/em&gt; nicer one. 
The panel that the laptop uses is a &lt;span class="caps"&gt;NV156FHM&lt;/span&gt;-N35 from the Chinese manufacturer &lt;span class="caps"&gt;BOE&lt;/span&gt;.&lt;br&gt;
Now &lt;span class="caps"&gt;BOE&lt;/span&gt; is a serious competitor to Samsung and &lt;span class="caps"&gt;LG&lt;/span&gt;, but all companies have their fair share of lower-end products.
I&amp;#8217;m convinced this is the absolute cheapest panel Dell could possibly&amp;nbsp;source.  &lt;/p&gt;
&lt;p&gt;The panel I decided to replace it with is from the same manufacturer but with much better specs:
The &lt;a href="http://www.panelook.com/NV156FHM-N61_BOE_15.6_LCM_overview_29824.html"&gt;&lt;span class="caps"&gt;NV156FHM&lt;/span&gt;-N61&lt;/a&gt; – a High Colour Gamut screen with 16M colours, much higher Luminance and Contrast.
The panel&amp;#8217;s connector, voltage, and physical size are identical to the old one. It&amp;#8217;s essential to look into these details before ordering
a&amp;nbsp;replacement. &lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;shop&lt;/h2&gt;
&lt;p&gt;I managed to find this screen on AliExpress from a Chinese seller. While this might not inspire trust to most people, I decided to take the chance.
The panel itself was about £40 plus another £12 for transport – so not a tremendous risk.
The seller assured me that they&amp;#8217;re sending me the exact model I request and not some replacement that they happen to have in stock.
The panel arrived well packaged and in perfect condition in about two weeks time (which is fast for&amp;nbsp;China).&lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;teardown&lt;/h2&gt;
&lt;p&gt;The teardown is the hardest part of course and one has to be extra careful.&lt;br&gt;
I used a few tools in order to achieve this without irreparably destroying my&amp;nbsp;laptop:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;two spudgers for prying (must be soft plastic! &lt;em&gt;never use a screwdriver&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;a guitar&amp;nbsp;pick&lt;/li&gt;
&lt;li&gt;a Philips&amp;nbsp;screwdriver&lt;/li&gt;
&lt;li&gt;Replacement double-sided 3M tape. I used a 5mm wide one, but you can also use&amp;nbsp;4mm.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dell are open about their hardware and have published a very comprehensive service manual for this laptop.
This manual is freely available to &lt;a href="http://topics-cdn.dell.com/pdf/inspiron-15-7570-laptop_service-manual_en-us.pdf"&gt;download from here&lt;/a&gt;.
Please read this manual together with this post as it has some very good advice.
This manual does cover the first part of this teardown in great detail. 
Unfortunately it does not cover the screen assembly&amp;nbsp;teardown.&lt;/p&gt;
&lt;p&gt;You first want to untighten the screws on the backplate. 
Only the bottom four will come out entirely, the others have stoppers on the other&amp;nbsp;side.&lt;/p&gt;
&lt;p&gt;Using the spudger stick carefully begin prying the laptop open. You want to start from the back of the 
screen where the vents are and slowly work your way around the&amp;nbsp;edges.&lt;/p&gt;
&lt;p&gt;The first time you open it it will be a bit harder. The plastic is tightly fit together and will not give so easily, 
but with enough care you should be able to get it done. &lt;em&gt;Go slowly and never force it open.&lt;/em&gt;
You should be able to pop the entire bottom plate if you pry around the&amp;nbsp;edges.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wooptoo.com/images/dell7570.jpg"&gt;&lt;img alt="Dell 7570 internals" src="https://wooptoo.com/images/dell7570.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This should be revealed before you if you manage to open the laptop.
All its internals are within reach which makes this machine very easy to&amp;nbsp;service.&lt;/p&gt;
&lt;h2&gt;Screen&amp;nbsp;teardown&lt;/h2&gt;
&lt;p&gt;What we&amp;#8217;re interested in next is the WiFi antenna connector in the upper left hand corner (left of the fan),
and the eDP connector of the screen in the upper right hand corner (above &lt;span class="caps"&gt;DIMM1&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;You need to remove the screw and little plate which holds the WiFi card in place, so that the antenna can be removed.
You should screw the card back in or remove it completely after this process, so it doesn&amp;#8217;t dangle&amp;nbsp;about.&lt;/p&gt;
&lt;p&gt;The eDP connector should be removed with extra care as to not damage it. You&amp;#8217;re going to be
out of luck if you damage this cable as the laptop is new enough that spare parts cannot be found online &lt;em&gt;yet&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Carefully remove the stickers which hold the eDP connector in place and remove the metal latch of the connector.
Use the pointy end of the spudger tool (the one that looks like a pencil) on the latch rather than your&amp;nbsp;fingers.&lt;/p&gt;
&lt;p&gt;Now remove the remove the two plastic hinge covers with the pointy end of the spudger. 
These should come off easy.
Take note of how the antenna and panel connectors are routed through the&amp;nbsp;hinges. &lt;/p&gt;
&lt;p&gt;Place the laptop with the screen open facing down on a soft surface. 
We&amp;#8217;re going to unscrew the hinges next and we want them in an &lt;em&gt;&amp;#8220;open&amp;#8221;&lt;/em&gt; position when the screen is&amp;nbsp;removed. &lt;/p&gt;
&lt;p&gt;The hinges are the two solid metal plates you see at the top of the laptop. 
Untighten the four M2 screws which hold the hinge on the left hand side and the three on the right hand&amp;nbsp;side.&lt;/p&gt;
&lt;h2&gt;Around the&amp;nbsp;edges&lt;/h2&gt;
&lt;p&gt;At this point the screen assembly should be completely detached from the rest of the laptop body.
Put aside the laptop with its back cover and let&amp;#8217;s get working on the screen itself.
Take note that advancing beyond this point might void your warranty with&amp;nbsp;Dell.&lt;/p&gt;
&lt;p&gt;Using the same technique that you learned in the first part of this tutorial, pry around the edges of the soft plastic frame of the screen.
Only this time you will need the guitar pick. The soft plastic is forgiving but again, never force it&amp;nbsp;open.&lt;/p&gt;
&lt;p&gt;I recommend starting at the top left hand side and once you get a bit opened with the pick continue with the spudger. 
At some point you&amp;#8217;ll be able to open it using your hands. &lt;em&gt;Go&amp;nbsp;slowly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One you get to the bottom bit take extra care as this might take a bit longer to come off. I&amp;#8217;ve used the spudger extensively here to 
pry the edges. Never shove the tool inside as this might damage the screen circuit at the bottom of the panel. Take extra care with the webcam bit at the top as well.
Even if you swap the panel out, it&amp;#8217;s still a good idea to keep the old one around so you want it in a good and reusable&amp;nbsp;condition.&lt;/p&gt;
&lt;p&gt;This frame also has some double sided sticky tape which might get damaged in the process of removing it. We&amp;#8217;ll replace this tape
in the following few&amp;nbsp;steps.&lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;switcheroo&lt;/h2&gt;
&lt;p&gt;Congrats, you&amp;#8217;ve got to the meat of this tutorial. You should now have the &lt;span class="caps"&gt;LCD&lt;/span&gt; in front of you ready to be changed.
The panel is stuck to the back cover of the laptop using two 1cm wide double sided pieces of tape, running from the top of the
panel to the bottom. We&amp;#8217;ll need to separate the panel from this cover. We&amp;#8217;ll also try not to damage this tape as it is essential
to holding the &lt;span class="caps"&gt;LCD&lt;/span&gt; into place – and can be easily reused if we don&amp;#8217;t stick our oily fingers to&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Using the spudger (or even two of them) slowly begin to separate the panel from the back cover. Again, do not force it apart.
Take your time. Do this on both sides. I would recommend not pulling from the tabs you see at the&amp;nbsp;bottom.&lt;/p&gt;
&lt;p&gt;If this is successful you should now have separated the panel. You need to unplug the eDP plug from its back.
This also has a metal latch which can be removed using the spudger&amp;#8217;s tip. Again, take extra care when handling this cable.
Part of it might be stuck to the back of your&amp;nbsp;panel.&lt;/p&gt;
&lt;p&gt;Move the new panel in, connect the eDP connector and its latch, and gently press it against the double sided sticky tape from
the back once it&amp;#8217;s in place. It should easily fit, if it doesn&amp;#8217;t check around the edges whether a piece of the WiFi antenna has
been caught behind the&amp;nbsp;panel.&lt;/p&gt;
&lt;p&gt;There should be some black double-sided tape inside of the frame, about 3mm&amp;#8212;4mm wide.
If this has been damaged or has too many finger prints you&amp;#8217;ll need to change it with a fresh one. 
Remove the existing tape from the frame and apply a new one. Use tweezers in order to not leave prints on it.
Once this is done around all edges press the frame back into the screen cover and gently press on the edges so
that the tape sticks to the&amp;nbsp;screen.&lt;/p&gt;
&lt;h2&gt;The final&amp;nbsp;touch&lt;/h2&gt;
&lt;p&gt;From this point on you can proceed in doing the steps in reverse order until you get back to a fully assembled laptop.
Take extra care when putting the screen assembly back together and plugging the WiFi and panel connectors back in.
Remember how the cables were&amp;nbsp;routed.&lt;/p&gt;
&lt;p&gt;If you got everything right you should have a fully assembled machine at the end of this, now with a much better screen!
The &lt;span class="caps"&gt;NV156FHM&lt;/span&gt;-N61 has great colour reproduction with 100% sRGB coverage and much warmer colours.
The new panel also retains its colour accuracy much better than the original when the backlight is&amp;nbsp;dimmed.&lt;/p&gt;
&lt;p&gt;All in all this makes this machine much more enjoyable to use, with colours surpassing even my old 
Inspiron&amp;nbsp;7537.&lt;/p&gt;
&lt;h2&gt;Read&amp;nbsp;more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.lifewire.com/lcd-monitors-and-color-gamuts-833038"&gt;Monitors and color&amp;nbsp;gamuts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.nanosysinc.com/dot-color-archive/2012/08/14/color-space-confusion"&gt;Color space&amp;nbsp;confusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="Teardown"/><category term="Dell"/><category term="Modding"/></entry><entry><title>Mirroring Git repos</title><link href="https://wooptoo.com/blog/mirror-git-repo/" rel="alternate"/><published>2018-01-10T00:00:00+00:00</published><updated>2018-01-10T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2018-01-10:/blog/mirror-git-repo/</id><summary type="html">&lt;p&gt;Here is a quick way to mirror a git repo from one remote to another. 
This is not only useful for mirroring but also for&amp;nbsp;backups.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;git clone --mirror git@gitlab.com/myuser/myrepo.git&lt;/code&gt; command will create a mirrored repo, which 
is pretty much like&amp;nbsp;a &lt;code&gt;bare&lt;/code&gt; repo …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here is a quick way to mirror a git repo from one remote to another. 
This is not only useful for mirroring but also for&amp;nbsp;backups.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;git clone --mirror git@gitlab.com/myuser/myrepo.git&lt;/code&gt; command will create a mirrored repo, which 
is pretty much like&amp;nbsp;a &lt;code&gt;bare&lt;/code&gt; repo, but it also maps all local branches to the remote branches and gets all the refs (like tags).
Basically a one to one copy of the remote&amp;nbsp;repo.&lt;/p&gt;
&lt;p&gt;Then it can be pushed to the new remote&amp;nbsp;with &lt;code&gt;git push --mirror git@mydomain.com/myuser/myrepo.git&lt;/code&gt;.
If you ever need to update the local clone with commits from the old remote (or the new one) it can be done&amp;nbsp;with &lt;code&gt;git fetch --all&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And done, a nice easy way to move entire&amp;nbsp;repos.&lt;/p&gt;</content><category term="articles"/><category term="Git"/><category term="Devops"/></entry><entry><title>Introduction to Python pathlib</title><link href="https://wooptoo.com/blog/python-pathlib/" rel="alternate"/><published>2017-11-05T00:00:00+00:00</published><updated>2017-11-05T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2017-11-05:/blog/python-pathlib/</id><summary type="html">&lt;p&gt;One of the first things many beginner Python tutorials teach is how to read or write files using
patterns&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then walk directories with libraries such&amp;nbsp;as &lt;code&gt;os&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which will go recursively through results …&lt;/p&gt;</summary><content type="html">&lt;p&gt;One of the first things many beginner Python tutorials teach is how to read or write files using
patterns&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then walk directories with libraries such&amp;nbsp;as &lt;code&gt;os&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which will go recursively through results depth first, leaving to &lt;em&gt;you&lt;/em&gt;, the programmer,
the joy of writing your own algorithm for 
searching files or listing files of a certain&amp;nbsp;type.&lt;/p&gt;
&lt;p&gt;Other calls such&amp;nbsp;as &lt;code&gt;os.rename&lt;/code&gt; or&amp;nbsp;to &lt;code&gt;os.path.join&lt;/code&gt; have the developer&lt;br&gt;
manipulate paths manually with string concatenation&amp;nbsp;and &lt;code&gt;os.sep&lt;/code&gt;. 
Not much&amp;nbsp;fun. &lt;/p&gt;
&lt;p&gt;Neither of these approaches is wrong. But they&amp;#8217;re cumbersome and code
can get thorny&amp;nbsp;quickly.&lt;/p&gt;
&lt;p&gt;Today we&amp;#8217;re going to take a look&amp;nbsp;at &lt;code&gt;pathlib&lt;/code&gt; which was introduced in Python 3.4
and simplifies our work with files tremendously.
We&amp;#8217;ll showcase pathlib&amp;#8217;s most popular features.&lt;br&gt;
Let&amp;#8217;s start with the basics: Reading and writing text&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re going to&amp;nbsp;use &lt;code&gt;read_text&lt;/code&gt; and &lt;code&gt;write_text&lt;/code&gt; on our Path instance.
Let&amp;#8217;s throw in some&amp;nbsp;yaml.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;yaml&lt;/span&gt;

&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp/file.yaml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;yaml_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# yaml_content&lt;/span&gt;
&lt;span class="c1"&gt;# {&amp;#39;name&amp;#39;: &amp;#39;Radu&amp;#39;, &amp;#39;uses&amp;#39;: &amp;#39;linux&amp;#39;, &amp;#39;twitter&amp;#39;: &amp;#39;wooptoo&amp;#39;}&lt;/span&gt;

&lt;span class="n"&gt;yaml_content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rad&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml_content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What if we want to rename our file? We&amp;#8217;ll reuse&amp;nbsp;the &lt;code&gt;path&lt;/code&gt; from the previous&amp;nbsp;example.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp/file.yaml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;new_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;myfile.yaml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/myfile.yaml&amp;#39;)&lt;/span&gt;

&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# False&lt;/span&gt;

&lt;span class="n"&gt;new_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our file was renamed in a few easy steps, in a nice object oriented fashion.&lt;br&gt;
&lt;code&gt;path.with_suffix('.txt')&lt;/code&gt; is very similar, and will just change our file&amp;#8217;s extension 
and keep the initial file&amp;nbsp;name.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PosixPath&lt;/code&gt; represents our path instance, which enables us to do all sorts
of operations, like creating a new directory, checking for existence,
checking for file type, getting size, checking for user, group, permissions, etc. 
Basically everything we would previously do with&amp;nbsp;the &lt;code&gt;os.path&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s fetch all our yaml files now&amp;nbsp;with &lt;code&gt;glob&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*.yaml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# [PosixPath(&amp;#39;tmp/myfile4.yaml&amp;#39;),&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/myfile3.yaml&amp;#39;),&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/myfile2.yaml&amp;#39;),&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/myfile.yaml&amp;#39;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we can take it from&amp;nbsp;there. &lt;code&gt;glob&lt;/code&gt; and &lt;code&gt;rglob&lt;/code&gt; are super handy for this kind of stuff.
Of course Python had&amp;nbsp;the &lt;code&gt;glob&lt;/code&gt; module before, but having it under pathlib&amp;#8217;s umbrella is extremely&amp;nbsp;handy.&lt;/p&gt;
&lt;p&gt;Next we can iterate through sub-directories using rglob with patterns&amp;nbsp;like &lt;code&gt;./&lt;/code&gt;
Or we can just&amp;nbsp;use &lt;code&gt;iterdir&lt;/code&gt; which is&amp;nbsp;nicer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dgen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dgen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# [PosixPath(&amp;#39;tmp/snappysnaps&amp;#39;),&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/cameraphotos&amp;#39;),&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/yamlfiles&amp;#39;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And one last thing which I&amp;#8217;m going to touch upon is traversing folders,
and even creating new&amp;nbsp;paths.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;new_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pictures&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;camera&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# PosixPath(&amp;#39;tmp/pictures/camera&amp;#39;)&lt;/span&gt;

&lt;span class="n"&gt;new_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;new_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This wizardry is done by overloading&amp;nbsp;the &lt;code&gt;/&lt;/code&gt; operator of the Path instance. 
In Python 3 this is the&amp;nbsp;private &lt;code&gt;__truediv__&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;myfile.txt&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__truediv__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;myfile.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In a nutshell these are the most common cases 
which you&amp;#8217;ll use on a daily basis if you work with files a lot.&lt;br&gt;
We&amp;#8217;ve managed to replace three different libraries (os, glob and open/read)&amp;nbsp;with 
&lt;code&gt;pathlib&lt;/code&gt; which gives us a neat developer&amp;nbsp;interface.&lt;/p&gt;
&lt;p&gt;Since pathlib is now part of the Python standard library there&amp;#8217;s
absolutely no reason to not use it for new projects.
More on the library can be read in &lt;a href="https://docs.python.org/3/library/pathlib.html"&gt;the manual page&lt;/a&gt;.&lt;/p&gt;</content><category term="articles"/><category term="Python"/></entry><entry><title>Scraping with BeautifulSoup</title><link href="https://wooptoo.com/blog/scraping-with-beautifulsoup/" rel="alternate"/><published>2017-03-09T00:00:00+00:00</published><updated>2017-03-09T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2017-03-09:/blog/scraping-with-beautifulsoup/</id><summary type="html">&lt;p&gt;&lt;a href="https://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; is a handy library for web scraping that&amp;#8217;s mature, easy to use and feature complete.
It can be regarded as jQuery&amp;#8217;s equivalent in the Python world.
In this post we&amp;#8217;re going to scrape the front page&amp;nbsp;of &lt;code&gt;wooptoo.com&lt;/code&gt; and output a clean, &lt;span class="caps"&gt;JSON&lt;/span&gt; version of …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; is a handy library for web scraping that&amp;#8217;s mature, easy to use and feature complete.
It can be regarded as jQuery&amp;#8217;s equivalent in the Python world.
In this post we&amp;#8217;re going to scrape the front page&amp;nbsp;of &lt;code&gt;wooptoo.com&lt;/code&gt; and output a clean, &lt;span class="caps"&gt;JSON&lt;/span&gt; version of it.
The library can also handle &lt;span class="caps"&gt;DOM&lt;/span&gt; manipulation, i.e. adding elements to the &lt;span class="caps"&gt;HTML&lt;/span&gt; document, but that&amp;#8217;s beyond the
scope of this&amp;nbsp;article.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://wooptoo.com/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The bread and butter of scraping with &lt;span class="caps"&gt;BS&lt;/span&gt; is&amp;nbsp;the &lt;code&gt;find_all&lt;/code&gt; method.
&lt;code&gt;select&lt;/code&gt; works similarly but it uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; selector syntax&lt;/a&gt; à la jQuery.
Their output will be identical in this&amp;nbsp;case.  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;_posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;_posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One catch to be aware of is that &lt;span class="caps"&gt;BS&lt;/span&gt; will work with special bs4 data structures, which inherit the 
built-in Python structures. So a list of posts will yield&amp;nbsp;a &lt;code&gt;bs4.element.ResultSet&lt;/code&gt; and each
individual entry will be&amp;nbsp;a &lt;code&gt;bs4.element.Tag&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;find_all&lt;/code&gt; method allows us to also select html elements using native regular expressions.
This enables us to fetch all the posts from 2014 for&amp;nbsp;example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;d2014&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^2014&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d2014&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can select the child of an element either using chained calls&amp;nbsp;to &lt;code&gt;find&lt;/code&gt;
or using&amp;nbsp;the &lt;code&gt;select_one&lt;/code&gt; method. Both will only fetch the first&amp;nbsp;match.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;post-title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.post-title a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Putting it all&amp;nbsp;together:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.post-title a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_titles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_titles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;datetimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.meta-tags a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;summaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;post-summary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;_posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetimes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;summary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;number&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;_posts&lt;/code&gt; will yield a generator object which can be iterated over only once,
as opposed to&amp;nbsp;the &lt;code&gt;_titles&lt;/code&gt; list which does not have the same&amp;nbsp;drawback.&lt;/p&gt;
&lt;p&gt;The BeautifulSoup library is much more complex than the example provided here.
It allows for things like walking the &lt;span class="caps"&gt;DOM&lt;/span&gt; tree in a Javascript-esque&amp;nbsp;manner: &lt;code&gt;page.body.footer.p&lt;/code&gt;,
fetching children nodes, parents, siblings on the same level, and much&amp;nbsp;more.&lt;/p&gt;
&lt;h2&gt;Read&amp;nbsp;more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; Selector&amp;nbsp;Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/"&gt;BeautifulSoup&amp;nbsp;Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wooptoo/blog_snippets/blob/master/scraping-with-beautifulsoup/scrape.py"&gt;Source code files&lt;/a&gt; for this&amp;nbsp;post&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="Python"/></entry><entry><title>Marshmallow schema basics</title><link href="https://wooptoo.com/blog/marshmallow-schema/" rel="alternate"/><published>2017-02-19T00:00:00+00:00</published><updated>2017-02-19T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2017-02-19:/blog/marshmallow-schema/</id><summary type="html">&lt;p&gt;In this post we&amp;#8217;re going to cover the basics of working with Marshmallow.
&lt;a href="https://marshmallow.readthedocs.io/en/latest/"&gt;Marshmallow&lt;/a&gt; is a Python library which enables us to easily sanitize and validate content according to a schema.&lt;br&gt;
Schemas are useful when we want to sift through user provided data en-masse 
as opposed to dealing with …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In this post we&amp;#8217;re going to cover the basics of working with Marshmallow.
&lt;a href="https://marshmallow.readthedocs.io/en/latest/"&gt;Marshmallow&lt;/a&gt; is a Python library which enables us to easily sanitize and validate content according to a schema.&lt;br&gt;
Schemas are useful when we want to sift through user provided data en-masse 
as opposed to dealing with each item&amp;nbsp;individually. &lt;/p&gt;
&lt;p&gt;Please note that this article refers to the version 2.x of the&amp;nbsp;library.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_Schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;dateformat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we extend the&amp;nbsp;default &lt;code&gt;Schema&lt;/code&gt; class with our own which will output a custom date format.&amp;nbsp;The &lt;code&gt;Meta&lt;/code&gt; subclass supports plenty of methods to customize our schema, 
such as excluding some of the fields or explictly including them.
More can be read in the&amp;nbsp;manual: &lt;code&gt;help(marshmallow.Schema.Meta)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Above we define a custom field which will serialize its value using the provided function.
And we use this field below in our crude blog&amp;nbsp;schema.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CommentSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CommentSchema&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last two are compound fields,&amp;nbsp;with &lt;code&gt;comments&lt;/code&gt; being more interesting
as it is a list of comment schemas. When referring to another schema instead of a field type
we need to use&amp;nbsp;the &lt;code&gt;Nested&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;And in a nutshell there we have it.
Now all that&amp;#8217;s left is to use it to sanitize our blog post through the&amp;nbsp;schema:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PostSchema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Validation was left out from here and we will cover it in another blog&amp;nbsp;post.&lt;/p&gt;</content><category term="articles"/><category term="Python"/><category term="Schema"/></entry><entry><title>A new AngularJS project seed</title><link href="https://wooptoo.com/blog/angular-seed/" rel="alternate"/><published>2016-07-22T00:00:00+00:00</published><updated>2016-07-22T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2016-07-22:/blog/angular-seed/</id><summary type="html">&lt;p&gt;A few months ago I started working on a project seed for&amp;nbsp;AngularJS &lt;code&gt;1.x&lt;/code&gt;.&lt;br&gt;
A project seed is the initial skeleton a developer would use to get things
up and running quickly when commencing a new project. There are plenty of these around
with the most popular being Yeoman …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A few months ago I started working on a project seed for&amp;nbsp;AngularJS &lt;code&gt;1.x&lt;/code&gt;.&lt;br&gt;
A project seed is the initial skeleton a developer would use to get things
up and running quickly when commencing a new project. There are plenty of these around
with the most popular being Yeoman with the &lt;a href="https://github.com/yeoman/generator-angular#readme"&gt;Angular generator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So why another one you would ask? Simply because I preferred to use
my own conventions and my preferred tools. And also because of a bit
of &lt;span class="caps"&gt;NIH&lt;/span&gt; syndrome&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Some of the my first requirements were using Gulp as a task runner, 
just because it&amp;#8217;s so much easier to write than Grunt.
Next up was using Bower for front-end asset management and Sass for styles.
Bower is a proven package manager that is easy to use and Sass needs no&amp;nbsp;introduction.&lt;/p&gt;
&lt;p&gt;Down the path it became obvious that I was going to need a few optimizations in place
in order to build great performing Angular&amp;nbsp;apps.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;templateCache&lt;/code&gt; is a great improvement over having every single view sent via &lt;span class="caps"&gt;XHR&lt;/span&gt; on demand.
It basically concatenates all of the views and loads them all at once when the app&amp;nbsp;starts.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ng-annotate&lt;/code&gt; does the automatic annotation of the app source (for Dependency Injection), which then can be minified with &lt;em&gt;mangling&lt;/em&gt; of the symbols. This greatly reduces file&amp;nbsp;size.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;localStorage.debug&lt;/code&gt; is a special flag which is set in the browser by the developer. This is read when the app starts and turns&amp;nbsp;on &lt;code&gt;$log.debug&lt;/code&gt; statements and Angular debug classes. These classes are used by tools such as &lt;a href="http://ng-inspector.org/"&gt;ng-inspector&lt;/a&gt;, but otherwise they would unnecesarily pollute the &lt;span class="caps"&gt;DOM&lt;/span&gt; of the&amp;nbsp;app.  &lt;/p&gt;
&lt;p&gt;Other things which I managed to squeeze in are: hinting of &lt;span class="caps"&gt;JS&lt;/span&gt; files with a friendly output, livereload support, configuring the Gulp task runner&amp;nbsp;via &lt;code&gt;gulpconfig.json&lt;/code&gt;. This enables things such as turning the integrated http server on or&amp;nbsp;off. &lt;/p&gt;
&lt;p&gt;The project can be &lt;a href="https://github.com/wooptoo/angular-seed/"&gt;found on Github&lt;/a&gt; and your contribution is more than welcome.
This is pretty much still a work in progress and support for things like &lt;span class="caps"&gt;ES6&lt;/span&gt; will be included in the&amp;nbsp;future.&lt;/p&gt;
&lt;p&gt;What I do not plan on including for now is a full blown generator. My aim is to keep the code simple and&amp;nbsp;lean.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/wooptoo/angular-seed/"&gt;Angular-seed&lt;/a&gt;&lt;/p&gt;</content><category term="articles"/><category term="AngularJS"/><category term="JS"/></entry><entry><title>Keeping home configuration in git</title><link href="https://wooptoo.com/blog/keeping-home-config-in-git/" rel="alternate"/><published>2016-03-06T00:00:00+00:00</published><updated>2016-03-06T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2016-03-06:/blog/keeping-home-config-in-git/</id><summary type="html">&lt;p&gt;Having &lt;em&gt;dotfiles&lt;/em&gt; from&amp;nbsp;the &lt;code&gt;$HOME&lt;/code&gt; directory in git is a great way to keep track of what&amp;#8217;s going on with some of our most
important config&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;However just creating a plain git repo won&amp;#8217;t play nice with other repos found further down in the home tree.
Chances …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Having &lt;em&gt;dotfiles&lt;/em&gt; from&amp;nbsp;the &lt;code&gt;$HOME&lt;/code&gt; directory in git is a great way to keep track of what&amp;#8217;s going on with some of our most
important config&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;However just creating a plain git repo won&amp;#8217;t play nice with other repos found further down in the home tree.
Chances are that at some point we&amp;#8217;ll accidentally issue a git command on the home repo 
instead of the one we actually want to work&amp;nbsp;on.&lt;/p&gt;
&lt;p&gt;So why not make our own git command which handles the home repo exclusively. 
We&amp;#8217;ll name this new&amp;nbsp;command &lt;code&gt;cfg&lt;/code&gt; an create an alias&amp;nbsp;in &lt;code&gt;~/.bashrc&lt;/code&gt; for it.&lt;br&gt;
Since we want git to use a&amp;nbsp;special &lt;code&gt;.cfg&lt;/code&gt; directory instead of the default one and still have the 
working tree set&amp;nbsp;to &lt;code&gt;$HOME&lt;/code&gt; the alias will look like&amp;nbsp;this:  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;git --git-dir=$HOME/.cfg --work-tree=$HOME&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can issue&amp;nbsp;a &lt;code&gt;git init&lt;/code&gt; command in the home dir. If you already have&amp;nbsp;a &lt;code&gt;.git&lt;/code&gt; directory you can
just rename it&amp;nbsp;to &lt;code&gt;.cfg&lt;/code&gt;.&lt;br&gt;
One tweak is configuring cfg not to show untracked&amp;nbsp;files.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cfg config status.showUntrackedFiles no
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we want to be absolutely sure that we won&amp;#8217;t mistakenly add files (like the Pictures directory),
then we need to create&amp;nbsp;a &lt;code&gt;.gitignore&lt;/code&gt; file, either with each ignored directory on a new line,
or with the wildcard&amp;nbsp;symbol:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now when adding files we have to issue&amp;nbsp;a &lt;code&gt;cfg -f add&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;And we&amp;#8217;re&amp;nbsp;done!&lt;/p&gt;</content><category term="articles"/><category term="Git"/><category term="Linux"/></entry><entry><title>Building apps with Gulp</title><link href="https://wooptoo.com/blog/building-apps-with-gulp/" rel="alternate"/><published>2014-04-16T00:00:00+00:00</published><updated>2014-04-16T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2014-04-16:/blog/building-apps-with-gulp/</id><summary type="html">&lt;p&gt;I never quite liked JavaScript building tools.
Grunt always seemed way too complicated for the simple things I wanted to accomplish. Like concatenating files and minifying them. One of the first bash commands that any Linux user learns about&amp;nbsp;is &lt;code&gt;cat&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;You can just cat a few files together and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I never quite liked JavaScript building tools.
Grunt always seemed way too complicated for the simple things I wanted to accomplish. Like concatenating files and minifying them. One of the first bash commands that any Linux user learns about&amp;nbsp;is &lt;code&gt;cat&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;You can just cat a few files together and pipe them to a minifying script and be done. 5 lines of Bash maximum.
Want to also include the build date in your JavaScript file as a header? Just&amp;nbsp;run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/*&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;date&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*/&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;build.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I think Grunt&amp;#8217;s highly declarative syntax was the real barrier here, since it prevented you from doing quick adjustments to the building&amp;nbsp;pipeline.&lt;/p&gt;
&lt;p&gt;This all changed with Gulp. Gulp promotes and imperative approach to building apps and it tries to stay simple and focused on its&amp;nbsp;task.&lt;/p&gt;
&lt;p&gt;Its syntax feels a lot like the Linux &lt;span class="caps"&gt;CLI&lt;/span&gt;. Commands are pipelined together in a very simple&amp;nbsp;manner:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;scripts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;js/*.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uglify&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;build.min.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;build/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Basically it reads files from an input directory, minifies, concatenates and puts the resulting file in&amp;nbsp;the &lt;code&gt;build/&lt;/code&gt; directory. &lt;/p&gt;
&lt;p&gt;Tasks can be created for any kind of task: linting the code, compiling lesscss files,
watching files and recompiling them on change,&amp;nbsp;etc.&lt;/p&gt;
&lt;h2&gt;The &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;The Gulp &lt;span class="caps"&gt;API&lt;/span&gt; is very small, it only has a few commands. Most of which we already encountered 
in the code snippet&amp;nbsp;above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gulp.task
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Declares a new Gulp task. You can also declare tasks which simply run a series of other tasks&amp;nbsp;(dependencies).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gulp.watch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Watches files for changes. For example lesscss&amp;nbsp;files.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gulp.src
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Returns a readable NodeJS stream which can be piped inside the task.
An array of files can also be passed to the src function. This is actually the most common use case.
A feature of src is that it will read the provided files in order. This is significant
for plugins such as&amp;nbsp;less.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gulp.src.pipe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pipes commands to the NodeJS stream. These commands can be gulp plugins such as: uglify, concat, less, etc. They can also be user defined functions.
Many commands can take options&amp;nbsp;(parameters).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gulp.dest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Writes the result to the destination. This does not happen immediately, but upon the completion of other piped&amp;nbsp;commands.&lt;/p&gt;
&lt;p&gt;Gulp makes extensive use of NodeJS streams. Files are read from the filesystem, entirely 
manipulated in memory, then written back to the filesystem. This might not seem like much,
but Grunt used to create many small files for every intermediary result.&lt;br&gt;
To achieve this Gulp&amp;nbsp;uses &lt;code&gt;vinyl&lt;/code&gt;, which creates virtual file&amp;nbsp;objects.&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;example &lt;code&gt;gulpfile.js&lt;/code&gt; used in this article can be found in &lt;a href="https://github.com/wooptoo/blog_snippets/blob/master/building-apps-with-gulp/gulpfile.js"&gt;this git repo&lt;/a&gt; with plenty of&amp;nbsp;comments.&lt;/p&gt;
&lt;h2&gt;Read&amp;nbsp;more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://markdalgleish.github.io/presentation-build-wars-gulp-vs-grunt/"&gt;Build&amp;nbsp;wars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jaysoo.ca/2014/01/27/gruntjs-vs-gulpjs/"&gt;Grunt vs Gulp - Beyond the&amp;nbsp;Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/substack/stream-handbook"&gt;Stream&amp;nbsp;Handbook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="JS"/><category term="NodeJS"/></entry><entry><title>Serving WebP today</title><link href="https://wooptoo.com/blog/serving-webp-today/" rel="alternate"/><published>2014-04-05T00:00:00+00:00</published><updated>2014-04-05T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2014-04-05:/blog/serving-webp-today/</id><summary type="html">&lt;p&gt;WebP is both a lossless image format, like &lt;span class="caps"&gt;PNG&lt;/span&gt;, which preserves every detail 
of the original image, and a lossy format, like &lt;span class="caps"&gt;JPG&lt;/span&gt;, which will compress 
the original image down to a certain&amp;nbsp;quality.&lt;/p&gt;
&lt;p&gt;WebP support in browsers such as Firefox and &lt;span class="caps"&gt;IE&lt;/span&gt; is &lt;a href="http://caniuse.com/webp"&gt;virtually nonexistent&lt;/a&gt;.
However if a big …&lt;/p&gt;</summary><content type="html">&lt;p&gt;WebP is both a lossless image format, like &lt;span class="caps"&gt;PNG&lt;/span&gt;, which preserves every detail 
of the original image, and a lossy format, like &lt;span class="caps"&gt;JPG&lt;/span&gt;, which will compress 
the original image down to a certain&amp;nbsp;quality.&lt;/p&gt;
&lt;p&gt;WebP support in browsers such as Firefox and &lt;span class="caps"&gt;IE&lt;/span&gt; is &lt;a href="http://caniuse.com/webp"&gt;virtually nonexistent&lt;/a&gt;.
However if a big chunk of your audience browses from Chrome, Android Browser or Opera,
you could serve WebP today and reap the&amp;nbsp;benefits.&lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;benchmark&lt;/h2&gt;
&lt;p&gt;So how much better is WebP actually? Here&amp;#8217;s a quick test to find&amp;nbsp;out.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cwebp&lt;/code&gt; from the official &lt;a href="https://developers.google.com/speed/webp/download"&gt;WebP distribution&lt;/a&gt; was used to convert images.
The files used by this benchmark can be found in &lt;a href="https://github.com/wooptoo/blog_snippets"&gt;this git repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source is a lossless 24-bit &lt;span class="caps"&gt;PNG&lt;/span&gt;, optimized&amp;nbsp;with &lt;code&gt;optipng&lt;/code&gt;.&lt;br&gt;
Lossless conversion from &lt;span class="caps"&gt;PNG&lt;/span&gt; to &lt;span class="caps"&gt;WEBP&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cwebp -lossless source.png -o lossless.webp

PNG: 532 KB
WEBP: 386 KB
Webp is 28% smaller.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For comparison I used a &lt;span class="caps"&gt;JPG&lt;/span&gt; image saved at 95% quality.&lt;br&gt;
Lossy conversion to &lt;span class="caps"&gt;WEBP&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cwebp -q 95 source.png -o lossy.webp

JPG is 243 KB.
WEBP is 160 KB.
Webp is 35% smaller.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Bandwidth savings offered by Google&amp;#8217;s new image format can be significant enough
to be worth the&amp;nbsp;trouble.&lt;/p&gt;
&lt;h2&gt;Serving&amp;nbsp;WebP&lt;/h2&gt;
&lt;p&gt;Every browser (user agent) will send&amp;nbsp;an &lt;code&gt;Accept&lt;/code&gt; header with each &lt;span class="caps"&gt;HTTP&lt;/span&gt; request. 
Serving WebP, or any other content actually, can be done in a browser neutral manner by looking at this 
header and responding with&amp;nbsp;a &lt;code&gt;Content-Type&lt;/code&gt; known to the user agent. 
This is the basic principle of content&amp;nbsp;negotiation.&lt;/p&gt;
&lt;p&gt;Chrome advertises its WebP support in a few&amp;nbsp;ways:&lt;/p&gt;
&lt;p&gt;For &lt;span class="caps"&gt;HTML&lt;/span&gt;&amp;nbsp;files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Accept: text/html,…,application/xml;q=0.9,image/webp,*/*;q=0.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For image&amp;nbsp;files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Accept: image/webp,*/*;q=0.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So all we have to do is to match against&amp;nbsp;the &lt;code&gt;image/webp&lt;/code&gt; part in our 
web server config and return the appropriate&amp;nbsp;file.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s another small trick which we will employ in this example. Every image linked
from a &lt;span class="caps"&gt;HTML&lt;/span&gt; document will be linked without a file&amp;nbsp;extension.&lt;/p&gt;
&lt;p&gt;That can&amp;#8217;t be right, can&amp;nbsp;it?&lt;/p&gt;
&lt;p&gt;It is actually right from a &lt;a href="https://wooptoo.com/blog/notes-on-restful-apis/"&gt;&lt;span class="caps"&gt;REST&lt;/span&gt; point of view&lt;/a&gt;. The image is a resource and 
its content type should not be included in the resource path. Content
negotiation should be reserved for the Accept&amp;nbsp;header. &lt;/p&gt;
&lt;h2&gt;Setting up&amp;nbsp;nginx&lt;/h2&gt;
&lt;p&gt;A very basic nginx configuration would&amp;nbsp;be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/webp/img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;add_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Vary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$http_accept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image\/webp&amp;quot;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;rewrite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(.*)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1.webp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$uri.png&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$uri.jpg&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This assumes that&amp;nbsp;the &lt;code&gt;location&lt;/code&gt; will only serve images.&amp;nbsp;The &lt;code&gt;Vary&lt;/code&gt; header is used to instruct proxies that the content type can
differ between server responses.&amp;nbsp;The &lt;code&gt;try_files&lt;/code&gt; directive is used in order to fallback to &lt;span class="caps"&gt;PNG&lt;/span&gt; or &lt;span class="caps"&gt;JPG&lt;/span&gt; images.&lt;br&gt;
This setup does not cover cases where nginx is used as a proxy.
You can read more on that in &lt;a href="http://www.igvita.com/2013/05/01/deploying-webp-via-accept-content-negotiation/"&gt;this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ok, but how about saving images? 
Chrome does another smart thing. If you try to save a WebP image, it will give you
the &lt;span class="caps"&gt;JPG&lt;/span&gt; or the &lt;span class="caps"&gt;PNG&lt;/span&gt; version instead, so that it can be viewed on any&amp;nbsp;device.&lt;/p&gt;
&lt;p&gt;More on WebP and content negotiation can be found&amp;nbsp;below.&lt;/p&gt;
&lt;h2&gt;More&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.igvita.com/2013/05/01/deploying-webp-via-accept-content-negotiation/"&gt;Deploying&amp;nbsp;WebP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/"&gt;Beginners Guide to &lt;span class="caps"&gt;HTTP&lt;/span&gt; cache&amp;nbsp;headers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/HTTP/Content_negotiation"&gt;Content Negotiation on &lt;span class="caps"&gt;MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="WebP"/><category term="HTTP"/><category term="Nginx"/></entry><entry><title>Prototype inheritance in JavaScript</title><link href="https://wooptoo.com/blog/prototypical-inheritance-in-javascript/" rel="alternate"/><published>2014-03-24T00:00:00+00:00</published><updated>2014-03-24T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2014-03-24:/blog/prototypical-inheritance-in-javascript/</id><summary type="html">&lt;p&gt;JavaScript has its own inheritance model that is quite unique among languages.&lt;br&gt;
Since JavaScript has no notion of classes yet and since everything is actually
an object, we will try to clear the confusion by defining a few&amp;nbsp;terms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;class&lt;/strong&gt; is a constructor function which has properties and other …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;JavaScript has its own inheritance model that is quite unique among languages.&lt;br&gt;
Since JavaScript has no notion of classes yet and since everything is actually
an object, we will try to clear the confusion by defining a few&amp;nbsp;terms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;class&lt;/strong&gt; is a constructor function which has properties and other functions defined on&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;instance&lt;/strong&gt; is an object created&amp;nbsp;with &lt;code&gt;new Class&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of inheriting from a base class, instances in JavaScript 
inherit from a prototype object.
The prototype is then referenced by every new instance of the object.&lt;br&gt;
An instance can then have its own properties and methods beyond those 
inherited from the prototype.&lt;br&gt;
JavaScript uses prototype chains: object instance C can have as prototype 
object instance B which in turn can have as prototype object instance A.&lt;br&gt;
A property lookup traverses the prototype chain&amp;nbsp;upwards.&lt;/p&gt;
&lt;h2&gt;Uses&lt;/h2&gt;
&lt;p&gt;So what are prototypes useful&amp;nbsp;for?&lt;/p&gt;
&lt;h3&gt;Subclassing&lt;/h3&gt;
&lt;p&gt;The classical inheritance model found in other languages can deal with 
subclassing very easily.
However, we can achieve subclassing in JavaScript using the&amp;nbsp;prototype.&lt;/p&gt;
&lt;p&gt;Suppose we have a parent class with a few methods declared on its&amp;nbsp;prototype.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ParentClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;ParentClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;firstMethod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;secondMethod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can then create a child class which has as its prototype set to an &lt;strong&gt;instance&lt;/strong&gt;
of the parent&amp;nbsp;class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ChildClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;ChildClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ParentClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we create a new instance of each of these two classes we will notice that the
child instance inherits from the parent&amp;nbsp;class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;parentInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ParentClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;childInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ChildClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;childInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;instanceof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ParentClass&lt;/span&gt;
&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We achieved this by chaining the two classes&amp;#8217; prototypes. A simplified schema 
of the inheritance would&amp;nbsp;be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;child class → child.prototype → parent class → parent.prototype
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Typed&amp;nbsp;Errors&lt;/h3&gt;
&lt;p&gt;Using the same pattern we can create typed errors. Errors are JavaScript&amp;#8217;s 
equivalent to Exceptions found in other languages.
First we need to define a constructor&amp;nbsp;function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;MyError&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then assign an Error instance to its&amp;nbsp;prototype.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we can use&amp;nbsp;it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;something happened&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A nice feature of typed errors is that we can handle each error type&amp;nbsp;using &lt;code&gt;instanceof&lt;/code&gt;. A lot like the exception handling from other&amp;nbsp;languages.&lt;/p&gt;
&lt;p&gt;Only handle MyErrors and rethrow the&amp;nbsp;others.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;something happened&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;instanceof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Read&amp;nbsp;more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://bonsaiden.github.io/JavaScript-Garden/#object.prototype"&gt;JavaScript Garden - The&amp;nbsp;Prototype&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"&gt;&lt;span class="caps"&gt;MDN&lt;/span&gt; Error&amp;nbsp;reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="JS"/></entry><entry><title>Promises in AngularJS</title><link href="https://wooptoo.com/blog/promises-in-angularjs/" rel="alternate"/><published>2014-02-10T00:00:00+00:00</published><updated>2014-02-10T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2014-02-10:/blog/promises-in-angularjs/</id><summary type="html">&lt;p&gt;Promises are quite an old concept that took off only recently in the JavaScript world.
Since version 1.2 promises have become such an important part of AngularJS that they cannot be overlooked anymore.
In this post we will cover the basic concepts of promises in AngularJS with a few …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Promises are quite an old concept that took off only recently in the JavaScript world.
Since version 1.2 promises have become such an important part of AngularJS that they cannot be overlooked anymore.
In this post we will cover the basic concepts of promises in AngularJS with a few use cases at the&amp;nbsp;end.&lt;/p&gt;
&lt;h2&gt;So what are&amp;nbsp;promises?&lt;/h2&gt;
&lt;p&gt;The simplest definition of promises is given by the &lt;a href="http://promises-aplus.github.io/promises-spec/"&gt;Promises/A+ specification&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A promise represents the eventual result of an asynchronous&amp;nbsp;operation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The two main functions performed by promises&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;provide an interface for handling incomplete (asynchronous)&amp;nbsp;operations&lt;/li&gt;
&lt;li&gt;error handling for the said&amp;nbsp;operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With a few nice extras derived from the&amp;nbsp;above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;chain asynchronous operations while avoiding callback&amp;nbsp;hell&lt;/li&gt;
&lt;li&gt;a syntax that loosely resembles&amp;nbsp;the &lt;code&gt;try-catch-finally&lt;/code&gt; construct&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Generic vs. AngularJS&amp;nbsp;implementation&lt;/h2&gt;
&lt;p&gt;Promise instances can either be constructed by specialized factories (the case with Angular), or by static methods on the Promise object&amp;nbsp;itself.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;&lt;span class="caps"&gt;MDN&lt;/span&gt; article&lt;/a&gt; on this topic defines&amp;nbsp;a &lt;code&gt;Promise&lt;/code&gt; as an object that has a few static methods such&amp;nbsp;as &lt;code&gt;resolve&lt;/code&gt; and &lt;code&gt;reject&lt;/code&gt;. This object is used to construct a new Promise instance.
Note that a promise will either: &lt;em&gt;resolve with a value&lt;/em&gt; &lt;strong&gt;or&lt;/strong&gt; &lt;em&gt;reject with a reason&lt;/em&gt;.
Resolve or reject can be called at any later time, a feature which gives promises their asynchronous&amp;nbsp;nature.&lt;/p&gt;
&lt;p&gt;In Angular promises are created with the Deferred &lt;span class="caps"&gt;API&lt;/span&gt;.&amp;nbsp;The &lt;code&gt;$q.defer()&lt;/code&gt; method will construct a new Deferred instance that can either be resolved or rejected. Note that Angular also has&amp;nbsp;a &lt;code&gt;notify&lt;/code&gt; method that provides updates on the execution status.&lt;br&gt;
The Promise instance can then be obtained from&amp;nbsp;the &lt;code&gt;promise&lt;/code&gt; property found on the Deferred&amp;nbsp;instance.&lt;/p&gt;
&lt;p&gt;Either approach works, but Angular might change its behavior in the future to reflect the (eventually) upcoming ECMAScript 6&amp;nbsp;specification.&lt;/p&gt;
&lt;p&gt;After construction, the Promise instance will expose a few methods such&amp;nbsp;as &lt;code&gt;then&lt;/code&gt;, &lt;code&gt;catch&lt;/code&gt; and &lt;code&gt;finally&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;then&lt;/code&gt; accepts a success and an error callback function. Angular also has a notify&amp;nbsp;callback.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catch&lt;/code&gt; only accepts an error callback and is a shorthand&amp;nbsp;for &lt;code&gt;then(null, errorCallback)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;finally&lt;/code&gt; will run regardless of the outcome of the&amp;nbsp;promise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Promises can be chained together&amp;nbsp;(composed):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each &lt;code&gt;then&lt;/code&gt; function will return a derived promise passing either: the success value down the chain, or the error&amp;nbsp;reason.&lt;/p&gt;
&lt;p&gt;A nice feature of the AngularJS implementation is that if a success callback returns a promise instance as value, the chain will resume execution only after that promise is fulfilled and its &lt;em&gt;value&lt;/em&gt; will be passed on to the next success callback.&lt;br&gt;
So basically, we can nest other promises inside the success callbacks and have them interleave with the main promise chain. 
This is immensely powerful and enables us to build services such as the ones mentioned in the &lt;em&gt;Use cases&lt;/em&gt;&amp;nbsp;below.&lt;/p&gt;
&lt;h2&gt;The $http&amp;nbsp;service&lt;/h2&gt;
&lt;p&gt;In AngularJS,&amp;nbsp;the &lt;code&gt;$http&lt;/code&gt; service depends heavily on promises. Each $http call will return a special kind of promise instance that has two extra methods&amp;nbsp;attached: &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;. These methods will receive parameters such&amp;nbsp;as &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;config&lt;/code&gt; from the service, which enable us to work more easily with&amp;nbsp;requests.&lt;/p&gt;
&lt;p&gt;However, it&amp;#8217;s the $http interceptors where promises really shine. Interceptors allow us to hook into http requests and responses, and react to them or modify their outcome. Here are a few&amp;nbsp;examples:&lt;/p&gt;
&lt;p&gt;Pre-processing of&amp;nbsp;requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Displaying a progress&amp;nbsp;indicator.&lt;/li&gt;
&lt;li&gt;Changing the&amp;nbsp;request &lt;code&gt;Content-Type&lt;/code&gt; header, useful when performing asynchronous file uploads for&amp;nbsp;example.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Post-processing&amp;nbsp;responses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication: each time a 401 http response is encountered the user could be redirected to the login&amp;nbsp;page.&lt;/li&gt;
&lt;li&gt;Global error handling: each time a certain kind of error is received from the backend (such as the user not having enough credits to perform an action) a notice could pop&amp;nbsp;up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interceptors can be chained together, since they can return a&amp;nbsp;promise.  &lt;/p&gt;
&lt;h2&gt;Use&amp;nbsp;cases&lt;/h2&gt;
&lt;p&gt;Here are two custom services that rely on&amp;nbsp;promises.&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;ACL&lt;/span&gt;&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;In larger client-side applications you will most probably need some form of ACLs.
&lt;span class="caps"&gt;ACL&lt;/span&gt; stands for Access Control Lists and represents a mechanism for authorizing users to certain parts of the application based on the user group they belong&amp;nbsp;to.&lt;/p&gt;
&lt;p&gt;Reduced to its simplest form, &lt;span class="caps"&gt;ACL&lt;/span&gt; could be expressed as a list of &lt;em&gt;user groups&lt;/em&gt;, with each group entry containing a list of allowed application &lt;em&gt;routes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;At Zooku, we use a hybrid &lt;span class="caps"&gt;ACL&lt;/span&gt; service for the upcoming Control Panel.
This service obtains its routes in two&amp;nbsp;ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;synchronous&lt;/em&gt; &amp;#8212; some of them are predefined and do not change while the application is&amp;nbsp;running.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;asynchronous&lt;/em&gt; &amp;#8212; some are requested from the server-side and change when the user&amp;#8217;s active group&amp;nbsp;changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Promises enable us to have a common interface for checking both synchronous and asynchronous requested&amp;nbsp;routes.&lt;/p&gt;
&lt;p&gt;By creating an initial promise that always resolves, we can then chain&amp;nbsp;a &lt;code&gt;getUserGroup&lt;/code&gt; and&amp;nbsp;a &lt;code&gt;getAcls&lt;/code&gt; request to it. These two requests are performed in the bootstrapping phase of the application or when the user changes credentials, such as logging&amp;nbsp;in.&lt;/p&gt;
&lt;p&gt;After the active user group and the asynchronous ACLs are set, we can then proceed to perform the&amp;nbsp;route &lt;code&gt;check&lt;/code&gt; itself, which will also be chained to the previous&amp;nbsp;requests.&lt;/p&gt;
&lt;p&gt;In our case subsequent route checks will not request the user group nor the &lt;span class="caps"&gt;ACL&lt;/span&gt; routes again (they will be cached for the active session), but by using promises we created an uniform interface that works regardless of the application session&amp;nbsp;state.&lt;/p&gt;
&lt;h3&gt;Validation&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;A well designed validation service has the potential to truly help the user and eliminate a lot of frustration.&lt;br&gt;
While &lt;span class="caps"&gt;HTML5&lt;/span&gt; introduced a few helpful attributes for dealing with validation, employing them in a large application can quickly result in views overloaded with validation attributes, or with attributes that are out of sync with the&amp;nbsp;backend.&lt;/p&gt;
&lt;p&gt;For both Zooku and Sendmachine, we developed a Validation service that has a few nice&amp;nbsp;features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses a chain of rules to validate a field, each rule providing the user with hints about the current state of the&amp;nbsp;field.&lt;/li&gt;
&lt;li&gt;Requests validation rules from the server-side, which enabled us to use the &lt;strong&gt;same&lt;/strong&gt; rules on both the server and the client&amp;nbsp;application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While most rules perform synchronous checks, like checking if a required field is filled-in, others perform asynchronous checks, like checking with the backend if an email is already&amp;nbsp;registered.&lt;/p&gt;
&lt;p&gt;This is another case there promises prove to be very useful and enable us to perform checks on a field regardless of their nature.&lt;br&gt;
The logic is very similar to the &lt;span class="caps"&gt;ACL&lt;/span&gt; service: an initial Promise instance that always resolves is created, and promises that perform further validations are chained to&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;These are just a few use cases where promises play a significant role.
More documentation and code samples can be found in the resources&amp;nbsp;below.&lt;/p&gt;
&lt;h2&gt;Further&amp;nbsp;reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://promises-aplus.github.io/promises-spec/"&gt;Promises/A+&amp;nbsp;specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promises on &lt;span class="caps"&gt;MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.angularjs.org/1.2.12/docs/api/ng.$q"&gt;AngularJS&amp;nbsp;$q&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.angularjs.org/1.2.12/docs/api/ng.$http"&gt;AngularJS&amp;nbsp;$http&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.html5rocks.com/en/tutorials/es6/promises/"&gt;&lt;span class="caps"&gt;HTML5&lt;/span&gt; Rocks - JavaScript&amp;nbsp;Promises&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="AngularJS"/><category term="JS"/></entry><entry><title>CORS Gotchas</title><link href="https://wooptoo.com/blog/cors-gotchas/" rel="alternate"/><published>2012-07-09T00:00:00+00:00</published><updated>2012-07-09T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2012-07-09:/blog/cors-gotchas/</id><summary type="html">&lt;p&gt;&lt;span class="caps"&gt;CORS&lt;/span&gt; stands for Cross-Origin Resource Sharing and represents a method of accessing/sharing resources across domains. These resources can be anything from web fonts to APIs. The &lt;span class="caps"&gt;CORS&lt;/span&gt; standard is only implemented in browsers, since this is the only place where it makes sense. While this is the preferred method …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;span class="caps"&gt;CORS&lt;/span&gt; stands for Cross-Origin Resource Sharing and represents a method of accessing/sharing resources across domains. These resources can be anything from web fonts to APIs. The &lt;span class="caps"&gt;CORS&lt;/span&gt; standard is only implemented in browsers, since this is the only place where it makes sense. While this is the preferred method of sharing resources, it has a few rough&amp;nbsp;edges.  &lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;Basics&lt;/h2&gt;
&lt;p&gt;In the following we&amp;#8217;ll refer to the response headers, since the request headers are automatically set by the browser.&lt;br&gt;
A special header defined in the &lt;a href="http://www.w3.org/TR/cors/"&gt;&lt;span class="caps"&gt;CORS&lt;/span&gt; specification&lt;/a&gt; must be sent by the server in each response to allow access to a&amp;nbsp;resource: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Allow-Origin: allowed-domain.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To allow any domain to access resources the header can be set to a&amp;nbsp;wildcard: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Allow-Origin: *
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the bare minimum and it will suffice for sharing static resources such as web fonts. But once we throw in authentication, which is needed by most APIs, things get&amp;nbsp;messy.&lt;/p&gt;
&lt;h2&gt;Authentication&lt;/h2&gt;
&lt;p&gt;Authentication, with either cookies or &lt;span class="caps"&gt;HTTP&lt;/span&gt; auth, requires an additional response&amp;nbsp;header: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Allow-Credentials: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also the browser needs to send cookies/credentials in the request, so for example in JavaScript we need to pass an additional parameter to &lt;span class="caps"&gt;XHR&lt;/span&gt;&amp;nbsp;object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withCredentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onreadystatechange&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A few notes on&amp;nbsp;authentication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We cannot use domain wildcards for&amp;nbsp;the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header when allowing credentials. The domain must be explicitly stated. This is a feature to prevent &lt;span class="caps"&gt;CSRF&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Credentials are not allowed with &lt;strong&gt;synchronous&lt;/strong&gt; requests in some browsers, since these requests cannot be&amp;nbsp;pre-flighted.&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;IE&lt;/span&gt; and Opera were slow to adopt &lt;span class="caps"&gt;CORS&lt;/span&gt;; only &lt;span class="caps"&gt;IE&lt;/span&gt; 10 and Opera 12 support it&amp;nbsp;properly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Pre-flighted&amp;nbsp;requests&lt;/h2&gt;
&lt;p&gt;Some cross-origin requests are pre-flighted, meaning that&amp;nbsp;an &lt;code&gt;OPTIONS&lt;/code&gt; request is automatically sent by the browser before each request, to query the server for allowed &lt;span class="caps"&gt;HTTP&lt;/span&gt; methods, allowed headers or to check whether credentials are&amp;nbsp;allowed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OPTIONS&lt;/code&gt; requests are only sent if &lt;strong&gt;any&lt;/strong&gt; of the following conditions are&amp;nbsp;met:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The request method is other&amp;nbsp;than &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;POST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The request Content-Type is other&amp;nbsp;than &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt; or &lt;code&gt;text/plain&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Custom headers are&amp;nbsp;set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously,&amp;nbsp;sending &lt;code&gt;OPTIONS&lt;/code&gt; before each real request has a bit of overhead. Fortunately, there is a header that tells the browser to cache these&amp;nbsp;requests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Max-Age: 86400
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Allowing methods and&amp;nbsp;headers&lt;/h2&gt;
&lt;p&gt;In order to create RESTful APIs we need to use more than&amp;nbsp;the &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; verbs.&lt;br&gt;
Servers can inform the clients of the allowed &lt;span class="caps"&gt;HTTP&lt;/span&gt; methods by using another type of &lt;span class="caps"&gt;CORS&lt;/span&gt;&amp;nbsp;header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Allow-Methods: GET, POST, PUT, DELETE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Any number of &lt;span class="caps"&gt;HTTP&lt;/span&gt; verbs can be listed here.&lt;br&gt;
Allowed headers can be set&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Access-Control-Allow-Headers: Origin, X-Reqested-With, Accept
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;X-Reqested-With&lt;/code&gt; is needed for asynchronous&amp;nbsp;requests.&lt;/p&gt;
&lt;p&gt;All these headers&amp;nbsp;except &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; are commonly included only in the pre-flight&amp;nbsp;response.&lt;/p&gt;
&lt;h2&gt;&lt;span class="caps"&gt;IE&lt;/span&gt;&amp;nbsp;support&lt;/h2&gt;
&lt;p&gt;As always, &lt;span class="caps"&gt;IE&lt;/span&gt; chooses to hit developers with a crowbar.&lt;br&gt;
Instead of&amp;nbsp;extending &lt;code&gt;XMLHttpRequest&lt;/code&gt;, &lt;span class="caps"&gt;IE&lt;/span&gt; 8 introduces another interface&amp;nbsp;named &lt;code&gt;XDomainRequest&lt;/code&gt;, with a similar interface, which supports &lt;span class="caps"&gt;CORS&lt;/span&gt;, but not its full set of features.&lt;br&gt;
Luckily &lt;span class="caps"&gt;IE&lt;/span&gt; 10 fixes this and adds support for &lt;span class="caps"&gt;CORS&lt;/span&gt; within the standard&amp;nbsp;XMLHttpRequest.&lt;/p&gt;
&lt;h2&gt;Further&amp;nbsp;reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en/http_access_control"&gt;&lt;span class="caps"&gt;MDN&lt;/span&gt; page on &lt;span class="caps"&gt;CORS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.w3.org/TR/cors/"&gt;&lt;span class="caps"&gt;W3C&lt;/span&gt; &lt;span class="caps"&gt;CORS&lt;/span&gt;&amp;nbsp;Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.opera.com/articles/view/dom-access-control-using-cross-origin-resource-sharing/"&gt;&lt;span class="caps"&gt;CORS&lt;/span&gt; support in&amp;nbsp;Opera&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx"&gt;XDomainRequest support in &lt;span class="caps"&gt;IE&lt;/span&gt;&amp;nbsp;8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="CORS"/><category term="HTTP"/></entry><entry><title>Notes on RESTful APIs (Updated)</title><link href="https://wooptoo.com/blog/notes-on-restful-apis/" rel="alternate"/><published>2012-04-17T00:00:00+00:00</published><updated>2015-11-25T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2012-04-17:/blog/notes-on-restful-apis/</id><summary type="html">&lt;p&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; and &lt;span class="caps"&gt;REST&lt;/span&gt; are not new, we just misused them for a long time.
A proper RESTful &lt;span class="caps"&gt;API&lt;/span&gt; should consider the following&amp;nbsp;principles.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;Resources are discrete entities &amp;#8212; like the entities from the &lt;a href="http://en.wikipedia.org/wiki/Entity_relationship_model"&gt;Entity-Relationship Model&lt;/a&gt;.
The web is modeled around resources. Everything is a resource, not a web page, not …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; and &lt;span class="caps"&gt;REST&lt;/span&gt; are not new, we just misused them for a long time.
A proper RESTful &lt;span class="caps"&gt;API&lt;/span&gt; should consider the following&amp;nbsp;principles.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;Resources are discrete entities &amp;#8212; like the entities from the &lt;a href="http://en.wikipedia.org/wiki/Entity_relationship_model"&gt;Entity-Relationship Model&lt;/a&gt;.
The web is modeled around resources. Everything is a resource, not a web page, not an application.
Resource names should be intuitive and should be nouns. Avoid Verbs in&amp;nbsp;URLs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/resource&lt;/strong&gt;&lt;br&gt;
  represents a collection of&amp;nbsp;items&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/resource/id&lt;/strong&gt;&lt;br&gt;
  represents an individual&amp;nbsp;item&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/users/1/address&lt;/strong&gt;&lt;br&gt;
  Resources can be nested in order to reflect the relationship
  between&amp;nbsp;them.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Querystring&amp;nbsp;parameters&lt;/h2&gt;
&lt;p&gt;In order to filter, order, limit or offset resource entries we should not create separate sub-resources.
Here&amp;#8217;s where querystring parameters come into&amp;nbsp;play.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/addressbook?orderby=lastname&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/users/1/vehicles?filter=bikes&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Methods&amp;nbsp;(Verbs)&lt;/h2&gt;
&lt;p&gt;The &lt;span class="caps"&gt;HTTP&lt;/span&gt; Verbs operate on resources. Resources can be queried and altered using Verbs. Some operations are&amp;nbsp;idempotent.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  read item / collection of items&lt;br&gt;
  nullipotent – does not modify state, no side&amp;nbsp;effects  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  create new item
  alters state each time it is&amp;nbsp;called  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;PUT&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  update item&lt;br&gt;
  idempotent – calling it multiple times has the same effect as the first&amp;nbsp;time  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;DELETE&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  delete item&lt;br&gt;&amp;nbsp;idempotent  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;PATCH&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  partially update item&lt;br&gt;&amp;nbsp;idempotent&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;OPTIONS&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  display allowed verbs on a resource&lt;br&gt;&amp;nbsp;nullipotent  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How verbs operate on&amp;nbsp;resources&lt;/h2&gt;
&lt;p&gt;Example&amp;nbsp;scenario:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/resource&lt;/strong&gt;&lt;br&gt;
  &lt;span class="caps"&gt;GET&lt;/span&gt; collection of items&lt;br&gt;
  &lt;span class="caps"&gt;POST&lt;/span&gt; create new item&lt;br&gt;
  &lt;span class="caps"&gt;PUT&lt;/span&gt; update collection of items – this is rarely used since it updates the whole collection&lt;br&gt;
  &lt;span class="caps"&gt;DELETE&lt;/span&gt; collection of items&lt;br&gt;
  &lt;span class="caps"&gt;OPTIONS&lt;/span&gt; could display a quick help on how to query the&amp;nbsp;resource  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/resource/id&lt;/strong&gt;&lt;br&gt;
  &lt;span class="caps"&gt;GET&lt;/span&gt; item&lt;br&gt;
  &lt;span class="caps"&gt;POST&lt;/span&gt; - using &lt;span class="caps"&gt;POST&lt;/span&gt; to create an item at an unexisting resource id is a nono&lt;br&gt;
  &lt;span class="caps"&gt;PUT&lt;/span&gt; (sometimes &lt;span class="caps"&gt;POST&lt;/span&gt;) update item&lt;br&gt;
  &lt;span class="caps"&gt;DELETE&lt;/span&gt;&amp;nbsp;item  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Responses&lt;/h2&gt;
&lt;p&gt;Give a proper response after each request. Applications can be much easier to develop if they get meaningful response&amp;nbsp;codes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after &lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  respond 200 &amp;#8216;&lt;span class="caps"&gt;OK&lt;/span&gt;&amp;#8217; (default)&lt;br&gt;
  body with&amp;nbsp;item  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after &lt;span class="caps"&gt;POST&lt;/span&gt; (create)&lt;/strong&gt;&lt;br&gt;
  respond 201 &amp;#8216;Created&amp;#8217; &lt;br&gt;
  location: /resource/id of created item&lt;br&gt;
  body with created&amp;nbsp;item  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after &lt;span class="caps"&gt;PUT&lt;/span&gt;/&lt;span class="caps"&gt;POST&lt;/span&gt; (update)&lt;/strong&gt;&lt;br&gt;
  respond 200 &amp;#8216;&lt;span class="caps"&gt;OK&lt;/span&gt;&amp;#8217;&lt;br&gt;
  body with updated&amp;nbsp;item  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after &lt;span class="caps"&gt;DELETE&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  respond 204 &amp;#8216;No Content&amp;#8217;&lt;br&gt;
  empty&amp;nbsp;body  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after a bad request/unallowed method&lt;/strong&gt;&lt;br&gt;
  respond 400 &amp;#8216;Bad Request&amp;#8217;&lt;br&gt;
  empty&amp;nbsp;body  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;after encountering a problem on the server side, like a failed &lt;span class="caps"&gt;SQL&lt;/span&gt; query&lt;/strong&gt;&lt;br&gt;
  respond 500 &amp;#8216;Internal Server&amp;nbsp;Error&amp;#8217;  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hypermedia&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Self describing &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  Each response should provide links to explore the &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;br&gt;
  Just like &lt;span class="caps"&gt;HTML&lt;/span&gt; connects multiple pages through links, &lt;span class="caps"&gt;API&lt;/span&gt; responses should have links to other resources.&lt;br&gt;
  Ideally an &lt;span class="caps"&gt;API&lt;/span&gt; could be explored entirely without prior knowledge of its resources &amp;#8212; just by knowing its base &lt;span class="caps"&gt;URL&lt;/span&gt;.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Content&amp;nbsp;negotiation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;span class="caps"&gt;HTTP&lt;/span&gt; headers&lt;/strong&gt;&lt;br&gt;
  Accept: application/json,&amp;nbsp;text/plain&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extension in the &lt;span class="caps"&gt;URL&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  Not RESTful &amp;#8212; URLs are not the place for&amp;nbsp;Content-Type&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Versioning&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Version in the &lt;span class="caps"&gt;URL&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;
  /v2/resource&lt;br&gt;
  Create separate resources for a new version of the &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Version media types&lt;/strong&gt;&lt;br&gt;&amp;nbsp;application/vnd.something.v1+json  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom header&lt;/strong&gt;&lt;br&gt;
  X-&lt;span class="caps"&gt;API&lt;/span&gt;-Version:&amp;nbsp;1  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Language&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Using &lt;span class="caps"&gt;HTTP&lt;/span&gt; headers&lt;/strong&gt;&lt;br&gt;
  Accept-Language:&amp;nbsp;en  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cache&lt;/h2&gt;
&lt;p&gt;Some responses can be&amp;nbsp;cached.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Expiration&lt;/strong&gt;&lt;br&gt;
  Cache-Control&lt;br&gt;&amp;nbsp;Expires  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;br&gt;
  Last-Modified&lt;br&gt;&amp;nbsp;ETag  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Authentication&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OAuth&lt;/strong&gt;&lt;br&gt;
  Industry standard. Used by most&amp;nbsp;services.  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Auth&lt;/strong&gt;&lt;br&gt;
  Part of the &lt;span class="caps"&gt;HTTP&lt;/span&gt; standard. Digest auth uses hashing and a&amp;nbsp;nonce.  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cookies&lt;/strong&gt;&lt;br&gt;
  Can be used for login similar to classic web&amp;nbsp;applications.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Also&amp;nbsp;Read&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://martinfowler.com/articles/richardsonMaturityModel.html"&gt;Richardson Maturity&amp;nbsp;Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://barelyenough.org/blog/2007/05/hypermedia-as-the-engine-of-application-state/"&gt;Hypermedia as the Engine of Application&amp;nbsp;State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://barelyenough.org/blog/2008/05/versioning-rest-web-services/"&gt;Versioning &lt;span class="caps"&gt;REST&lt;/span&gt; web&amp;nbsp;services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://timelessrepo.com/haters-gonna-hateoas"&gt;Haters gonna &lt;span class="caps"&gt;HATEOAS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.steveklabnik.com/posts/2011-08-07-some-people-understand-rest-and-http"&gt;Some People Understand &lt;span class="caps"&gt;REST&lt;/span&gt; and &lt;span class="caps"&gt;HTTP&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html"&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; &lt;span class="caps"&gt;RFC&lt;/span&gt;&lt;/a&gt; is your best&amp;nbsp;friend&lt;/li&gt;
&lt;/ul&gt;</content><category term="articles"/><category term="HTTP"/><category term="REST"/><category term="API"/></entry><entry><title>ACTA, SOPA, PIPA, CISPA</title><link href="https://wooptoo.com/blog/acta-sopa-pipa-cispa/" rel="alternate"/><published>2012-04-08T00:00:00+00:00</published><updated>2012-04-08T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2012-04-08:/blog/acta-sopa-pipa-cispa/</id><summary type="html">&lt;p&gt;It seems like the big players of the software and hardware industry are dead set to bring to life an incarnation of one of these laws. Never before in the history of computing has a piece of legislation been pushed forward so ferociously by so many significant&amp;nbsp;companies.&lt;/p&gt;
&lt;p&gt;The core …&lt;/p&gt;</summary><content type="html">&lt;p&gt;It seems like the big players of the software and hardware industry are dead set to bring to life an incarnation of one of these laws. Never before in the history of computing has a piece of legislation been pushed forward so ferociously by so many significant&amp;nbsp;companies.&lt;/p&gt;
&lt;p&gt;The core of the law is mostly the same, and the name doesn&amp;#8217;t even matter anymore since it masquerades under so many acronyms. Of course &lt;span class="caps"&gt;ACTA&lt;/span&gt; is an international agreement, while &lt;span class="caps"&gt;SOPA&lt;/span&gt;, &lt;span class="caps"&gt;PIPA&lt;/span&gt;, &lt;a href="http://intelligence.house.gov/bill/cyber-intelligence-sharing-and-protection-act-2011"&gt;&lt;span class="caps"&gt;CISPA&lt;/span&gt;&lt;/a&gt; are &lt;span class="caps"&gt;US&lt;/span&gt; bills. But once either of these is adopted, slightly modified versions will spread around the globe and will be adopted by other countries as&amp;nbsp;well.&lt;/p&gt;
&lt;p&gt;So it seems that no matter the number of protests, they will keep pushing them forward under different names. A lot of internet users are aware at this moment of one form or another of these bills, thanks to folks like Google and Wikipedia who brought mass awareness, but also thanks to individuals and lobbyists who spread the word. So there is not a lack of&amp;nbsp;awareness.&lt;/p&gt;
&lt;p&gt;Sadly, I don&amp;#8217;t think there is much we can do. We could boycott one company after another, like &lt;a href="http://thebestpageintheuniverse.net/c.cgi?u=pass_sopa"&gt;Maddox proposes&lt;/a&gt; and hit them right in the money. If we manage to do this, it might prove effective for a while and one or two companies might even go out of business. But in the long run the bill will pass and drastic measures will be taken.
ISPs will be turned into copyright cops and &lt;span class="caps"&gt;SOPA&lt;/span&gt; supporting companies into big bad&amp;nbsp;wolves.&lt;/p&gt;
&lt;p&gt;Look, the truth is that we pirate stuff. We pirate games, music, movies, tv shows, books and whatnot. This has got to stop at some point, we can&amp;#8217;t just pirate media indefinitely. If we want to be civil and honest we must pay content creators for their sweat and blood, so they can create more of the things we&amp;nbsp;love.&lt;/p&gt;
&lt;p&gt;The problem is that only a (very) small percent of the money makes it to the content creators. Most of it ends up in &lt;span class="caps"&gt;MPAA&lt;/span&gt; and &lt;span class="caps"&gt;RIAA&lt;/span&gt;&amp;#8217;s pockets. And they provide little value for that large percent which they keep for themselves. They do not provide modern channels of media delivery, they do not provide fair prices and they do not let us manage our own legally bought media by enforcing draconian &lt;span class="caps"&gt;DRM&lt;/span&gt;&amp;nbsp;schemes.&lt;/p&gt;
&lt;p&gt;They are outdated. They hold on to their old ways of doing business and are very afraid of this whole thing called the Internet. If it were for them Blu-ray discs with heavy &lt;span class="caps"&gt;DRM&lt;/span&gt; would be the only way of distributing content. Oh, and the Internet would only be a poster for advertising their brick and mortar&amp;nbsp;shops.&lt;/p&gt;
&lt;p&gt;This has got to change. We need better distribution channels, we want to buy songs and movies from anywhere in the world, delivered instantly and available for consumption on any device we own. If Bittorrent is such a great technology for delivering media, why not use it for delivering your own content to end users?
We need more flexible and fair pricing schemes. If private Bittorrent trackers are such a success, why not make one where you actually buy ratio and then spend it on any content you like? The monetization possibilities are endless, you just need to give your users something so advantageous that it would make pirating look like an unattractive option.
&lt;span class="caps"&gt;DRM&lt;/span&gt; is also a big issue since you don&amp;#8217;t feel like you own the content. They own it and they can erase it from your device at any given moment. We need to own it. Maybe it&amp;#8217;s more of a psychological need than anything&amp;nbsp;else.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m sure big media companies could manage to create any of these outlets for consumption. The only thing keeping them from doing so is themselves. Until these issues are solved we will keep pirating for as long as we can. Because pirating solves these practical issues, even though it does not address the ethical&amp;nbsp;part.&lt;/p&gt;</content><category term="articles"/><category term="Copyright"/><category term="Rants"/></entry><entry><title>PDO vs MySQLi performance comparison</title><link href="https://wooptoo.com/blog/pdo-vs-mysqli-performance-comparison/" rel="alternate"/><published>2012-04-03T00:00:00+00:00</published><updated>2012-04-03T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2012-04-03:/blog/pdo-vs-mysqli-performance-comparison/</id><summary type="html">&lt;p&gt;After reading &lt;a href="http://net.tutsplus.com/tutorials/php/pdo-vs-mysqli-which-should-you-use/"&gt;this article&lt;/a&gt; on Nettuts, I was curious how &lt;span class="caps"&gt;PDO&lt;/span&gt; would compare to MySQLi in a real-world scenario, perfomance-wise. So I created a benchmark to dispell the myths surrounding this dispute. MySQLi is percieved to be the better performer, since it’s the official extension supported by Oracle. In …&lt;/p&gt;</summary><content type="html">&lt;p&gt;After reading &lt;a href="http://net.tutsplus.com/tutorials/php/pdo-vs-mysqli-which-should-you-use/"&gt;this article&lt;/a&gt; on Nettuts, I was curious how &lt;span class="caps"&gt;PDO&lt;/span&gt; would compare to MySQLi in a real-world scenario, perfomance-wise. So I created a benchmark to dispell the myths surrounding this dispute. MySQLi is percieved to be the better performer, since it’s the official extension supported by Oracle. In the current post we&amp;#8217;ll test each database&amp;nbsp;driver.&lt;/p&gt;
&lt;h2&gt;The&amp;nbsp;Setup&lt;/h2&gt;
&lt;p&gt;For any test result to be relevant we&amp;nbsp;need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an isolated test&amp;nbsp;environment&lt;/li&gt;
&lt;li&gt;to run multiple rounds of the&amp;nbsp;test&lt;/li&gt;
&lt;li&gt;to specify the software version&amp;nbsp;used&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The software I used was: &lt;span class="caps"&gt;PHP&lt;/span&gt; 5.3.6, MySQL 5.1.58 on Ubuntu 11.10 with Linux kernel&amp;nbsp;3.0.0&lt;/p&gt;
&lt;p&gt;I tested the performance of the four most important &lt;span class="caps"&gt;SQL&lt;/span&gt;&amp;nbsp;operations: &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;. These four operations are enough to determine the raw performance of each driver.&lt;br&gt;
The test suite consists of running these queries 100000 times (100k) and 1000000 times (1M) for each database&amp;nbsp;driver.&lt;/p&gt;
&lt;p&gt;To make sure that the test is not affected by other applications (such as a sudden cron job), I ran the tests from the &lt;span class="caps"&gt;CLI&lt;/span&gt;, with other daemons shut&amp;nbsp;down.&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;SQL&lt;/span&gt; schema for the database&amp;nbsp;is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Groups&amp;nbsp;table&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;id_group (PK)
title
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Users&amp;nbsp;table&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;id_user (PK)
id_group (FK)
first_name
fam_name
email
pass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not very complicated. I ran the queries only on users table. In both cases I used prepared statements (with named parameters for &lt;span class="caps"&gt;PDO&lt;/span&gt;).&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;Here are the results of the tests. Lower values are better, of&amp;nbsp;course.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;PDO&lt;/span&gt; results for 100k&amp;nbsp;queries&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;query&lt;/th&gt;
&lt;th&gt;time in seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert&lt;/td&gt;
&lt;td&gt;13.079864025116&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;select&lt;/td&gt;
&lt;td&gt;19.150141954422&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update&lt;/td&gt;
&lt;td&gt;16.263291120529&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;14.891561985016&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MySQLi results for 100k&amp;nbsp;queries&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;query&lt;/th&gt;
&lt;th&gt;time in seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert&lt;/td&gt;
&lt;td&gt;19.463269948959&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;select&lt;/td&gt;
&lt;td&gt;27.461564064026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update&lt;/td&gt;
&lt;td&gt;22.705169916153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;21.583913087845&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;span class="caps"&gt;PDO&lt;/span&gt; results for 1M&amp;nbsp;queries&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;query&lt;/th&gt;
&lt;th&gt;time in seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert&lt;/td&gt;
&lt;td&gt;133.66887807846&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;select&lt;/td&gt;
&lt;td&gt;197.49514484406&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update&lt;/td&gt;
&lt;td&gt;164.97567796707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;150.77637290955&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MySQLi results for 1M&amp;nbsp;queries&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;query&lt;/th&gt;
&lt;th&gt;time in seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert&lt;/td&gt;
&lt;td&gt;202.65716481209&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;select&lt;/td&gt;
&lt;td&gt;283.04056501389&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update&lt;/td&gt;
&lt;td&gt;231.89973783493&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;232.20053887367&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The results are quite surprising: &lt;span class="caps"&gt;PDO&lt;/span&gt; performs better than MySQLi in all tested cases. You can find the source of these tests on &lt;a href="https://github.com/wooptoo/blog_snippets"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Apart from better performance, &lt;span class="caps"&gt;PDO&lt;/span&gt; has proper exception handling, a better object-oriented interface and supports binding of entire arrays. Now you have no reason not to use &lt;span class="caps"&gt;PDO&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Erratum&lt;/h2&gt;
&lt;p&gt;This benchmark is flawed, as &lt;span class="caps"&gt;PDO&lt;/span&gt; does escaping/preparing entirely on the client-side, while MySQLi uses the mysql protocol for that (thus network &lt;span class="caps"&gt;IO&lt;/span&gt;).&lt;/p&gt;</content><category term="articles"/><category term="PHP"/><category term="MySQL"/></entry><entry><title>Hello world</title><link href="https://wooptoo.com/blog/hello-world/" rel="alternate"/><published>2012-03-22T00:00:00+00:00</published><updated>2012-03-22T00:00:00+00:00</updated><author><name>Radu</name></author><id>tag:wooptoo.com,2012-03-22:/blog/hello-world/</id><summary type="html">&lt;p&gt;Hello world! It&amp;#8217;s about time I launched this thing.
While not being perfectly polished yet, it is close enough to what I wanted so I decided to give it a&amp;nbsp;go.&lt;/p&gt;
&lt;p&gt;I will post here my findings, notes and scribblings related to development.
Hopefully I&amp;#8217;ll do it on …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello world! It&amp;#8217;s about time I launched this thing.
While not being perfectly polished yet, it is close enough to what I wanted so I decided to give it a&amp;nbsp;go.&lt;/p&gt;
&lt;p&gt;I will post here my findings, notes and scribblings related to development.
Hopefully I&amp;#8217;ll do it on a regular&amp;nbsp;basis.&lt;/p&gt;</content><category term="articles"/></entry></feed>