--n003KyEaXMD4XKovKt1
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

(Apologies if this turns out to be a dupe - I posted this about an hour
ago on the newsgroup and it doesn't seem to be showing up anywhere).

Here's my solution to this quiz. It's basically a non-too-efficient
regex-based solution wrapped in a combination command-line script and
library. The main file (lrb.rb) contains both the library and script
code, while the other three files make up some tests of the
functionality in lrb.rb. 

To run the tests, use the lrb script:

	$ ./lrb.rb test.lrb

If you like, lrb will output the code it's about to run before running
it if you enable ruby debugging:

	$ ./lrb.rb test.lrb -d

The first argument has to be the filename. If the second argument is a
number, it's taken as the index of the code block to run. Following
arguments are passed to the ruby interpreter that replaces the one
running lrb. You can run individual tests using this, but you need to
manually require test/unit. E.g:

	$ ./lrb.rb test.lrb 1 -rtest/unit
	$ ./lrb.rb test.lrb 2 -rtest/unit

(the numbers are 0-based, but the tests have a 'require test/unit' line
at block 0 which does nothing on it's own). If you need to pass a number
as the first argument to your script, give the block number as -- .

You can also get a dump of the extracted code, rather than running it:

	$ ./lrb.rb test.lrb --dump

When used as a library, lrb provides a literate version of eval
(lrb_eval) and an lrb-enabled require (lrb_require) that won't work with
rubygems :(. The library is not auto-required into the interpreter that
runs the lrb code.

It has it's warts (especially, no handling of piped-in code or data, no
nested \begin...\end blocks, and debugging can be a PITA) but generally
it seems to work and is pretty flexible.

Thanks for this quiz - literate programming is something I'm still
finding out about (mostly via Haskell, which I've just started looking
at properly) so this was an interesting quiz topic for me :)

-- 
Ross Bamford - rosco / roscopeco.REMOVE.co.uk

--n003KyEaXMD4XKovKt1
Content-Disposition: attachment; filename=lrb.rb
Content-Type: application/x-ruby; name=lrb.rb
Content-Transfer-Encoding: 7bit

#!/usr/bin/env ruby
#
# Literate ruby.
# for Ruby Quiz #102
require 'tempfile'

MAIN_BINDING  inding

module Lrb
  LRB_RX  (?:^\s*>(.*?)$)    |
            (?:[^\\]\\begin\{code\}
                 (.*?)
               [^\\]\\end\{code\})/mxu

  class << self
    # Get individual blocks of code
    def lrb_blocks(str)
      str.scan(LRB_RX).map do |rb| 
        rb.compact.first.strip 
      end
    end
    
    # Get the whole code
    def lrb_code(str)
      lrb_blocks(str).join("\n") 
    end

    def lrb_dump(str, blk_idx  il)
      get_lrb_code(str, blk_idx, false)
    end
        
    def lrb_exec(fn, args  ], blk_idx  il, debug  DEBUG)
      # Ruby will interpet the -d and set DEBUG in the new ruby
      # that replaces this process. However, to get output printed
      # now we need to check it manually.
      code  et_lrb_code(File.read(fn), blk_idx, debug)

      # Use a TempFile. We don't get the auto-delete stuff (finalizer
      # won't get run after we replace the process) but it's 
      # convenient for the auto naming, and we can delete it 
      # later, from the new interpreter.
      #
      # The added bonus is, if theres a parse error, the tempfile 
      # gets left behind which helps with debugging...
      tf  empfile.open(File.basename(fn), '.')
      tf << "at_exit { File.unlink(__FILE__) }\n"
      tf << code 
      tf.close(false) # finalizer will never run

      exec('ruby', *(args << tf.path))
    end

    def lrb_eval(code, blk_idx  il, fn  il, debug  DEBUG)
      fn || (lrb_eval)'
      eval(get_lrb_code(code, blk_idx, debug), MAIN_BINDING, 
           "#{fn} #{('(#' + blk_idx.to_s + ')') if blk_idx}")
    end

    def lrb_require(fn, blk_idx  il, debug  DEBUG)
      unless $".include?(fn)
        fn + .lrb' if File.extname(fn).empty?

        unless File.exists?(fn + blk_idx.to_s)    # nil.to_s "" so all good
          rfn  il
          fn  :.detect { |dir| File.exists?(rfn  ile.join(dir,fn)) }
          fn  fn if fn
        end

        if fn
          begin
            code  et_lrb_code(File.read(fn), blk_idx, debug)
          rescue
            raise LoadError, $!.message
          end
          
          eval(code, MAIN_BINDING, 
               "#{fn} #{('(#' + blk_idx.to_s + ')') if blk_idx}")
          $" << fn + blk_idx.to_s

          true
        else
          raise LoadError, "no such file to load - #{fn}"
        end 

      else
        false
      end
    end
    
    private
    
    def get_lrb_code(str, blk_idx  il, debug  alse)
      if blk_idx
        codeb.lrb_blocks(str)[blk_idx] or raise "LRB: No block ##{blk_idx}"
      else
        codeb.lrb_code(str) or raise "LRB: No code found"
      end

      if debug
        $stderr.puts " ************ LRB: Will execute ************** "
        $stderr.puts code
        $stderr.puts " ***************** LRB: END ****************** "
      end

      code
    end
  end
