eigenclass logo
MAIN  Index  Search  Changes  PageRank  Login

A blog quine and deferred type checks in Ruby

It took me some time to realize that my blogging could benefit from some automation. Indeed, I'm often doing some sort of literate programming to show the internals of some Ruby voodoo, using my xmp decorator to display execution results, which means that I cannot use something like WEB (or a language-independent tool like noweb).

This meta-entry shows how I usually blog, using a sort of "reverse|ruby weave" script I hacked in 15 (?) minutes, shown below. It extracts the code snippets so that I can scp them to the webserver, where they get prettified by my modified attach.rb plugin for hiki. The following (~90s) movie shows what I did to create this entry:


/hiki/Ablogquine/blog_quine.gif

(I changed a few bits before actually posting this)

Staying on-topic: some Ruby, instance methods and deferred type checking

Here's a mildly surprising result: we can move instance methods up the klass chain:

class A
  def foo; "A#foo" end
end

a = A.new
a.foo                                              # => "A#foo"

module B; end

class A
  m = instance_method(:foo)
  B.class_eval{ define_method(:foo, m) }
  remove_method(:foo)
end

a.foo rescue "no method..."                        # => "no method..."

class A; include B end
a.foo                                              # => "A#foo"

This works because the type check is deferred until the method is actually called (instead of being performed when the method is defined in the upper class/module):

class C; include B end
C.new.foo                                          # => 
# ~> -:53:in `foo': bind argument must be an instance of A (TypeError)
# ~> 	from -:53


Here's the source code for rweave (remember it's a kludge):

#!/usr/bin/env ruby


require 'fileutils'

output_lines = []
replacements = {}
snippets = {}

file = ARGV[0] || "defout"
ARGV.replace [] if !File.exist?(file)

dir = "#{file}.files"
state = :begin
current_filename = nil
current_file = nil

ARGF.each do |line|
  case state

  when :begin
    case line
    when /^#(begin|sbegin)\(([^)]+)\)/
      state = :want_end
      current_filename = $2
      if $1 == "begin"
        FileUtils.mkdir_p dir
        current_file = File.open(File.join(dir, current_filename), "w")
      else
        current_file = nil
      end
      output_lines << line
      snippets[$2] = ""
    when %r{^//insert\(([^)]+)\)}
      state = :want_endinsert
      current_filename = $1
      replacements[output_lines.size - 1] = $1
    when %r{^#insert\(([^)]+)\)}
      state = :begin
      current_filename = $1
      replacements[output_lines.size - 1] = $1
    else
      output_lines << line
    end

  when :want_end
    case line
    when /^#end\(#{Regexp.escape(current_filename)}\)/
      current_file.close if current_file
      state = :begin
    else
      snippets[current_filename] << line
      current_file.puts line if current_file
    end
    output_lines << line

  when :want_endinsert
    if md = %r{^//endinsert\(#{Regexp.escape(current_filename)}\)}.match(line)
      state = :begin
    end
  end
end

replacements.each_pair do |idx, snippetname|
  output_lines[idx] << "//insert(#{snippetname})\n" << snippets[snippetname] <<
                       "//endinsert(#{snippetname})"
end
puts output_lines


i'm impressed - nec (2005-12-16 (Fri) 00:26:16)

you rock!

and is this your actual typing speed?


mfp 2005-12-16 (Fri) 17:01:35

haha I "encoded" the gif at higher speed (don't remember the multiplicative constant). What you see in the animation seems to correspond roughly to 330 wpm. My typing speed depends on several factors, the most important being ambient temperature, posture, sleep deprivation. My speed ranges from as low as ~50wpm to ~110wpm. I'm a very lousy typist below 20-21C. That day was moderately cold so I must have done ~80wpm or so; the animation plays (maybe) 4X faster.


testing - mfp (2006-06-08 (Thr) 15:51:21)

did I just break comments in my last commit? answer: no


Last modified:2005/12/14 12:07:21
Keyword(s):[blog] [ruby] [quine] [meta] [instance] [method]
References: