--------------040300080408080201000206
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Francis Hwang wrote:
> I've just released the first version of LibInject, which is a
> developer tool for injecting external dependencies into a Ruby file.
> More specifically, it finds require statements that point to files
> that aren't in the standard library, expands those files, and drops
> the raw text into the original file.
> 
> http://libinject.rubyforge.org/

That's nice. Has anybody thought about a custom #require that looks in
the DATA of the main file, and if it finds a table of filenameffsets
there, loads that file from between those offsets in DATA, else
falls back to Kernel#require ?

Anyway, here's a little proof of concept, called darb (DATA-Archived
RuBy script). Note that this solves a different problem than Francis's
LibInject: you provide list of files, and it generates an archive from
which those files can be loaded on demand. Francis's solution finds the
files for you, but they are all loaded automatically. (Also, note that
darb doesn't unpack the archive, as tar2rubyscript does. Everything
stays in the original archive file.)

What does darb mean? According to http://www.bartleby.com/68/2/1602.html:

> darb is an Americanism probably nearly obsolete today, a slang word
> from the 1920s meaning "something or someone very handsome, valuable,
> attractive, or otherwise excellent."

No comment!

-- 
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407




--------------040300080408080201000206
Content-Type: text/plain;
 name
arb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename
arb"

#!/usr/bin/env ruby

if ARGV.delete("-h")
  puts <<-HELP; exit
    Usage: darb main.rb feature1 feature2 feature3
th/to/file ... > script.rb

    DARB DATA-Archived RuBy script.

    Writes a darb archive to standard out.

    The main.rb code is executed when the archive is executed. Darb
    simply inserts this code into the archive, after one initial line
    to set up a customized #require. This main part of the archive can
    be freely edited after generating the archive. After the __END__
    line, the archive contains the #require implementation, an offset
    table, and the library files themselves. Darb makes no effort to
    separate the embedded files or make them easily extractable

    Each of the features can be #require-d from the main.rb file, and
    will be loaded as needed from the DATA segment of the archive. As
    with Kernel#require, feature names are added to $LOADED_FEATURES
    and only loaded once per feature name. #require falls back to the
    original Kernel#require if the feature is not found in the archive.

    Feature syntax:

      - the .rb is optional
      
      - other extensions are pemitted, but .so is not supported

      - the 'feature3
th/to/file' argument means that darb should
        copy the file from the given path into the archive, and load
        it in response to "require 'feature3'".

    Error messages are reported correctly in terms of the original
    file and line.
    
    Limitations: main.rb cannot have its own __DATA__ section.
    
    Side effects: Darb defines the private instance method
    Kernel#empty_binding, which simply returns a new binding with
    no local variables.
    
    AUTHOR  :    Joel VanderWerf, vjoel / path.berkeley.edu
    VERSION :    0.1
    LICENSE :    ruby license (credit appreciated)
  HELP
end

main  RGV.shift

puts %{eval(DATA.inject("") {|s,l| break s if /^__END__$/; s<<l})}
puts
puts File.read(main)
puts "__END__"
puts <<EOS
Kernel.module_eval do
  table  
EOS

offset  
files  ]

table_lines  RGV.map do |feature|
  if /feature
    feature, file  eature.split(".map {|f|f.strip}
  else
    feature, file  eature, feature
  end
  
  feature  eature.dup
  feature.slice! /\.rb$/
  
  file + .rb" unless /\.\w+$/ file
  file  ile.expand_path(file)
  files << file
  
  len  ile.size(file)
  line  {    "#{feature}" [#{offset}, #{len}]}
  offset + en
  line
end

puts table_lines.join(",\n")

puts <<EOS
  }
  start_pos  ATA.pos

  meth  nstance_method(:require) # Thanks, batsman!
  
  def empty_binding
    binding
  end
  private :empty_binding

  define_method(:require) do |feature|
    feature  eature.dup
    feature.slice! /\.rb$/
    k, (pos,len)  able.find {|k,v|keature}
    if k
      if $LOADED_FEATURES.include? feature
        false
      else
        DATA.seek start_pos + pos
        str  ATA.read(len)
        eval str, empty_binding, feature
        $LOADED_FEATURES << feature
        true
      end
    else
      meth.bind(self).call(feature)
    end
  end
end

__END__
EOS

files.each do |file|
  puts File.read(file)
end

--------------040300080408080201000206
Content-Type: text/plain;
 nameranscript.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filenameranscript.txt"

 main.rb
require 'foo'
require 'foo'
require 'bar'
require 'zap'
require 'yaml'

p foo
p bar
p zap
p YAML

 foo.rb
def foo
  "foo"
end

puts "loading foo once!"

# add something to the foo.rb top-level binding -- see bar.rb
x  
 bar.rb
def bar
  "bar"
end

p self #  main

# bar.rb gets a different top-level binding from foo.rb,
# as it should
raise if defined?(x)
 path/to/some-file.rb
def zap
  "zap"
end
 

$ darb main.rb foo.rb bar zap
th/to/some-file.rb >arc.rb
$ ruby arc.rb
loading foo once!
main
"foo"
"bar"
"zap"
YAML

--------------040300080408080201000206--