end

unless ($NO_CORE_LRB || alse)  
  module Kernel
    private
    def lrb_exec(fn, args  ], blk_idx  il, debug  DEBUG)
      Lrb.lrb_exec(fn, args, blk_idx, debug)
    end
    
    def lrb_eval(code, blk_idx  il, fn  il, debug  DEBUG)
      Lrb.lrb_eval(code, blk_idx, fn, debug)
    end

    def lrb_require(fn, blk_idx  il, debug  DEBUG)
      Lrb.lrb_require(fn, blk_idx, debug)
    end
  end
end

if $0 __FILE__
  fn  RGV.shift or raise ArgumentError,
              "LRB: lrb [--dump | filename [block#] [ruby / program opts...]]"
  
  if (poss_idx  RGV.first).to_i.to_s poss_idx
    blk_idx  RGV.shift.to_i
  elsif poss_idx '--'
    ARGV.shift
  end

  if File.extname(fn) '.lrb'
    if ARGV.include?('--dump')
      puts Lrb.lrb_dump(File.read(fn), blk_idx)
    else
      lrb_exec(fn, ARGV, blk_idx, ARGV.include?('-d'))
    end
  else 
    exec('ruby', *(ARGV <<  fn))
  end
end

--n003KyEaXMD4XKovKt1
Content-Disposition: attachment; filename=quiz.lrb
Content-Type: text/plain; name=quiz.lrb; charset=utf-8
Content-Transfer-Encoding: 7bit

The three rules of Ruby Quiz:
 
1.  Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.
 
2.  Support Ruby Quiz by submitting ideas as often as you can:
 
http://www.rubyquiz.com/
 
3.  Enjoy!
 
Suggestion:  A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion.  Please reply to the original quiz message,
if you can.
 
--------------------
 
by Justin Bailey
 
"Literate Programming"[1] is an idea popularized by Donald Knuth, where the
traditional order of code and comments in a source file is switched. Instead of
using special delimiters to mark comments, special delimiters are used to mark
*code*.
 
Innocuous as it sounds, this style of programming makes for a great way to post
code snippets, tutorials, or even whole libraries to mailing lists, blogs, and
web pages. It's also an excellent way to develop your CS homework 
 
There are, of course, a variety of ways to make a source file "literate". One
popular method is called "bird notation". Code is delimited by lines starting
with ">":
 
	> puts "The first line of literate Ruby you may have ever seen"
 
Another method, used in the Haskell language, is borrowed from LateX and makes
it very easy to embed working code into longer papers:
 
	\begin{code}
	puts "And here, we have"
	puts "the second and third lines of literate Ruby to be produced."
	\end{code}
 
Beyond *how* to represent literate code, a host of issues present themselves.
Can a class, method, or even string span multiple code sections? Can the
different styles of code demarcation be mixed in one file? How do you "escape"
code demarcation? What about inserting the output of code lines into the same
literate file?
 
Your task is to enable literate Ruby. What that means is up to you. Is literate
programming only available at the file level (e.g. only files ending in ".lrb"
are considered literate)? Or is literate programming supported with
eval/class_eval/module_eval? Would this enable embedded literated here (i.e. <<)
docs?
 
