Try my new book!

(currently in beta)

Algorithms, circle mazes, hex grids, masking, weaving, braiding, 3D and 4D grids, spheres, and more!

The Buckblog

assorted ramblings by Jamis Buck

Mar 2015

Playing with Constants, Methods, and Superclasses

24 March 2015 — A few curious Rubyisms of dubious use, which may yet be worth knowing about — 3-minute read

File this one under “Ruby Tricks of Questionable Usefulness.” Still, it ought to be admitted that even questionably-useful tricks can sometimes inspire unexpectedly-creative solutions. To that end, I present the following, which I first saw used years ago in Why the Lucky Stiff’s Camping web microframework. (Kudos to @vinbarnes for helping me remember that!)

Let’s begin by observing the following (possibly unexpected) feature of Ruby. Did you know that you can have a constant and a method with the exact same name, and they won’t collide?

Check this out.

Sum = 0

def Sum(*args)
  args.inject(Sum) { |s,i| s + i }

p Sum      #-> 0
p Sum(1)   #-> 1
p Sum(1,2) #-> 3

See that? The first time we display Sum, we’re actually printing the value of the constant. But as soon as we start adding arguments to the call, that’s when we start hitting the method.

No errors, no warnings, no symbols shadowing other symbols. Just crazy Ruby fun.

The next (seemingly unrelated) tidbit is this: did you know that when you define a class, the bit where you specify the superclass is actually an expression? We usually just put a constant there, but it can be anything (as long as it evaluates to a class).

class Shape

circle_is_shape = true
class Circle < (circle_is_shape ? Shape : Object)

p Circle.superclass #-> Shape

I know what you’re thinking. “Whoa, that’s pretty cool! But why would anyone ever want to do that?”

I’m glad you asked, because that segues neatly into the third little Ruby trick of the day.

You probably already knew that defining a class is the same as creating a new Class object and assigning it to a constant, right?

# This:
class Shape

# is the same as this:
Shape =

This means that our class names are just constants…and we’ve already seen that we can have methods that share those same names. Further, we’ve also seen that the superclass expression in a class definition can be any expression at all… We can stick a method invocation in there!


class Shape

def Shape(which)
  require "shapes/#{which}"

class Square < Shape

class Circle < Shape(:ellipse)

So, we have Shape declared as both a constant (our base Shape class) and a method, where the method just does some work to load a hypothetical class from a file derived from the parameter.

Then, you can see that we can declare subclasses of Shape as normal, with the Square class being a typical example. However, we can get tricky. See that? Circle is a subclass of whatever is returned by our Shape method.

So what? Well, for one thing, it means you can do crazy things like data-driven class hierarchies, where an external configuration file lets you specify (for instance) the type of spline to be used, or the orientation and number of points on a star:

require 'yaml'
config = YAML.load_file('definitions.yml')

class Spline < Shape(config[:spline])

class Star < Shape(config[:star])

As I said at the beginning, though, this may not have any real practical use. There are certainly other (possibly less-obscure) ways to accomplish this same thing. Still, you have to admit, it’s rather fun to think about!

Task Tracking for Neurochemical Brains

17 March 2015 — Presenting a simple (and perhaps very unoriginal) technique for keeping track of a list of tasks in the face of interruption and exploration — 4-minute read
Feb 2015

Mazes for Programmers: Beta!

4 February 2015 — The author announces the beta availability of his new book, "Mazes for Programmers", and invites the reader to participate in its completion by offering feedback, corrections, and suggestions — 2-minute read
Jan 2015

Lessons from the Kitchen

30 January 2015 — A retrospective on a personal journey, wherein the author's experiences of growing as a cook are compared with learning how to write software — 5-minute read

Hanging Out a Shingle

26 January 2015 — Our hero's plans change, and his journey takes an unexpected turn. — 1-minute read

Getting Back in the Pool

20 January 2015 — 2-minute read

A Better Recursive Division Algorithm

15 January 2015 — 5-minute read

Winding Back Up

13 January 2015 — 3-minute read

Sep 2011

Winding down...

1 September 2011 — 1-minute read

Jun 2011

Sharing the Inheritance Hierarchy

7 June 2011 — 3-minute read

Mar 2011

Maze Generation: More weave mazes

17 March 2011 — 8-minute read

Maze Generation: Weave mazes

4 March 2011 — 11-minute read

Feb 2011

Weave Mazes: Your Take?

28 February 2011 — 1-minute read

Programming Language Survey Results

22 February 2011 — 3-minute read


19 February 2011 — 2-minute read

Mazes in CoffeeScript

9 February 2011 — 2-minute read

Maze Generation: Algorithm Recap

7 February 2011 — 5-minute read

Maze Generation: Sidewinder algorithm

3 February 2011 — 12-minute read

Maze Generation: Binary Tree algorithm

1 February 2011 — 7-minute read

Jan 2011

Maze Generation: Growing Tree algorithm

27 January 2011 — 9-minute read

Maze Generation: Hunt-and-Kill algorithm

24 January 2011 — 15-minute read

Maze Generation: Wilson's algorithm

20 January 2011 — 16-minute read

Maze Generation: Aldous-Broder algorithm

17 January 2011 — 11-minute read

Maze Generation: Recursive Division

12 January 2011 — 11-minute read

Maze Generation: Prim's Algorithm

10 January 2011 — 10-minute read

Maze Generation: Kruskal's Algorithm

3 January 2011 — 7-minute read

Dec 2010

Maze Generation: Eller's Algorithm

29 December 2010 — 11-minute read

Maze Generation: Recursive Backtracking

27 December 2010 — 5-minute read

Theseus 1.0

20 December 2010 — 3-minute read

Nov 2010

Ekawada: Approved for Sale!

23 November 2010 — 1-minute read

Way Back

The Buckblog Archives

Dating to 2004 — Hundreds more articles