]*>(.*)<\/h1>/i
13 |
14 | return item.identifier.split("/").last
15 | end
16 |
17 | def description_of(item)
18 | content = item.compiled_content(:snapshot => :body)
19 | html = Nokogiri::HTML(content)
20 | unless (summary = html.css('p.summary')).empty?
21 | return summary.text
22 | else
23 | return html.css("p").first.text
24 | end
25 | end
26 |
27 | def rel_url_for(item)
28 | url_for(item).gsub(%r{^#{Regexp.escape(config[:base_url])}}, "")
29 | end
30 |
31 | class Fixnum
32 | def ordinal_suffix
33 | if (11..13).include?(self % 100)
34 | "th"
35 | else
36 | case self.to_i % 10
37 | when 1; "st"
38 | when 2; "nd"
39 | when 3; "rd"
40 | else "th"
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/content/articles/2011/07/20/joining-dydra.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2011-07-20
3 | kind: article
4 | title: "Joining Dydra"
5 | ---
6 |
7 | With jubilance, I'd like to announce that I have officially joined the
8 | [Dydra team][] as a Developer Evangelist.
9 |
10 | [dydra team]: http://dydra.com/about
11 |
12 | Anyone who knows me professionally knows that I love working in open-source,
13 | whether it’s creating, contributing to or even just talking about software. I
14 | enjoy the fraternity, the absence of egos, and the recognition that a line of
15 | code is worth a thousand words. In the coming months I will be working very
16 | closely with the open-source community, aiming both to demonstrate to
17 | developers the power of our platform, and to harvest ideas and feedback for
18 | the product itself. I’ll also be taking care of the client SDKs, initially
19 | focusing on Python and JavaScript (my fortes).
20 |
21 | Aside from being able to work on such a revolutionary product, it’s a joy to be
22 | on a team with some of the most intelligent and talented people I know—and I’m
23 | being sincere.
24 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | activesupport (3.2.11)
5 | i18n (~> 0.6)
6 | multi_json (~> 1.0)
7 | adsf (1.1.1)
8 | rack (>= 1.0.0)
9 | chunky_png (1.2.7)
10 | colored (1.2)
11 | compass (0.12.2)
12 | chunky_png (~> 1.2)
13 | fssm (>= 0.2.7)
14 | sass (~> 3.1)
15 | cri (2.3.0)
16 | colored (>= 1.2)
17 | fssm (0.2.9)
18 | haml (3.1.7)
19 | htmlentities (4.3.1)
20 | i18n (0.6.1)
21 | json (1.7.6)
22 | kramdown (0.14.2)
23 | mime-types (1.19)
24 | multi_json (1.5.0)
25 | nanoc (3.4.3)
26 | cri (~> 2.2)
27 | nokogiri (1.5.6)
28 | posix-spawn (0.3.6)
29 | pygments.rb (0.3.7)
30 | posix-spawn (~> 0.3.6)
31 | yajl-ruby (~> 1.1.0)
32 | rack (1.5.0)
33 | rake (10.0.3)
34 | rubypants (0.2.0)
35 | sass (3.2.5)
36 | systemu (2.5.2)
37 | yajl-ruby (1.1.0)
38 |
39 | PLATFORMS
40 | ruby
41 |
42 | DEPENDENCIES
43 | activesupport
44 | adsf
45 | compass
46 | haml
47 | htmlentities
48 | i18n
49 | json
50 | kramdown
51 | mime-types
52 | nanoc
53 | nokogiri
54 | pygments.rb
55 | rack
56 | rake
57 | rubypants
58 | systemu
59 |
--------------------------------------------------------------------------------
/content/articles/2013/01/21/paleo.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-01-21
3 | kind: article
4 | title: "What Paleo Means (To Me)"
5 | ---
6 |
7 | I want to clarify what I mean when I say ["I eat Paleo"](http://zacharyvoase.com/2013/01/21/diet/#paleo).
8 |
9 | 'Paleo' is a framework for generating *falsifiable* hypotheses about nutrition,
10 | based on the principle that the foods human beings adapted to eat over millions
11 | of years of evolution are definitely safe, and that for new additions to our
12 | diet, there exists a burden of proof to demonstrate safety.
13 |
14 | It is strongly desirable for these hypotheses to be tested through randomized
15 | intervention studies (rather than epidemiological observations); *in vitro*
16 | results should be taken with a pinch of salt but may still provide useful
17 | suggestions for further study.
18 |
19 | It is recognized that safety of a food is not defined solely by short-term
20 | toxicity or lack thereof, but with respect to the entire organism: chronic
21 | effects on the serum and hormonal responses (e.g. glucose and insulin), organs
22 | (e.g. non-alcoholic fatty liver disease), systemic inflammation, the immune
23 | system, gut flora, interactions with other components of the diet, et cetera.
24 | We realize that practical studies can't be perfect, but we can interpret the
25 | evidence we get, and individually decide whether to take the risk.
26 |
27 | 'Paleo' completely defies the typical 'fad diet' label—because it's just
28 | science-based nutrition.
29 |
--------------------------------------------------------------------------------
/content/articles/2013/03/06/qr-codify.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-03-06
3 | kind: article
4 | title: "QR Codify: The Most Useful Snippet I've Ever Written"
5 | ---
6 |
7 | **tl;dr**: I wrote a Mac OS X Service which allows you to display the
8 | currently-selected text as an on-screen QR code. Buy it
9 | [here](http://pul.ly/b/59512) for only £1.07.
10 | {: .summary}
11 |
12 | It really annoys me when there's some text on my computer that I need to be
13 | available immediately on my Android phone. Especially when that text is a phone
14 | number or a URL. Fortunately, OS X now has
15 | [Services](https://en.wikipedia.org/wiki/Services_menu#Mac_OS_X), which are
16 | scriptable actions that can be performed on GUI elements by right-clicking.
17 |
18 | I opened up Automator and created the following:
19 |
20 | 
21 |
22 |
23 | Here's a [Gist](https://gist.github.com/zacharyvoase/5102470) for those who
24 | want the raw source code.
25 |
26 |
27 | The service simply reads the selected text from stdin, generates a [Google
28 | Image Chart][] URL for a QR code with that text, downloads it to a temporary
29 | local file and displays it via QuickLook (using the `qlmanage` CLI). It looks
30 | something like this:
31 |
32 | [google image chart]: https://developers.google.com/chart/image/docs/making_charts?hl=en
33 |
34 | 
35 |
36 | 
37 |
38 | When I'm done with it, I hit the space bar and it goes away.
39 |
40 | If this is something that'd be useful to you, you can grab it
41 | [here](http://pul.ly/b/59512).
42 |
--------------------------------------------------------------------------------
/content/media/sass/_reset.scss:
--------------------------------------------------------------------------------
1 | /* (c) 2009, Yahoo! Inc. All rights reserved.
2 | Code licensed under the BSD License:
3 | http://developer.yahoo.net/yui/license.txt
4 | version: 3.0.0
5 | build: 1549*/
6 | html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}p,fieldset,table,pre{margin-bottom:1em;}input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
--------------------------------------------------------------------------------
/content/articles/2009/07/03/http-post-put-diff.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-07-03
4 | title: "The Difference Between POST and PUT—Get it Right!"
5 | ---
6 |
7 | I’ve been getting pretty annoyed lately by a popular misconception by web
8 | developers that a POST is used to create a resource, and a PUT is used to
9 | update/change one.
10 |
11 | If you take a look at page 55 of [RFC 2616](http://www.ietf.org/rfc/rfc2616.txt)
12 | (“Hypertext Transfer Protocol -- HTTP/1.1”), Section 9.6 (“PUT”), you’ll see
13 | what PUT is actually for:
14 |
15 | > The PUT method requests that the enclosed entity be stored under the
16 | > supplied Request-URI.
17 |
18 | There’s also a handy paragraph to explain the difference between POST and PUT:
19 |
20 | > The fundamental difference between the POST and PUT requests is reflected
21 | > in the different meaning of the Request-URI. The URI in a POST request
22 | > identifies the resource that will handle the enclosed entity. That
23 | > resource might be a data-accepting process, a gateway to some other
24 | > protocol, or a separate entity that accepts annotations. In contrast, the
25 | > URI in a PUT request identifies the entity enclosed with the request --
26 | > the user agent knows what URI is intended and the server MUST NOT attempt
27 | > to apply the request to some other resource.
28 |
29 | It doesn’t mention anything about the difference between updating/creating,
30 | because that’s not what it’s about. It’s about the difference between this:
31 |
32 | #!python
33 | obj.set_attribute(value) # A POST request.
34 |
35 | And this:
36 |
37 | #!python
38 | obj.attribute = value # A PUT request.
39 |
40 | So please, stop the spread of this popular misconception. Read your RFCs.
41 |
--------------------------------------------------------------------------------
/content/articles/2012/09/07/comments.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2012-09-07
3 | kind: article
4 | title: "The Problem With Comments"
5 | ---
6 |
7 | As much as we appreciate well-written comments, most of us would admit to
8 | finding it difficult to keep comments up-to-date. Why is this? Surely, when
9 | you're editing code, the comments are *right there* and easy to update along
10 | with the code? Yet still we forget, overlooking a comment when changing the
11 | fundamental behavior of semantics of the code to which it relates.
12 |
13 | My argument is that this is actually a UX failure on the part of our text
14 | editors; to see why, here's a real-world example of a contextual message which
15 | doesn't change the substance of the object to which it relates:
16 |
17 | 
18 |
19 | The Post-it note is more than just an optional message—it's an admonition, a
20 | sign to whoever is using the object that there is some critical piece of
21 | information that should be considered before proceeding. In fact, it's
22 | deliberately colored in such a way as to *clash* with almost any background
23 | you'd put it against.
24 |
25 | Compare this with how comments are typically displayed:
26 |
27 | 
28 |
29 | The comment, as a syntactic structure, is supposed to be a piece of
30 | human-readable information which is ignored by the compiler. But why, then, do
31 | our editors display comments in such a way as to be *ignored by the
32 | programmer?* Imagine if comments were displayed like
33 | this instead:
34 |
35 | 
36 |
37 | I think there would be a lot fewer meaningless, out-of-date and unhelpful
38 | comments.
39 |
--------------------------------------------------------------------------------
/content/rss.rdf.erb:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | <%= sorted_articles.last[:created_at].to_s %>
11 | Zachary Voase
12 | Public Domain
13 | The Blog of Zachary Voase, brought to you in glorious HyperText.
14 |
15 |
16 | <% sorted_articles.each do |article| %>
17 |
18 | <% end %>
19 |
20 |
21 | http://blog.zacharyvoase.com/
22 | <%= sorted_articles.last[:created_at].to_s %>
23 | 2
24 | daily
25 | Zack’s Blog
26 |
27 | <% sorted_articles.each do |article| %>
28 |
29 | <%= article[:created_at].to_s %>
30 | <%=h url_for(article) %>
31 | <%=h title_of(article) %>
32 | <%=h article.compiled_content(:snapshot => :pre) %>
33 |
34 | <% end %>
35 |
36 |
--------------------------------------------------------------------------------
/content/articles/2009/05/17/django-staff-databrowse.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-05-17
4 | title: "Django Tip: Staff-only Access to Databrowse"
5 | ---
6 |
7 | Databrowse has to be one of the most underappreciated Django apps. It’s been
8 | included with Django since 1.0, and it’s really simple to use; just register
9 | some models to a site, point to that site from your URLconf and you get a
10 | fully-featured data browser for free. You can read the databrowse docs
11 | [here](http://docs.djangoproject.com/en/dev/ref/contrib/databrowse/), but
12 | there’s something they don’t mention which I think is really nifty.
13 |
14 | Down at the bottom of that page, it recommends using the `login_required()`
15 | decorator to restrict access to registered users, like so:
16 |
17 | #!python
18 | from django.conf.urls.defaults import *
19 | from django.contrib import databrowse
20 | from django.contrib.auth.decorators import login_required
21 |
22 | urlpatterns = patterns('',
23 | (r'^databrowse/(.*)$', login_required(databrowse.site.root)),
24 | (r'^login/$', 'django.contrib.auth.views.login'),
25 | )
26 |
27 | But if you want to restrict access to staff (i.e. users who can access the
28 | admin), you’ll have to use another (undocumented) decorator instead.
29 |
30 | #!python
31 | from django.conf.urls.defaults import *
32 | from django.contrib import databrowse
33 | from django.contrib.admin.views.decorators import staff_member_required
34 |
35 | urlpatterns = patterns('',
36 | (r'^databrowse/(.*)$', staff_member_required(databrowse.site.root)),
37 | )
38 |
39 | Only users with the `is_staff` flag will be able to access databrowse now, and the login form presented is essentially that of the Django admin. Note that `django.contrib.admin` must be in your `INSTALLED_APPS` for this to work.
40 |
--------------------------------------------------------------------------------
/content/media/sass/_border-radius.scss:
--------------------------------------------------------------------------------
1 | // Border-radius helps to make it easier to round the corners of your HTML elements
2 | // Sample Usage:
3 | // #container
4 | // +border-radius("5px")
5 |
6 | // All corners
7 | @mixin border-radius($radius) {
8 | border-radius: $radius;
9 | -moz-border-radius: $radius;
10 | -webkit-border-radius: $radius; }
11 |
12 | // Top
13 | @mixin border-radius-top($radius) {
14 | @include border-radius-top-left($radius);
15 | @include border-radius-top-right($radius); }
16 |
17 | // Right
18 | @mixin border-radius-right($radius) {
19 | @include border-radius-top-right($radius);
20 | @include border-radius-bottom-right($radius); }
21 |
22 | // Bottom
23 | @mixin border-radius-bottom($radius) {
24 | @include border-radius-bottom-right($radius);
25 | @include border-radius-bottom-left($radius); }
26 |
27 | // Left
28 | @mixin border-radius-left($radius) {
29 | @include border-radius-top-left($radius);
30 | @include border-radius-bottom-left($radius); }
31 |
32 | // Let's setup the rules so we don't have to repeat ourselves
33 | // These are mixins for this mixin and are re-used above
34 | @mixin border-radius-top-right($radius) {
35 | border-top-right-radius: $radius;
36 | -moz-border-radius-topright: $radius;
37 | -webkit-border-top-right-radius: $radius; }
38 |
39 | @mixin border-radius-bottom-right($radius) {
40 | border-bottom-right-radius: $radius;
41 | -moz-border-radius-bottomright: $radius;
42 | -webkit-border-bottom-right-radius: $radius; }
43 |
44 | @mixin border-radius-bottom-left($radius) {
45 | border-bottom-left-radius: $radius;
46 | -moz-border-radius-bottomleft: $radius;
47 | -webkit-border-bottom-left-radius: $radius; }
48 |
49 | @mixin border-radius-top-left($radius) {
50 | border-top-left-radius: $radius;
51 | -moz-border-radius-topleft: $radius;
52 | -webkit-border-top-left-radius: $radius; }
53 |
--------------------------------------------------------------------------------
/Rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler"
4 | Bundler.setup
5 |
6 | preprocess do
7 | system('rsync -a static/ output') # Copy static files.
8 | end
9 |
10 |
11 | # ---- Compilation ----
12 |
13 | # Don't modify Sass partials.
14 | compile '/media/sass/_*/' do
15 | end
16 |
17 | # Render Sass files with Compass.
18 | require 'compass'
19 | compile '/media/sass/*/' do
20 | load_paths = ['content/media/sass', 'output/media/css']
21 | load_paths += Compass.sass_engine_options[:load_paths]
22 | filter :sass, :syntax => :scss, :load_paths => load_paths, :style => :compressed
23 | end
24 |
25 | # Don't modify any other files in content/media/.
26 | compile '/media/*/' do
27 | end
28 |
29 | compile '/rss/' do
30 | filter :erb
31 | end
32 |
33 | compile '*' do
34 | case item[:extension]
35 | when 'md'
36 | filter :erb
37 | filter :kramdown
38 | filter :rubypants
39 | filter :fix_entities
40 | filter :colorize_syntax, :default_colorizer => :pygmentsrb, :syntax => :xhtml
41 | snapshot :body
42 | layout '/default/'
43 | when 'erb'
44 | filter :erb
45 | layout '/default/'
46 | when 'haml'
47 | filter :haml, :attr_wrapper => '"'
48 | layout '/default/'
49 | end
50 | end
51 |
52 |
53 | # ---- Routing ----
54 |
55 | # Don't route Sass partials.
56 | route '/media/sass/_*/' do
57 | end
58 |
59 | # content/media/sass/file.sass => /media/css/file.css
60 | route '/media/sass/*/' do
61 | item.identifier.gsub(%r{^/media/sass/}, "/media/css/").chop + ".css"
62 | end
63 |
64 | # content/media/file.ext => /file.ext
65 | route '/media/*/' do
66 | item.identifier.chop + '.' + item[:extension]
67 | end
68 |
69 | route '/articles/*/' do
70 | if item.binary?
71 | item.identifier.gsub(%r{^/articles/}, "/").chop + "." + item[:extension]
72 | else
73 | item.identifier.gsub(%r{^/articles/}, "/") + "index.html"
74 | end
75 | end
76 |
77 | route '/favicon/' do
78 | '/favicon.ico'
79 | end
80 |
81 | route '/not-found/' do
82 | "/not-found.html"
83 | end
84 |
85 | route '/rss/' do
86 | "/rss.rdf"
87 | end
88 |
89 | # content/file.md => /file/index.html
90 | route '*' do
91 | item.identifier + "index.html"
92 | end
93 |
94 | # ---- Layout ----
95 |
96 | layout '*', :haml, :attr_wrapper => '"'
97 |
--------------------------------------------------------------------------------
/content/articles/2009/07/23/django-orm-trick.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-07-23
4 | title: "Django ORM: Neat (undocumented) trick"
5 | ---
6 |
7 |
8 | UPDATE:
9 | I’m actually working on a
10 | ticket and patch
11 | so that this feature is both documented and regression-tested for future
12 | releases of Django (starting with 1.1).
13 |
14 |
15 |
16 | UPDATE 2:
17 | My patch was accepted and
18 | included as part
19 | of Django 1.1.
20 |
21 |
22 | I just found out something pretty damn cool about Django’s ORM which, as it
23 | happens, is completely undocumented (as far as I can tell). Let’s assume your
24 | model definition is something like:
25 |
26 | #!python
27 | from django.db import models
28 |
29 | class MyModel(models.Model):
30 |
31 | count = models.IntegerField(default=0)
32 |
33 | The following is completely valid, and actually eliminates a lot of the race
34 | conditions that have plagued the Django ORM in the past:
35 |
36 | #!pycon
37 | >>> from django.db.models import F
38 | >>> from myapp.models import MyModel
39 | >>> obj = MyModel(count=4)
40 | >>> obj.save()
41 | >>> obj.count
42 | 4
43 | >>> obj.count = F('count') + 3
44 | >>> obj.save()
45 | >>> obj = MyModel.objects.get(pk=obj.pk) # We need to reload the object.
46 | >>> obj.count
47 | 7
48 |
49 | Typically you’d do something like `obj.count += 3`, but that sets the attribute
50 | to an absolute value, which can be the cause of many a race condition wherein
51 | two threads/processes are editing the same record at a time; the `obj.save()`
52 | would cause one thread to clobber another’s changes. Using `F()`, the SQL
53 | expression instead looks like:
54 |
55 | #!sql
56 | UPDATE "myapp_mymodel" SET "count" = "myapp_mymodel"."count" + 3 WHERE "myapp_mymodel"."id" = 1;
57 |
58 | Here, the ACIDity of the RDBMS ensures that parallel attempts to increment the
59 | count occur without issue.
60 |
61 | This behaviour’s undocumented status means it could break at any minute, and
62 | reloading the object is necessary because otherwise `obj.count` ends up being an
63 | instance of `django.db.models.expressions.ExpressionNode`, even after the object
64 | is saved.
65 |
--------------------------------------------------------------------------------
/content/articles/2012/05/21/pull-requests.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2012-05-21
3 | kind: article
4 | title: "Pinned Pull Requests"
5 | ---
6 |
7 | I was taking a look at the [Crossroads I/O](http://www.crossroads.io/) project
8 | today, and stumbled across this in the [FAQ](http://www.crossroads.io/faq):
9 |
10 | > **Why do you not accept pull requests?**
11 | >
12 | > Pull requests can change while being reviewed. This makes it impossible to
13 | > ensure that the code being merged is the same code that has been reviewed and
14 | > discussed, which compromises integrity of the codebase.
15 | >
16 | > Pull requests are meant for delegation of work to sub-maintainers and require
17 | > an established web of trust. We may consider moving to this model in future.
18 | >
19 | > The method for contributing that we are currently using is posting patches to
20 | > the mailing list, as explained
21 | > [here](http://www.crossroads.io/dev:start#toc3).
22 |
23 | What this refers to is the fact that GitHub Pull Requests are dynamic; if you
24 | nominate a branch for a pull request, and then push to that branch, the pull
25 | request is updated with the new commits. I’m no gitmeister, but I’m aware that
26 | git doesn’t merge histories, it merges trees (albeit with an awareness of
27 | history). A branch, in git, is simply a pointer to a tree which is updated as
28 | you make subsequent commits. Therefore, I figured, it should be possible to
29 | create a pull request which references a *specific* commit and doesn’t change
30 | with the branch.
31 |
32 | And it is.
33 |
34 | I create a branch with some commits and push it to GitHub:
35 |
36 | 
37 |
38 | I submit my pull request using the commit identifier instead of the branch
39 | name. Helpfully, if you just enter the branch name into the input box, GitHub
40 | will display the most recent commit identifier, which you can just
41 | copy-and-paste into the box:
42 |
43 | 
44 |
45 | I go back to my terminal and add some more commits to the branch:
46 |
47 | 
48 |
49 | Note that the pull request hasn’t changed. It was pinned to that commit, rather
50 | than following the branch:
51 |
52 | 
53 |
54 | The only issue from GitHub’s end is that it still displays the message about
55 | being able to add commits to the pull request (which I’m pretty sure I can’t).
56 |
57 | I wonder if this will change the attitudes of the Crossroads I/O maintainers to
58 | pull requests.
59 |
--------------------------------------------------------------------------------
/content/articles/2013/03/01/big-o.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-03-01
3 | kind: article
4 | title: "Big-O"
5 | ---
6 |
7 | As a self-taught programmer, I've happened upon less formal education in the
8 | art of data structures and algorithms than those who attended university. Most
9 | of what I've learned came through long nights spent on Wikipedia, all of it
10 | directed towards solving problems I was experiencing in a company or project I
11 | was working on. But sometimes this just-in-time learning approach doesn't work
12 | so well. Outside of reading a large textbook, or taking a course on
13 | [Coursera][], it can be hard to discover new knowledge if you aren't in an
14 | academic environment. So that's why I started the [London Big-O
15 | Meetup](http://www.meetup.com/big-o-london/).
16 |
17 | [coursera]: http://www.coursera.org/
18 |
19 | The aim is simple: to provide a forum where working and studying programmers in
20 | the city can share their favorite data structures and algorithms in a
21 | language-agnostic setting, and thus raise the knowledge level of the entire
22 | group in a way that wouldn't be possible otherwise. We want to hear real
23 | stories of how people apply 'academic' DS&A theory in their daily professional
24 | lives, or cutting-edge research that could provide faster or more accurate ways
25 | of doing difficult things with computers. Anything that can be run on a
26 | computer is up for discussion. The only requirement is that it be accessible to
27 | an audience of people who code on a daily basis.
28 |
29 | The first meetup ran in December 2012, and went pretty well. I gave an intro to
30 | the goals of the group, and a run-through of [Diffie-Hellman Key Exchange][].
31 | [Tiberiu](http://underrated.org/) spoke about discrete event simulation for
32 | deterministic hardware verification (including how to simulate parallelism),
33 | and we also had a spur-of-the-moment speaker jump in to add some thoughts on
34 | the [frame problem][].
35 |
36 | [diffie-hellman key exchange]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
37 | [frame problem]: https://en.wikipedia.org/wiki/Frame_problem
38 |
39 | I'm organizing the second meetup for Thursday the 7th of March, so we can get
40 | it done before I fly off to [PyCon](https://us.pycon.org/2013/) on the 11th.
41 | I've already reached out to speakers and an event space, and I'm waiting on
42 | confirmation, but if you're in London and want to broaden the horizons of your
43 | mind, you should [come along](http://www.meetup.com/big-o-london/events/106036312/).
44 |
--------------------------------------------------------------------------------
/content/articles/2009/06/20/bureaucratic-breakdown.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-06-20
4 | title: "Bureaucratic Breakdown"
5 | ---
6 |
7 | My math teacher, who I admire greatly, showed me a small document he’d written a
8 | while ago detailing the first signs of what he called ‘executive complexity’.
9 | It’s a list of 8 recognizable symptoms which show that your organization (be it
10 | a business, a school or a project) could be dealing with what he referred to as
11 | a ‘Bureaucratic Breakdown’:
12 |
13 | The Invisible Decision
14 | : No-one knows how or where decisions are made (there is no transparency).
15 |
16 | Unfinished Business
17 | : Too many tasks are started but very few carried through to the end.
18 |
19 | Co-ordination Paralysis
20 | : Nothing can be done without checking with a host of interconnected units.
21 |
22 | Nothing New
23 | : There are no radical ideas, inventions or lateral thinking—a general lack of
24 | initiative.
25 |
26 | Pseudo-problems
27 | : Minor issues become magnified out of all proportion.
28 |
29 | Embattled Centre
30 | : The centre battles for consistency and control against local/regional units.
31 |
32 | Negative deadlines
33 | : The deadlines for work become more important than the quality of the work
34 | being done.
35 |
36 | In-tray Domination
37 | : Individuals react to inputs—i.e. whatever gets put in their in-tray—as
38 | opposed to using their own initiative (the difference between being reactive
39 | and proactive).
40 |
41 | Overall, the culture becomes less responsive to the environment and less capable
42 | of changing or adapting. It appears impressive but is out of touch, and often
43 | out of control. These symptoms can accompany a general movement towards the
44 | expansion, consistency, conformity, comparability, control and centralization of
45 | the organization.
46 |
47 | I found it very interesting to read this (I’m reproducing it here almost
48 | word-for-word). I’m lucky enough to be working with a
49 | [fantastic group of people](http://fantastichq.com), and we’re very open and
50 | honest with each other. For me, reading my teacher’s thoughts on this sort of
51 | ‘corporate culture’ gave me something of an insight into the issues which
52 | individuals in large organizations have to deal with on a daily basis. The thing
53 | that struck me most was that all of these phenomena are emergent—they seem to
54 | arise out of a collective will to obey (or lack of will to disobey) the system
55 | rather than the behavior and decisions of any single individual.
56 |
--------------------------------------------------------------------------------
/content/articles/2012/08/31/slowness.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2012-08-31
3 | kind: article
4 | title: "Slowness is a Side Effect"
5 | ---
6 |
7 | Functional programmers use the term ‘side effect’ to refer to the impact a
8 | function has on the world around it. Side effects are implicit—they are not
9 | represented in the input arguments or output types of the function. It’s
10 | considered good functional style to write programs without side effects, or at
11 | least with a minimal and predictable number. In imperative programming, we
12 | still try to keep side effects restricted to recognizable places: Ruby uses a
13 | `!` suffix on ‘dangerous’ methods, Python’s core string and number types are
14 | immutable, and the popularity of jQuery even demonstrates the appreciation for
15 | fluent and functional interfaces in the JavaScript community.
16 |
17 | However, it’s easy to fall into the trap of believing that the footprint of a
18 | function begins and ends with its prototype. The question of whether a function
19 | has side effects a simple one: **is the world you are in when the function
20 | terminates equal to the one you were in when it started?** At the level of
21 | fast, atomic operations, like `1 + 1`, this is obviously true. But as a
22 | function gets slower, no matter whether it acts on its environment or not,
23 | **the world you are in when it terminates will have changed significantly since
24 | it started.**
25 |
26 | What does this mean to those of us who don’t care so much about functional
27 | purity? Well, consider the case of defining a capital-I *Interface*, and the
28 | concomitant claim that you can swap out the implementation without changing any
29 | properties of the system. This is often used as an argument for incurring
30 | technical debt, with the promise that the details of an API, database or
31 | service can be ‘abstracted away’.
32 |
33 | The fallacy—or false hope—in this argument is exposed when one considers that
34 | there’s more to an interface than its component function signatures. The
35 | performance and implementation details of various methods have a direct impact
36 | on the experience of their use, and this will, in turn, shape the software
37 | written against them. When you design an interface, it designs back.
38 |
39 | My sole recommendation—and it’s not a panacea—is to take a holistic approach to
40 | implementation and interface. Recognize that performance characteristics *do*
41 | have an effect on how you code. [Write opinionated software][opinionated],
42 | because portable, generic software is usually slow, buggy software. Finally,
43 | take pride in designing robust interfaces and schemas that stand the test of
44 | time.
45 |
46 | [opinionated]: http://gettingreal.37signals.com/ch04_Make_Opinionated_Software.php
47 |
--------------------------------------------------------------------------------
/content/articles/2009/09/11/path.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-09-11
4 | title: "Easy Path Manipulation in Python"
5 | ---
6 |
7 | One of the things that really gets on my nerves when working with file paths in
8 | Python is the aesthetically ugly use of `os.path` functions to perform even
9 | simple manipulations of path names.
10 |
11 | Luckily, Jason Orendorff’s `path.py` module provides a very simple wrapper over
12 | these path operations. It contains a single `path` class which you can use like
13 | this:
14 |
15 | #!pycon
16 | >>> from path import path
17 | >>> homedir = path('/Users/zacharyvoase/')
18 | >>> homedir
19 | path('/Users/zacharyvoase/')
20 |
21 | You can manipulate these paths easily, using methods and operator overrides.
22 | `path` also has a lot of the functions from `shutil`, `glob`, `os.path`, et
23 | cetera defined as methods.
24 |
25 | In fact, this module was the inspiration for
26 | [URLObject](http://bitbucket.org/zacharyvoase/urlobject/)’s use of operator
27 | overrides for URL manipulation.
28 |
29 | ### Examples
30 |
31 | #### Path Manipulation
32 |
33 | #!pycon
34 | >>> filename = homedir / 'file.txt'
35 | >>> filename
36 | path('/Users/zacharyvoase/file.txt')
37 | >>> filename.splitext()
38 | (path('/Users/zacharyvoase/file'), '.txt')
39 |
40 |
41 | #### Filesystem Operations
42 |
43 | #!pycon
44 | >>> filename.exists()
45 | False
46 | >>> filename.touch()
47 | >>> filename.exists()
48 | True
49 |
50 | >>> homedir.isdir()
51 | True
52 | >>> homedir.listdir()
53 | [path('/Users/zacharyvoase/.bash_history'), ...]
54 |
55 |
56 |
57 | #### Globs
58 |
59 | #!pycon
60 | >>> homedir.files('*.txt')
61 | [path('/Users/zacharyvoase/file.txt')]
62 | >>> homedir.dirs('Desk*')
63 | [path('/Users/zacharyvoase/Desktop')]
64 |
65 | ### Installing `path`
66 |
67 | There is a slight problem in that Jason’s website, where the original version of
68 | the module was hosted, has disappeared. Luckily, the `path` module lives on in
69 | the [Paver](http://www.blueskyonmars.com/projects/paver/) project, which can be
70 | installed via `easy_install Paver`. You can then use the class with `from
71 | paver.path import path`.
72 |
73 | ### How I Use `path`
74 |
75 | I like to put `path` to use in my Django settings module. Have a look at this
76 | for an example:
77 |
78 | #!python
79 | from paver.path import path
80 |
81 | PROJECT_ROOT = path(__file__).abspath().dirname()
82 |
83 | MEDIA_ROOT = PROJECT_ROOT / 'media'
84 | UPLOAD_ROOT = PROJECT_ROOT / 'uploads'
85 |
86 | TEMPLATE_DIRS = (
87 | PROJECT_ROOT / 'templates',
88 | )
89 |
90 | Elsewhere in my Django project, I can then use those paths without having to
91 | import `os.path` and friends.
92 |
--------------------------------------------------------------------------------
/content/articles/2009/11/02/bioseq.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-11-02
4 | title: "BioSeq"
5 | ---
6 |
7 | A key problem in the field of Bioinformatics is referring to biological
8 | sequences. A single sequence may be known by many names across many databases on
9 | the web, with distinct ‘IDs’ on different sites and little to tie them together.
10 |
11 | This is where BioSeq comes in. BioSeq is a technology I’m currently working on
12 | to leverage Content-Addressable Storage (CAS), powered by
13 | [Bitcache](http://bitcache.org/), to store and refer to biological sequence
14 | data. Sequences are identified with a URI like the following:
15 |
16 | bioseq:a21268b77c91c67973efa8289cc42a62772d8c33
17 |
18 | The scheme-specific part of a `bioseq:` URI is that sequence’s unique
19 | identifier, generated by applying a cryptographic hash algorithm (currently
20 | SHA1) to the sequence. The sequence itself can then be retrieved by querying a
21 | Bitcache server for the hash, usually via a simple HTTP GET to
22 | `http://bitcache.example.com/-identifier-`.
23 |
24 | The use of cryptographic hashes as identifiers causes the URI to have several
25 | useful properties:
26 |
27 | * A cryptographic hash algorithm will always produce a fixed-length output
28 | regardless of the size of the input, so URIs have a fixed-length.
29 |
30 | * The hashes of two identical sequences will always be identical, and the
31 | hashes of two different sequences have such a small probability of collision
32 | that we may safely ignore this risk. This means `bioseq:` URIs are truly
33 | universal, since a given URI will always refer unambiguously to one
34 | biological sequence.
35 |
36 | * This also gives the property that `bioseq:` URIs are independent of the
37 | server on which the sequences themselves are stored; it is up to the client
38 | which wants to fetch the sequence to resolve the URI into a URL. This can
39 | actually be done with URI prefixes: setting `bioseq:` as a prefix pointing
40 | to a Bitcache server (in an RDF document, for example) will make URI-to-URL
41 | resolution occur automatically.
42 |
43 | * Since two identical sequences will have the same address, redundancy (on the
44 | sequence level) is eradicated entirely.
45 |
46 | * Because changing a sequence will cause its hash to change, the
47 | fetching/updating of sequences will also verify data integrity.
48 |
49 | * CAS makes incremental server-to-server replication very easy, since the
50 | dataset is append-only. See Chris Anderson’s
51 | [blog post](http://jchrisa.net/drl/_design/sofa/_show/post/CouchDB-Implements-a-Fundamental-Algorithm)
52 | on CouchDB replication to see how replication could be implemented on top of
53 | Bitcache.
54 |
55 | The whole concept is still in the planning stages, but the technology to realize
56 | it is all present. In a following blog post, I’ll describe how BioSeq will
57 | integrate with other semantic web technologies, allowing for the creation of a
58 | scalable, distributed infrastructure for storing and querying biological
59 | sequence data.
60 |
--------------------------------------------------------------------------------
/content/articles/2013/02/21/email.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-02-21
3 | kind: article
4 | title: "Improving Email, Realistically"
5 | ---
6 |
7 | Every day it seems there's more hype about a new startup that's setting out to
8 | destrominate email as we know it. The thing is, email has been around longer
9 | than even the Web, and I reckon that it'll still be around in its current form
10 | for a long time to come. Rather than trying to revolutionize the way we
11 | communicate, what are some small, incremental, backwards-compatible
12 | improvements we can make to the protocol and the UX of email clients?
13 |
14 | A real problem for me is dealing with a backlog of personal emails over brief
15 | periods of time when I'm busy, traveling, in a different timezone or just plain
16 | tired. So what I really need is not a new interface to my email, but rather
17 | a way of *managing expectations* for people who send me emails. This
18 | expectation management could have the effect of preventing someone from sending
19 | the email in the first place, or perhaps reaching out to me through a different
20 | medium (like telephone, Skype or IM).
21 |
22 | One way of solving this problem would be to build a client which works in
23 | exactly the same way as modern ones, but recognizes patterns in my behavior and
24 | publishes a metric based on info like:
25 |
26 | * my calendar;
27 | * my current timezone;
28 | * the average time I take to respond to other emails;
29 | * the length of the email I need to respond to; etc.
30 |
31 | When someone is drafting an email to me, they should be able to see an
32 | *estimated time to response* live-updating as they write, based on information
33 | published by my email client:
34 |
35 | 
36 |
37 | The 'information' published would effectively have to be some kind of function
38 | over the length of the e-mail, its sender, my calendar, plus some indication of
39 | my present 'business' (including whether or not I'm likely to be asleep). So
40 | the obvious strategy is to build an API that works at Layer 7 (either DNS
41 | TXT/SRV records, or just HTTP [à la Gravatar][gravatar]) where you can post a
42 | bunch of information in a JSON object and receive a best-guess estimate of the
43 | time I would take to respond.
44 |
45 | [gravatar]: http://en.gravatar.com/site/implement/
46 |
47 | Such a system has several benefits:
48 |
49 | * It's 100% non-intrusive. You don't need to change your current email client,
50 | or join a waiting list behind 56,000 other people to use it.
51 | * It's decentralized: you can point to your 'response expectation management
52 | provider' from your DNS records, enabling a competitive market of such
53 | providers; some might be free and advert/VC-supported, others would be
54 | 'profitable and proud', others still would be open-source.
55 | * It can be private, if you want it to be: just run your own server. Obviously
56 | there'd be a benefit to using Google's provider—they're really good at
57 | predicting this sort of stuff, and it'd integrate seamlessly with Google
58 | Apps.
59 |
60 | I just had this idea while drinking my espresso this morning, but you can
61 | discuss it further on [Hacker News](https://news.ycombinator.com/item?id=5259032).
62 |
--------------------------------------------------------------------------------
/content/articles/2012/03/31/reification.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2012-03-31
3 | kind: article
4 | title: "The Dangers of Reification"
5 | ---
6 |
7 | In a 1987 interview for Woman’s Own magazine, Margaret Thatcher famously
8 | quipped that “there is no such thing as society”. This quote caused a lot of
9 | controversy, such that Thatcher addressed it in her 1993 autobiography, *The
10 | Downing Street Years*:
11 |
12 | > They never quoted the rest. I went on to say: There are individual men and
13 | > women, and there are families. And no government can do anything except
14 | > through people, and people must look to themselves first. It’s our duty to
15 | > look after ourselves and then to look after our neighbour. My meaning, clear
16 | > at the time but subsequently distorted beyond recognition, was that society
17 | > was not an abstraction, separate from the men and women who composed it, but
18 | > a living structure of individuals, families, neighbours and voluntary
19 | > associations.
20 |
21 | It’s true that *society* is an abstraction—an oft useful one, but an
22 | abstraction nonetheless. And therefore when we say ‘it is the responsibility of
23 | *society* to do XYZ,’ we must bear in mind that we are talking about the
24 | components which make up that abstraction: the individuals. When we do err in
25 | our use of it, we are committing the [Fallacy of
26 | Reification](http://en.wikipedia.org/wiki/Reification_(fallacy)). I find it a
27 | lot easier to use standard terms of art from the field of logic rather than
28 | explain the point every time.
29 |
30 | In any dialogue, we hope to improve our own argument by having a skilled
31 | opponent who can point out these mistakes. But we also have to be our own
32 | critics. As libertarians, as much as we like to point out the fallacy of
33 | reifying *society* when committed by left-leaning thinkers, we also have to
34 | avoid the trap of referring to *the market* as a reified entity.
35 |
36 | There is no such thing as the market. There are individual actors, and there
37 | are businesses. The business is reified through the concept of the legal
38 | corporation, which is a little more concrete, but the corporate veil remains a
39 | thin one. When we say “the market will fix this problem”, what we mean to say
40 | is that businesses and individuals will rise to a challenge because it is in
41 | their economic interest to solve it. But this is never guaranteed, and assuming
42 | that a ‘market’ even exists for a given solution is dangerous.
43 |
44 | I’m not arguing in favour of the government running everything; I remain a
45 | lover of economic freedom and I believe that a small government is better than
46 | a large one. But there have been many tragic situations where a government has
47 | cut-and-run from a project they couldn’t manage effectively, expecting ‘the
48 | market’ to pick it up and instantly solve all the issues. The simple
49 | libertarian recommendation of “let the market fix it” needs to be rethought in
50 | terms of establishing robust ‘starting variables’ for common pool resource
51 | management systems that allow a market to grow—a phrase which here means
52 | ‘encourage motivated economic agents to sustainably compete over resources’.
53 |
54 | As participants of a debate, we must be aware of the language we use to think
55 | and talk about these matters, so that we can improve the quality of our
56 | discourse and bring better arguments to the table, even if that means admitting
57 | to a few past mistakes.
58 |
--------------------------------------------------------------------------------
/content/media/sass/_pygments.scss:
--------------------------------------------------------------------------------
1 | pre { background: #002B36; color: #93A1A1 }
2 | pre > code {
3 | color: #93A1A1;
4 | .c { color: #586E75 } /* Comment */
5 | .err { color: #93A1A1 } /* Error */
6 | .g { color: #93A1A1 } /* Generic */
7 | .k { color: #859900 } /* Keyword */
8 | .l { color: #93A1A1 } /* Literal */
9 | .n { color: #93A1A1 } /* Name */
10 | .o { color: #859900 } /* Operator */
11 | .x { color: #CB4B16 } /* Other */
12 | .p { color: #93A1A1 } /* Punctuation */
13 | .cm { color: #586E75 } /* Comment.Multiline */
14 | .cp { color: #859900 } /* Comment.Preproc */
15 | .c1 { color: #586E75 } /* Comment.Single */
16 | .cs { color: #859900 } /* Comment.Special */
17 | .gd { color: #2AA198 } /* Generic.Deleted */
18 | .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */
19 | .gr { color: #DC322F } /* Generic.Error */
20 | .gh { color: #CB4B16 } /* Generic.Heading */
21 | .gi { color: #859900 } /* Generic.Inserted */
22 | .go { color: #93A1A1 } /* Generic.Output */
23 | .gp { color: #93A1A1 } /* Generic.Prompt */
24 | .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */
25 | .gu { color: #CB4B16 } /* Generic.Subheading */
26 | .gt { color: #93A1A1 } /* Generic.Traceback */
27 | .kc { color: #CB4B16 } /* Keyword.Constant */
28 | .kd { color: #268BD2 } /* Keyword.Declaration */
29 | .kn { color: #859900 } /* Keyword.Namespace */
30 | .kp { color: #859900 } /* Keyword.Pseudo */
31 | .kr { color: #268BD2 } /* Keyword.Reserved */
32 | .kt { color: #DC322F } /* Keyword.Type */
33 | .ld { color: #93A1A1 } /* Literal.Date */
34 | .m { color: #2AA198 } /* Literal.Number */
35 | .s { color: #2AA198 } /* Literal.String */
36 | .na { color: #93A1A1 } /* Name.Attribute */
37 | .nb { color: #B58900 } /* Name.Builtin */
38 | .nc { color: #268BD2 } /* Name.Class */
39 | .no { color: #CB4B16 } /* Name.Constant */
40 | .nd { color: #268BD2 } /* Name.Decorator */
41 | .ni { color: #CB4B16 } /* Name.Entity */
42 | .ne { color: #CB4B16 } /* Name.Exception */
43 | .nf { color: #268BD2 } /* Name.Function */
44 | .nl { color: #93A1A1 } /* Name.Label */
45 | .nn { color: #93A1A1 } /* Name.Namespace */
46 | .nx { color: #93A1A1 } /* Name.Other */
47 | .py { color: #93A1A1 } /* Name.Property */
48 | .nt { color: #268BD2 } /* Name.Tag */
49 | .nv { color: #268BD2 } /* Name.Variable */
50 | .ow { color: #859900 } /* Operator.Word */
51 | .w { color: #93A1A1 } /* Text.Whitespace */
52 | .mf { color: #2AA198 } /* Literal.Number.Float */
53 | .mh { color: #2AA198 } /* Literal.Number.Hex */
54 | .mi { color: #2AA198 } /* Literal.Number.Integer */
55 | .mo { color: #2AA198 } /* Literal.Number.Oct */
56 | .sb { color: #586E75 } /* Literal.String.Backtick */
57 | .sc { color: #2AA198 } /* Literal.String.Char */
58 | .sd { color: #93A1A1 } /* Literal.String.Doc */
59 | .s2 { color: #2AA198 } /* Literal.String.Double */
60 | .se { color: #CB4B16 } /* Literal.String.Escape */
61 | .sh { color: #93A1A1 } /* Literal.String.Heredoc */
62 | .si { color: #2AA198 } /* Literal.String.Interpol */
63 | .sx { color: #2AA198 } /* Literal.String.Other */
64 | .sr { color: #DC322F } /* Literal.String.Regex */
65 | .s1 { color: #2AA198 } /* Literal.String.Single */
66 | .ss { color: #2AA198 } /* Literal.String.Symbol */
67 | .bp { color: #268BD2 } /* Name.Builtin.Pseudo */
68 | .vc { color: #268BD2 } /* Name.Variable.Class */
69 | .vg { color: #268BD2 } /* Name.Variable.Global */
70 | .vi { color: #268BD2 } /* Name.Variable.Instance */
71 | .il { color: #2AA198 } /* Literal.Number.Integer.Long */
72 | }
73 |
--------------------------------------------------------------------------------
/content/articles/2013/02/03/airblade.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-02-03
3 | kind: article
4 | title: "Airblade"
5 | ---
6 |
7 | Imagine it's mid-2006, and you manufacture and sell hand dryers. Your product
8 | probably looks something like this:
9 |
10 |
11 |
12 | Then, one day in October, a company that manufactures vacuum cleaners releases
13 | this machine, and literally turns your industry upside-down:
14 |
15 |
16 |
17 | How do you react to something like that? Well, like most people in your
18 | industry at the time, you probably criticize the device for being loud,
19 | weird-looking, and creating a puddle of water on the floor after use.
20 | Unfortunately for you, the die has already been cast. Until now, consumers much
21 | preferred paper towels, and only tolerated your product because premise owners
22 | wanted to save money and waste. Most hand dryers took a minute to dry a pair of
23 | hands, resulting in queues, and customers giving up, just to wipe their hands
24 | on their clothes. But an Airblade can dry a pair of hands completely in 10
25 | seconds.
26 |
27 | A single company, with a little innovation, design and smooth marketing, has
28 | turned a utilitarian product which had to be 'put up with' into a status symbol
29 | for high-end venues and offices.
30 |
31 | * * * *
32 |
33 | Dyson was by no means the first to come up with the general idea, the
34 | Mitsubishi Jet Towel having preceded the Airblade by 13 years. But to ascribe
35 | all of the Airblade's success to pure luck would be a clear mistake—design,
36 | sales and brand matter in the toilet industry as much as any other.
37 | Conceivably, any other company *could* have made a hand dryer with the impact,
38 | impression and technology of the Airblade. The question is: why didn't they?
39 |
40 | I'd guess that garden variety complacency is the answer. Your cash flows are
41 | just fine as they are, and you're not particularly worried about the future.
42 | Premise owners who have already bought your product are now locked into service
43 | contracts, and they've probably amortized the initial cost of the dryers over
44 | several years. High-end establishments would use paper towels anyway, and the
45 | highest-end ones use real cloth towels that get laundered.
46 |
47 | The ease with which the human brain can rationalize away laziness never fails
48 | to surprise. But, as Darwin teaches us, complacency = death. This parable keeps
49 | playing out, in different industries, but always the same way.
50 |
51 |
54 |
55 | As Airblade-like technology becomes cheaper, and second- and third-generation
56 | derivatives thereof (sometimes referred to as 'knock-offs') spring up, how many
57 | mid-market venues do you think will be upgrading? How many greasy spoons and
58 | diners and cafés? This is Gibson's unevenly-distributed future—an economic
59 | tidal wave which sees 'premium' products entering the economy at one end and
60 | dispersing across every stratum within a few years. It's the reason why there
61 | are whole families on welfare carrying iPhones.
62 |
63 | * * * *
64 |
65 | Being a human carries an inevitable amount of economic risk. Being an
66 | innovator, pioneer or entrepreneur carries more still. But it's important to
67 | remember that *non progredi est regredi*—no matter how safe we feel, the Red
68 | Queen effect is always at play, and a Dyson or Apple or Tesla is always working
69 | away in the darkness, ready to eat our lunch.
70 |
71 | Go forth and make Airblades.
72 |
--------------------------------------------------------------------------------
/static/.htaccess:
--------------------------------------------------------------------------------
1 | Options +FollowSymlinks +MultiViews
2 | ErrorDocument 404 /not-found.html
3 |
4 | # AddOutputFilterByType DEFLATE text/html application/xhtml+xml text/css text/javascript
5 | FileETag MTime Size
6 |
7 | ExpiresActive On
8 | ExpiresDefault "access plus 1 hour"
9 | ExpiresByType application/xhtml+xml "access plus 5 minutes"
10 | ExpiresByType text/css "access plus 1 year"
11 | ExpiresByType text/javascript "access plus 1 year"
12 | ExpiresByType image/gif "access plus 1 year"
13 | ExpiresByType image/jpeg "access plus 1 year"
14 | ExpiresByType image/png "access plus 1 year"
15 |
16 | RewriteEngine on
17 | RewriteRule ^rss$ rss.rdf
18 |
19 | AddType application/rdf+xml .rdf
20 | AddType application/xhtml+xml .html
21 | AddCharset UTF-8 .html .rdf
22 |
23 | RedirectMatch permanent ^/2010-03-05-deployment-with-uwsgi-and-nginx$ http://blog.zacharyvoase.com/2010/03/05/django-uwsgi-nginx/
24 | RedirectMatch permanent ^/2010-02-03-django-project-conventions-revisited$ http://blog.zacharyvoase.com/2010/02/03/django-project-conventions/
25 | RedirectMatch permanent ^/2010-01-31-civil-disobedience$ http://blog.zacharyvoase.com/2010/01/31/civil-disobedience/
26 | RedirectMatch permanent ^/2010-01-04-why-im-going-public$ http://blog.zacharyvoase.com/2010/01/04/unlicense/
27 | RedirectMatch permanent ^/2010-01-02-a-nicer-redis-client-for-eventmachine$ http://blog.zacharyvoase.com/2010/01/02/nice-redis-em/
28 | RedirectMatch permanent ^/2009-12-09-fixing-django-management-commands$ http://blog.zacharyvoase.com/2009/12/09/django-boss/
29 | RedirectMatch permanent ^/2009-11-17-announcing-markdoc$ http://blog.zacharyvoase.com/2009/11/17/markdoc/
30 | RedirectMatch permanent ^/2009-11-06-capitalism-or-something-unlike-it$ http://blog.zacharyvoase.com/2009/11/06/capitalism/
31 | RedirectMatch permanent ^/2009-11-02-bioseq$ http://blog.zacharyvoase.com/2009/11/02/bioseq/
32 | RedirectMatch permanent ^/2009-11-02-bioinformatics-and-the-semantic-web$ http://blog.zacharyvoase.com/2009/11/02/bioinformatics-semweb/
33 | RedirectMatch permanent ^/2009-09-11-easy-path-manipulation-in-python$ http://blog.zacharyvoase.com/2009/09/11/path/
34 | RedirectMatch permanent ^/2009-09-10-django-settings-flavours$ http://blog.zacharyvoase.com/2009/09/10/django-settings-flavours/
35 | RedirectMatch permanent ^/2009-09-08-serving-authenticated-static-files-with-django$ http://blog.zacharyvoase.com/2009/09/08/sendfile/
36 | RedirectMatch permanent ^/2009-08-29-getting-a-proper-readline-module-for-python-on-snow-leopard$ http://blog.zacharyvoase.com/2009/08/29/python-readline-sl/
37 | RedirectMatch permanent ^/2009-08-20-openpgp-for-complete-beginners$ http://blog.zacharyvoase.com/2009/08/20/openpgp/
38 | RedirectMatch permanent ^/2009-07-23-django-orm-neat-undocumented-trick$ http://blog.zacharyvoase.com/2009/07/23/django-orm-trick/
39 | RedirectMatch permanent ^/2009-07-20-idea-distributed-http-lock-server$ http://blog.zacharyvoase.com/2009/07/20/http-lock/
40 | RedirectMatch permanent ^/2009-07-03-the-difference-between-post-and-put-get-it-right$ http://blog.zacharyvoase.com/2009/07/03/http-post-put-diff/
41 | RedirectMatch permanent ^/2009-06-20-bureaucratic-breakdown$ http://blog.zacharyvoase.com/2009/06/20/bureaucratic-breakdown/
42 | RedirectMatch permanent ^/2009-05-18-why-json-will-save-bioinformatics$ http://blog.zacharyvoase.com/2009/05/18/json-bioinformatics/
43 | RedirectMatch permanent ^/2009-05-17-django-tip-staff-only-access-to-databrowse$ http://blog.zacharyvoase.com/2009/05/17/django-staff-databrowse/
44 |
--------------------------------------------------------------------------------
/content/articles/2013/01/22/django-objviews.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-01-22
3 | kind: article
4 | title: "Object-based Views in Django"
5 | ---
6 |
7 | I'm not a fan of [class-based views][cbv], much for the same reason I prefer C
8 | over C++—why use classes when simple functions and data structures will
9 | suffice? I think it stems from misinterpretation of [DRY][]; it's a way of
10 | reducing complexity, but it's often interpreted as a call to effectively gzip
11 | your code, and taken to the extreme you get highly nested structures, amounting
12 | to a [Huffman code][] of your business logic.
13 |
14 | [cbv]: https://docs.djangoproject.com/en/dev/topics/class-based-views/
15 | [dry]: http://www.artima.com/intv/dry.html
16 | [huffman code]: https://en.wikipedia.org/wiki/Huffman_coding
17 |
18 | Additionally, I'm worried that many Python developers view class syntax as a
19 | way of producing DSLs instead of creating actual type hierarchies. In my
20 | experience, such clever programming leads to a much higher difficulty debugging
21 | errors, with only marginal improvements (or often drops) in code legibility. So
22 | here's a classless (hehe) alternative to solving the code reuse and
23 | authoritative definition problems.
24 |
25 | The Django docs include a section on [generic form handling][cbv-forms], so
26 | let's start with that (because it's a pretty common use case).
27 |
28 | [cbv-forms]: https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
29 |
30 | Here's what I want the basic API to look like:
31 |
32 | #!python
33 | # myapp/views.py
34 | from objviews import ModelResource
35 |
36 | from myapp.models import Contact # Assume this exists.
37 |
38 | contact = ModelResource(name='contact', model=Contact)
39 |
40 | From your URLconf:
41 |
42 | #!python
43 | from django.conf.urls import patterns, include
44 | from myapp.views import contact
45 |
46 | urlpatterns = patterns('myapp.views',
47 | (r'^contacts/', include(contact.urls)),
48 | # Effectively produces these rules (URL => name):
49 | # contacts/ => contact_list
50 | # contacts/add/ => contact_add
51 | # contacts/\d+/ => contact_detail
52 | # contacts/\d+/edit/ => contact_edit
53 | # contacts/\d+/delete/ => contact_delete
54 | )
55 |
56 | Here's a more sophisticated example, featuring a different URL identifier (slug
57 | instead of numeric):
58 |
59 | #!python
60 | from objviews import ids # Effectively an enum of URL-suitable regexes.
61 |
62 | # Here you'd only get the /contacts/, /contacts/add/ and /contacts//
63 | # URL patterns being generated.
64 | contact = ModelResource(name='contact', model=Contact, id=ids.SLUG,
65 | actions=('list', 'add', 'detail'))
66 |
67 | Note that you configure the 'resource' object through keyword parameters, not
68 | subclassing and extending. This limits the possibilities for extension, but
69 | thereby keeps things constrained and more easily debuggerable. You might be
70 | personally super-familiar with Python's multiple inheritance mechanism, but I
71 | bet not every developer on your team is—and it still adds a layer of
72 | complication when stuff breaks (think: mixins). When you do need extension and
73 | polymorphic behavior, object-based views can create sub-objects and delegate to
74 | them—favouring [composition over inheritance][coi].
75 |
76 | [coi]: https://en.wikipedia.org/wiki/Composition_over_inheritance
77 |
78 | You can also build a number of standard URLs from a single definition by using
79 | ``include()`` and having a ``urls`` property on the object. If you wanted the
80 | same thing for a class-based view, you'd have to implement 'class properties'
81 | (that is, descriptors on the metaclass—it sounds complicated because it is), or
82 | otherwise call a classmethod to generate the ``patterns`` object. And with
83 | objects, because you can directly call them or their methods, you get to skip
84 | all of the ``as_view()`` malarkey, too.
85 |
86 | I'm aiming to provide a working prototype of an object-based view, but for now
87 | I want to just put the idea and potential API out there.
88 |
--------------------------------------------------------------------------------
/content/articles/2013/02/15/activism.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-02-15
3 | kind: article
4 | title: "Activism"
5 | ---
6 |
7 | You might come down on either side in the positive discrimination debate, and
8 | you may have your own opinion on the paucity of women in the tech community,
9 | but one of the aspects of our engineering culture that makes me so proud is
10 | that we can (if we want) have rational discussions about the benefits and
11 | drawbacks of various approaches, and things largely remain civil.
12 |
13 | But then once in a while someone comes along and [threatens that peace](http://www.kernelmag.com/yiannopoulos/4115/put-a-sock-in-it-you-dickless-wonders/):
14 |
15 | > Is there anything more utterly fucking boring than well-meaning commentators
16 | > tweeting the obligatory “Why are there no women on stage?” at tech
17 | > conferences?
18 | > …
19 | > No matter the significance, newsworthiness or even comic potential of what’s
20 | > happening on stage, a male tech blogger can always be relied upon to bleat
21 | > out the same, tired old commentary as if he were a bold social reformer,
22 | > rather than the bland, craven hack he so often is.
23 | > …
24 | > […]this is the technology industry: there are more men in it because the male
25 | > mind is, in general, better primed with the sorts of skills the industry
26 | > values; men are simply better suited to most technology jobs.
27 | >
28 | > Women therefore tend to work in roles that require finesse and communicative
29 | > skills, where they pop up in this world at all.
30 |
31 | These are the words of a man called Milo Yiannopoulos. I could rant for hours
32 | about the flaws in his proposition (believe me when I say his rant continues
33 | with accelerating grimness), but I won't. Instead, I want to make a plea to the
34 | companies highlighted in [his
35 | biography](http://www.kernelmag.com/author/yiannopoulos/):
36 |
37 | > Milo specialises in privacy, piracy, technology start-ups, internet culture
38 | > and the media. He was previously Consulting Editor (Technology) for
39 | > **Telegraph.co.uk** and a European contributing editor at **TechCrunch**.
40 | >
41 | > His writing has also appeared in the **Wall Street Journal**, **the Times**,
42 | > **the Observer**, **the Guardian**, **WIRED**, **the Spectator**, **Corriere
43 | > della Sera**, **Directors’ Guild of America Quarterly**, the **500 Startups**
44 | > blog, **Management Today**, **L’Osservatore Romano** and many other
45 | > publications in Europe and America.
46 | >
47 | > Milo is Chief Feature Writer for **The Catholic Herald**. He is an advisor to
48 | > citizen journalism news service **Blottr** and he also serves as mentor on
49 | > the **500 Startups** and **Springboard** accelerator programs, where he
50 | > advises technology start-ups on marketing and public relations.
51 | >
52 | > In 2011 and again in 2012, Milo was named one of the 100 most influential
53 | > people in Britain’s digital economy by **WIRED** magazine. He was inducted
54 | > into **Courvoisier’s** The Future 500 in 2012 and he has been profiled by the
55 | > **Observer**, **Forbes** and others.
56 |
57 | If you own or manage an organization on this list, I'm speaking to you
58 | directly. **After the dust has settled on this one, there will be two
59 | categories of company: those who spoke up and disavowed any connection with Mr.
60 | Yiannopoulos, and those who did not.** I hope for your sake you have the wisdom
61 | to position yourself on the right side of history in this matter.
62 |
63 | Incidentally, I checked the [500 Startups](http://500.co/) mentor list,
64 | and couldn't find a mention of Mr. Yiannopoulos's name anywhere.
65 |
66 | ## Community
67 |
68 | The Internet is the great enabler, and by empowering everyone, it levels the
69 | playing field, such that no single individual is inherently stronger or weaker
70 | than any other. So if we want to confront threats to our peace like this, and
71 | hold them to account, we have to present a united front.
72 |
73 | I want all bigots and trolls and uncivil people out there to heed these words:
74 | we are more numerous than you, and together we have the ability to shut you
75 | out. If you want to work with us or for us *ever again*, if you want to attend
76 | our conferences and talk at our meetups and drink our sponsored beer and write
77 | for our journals, you need to play by the rules of civil discussion.
78 |
79 | Zack out.
80 |
--------------------------------------------------------------------------------
/content/media/sass/_typography.scss:
--------------------------------------------------------------------------------
1 | @import "_colors";
2 |
3 | $serif: ff-meta-serif-web-pro, Georgia, "Liberation Serif", serif;
4 | $sans-serif: futura-pt, Futura, "Gill Sans", "Gill Sans MT", "Liberation Sans", "Trebuchet MS", "DejaVu Sans", "Bitstream Vera Sans", Verdana, sans-serif;
5 | $monospace-inline: "Courier", "Courier 10 Pitch", "Courier New", "monospace";
6 | $monospace-block: "Menlo", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier", "Courier 10 Pitch", "Courier New", "monospace";
7 |
8 | @mixin dark-bg {
9 | color: white;
10 | text-decoration: none;
11 |
12 | a, a:visited, a:active { color: $teal * 3; }
13 | }
14 |
15 | .titling {
16 | font-family: $sans-serif;
17 | font-weight: 700;
18 | letter-spacing: 1px;
19 | text-transform: uppercase;
20 | text-align: center;
21 | }
22 |
23 | .summary {
24 | font-style: italic;
25 | }
26 |
27 | #header {
28 | h1 {
29 | color: white;
30 | font-size: 38px;
31 | line-height: 44px;
32 | @extend .titling;
33 |
34 | @include mobile {
35 | font-size: 24px;
36 | line-height: 32px;
37 | }
38 |
39 | a {
40 | color: inherit;
41 | text-decoration: none;
42 | &:hover { text-decoration: none; }
43 | }
44 |
45 | .prefix {
46 | font-size: 16px;
47 | line-height: 20px;
48 | }
49 | }
50 | }
51 |
52 | #meta {
53 | text-align: center;
54 | text-transform: uppercase;
55 | font-size: 14px;
56 | line-height: 20px;
57 |
58 | p.copy {
59 | font-family: $serif;
60 | text-transform: none;
61 | font-size: 18px;
62 | line-height: 24px;
63 | }
64 | }
65 |
66 | #links {
67 | @extend .titling;
68 | color: $grey;
69 | font-size: 14px;
70 |
71 | ul { text-align: center; }
72 | a, a:visited { color: $grey; }
73 | }
74 |
75 | #footer p {
76 | @include dark-bg;
77 | @extend .titling;
78 | font-size: 12px;
79 | }
80 |
81 | #articles {
82 | li {
83 | .date {
84 | @extend .titling;
85 | color: $grey;
86 | font-size: 12px;
87 | text-align: right;
88 | font-weight: normal;
89 | @include mobile {
90 | text-align: left;
91 | }
92 | }
93 |
94 | .date_month {
95 | display: inline-block;
96 | width: 32px;
97 | text-align: center;
98 | }
99 |
100 | .link {
101 | @extend .titling;
102 | color: #000;
103 | text-align: left;
104 | font-size: 13px;
105 | @include mobile { font-size: 18px; }
106 | }
107 | }
108 | }
109 |
110 | div.article, div.page {
111 | text-rendering: optimizeLegibility;
112 |
113 | a.index-link {
114 | font-size: 12px;
115 | text-transform: uppercase;
116 | }
117 |
118 | h1 {
119 | @extend .titling;
120 | font-size: 24px;
121 | line-height: 32px;
122 | }
123 |
124 | p.date {
125 | color: $grey * 0.5;
126 | font-size: 14px;
127 | text-align: center;
128 | }
129 | }
130 |
131 | body {
132 | font-family: $sans-serif;
133 | font-size: 16px;
134 | line-height: 26px;
135 | color: #333;
136 |
137 | .contents {
138 | font-family: $serif;
139 | text-align: justify;
140 |
141 | h1, h2, h3, h4, h5 {
142 | @extend .titling;
143 | text-align: left;
144 | }
145 | h2 { font-size: 20px; }
146 | h3 { font-size: 18px; }
147 | h4 { font-size: 16px; }
148 | h5 { font-size: 15px; }
149 | p { margin-bottom: 26px; }
150 | }
151 | }
152 |
153 | a {
154 | color: #333;
155 | text-decoration: none;
156 |
157 | &:hover { text-decoration: underline; }
158 | &:visited, &:active { color: #333; }
159 |
160 | // Links in the body of an article/page.
161 | .article .contents &, .page .contents & {
162 | text-decoration: underline;
163 |
164 | &:visited { color: $grey; }
165 | }
166 | }
167 |
168 | code {
169 | font-family: $monospace-inline;
170 | font-size: 14px;
171 | line-height: 24px;
172 | }
173 |
174 | pre {
175 | white-space: normal;
176 | & > code {
177 | color: $grey * 0.5;
178 | font-family: $monospace-block;
179 | font-size: 12px;
180 | line-height: 18px;
181 | white-space: pre;
182 | }
183 | }
184 |
185 | blockquote { color: $grey / 2; }
186 | dl dt { font-weight: bold; }
187 | .article .contents {
188 | ins, del { @include dark-bg; }
189 | }
190 | sup { vertical-align: super; }
191 |
--------------------------------------------------------------------------------
/content/articles/2010/05/17/semblog.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2010-05-17
3 | kind: article
4 | title: "The Semantic Blog"
5 | ---
6 |
7 | You might see that I’m up and running on a new blog of late; I just wanted to
8 | take some time to go over its architecture and some neat features.
9 |
10 |
11 | ## Cool URIs
12 |
13 | Firstly, I’ve changed the URI structure for my posts. Before I had URIs like:
14 |
15 | /2009-08-20-openpgp-for-complete-beginners
16 |
17 | Now they’re more like:
18 |
19 | /2009/08/20/openpgp/
20 |
21 | Yes, I know. [Cool URIs don’t change](http://www.w3.org/Provider/Style/URI). But
22 | the URIs I had before weren’t cool. These ones are, and I don’t see any reason
23 | to change them in the future.
24 |
25 |
26 | ## Powered by nanoc3
27 |
28 | My previous blog was powered by my very own [Markdoc](http://markdoc.org/),
29 | which I’m still quite proud of. Unfortunately it doesn’t really accommodate the
30 | blogging model (i.e. chronologically-ordered content) very well.
31 |
32 | This blog is powered by [nanoc3](http://nanoc.stoneship.org/), which is a great
33 | Ruby utility for static publishing. It provides much more control over how the
34 | content is built into a HTML site, and it’s definitely a better fit for
35 | blogging. My templates are written in [Haml](http://haml-lang.com), stylesheets
36 | in [Sass](http://sass-lang.com/), and content in
37 | [Markdown](http://daringfireball.net/projects/markdown/).
38 |
39 |
40 | ## No Comment
41 |
42 | There’s no real reason to host my own peanut gallery. Disqus comments break with
43 | proper XHTML (that’s XHTML served with `Content-Type: application/xhtml+xml`),
44 | and they’re quite ugly. If you really feel the need to get something off your
45 | chest, I’m sure someone on [Hacker News](http://news.ycombinator.com/) cares. If
46 | you have a question, e-mail me, and if I feel it’s important enough, I’ll update
47 | the relevant post.
48 |
49 |
50 | ## Public
51 |
52 | It’s worth pointing out again that the contents of this blog—design, code and
53 | text—have all been released into the Public Domain. You are free to remix, reuse
54 | and redistribute whatever you find here, without any encumbrance or attribution
55 | requirement.
56 |
57 |
58 | ## Semantic
59 |
60 | I saved the best ’til last. Take a look at the source of this page. You’ll
61 | notice a few things that aren’t so common—the doctype:
62 |
63 | #!html
64 |
66 |
67 | The big set of namespace declarations:
68 |
69 | #!html
70 |
80 |
81 | `property` attributes on almost every text-containing element:
82 |
83 | #!html
84 |
85 | The Blog of Zachary Voase, brought to you in glorious HyperText.
86 |
87 |
88 | This is called [RDFa](http://en.wikipedia.org/wiki/RDFa), and it is a big part
89 | of the web’s future. It is a standard for embedding RDF triples in the natural
90 | structure of your HTML documents, such that they provide semantic context
91 | *and* can easily be parsed out into raw triples later.
92 |
93 |
94 | ### Demonstration
95 |
96 | To get a valid [RSS 1.0](http://web.resource.org/rss/1.0/) feed for this site:
97 |
98 | #!bash
99 | rapper -q -i rdfa -o rdfxml-abbrev 'http://blog.zacharyvoase.com/'
100 |
101 |
102 | You’ll need to
103 | install rapper.
104 |
105 |
106 | Since RSS 1.0 is just an RDF ontology, you can embed its semantics into the
107 | fabric of the page itself. `http://blog.zacharyvoase.com/` *is* an RSS 1.0 feed,
108 | although represented in XHTML+RDFa instead of RDF/XML. Ideally I’d like to have
109 | the following retrieve the XML version of the feed:
110 |
111 | #!bash
112 | curl -H 'Accept: application/rss+xml' 'http://blog.zacharyvoase.com/'
113 |
114 | I’ll set this up once I figure out all the Apache options.
115 |
--------------------------------------------------------------------------------
/content/articles/2013/03/04/san-francisco.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-03-04
3 | kind: article
4 | title: "San Francisco"
5 | ---
6 |
7 | As I write this, I'm sat on a Boeing 747-400 from SFO to LHR, planning out a
8 | decision tree with a million branches for my immediate and long-term future.
9 | I'm returning home after attending a job interview which didn't pan out in the
10 | end, determined to find a way to get into the San Francisco Bay Area in the
11 | next few months, and stay there.
12 |
13 | * * * * *
14 |
15 | Filmmakers have Hollywood; financiers have New York City; wine makers have
16 | Champagne; hackers have San Francisco. This, of course, is not to say it is
17 | impossible to produce code outside of the Bay Area—some wonderful wines come
18 | from Bordeaux, Germany and Italy; great films are made in Berlin and Canada;
19 | London has a long, strong history of financial innovation. But an *economy of
20 | agglomeration* undeniably exists in San Francisco and its neighbouring towns
21 | (Palo Alto, Mountain View, etc.). Every day, apps and websites and gadgets are
22 | conceived, designed, managed, built, tested and sold on a small peninsula which
23 | can be driven across entirely in little over an hour.
24 |
25 | Certainly, the men and women who do this work demand a pretty penny for it. But
26 | the capital available to pay them is as abundant as you'd expect, because
27 | technological innovation in particular is the art of force multiplication—if
28 | you can build something which saves millions of people several minutes each
29 | day, why *shouldn't* you be rewarded handsomely for it?
30 |
31 | Many of the ideas executed upon are silly. If they weren't, someone else would
32 | have thought of them already. Even more of the executions are imperfect.
33 | Failure is the median. But success is still the mean.
34 |
35 | If you're a good programmer, there's nowhere else on Earth where you can rack
36 | up as much XP as the Valley. You won't be paid this much for what seems to be a
37 | 'simple' job anywhere else, short of working in finance. You certainly can't
38 | rock up anywhere else in the world *without* any letters after your name and
39 | expect a six-figure salary.
40 |
41 | The hardware, software and techniques that people are using in San Francisco
42 | *today* are the those everyone else will be using in six months. If you're
43 | looking to build a product, and it fails with the über-early adopter crowd of
44 | San Francisco, it probably won't work elsewhere. If it works in SF, it doesn't
45 | matter if it succeeds elsewhere, because people here have enough money to make
46 | you rich anyway.
47 |
48 | Even multi-national companies distinguish between their SF workers and foreign
49 | counterparts. If you're working as a Software Engineer at Google in London, you
50 | can probably expect to earn (*gross*) 64% of what your Mountain View-based
51 | colleagues make (£45k ~= $70k vs. $110k+), whilst laying out for a cost of
52 | living that's 16–22% higher than someone in the city of San Francisco, and
53 | probably paying more taxes too. In 'real-world' terms, this means you're going
54 | to be near-unable to save, you'll have a smaller house/apartment, further away
55 | from the centre of town, with fewer labor-saving amenities, and an overall
56 | poorer quality of life (including the shitty weather). For exactly the same
57 | job.
58 |
59 | San Francisco really is the land of opportunity.
60 |
61 | * * * * *
62 |
63 | Those who know me well know I've been strict Paleo for around a year, and
64 | weightlifting for about two (although with varying strictness of routine).
65 | Another big drive for me in SF is the large number of people doing Paleo,
66 | CrossFit, [keto](/2013/01/21/diet/), powerlifting and/or Olympic lifting, and
67 | even just the general awareness of these phenomena amongst those not doing
68 | them. I can get bunless lettuce-wrap burgers without weird looks (or plain
69 | ignorance on the part of serving staff). I can talk to other people about
70 | squats and deadlifts without blank stares.
71 |
72 | * * * * *
73 |
74 | My goal in writing this is to both explain and understand my motivations for
75 | pursuing San Francisco as a location to live and work. I don't expect everyone
76 | to agree with me—but they don't have to.
77 |
78 | I write this as a 20-year old programmer who has so far made a living out of
79 | not listening to 'good' advice. I'm scared about my future: I'm not guaranteed
80 | a job or a work visa at this stage. But if things don't work out in the next 6
81 | months, I'll keep trying, because they might come through in the next 12, or
82 | 18, or 24. I have nothing to lose.
83 |
--------------------------------------------------------------------------------
/content/articles/2010/01/04/unlicense.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2010-01-04
4 | title: "Why I’m Going Public"
5 | ---
6 |
7 | I just made the decision to unlicense my Markdown-based wiki management software
8 | [Markdoc][], and re-release it into the public domain. I’m about to start the
9 | lengthy but valuable process of unlicensing [all of my software][], and I felt
10 | it would be a good idea to explain my actions, and perhaps convince others to
11 | follow suit.
12 |
13 | [markdoc]: http://markdoc.org/
14 | [all of my software]: http://bitbucket.org/zacharyvoase/
15 |
16 | The trail that led me here began several years ago, when I wrote my first
17 | complete program in Python. I licensed it under the GNU GPL, because as far as I
18 | could tell that was *the* open-source software license (I just wasn’t aware of
19 | any others). I read the full text of the GPL, and it seemed reasonable enough to
20 | me. I did always have a nagging in the back of my head at the time, that the
21 | ‘viral’ nature of the license might be harmful in the long term, but I went with
22 | it anyway. Nothing I had written at that stage was worthy of being redistributed
23 | by anyone else, so I had little to worry about.
24 |
25 | My encounter with Django convinced me to start using MIT/X11 as my license of
26 | choice. It turns out a lot of other people shared my opinion on the GPL, and
27 | that the choice of license really *did* have real-world consequences. Like Mac
28 | OS X coming with a sucky readline library, or stupidly long installations
29 | because a developer couldn’t include a required library with their software for
30 | fear of being sued. As I saw it, GPL was a different kind of free software — it
31 | was freedom **whether you like it or not**.
32 |
33 | By this point I was just a step away from public domain. Not being a lawyer, I
34 | had neither the incentive nor the ability to draft a comprehensive ‘unlicense’
35 | that would declare my code as public domain. The MIT license was suiting me
36 | fine, and I didn’t expect anyone to even want to redistribute my software. After all, this is the era of the dynamic language, and it’s relatively simple to just declare an external dependency and call it a day. But then came the revelation.
37 |
38 |
39 | ## Software Freedom
40 |
41 | The word ‘free’ is bandied about a lot today. Sometimes it’s written as ‘Free’,
42 | because with capitalization it is no longer a word, but a platonic ideal. The
43 | FSF describes free software as “free as in speech, not as in beer” (although
44 | it’s usually both). I, however, like to consider the *spirit* of freedom. And,
45 | [unlike the FSF][fsd], I’m not going to waste over 12,000 bytes telling you what
46 | it is.
47 |
48 | [fsd]: http://www.gnu.org/philosophy/free-sw.html
49 |
50 |
51 | ### The Spirit of Freedom
52 |
53 | So I’ve written a piece of software.
54 |
55 | * I’m not making any promises about this piece of software.
56 | * You are free to do **whatever the fuck** you want *with* this piece of software.
57 | * You are free to do **whatever the fuck** you want *to* this piece of software.
58 |
59 | And isn’t *that* what being free is all about? Not having to worry that you
60 | might be breaking a rule? Even though I think the MIT license is relatively loose, it still forces requirements on the users:
61 |
62 | Permission is hereby granted ...snip... subject to the following
63 | conditions:
64 |
65 | The above copyright notice and this permission notice shall be
66 | included in all copies or substantial portions of the Software.
67 |
68 | OK, so it’s a small requirement, but even this turns out to be a big issue when
69 | you’re dealing with mixing and redistributing multiple pieces of software with
70 | various licenses.
71 |
72 | The way public domain works is as follows:
73 |
74 | * You see my code.
75 |
76 | * You like my code.
77 |
78 | * You copy-and-paste my code into your application, and if you’re gracious
79 | enough, you send me a tweet or an e-mail to let me know. Perhaps you even
80 | acknowledge my contribution in an AUTHORS file somewhere.
81 |
82 | But you don’t have to.
83 |
84 | Now that’s what I call freedom.
85 |
86 |
87 | ## The Unlicense
88 |
89 | [The Unlicense][] has recently been published, and it’s provided the impetus I needed to start putting my software into the public domain (henceforth known as ‘pubdomming’). I can now `cp`-and-go as I could with the GPL and MIT licenses. The text of the license is short and sweet, and remains perfectly aligned with the spirit of freedom. So why don’t you unlicense *your* code?
90 |
91 | [the unlicense]: http://unlicense.org/
92 |
--------------------------------------------------------------------------------
/content/articles/2013/02/10/smallweb.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2013-02-10
3 | kind: article
4 | title: "The Web Is Becoming Smalltalk"
5 | ---
6 |
7 | The concept of a 'web page' is quickly becoming meaningless. I believe there's
8 | a new way to look at the Web and the browser, and synthesizing it with old
9 | technologies could result in a novel technique for Web development and content
10 | editing.
11 | {: .summary}
12 |
13 | Just today I found out that [the Chrome JS editor can 'hot-swap' your
14 | code][chrome-js]. It got me thinking of other systems I know of that allow you
15 | to edit code on-the-fly, breaking out of the tedious write/compile/test/run
16 | cycle.
17 |
18 | [chrome-js]: http://smotko.si/using-chrome-as-a-javascript-editor/
19 |
20 | Smalltalk is an uncommon language these days, with the exception of its
21 | half-descendant, Objective-C. But whilst message passing, dynamic typing and
22 | object orientation are nice things, Objective-C missed the big, game-changing
23 | aspect of Smalltalk: image-based persistence, and the modeless editing
24 | environment which worked with it.
25 |
26 | Traditionally, applications get loaded into memory, and the operating system
27 | kicks off execution of the program starting with a well-known location (i.e.
28 | the start of the `main()` function). At this point you have to instantiate and
29 | initialize whatever objects you have manually, whether that's by accepting user
30 | input or reading data from the network or disk, *et cetera*. Your program gets
31 | into a fuzzy state between 'startup' and 'ready'.
32 |
33 | Smalltalk applications don't do that. Instead, you start with an image, load
34 | that directly into memory, and that already contains all the class definitions
35 | and object instances you need. The clever part is that to *change* a program
36 | you open this same image file in the VM and point-and-click through the
37 | definitions of various methods. When you've made the changes you need to, or
38 | perhaps just tweaked the appropriate objects, you save the image file back to
39 | disk, and it's ready to run again. If you want to start an application from
40 | scratch, you clone a blank image and fill it up with classes, methods and
41 | objects.
42 |
43 | Since this editing environment was typically part of the Smalltalk VM that ran
44 | the image, you wouldn't even need to cycle between editing and running your
45 | application: just open up the editor in the VM and live-edit the objects.
46 |
47 | * * * * *
48 |
49 | What does this mean for the Web? Well, the way I (and [Roy Fielding][rest]) see
50 | it, the browser is a stateful agent that just renders HTML to a viewport, and
51 | executes JavaScript. When you first load up your browser, it's a blank VM.
52 | You direct it to a URL, and HTML, CSS and JavaScript are fetched, which
53 | instruct the browser to render some kind of interface to the viewport. This
54 | interface is linked to the JS interpreter and the server through DOM events,
55 | with the most prominent and noticeable one being the default action of clicking
56 | an `` element.
57 |
58 | [rest]: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
59 |
60 | Those of us who first grokked the internet before the advent of JS-heavy web
61 | 'apps' still have this concept of navigating through pages. Now that
62 | conventions are moving towards 'single-page' web apps, the concept of a 'page'
63 | is losing its special meaning. In New World terms, a page is an artifact of a
64 | navigation event which completely trashes all but a small part of the VM (i.e.
65 | the cookie jar), replacing it with a new object tree and code definitions. Make
66 | sense?
67 |
68 | In a way, browsers have been capable of running web apps since the advent of
69 | `XMLHttpRequest`. The only difference is that today, the mean time between
70 | complete obliterations of that VM state is much longer.
71 |
72 | So if we're moving into a world of treating the browser as a VM, the manager of
73 | a long-lived application state, we need the other parts of the Smalltalk model:
74 | live-editing and persistence. The Chrome Web Inspector is great for modifying
75 | CSS rules on-the-fly (it's a declarative language, DOM redraws are cheap on a
76 | human timescale, go figure). Editing JS is trickier due to its functional,
77 | callback-based nature; the average JS object tree is much more nested than that
78 | of the average Smalltalk VM. But Chrome is again showing that it is possible.
79 | So the final piece of the puzzle is persistence: this bundle of HTML,
80 | JavaScript and CSS needs to be written *back* to the server.
81 |
82 | I have a hunch that WebDAV combined with standard HTTP authentication could be
83 | the answer. I'm not 100% sure on it, but I can easily envision a world where
84 | you fix bugs in your website by opening it up in a browser, reading a stack
85 | trace, fixing the JS in that same browser and persisting your changes
86 | back to the server.
87 |
88 | I dream of the days when the Web truly does resemble Smalltalk.
89 |
90 | * * * * *
91 |
92 | Voice your disagreement on [Hacker News](https://news.ycombinator.com/item?id=5198425).
93 |
--------------------------------------------------------------------------------
/layouts/default.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{html_attrs("en"), xmlns}
3 | %head
4 | %meta{'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"}
5 | %meta{:name => "viewport", :content => "width=device-width"}
6 |
7 | %meta{:property => "og:image:url", :content => "http://gravatar.com/avatar/fcd3a40babe606ef30cb342a6a74c54c.png"}
8 | %meta{:property => "og:image:secure_url", :content => "https://secure.gravatar.com/avatar/fcd3a40babe606ef30cb342a6a74c54c.png"}
9 | %meta{:property => "dc:creator", :content => "Zachary Voase"}
10 | %meta{:property => "dc:rights", :content => "Public Domain"}
11 | %meta{:property => "twitter:card", :content => "summary"}
12 | %meta{:property => "twitter:site", :content => "@zackwurst"}
13 | %meta{:property => "twitter:domain", :content => "zacharyvoase.com"}
14 |
15 | - if item[:kind] == "index"
16 | %meta{:name => "description", :content => "The Blog of Zachary Voase, brought to you in glorious HyperText."}
17 | %link{:rel => "alternate", :type => "application/rss+xml", :title => "Zack’s Blog", :href => "/rss.rdf"}
18 | %link{:rel => "alternate", :type => "application/rdf+xml", :title => "Zack’s Blog", :href => "/rss.rdf"}
19 |
20 | %link{ :rev => "foaf:weblog", :href => "http://zacharyvoase.com/"}
21 |
22 | %link{ :rel => "rdf:type", :href => "rss:channel"}
23 | %meta{:property => "dc:date", :content => sorted_articles.last[:created_at].to_s}
24 | %meta{:property => "rss:title", :content => "Zack’s Blog"}
25 | %link{ :rel => "rss:link", :href => "http://zacharyvoase.com/"}
26 | %link{ :rel => "rss:items", :href => "http://zacharyvoase.com/#articles"}
27 |
28 | %meta{:property => "twitter:title", :content => "Zack’s Blog"}
29 | %meta{:property => "twitter:url", :content => "http://zacharyvoase.com/"}
30 | %meta{:property => "twitter:description", :content => "The Blog of Zachary Voase, brought to you in glorious HyperText."}
31 |
32 | %meta{:property => "sy:updatePeriod", :content => "daily"}
33 | %meta{:property => "sy:updateFrequency", :content => "1"}
34 | %meta{:property => "sy:updateBase", :content => "2010-05-08T12:00+00:00"}
35 |
36 | - elsif item[:kind] == "article"
37 | %link{:rel => "rdf:type", :href => "rss:item"}
38 | %meta{:name => "description", :content => description_of(@item)}
39 | %meta{:property => "dc:date", :content => @item[:created_at].to_s, :datatype => "xs:date"}
40 | %meta{:property => "rss:title", :content => title_of(@item)}
41 | %meta{:property => "twitter:creator", :content => "@zackwurst"}
42 | %meta{:property => "twitter:url", :content => url_for(@item)}
43 | %meta{:property => "twitter:title", :content => title_of(@item)}
44 | %meta{:property => "twitter:description", :content => description_of(@item)}
45 | %link{:rel => "rss:link", :content => rel_url_for(@item)}
46 |
47 | %link{:rel => "stylesheet", :href => "/media/css/style.css", :media => "screen, projection"}
48 |
49 | %script{:type => "text/javascript", :src => "//use.typekit.net/jfx6whv.js"}
50 | %script{:type => "text/javascript"}
51 | :plain
52 | try{Typekit.load()}catch(e){}
53 | var _gaq = _gaq || [];
54 | _gaq.push(['_setAccount', 'UA-9915287-1']);
55 | _gaq.push(['_trackPageview']);
56 | (function() {
57 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
58 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
59 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
60 | })();
61 |
62 | - if item[:kind] == "index"
63 | %title Zack’s Blog
64 | - else
65 | %title
66 | = title_of(@item)
67 | | Zack’s Blog
68 |
69 | %body{:id => @item[:id]}
70 | %div#header-container
71 | %div#header
72 | - if item[:kind] == "index"
73 | %h1{:property => "rss:description", :datatype => ""}<
74 | %span.prefix The Blog of
75 | Zachary Voase
76 | - else
77 | %h1<
78 | %a{:href => "/"}<
79 | %span.prefix The Blog of
80 | Zachary Voase
81 |
82 | = render('_about')
83 | = render('_spacer')
84 |
85 | - if @item[:kind] == 'article'
86 | = render('_article', :item => @item, :content => content)
87 | - elsif @item[:kind] == 'page'
88 | = render('_page', :item => @item, :content => content)
89 | - else
90 | = content
91 |
92 | %div#footer-container
93 | %div#footer
94 | - if item[:kind] == "article"
95 | %p.share-buttons
96 | = render('_tweet', :item => @item)
97 | %p.licensing
98 | All content on this site is released into the
99 | Public Domain.
100 |
--------------------------------------------------------------------------------
/content/articles/2009/07/20/http-lock.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-07-20
4 | title: "Idea: Distributed HTTP Lock Server"
5 | ---
6 |
7 | I’d like some input on this idea: a HTTP server which acts as a distributed lock
8 | system, whereby:
9 |
10 | POST /lock//acquire/
11 |
12 | would acquire a lock, and
13 |
14 | POST /lock//release/
15 |
16 | would release it. If the lock doesn’t exist, it’s created. Locks could be
17 | ‘sharded’ across multiple processes or servers (although single locks would
18 | reside on a single process or server), and an attempt to acquire a lock that is
19 | in use would block (unless a timeout parameter is supplied).
20 |
21 | The basic problem which this would try to solve is dealing with a resource
22 | shared between multiple processes. By implementing it on top of HTTP, client
23 | libraries and tools could be written in practically any language or for any
24 | platform in existence.
25 |
26 | Here’s an example of where you’d use it: you’re running a very AJAX-intensive
27 | website. While the user might only have one browser window open, they’re still
28 | making multiple concurrent AJAX requests back to your server. On the
29 | server-side, you’re running multiple processes to handle these HTTP requests
30 | (this might be a Mongrel cluster, or perhaps a bunch of WSGI-handling Python
31 | processes). So as a request comes in, you don’t actually know which one of these
32 | processes the request will be handled by. Now let’s also propose that your AJAX
33 | views modify the request session in some way. So this is what happens if two
34 | requests come in at nearly the same time, one very shortly after the other:
35 |
36 | Request 1 |
37 | ------------------------+
38 | POST /ajax/view1/ |
39 | loads session data | Request 2
40 | begins processing +-------------------------
41 | ... | POST /ajax/view2/
42 | ... | loads session data
43 | ... | begins processing
44 | changes session data | ...
45 | saves session data | ...
46 | 200 OK (responds) | changes session data
47 | | saves session data
48 | | 200 OK (responds)
49 |
50 | By the end, only the changes `view2` made are actually present in the session,
51 | since it made those changes to a stale copy of the session data and clobbered
52 | all of `view1`’s changes. With locking, something else would happen:
53 |
54 | Request 1 |
55 | ------------------------+
56 | POST /ajax/view1/ |
57 | ACQUIRE SESSION_KEY |
58 | loads session data | Request 2
59 | begins processing +---------------------------
60 | ... | POST /ajax/view2/
61 | ... | ACQUIRE SESSION_KEY
62 | ... | acquisition blocks
63 | ... | ...
64 | changes session data | ...
65 | saves session data | ...
66 | RELEASE SESSION_KEY ==> acquisition succeeds
67 | 200 OK (responds) | loads session data
68 | | begins processing
69 | | ...
70 | | ...
71 | | changes session data
72 | | saves session data
73 | | RELEASE SESSION_KEY
74 | | 200 OK (responds)
75 |
76 | In this case, by the time `view2` has loaded the session data, it has already
77 | been updated by `view1`, and so it doesn’t clobber the old data. This follows on
78 | to subsequent AJAX requests; other views attempting to change the session will
79 | have to wait until `view2` has finished doing so.
80 |
81 | That’s the general idea about locking. The system I’ve described, however, would
82 | make such behavior possible across the multiple processes and even machines
83 | which are so often used in web-serving environments. At the moment, most
84 | languages support in-process locks and filesystem locks. These are difficult if
85 | not impossible to distribute, since the first can only live within one process
86 | and the latter can only live within one machine (and even still may not be very
87 | cross-platform).
88 |
89 | I’m sure there are a bunch more use-cases, but this was the most obvious one
90 | which came to mind.
91 |
92 | The basic questions I’d like answered by the community (not because I’m lazy,
93 | but because I can’t speak for everyone) are:
94 |
95 | * Is this a good idea (i.e. worth spending the time/effort to implement)?
96 |
97 | * Are there already similar implementations available, preferably open-source?
98 |
99 | * Is there a feature you’d like to see implemented in such a system, whilst
100 | sticking to [D1T&DIW](http://en.wikipedia.org/wiki/Unix_philosophy)?
101 |
102 | * How might you go about implementing such a system?
103 |
104 | * Do you have good ideas for a name for such a system?
105 |
--------------------------------------------------------------------------------
/content/articles/2009/11/06/capitalism.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-11-06
4 | title: "Capitalism, or Something Unlike It?"
5 | ---
6 |
7 | This is my response to an article in Forbes by Sramana Mitra, entitled
8 | “[Capitalism’s Fundamental Flaw](http://www.forbes.com/2009/11/05/innovation-ayn-rand-intelligent-technology-capitalism.html)”.
9 |
10 | It’s interesting that the author chooses the banking crisis as an example of the
11 | ‘fundamental flaw of capitalism’. No other industry on Earth is subject to as
12 | much tacit government intervention and taxation, through the government monopoly
13 | on the issue of currency, the institutionalized fraud of the fractional reserve
14 | system, and inflation.
15 |
16 | A (probably false) anecdote is brought to mind by this, actually. The bank
17 | robber [Willie Sutton](http://en.wikipedia.org/wiki/Willie_Sutton), when asked
18 | by a reporter why he robbed banks, reputedly replied “because that’s where the
19 | money is.” False or not, it seems fitting that governments would adopt a similar
20 | policy. And most importantly, with Willie as it is with governments, it’s not
21 | the bank’s money they take. It’s **yours**.
22 |
23 | It’s very easy for a government with a monopoly on a currency to begin an
24 | inflationary death spiral. The methods are many: fractional reserve banking (in
25 | any form), actual money printing, ‘quantitative easing’ or lowering of reserve
26 | requirements are all ways to ‘increase the money supply’. Once the inflation is
27 | set into motion, it is impossible to stop without incurring a catastrophic
28 | economic recession. Since politicians are elected in the short-term, they decide
29 | to continue with the short-term benefits (but long-term damages) of inflation. A
30 | quote from Macbeth seems unnervingly relevant: ”I am in blood stepp’d in so far
31 | that should I wade no more, Returning were as tedious as go o’er.” In his
32 | documentary series ‘Free to Choose’, Milton Friedman notes that inflation is
33 | comparable to alcoholism; to paraphrase, it’s fun in the beginning, bad later
34 | on, and increasingly difficult to give up ‘cold turkey’ the longer you continue.
35 |
36 | Many people I speak to today still don’t understand how currencies go into
37 | hyperinflation. The recent case of Zimbabwe illustrates this perfectly. It seems
38 | *insane* that a government could allow such rampant inflation to occur, doesn’t
39 | it? And yet the situation still continued to descend rapidly into chaos, until
40 | people gave up on Zimbabwe dollars and
41 | [started using gold instead](http://www.guardian.co.uk/world/video/2009/feb/11/zimbabwe-gold-panning-starvation-food).
42 | Of course, this is the nature of inflation. The easy credit made available
43 | through governmental economic policies in the U.S.A. and Europe have created a
44 | similar environment, albeit a [boiling
45 | frog](http://en.wikipedia.org/wiki/Boiling_frog) if ever there was one.
46 |
47 | So how does this relate to the article I was originally responding to? Well, the
48 | author’s main gripe is that the majority of capital in our so-called ‘free
49 | market economy’ (which, incidentally, hasn’t been free since
50 | [December 23, 1913](http://en.wikipedia.org/wiki/Federal_Reserve_Act)) seems to
51 | gravitate towards the speculators and market players rather than the innovators
52 | and creators of value.
53 |
54 | I can’t find much fault with her assessment, but I think the finger is being
55 | pointed in the wrong direction. Where do you think speculators get their initial
56 | investment capital from? It’s all **credit**. Speculators earn large amounts of
57 | money through leverage (i.e. borrowing large amounts of money to turn a good
58 | investment into a great one), and this leverage is provided by banks with a
59 | license to print money, thanks in no small part to the fractional reserve
60 | system. And of course, with the government monopoly on currency through legal
61 | tender laws, this basically has everyone *except* the banks and government bent
62 | over a financial barrel.
63 |
64 | The gist of my argument is that the speculator problem is completely a
65 | consequence of the government-controlled economy. The first instinct most people
66 | have is to blame capitalism for the recent economic issues, but a closer,
67 | rational inspection exposes who is really at fault here.
68 |
69 | ### Further Reading
70 |
71 | If you’re interested in further exploring some of the views I’ve shared in this
72 | post, see the following Wikipedia articles for more information:
73 |
74 | #### Individual Economists
75 |
76 | * [Murray Rothbard](http://en.wikipedia.org/wiki/Murray_Rothbard)
77 | * [Milton Friedman](http://en.wikipedia.org/wiki/Milton_Friedman)
78 | * [Friedrich Hayek](http://en.wikipedia.org/wiki/Friedrich_Hayek)
79 | * [Ludwig von Mises](http://en.wikipedia.org/wiki/Ludwig_von_Mises)
80 | * [Henry Hazlitt](http://en.wikipedia.org/wiki/Henry_Hazlitt)
81 |
82 | #### Economic Theory
83 |
84 | * [The Austrian School of Economics](http://en.wikipedia.org/wiki/Austrian_School_of_economics)
85 | (where ‘school’ == school of thought)
86 | * [Austrian Business Cycle Theory](http://en.wikipedia.org/wiki/Austrian_business_cycle_theory)
87 | * [Spontaneous Order](http://en.wikipedia.org/wiki/Spontaneous_order)
88 |
--------------------------------------------------------------------------------
/content/media/sass/_layout.scss:
--------------------------------------------------------------------------------
1 | @import "compass/utilities/general/clearfix";
2 | @import "_border-radius";
3 | @import "_colors";
4 |
5 | @mixin mobile {
6 | @media only screen and (max-width: 640px),
7 | only screen and (max-device-width: 640px) {
8 | @content;
9 | }
10 | }
11 |
12 | @mixin container {
13 | clear: both;
14 | float: left;
15 | margin: 0;
16 | padding: 0;
17 | width: 100%;
18 | }
19 |
20 | @mixin section {
21 | margin: 0 auto;
22 | width: 640px;
23 |
24 | @include mobile {
25 | width: 90%;
26 | padding-left: 5%;
27 | padding-right: 5%;
28 | }
29 | }
30 |
31 | hr {
32 | margin: 0 0 26px 0;
33 | border: none;
34 | border-top: 1px solid $grey * 2;
35 | }
36 |
37 | p, iframe, fieldset, table, pre {
38 | margin-bottom: 26px;
39 | }
40 |
41 | p.summary {
42 | padding-bottom: 26px;
43 | border-bottom: 3px double $grey * 2;
44 | }
45 |
46 | .contents {
47 | img {
48 | width: 640px;
49 | @include mobile { width: 100%; }
50 | }
51 | .center img { width: inherit; }
52 | }
53 |
54 | p.share-buttons {
55 | iframe { margin-bottom: 0; }
56 | }
57 |
58 | body, html {
59 | background-color: $teal;
60 | }
61 |
62 | #header-container {
63 | @include container;
64 |
65 | #header {
66 | @include section;
67 |
68 | @include mobile {
69 | width: 100%;
70 | padding-left: 0;
71 | padding-right: 0;
72 | }
73 |
74 | h1 {
75 | background-color: $light-teal;
76 | width: 75%;
77 | margin: 0;
78 | padding: 40px 12.5%;
79 |
80 | @include mobile { padding: 10px 12.5%; }
81 |
82 | .prefix { display: block; }
83 | }
84 | }
85 | }
86 |
87 | .content-container {
88 | @include container;
89 |
90 | background-color: #fff;
91 | border-top: 8px solid $teal;
92 |
93 | .content {
94 | @include section;
95 |
96 | padding: 20px 0 0 0;
97 | }
98 |
99 | @include mobile {
100 | #index & div.content { padding-top: 10px; }
101 | }
102 | }
103 |
104 | #footer-container {
105 | @include container;
106 |
107 | border-top: 8px solid $teal;
108 | padding-bottom: 20px;
109 | @include mobile {
110 | padding-bottom: 0;
111 | }
112 |
113 | #footer {
114 | @include section;
115 | @include border-radius-bottom(10px);
116 | @include mobile {
117 | @include border-radius-bottom(0);
118 | }
119 |
120 | background-color: $light-teal;
121 | height: 100px - (20px * 2);
122 | padding: 20px;
123 | width: 640px - (20px * 2);
124 |
125 | p {
126 | margin-bottom: 0;
127 | }
128 | }
129 | }
130 |
131 | .spacer {
132 | @include container;
133 |
134 | border-top: 8px solid $teal;
135 |
136 | .spacer-child {
137 | @include section;
138 | height: 40px;
139 | background-color: $light-teal;
140 | }
141 | }
142 |
143 | #links {
144 | @include pie-clearfix;
145 |
146 | margin-bottom: 20px;
147 |
148 | ul {
149 | margin: 0;
150 | padding: 0;
151 | }
152 |
153 | li {
154 | display: inline;
155 | margin: 0;
156 | padding: 0;
157 | margin-left: 8px;
158 |
159 | &:before {
160 | content: "★";
161 | margin-right: 8px;
162 | }
163 |
164 | &.first {
165 | margin-left: 0;
166 | &:before { content: ''; }
167 | }
168 |
169 | a { white-space: nowrap; }
170 | }
171 | }
172 |
173 | #articles {
174 | @include clearfix;
175 |
176 | margin: 10px 0 20px 0;
177 |
178 | li {
179 | display: block;
180 | list-style: none;
181 | margin-bottom: 10px;
182 |
183 | .date {
184 | clear: both;
185 | display: block;
186 | float: right;
187 | width: 100px;
188 | }
189 |
190 | .link { white-space: nowrap; }
191 | }
192 |
193 | @include mobile {
194 | margin-top: 0;
195 | margin-bottom: 10px;
196 |
197 | li {
198 | margin-bottom: 0;
199 | padding-bottom: 10px;
200 | padding-top: 10px;
201 | &:first-child { padding-top: 0; }
202 | border-bottom: 1px solid #AAA;
203 | &:last-child { border-bottom: none; }
204 |
205 | .date { float: none; }
206 | .link { white-space: normal; }
207 | }
208 | }
209 | }
210 |
211 | p.index-link-container { margin-bottom: 10px; }
212 |
213 | p.center { text-align: center; }
214 |
215 | h1 { margin: 8px 0; }
216 |
217 | div.content.page h1, p.date {
218 | border-bottom: 3px double $grey * 2;
219 | padding-bottom: 16px;
220 | margin-bottom: 26px;
221 | }
222 |
223 | h4 { margin-bottom: 16px; }
224 |
225 | blockquote, pre {
226 | @include border-radius(5px);
227 |
228 | background-color: $grey * 2;
229 | padding: 10px;
230 | float: none;
231 | overflow-x: scroll;
232 | }
233 |
234 | blockquote {
235 | margin: 0 0 16px 0;
236 | padding: 10px 20px;
237 | min-width: 640px - (20px * 2);
238 |
239 | @include mobile { min-width: 0; }
240 |
241 | & :last-child { margin-bottom: 0 !important; }
242 | }
243 |
244 | ul, ol { margin-right: 0; }
245 |
246 | dl {
247 | margin: 0 0 16px 0;
248 |
249 | dd { margin-bottom: 8px; }
250 | }
251 |
252 | ins, del {
253 | @include border-radius(5px);
254 |
255 | background-color: $teal;
256 | display: block;
257 | padding: 10px;
258 | width: 640px - (10px * 2);
259 | @include mobile { width: auto; }
260 | }
261 |
262 | p, ins, del, pre, h1, h2, h3, h4, h5, h6 { clear: both; }
263 |
--------------------------------------------------------------------------------
/content/articles/2009/09/10/django-settings-flavours.md:
--------------------------------------------------------------------------------
1 | ---
2 | kind: article
3 | created_at: 2009-09-10
4 | title: "Django Settings Flavours"
5 | ---
6 |
7 | Anyone who’s ever deployed a Django site will have encountered a common problem.
8 | You’ll usually have both a development and a production environment in which
9 | you’re running your Django project; many large-scale deployments will also have
10 | a staging environment. How do you maintain separate configurations for each of
11 | these environments, using each configuration in its appropriate environment?
12 |
13 | In my Django projects, I have a setup, refined over a period of several months,
14 | which I feel provides a very neat, elegant and flexible solution to this
15 | problem. It leverages the fact that the default Django project layout, as
16 | generated by `django-admin startproject`, assumes very little about the
17 | individual development process or deployment architecture which the project will
18 | have as it matures. While this can be intimidating for newcomers, it is a design
19 | decision which allows seasoned Djangonauts to stretch the boundaries of the
20 | framework, taking advantage of the power and flexibility provided.
21 |
22 | ### Getting Started
23 |
24 | I begin with the default project layout. Running
25 | `django-admin startproject myproject` will give this:
26 |
27 | myproject/
28 | |-- __init__.py
29 | |-- manage.py
30 | |-- settings.py
31 | `-- urls.py
32 |
33 | I then break `settings.py` into a directory, like so:
34 |
35 | myproject/
36 | |-- __init__.py
37 | |-- manage.py
38 | |-- settings
39 | | |-- __init__.py
40 | | |-- common.py
41 | | |-- development.py
42 | | `-- production.py
43 | `-- urls.py
44 |
45 | There are a few things to note about the `settings` directory and its contents:
46 |
47 | * It contains an `__init__.py` file, which makes it a Python package.
48 |
49 | * `common.py` contains the settings which are common to all environments. This
50 | includes stuff like `ROOT_URLCONF`, `INSTALLED_APPS`, `USE_I18N`, context
51 | processors, middleware and so on. It also includes a function called
52 | `_merge()`, which I’ll get to later.
53 |
54 | * There are modules for each deployment environment. These contain database
55 | settings, `DEBUG` and `TEMPLATE_DEBUG`, `CACHE_BACKEND`, et cetera.
56 |
57 | In order to use a particular settings ‘flavour’, simply set the
58 | `DJANGO_SETTINGS_MODULE` environment variable in the context where you’ll be
59 | running your application. For example, it might be
60 | `myproject.settings.development` for your development environment, and
61 | `myproject.settings.production` in production.
62 |
63 | ### The `_merge()` function
64 |
65 | The only issue with this solution, as it stands, is how to get the settings from
66 | `common.py` into the environment-specific modules. You could try
67 | `from myproject.settings.common import *`, but that ties you into an absolute
68 | import. Instead, add the following function to the bottom of `common.py`:
69 |
70 | #!python
71 | def _merge(local_vars):
72 | local_vars.update((k, v) for k, v in globals().items() if k[0] != '_')
73 |
74 | And at the top of `development.py` (or `production.py`, et cetera):
75 |
76 | #!python
77 | from . import common; common._merge(vars())
78 |
79 | This is essentially a workaround for the fact that relative imports cannot use
80 | `import *`. When `_merge()` calls `globals()`, it gets the dictionary of the
81 | `common.py` namespace, with each variable in the namespace being represented by
82 | a `key => value` pair in the dictionary. At the module level in
83 | `development.py`, `vars()` returns this same dictionary, but for that module
84 | instead. Where it really helps is that both of these dictionaries support
85 | assignment; that is, `vars()['foo'] = "bar"` is perfectly valid and will assign
86 | `"bar"` to the variable `foo`. Hence, `_merge()` now has dictionaries for both
87 | the `common.py` and `development.py` namespaces, and it simply copies over all
88 | the public variables from `common.py` (i.e. those not prefixed with an
89 | underscore) to `development.py`.
90 |
91 | It’s a concept best demonstrated by example. Let’s say `common.py` contained the
92 | following:
93 |
94 | #!python
95 | A = 1
96 | B = 2
97 |
98 | def _merge(local_vars):
99 | local_vars.update((k, v) for k, v in globals().items() if k[0] != '_')
100 |
101 | And `development.py` contained this:
102 |
103 | #!python
104 | from . import common; common._merge(vars())
105 |
106 | C = 3
107 | print (A, B, C)
108 |
109 | Running `python -m settings.development` from the project root will print
110 | `(1, 2, 3)`. The variables `A` and `B` have been copied over into the
111 | `development.py` namespace for you to use as you wish.
112 |
113 | ### Applying the Solution
114 |
115 | All it takes is writing up your `common.py` followed by your
116 | environment-specific modules, and you’re done. You can set the
117 | `DJANGO_SETTINGS_MODULE` environment variable from an `application.wsgi` file, a
118 | virtualenv `activate` script, a web server configuration, or on the command line
119 | (if you’re using `./manage.py`).
120 |
121 | For your information, I used the UNIX
122 | [`tree` command](http://mama.indstate.edu/users/ice/tree/) to generate the file
123 | listings for the example project.
124 |
--------------------------------------------------------------------------------
/content/articles/2010/11/11/sockets-and-nodes-i.md:
--------------------------------------------------------------------------------
1 | ---
2 | created_at: 2010-11-11
3 | kind: article
4 | title: "Sockets and Nodes—An Experiment, Part I"
5 | ---
6 |
7 | In anticipation of [Full Frontal][ff] tomorrow, I decided to play around with
8 | two hot new tools, [node.js][] and [socket.io][], in putting together a useful
9 | ‘toy’ application. Over a few blog posts, I’ll demonstrate how to build a
10 | lightweight app to track live Twitter feeds, starting with something incredibly
11 | basic and adding features incrementally.
12 |
13 | [ff]: http://2010.full-frontal.org
14 | [node.js]: http://nodejs.org/
15 | [socket.io]: http://socket.io/
16 |
17 |
18 | ## Requirements
19 |
20 | Start by installing [node.js][], `npm` (the node package manager), and a
21 | couple of JS libraries. Using [homebrew][]:
22 |
23 | [homebrew]: http://mxcl.github.com/homebrew/
24 |
25 | #!bash
26 | brew install node npm
27 | # Add NODE_PATH to your shell environment and config at the same time!
28 | `echo 'export NODE_PATH=/usr/local/lib/node' | tee -a ~/.zsh_profile`
29 |
30 | npm install socket.io
31 | npm install twitter-node
32 |
33 | Installation instructions for other systems may be found on the internet. I
34 | have faith in your Googling abilities.
35 |
36 |
37 | ## Client-side
38 |
39 | Version zero’s client will consist of a single HTML file; start with a basic
40 | skeleton in a file called `index.html`:
41 |
42 | #!html
43 |
44 |
45 |
46 | BIEBER!!!!1!
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Now for the client JavaScript (which you should add to the empty `