At the minimum, this quiz should be seen as a literate program, and your code
should be able to run it!  [Editor's Note: The indention added to Ruby blocks in
this quiz are a side effect of the Ruby Quiz software.  Feel free to remove them
when treating this quiz as literate code.  --JEG2]
 
Justin
 
	> puts "Here's to hoping you enjoyed the quiz!"
 
[1] http://en.wikipedia.org/wiki/Literate_programming

--n003KyEaXMD4XKovKt1
Content-Disposition: attachment; filename=test.lrb
Content-Type: text/plain; name=test.lrb; charset=utf-8
Content-Transfer-Encoding: 7bit

This is a literate ruby test file. You can run the whole file
by simply running it through lrb, or individual tests by 
supplying the block number to execute. In that case though, 
you'll have to 

  > require 'test/unit'

yourself with a command like:

  lrb test.lrb 2 -rtest/unit

Test numbers are 1-based (zero is that require 'test/unit' 
above) You can have lrb show the code it's about to execute 
using e.g:

  lrb test.lrb -d

So, here's the first test.

\begin{code}
class TC_LRB < Test::Unit::TestCase
  def test_01
    assert true
  end
end
\end{code}

We need to make sure we can \begin{code}class TC_LRB < Test::Unit::TestCase
def test_02
  assert true
end
end
\end{code} anywhere on a line.

Also, we need to make sure we can escape \\begin{code} raise "Not code!"
\\end{code} both within and at start of a line.

Okay, let's just check that lines starting with > aren't treated as
single line blocks if they're inside a \begin...\end block.

  \begin{code}
  class TC_LRB < Test::Unit::TestCase
    def test_03
      s  
        > This is not a line of code
        > And wouldn't compile on it's own
        "

      en> This is not a line of code\n> And wouldn't compile on it's own\n"
  
      assert_equal e, s.gsub(/\n\s*/, "\n")
    end
  end
      \end{code}

Make sure lrb_require works properly:

\begin{code}
require 'lrb'

class TC_LRB < Test::Unit::TestCase
  def test_04
    assert_raise(NoMethodError) { meth_one() }
   
    lrb_require('testreq', 0)

    assert_equal 1, meth_one()

    assert_raise(NoMethodError) { meth_two() }
    
    lrb_require('testreq', 1)

    assert_equal 2, meth_two()
  end
end
\end{code}

Finally, make sure lrb_eval works.

\begin{code}
require 'lrb'

class TC_LRB < Test::Unit::TestCase
  def test_06
    lrb_require('testreq', 2)

    # does it's own assertions
    meth_three()
  end
end
\end{code}

That's it. Now just run this test through lrb.

 

--n003KyEaXMD4XKovKt1
Content-Disposition: attachment; filename=testreq.lrb
Content-Type: text/plain; name=testreq.lrb; charset=utf-8
Content-Transfer-Encoding: 7bit

Just test that lrb_require works

\begin{code}
class TC_LRB < Test::Unit::TestCase
  def meth_one
    1
  end
end
\end{code}

\\begin{code} a red herring 

\begin{code}
class TC_LRB < Test::Unit::TestCase
  def meth_two
    2
  end
end
\end{code}

But we also need to make sure lrb_eval works too.

\begin{code}
require 'stringio'

class TC_LRB < Test::Unit::TestCase
  def meth_three
    test_block_0
    test_block_1
    test_block_2
  end

  def test_block_0
    $stdout  s  tringIO.new)
    lrb_eval(File.read('quiz.lrb'), 0)
    assert_equal(
      "The first line of literate Ruby you may have ever seen",
      s.string.chomp)
    $stdout  TDOUT
  end
  
  def test_block_1
    $stdout  s  tringIO.new)
    lrb_eval(File.read('quiz.lrb'), 1)
    assert_equal( 
      "And here, we have\nthe second and third lines of literate Ruby to be produced.",
      s.string.chomp)
    $stdout  TDOUT
  end
  
  def test_block_2
    $stdout  s  tringIO.new)
    lrb_eval(File.read('quiz.lrb'), 2)
    assert_equal "Here's to hoping you enjoyed the quiz!", s.string.chomp
    $stdout  TDOUT
  end
end
\end{code}

The End.


--n003KyEaXMD4XKovKt1--