summaryrefslogtreecommitdiff
path: root/posts
diff options
context:
space:
mode:
Diffstat (limited to 'posts')
-rw-r--r--posts/2013-06-15-my-first-foss-contribution.skr39
-rw-r--r--posts/2013-06-30-find-me-on-diaspora.skr57
-rw-r--r--posts/2013-07-15-maine.skr34
-rw-r--r--posts/2013-07-20-stumpwm-on-debian-wheezy.skr62
-rw-r--r--posts/2013-08-07-guile-2d.skr48
-rw-r--r--posts/2013-08-08-angularjs-post-mortem.skr135
-rw-r--r--posts/2013-08-11-the-little-schemer.skr58
-rw-r--r--posts/2013-08-17-font-rendering-pango-cairo.skr328
-rw-r--r--posts/2013-09-22-liberating-a-thinkpad-x220.skr68
-rw-r--r--posts/2013-09-27-guile-2d-0.1.skr31
-rw-r--r--posts/2015-04-10-sxml-html-guile.skr235
-rw-r--r--posts/2015-08-30-ruby-on-guix.skr578
-rw-r--r--posts/angularjs.md135
-rw-r--r--posts/diaspora.md53
-rw-r--r--posts/emacs-required-packages.md62
-rw-r--r--posts/first-foss-contribution.md38
-rw-r--r--posts/first-guile-patch.md150
-rw-r--r--posts/first-guix-package.md43
-rw-r--r--posts/gnu30.md62
-rw-r--r--posts/guile-2d-0.1.md28
-rw-r--r--posts/guile-2d-devlog-1.md44
-rw-r--r--posts/guile-2d-frp.md64
-rw-r--r--posts/guile-2d-is-now-sly.md33
-rw-r--r--posts/guile-2d-live-asset-reload.md51
-rw-r--r--posts/guile-cooperative-repl.md73
-rw-r--r--posts/hello-world.md29
-rw-r--r--posts/little-schemer.md57
-rw-r--r--posts/maine-2013.md41
-rw-r--r--posts/reproducible-development-environments.md120
-rw-r--r--posts/rinari-jasmine.md36
-rw-r--r--posts/ruby-on-guix.md302
-rw-r--r--posts/stumpwm.md74
-rw-r--r--posts/sxml-html-guile.md501
-rw-r--r--posts/thinkpad-x220.md63
34 files changed, 2059 insertions, 1673 deletions
diff --git a/posts/2013-06-15-my-first-foss-contribution.skr b/posts/2013-06-15-my-first-foss-contribution.skr
deleted file mode 100644
index 821bb02..0000000
--- a/posts/2013-06-15-my-first-foss-contribution.skr
+++ /dev/null
@@ -1,39 +0,0 @@
-(post
- :title "My First Real FOSS Contribution"
- :date (make-date* 2013 06 15)
- :tags '("foss" "mediagoblin" "python" "federated" "decentralized" "wsu")
- :summary "I added a small feature!"
-
- (p [I spend a lot of my free time writing code. I usually work on my own
-personal projects that never really go anywhere. So, I decided to take
-a detour from my normal hacking routine and contribute to an existing
-free software project. My contribution was accepted awhile ago now,
-but I wasn’t blogging then so I’m rambling about it now.])
-
- (p [It’s wise to find a project with a low barrier of entry. An
-active IRC channel and/or mailing list with people willing to help
-newcomers is ideal. I remembered hearing about
-,(anchor [GNU MediaGoblin] "http://mediagoblin.org")
-at LibrePlanet 2012, so I decided to check things out. MediaGoblin is
-a media sharing web application written in Python. Their bug tracker
-marks tickets that require little work and don’t require a deep
-understanding of MediaGoblin as “bitesized”.])
-
- (p [I chose to work on
-,(anchor [this ticket] "http://issues.mediagoblin.org/ticket/453")
-because it didn’t require any complicated database migrations or
-knowledge of the media processing code. I added a new configuration
-option, ,(code [allow_comments]), and a small amount of code to enforce
-the setting.])
-
- (p [Eventually, the ticket got reviewed and
-,(anchor [Christopher Webber] "http://dustycloud.org")
-(MediaGoblin’s friendly project leader) merged it: “Heya. Great
-branch, this works perfectly. Merged!”])
-
- (p [It was a very small change, but I was happy to ,(em [finally])
-have some actual code of mine in a real free software project. I have
-a strong passion for free software and the GNU philosophy, so it’s
-really great to participate in the community. My job as a professional
-software developer eats up a lot of my time these days, but I hope to
-find the time to continue hacking and contributing.]))
diff --git a/posts/2013-06-30-find-me-on-diaspora.skr b/posts/2013-06-30-find-me-on-diaspora.skr
deleted file mode 100644
index fc2410d..0000000
--- a/posts/2013-06-30-find-me-on-diaspora.skr
+++ /dev/null
@@ -1,57 +0,0 @@
-(post
- :title "Find Me on Diaspora"
- :date (make-date* 2013 06 30)
- :tags '("foss" "diaspora" "federated" "decentralized" "rails" "wsu")
- :summary "I have started using and contributing to Diaspora."
-
- (p [With all of the recent news about the NSA’s widespread spying, I
-have decided to ween myself off of proprietary, centralized web
-services. Facebook, Google, and other such corporations hold onto
-massive amounts of our data that we’ve willingly given to them via
-status messages, “like” buttons, searches, and emails. Using and
-contributing to free (as in freedom), decentralized (like email) web
-services is a really great way to re-establish control of our data.
-These services rely on many small, interconnected nodes to operate,
-rather than a monolithic website that is under the control of one
-entity. If the distinction between centralized and decentralized
-isn’t clear, consider how email functions. There are many email
-providers to choose from. Users can communicate with others that
-choose to use a different email provider. This is how web services
-should work, but unfortunately very few work this way now.])
-
- (p [The centralized web application that I spend too much time using
-is Facebook. I have knowingly given Facebook a “frontdoor” into my
-life for years now and I’m ready to move on. I think that the concept
-of a “social network” is fun, so I wanted a Facebook replacement.
-Fortunately, there is one:
-,(anchor [Diaspora] "http://diasporaproject.org/").])
-
- (p [Diaspora is a
-,(anchor [free] "https://github.com/diaspora/diaspora"),
-distributed, social networking web application written in Ruby using
-the Rails framework. Diaspora is a community-run project. Its success
-depends upon users, developers, technical writers, user interface
-designers, etc. investing their time and/or money into making it
-better. The Diaspora network is broken up into many servers, known as
-,(anchor [pods] "http://podupti.me").
-Users have the choice of which pod to store their data on.
-Pods assert no ownership over their user’s data, unlike Facebook, and
-do not use that data for targeted advertisements. Diaspora is still a
-rather young project, but it does everything that I need it to
-do. Goodbye, Facebook!])
-
- (p [Since I’m a programmer, I naturally wanted to hack on some code and
-contribute. The main developers are very friendly and give great
-guidance to newcomers that want to help out. Every Monday is a “Bug
-Mash Monday”, where a list of open issues is presented to entice
-contributors to resolve them. In the past few weeks, I have made two
-contributions to the Diaspora project: a
-,(anchor [bug fix] "https://github.com/diaspora/diaspora/issues/2948")
-and a
-,(anchor [small feature] "https://github.com/diaspora/diaspora/issues/2948").
-Diaspora is very hackable and I encourage other developers
-with Ruby/Rails and Javascript knowledge to join in.])
-
- (p [TL\;R: Diaspora is great. Create an account. Check out my
-,(anchor [profile] "https://joindiaspora.com/u/davexunit").
-Start sharing. Happy hacking. :\)]))
diff --git a/posts/2013-07-15-maine.skr b/posts/2013-07-15-maine.skr
deleted file mode 100644
index 0c8e7e9..0000000
--- a/posts/2013-07-15-maine.skr
+++ /dev/null
@@ -1,34 +0,0 @@
-(post
- :title "Maine!"
- :date (make-date* 2013 07 15)
- :tags '("maine" "vacation")
- :summary "My vacation in Maine"
-
- (p [Every summer, my friends and I go to Maine for a week. We stay in
-the Rockland area in an old house on a blueberry field. This year we
-hiked a mountain in Camden, hiked Mt. Katahdin, and went to the
-Rockland Blues Festival.])
-
- (p [Here are some pictures taken from my not-so-great cellphone camera:])
-
- (image/caption "/images/maine-2013/view-from-house.jpg"
- [The view from the driveway of our house.])
-
- (image/caption "/images/maine-2013/zekes-lookout.jpg"
- [Sign at Zeke's Lookout. Somewhere on a mountain near Camden.])
-
- (image/caption "/images/maine-2013/katahdin-camp.jpg"
- [Mt. Katahdin as seen from the foot of the Hunt trail.])
-
- (image/caption "/images/maine-2013/katahdin-waterfall.jpg"
- [A beautiful waterfall that can be seen about a mile
-into the hike.])
-
- (image/caption "/images/maine-2013/katahdin-view-1.jpg" [Katahdin])
- (image/caption "/images/maine-2013/katahdin-view-2.jpg" [Katahdin again])
-
- (image/caption "/images/maine-2013/katahdin-fog.jpg"
- [Thick fog past the treeline.])
-
- (image/caption "/images/maine-2013/blues-festival.jpg"
- [Closed down street packed with people and bands in Rockland.]) )
diff --git a/posts/2013-07-20-stumpwm-on-debian-wheezy.skr b/posts/2013-07-20-stumpwm-on-debian-wheezy.skr
deleted file mode 100644
index 2268872..0000000
--- a/posts/2013-07-20-stumpwm-on-debian-wheezy.skr
+++ /dev/null
@@ -1,62 +0,0 @@
-(post
- :title "StumpWM on Debian Wheezy"
- :date (make-date* 2013 07 20)
- :tags '("stumpwm" "common lisp" "debian" "wheezy" "wsu")
- :summary "First steps with StumpWM on Debian Wheezy"
-
- (p [Everyone that's ever talked to me about software development
-knows that I am in love with Emacs. Emacs has a wonderful keyboard
-driven interface and is almost infinitely customizable via Emacs Lisp.
-I've done a lot of programming in Emacs from my not-so-great laptop
-lately. My laptop has a rather poor 1280x800 resolution and low
-performing integrated graphics chip. Until today, I was running the
-GNOME 3 desktop environment on it. Unlike most people (or perhaps
-just a loud minority), I like GNOME 3. However, I wanted something
-that was both less graphics intensive and more keyboard driven than
-GNOME Shell and Mutter.])
-
- (p [Someone on IRC told me about
-,(anchor [StumpWM] "http://www.nongnu.org/stumpwm/"),
-a window manager written entirely in Common Lisp. I had heard of
-StumpWM before, but back then I wasn't an Emacs user and I've never
-really stuck with any tiling window manager that I've tried (DWM,
-Awesome). Now that I know the power of a fully programmable
-environment thanks to Emacs, I decided to give StumpWM a try. After
-some initial pains trying to get it to run, I am now using it very
-happily.])
-
- (p [Here is what I had to do to get StumpWM running on Debian Wheezy.])
-
- (ol
- (li [Install StumpWM] (source-code "sudo apt-get install stumpwm"))
-
- (li [Create an ,(code [.xinitrc]) file in my home directory with the
-following text]
- (source-code "exec stumpwm"))
-
- (li (p [Workaround clisp "component not found" errors])
- (p [I could not get StumpWM to start until I created the
-following symbolic links:])
- (source-code
- "ln -s /usr/share/common-lisp/source/stumpwm/stumpwm.asd \\
- /usr/share/common-lisp/systems/stumpwm.asd
-ln -s /usr/share/common-lisp/source/cl-ppcre/cl-ppcre.asd \\
- /usr/share/common-lisp/systems/cl-ppcre.asd"))
-
- (li (p [Start the X server])
- (source-code "startx")
- (p [I use the GNOME Desktop Manager, so I also created a session
-file for StumpWM in ,(code [/usr/share/xsessions/stumpwm.desktop.])])
- (source-code
- "[Desktop Entry]
-Encoding=UTF-8
-Name=StumpWM
-Comment=This session logs you into StumpWM
-Exec=stumpwm
-TryExec=stumpwm
-Icon=
-Type=Application")))
-
- (p [I hope this brief installation guide can be of use to one of you
-out there in Internet land. Perhaps in the future I will write an
-article about customizing StumpWM with Common Lisp.]))
diff --git a/posts/2013-08-07-guile-2d.skr b/posts/2013-08-07-guile-2d.skr
deleted file mode 100644
index f612977..0000000
--- a/posts/2013-08-07-guile-2d.skr
+++ /dev/null
@@ -1,48 +0,0 @@
-(post
- :title "guile-2d - A 2D Game Development Framework for GNU Guile"
- :date (make-date* 2013 08 07)
- :tags '("foss" "gnu" "guile" "scheme" "gamedev" "wsu")
- :summary "I have started work on a 2D game framework"
-
- (p [This is the very first devlog entry for my pet project,
-guile-2d. As the title suggests, guile-2d is a 2D game development
-framework for
-,(anchor [GNU Guile] "http://gnu.org/s/guile"),
-a Scheme implementation that has the honor of being the official
-extension language of the GNU project. Guile is a language with a
-growing number of features, but it still lacks a large assortment of
-libraries. I like to do 2D game programming, and I saw a niche that
-needed to be filled. Python has Pygame, Lua has Love, but there's no
-fun and accessible game programming library for Guile. Guile-2d is
-working to correct that.])
-
- (p [The goal of Guile-2d is to create an easy to use 2D game
-programming framework. Libraries like SDL give the programmer a
-rather low-level set of tools that they can use to build a game,
-guile-2d will provide high-level tools over low-level SDL and OpenGL
-for commonly used elements of 2D games: tile maps, sprite animation,
-particle systems, collision detection, vector math, A* pathfinding,
-etc. Such features will allow a game developer to very quickly
-produce a working prototype with guile-2d.])
-
- (p [Guile-2d is a framework, which means that it has some opinion
-about what the right way to do things is. The most apparent example
-of this is the game loop. The game loop runs at 60 frames-per-second
-and uses fixed timestep updates. Those that have read
-,(anchor [Fix Your Timestep]
- "http://gafferongames.com/game-physics/fix-your-timestep/")
-will know that this decision is a good thing.])
-
- (p [Perhaps the most important feature of guile-2d is the ability to
-do “live coding”. When the game loop starts, a REPL
-\(read-eval-print-loop\) server is started. Using the great
-,(anchor [Geiser] "http://geiser.nongnu.org/")
-extension for Emacs to connect to the REPL server, one can modify
-their game as it is running. This gives users the power to evaluate
-some new code and see the changes reflected immediately in the game
-window. No need to restart the game unless you crash it!])
-
- (p [This has been a brief overview of some of the features and goals
-of guile-2d. If this project interests you, you can check out the
-source code on
-,(anchor [Github] "https://github.com/davexunit/guile-2d").]))
diff --git a/posts/2013-08-08-angularjs-post-mortem.skr b/posts/2013-08-08-angularjs-post-mortem.skr
deleted file mode 100644
index 2f7a804..0000000
--- a/posts/2013-08-08-angularjs-post-mortem.skr
+++ /dev/null
@@ -1,135 +0,0 @@
-(post
- :title "AngularJS Post-mortem"
- :date (make-date* 2013 08 08)
- :tags '("web" "javascript" "angularjs" "wsu")
- :summary "AngularJS likes/dislikes and what went right/wrong"
-
- (p [,(anchor [AngularJS] "http://angularjs.org/")
-is the new popular client-side Javascript application framework
-developed by Google. We have recently adopted it at Vista Higher
-Learning for building our latest features that require a lot
-client-side logic. Now that I have a few applications under my belt,
-it’s time to talk about my experience.])
-
- (p [If you want a quick TL\;DR: I think AngularJS is good, but it has
-a steep learning curve and there’s no well defined set of best
-practices.])
-
- (p [Note: I will be using plenty of terms that will probably only
-make sense for people that have used AngularJS.])
-
- (h2 [The Good Stuff])
-
- (p [These are the things that went well. The things that made me glad
-that we chose to use AngularJS.])
-
- (h3 [Easier Testing])
-
- (p [Our Javascript test suite uses
-,(anchor [Jasmine.] "http://pivotal.github.io/jasmine/")
-AngularJS is built with test frameworks like Jasmine in mind.
-AngularJS could tends to be easily testable due to dependency
-injection. When the components of an application don’t rely on global
-state, it is easier to mock services for unit tests.])
-
- (h3 [Separation of Concerns])
-
- (p [AngularJS stresses separating the view from the data structures
-and logic behind it. I think everyone that’s written a somewhat
-complex JQuery application can relate to the mess of CSS selectors and
-click callbacks that the code quickly degenerates into.])
-
- (p [AngularJS allows you to break up the DOM into logical chunks that
-are handled by separate controllers. Treating the application as many
-small pieces working together rather than one giant DOM blob keeps the
-code clean. Message passing via
-,(code [$emit]) and ,(code [$broadcast])
-keeps controllers loosely coupled to each other.])
-
- (h3 [No More JQuery Spaghetti])
-
- (p [Directives, the strategy for encapsulating DOM manipulation, are
-wonderful. It is an elegant solution to the mess that was JQuery
-selectors and event callbacks. AngularJS comes with a lot of
-directives out of the box to handle the most common stuff like
-showing/hiding elements, handling clicks, dynamically setting CSS
-classes.])
-
- (h3 [More Maintainable Code])
-
- (p [AngularJS is feature-rich. It does a lot on your behalf, which
-greatly reduces the amount of boilerplate code needed to get a
-prototype up and running. I had the opportunity to essentially
-rewrite an existing JQuery application using AngularJS. The results
-were clear: The AngularJS version had fewer lines of code, was more
-readable, and was easier to debug.])
-
- (h2 [Bumps in the Road])
-
- (p [These are the things that didn’t go smoothly. They boil down to
-AngularJS having a steep learning curve and ill-informed software
-design decisions on my part.])
-
- (h3 [Understanding the Magic])
-
- (p [A lot of things seem to happen by magic. For example, it is
-possible to make a asynchronous request and get a promise object in
-return. When the promise is assigned to a variable on ,(code [$scope]),
-AngularJS not only knows to ignore it while the request hasn’t
-finished, but it will re-assign to that variable the value of the
-asynchronous call when it completes. It is a nice feature, but it
-takes some digging to find out what is really going on.])
-
- (h3 [Poor Documentation])
-
- (p [I know I’m not the only one that hates the official AngularJS
-documentation. The documentation is getting more complete, but it’s
-still not very useful. Functions frequently have a blurb describing
-what they do, but no explanation of the parameter list. It’s hard to
-use a function that doesn’t describe what it expects for input.])
-
- (p [When the documentation confused us, which it did frequently, we
-turned to the AngularJS book from
-,(anchor [O’Reilly] "http://shop.oreilly.com/product/0636920028055.do")
-for help. I need to get around to reading more of it.])
-
- (h3 [RESTful Resources and Rails])
-
- (p [AngularJS claims to be targeted at CRUD applications, but using
-the HTTP backend and the ,(code [Resource]) abstraction that sits on
-top of it was particularly difficult. A good amount of time was spent
-on trying to get the HTTP requests from resources to conform to what
-our Rails backend expects, like root-wrapping.])
-
- (h3 [Bloated Controllers])
-
- (p [I frequently made controllers that had too much state and logic
-that should have been extracted into services/factories/etc. A
-controller would start out slim but would quickly grow to several
-hundred lines of code as features were added. Controllers should be
-the middle man between the view and the model and that’s it.])
-
- (p [Some tell-tale signs that a controller is becoming bloated:])
-
- (ul
- (li [There are a lot of private functions (not defined on
-,(code [$scope]))])
- (li [Functions are defined on ,(code [$scope]) just so you can
-unit-test them, but are never used in the template]))
-
- (p [I attribute controller bloat to a lack of knowing the appropriate uses
-for other AngularJS components. It was easy to just keep adding to the
-controller.])
-
- (h2 [Conclusion])
-
- (p [Overall, I think things went well, but I (and the rest of my
-team) made a lot of beginner mistakes. But that’s the learning
-process, isn’t it?])
-
- (p [Now that I know more about AngularJS, it will be easier to make
-better design decisions moving forward.])
-
- (p [I believe that as AngularJS continues to mature, some concensus
-in the community about best practices will emerge that will make
-things easier for beginners.]))
diff --git a/posts/2013-08-11-the-little-schemer.skr b/posts/2013-08-11-the-little-schemer.skr
deleted file mode 100644
index d4e4211..0000000
--- a/posts/2013-08-11-the-little-schemer.skr
+++ /dev/null
@@ -1,58 +0,0 @@
-(post
- :title "The Little Schemer"
- :date (make-date* 2013 08 11)
- :tags '("scheme" "books" "wsu")
- :summary "I bought “The Little Schemer”"
-
- (p [Yesterday, I took a trip to the MIT Press Bookstore and picked up
-a copy of
-,(anchor [The Little Schemer] "http://mitpress.mit.edu/books/little-schemer").
-I’ve only spent a few hours reading and coding along with it, but I’ve
-had a lot of fun. The following is a mini-review based on my
-experience thus far.])
-
- (p [“The Little Schemer” teaches you to think recursively using an
-interesting and comedic writing style and the Scheme programming
-language. While Scheme is the language of choice, the real goal is to
-teach you problem solving rather than the details of a specific
-language. The book starts off simple, explaining what atoms, lists,
-and s-expressions are. Rather than providing the definition and then
-showing examples, it first gives examples in the form of a question
-and answer.])
-
- (p [Example:])
-
- (blockquote
- (p [Is it true that this an atom?])
- (p (strong [atom]))
- (p [Yes, because ,(strong [atom]) is a string of characters
-beginning with a letter.]))
-
- (p [From the examples given, a definition is created. In later
-examples, a Scheme procedure is written that produces the correct
-answers for all of the questions stated before it. It’s fun to build
-the procedure, verify that it works for all cases, and compare your
-implementation with the book’s.])
-
- (p [“The Little Schemer” defines ten commandments that are essential
-to correctly solving the problems in the book. Some commandments are
-first given in an incomplete form, and expanded later when a greater
-level of understanding has been achieved. The problems that you solve
-reinforce the commandments. You might notice that you start writing
-procedures without thinking much about it, much like the muscle memory
-earned from using Emacs a lot. Gerald J. Sussman was right when he
-said that this book “can perform the same service that Hanon’s finger
-exercises or Czerny’s piano studies perform for the student of the
-piano.” I have no idea who Hanon and Czerny are, but I get it. For
-the drummers out there, you could liken this book to
-,(anchor [Stick Control]
- "http://www.amazon.com/Stick-Control-For-Snare-Drummer/dp/1892764040").])
-
- (p [The writing style is very informal, comedic, and food themed.
-Page 13 has a space reserved for jelly stains, and page 52 tells you
-to “go cons a piece of cake onto your mouth.” I have laughed a number
-of times while reading. Oh, and let’s not forget about the cute
-elephant drawings. This is definitely not your average boring, dry
-computer science book. If you are interested in a unique and
-enjoyable learning experience, then I highly recommend reading “The
-Little Schemer”.]))
diff --git a/posts/2013-08-17-font-rendering-pango-cairo.skr b/posts/2013-08-17-font-rendering-pango-cairo.skr
deleted file mode 100644
index 59e0f96..0000000
--- a/posts/2013-08-17-font-rendering-pango-cairo.skr
+++ /dev/null
@@ -1,328 +0,0 @@
-(define guile-2d-url "https://github.com/davexunit/guile-2d")
-(define madsy9-url "http://www.reddit.com/user/Madsy9")
-(define madsy9-comment-url
- "http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk")
-(define pango-url "http://www.pango.org/")
-(define cairo-url "http://cairographics.org/")
-;; TODO: Move to files.dthompson.us
-(define tarball-url "/src/pangocairo.tar.gz")
-
-(post
- :title "Font Rendering in OpenGL with Pango and Cairo"
- :date (make-date* 2013 08 17)
- :tags '("opengl" "pango" "cairo" "font" "wsu")
- :summary "A brief tutorial for rendering fonts in OpenGL with libpangocairo"
-
- (p [I am working towards a 0.1 release of my game development
- framework for GNU Guile, ,(anchor "guile-2d" guile-2d-url). One of
- the few remaining blockers on my to-do list is font rendering. A
- reddit user, ,(anchor "Madsy9" madsy9-url), pointed me in the right
- direction with this ,(anchor "comment" madsy9-comment-url). There are
- two libraries needed to perform nice font rendering with proper
- internationalization support: ,(anchor "Pango" pango-url), “a library
- for laying out and rendering of text, with an emphasis on
- internationalization,” and ,(anchor "Cairo" cairo-url), “a 2D graphics
- library with support for multiple output devices.”])
-
- (p [It took me awhile to put together all of the pieces and build a
- working sample program. The goal of this post is to help others that
- may be trying to accomplish a similar task that have no prior
- knowledge of Pango and Cairo. I will assume basic knowledge of C,
- SDL, and OpenGL throughout this post.])
-
- (p [Let's get the basic SDL and OpenGL initialization out of the way:])
-
- (source-code
- (c-source
- "#include <pango/pangocairo.h>
-#include <SDL.h>
-#include <SDL_opengl.h>
-
-#define WINDOW_WIDTH 800
-#define WINDOW_HEIGHT 600
-#define FONT \"Sans Bold 18\"
-#define TEXT \"The quick brown fox is so かわいい!\"
-
-void
-init_sdl ()
-{
- SDL_Init (SDL_INIT_EVERYTHING);
- SDL_SetVideoMode (WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_OPENGL);
-}
-
-void
-init_gl ()
-{
- glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
- glDisable (GL_DEPTH_TEST);
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable (GL_TEXTURE_2D);
- glViewport (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- glOrtho (0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, -1, 1);
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity ();
-}"))
-
- (p [,(code "create_texture") simply creates an OpenGL texture given an array of
- pixel data and the texture dimensions. Our Cairo surface will use BGRA
- color.])
-
- (source-code
- (c-source
- "unsigned int
-create_texture (unsigned int width,
- unsigned int height,
- unsigned char *pixels)
-{
- unsigned int texture_id;
-
- glGenTextures (1, &texture_id);
- glBindTexture (GL_TEXTURE_2D, texture_id);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D (GL_TEXTURE_2D,
- 0,
- GL_RGBA,
- width,
- height,
- 0,
- GL_BGRA,
- GL_UNSIGNED_BYTE,
- pixels);
-
- return texture_id;
-}"))
-
- (p [,(code "draw_texture") clears the screen, renders a simple textured quad
- using OpenGL’s immediate mode, and then swaps buffers.])
-
- (source-code
- (c-source
- "void
-draw_texture (int width,
- int height,
- unsigned int texture_id)
-{
- /* Render a texture in immediate mode. */
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity ();
- glClear (GL_COLOR_BUFFER_BIT);
- glPushMatrix ();
- glBindTexture (GL_TEXTURE_2D, texture_id);
- glColor3f (1.f, 1.0f, 1.0f);
-
- glBegin (GL_QUADS);
- glTexCoord2f (0.0f, 0.0f);
- glVertex2f (0.0f, 0.0f);
- glTexCoord2f (1.0f, 0.0f);
- glVertex2f (width, 0.0f);
- glTexCoord2f (1.0f, 1.0f);
- glVertex2f (width , height);
- glTexCoord2f (0.0f, 1.0f);
- glVertex2f (0.0f, height);
- glEnd ();
-
- glPopMatrix ();
- SDL_GL_SwapBuffers();
-}"))
-
- (p [,(code [create_cairo_context]) is used to make a new Cairo context that
-draws to a raw data surface. The return value, a ,(code [cairo_t]), is the
-main object in Cairo. All drawing is done via a ,(code [cairo_t]) object. A
-context needs a surface to draw on.
-,(code [cairo_image_surface_create_for_data]) creates a raw data surface for
-us. We will be translating the surface into a texture later on.])
-
- (source-code
- (c-source
- "cairo_t*
-create_cairo_context (int width,
- int height,
- int channels,
- cairo_surface_t** surf,
- unsigned char** buffer)
-{
- *buffer = calloc (channels * width * height, sizeof (unsigned char));
- *surf = cairo_image_surface_create_for_data (*buffer,
- CAIRO_FORMAT_ARGB32,
- width,
- height,
- channels * width);
- return cairo_create (*surf);
-}"))
-
- (p [,(code [create_layout_context]) also makes a new Cairo context, but this
-context is for PangoLayout objects. In Pango, a layout describes the
-style of a paragraph of text. The layout needs a context in order to
-function. We use ,(code [cairo_image_surface_create]) with dimensions of 0x0
-because we won’t actually be rendering to this surface. Instead, we
-will layout our text and use ,(code [create_cairo_context]) to build a
-context with a surface that is the size of the rendered text. Cairo
-uses reference counting for dynamically allocated objects, so we need
-to call ,(code [cairo_surface_destroy]) when we’re done with the temporary
-surface. The context still maintains a reference to the surface, so
-the memory for the surface will not be freed until the context is.])
-
- (source-code
- (c-source
- "cairo_t*
-create_layout_context ()
-{
- cairo_surface_t *temp_surface;
- cairo_t *context;
-
- temp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
- context = cairo_create (temp_surface);
- cairo_surface_destroy (temp_surface);
-
- return context;
-}"))
-
- (p [,(code [get_text_size]) tells us the size of the text that’s in the layout,
-in pixels. Pango’s units are not in pixels, so we must divide by
-,(code [PANGO_SCALE]) in order to get pixel units.])
-
- (source-code
- (c-source
- "void
-get_text_size (PangoLayout *layout,
- unsigned int *width,
- unsigned int *height)
-{
- pango_layout_get_size (layout, width, height);
- /* Divide by pango scale to get dimensions in pixels. */
- *width /= PANGO_SCALE;
- *height /= PANGO_SCALE;
-}"))
-
- (p [,(code [render_text]) is where all of the magic happens. First, we create a
-layout with a layout context and set the text that we will render with
-this layout. ,(code [TEXT]) is defined earlier in the program as “The quick
-brown fox is so かわいい!”])
-
- (p [Then we create a ,(code [PangoFontDescription]) object. This object
-represents the font that we want to render. Earlier in the program,
-,(code [FONT]) is defined as “Sans Bold 18”. Pango is able to figure out
-how to load a font from a string in this format. Your system must be
-able to recognize the font family and font face, though. I haven’t
-yet figured out how to have Pango render an arbitrary font from a
-,(code [*.ttf]) file.])
-
- (p [Next, we create a rendering context by getting the layout’s size and
-creating a context with a surface big enough to show all of the
-rendered text.])
-
- (p [Finally, we set the font color to white, render the text to the
-surface with ,(code [pango_cairo_show_layout]), and create an OpenGL texture
-from the surface. We also clean up all the objects that we no longer
-need before returning.])
-
- (source-code
- (c-source
- "unsigned int
-render_text (const char *text,
- unsigned int *text_width,
- unsigned int *text_height,
- unsigned int *texture_id)
-{
- cairo_t *layout_context;
- cairo_t *render_context;
- cairo_surface_t *temp_surface;
- cairo_surface_t *surface;
- unsigned char* surface_data = NULL;
- PangoFontDescription *desc;
- PangoLayout *layout;
-
- layout_context = create_layout_context ();
-
- /* Create a PangoLayout, set the font and text */
- layout = pango_cairo_create_layout (layout_context);
- pango_layout_set_text (layout, text, -1);
-
- /* Load the font */
- desc = pango_font_description_from_string (FONT);
- pango_layout_set_font_description (layout, desc);
- pango_font_description_free (desc);
-
- /* Get text dimensions and create a context to render to */
- get_text_size (layout, text_width, text_height);
- render_context = create_cairo_context (*text_width,
- *text_height,
- 4,
- &surface,
- &surface_data);
-
- /* Render */
- cairo_set_source_rgba (render_context, 1, 1, 1, 1);
- pango_cairo_show_layout (render_context, layout);
- *texture_id = create_texture(*text_width, *text_height, surface_data);
-
- /* Clean up */
- free (surface_data);
- g_object_unref (layout);
- cairo_destroy (layout_context);
- cairo_destroy (render_context);
- cairo_surface_destroy (surface);
-}"))
-
- (p [,(code [main]) is pretty simple. We initialize SDL and OpenGL, render text
-to a texture, and enter the rendering loop. The program will run
-until you click the close button, press “enter”, or press “q”.])
-
- (source-code
- (c-source
- "int main (int argc, char **argv)
-{
- SDL_Event event;
- int keep_running = 1;
- unsigned int texture_id;
- unsigned int text_width = 0;
- unsigned int text_height = 0;
-
- init_sdl ();
- init_gl ();
- render_text(TEXT,
- &texture_id,
- &text_width,
- &text_height);
-
- /* Update/render loop */
- while (keep_running) {
- SDL_PollEvent (&event);
-
- switch (event.type) {
- case SDL_QUIT :
- keep_running = 0;
- break;
-
- case SDL_KEYDOWN :
- if (event.key.keysym.sym == SDLK_ESCAPE)
- keep_running = 0;
- if (event.key.keysym.sym == SDLK_q)
- keep_running = 0;
- break;
- }
-
- draw_texture (texture_id, text_width, text_height);
- SDL_Delay (16);
- }
-
- /* Clean up */
- glDeleteTextures (1, &texture_id);
-
- SDL_Quit();
-
- return 0;
-}"))
-
- (p [And we’re done! You should now be able to render some text in an
-OpenGL context. I hope this brief tutorial was helpful. Font rendering
-isn’t easy, and it’s not really my area of interest. I’m glad that
-Pango exists to do all of the real work for me so that I can more
-quickly move on to the parts of graphics programming that I actually
-enjoy.])
-
- (p [You can download the full source code ,(anchor [here] tarball-url).]))
diff --git a/posts/2013-09-22-liberating-a-thinkpad-x220.skr b/posts/2013-09-22-liberating-a-thinkpad-x220.skr
deleted file mode 100644
index a527482..0000000
--- a/posts/2013-09-22-liberating-a-thinkpad-x220.skr
+++ /dev/null
@@ -1,68 +0,0 @@
-(post
- :title "Liberating a Thinkpad X220"
- :date (make-date* 2013 09 22)
- :tags '("thinkpad" "free software" "wsu")
- :summary "I bought a used Thinkpad X220 and made it more free
-software friendly."
-
- (p [I had been looking for a suitable replacement to my old, slow
-Compaq laptop that I purchased during my freshman year of college when
-I had very little money. What I liked about my old laptop was that it
-played well with free software. I had no trouble getting all of my
-hardware to work out-of-the-box with fully free GNU/Linux
-distributions such as Trisquel, and I wanted any future laptops of
-mine to play nicely, too.])
-
- (p [I have heard much praise for Thinkpads over the years. Solid
-build quality, utilitarian design, and cheap to buy used. However,
-upon further reading, I realized that most newer Thinkpads require
-nonfree software in order to the drive the Intel wireless chip.
-Furthermore, there was DRM present in the BIOS that would prevent the
-installation of PCIe wireless chips that weren't in the whitelist.])
-
- (p [This really bummed me out, but I bought a Thinkpad anyway. I
-found a great deal on a used X220 on ebay for $400. In order to
-liberate it, I had to make a small deal with the devil: Use the
-pre-installed Windows 7 to flash a hacked BIOS that removes the
-whitelist. I could only find the needed BIOS as a Windows executable,
-so I didn't have much choice. This process left me hoping that
-coreboot gains wider adoption.])
-
- (p [Once I had verified that I didn't brick my Thinkpad, I installed
-the new wireless card. I purchased a Wireless N, half-height, mini
-PCIe card from
-,(anchor [Thinkpenguin]
- "https://www.thinkpenguin.com/gnu-linux/penguin-wireless-n-half-height-mini-pcie-card").
-It uses an Atheros chipset and is free software compatible. I met
-Chris, the owner of Thinkpenguin, at this year's Northeast GNU/Linux
-Fest at Harvard. He is doing some great work and I wanted to support
-his business. It was nice to buy from someone who could assure me
-that the hardware I purchased is fully supported on a libre GNU/Linux
-distribution.])
-
- (p [Now that my Thinkpad was free (sans BIOS, of course), it was time
-for the final touch. I replaced the hard drive with a 128GB SSD and
-installed Debian testing. It takes roughly 9 seconds to get from GRUB
-to the GDM login screen. It feels very nice to have a device that
-boots so quickly.])
-
- (p [Now that everything had been installed and configured, I was able
-to start hacking and get a feel for things. The keyboard is the
-nicest I've ever used on a laptop. The
-,(anchor [TrackPoint] "http://xkcd.com/243/")
-is quite a nice way to move around once you get used to it. The
-ThinkLight is pretty neat when you're in a dark area. The battery
-life is extremely impressive. I don't know exactly how long it lasts
-yet but I never have to charge it while I am using it. I was lucky if
-I got 2 hours of battery life out of my old laptop, which caused me to
-be constantly tethered to an AC adapter. The screen is matte, which
-is awesome because it's very difficult to use a laptop outdoors when
-the screen is glossy. 1366x768 is not an ideal resolution (16:9 be
-damned), but I can live with it on a 12.5\" screen. Last but not
-least, I honestly like the aesthetics. A lot of people are enamored
-with the brushed aluminum designs by that fruit company, but I love
-the flat black, functional design of the Thinkpad.])
-
- (p [I hope to really break this thing in over the weekend at the
-,(anchor [GNU 30th Anniversary] "https://www.gnu.org/gnu30/")
-hackathon. :\)]))
diff --git a/posts/2013-09-27-guile-2d-0.1.skr b/posts/2013-09-27-guile-2d-0.1.skr
deleted file mode 100644
index 0a5f01f..0000000
--- a/posts/2013-09-27-guile-2d-0.1.skr
+++ /dev/null
@@ -1,31 +0,0 @@
-(post
- :title "Guile-2D 0.1 Released"
- :date (make-date* 2013 09 27)
- :tags '("foss" "gnu" "guile" "scheme" "gamedev" "wsu")
- :summary "Official release of Guile-2D Version 0.1"
-
- (p [To celebrate the GNU Project's 30th anniversary, I have decided
-to make the very first release of my 2D game development framework for
-,(anchor [GNU Guile] "http://gnu.org/software/guile").
-GNU Guile is a Scheme implementation, and has the honor of being the
-official extension language of the GNU project. Guile-2D is a layer
-above SDL, OpenGL, FreeImage, and FTGL that provides abstractions for
-common 2D game programming requirements such as sprites, tilesets,
-animations, scripting, and collision detection.])
-
- (p [There is a lot of work to do in order to get Guile-2D up to snuff
-with the game libraries for more popular languages like Python and
-Lua. I am looking for contributors who share my vision of creating a
-fully featured, easy to use game library in Scheme.])
-
- (p [Guile-2D currently supports GNU/Linux distributions. I am
-looking for help to get it running on OS X and Windows.])
-
- (p [Please refer to the ,(code [INSTALL.org]), ,(code [README.org]),
-and texinfo files to learn how to install Guile-2D, run example
-programs, and write your own games.])
-
- ;; TODO: Move to files.dthompson.us
- (p (anchor [Download the release tarball] "/src/guile-2d-0.1.tar.gz"))
- (p (anchor [Browse the source code on GitHub]
- "https://github.com/davexunit/guile-2d")))
diff --git a/posts/2015-04-10-sxml-html-guile.skr b/posts/2015-04-10-sxml-html-guile.skr
deleted file mode 100644
index beea865..0000000
--- a/posts/2015-04-10-sxml-html-guile.skr
+++ /dev/null
@@ -1,235 +0,0 @@
-(post
- :title "Rendering HTML with SXML and GNU Guile"
- :date (make-date* 2015 04 10)
- :tags '("gnu" "guile" "wsu")
- :summary "With a little effort, SXML can be used for HTML templates"
-
- (p [GNU Guile provides modules for working with XML documents called
- SXML. SXML provides an elegant way of writing XML documents as
- s-expressions that can be easily manipulated in Scheme. Here’s an
- example:])
-
- (source-code
- (scheme-source
- "(sxml->xml '(foo (bar (@ (attr \"something\")))))"))
-
- (source-code
- (xml-source
- "<foo><bar attr=\"something\" /></foo>"))
-
- (p [I don’t know about you, but I work with HTML documents much more
- often than XML. Since HTML is very similar to XML, we should be able
- to represent it with SXML, too!])
-
- (source-code
- (scheme-source
- "(sxml->xml
- '(html
- (head
- (title \"Hello, world!\")
- (script (@ (src \"foo.js\"))))
- (body
- (h1 \"Hello!\"))))"))
-
- (source-code
- (xml-source
- "<html>
- <head>
- <title>Hello, world!</title>
- <script src=\"foo.js\" /> <!-- what? -->
- </head>
- <body>
- <h1>Hello!</h1>
- </body>
-</html>"))
-
- (p [That ,(code [<script>]) tag doesn’t look right! Script tags
- don’t close themselves like that. Well, we could hack around it:])
-
- (source-code
- (scheme-source
- "(sxml->xml
- '(html
- (head
- (title \"Hello, world!\")
- (script (@ (src \"foo.js\")) \"\"))
- (body
- (h1 \"Hello!\"))))"))
-
- (source-code
- (xml-source
- "<html>
- <head>
- <title>Hello, world!</title>
- <script src=\"foo.js\"></script>
- </head>
- <body>
- <h1>Hello!</h1>
- </body>
-</html>"))
-
- (p [There’s no bug. The improper rendering happens because HTML, while
- similar to XML, has some different syntax rules. Instead of using
- ,(code [sxml->xml]) a new procedure that is tailored to the HTML syntax
- is needed. Introducing ,(code [sxml->html]):])
-
- (source-code
- (scheme-source
- "(define* (sxml->html tree #:optional (port (current-output-port)))
- \"Write the serialized HTML form of TREE to PORT.\"
- (match tree
- (() *unspecified*)
- (('doctype type)
- (doctype->html type port))
- (((? symbol? tag) ('@ attrs ...) body ...)
- (element->html tag attrs body port))
- (((? symbol? tag) body ...)
- (element->html tag '() body port))
- ((nodes ...)
- (for-each (cut sxml->html <> port) nodes))
- ((? string? text)
- (string->escaped-html text port))
- ;; Render arbitrary Scheme objects, too.
- (obj (object->escaped-html obj port))))"))
-
- (p [In addition to being aware of void elements and escape
-characters, it can also render ,(code ['(doctype "html")]) as
-,(code [<!DOCTYPE html>]). If we replace ,(code [sxml->xml])
-with ,(code [sxml->html]) in the failing example above we can see
-that it does the right thing.])
-
- (source-code
- (scheme-source
- "(sxml->html
- '((script (@ (src \"foo.js\")))
- \"Copyright © 2015 David Thompson <davet@gnu.org>\"))"))
-
- (source-code
- (xml-source
- "<script src=\"foo.js\"></script>
-Copyright © 2015 David Thompson &lt;davet@gnu.org&gt;"))
-
- (p [Here’s the full version of my ,(code "(sxml html)") module. It’s
-quite brief, which is a nice bonus. This code requires Guile 2.0.11
-or greater.])
-
- (p [Happy hacking!])
-
- (source-code
- (scheme-source
- ";;; Copyright © 2015 David Thompson <davet@gnu.org>
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 3 of
-;;; the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library. If not, see
-;;; <http://www.gnu.org/licenses/>.
-
-(define-module (sxml html)
- #:use-module (sxml simple)
- #:use-module (srfi srfi-26)
- #:use-module (ice-9 match)
- #:use-module (ice-9 format)
- #:use-module (ice-9 hash-table)
- #:export (sxml->html))
-
-(define %void-elements
- '(area
- base
- br
- col
- command
- embed
- hr
- img
- input
- keygen
- link
- meta
- param
- source
- track
- wbr))
-
-(define (void-element? tag)
- \"Return #t if TAG is a void element.\"
- (pair? (memq tag %void-elements)))
-
-(define %escape-chars
- (alist->hash-table
- '((#\\\" . \"quot\")
- (#\\& . \"amp\")
- (#\\' . \"apos\")
- (#\\< . \"lt\")
- (#\\> . \"gt\"))))
-
-(define (string->escaped-html s port)
- \"Write the HTML escaped form of S to PORT.\"
- (define (escape c)
- (let ((escaped (hash-ref %escape-chars c)))
- (if escaped
- (format port \"&~a;\" escaped)
- (display c port))))
- (string-for-each escape s))
-
-(define (object->escaped-html obj port)
- \"Write the HTML escaped form of OBJ to PORT.\"
- (string->escaped-html
- (call-with-output-string (cut display obj <>))
- port))
-
-(define (attribute-value->html value port)
- \"Write the HTML escaped form of VALUE to PORT.\"
- (if (string? value)
- (string->escaped-html value port)
- (object->escaped-html value port)))
-
-(define (attribute->html attr value port)
- \"Write ATTR and VALUE to PORT.\"
- (format port \"~a=\\\"\" attr)
- (attribute-value->html value port)
- (display #\\\" port))
-
-(define (element->html tag attrs body port)
- \"Write the HTML TAG to PORT, where TAG has the attributes in the
-list ATTRS and the child nodes in BODY.\"
- (format port \"<~a\" tag)
- (for-each (match-lambda
- ((attr value)
- (display #\\space port)
- (attribute->html attr value port)))
- attrs)
- (if (and (null? body) (void-element? tag))
- (display \" />\" port)
- (begin
- (display #\\> port)
- (for-each (cut sxml->html <> port) body)
- (format port \"</~a>\" tag))))
-
-(define (doctype->html doctype port)
- (format port \"<!DOCTYPE ~a>\" doctype))
-
-(define* (sxml->html tree #:optional (port (current-output-port)))
- \"Write the serialized HTML form of TREE to PORT.\"
- (match tree
- (() *unspecified*)
- (('doctype type)
- (doctype->html type port))
- (((? symbol? tag) ('@ attrs ...) body ...)
- (element->html tag attrs body port))
- (((? symbol? tag) body ...)
- (element->html tag '() body port))
- ((nodes ...)
- (for-each (cut sxml->html <> port) nodes))
- ((? string? text)
- (string->escaped-html text port))
- ;; Render arbitrary Scheme objects, too.
- (obj (object->escaped-html obj port))))")))
diff --git a/posts/2015-08-30-ruby-on-guix.skr b/posts/2015-08-30-ruby-on-guix.skr
deleted file mode 100644
index 5da4ab4..0000000
--- a/posts/2015-08-30-ruby-on-guix.skr
+++ /dev/null
@@ -1,578 +0,0 @@
-(post
- :title "Ruby on Guix"
- :date (make-date* 2015 08 30)
- :tags '("gnu" "guix" "scheme" "guile" "ruby" "wsu")
- :summary "How to use Guix + some elbow grease to replace RVM and
-Bundler on GNU/Linux"
-
- (p [I’ve been working with Ruby professionally for over 3 years now
-and I’ve grown frustrated with two of its most popular development
-tools: RVM and Bundler. For those that may not know, RVM is the Ruby
-version manager and it allows unprivileged users to download, compile,
-install, and manage many versions of Ruby instead of being stuck with
-the one that is installed globally by your distro’s package manager.
-Bundler is the tool that allows developers to keep a version
-controlled “Gemfile” that specifies all of the project’s dependencies
-and provides utilities to install and update those gems. These tools
-are crucial because Ruby developers often work with many applications
-that use different versions of Ruby and/or different versions of gems
-such as Rails. Traditional GNU/Linux distributions install packages
-to the global ,(code [/usr]) directory, limiting users to a single
-version of Ruby and associated gems, if they are packaged at all.
-Traditional package management fails to meet the needs of a lot of
-users, so many niche package managers have been developed to
-supplement them.])
-
- (source-code
- (scheme-source
- ";;; guile-syntax-highlight --- General-purpose syntax highlighter
-;;; Copyright © 2015 David Thompson <davet@gnu.org>
-;;;
-;;; Guile-syntax-highlight is free software; you can redistribute it
-;;; and/or modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; Guile-syntax-highlight is distributed in the hope that it will be
-;;; useful, but WITHOUT ANY WARRANTY; without even the implied
-;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-;;; See the GNU Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with guile-syntax-highlight. If not, see
-;;; <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-;;
-;; Lexing utilities.
-;;
-;;; Code:
-
-(define-module (syntax-highlight lexers)
- #:use-module (ice-9 match)
- #:use-module (ice-9 regex)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-26)
- #:export (make-cursor
- cursor?
- cursor-text
- cursor-position
- cursor-end?
- move-cursor
- move-cursor-by
-
- lex-fail
- lex-bind
- lex-return
- lex-lift
- lex-map
- lex-filter
- lex-any*
- lex-any
- lex-all*
- lex-all
- lex-consume
- lex-regexp
- lex-string
- lex-char-set
- lex-delimited
- lex-tag))
-
-(define (string-prefix?* s1 s2 start-s2)
- (string-prefix? s1 s2 0 (string-length s1) start-s2))
-
-
-;;;
-;;; Cursor
-;;;
-
-(define-record-type <cursor>
- (make-cursor text position)
- cursor?
- (text cursor-text)
- (position cursor-position))
-
-(define (cursor-end? cursor)
- \"Return #t if the cursor is at the end of the text.\"
- (>= (cursor-position cursor) (string-length (cursor-text cursor))))
-
-(define (move-cursor cursor position)
- \"Move CURSOR to the character at POSITION.\"
- (make-cursor (cursor-text cursor) position))
-
-(define (move-cursor-by cursor offset)
- \"Move CURSOR by OFFSET characters relative to its current
-position.\"
- (move-cursor cursor (+ (cursor-position cursor) offset)))
-
-
-;;;
-;;; Lexers
-;;;
-
-(define (lex-fail cursor)
- \"Always fail to lex STR without consuming any of it.\"
- (values #f cursor))
-
-(define (lex-bind proc lexer)
- \"Return a lexer that applies the result of LEXER to PROC, a
-procedure that returns a lexer, and then applies that new lexer.\"
- (lambda (cursor)
- (let-values (((result remainder) (lexer cursor)))
- (if result
- ((proc result) remainder)
- (lex-fail cursor)))))
-
-(define (lex-return x)
- \"Return a lexer that always yields X as the lex result.\"
- (lambda (cursor)
- (values x cursor)))
-
-(define (lex-lift proc)
- \"Return a procedure that wraps the result of PROC in a lexer.\"
- (lambda args
- (lex-return (apply proc args))))
-
-(define (lex-map proc lexer)
- \"Return a lexer that applies PROC to result of LEXER.\"
- (lex-bind (lex-lift proc) lexer))
-
-(define (lex-any* lexers)
- \"Return a lexer that succeeds with the result of the first
-successful lexer in LEXERS or fails if all lexers fail.\"
- (define (either a b)
- (lambda (cursor)
- (let-values (((result remainder) (a cursor)))
- (if result
- (values result remainder)
- (b cursor)))))
-
- (fold-right either lex-fail lexers))
-
-(define (lex-any . lexers)
- \"Return a lexer that succeeds with the result of the first
-successful lexer in LEXERS or fails if all lexers fail.\"
- (lex-any* lexers))
-
-(define (lex-all* lexers)
- \"Return a lexer that succeeds with the results of all LEXERS in
-order, or fails if any lexer fails.\"
- (define (both a b)
- (lambda (cursor)
- (let-values (((result-a remainder-a) (a cursor)))
- (if result-a
- (let-values (((result-b remainder-b) (b remainder-a)))
- (if result-b
- (values (cons result-a result-b) remainder-b)
- (lex-fail cursor)))
- (lex-fail cursor)))))
-
- (fold-right both (lex-return '()) lexers))
-
-(define (lex-all . lexers)
- \"Return a lexer that succeeds with the results of all LEXERS in
-order, or fails if any lexer fails.\"
- (lex-all* lexers))
-
-(define (lex-consume lexer)
- \"Return a lexer that always succeeds with a list of as many
-consecutive successful applications of LEXER as possible, consuming
-the entire input text. Sections of text that could not be lexed are
-returned as plain strings.\"
- (define (substring* cursor start)
- (substring (cursor-text cursor) start (cursor-position cursor)))
-
- (lambda (cursor)
- (let loop ((cursor cursor)
- (memo '())
- (fail-start #f))
- (if (cursor-end? cursor)
- (values (reverse memo) cursor)
- (let-values (((result remainder) (lexer cursor)))
- (cond
- ;; Regular successful result.
- ((and result (not fail-start))
- (loop remainder (cons result memo) #f))
- ;; Successful result after some amount of unmatched
- ;; characters.
- ((and result fail-start)
- (loop remainder
- (cons* result (substring* cursor fail-start) memo)
- #f))
- ;; Consecutive failure.
- (fail-start
- (loop (move-cursor-by cursor 1)
- memo
- fail-start))
- ;; First failure.
- (else
- (loop (move-cursor-by cursor 1)
- memo
- (cursor-position cursor)))))))))
-
-(define (lex-regexp pattern)
- \"Return a lexer that succeeds with the matched substring when the
-input matches the string PATTERN.\"
- (let ((rx (make-regexp (string-append \"^\" pattern))))
- (lambda (cursor)
- (if (cursor-end? cursor)
- (lex-fail cursor)
- (let ((result (regexp-exec rx (cursor-text cursor)
- (cursor-position cursor))))
- (if result
- (let ((str (match:substring result 0)))
- (values str (move-cursor-by cursor (string-length str))))
- (lex-fail cursor)))))))
-
-(define (lex-string str)
- \"Return a lexer that succeeds with STR when the input starts with
-STR.\"
- (lambda (cursor)
- (if (string-prefix?* str (cursor-text cursor) (cursor-position cursor))
- (values str (move-cursor-by cursor (string-length str)))
- (lex-fail cursor))))
-
-(define (lex-char-set char-set)
- \"Return a lexer that succeeds with the nonempty input prefix that
-matches CHAR-SET, or fails if the first input character does not
-belong to CHAR-SET.\"
- (define (char-set-substring str start)
- (let ((len (string-length str)))
- (let loop ((index start))
- (cond
- ((>= index len)
- (substring str start len))
- ((char-set-contains? char-set (string-ref str index))
- (loop (1+ index)))
- (else
- (substring str start index))))))
-
- (lambda (cursor)
- (match (char-set-substring (cursor-text cursor) (cursor-position cursor))
- (\"\" (lex-fail cursor))
- (str (values str (move-cursor-by cursor (string-length str)))))))
-
-(define* (lex-delimited open #:key (until open) (escape #\\) nested?)
- \"Return a lexer that succeeds with the string delimited by the
-opening string OPEN and the closing string UNTIL. Characters within
-the delimited expression may be escaped with the character ESCAPE. If
-NESTED?, allow for delimited expressions to be arbitrarily nested
-within.\"
- (define (delimit str start)
- (let ((len (string-length str)))
- (let loop ((index start))
- (cond
- ;; Out of bounds.
- ((>= index len)
- len)
- ;; Escape character.
- ((eqv? escape (string-ref str index))
- (loop (+ index 2)))
- ;; Closing delimiter.
- ((string-prefix?* until str index)
- (+ index (string-length until)))
- ;; Nested delimited string.
- ((and nested? (string-prefix?* open str index))
- (loop (delimit str (+ index (string-length open)))))
- (else
- (loop (1+ index)))))))
-
- (lambda (cursor)
- (let ((str (cursor-text cursor))
- (pos (cursor-position cursor)))
- (if (string-prefix?* open str pos)
- (let ((end (delimit str (+ pos (string-length open)))))
- (values (substring str pos end) (move-cursor cursor end)))
- (lex-fail cursor)))))
-
-(define (lex-tag tag lexer)
- \"Wrap the results of LEXER in a two-element tuple whose head is
-TAG.\"
- (lex-map (cut list tag <>) lexer))
-"))
-
- (p [Taking a step back, it becomes apparent that dependency isolation
-is a general problem that isn’t confined to software written in Ruby:
-Node has npm and nvm, Python has pip and virtualenv, and so on. A big
-limitation of all these language-specific package managers is that
-they cannot control what is outside of their language domain. In
-order to use RVM to successfully compile a version of Ruby, you need
-to make sure you have the GCC toolchain, OpenSSL, readline, libffi,
-etc. installed using the system package manager (note: I’ve seen RVM
-try to build prerequisites like OpenSSL before, which I then disabled
-to avoid duplication and security issues and I recommend you do the
-same.) In order to use Bundler to install Nokogiri, you need to make
-sure libxml2 has been installed using the system package manager. If
-you work with more than a single language, the number of different
-package management tools needed to get work done is staggering. For
-web applications, it’s not uncommon to use RVM, Bundler, NPM, Bower,
-and the system package manager simultaneously to get all of the
-necessary programs and libraries. Large web applications are
-notoriously difficult to deploy, and companies hire a bunch of
-operations folk like me to try to wrangle it all.])
-
- (p [Anyway, let’s forget about Node, Python, etc. and just focus on
-Ruby. Have you or someone you work with encountered hard to debug
-issues and Git merge conflicts due to a problem with
-,(code [Gemfile.lock])? Bundler’s fast and loose versioning in the
-,(code [Gemfile]) (e.g. ,(code [rails >= 4.0])) causes headaches when
-different users update different gems at different times and check the
-resulting auto-generated ,(code [Gemfile.lock]) into the repository.
-Have you ever been frustrated that it’s difficult to deduplicate gems
-that are shared between multiple bundled gem sets? Have you looked at
-the ,(anchor [RVM home page] "https://rvm.io") and been frustrated
-that they recommend you to ,(code [curl bash]) to install their
-software? Have you been annoyed by RVM’s strange system of overriding
-shell built-ins in order to work its magic? I’m not sure how you
-feel, dear reader, but my Ruby environments feel like one giant,
-brittle hack, and I’m often enough involved in fixing issues with them
-on my own workstation, that of my colleagues, and on production
-servers.])
-
- (p [So, if you’re still with me, what do we do about this? How can we
-work to improve upon the status quo? Just use Docker? Docker is
-helpful, and certainly much better than no isolation at all, but it
-hides the flaws of package management inside an opaque disk image and
-restricts the environments in which your application is built to
-function. The general problem of dependency isolation is orthogonal
-to the runtime environment, be it container, virtual machine, or “bare
-metal.” Enter functional package management. What does it mean for a
-package manager to be functional? GNU Guix, the functional package
-manager that I contribute to and recommend, has this to say:])
-
- (blockquote
- (p [GNU Guix is a functional package management tool for the GNU
-system. Package management consists of all activities that relate
-to building packages from sources, honoring their build-time and
-run-time dependencies, installing packages in user environments,
-upgrading installed packages to new versions or rolling back to a
-previous set, removing unused software packages, etc.])
- (p [The term functional refers to a specific package management
-discipline. In Guix, the package build and installation process
-is seen as a function, in the mathematical sense. That function
-takes inputs, such as build scripts, a compiler, and libraries,
-and returns an installed package.]))
-
- (p [Guix has a rich set of features, some of which you may find in
-other package managers, but not all of them (unless you use another
-functional package manager such as Nix.) Gem/Bundler can do
-unprivileged gem installation, but it cannot do transactional upgrades
-and rollbacks or install non-Ruby dependencies. Dpkg/yum/pacman can
-install all build-time and runtime dependencies, but it cannot do
-unprivileged package installation to isolated user environments. And
-none of them can precisely describe the ,(em [full]) dependency
-graph (all the way down to the C compiler’s compiler) but ,(em [Guix
-can]).])
-
- (p [Guix is written in Guile, an implementation of the Scheme
-programming language. The upcoming release of Guix will feature a
-Ruby build system that captures the process of installing gems from
-,(code [.gem]) archives and a RubyGems import utility to make it
-easier to write Guix packages by using the metadata available on
-,(anchor "RubyGems.org" "https://rubygems.org"). Ruby developers
-interested in functional package management are encouraged to try
-packaging their gems (and dependencies) for Guix.])
-
- (p [Now, how exactly can Guix replace RVM and Bundler? Guix uses an
-abstraction called a “profile” that represents a user-defined set of
-packages that should work together. Think of it as having many
-,(code [/usr]) file system trees that can be used in isolation from the
-others (without invoking virtualization technologies such as virtual
-machines or containers.) To install multiple versions of Ruby and
-various gems, the user need only create a separate profile for them:])
-
- (source-code
- "$ guix package --profile=project-1 --install ruby-2.2 ruby-rspec-3
-# Hypothetical packages:
-$ guix package --profile=project-2 --install ruby-1.9 ruby-rspec-2")
-
- (p [A profile is a “symlink forest” that is the union of all the
-packages it includes, and files are deduplicated among all of them.
-To actually use the profile, the relevant environment variables must
-be configured. Guix is aware of such variables, and can tell you what
-to set by running the following:])
-
- (source-code
- "$ guix package --search-paths --profile=project-1")
-
- (p [Additionally, you can also create ad-hoc development environments
-with the ,(code [guix environment]) tool. This tool will spawn a
-sub-shell (or another program of your choice) in an environment in
-which a set of specified packages are available. This is my preferred
-method as it automagically sets all of the environment variables for
-me and Guix is free to garbage collect the packages when I close the
-sub-shell:])
-
- (source-code
- "# Launch a Ruby REPL with ActiveSupport available.
-$ guix environment --ad-hoc ruby ruby-activesupport -E irb")
-
- (p [In order to make this environment reproducible for others, I
-recommend keeping a ,(code [package.scm]) file in version control that
-describes the complete dependency graph for your project, as well as
-metadata such as the license, version, and description:])
-
- (source-code
- (scheme-source
- "(use-modules (guix packages)
- (guix licenses)
- (guix build-system ruby)
- (gnu packages)
- (gnu packages version-control)
- (gnu packages ssh)
- (gnu packages ruby))
-
-(package
- (name \"cool-ruby-project\")
- (version \"1.0\")
- (source #f) ; not needed just to create dev environment
- (build-system ruby-build-system)
- ;; These correspond roughly to \"development\" dependencies.
- (native-inputs
- `((\"git\" ,git)
- (\"openssh\" ,openssh)
- (\"ruby-rspec\" ,ruby-rspec)))
- (propagated-inputs
- `((\"ruby-pg\" ,ruby-pg)
- (\"ruby-nokogiri\" ,ruby-nokogiri)
- (\"ruby-i18n\" ,ruby-i18n)
- (\"ruby-rails\" ,ruby-rails)))
- (synopsis \"A cool Ruby project\")
- (description \"This software does some cool stuff, trust me.\")
- (home-page \"https://example.com\")
- (license expat))"))
-
- (p [With this package file, it is simple to an instantiate a
-development environment:])
-
- (pre (code [$ guix environment -l package.scm]))
-
- (p [I’m not covering it in this post, but properly filling out the
-blank ,(code [source]) field above would allow for building
-development snapshots, including running the test suite, in an
-isolated build container using the ,(code [guix build]) utility. This
-is very useful when composed with a continuous integration system.
-Guix itself uses ,(anchor "Hydra" "https://nixos.org/hydra/") as its
-CI system to perform all package builds.])
-
- (p [As mentioned earlier, one of the big advantages of writing Guix
-package recipes is that the full dependency graph can be captured,
-including non-Ruby components. The pg gem provides a good example:])
-
- (source-code
- (scheme-source
-"(define-public ruby-pg
- (package
- (name \"ruby-pg\")
- (version \"0.18.2\")
- (source
- (origin
- (method url-fetch)
- (uri (rubygems-uri \"pg\" version))
- (sha256
- (base32
- \"1axxbf6ij1iqi3i1r3asvjc80b0py5bz0m2wy5kdi5xkrpr82kpf\"))))
- (build-system ruby-build-system)
- (arguments
- '(#:test-target \"spec\"))
- ;; Native inputs are used only at build and test time.
- (native-inputs
- `((\"ruby-rake-compiler\" ,ruby-rake-compiler)
- (\"ruby-hoe\" ,ruby-hoe)
- (\"ruby-rspec\" ,ruby-rspec)))
- ;; Native extension links against PostgreSQL shared library.
- (inputs
- `((\"postgresql\" ,postgresql)))
- (synopsis \"Ruby interface to PostgreSQL\")
- (description \"Pg is the Ruby interface to the PostgreSQL RDBMS. It works
- with PostgreSQL 8.4 and later.\")
- (home-page \"https://bitbucket.org/ged/ruby-pg\")
- (license license:ruby)))"))
-
- (p [Note how the recipe specifies the PostgreSQL dependency. Below
-is the dependency graph for ruby-pg as produced by ,(code [guix
-graph]), excluding the GCC compiler toolchain and other low-level
-tools for brevity. Pretty neat, eh?])
-
- (image/caption "/images/ruby-pg-graph.png"
- "Abbreviated dependency graph for the pg gem")
-
- (p [Given that Guix doesn’t yet have many gems packaged (help
-wanted), it can still be advantageous to use it for getting more
-up-to-date packages than many distros provide, but in conjuction with
-Bundler for fetching Ruby gems. This gets RVM out of your hair whilst
-creating a migration path away from Bundler at a later time once the
-required gems have been packaged:])
-
- (source-code
- "$ cd my-project/
-$ guix environment --ad-hoc ruby bundler libxml2 libxslt # etc.
-# A small bash script can be used to make these gem sets.
-$ mkdir .gems
-$ export GEM_HOME=$PWD/.gems
-$ export GEM_PATH=$GEM_HOME:$GEM_PATH
-$ export PATH=$GEM_HOME/bin:$PATH
-$ bundle install")
-
- (p [As you’ve seen in the above package snippets, Guix package
-definitions are typically very short and rather easy to write
-yourself. The ,(code [guix import gem]) tool was made to lower ,(code
- [foo bar]) the barrier even more by generating most of the boilerplate
-code. For example:])
-
- (source-code "$ guix import gem pry")
-
- (p [Produces this Scheme code:])
-
-(source-code
- (scheme-source
- [;;; hello there
-(+ 1 2 3)]))
-
- (source-code
- (scheme-source
- "(package
- (name \"ruby-pry\")
- (version \"0.10.1\")
- (source
- (origin
- (method url-fetch)
- (uri (rubygems-uri \"pry\" version))
- (sha256
- (base32
- \"1j0r5fm0wvdwzbh6d6apnp7c0n150hpm9zxpm5xvcgfqr36jaj8z\"))))
- (build-system ruby-build-system)
- (propagated-inputs
- `((\"ruby-coderay\" ,ruby-coderay)
- (\"ruby-method-source\" ,ruby-method-source)
- (\"ruby-slop\" ,ruby-slop)))
- (synopsis
- \"An IRB alternative and runtime developer console\")
- (description
- \"An IRB alternative and runtime developer console\")
- (home-page \"http://pryrepl.org\")
- (license expat))"))
-
- (p [One still has to package the propagated inputs if they aren’t
-yet available, add the necessary inputs for building native extensions
-if needed, and fiddle with the native inputs needed to run the test
-suite, but for most pure Ruby gems this gets you close to a working
-package quickly.])
-
- (p [In conclusion, while support for Ruby in Guix is still in its
-early days, I hope that you have seen the benefits that using a
-general-purpose, functional package manager can bring to your Ruby
-environments (and all other environments, too.) For more information
-about Guix concepts, installation instructions, programming interface,
-and tools, please refer to the
-,(anchor [official manual] "https://gnu.org/software/guix/manual").
-
-Check out the ,(anchor [help] "https://gnu.org/software/guix/help")
-page for ways to contact the development team for help or to report
-bugs. If you are interested in getting your hands dirty, please
-,(anchor [contribute] "https://gnu.org/software/guix/contribute").
-Besides contributions of code, art, and docs, we also need ,(anchor
-[hardware donations] "https://gnu.org/software/guix/donate") to grow
-our build farm to meet the needs of all our users. Happy hacking!]))
diff --git a/posts/angularjs.md b/posts/angularjs.md
new file mode 100644
index 0000000..0ea8c19
--- /dev/null
+++ b/posts/angularjs.md
@@ -0,0 +1,135 @@
+title: AngularJS Post-mortem
+date: 2013-08-08 11:00:00
+tags: web, javascript, angularjs, wsu
+summary: AngularJS likes/dislikes and what went right/wrong
+---
+
+[AngularJS](http://angularjs.org/) is the new popular client-side
+Javascript application framework developed by Google. We have recently
+adopted it at Vista Higher Learning for building our latest features
+that require a lot client-side logic. Now that I have a few
+applications under my belt, it’s time to talk about my experience.
+
+If you want a quick TL;DR: I think AngularJS is good, but it has a
+steep learning curve and there’s no well defined set of best
+practices.
+
+Note: I will be using plenty of terms that will probably only make
+sense for people that have used AngularJS.
+
+The Good Stuff
+--------------
+
+These are the things that went well. The things that made me glad that
+we chose to use AngularJS.
+
+### Easier Testing
+
+Our Javascript test suite uses
+[Jasmine](http://pivotal.github.io/jasmine/). AngularJS is built with
+test frameworks like Jasmine in mind. AngularJS could tends to be
+easily testable due to dependency injection. When the components of an
+application don’t rely on global state, it is easier to mock services
+for unit tests.
+
+### Separation of Concerns
+
+AngularJS stresses separating the view from the data structures and
+logic behind it. I think everyone that’s written a somewhat complex
+JQuery application can relate to the mess of CSS selectors and click
+callbacks that the code quickly degenerates into.
+
+AngularJS allows you to break up the DOM into logical chunks that are
+handled by separate controllers. Treating the application as many
+small pieces working together rather than one giant DOM blob keeps the
+code clean. Message passing via `$emit` and `$broadcast` keeps
+controllers loosely coupled to each other.
+
+### No More JQuery Spaghetti
+
+Directives, the strategy for encapsulating DOM manipulation, are
+wonderful. It is an elegant solution to the mess that was JQuery
+selectors and event callbacks. AngularJS comes with a lot of
+directives out of the box to handle the most common stuff like
+showing/hiding elements, handling clicks, dynamically setting CSS
+classes.
+
+### More Maintainable Code
+
+AngularJS is feature-rich. It does a lot on your behalf, which greatly
+reduces the amount of boilerplate code needed to get a prototype up
+and running. I had the opportunity to essentially rewrite an existing
+JQuery application using AngularJS. The results were clear: The
+AngularJS version had fewer lines of code, was more readable, and
+was easier to debug.
+
+Bumps in the Road
+-----------------
+
+These are the things that didn’t go smoothly. They boil down to
+AngularJS having a steep learning curve and ill-informed software
+design decisions on my part.
+
+### Understanding the Magic
+
+A lot of things seem to happen by magic. For example, it is possible
+to make a asynchronous request and get a promise object in
+return. When the promise is assigned to a variable on `$scope`,
+AngularJS not only knows to ignore it while the request hasn’t
+finished, but it will re-assign to that variable the value of the
+asynchronous call when it completes. It is a nice feature, but it
+takes some digging to find out what is really going on.
+
+### Poor Documentation
+
+I know I’m not the only one that hates the official AngularJS
+documentation. The documentation is getting more complete, but its
+still not very useful. Functions frequently have a blurb describing
+what they do, but no explanation of the parameter list. It’s hard to
+use a function that doesn’t describe what it expects for input.
+
+When the documentation confused us, which it did frequently, we turned
+to the AngularJS book from
+[O’Reilly](http://shop.oreilly.com/product/0636920028055.do) for
+help. I need to get around to reading more of it.
+
+### RESTful Resources and Rails
+
+AngularJS claims to be targeted at CRUD applications, but using the
+HTTP backend and the `Resource` abstraction that sits on top of it was
+particularly difficult. A good amount of time was spent on trying to
+get the HTTP requests from resources to conform to what our Rails
+backend expects, like root-wrapping.
+
+### Bloated Controllers
+
+I frequently made controllers that had too much state and logic that
+should have been extracted into services/factories/etc. A controller
+would start out slim but would quickly grow to several hundred lines
+of code as features were added. Controllers should be the middle man
+between the view and the model and that’s it.
+
+Some tell-tale signs that a controller is becoming bloated:
+
+* There are a lot of private functions (not defined on `$scope`)
+
+* Functions are defined on `$scope` just so you can unit-test them,
+ but are never used in the template
+
+I attribute controller bloat to a lack of knowing the appropriate uses
+for other AngularJS components. It was easy to just keep adding to the
+controller.
+
+Conclusion
+----------
+
+Overall, I think things went well, but I (and the rest of my team)
+made a lot of beginner mistakes. But that’s the learning process,
+isn’t it?
+
+Now that I know more about AngularJS, it will be easier to make better
+design decisions moving forward.
+
+I believe that as AngularJS continues to mature, some concensus in the
+community about best practices will emerge that will make things
+easier for beginners.
diff --git a/posts/diaspora.md b/posts/diaspora.md
new file mode 100644
index 0000000..32a8991
--- /dev/null
+++ b/posts/diaspora.md
@@ -0,0 +1,53 @@
+title: Find Me on Diaspora
+date: 2013-06-30 15:00
+tags: foss, diaspora, federated, decentralized, rails, wsu
+summary: I have started using and contributing to Diaspora.
+---
+
+With all of the recent news about the NSA’s widespread spying, I have
+decided to ween myself off of proprietary, centralized web
+services. Facebook, Google, and other such corporations hold onto
+massive amounts of our data that we’ve willingly given to them via
+status messages, “like” buttons, searches, and emails. Using and
+contributing to free (as in freedom), decentralized (like email) web
+services is a really great way to re-establish control of our
+data. These services rely on many small, interconnected nodes to
+operate, rather than a monolithic website that is under the control of
+one entity. If the distinction between centralized and decentralized
+isn’t clear, consider how email functions. There are many email
+providers to choose from. Users can communicate with others that
+choose to use a different email provider. This is how web services
+should work, but unfortunately very few work this way now.
+
+The centralized web application that I spend too much time using is
+Facebook. I have knowingly given Facebook a “frontdoor” into my life
+for years now and I’m ready to move on. I think that the concept of a
+“social network” is fun, so I wanted a Facebook replacement.
+Fortunately, there is one: [Diaspora](http://diasporaproject.org/).
+
+Diaspora is a [free](https://github.com/diaspora/diaspora),
+distributed, social networking web application written in Ruby using
+the Rails framework. Diaspora is a community-run project. Its success
+depends upon users, developers, technical writers, user interface
+designers, etc. investing their time and/or money into making it
+better. The Diaspora network is broken up into many servers, known as
+[pods](http://podupti.me). Users have the choice of which pod to store
+their data on. Pods assert no ownership over their user’s data, unlike
+Facebook, and do not use that data for targeted
+advertisements. Diaspora is still a rather young project, but it does
+everything that I need it to do. Goodbye, Facebook!
+
+Since I’m a programmer, I naturally wanted to hack on some code and
+contribute. The main developers are very friendly and give great
+guidance to newcomers that want to help out. Every Monday is a “Bug
+Mash Monday”, where a list of open issues is presented to entice
+contributors to resolve them. In the past few weeks, I have made two
+contributions to the Diaspora project: a
+[bug fix](https://github.com/diaspora/diaspora/issues/2948) and a
+[small feature](https://github.com/diaspora/diaspora/issues/2948). Diaspora
+is very hackable and I encourage other developers with Ruby/Rails and
+Javascript knowledge to join in.
+
+TL;DR: Diaspora is great. Create an account. Check out my
+[profile](https://joindiaspora.com/u/davexunit). Start sharing. Happy
+hacking. :)
diff --git a/posts/emacs-required-packages.md b/posts/emacs-required-packages.md
new file mode 100644
index 0000000..58874f8
--- /dev/null
+++ b/posts/emacs-required-packages.md
@@ -0,0 +1,62 @@
+title: Syncing Required Packages in Emacs
+date: 2013-12-30 11:30
+tags: emacs, wsu
+summary: A simple way to keep Emacs packages synced across machines using package.el
+---
+
+I use Emacs on several different computers. To keep my configuration
+consistent across all of them, I do what many people do and made the
+`~/.emacs.d` directory a
+[git repository](https://github.com/davexunit/.emacs.d). I don’t like
+to keep copies of all of the Elisp extensions that I use, such as
+paredit and geiser, in this repository. Instead, I prefer to use
+package.el (introduced in Emacs 24) with the
+[MELPA](https://melpa.org) repository. This saves me from having to
+manually keep all of the extensions I use up-to-date, but requires
+another method to keep useful packages in sync between computers.
+
+There’s a project called
+[Pallet](https://github.com/rdallasgray/pallet) that solves this
+problem, but it was too heavy for my liking. Instead, I wrote a short
+function that simply iterates over a list of required packages and
+installs those that are not currently installed.
+
+```elisp
+;; Additional packages that I use.
+(setq required-packages
+ '(better-defaults
+ elfeed
+ geiser
+ ido-ubiquitous
+ js2-mode
+ magit
+ paredit
+ rainbow-delimiters
+ smex))
+
+(defun install-missing-packages ()
+ "Install all required packages that haven’t been installed."
+ (interactive)
+ (mapc (lambda (package)
+ (unless (package-installed-p package)
+ (package-install package)))
+ required-packages)
+ (message "Installed all missing packages!"))
+```
+
+Now, it’s as easy as typing `M-x install-missing-packages RET` when
+starting Emacs for the first time on a new computer to download all of
+the extensions that I need. Note that before calling
+`install-missing-packages` you must have already initialized the
+package manager via the `package-initialize` function. This approach
+does require some manual bookkeeping in order to keep the
+`required-packages` list up-to-date with your workflow, but I haven’t
+found it to be problematic.
+
+Update: If this solution is too simplistic for you, you should check
+out [use-package](https://github.com/jwiegley/use-package), which
+reddit user [lunayorn](http://www.reddit.com/user/lunaryorn) pointed
+out to me. Thanks!
+
+Check out the comments on
+[reddit](http://www.reddit.com/r/emacs/comments/1u0xr4/quick_hack_syncing_required_packages_in_emacs/).
diff --git a/posts/first-foss-contribution.md b/posts/first-foss-contribution.md
new file mode 100644
index 0000000..32aa752
--- /dev/null
+++ b/posts/first-foss-contribution.md
@@ -0,0 +1,38 @@
+title: My First Real FOSS Contribution
+date: 2013-06-15 18:00
+tags: foss, mediagoblin, python, federated, decentralized, wsu
+summary: I added a small feature!
+---
+
+I spend a lot of my free time writing code. I usually work on my own
+personal projects that never really go anywhere. So, I decided to take
+a detour from my normal hacking routine and contribute to an existing
+free software project. My contribution was accepted awhile ago now,
+but I wasn’t blogging then so I’m rambling about it now.
+
+It’s wise to find a project with a low barrier of entry. An active IRC
+channel and/or mailing list with people willing to help newcomers is
+ideal. I remembered hearing about
+[GNU MediaGoblin](http://mediagoblin.org) at LibrePlanet 2012, so I
+decided to check things out. MediaGoblin is a media sharing web
+application written in Python. Their bug tracker marks tickets that
+require little work and don’t require a deep understanding of
+MediaGoblin as bitesized’.
+
+I chose to work on
+[this ticket](http://issues.mediagoblin.org/ticket/453) because it
+didn’t require any complicated database migrations or knowledge of the
+media processing code. I added a new configuration option,
+`allow_comments`, and a small amount of code to enforce the setting.
+
+Eventually, the ticket got reviewed and
+[Christopher Webber](http://dustycloud.org) (MediaGoblin’s friendly
+project leader) merged it: “Heya. Great branch, this works
+perfectly. Merged!”
+
+It was a very small change, but I was happy to _finally_ have some
+actual code of mine in a real free software project. I have a strong
+passion for free software and the GNU philosophy, so it’s really great
+to participate in the community. My job as a professional software
+developer eats up a lot of my time these days, but I hope to find the
+time to continue hacking and contributing.
diff --git a/posts/first-guile-patch.md b/posts/first-guile-patch.md
new file mode 100644
index 0000000..d67c786
--- /dev/null
+++ b/posts/first-guile-patch.md
@@ -0,0 +1,150 @@
+title: First GNU Guile Patch and More Guix Packages
+date: 2013-11-22 21:00
+tags: gnu, guix, scheme, guile, wsu
+summary: I added alist->hash-table procedures for Guile’s native hash table implementation and packaged SDL libraries for Guix.
+---
+
+I have spent some of the last month working on contributing to GNU
+Guile and now I can finally say that I have contributed code to the
+project. Guile has several hash table implementations: a Guile native
+one, SRFI-69, and R6RS. SRFI-69 contains a handy procedure,
+`alist->hash-table`, which allows for a sort of hash literal-like
+syntax:
+
+```scheme
+(alist->hash-table
+ '((foo . 1)
+ (bar . 2)
+ (baz . 3)))
+```
+
+However, I prefer to use Guile’s native hash implementation, and
+SRFI-69 is incompatible with it, so I decided to port
+`alist->hash-table`. Unfortunately, it was not as simple as writing
+just one procedure. The native hash tables actually have 4 different
+ways of doing key comparisons: Using `equal?`, `eq?`, `eqv?`, and
+user-defined procedures. This is a design flaw because the user must
+make sure to use the right procedures at all times rather than simply
+set the `hash` and `assoc` procedures when creating the hash table
+instance.
+
+Below is the final implementation. Since 3 of the 4 variations were
+essentially the same, I wrote a short macro to reduce code
+duplication.
+
+```scheme
+;;;; Copyright (C) 2013 Free Software Foundation, Inc.
+;;;;
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;;
+
+(define-syntax-rule (define-alist-converter name hash-set-proc)
+ (define (name alist)
+ "Convert ALIST into a hash table."
+ (let ((table (make-hash-table)))
+ (for-each (lambda (pair)
+ (hash-set-proc table (car pair) (cdr pair)))
+ (reverse alist))
+ table)))
+
+(define-alist-converter alist->hash-table hash-set!)
+(define-alist-converter alist->hashq-table hashq-set!)
+(define-alist-converter alist->hashv-table hashv-set!)
+
+(define (alist->hashx-table hash assoc alist)
+ "Convert ALIST into a hash table with custom HASH and ASSOC
+procedures."
+ (let ((table (make-hash-table)))
+ (for-each (lambda (pair)
+ (hashx-set! hash assoc table (car pair) (cdr pair)))
+ (reverse alist))
+ table))
+```
+
+Not only did I manage to get my code into Guile, I was given commit
+access to GNU Guix by the lead developer, Ludovic Courtès. I have
+never had commit access to any free software project repositories
+besides my own. I feel quite honored, as cheesy as that may sound.
+
+Packages for SDL and SDL2 have been merged into master, and packages
+for SDL_gfx, SDL_image, SDL_mixer, SDL_net, and SDL_ttf for SDL 1.2
+are pending review.
+
+I like to show off the elegance of Guix package recipes, so here’s
+some code:
+
+```scheme
+;;; Copyright © 2013 David Thompson <dthompson2@worcester.edu>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define sdl
+ (package
+ (name "sdl")
+ (version "1.2.15")
+ (source (origin
+ (method url-fetch)
+ (uri
+ (string-append "http://libsdl.org/release/SDL-"
+ version ".tar.gz"))
+ (sha256
+ (base32
+ "005d993xcac8236fpvd1iawkz4wqjybkpn8dbwaliqz5jfkidlyn"))))
+ (build-system gnu-build-system)
+ (arguments '(#:tests? #f)) ; no check target
+ (inputs `(("libx11" ,libx11)
+ ("libxrandr" ,libxrandr)
+ ("mesa" ,mesa)
+ ("alsa-lib" ,alsa-lib)
+ ("pkg-config" ,pkg-config)
+ ("pulseaudio" ,pulseaudio)))
+ (synopsis "Cross platform game development library")
+ (description "Simple DirectMedia Layer is a cross-platform development
+library designed to provide low level access to audio, keyboard, mouse,
+joystick, and graphics hardware.")
+ (home-page "http://libsdl.org/")
+ (license lgpl2.1)))
+
+(define sdl2
+ (package (inherit sdl)
+ (name "sdl2")
+ (version "2.0.0")
+ (source (origin
+ (method url-fetch)
+ (uri
+ (string-append "http://libsdl.org/release/SDL2-"
+ version ".tar.gz"))
+ (sha256
+ (base32
+ "0y3in99brki7vc2mb4c0w39v70mf4h341mblhh8nmq4h7lawhskg"))))
+ (license bsd-3)))
+```
+
+Much help is needed to package the GNU system for Guix. If you are
+interested in helping, please drop by `#guix` on freenode, we are
+friendly. :)
diff --git a/posts/first-guix-package.md b/posts/first-guix-package.md
new file mode 100644
index 0000000..1b5c704
--- /dev/null
+++ b/posts/first-guix-package.md
@@ -0,0 +1,43 @@
+title: My First GNU Guix Patch
+date: 2013-10-16 21:00
+tags: gnu, guix, scheme, guile, wsu
+summary: I packaged libtheora for GNU Guix
+---
+
+Over the weekend, I decided to try out GNU Guix: A fully functional
+package manager based on Nix and a distribution of the GNU system. I’m
+a big proponent of GNU Guile, thus I was excited to see a DSL for
+package management written with Guile.
+
+I was told that libtheora would be pretty easy to package, and it
+was. Here’s what the package definition looks like:
+
+```scheme
+(define libtheora
+ (package
+ (name "libtheora")
+ (version "1.1.1")
+ (source (origin
+ (method url-fetch)
+ (uri (string-append "http://downloads.xiph.org/releases/theora/libtheora-"
+ version ".tar.xz"))
+ (sha256
+ (base32
+ "0q8wark9ribij57dciym5vdikg2464p8q2mgqvfb78ksjh4s8vgk"))))
+ (build-system gnu-build-system)
+ (inputs `(("libvorbis" ,libvorbis)))
+ ;; The .pc files refer to libogg.
+ (propagated-inputs `(("libogg" ,libogg)))
+ (synopsis "Library implementing the Theora video format")
+ (description
+ "The libtheora library implements the ogg theora video format,
+a fully open, non-proprietary, patent-and-royalty-free, general-purpose
+compressed video format.")
+ (license license:bsd-3)
+ (home-page "http://xiph.org/theora/")))
+```
+
+Pretty slick, eh? Now, I’m starting to work on packaging SDL (1.2
+and 2) and the SDL extensions (gfx, ttf, etc.), which are not quite as
+easy. I hope to package all of the dependencies that guile-2d will
+need to be available as a Guix package.
diff --git a/posts/gnu30.md b/posts/gnu30.md
new file mode 100644
index 0000000..3d42bf3
--- /dev/null
+++ b/posts/gnu30.md
@@ -0,0 +1,62 @@
+title: GNU 30th Anniversary Hackathon
+date: 2013-09-30 22:00
+tags: gnu, fsf, free software
+summary: Happy birthday, GNU!
+---
+
+I spent my weekend at MIT at the GNU 30th anniversary hackathon. I had
+never participated in a hackathon before and was excited to see what
+it was like. Developers from many GNU and non-GNU projects were there
+to hack and help others get involved, and RMS was there to give the
+keynote speech.
+
+On Saturday, I spent nearly the entire day in the GNU FM room. About a
+year ago I wrote an
+[installation guide](http://bugs.foocorp.net/projects/fm/wiki/How_to_install)
+on the GNU FM wiki, and so Matt Lee asked me to walk some newcomers
+through getting a development environment up and running. I was able
+to help four people with this. They all had a functioning GNU FM
+server and they were able to scrobble their music to it. Setting up
+GNU FM can be quite a pain, and the guide I had written was missing
+some information and gave some bad advice. I simplified and rewrote
+some of it so that it’s easier to follow. Hopefully this will benefit
+a future contributor to GNU FM.
+
+At 5PM on Saturday, RMS gave a talk about the future of free software
+and the GNU project. He discussed the value of reverse engineering
+proprietary applications and device drivers in order to write free
+replacements. He also talked about the dangers of
+software-as-a-service and the “iThings”. His
+[new article](http://www.wired.com/opinion/2013/09/why-free-software-is-more-important-now-than-ever-before/)
+on Wired covers much of the same subject matter. After his speech, he
+raffled off a stuffed baby gnu and an “adorable” GNU 30th anniversary
+mug. Chris, owner of ThinkPenguin, won both items! After the speech
+came the reception in which I ate some delicious vegan cupcakes and
+acquired two 3D printed gnu logos that were sitting atop each
+cupcake. After the reception, I briefly went out to a pub with Matt
+Lee and Matthew Garrett. Donald Robertson of the FSF joined in later.
+
+On Sunday, I spent the first couple of hours helping out more with GNU
+FM because Matt Lee was sick. For lunch, I went with a large group to
+a chinese restaurant. Included in the group was the John Eaton, the
+GNU Octave author, and Zak Rogoff, Campaigns Manager at the FSF. It
+was interesting to talk to John about the challenges that he faced and
+continues to face when trying to keep up with Matlab and maintaining
+compatibility even when the Matlab engineers make bad design decisions.
+
+After lunch, I met up with Mark Weaver, one of the GNU Guile
+developers. He helped me write my first patch for Guile: a new REPL
+option called “read-wrapper” that allows external code to hook into
+the part of the REPL that waits for user input. Guile-2D needs this
+functionality in order to create a REPL that plays nice with the game
+event loop. Since the main thread is in an event loop, waiting for
+user input at the REPL prompt would stop the game entirely. To get
+around this, we used the “read-wrapper” option to pass the procedure
+that reads user input into another thread so that Guile-2D’s event
+loop can continue running. We achieved this functionality in less than
+100 lines of code. This hack showed me how great it is to use a
+language with first-class continuations.
+
+tl;dr: The hackathon was a great time. Happy birthday, GNU.
+
+![GNU 30th logo](/images/gnu30.jpg)
diff --git a/posts/guile-2d-0.1.md b/posts/guile-2d-0.1.md
new file mode 100644
index 0000000..150a760
--- /dev/null
+++ b/posts/guile-2d-0.1.md
@@ -0,0 +1,28 @@
+title: Guile-2D 0.1 Release
+date: 2013-09-27 12:00:00
+tags: foss, gnu, guile, scheme, gamedev, wsu
+summary: Official release of Guile-2D Version 0.1
+---
+
+To celebrate the GNU Project’s 30th anniversary, I have decided to
+make the very first release of my 2D game development framework for
+[GNU Guile](https://gnu.org/s/guile). GNU Guile is a Scheme
+implementation, and has the honor of being the official extension
+language of the GNU project. Guile-2D is a layer above SDL, OpenGL,
+FreeImage, and FTGL that provides abstractions for common 2D game
+programming requirements such as sprites, tilesets, animations,
+scripting, and collision detection.
+
+There is a lot of work to do in order to get Guile-2D up to snuff with
+the game libraries for more popular languages like Python and Lua. I
+am looking for contributors who share my vision of creating a fully
+featured, easy to use game library in Scheme.
+
+Guile-2D currently supports GNU/Linux distributions. I am looking for
+help to get it running on OS X and Windows.
+
+Please refer to the `INSTALL.org`, `README.org`, and texinfo files to
+learn how to install Guile-2D, run example programs, and write your
+own games.
+
+[Browse the source code on GitHub](https://github.com/davexunit/guile-2d)
diff --git a/posts/guile-2d-devlog-1.md b/posts/guile-2d-devlog-1.md
new file mode 100644
index 0000000..1b36772
--- /dev/null
+++ b/posts/guile-2d-devlog-1.md
@@ -0,0 +1,44 @@
+title: guile-2d - A 2D Game Development Framework for GNU Guile
+date: 2013-08-07 23:00:00
+tags: foss, gnu, guile, scheme, gamedev, wsu
+summary: I have started work on a 2D game framework
+---
+
+This is the very first devlog entry for my pet project, guile-2d. As
+the title suggests, guile-2d is a 2D game development framework for
+[GNU Guile](https://gnu.org/s/guile), a Scheme implementation that has
+the honor of being the official extension language of the GNU
+project. Guile is a language with a growing number of features, but it
+still lacks a large assortment of libraries. I like to do 2D game
+programming, and I saw a niche that needed to be filled. Python has
+Pygame, Lua has Love, but there’s no fun and accessible game
+programming library for Guile. Guile-2d is working to correct that.
+
+The goal of Guile-2d is to create an easy to use 2D game programming
+framework. Libraries like SDL give the programmer a rather low-level
+set of tools that they can use to build a game, guile-2d will provide
+high-level tools over low-level SDL and OpenGL for commonly used
+elements of 2D games: tile maps, sprite animation, particle systems,
+collision detection, vector math, A* pathfinding, etc. Such features
+will allow a game developer to very quickly produce a working
+prototype with guile-2d.
+
+Guile-2d is a framework, which means that it has some opinion about
+what the right way to do things is. The most apparent example of this
+is the game loop. The game loop runs at 60 frames-per-second and uses
+fixed timestep updates. Those that have read
+[Fix Your Timestep](http://gafferongames.com/game-physics/fix-your-timestep/)
+will know that this decision is a good thing.
+
+Perhaps the most important feature of guile-2d is the ability to do
+“live coding”. When the game loop starts, a REPL
+(read-eval-print-loop) server is started. Using the great
+[Geiser](http://www.nongnu.org/geiser/) extension for Emacs to connect
+to the REPL server, one can modify their game as it is running. This
+gives users the power to evaluate some new code and see the changes
+reflected immediately in the game window. No need to restart the game
+unless you crash it!
+
+This has been a brief overview of some of the features and goals of
+guile-2d. If this project interests you, you can check out the source
+code on [Github](https://github.com/davexunit/guile-2d).
diff --git a/posts/guile-2d-frp.md b/posts/guile-2d-frp.md
new file mode 100644
index 0000000..7dedbbb
--- /dev/null
+++ b/posts/guile-2d-frp.md
@@ -0,0 +1,64 @@
+title: Functional Reactive Programming in Scheme with guile-2d
+date: 2014-03-09 21:00:00
+tags: foss, gnu, guile, scheme, gamedev, wsu
+summary: Introduction to FRP in Scheme with guile-2d
+---
+
+Last month, the [GNU Guile](https://gnu.org/s/guile) project
+celebrated the 3rd anniversary of its 2.0 release with a hacker
+[potluck](http://savannah.gnu.org/forum/forum.php?forum_id=7887).
+Guilers were encouraged to bring a tasty hack to the mailing list to
+share with everyone. My dish was a simple
+[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming)
+library.
+
+Functional reactive programming (FRP) provides a way to describe
+time-varying values and their relationships using a functional and
+declarative programming style. To understand what this means, let’s
+investigate a typical variable definition in Scheme. The expression
+`(define c (+ a b))` defines the variable `c` to be the sum of
+variables `a` and `b` at the time of evaluation. If `a` or `b` is
+assigned a new value, `c` remains the same. However, for applications
+that deal with state that transforms over time, it would be convenient
+if we could define `c` to react to changes in `a` and `b` by
+recomputing the sum. Contrast this approach with the more traditional
+style of modeling dynamic state via events and callbacks. A lot of
+programmers, myself included, have written code with so many callbacks
+that the resulting program is unmaintainable spaghetti code. Callback
+hell is real, but if you accept FRP into your heart then you will be
+saved!
+
+By now you’re probably wondering: “What the hell does all this mean?”
+So, here’s a real-world example of guile-2d’s FRP API:
+
+```scheme
+(define-signal position
+ (signal-fold v+ (vector2 320 240)
+ (signal-map (lambda (v)
+ (vscale v 4))
+ (signal-sample game-agenda 1 key-arrows))))
+```
+
+In guile-2d, time-varying values are called “signals”. The above
+signal describes a relationship between the arrow keys on the keyboard
+and the position of the player. `signal-sample` is used to trigger a
+signal update upon every game tick that provides the current state of
+the arrow keys. `key-arrows` is a vector2 that maps to the current
+state of the arrow keys, allowing for 8 direction movement. This
+vector2 is then scaled 4x to make the player move faster. Finally,
+the scaled vector is added to the previous player position via
+`signal-fold`. The player’s position is at `(320, 240)` initially.
+As you can see, there are no callbacks and explicit mutation needed.
+Those details have been abstracted away, freeing the programmer to
+focus on more important things.
+
+I think it’s helpful to see FRP in action to really appreciate the
+magic. So, check out this screencast!
+
+ <video src="/videos/guile-2d-frp-demo.webm"
+ style="width:640px;height:360px;" controls>
+ </video>
+
+To see all of the juicy implementation details, check out the
+[git repository](https://gitorious.org/sly/sly/source/sly/signal.scm).
+Thanks for following along!
diff --git a/posts/guile-2d-is-now-sly.md b/posts/guile-2d-is-now-sly.md
new file mode 100644
index 0000000..4ec8255
--- /dev/null
+++ b/posts/guile-2d-is-now-sly.md
@@ -0,0 +1,33 @@
+title: Guile-2D is now named “Sly”
+date: 2014-08-11 19:00
+tags: foss, gnu, guile, scheme, gamedev, wsu
+---
+
+Guile-2D has been the working title for my game engine written in
+Guile Scheme for over a year now. The name has become limiting since
+I realized that it wouldn’t be much extra work to support 3D graphics.
+After much indecision, I’ve finally decided on an official name: Sly.
+I think it’s a great name. It’s short, easy to type, and slyness is
+one of the definitions of “guile”.
+
+In other news:
+
+* Sly has a new contributor!
+ [Jordan Russel](https://gitorious.org/~seconded) has written a
+ [new module](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/joystick.scm)
+ that provides joystick input support.
+
+* I have written a
+ [module for describing animations](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/transition.scm).
+
+* I have been slowly working on a scene graph implementation that
+ plays well with the functional reactive programming API.
+
+* As mentioned above, 3D graphics support is on the way! So far, I
+ have implemented a perspective projection matrix, a "look at"
+ matrix, and a cube primitive.
+
+![3D Scene Graph](/images/sly/scene-graph-prototype.png)
+
+Check out the Sly source code repository on
+[Gitorious](https://gitorious.org/sly/sly)!
diff --git a/posts/guile-2d-live-asset-reload.md b/posts/guile-2d-live-asset-reload.md
new file mode 100644
index 0000000..7dadf87
--- /dev/null
+++ b/posts/guile-2d-live-asset-reload.md
@@ -0,0 +1,51 @@
+title: Live Asset Reloading with guile-2d
+date: 2014-05-04 22:00:00
+tags: foss, gnu, guile, scheme, gamedev, wsu
+summary: Automatically reload modified assets while developing guile-2d progams
+---
+
+Guile-2d provides a dynamic environment in which a developer can build
+a game incrementally as it runs via the Guile REPL. It’s nice to be
+able to hot-swap code and have the running game reflect the changes
+made, but what about the game data files? If an image file or other
+game asset is modified, it would be nice if the game engine took
+notice and reloaded it automatically. This is what guile-2d’s live
+asset reloading feature does.
+
+The new `(2d live-reload)` module provides the `live-reload`
+procedure. `live-reload` takes a procedure like `load-texture` and
+returns a new procedure that adds the live reload magic. The new
+procedure returns assets wrapped in a signal, a time-varying value. A
+coroutine is started that periodically checks if the asset file has
+been modified, and if so, reloads the asset and propagates it via the
+signal. Game objects that depend on the asset will be regenerated
+immediately.
+
+Here’s some example code:
+
+```scheme
+(define load-texture/live
+ (live-reload load-texture))
+
+(define-signal texture
+ (load-texture/live "images/p1_front.png"))
+
+(define-signal sprite
+ (signal-map
+ (lambda (texture)
+ (make-sprite texture
+ #:position (vector2 320 240)))
+ texture))
+```
+
+`load-texture/live` loads textures and reloads them when they change
+on disk. Every time the texture is reloaded, the sprite is
+regenerated using the new texture.
+
+Here’s a screencast to see live reloading in action:
+
+ <video src="http://media.dthompson.us/mgoblin_media/media_entries/10/Screencast_from_05-04-2014_053639_PM.medium.webm"
+ style="width:640px;height:360px;" controls>
+ </video>
+
+Guile-2d is ever-so-slowly approaching a 0.2 release. Stay tuned!
diff --git a/posts/guile-cooperative-repl.md b/posts/guile-cooperative-repl.md
new file mode 100644
index 0000000..50cf39e
--- /dev/null
+++ b/posts/guile-cooperative-repl.md
@@ -0,0 +1,73 @@
+title: A Cooperative REPL Server for Guile 2.0.10
+date: 2014-01-24 22:00
+tags: gnu, scheme, guile, wsu
+summary: Guile 2.0.10 will include a new type of REPL server suited for single-threaded applications.
+---
+
+The next release of GNU Guile, 2.0.10, is to be released “real soon
+now”. My contribution to this release is the new `(system repl
+coop-server)` module. This module introduces a useful variant of the
+REPL server that I’ve named the “cooperative” REPL server. It is
+cooperative because it can be integrated with single-threaded programs
+without the thread synchronization issues present in the ordinary REPL
+server.
+
+Using delimited continuations and mvars (another new feature coming in
+Guile 2.0.10), it was possible to create a REPL server whose clients
+all ran in the context of a single thread. The cooperative server
+puts the user in control of when it is safe to evaluate expressions
+entered at the REPL prompt.
+
+Here’s an example of how to use it:
+
+```scheme
+(use-modules (system repl coop-server))
+
+(define server (spawn-coop-repl-server))
+
+(while #t
+ (poll-coop-repl-server server)
+ (sleep 1))
+```
+
+That’s all it takes. There are only 2 public procedures!
+`spawn-coop-repl-server` creates a new cooperative REPL server
+object and starts listening for clients. `poll-coop-repl-server`
+applies pending operations for the server.
+
+Now that I’ve explained the API, onto the implementation details!
+Although the REPLs run in the context of a single thread, there are
+other threads needed to make it all work. To avoid thinking too much
+about thread synchronization issues, I used the new `(ice-9 mvars)`
+module to provide thread-safe objects for read/write operations. I
+used delimited continuations in order to yield control back to the
+user from inside the loop.
+
+When the server is spawned, a new thread is created to listen for
+client connections. When a client connects, a request to create a new
+REPL is put into the server’s "evaluation" mvar, the storage place for
+pending server operations. The job of `poll-coop-repl-server` is to
+attempt to take from the evaluation mvar and execute the operation
+stored within, if any. In this case, an operation called `new-repl`
+is hanging out along with the client socket. The contents of the mvar
+are removed, and a new REPL is started in the main thread.
+
+This new REPL is run within a "prompt", Guile’s implementation of
+delimited continuations. The prompt allows the REPL to be paused
+temporarily while waiting for user input. Just before the prompt is
+aborted, a thunk containing the logic to read user input is stored
+within the client’s "read" mvar for yet another thread to use.
+Without this additional thread and the use of a prompt, the main
+thread would block while waiting for input, defeating the purpose of
+the cooperative REPL. The reader thread waits for this thunk to
+appear in the read mvar. When it appears, the thunk is applied and
+the resulting expression is stored as an `eval` operation within the
+evaluation mvar. When `poll-coop-repl-server` is called, the REPL
+prompt is resumed. The input expression is evaluated in the context
+of the main thread, the result is printed, and the process repeats
+itself.
+
+That’s all, folks. Thanks for following along. I’m very excited to
+use the cooperative REPL server in guile-2d and I hope that others
+find it useful as well. Many thanks to Mark Weaver for helping me out
+when I got stuck and for all of the helpful code review.
diff --git a/posts/hello-world.md b/posts/hello-world.md
new file mode 100644
index 0000000..9a8be46
--- /dev/null
+++ b/posts/hello-world.md
@@ -0,0 +1,29 @@
+title: Hello, world!
+date: 2013-06-08 17:18
+tags: hello
+summary: I has a shiny new website
+---
+
+Hey! I have a shiny new website. I just purchased a cheap VPS from
+[Digital Ocean](http://digitalocean.com) and registered the
+[dthompson.us](https://dthompson.us) domain for this site. Instead of
+the going the Wordpress route (or using similar blogging software),
+I’ve decided to use a static site generator called
+[Pelican](http://getpelican.com) after seeing that the
+[Linux](http://kernel.org) kernel website was using it. I’m going to
+use this site to host this blog, my resume, and information about my
+software projects.
+
+A little about myself: I’m a software developer from Massachusetts. I
+graduated from [Worcester State University](http://worcester.edu) in
+2012 with a BS in Computer Science. I currently work as a junior
+[Ruby on Rails](http://rubyonrails.org) developer at
+[Vista Higher Learning](http://vistahigherlearning.com). I am an
+advocate of free and open source software and I use GNU/Linux as my
+operating system of choice. In my spare time, I like to write free
+software (mostly my own never-to-be-finished projects). Oddly enough,
+I’m still using a proprietary web service to host all of my software
+projects, so check out my [Github](https://github.com/davexunit) page.
+
+Connect with me on [Diaspora](https://joindiaspora.com/u/davexunit) or
+[Twitter](https://twitter.com/davexunit).
diff --git a/posts/little-schemer.md b/posts/little-schemer.md
new file mode 100644
index 0000000..d58c96b
--- /dev/null
+++ b/posts/little-schemer.md
@@ -0,0 +1,57 @@
+title: The Little Schemer
+date: 2013-08-11 15:00:00
+tags: scheme, books, wsu
+summary: I bought "The Little Schemer"
+---
+
+Yesterday, I took a trip to the MIT Press Bookstore and picked up a
+copy of
+[The Little Schemer](http://mitpress.mit.edu/books/little-schemer). I’ve
+only spent a few hours reading and coding along with it, but I’ve had
+a lot of fun. The following is a mini-review based on my experience
+thus far.
+
+“The Little Schemer” teaches you to think recursively using an
+interesting and comedic writing style and the Scheme programming
+language. While Scheme is the language of choice, the real goal is to
+teach you problem solving rather than the details of a specific
+language. The book starts off simple, explaining what atoms, lists,
+and S-expressions are. Rather than providing the definition and then
+showing examples, it first gives examples in the form of a question
+and answer.
+
+Example:
+
+> Is it true that this an atom?
+>
+> **atom**
+>
+> Yes, because **atom** is a string of characters beginning with a
+> letter.
+
+From the examples given, a definition is created. In later examples, a
+Scheme procedure is written that produces the correct answers for all
+of the questions stated before it. It’s fun to build the procedure,
+verify that it works for all cases, and compare your implementation
+with the book’s.
+
+“The Little Schemer” defines ten commandments that are essential to
+correctly solving the problems in the book. Some commandments are
+first given in an incomplete form, and expanded later when a greater
+level of understanding has been achieved. The problems that you solve
+reinforce the commandments. You might notice that you start writing
+procedures without thinking much about it, much like the muscle memory
+earned from using Emacs a lot. Gerald J. Sussman was right when he
+said that this book “can perform the same service that Hanon’s finger
+exercises or Czerny’s piano studies perform for the student of the
+piano.” I have no idea who Hanon and Czerny are, but I get it. For the
+drummers out there, you could liken this book to
+[Stick Control](http://www.amazon.com/Stick-Control-For-Snare-Drummer/dp/1892764040).
+
+The writing style is very informal, comedic, and food themed. Page 13
+has a space reserved for jelly stains, and page 52 tells you to “go
+cons a piece of cake onto your mouth.” I have laughed a number of
+times while reading. Oh, and let’s not forget about the cute elephant
+drawings. This is definitely not your average boring, dry computer
+science book. If you are interested in a unique and enjoyable learning
+experience, then I highly recommend reading “The Little Schemer”.
diff --git a/posts/maine-2013.md b/posts/maine-2013.md
new file mode 100644
index 0000000..2e979f9
--- /dev/null
+++ b/posts/maine-2013.md
@@ -0,0 +1,41 @@
+title: Maine!
+date: 2013-07-15 22:00
+tags: maine, vacation
+summary: My vacation in Maine
+---
+
+Every summer, my friends and I go to Maine for a week. We stay in the
+Rockland area in an old house on a blueberry field. This year we hiked
+a mountain in Camden, hiked Mt. Katahdin, and went to the Rockland
+Blues Festival.
+
+Here are some pictures taken from my not-so-great cellphone camera:
+
+![View from the house](/images/maine-2013/view-from-house.jpg)
+
+The view from the driveway of our house.
+
+![Zeke's Lookout](/images/maine-2013/zekes-lookout.jpg)
+
+Sign at Zeke’s Lookout. Somewhere on a mountain near Camden.
+
+![Mt. Katahdin, foot of the Hunt trail](/images/maine-2013/katahdin-camp.jpg)
+
+Mt. Katahdin as seen from the foot of the Hunt trail.
+
+![Waterfall on Mt. Katahdin](/images/maine-2013/katahdin-waterfall.jpg)
+
+A beautiful waterfall that can be seen about a mile into the hike.
+
+![View from the mountain 1](/images/maine-2013/katahdin-view-1.jpg)
+![View from the mountain 2](/images/maine-2013/katahdin-view-2.jpg)
+
+Views from the mountain.
+
+![Fog on the mountain](/images/maine-2013/katahdin-fog.jpg)
+
+Thick fog past the treeline.
+
+![Rockland Blues Festival](/images/maine-2013/blues-festival.jpg)
+
+Closed down street packed with people and bands in Rockland.
diff --git a/posts/reproducible-development-environments.md b/posts/reproducible-development-environments.md
new file mode 100644
index 0000000..1672c78
--- /dev/null
+++ b/posts/reproducible-development-environments.md
@@ -0,0 +1,120 @@
+title: Reproducible Development Environments with GNU Guix
+date: 2014-11-08 22:00
+tags: gnu, guix, scheme, guile, wsu
+summary: Easily acquire the dependencies needed to hack on a project
+---
+
+If you’re a software developer, then you probably know very well that
+setting up a project’s development environment for the first time can
+be a real pain. Installing all of the necessary dependencies using
+your system’s package manager can be very tedious. To "solve" this
+problem, we have resorted to inventing new package managers and
+dependency bundlers for pretty much every programming language. Ruby
+has rubygems and bundler, Python has pip and virtualenv, PHP has
+composer, node.js has npm, and so on. Wouldn’t it be nice to instead
+have a single package manager that can handle it all? Enter
+[GNU Guix](https://gnu.org/s/guix), a purely functional package
+manager and GNU/Linux distribution. Using Guix, you can easily create
+a development environment for any software project using the `guix
+environment` tool.
+
+`guix environment` is a new utility added in Guix 0.8, which should be
+released in a few weeks. It accepts one or more packages as input and
+produces a new shell environment in which all of the dependencies for
+those packages are made available. For example, `guix environment
+emacs` will get you everything you need to build GNU Emacs from a
+source release tarball. By default, your `$SHELL` is spawned, but
+you may opt to use any arbitrary shell command instead. If you’ve
+used Nix before, you may notice that this sounds exactly like
+`nix-shell`, and that’s because it was what inspired me to write
+`guix environment`.
+
+Now, let’s take a look at an example. One of my hobby projects is
+[Sly](https://gitorious.org/sly/sly), a game engine written in Guile
+Scheme. Here’s the relevant Guix code that will produce a complete
+development environment:
+
+```scheme
+
+ ;;; Copyright (C) 2014 David Thompson <davet@gnu.org>
+ ;;;
+ ;;; Sly is free software: you can redistribute it and/or modify it
+ ;;; under the terms of the GNU General Public License as published by
+ ;;; the Free Software Foundation, either version 3 of the License, or
+ ;;; (at your option) any later version.
+
+ (use-modules (guix packages)
+ (guix licenses)
+ (guix build-system gnu)
+ (gnu packages)
+ (gnu packages autotools)
+ (gnu packages guile)
+ (gnu packages gl)
+ (gnu packages pkg-config)
+ (gnu packages sdl)
+ (gnu packages maths)
+ (gnu packages image))
+
+ ;; The development environment needs a tweaked LTDL_LIBRARY_PATH
+ ;; for finding libfreeimage.
+ (define freeimage
+ (package (inherit freeimage)
+ (native-search-paths
+ (list (search-path-specification
+ (variable "LTDL_LIBRARY_PATH")
+ (directories '("lib")))))))
+
+ (package
+ (name "sly")
+ (version "0.0")
+ (source #f)
+ (build-system gnu-build-system)
+ (inputs
+ `(("pkg-config" ,pkg-config)
+ ("autoconf" ,autoconf)
+ ("automake" ,automake)
+ ("guile" ,guile-2.0)
+ ("guile-sdl" ,guile-sdl)
+ ("guile-opengl" ,guile-opengl)
+ ("gsl" ,gsl)
+ ("freeimage" ,freeimage)
+ ("mesa" ,mesa)))
+ (synopsis "2D/3D game engine for GNU Guile")
+ (description "Sly is a 2D/3D game engine written in Guile Scheme.
+ Sly differs from most game engines in that it emphasizes functional
+ reactive programming and live coding.")
+ (home-page "https://gitorious.org/sly/sly")
+ (license gpl3+))
+```
+
+You may have noticed that the source field has been set to false.
+This is because the package is not for building and installing. It’s
+sole purpose is to provide the necessary software needed to build Sly
+from a fresh git checkout.
+
+Assuming this code is in a file called `package.scm`, you can simply
+run `guix environment -l package.scm` to spawn a shell for hacking
+on Sly. By default, the environment created is an augmented version
+of your pre-existing shell’s environment. This is convenient when you
+want to use your installed software in the new environment, such as
+git. However, it’s important to make sure that the environment really
+does have everything needed for development without relying on any
+impurities introduced by your existing environment. Without verifying
+this, new developers might be frustrated to find out that the
+environment provided to them is incomplete. To verify, pass the
+`--pure` flag and build from scratch.
+
+So, `guix environment` is a pretty nice way to acquire all the
+dependencies you need to work on a project, but there is still a lot
+of room for improvement. What if you were working on a web
+application that required a running PostgreSQL database, Redis server,
+and XMPP server? It would be really great if Guix could handle
+setting this up for you, too, a la Vagrant. To do so, `guix
+environment` could use the existing features of `guix system` to spawn
+a new virtual machine that shares the host system’s package store and
+the project’s source tree, and then spawn a shell with your
+development environment. I hope to implement this in the
+not-too-distant future. Until next time, happy hacking!
+
+Read the discussion about this post on
+[Hacker News](https://news.ycombinator.com/item?id=8616918).
diff --git a/posts/rinari-jasmine.md b/posts/rinari-jasmine.md
new file mode 100644
index 0000000..cb94b1f
--- /dev/null
+++ b/posts/rinari-jasmine.md
@@ -0,0 +1,36 @@
+title: Jump to Jasmine Specs with Rinari
+date: 2013-12-17 13:00
+tags: emacs, javascript, wsu
+summary: I hacked rinari mode to jump between JS sources and jasmine tests
+---
+
+I use the [rinari](https://github.com/eschulte/rinari) Emacs mode to
+assist me when working on rails projects. One of rinari’s most useful
+features is the ability to quickly jump from one file to another
+related file. I use this feature almost exclusively for jumping
+between a ruby class file and its associated rspec file, but lately
+I’ve been spending most of my time writing javascript. At VHL, we use
+[jasmine](http://pivotal.github.io/jasmine/) for our unit testing
+framework and the
+[jasmine ruby gem](https://github.com/pivotal/jasmine-gem) to
+integrate it with our rails projects. Rinari doesn’t have any
+built-in jump settings for jasmine test files, so I wrote this quick
+hack to make it work:
+
+```elisp
+;; Make rinari jump to/from javascript source files and specs.
+(setcdr (assoc 'javascript rinari-jump-schema)
+ '("j"
+ (("app/assets/javascripts/\\1.js" . "spec/javascripts/\\1_spec.js")
+ ("spec/javascripts/\\1_spec.js" . "app/assets/javascripts/\\1.js")
+ (t . "spec/javascripts/.*")
+ (t . "app/javascripts/.*"))
+ t))
+(rinari-apply-jump-schema rinari-jump-schema)
+```
+
+Now I can press `C-c ; f j` to jump between a javascript file in
+`app/assets/javascripts/` and its jasmine test file in
+`spec/javascripts/`. Perhaps I shouldn’t be overwriting the
+predefined (but not very useful) javascript jump settings, but I
+really wanted to use the `j` key.
diff --git a/posts/ruby-on-guix.md b/posts/ruby-on-guix.md
new file mode 100644
index 0000000..29804c6
--- /dev/null
+++ b/posts/ruby-on-guix.md
@@ -0,0 +1,302 @@
+title: Ruby on Guix
+date: 2015-08-30 15:00
+tags: gnu, guix, scheme, guile, ruby, wsu
+summary: How to use Guix + some elbow grease to replace RVM and Bundler on GNU/Linux
+---
+
+I’ve been working with Ruby professionally for over 3 years now and
+I’ve grown frustrated with two of its most popular development tools:
+RVM and Bundler. For those that may not know, RVM is the Ruby version
+manager and it allows unprivileged users to download, compile,
+install, and manage many versions of Ruby instead of being stuck with
+the one that is installed globally by your distro’s package manager.
+Bundler is the tool that allows developers to keep a version
+controlled “Gemfile” that specifies all of the project’s dependencies
+and provides utilities to install and update those gems. These tools
+are crucial because Ruby developers often work with many applications
+that use different versions of Ruby and/or different versions of gems
+such as Rails. Traditional GNU/Linux distributions install packages
+to the global `/usr` directory, limiting users to a single version of
+Ruby and associated gems, if they are packaged at all. Traditional
+package management fails to meet the needs of a lot of users, so many
+niche package managers have been developed to supplement them.
+
+Taking a step back, it becomes apparent that dependency isolation is a
+general problem that isn’t confined to software written in Ruby: Node
+has npm and nvm, Python has pip and virtualenv, and so on. A big
+limitation of all these language-specific package managers is that
+they cannot control what is outside of their language domain. In
+order to use RVM to successfully compile a version of Ruby, you need
+to make sure you have the GCC toolchain, OpenSSL, readline, libffi,
+etc. installed using the system package manager (note: I’ve seen RVM
+try to build prerequisites like OpenSSL before, which I then disabled
+to avoid duplication and security issues and I recommend you do the
+same.) In order to use Bundler to install Nokogiri, you need to make
+sure libxml2 has been installed using the system package manager. If
+you work with more than a single language, the number of different
+package management tools needed to get work done is staggering. For
+web applications, it’s not uncommon to use RVM, Bundler, NPM, Bower,
+and the system package manager simultaneously to get all of the
+necessary programs and libraries. Large web applications are
+notoriously difficult to deploy, and companies hire a bunch of
+operations folk like me to try to wrangle it all.
+
+Anyway, let’s forget about Node, Python, etc. and just focus on Ruby.
+Have you or someone you work with encountered hard to debug issues and
+Git merge conflicts due to a problem with `Gemfile.lock`? Bundler’s
+fast and loose versioning in the `Gemfile` (e.g. `rails >= 4.0`)
+causes headaches when different users update different gems at
+different times and check the resulting auto-generated `Gemfile.lock`
+into the repository. Have you ever been frustrated that it’s
+difficult to deduplicate gems that are shared between multiple bundled
+gem sets? Have you looked at the [RVM home page](https://rvm.io) and
+been frustrated that they recommend you to `curl | bash` to install
+their software? Have you been annoyed by RVM’s strange system of
+overriding shell built-ins in order to work its magic? I’m not sure
+how you feel, dear reader, but my Ruby environments feel like one
+giant, brittle hack, and I’m often enough involved in fixing issues
+with them on my own workstation, that of my colleagues, and on
+production servers.
+
+So, if you’re still with me, what do we do about this? How can we
+work to improve upon the status quo? Just use Docker? Docker is
+helpful, and certainly much better than no isolation at all, but it
+hides the flaws of package management inside an opaque disk image and
+restricts the environments in which your application is built to
+function. The general problem of dependency isolation is orthogonal
+to the runtime environment, be it container, virtual machine, or “bare
+metal.” Enter functional package management. What does it mean for a
+package manager to be functional? GNU Guix, the functional package
+manager that I contribute to and recommend, has this to say:
+
+> GNU Guix is a functional package management tool for the GNU
+> system. Package management consists of all activities that relate
+> to building packages from sources, honoring their build-time and
+> run-time dependencies, installing packages in user environments,
+> upgrading installed packages to new versions or rolling back to a
+> previous set, removing unused software packages, etc.
+>
+> The term functional refers to a specific package management
+> discipline. In Guix, the package build and installation process
+> is seen as a function, in the mathematical sense. That function
+> takes inputs, such as build scripts, a compiler, and libraries,
+> and returns an installed package.
+
+Guix has a rich set of features, some of which you may find in other
+package managers, but not all of them (unless you use another
+functional package manager such as Nix.) Gem/Bundler can do
+unprivileged gem installation, but it cannot do transactional upgrades
+and rollbacks or install non-Ruby dependencies. Dpkg/yum/pacman can
+install all build-time and runtime dependencies, but it cannot do
+unprivileged package installation to isolated user environments. And
+none of them can precisely describe the *full* dependency graph (all
+the way down to the C compiler’s compiler) but *Guix can*.
+
+Guix is written in Guile, an implementation of the Scheme programming
+language. The upcoming release of Guix will feature a Ruby build
+system that captures the process of installing gems from `.gem`
+archives and a RubyGems import utility to make it easier to write Guix
+packages by using the metadata available on https://rubygems.org.
+Ruby developers interested in functional package management are
+encouraged to try packaging their gems (and dependencies) for Guix.
+
+Now, how exactly can Guix replace RVM and Bundler? Guix uses an
+abstraction called a “profile” that represents a user-defined set of
+packages that should work together. Think of it as having many `/usr`
+file system trees that can be used in isolation from the others
+(without invoking virtualization technologies such as virtual machines
+or containers.) To install multiple versions of Ruby and various
+gems, the user need only create a separate profile for them:
+
+```sh
+guix package --profile=project-1 --install ruby-2.2 ruby-rspec-3
+# Hypothetical packages:
+guix package --profile=project-2 --install ruby-1.9 ruby-rspec-2
+```
+
+A profile is a “symlink forest” that is the union of all the packages
+it includes, and files are deduplicated among all of them. To
+actually use the profile, the relevant environment variables must be
+configured. Guix is aware of such variables, and can tell you what to
+set by running the following:
+
+```sh
+guix package --search-paths --profile=project-1
+```
+
+Additionally, you can also create ad-hoc development environments with
+the `guix environment` tool. This tool will spawn a sub-shell (or
+another program of your choice) in an environment in which a set of
+specified packages are available. This is my preferred method as it
+automagically sets all of the environment variables for me and Guix is
+free to garbage collect the packages when I close the sub-shell:
+
+```sh
+# Launch a Ruby REPL with ActiveSupport available.
+guix environment --ad-hoc ruby ruby-activesupport -E irb
+```
+
+In order to make this environment reproducible for others, I recommend
+keeping a `package.scm` file in version control that describes the
+complete dependency graph for your project, as well as metadata such
+as the license, version, and description:
+
+```scheme
+(use-modules (guix packages)
+ (guix licenses)
+ (guix build-system ruby)
+ (gnu packages)
+ (gnu packages version-control)
+ (gnu packages ssh)
+ (gnu packages ruby))
+
+(package
+ (name "cool-ruby-project")
+ (version "1.0")
+ (source #f) ; not needed just to create dev environment
+ (build-system ruby-build-system)
+ ;; These correspond roughly to "development" dependencies.
+ (native-inputs
+ `(("git" ,git)
+ ("openssh" ,openssh)
+ ("ruby-rspec" ,ruby-rspec)))
+ (propagated-inputs
+ `(("ruby-pg" ,ruby-pg)
+ ("ruby-nokogiri" ,ruby-nokogiri)
+ ("ruby-i18n" ,ruby-i18n)
+ ("ruby-rails" ,ruby-rails)))
+ (synopsis "A cool Ruby project")
+ (description "This software does some cool stuff, trust me.")
+ (home-page "https://example.com")
+ (license expat))
+```
+
+With this package file, it is simple to an instantiate a development
+environment:
+
+```sh
+guix environment -l package.scm
+```
+
+I’m not covering it in this post, but properly filling out the blank
+`source` field above would allow for building development snapshots,
+including running the test suite, in an isolated build container using
+the `guix build` utility. This is very useful when composed with a
+continuous integration system. Guix itself uses
+[Hydra](https://nixos.org/hydra/) as its CI system to perform all
+package builds.
+
+As mentioned earlier, one of the big advantages of writing Guix
+package recipes is that the full dependency graph can be captured,
+including non-Ruby components. The pg gem provides a good example:
+
+```scheme
+(define-public ruby-pg
+ (package
+ (name "ruby-pg")
+ (version "0.18.2")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (rubygems-uri "pg" version))
+ (sha256
+ (base32
+ "1axxbf6ij1iqi3i1r3asvjc80b0py5bz0m2wy5kdi5xkrpr82kpf"))))
+ (build-system ruby-build-system)
+ (arguments
+ '(#:test-target "spec"))
+ ;; Native inputs are used only at build and test time.
+ (native-inputs
+ `(("ruby-rake-compiler" ,ruby-rake-compiler)
+ ("ruby-hoe" ,ruby-hoe)
+ ("ruby-rspec" ,ruby-rspec)))
+ ;; Native extension links against PostgreSQL shared library.
+ (inputs
+ `(("postgresql" ,postgresql)))
+ (synopsis "Ruby interface to PostgreSQL")
+ (description "Pg is the Ruby interface to the PostgreSQL RDBMS. It works
+with PostgreSQL 8.4 and later.")
+ (home-page "https://bitbucket.org/ged/ruby-pg")
+ (license license:ruby)))
+```
+
+Note how the recipe specifies the PostgreSQL dependency. Below is the
+dependency graph for ruby-pg as produced by `guix graph`, excluding
+the GCC compiler toolchain and other low-level tools for brevity.
+Pretty neat, eh?
+
+![ruby-pg dependency graph](/images/guix/ruby-pg-graph.png)
+
+Given that Guix doesn’t yet have many gems packaged (help wanted), it
+can still be advantageous to use it for getting more up-to-date
+packages than many distros provide, but in conjuction with Bundler for
+fetching Ruby gems. This gets RVM out of your hair whilst creating a
+migration path away from Bundler at a later time once the required
+gems have been packaged:
+
+```sh
+cd my-project/
+guix environment --ad-hoc ruby bundler libxml2 libxslt # etc.
+# A small bash script can be used to make these gem sets.
+mkdir .gems
+export GEM_HOME=$PWD/.gems
+export GEM_PATH=$GEM_HOME:$GEM_PATH
+export PATH=$GEM_HOME/bin:$PATH
+bundle install
+```
+
+As you’ve seen in the above package snippets, Guix package definitions
+are typically very short and rather easy to write yourself. The
+`guix import gem` tool was made to lower the barrier even more by
+generating most of the boilerplate code. For example:
+
+```sh
+guix import gem pry
+```
+
+Produces this Scheme code:
+
+```scheme
+(package
+ (name "ruby-pry")
+ (version "0.10.1")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (rubygems-uri "pry" version))
+ (sha256
+ (base32
+ "1j0r5fm0wvdwzbh6d6apnp7c0n150hpm9zxpm5xvcgfqr36jaj8z"))))
+ (build-system ruby-build-system)
+ (propagated-inputs
+ `(("ruby-coderay" ,ruby-coderay)
+ ("ruby-method-source" ,ruby-method-source)
+ ("ruby-slop" ,ruby-slop)))
+ (synopsis
+ "An IRB alternative and runtime developer console")
+ (description
+ "An IRB alternative and runtime developer console")
+ (home-page "http://pryrepl.org")
+ (license expat))
+```
+
+One still has to package the propagated inputs if they aren’t yet
+available, add the necessary inputs for building native extensions if
+needed, and fiddle with the native inputs needed to run the test
+suite, but for most pure Ruby gems this gets you close to a working
+package quickly.
+
+In conclusion, while support for Ruby in Guix is still in its early
+days, I hope that you have seen the benefits that using a
+general-purpose, functional package manager can bring to your Ruby
+environments (and all other environments, too.) For more information
+about Guix concepts, installation instructions, programming interface,
+and tools, please refer to the
+[official manual](https://gnu.org/software/guix/manual/). Check out
+the [help page](https://gnu.org/software/guix/help/) for ways to
+contact the development team for help or to report bugs. If you are
+interested in getting your hands dirty, please
+[contribute](https://gnu.org/software/guix/contribute/). Besides
+contributions of code, art, and docs, we also need
+[hardware donations](https://gnu.org/software/guix/donate/) to grow
+our build farm to meet the needs of all our users. Happy hacking!
diff --git a/posts/stumpwm.md b/posts/stumpwm.md
new file mode 100644
index 0000000..e73412f
--- /dev/null
+++ b/posts/stumpwm.md
@@ -0,0 +1,74 @@
+title: StumpWM on Debian Wheezy
+date: 2013-07-20 15:00
+tags: stumpwm, common lisp, debian, wheezy, wsu
+summary: First steps with StumpWM on Debian Wheezy
+---
+
+Everyone that’s ever talked to me about software development knows
+that I am in love with Emacs. Emacs has a wonderful keyboard driven
+interface and is almost infinitely customizable via Emacs Lisp. I’ve
+done a lot of programming in Emacs from my not-so-great laptop
+lately. My laptop has a rather poor 1280x800 resolution and low
+performing integrated graphics chip. Until today, I was running the
+GNOME 3 desktop environment on it. Unlike most people (or perhaps just
+a loud minority), I like GNOME 3. However, I wanted something that was
+both less graphics intensive and more keyboard driven than GNOME
+Shell and Mutter.
+
+Someone on IRC told me about
+[StumpWM](http://www.nongnu.org/stumpwm/), a window manager written
+entirely in Common Lisp. I had heard of StumpWM before, but back then
+I wasn’t an Emacs user and I’ve never really stuck with any tiling
+window manager that I’ve tried (DWM, Awesome). Now that I know the
+power of a fully programmable environment thanks to Emacs, I decided
+to give StumpWM a try. After some initial pains trying to get it to
+run, I am now using it very happily.
+
+Here is what I had to do to get StumpWM running on Debian Wheezy.
+
+1) Install StumpWM
+
+```sh
+sudo apt-get install stumpwm
+```
+
+2) Create an `.xinitrc` file in my home directory with the following
+ text
+
+```sh
+exec stumpwm
+```
+
+3) Workaround clisp “component not found” errors
+
+I could not get StumpWM to start until I created the following
+symbolic links:
+
+```sh
+ln -s /usr/share/common-lisp/source/stumpwm/stumpwm.asd /usr/share/common-lisp/systems/stumpwm.asd
+ln -s /usr/share/common-lisp/source/cl-ppcre/cl-ppcre.asd /usr/share/common-lisp/systems/cl-ppcre.asd
+```
+
+4) Start the X server
+
+```sh
+startx
+```
+
+I use the GNOME Desktop Manager, so I also created a session file for
+StumpWM in `/usr/share/xsessions/stumpwm.desktop`.
+
+```
+[Desktop Entry]
+Encoding=UTF-8
+Name=StumpWM
+Comment=This session logs you into StumpWM
+Exec=stumpwm
+TryExec=stumpwm
+Icon=
+Type=Application
+```
+
+I hope this brief installation guide can be of use to one of you out
+there in Internet land. Perhaps in the future I will write an article
+about customizing StumpWM with Common Lisp.
diff --git a/posts/sxml-html-guile.md b/posts/sxml-html-guile.md
new file mode 100644
index 0000000..f600d7f
--- /dev/null
+++ b/posts/sxml-html-guile.md
@@ -0,0 +1,501 @@
+title: Rendering HTML with SXML and GNU Guile
+date: 2015-04-10 18:00
+tags: gnu, guile, wsu
+summary: With a little effort, SXML can be used for HTML templates
+---
+
+GNU Guile provides modules for working with XML documents called SXML.
+SXML provides an elegant way of writing XML documents as s-expressions
+that can be easily manipulated in Scheme. Here’s an example:
+
+```scheme
+(sxml->xml '(foo (bar (@ (attr "something")))))
+```
+
+```xml
+<foo><bar attr="something" /></foo>
+```
+
+I don’t know about you, but I work with HTML documents much more often
+than XML. Since HTML is very similar to XML, we should be able to
+represent it with SXML, too!
+
+```scheme
+(sxml->xml
+ '(html
+ (head
+ (title "Hello, world!")
+ (script (@ (src "foo.js"))))
+ (body
+ (h1 "Hello!"))))
+```
+
+```html
+<html>
+ <head>
+ <title>Hello, world!</title>
+ <script src="foo.js" /> <!-- what? -->
+ </head>
+ <body>
+ <h1>Hello!</h1>
+ </body>
+</html>
+```
+
+That `<script>` tag doesn’t look right! Script tags don’t close
+themselves like that. Well, we could hack around it:
+
+```scheme
+(sxml->xml
+ '(html
+ (head
+ (title "Hello, world!")
+ (script (@ (src "foo.js")) ""))
+ (body
+ (h1 "Hello!"))))
+```
+
+```html
+<html>
+ <head>
+ <title>Hello, world!</title>
+ <script src="foo.js"></script>
+ </head>
+ <body>
+ <h1>Hello!</h1>
+ </body>
+</html>
+```
+
+Note the use of the empty string in `(script (@ (src "foo.js")) "")`.
+The output looks correct now, great! But what about the other void
+elements? We’ll have to remember to use the empty string hack each
+time we use one. That doesn’t sound very elegant.
+
+Furthermore, text isn’t even escaped properly!
+
+```scheme
+(sxml->xml "Copyright © 2015 David Thompson <davet@gnu.org>")
+```
+
+```html
+Copyright © 2015 David Thompson &lt;davet@gnu.org&gt;
+```
+
+The `<` and `>` braces were escaped, but `©` should’ve been rendered
+as `&copy;`. Why does this fail, too? Is there a bug in SXML?
+
+There’s no bug. The improper rendering happens because HTML, while
+similar to XML, has some different syntax rules. Instead of using
+`sxml->xml`, a new procedure that is tailored to the HTML syntax is
+needed. Introducing `sxml->html`:
+
+```scheme
+(define* (sxml->html tree #:optional (port (current-output-port)))
+ "Write the serialized HTML form of TREE to PORT."
+ (match tree
+ (() *unspecified*)
+ (('doctype type)
+ (doctype->html type port))
+ ;; Unescaped, raw HTML output
+ (('raw html)
+ (display html port))
+ (((? symbol? tag) ('@ attrs ...) body ...)
+ (element->html tag attrs body port))
+ (((? symbol? tag) body ...)
+ (element->html tag '() body port))
+ ((nodes ...)
+ (for-each (cut sxml->html <> port) nodes))
+ ((? string? text)
+ (string->escaped-html text port))
+ ;; Render arbitrary Scheme objects, too.
+ (obj (object->escaped-html obj port))))
+```
+
+In addition to being aware of void elements and escape characters, it
+can also render `'(doctype "html")` as `<!DOCTYPE html>`, or render an
+unescaped HTML string using `'(raw "frog &amp; toad")`. If we replace
+`sxml->xml` with `sxml->html` in the failing example above we can see
+that it does the right thing.
+
+```scheme
+(sxml->html
+ '((script (@ (src "foo.js")))
+ "Copyright © 2015 David Thompson <davet@gnu.org>"))
+```
+
+```html
+<script src="foo.js"></script>
+Copyright &copy; 2015 David Thompson &lt;davet@gnu.org&gt;
+```
+
+Here’s the full version of my `(sxml html)` module. It’s quite brief,
+if you don’t count the ~250 lines of escape codes! This code requires
+Guile 2.0.11 or greater.
+
+Happy hacking!
+
+```scheme
+;; Copyright © 2015 David Thompson <davet@gnu.org>
+;;
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public License
+;; as published by the Free Software Foundation; either version 3 of
+;; the License, or (at your option) any later version.
+;;
+;; This library is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library. If not, see
+;; <http://www.gnu.org/licenses/>.
+
+define-module (sxml html)
+ #:use-module (sxml simple)
+ #:use-module (srfi srfi-26)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 hash-table)
+ #:export (sxml->html))
+
+define %void-elements
+ '(area
+ base
+ br
+ col
+ command
+ embed
+ hr
+ img
+ input
+ keygen
+ link
+ meta
+ param
+ source
+ track
+ wbr))
+
+define (void-element? tag)
+ "Return #t if TAG is a void element."
+ (pair? (memq tag %void-elements)))
+
+define %escape-chars
+ (alist->hash-table
+ '((#\" . "quot")
+ (#\& . "amp")
+ (#\' . "apos")
+ (#\< . "lt")
+ (#\> . "gt")
+ (#\¡ . "iexcl")
+ (#\¢ . "cent")
+ (#\£ . "pound")
+ (#\¤ . "curren")
+ (#\¥ . "yen")
+ (#\¦ . "brvbar")
+ (#\§ . "sect")
+ (#\¨ . "uml")
+ (#\© . "copy")
+ (#\ª . "ordf")
+ (#\« . "laquo")
+ (#\¬ . "not")
+ (#\® . "reg")
+ (#\¯ . "macr")
+ (#\° . "deg")
+ (#\± . "plusmn")
+ (#\² . "sup2")
+ (#\³ . "sup3")
+ (#\´ . "acute")
+ (#\µ . "micro")
+ (#\¶ . "para")
+ (#\· . "middot")
+ (#\¸ . "cedil")
+ (#\¹ . "sup1")
+ (#\º . "ordm")
+ (#\» . "raquo")
+ (#\¼ . "frac14")
+ (#\½ . "frac12")
+ (#\¾ . "frac34")
+ (#\¿ . "iquest")
+ (#\À . "Agrave")
+ (#\Á . "Aacute")
+ (#\Â . "Acirc")
+ (#\Ã . "Atilde")
+ (#\Ä . "Auml")
+ (#\Å . "Aring")
+ (#\Æ . "AElig")
+ (#\Ç . "Ccedil")
+ (#\È . "Egrave")
+ (#\É . "Eacute")
+ (#\Ê . "Ecirc")
+ (#\Ë . "Euml")
+ (#\Ì . "Igrave")
+ (#\Í . "Iacute")
+ (#\Î . "Icirc")
+ (#\Ï . "Iuml")
+ (#\Ð . "ETH")
+ (#\Ñ . "Ntilde")
+ (#\Ò . "Ograve")
+ (#\Ó . "Oacute")
+ (#\Ô . "Ocirc")
+ (#\Õ . "Otilde")
+ (#\Ö . "Ouml")
+ (#\× . "times")
+ (#\Ø . "Oslash")
+ (#\Ù . "Ugrave")
+ (#\Ú . "Uacute")
+ (#\Û . "Ucirc")
+ (#\Ü . "Uuml")
+ (#\Ý . "Yacute")
+ (#\Þ . "THORN")
+ (#\ß . "szlig")
+ (#\à . "agrave")
+ (#\á . "aacute")
+ (#\â . "acirc")
+ (#\ã . "atilde")
+ (#\ä . "auml")
+ (#\å . "aring")
+ (#\æ . "aelig")
+ (#\ç . "ccedil")
+ (#\è . "egrave")
+ (#\é . "eacute")
+ (#\ê . "ecirc")
+ (#\ë . "euml")
+ (#\ì . "igrave")
+ (#\í . "iacute")
+ (#\î . "icirc")
+ (#\ï . "iuml")
+ (#\ð . "eth")
+ (#\ñ . "ntilde")
+ (#\ò . "ograve")
+ (#\ó . "oacute")
+ (#\ô . "ocirc")
+ (#\õ . "otilde")
+ (#\ö . "ouml")
+ (#\÷ . "divide")
+ (#\ø . "oslash")
+ (#\ù . "ugrave")
+ (#\ú . "uacute")
+ (#\û . "ucirc")
+ (#\ü . "uuml")
+ (#\ý . "yacute")
+ (#\þ . "thorn")
+ (#\ÿ . "yuml")
+ (#\Œ . "OElig")
+ (#\œ . "oelig")
+ (#\Š . "Scaron")
+ (#\š . "scaron")
+ (#\Ÿ . "Yuml")
+ (#\ƒ . "fnof")
+ (#\ˆ . "circ")
+ (#\˜ . "tilde")
+ (#\Α . "Alpha")
+ (#\Β . "Beta")
+ (#\Γ . "Gamma")
+ (#\Δ . "Delta")
+ (#\Ε . "Epsilon")
+ (#\Ζ . "Zeta")
+ (#\Η . "Eta")
+ (#\Θ . "Theta")
+ (#\Ι . "Iota")
+ (#\Κ . "Kappa")
+ (#\Λ . "Lambda")
+ (#\Μ . "Mu")
+ (#\Ν . "Nu")
+ (#\Ξ . "Xi")
+ (#\Ο . "Omicron")
+ (#\Π . "Pi")
+ (#\Ρ . "Rho")
+ (#\Σ . "Sigma")
+ (#\Τ . "Tau")
+ (#\Υ . "Upsilon")
+ (#\Φ . "Phi")
+ (#\Χ . "Chi")
+ (#\Ψ . "Psi")
+ (#\Ω . "Omega")
+ (#\α . "alpha")
+ (#\β . "beta")
+ (#\γ . "gamma")
+ (#\δ . "delta")
+ (#\ε . "epsilon")
+ (#\ζ . "zeta")
+ (#\η . "eta")
+ (#\θ . "theta")
+ (#\ι . "iota")
+ (#\κ . "kappa")
+ (#\λ . "lambda")
+ (#\μ . "mu")
+ (#\ν . "nu")
+ (#\ξ . "xi")
+ (#\ο . "omicron")
+ (#\π . "pi")
+ (#\ρ . "rho")
+ (#\ς . "sigmaf")
+ (#\σ . "sigma")
+ (#\τ . "tau")
+ (#\υ . "upsilon")
+ (#\φ . "phi")
+ (#\χ . "chi")
+ (#\ψ . "psi")
+ (#\ω . "omega")
+ (#\ϑ . "thetasym")
+ (#\ϒ . "upsih")
+ (#\ϖ . "piv")
+ (#\  . "ensp")
+ (#\  . "emsp")
+ (#\  . "thinsp")
+ (#\– . "ndash")
+ (#\— . "mdash")
+ (#\‘ . "lsquo")
+ (#\’ . "rsquo")
+ (#\‚ . "sbquo")
+ (#\“ . "ldquo")
+ (#\” . "rdquo")
+ (#\„ . "bdquo")
+ (#\† . "dagger")
+ (#\‡ . "Dagger")
+ (#\• . "bull")
+ (#\… . "hellip")
+ (#\‰ . "permil")
+ (#\′ . "prime")
+ (#\″ . "Prime")
+ (#\‹ . "lsaquo")
+ (#\› . "rsaquo")
+ (#\‾ . "oline")
+ (#\⁄ . "frasl")
+ (#\€ . "euro")
+ (#\ℑ . "image")
+ (#\℘ . "weierp")
+ (#\ℜ . "real")
+ (#\™ . "trade")
+ (#\ℵ . "alefsym")
+ (#\← . "larr")
+ (#\↑ . "uarr")
+ (#\→ . "rarr")
+ (#\↓ . "darr")
+ (#\↔ . "harr")
+ (#\↵ . "crarr")
+ (#\⇐ . "lArr")
+ (#\⇑ . "uArr")
+ (#\⇒ . "rArr")
+ (#\⇓ . "dArr")
+ (#\⇔ . "hArr")
+ (#\∀ . "forall")
+ (#\∂ . "part")
+ (#\∃ . "exist")
+ (#\∅ . "empty")
+ (#\∇ . "nabla")
+ (#\∈ . "isin")
+ (#\∉ . "notin")
+ (#\∋ . "ni")
+ (#\∏ . "prod")
+ (#\∑ . "sum")
+ (#\− . "minus")
+ (#\∗ . "lowast")
+ (#\√ . "radic")
+ (#\∝ . "prop")
+ (#\∞ . "infin")
+ (#\∠ . "ang")
+ (#\∧ . "and")
+ (#\∨ . "or")
+ (#\∩ . "cap")
+ (#\∪ . "cup")
+ (#\∫ . "int")
+ (#\∴ . "there4")
+ (#\∼ . "sim")
+ (#\≅ . "cong")
+ (#\≈ . "asymp")
+ (#\≠ . "ne")
+ (#\≡ . "equiv")
+ (#\≤ . "le")
+ (#\≥ . "ge")
+ (#\⊂ . "sub")
+ (#\⊃ . "sup")
+ (#\⊄ . "nsub")
+ (#\⊆ . "sube")
+ (#\⊇ . "supe")
+ (#\⊕ . "oplus")
+ (#\⊗ . "otimes")
+ (#\⊥ . "perp")
+ (#\⋅ . "sdot")
+ (#\⋮ . "vellip")
+ (#\⌈ . "lceil")
+ (#\⌉ . "rceil")
+ (#\⌊ . "lfloor")
+ (#\⌋ . "rfloor")
+ (#\〈 . "lang")
+ (#\〉 . "rang")
+ (#\◊ . "loz")
+ (#\♠ . "spades")
+ (#\♣ . "clubs")
+ (#\♥ . "hearts")
+ (#\♦ . "diams"))))
+
+define (string->escaped-html s port)
+ "Write the HTML escaped form of S to PORT."
+ (define (escape c)
+ (let ((escaped (hash-ref %escape-chars c)))
+ (if escaped
+ (format port "&~a;" escaped)
+ (display c port))))
+ (string-for-each escape s))
+
+define (object->escaped-html obj port)
+ "Write the HTML escaped form of OBJ to PORT."
+ (string->escaped-html
+ (call-with-output-string (cut display obj <>))
+ port))
+
+define (attribute-value->html value port)
+ "Write the HTML escaped form of VALUE to PORT."
+ (if (string? value)
+ (string->escaped-html value port)
+ (object->escaped-html value port)))
+
+define (attribute->html attr value port)
+ "Write ATTR and VALUE to PORT."
+ (format port "~a=\"" attr)
+ (attribute-value->html value port)
+ (display #\" port))
+
+define (element->html tag attrs body port)
+ "Write the HTML TAG to PORT, where TAG has the attributes in the
+ist ATTRS and the child nodes in BODY."
+ (format port "<~a" tag)
+ (for-each (match-lambda
+ ((attr value)
+ (display #\space port)
+ (attribute->html attr value port)))
+ attrs)
+ (if (and (null? body) (void-element? tag))
+ (display " />" port)
+ (begin
+ (display #\> port)
+ (for-each (cut sxml->html <> port) body)
+ (format port "</~a>" tag))))
+
+define (doctype->html doctype port)
+ (format port "<!DOCTYPE ~a>" doctype))
+
+define* (sxml->html tree #:optional (port (current-output-port)))
+ "Write the serialized HTML form of TREE to PORT."
+ (match tree
+ (() *unspecified*)
+ (('doctype type)
+ (doctype->html type port))
+ ;; Unescaped, raw HTML output
+ (('raw html)
+ (display html port))
+ (((? symbol? tag) ('@ attrs ...) body ...)
+ (element->html tag attrs body port))
+ (((? symbol? tag) body ...)
+ (element->html tag '() body port))
+ ((nodes ...)
+ (for-each (cut sxml->html <> port) nodes))
+ ((? string? text)
+ (string->escaped-html text port))
+ ;; Render arbitrary Scheme objects, too.
+ (obj (object->escaped-html obj port))))
+```
diff --git a/posts/thinkpad-x220.md b/posts/thinkpad-x220.md
new file mode 100644
index 0000000..fa69b98
--- /dev/null
+++ b/posts/thinkpad-x220.md
@@ -0,0 +1,63 @@
+title: Liberating a Thinkpad X220
+date: 2013-09-22 21:00
+tags: thinkpad, free software, wsu
+summary: I bought a used Thinkpad X220
+---
+
+I had been looking for a suitable replacement to my old, slow Compaq
+laptop that I purchased during my freshman year of college when I had
+very little money. What I liked about my old laptop was that it played
+well with free software. I had no trouble getting all of my hardware
+to work out-of-the-box with fully free GNU/Linux distributions such as
+Trisquel, and I wanted any future laptops of mine to play nicely, too.
+
+I have heard much praise for Thinkpads over the years. Solid build
+quality, utilitarian design, and cheap to buy used. However, upon
+further reading, I realized that most newer Thinkpads require nonfree
+software in order to the drive the Intel wireless chip. Furthermore,
+there was DRM present in the BIOS that would prevent the installation
+of PCIe wireless chips that weren't in the whitelist.
+
+This really bummed me out, but I bought a Thinkpad anyway. I found a
+great deal on a used X220 on ebay for $400. In order to liberate it, I
+had to make a small deal with the devil: Use the pre-installed Windows
+7 to flash a hacked BIOS that removes the whitelist. I could only find
+the needed BIOS as a Windows executable, so I didn't have much
+choice. This process left me hoping that coreboot gains wider
+adoption.
+
+Once I had verified that I didn't brick my Thinkpad, I installed the
+new wireless card. I purchased a Wireless N, half-height, mini PCIe
+card from
+[Thinkpenguin](https://www.thinkpenguin.com/gnu-linux/penguin-wireless-n-half-height-mini-pcie-card). It
+uses an Atheros chipset and is free software compatible. I met Chris,
+the owner of Thinkpenguin, at this year's Northeast GNU/Linux Fest at
+Harvard. He is doing some great work and I wanted to support his
+business. It was nice to buy from someone who could assure me that the
+hardware I purchased is fully supported on a libre GNU/Linux
+distribution.
+
+Now that my Thinkpad was free (sans BIOS, of course), it was time for
+the final touch. I replaced the hard drive with a 128GB SSD and
+installed Debian testing. It takes roughly 9 seconds to get from GRUB
+to the GDM login screen. It feels very nice to have a device that
+boots so quickly.
+
+Now that everything had been installed and configured, I was able to
+start hacking and get a feel for things. The keyboard is the nicest
+I've ever used on a laptop. The [TrackPoint](http://xkcd.com/243/) is
+quite a nice way to move around once you get used to it. The
+ThinkLight is pretty neat when you're in a dark area. The battery life
+is extremely impressive. I don't know exactly how long it lasts yet
+but I never have to charge it while I am using it. I was lucky if I
+got 2 hours of battery life out of my old laptop, which caused me to
+be constantly tethered to an AC adapter. The screen is matte, which is
+awesome because it's very difficult to use a laptop outdoors when the
+screen is glossy. 1366x768 is not an ideal resolution (16:9 be
+damned), but I can live with it on a 12.5" screen. Last but not least,
+I honestly like the aesthetics. A lot of people are enamored with the
+brushed aluminum designs by that fruit company, but I love the flat
+black, functional design of the Thinkpad.
+
+I hope to really break this thing in over the weekend at the
+[GNU 30th Anniversary](https://www.gnu.org/gnu30/) hackathon.