diff options
Diffstat (limited to 'posts')
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 <davet@gnu.org>")) - - (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 <davet@gnu.org> +``` + +The `<` and `>` braces were escaped, but `©` should’ve been rendered +as `©`. 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 & 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 © 2015 David Thompson <davet@gnu.org> +``` + +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. |