Hello,
Are there any gotchas in requiring files from within a method? It
seems to work, but I have a sinking feeling that I'm missing something
here.
def require_relative(start, path)
require File.expand_path( File.join( File.dirname(start), path ) )
end
require_relative __FILE__, %w{.. tests tester.rb}
1.upto(3) {|i|
Tester.run i, "some", [3,6,9], :foo => :bar
}
Thanks,
Ammar
Hi Ammar,
I recently wrote a script to graph out (using a .dot file and Graphviz
for visualization) what files load when a given gem is loaded. When I
started looking at gems this way I realized that it was not uncommon
for one file to require a second more than once. When I inspect in the
code, there is usually two require statement within separate method
calls. The logic is just as Robert explained, better efficiency by
only loading something when you need it. I can give you a quick
example from the yadis gem. requiring 'yadis' causes a chain reaction
of file loading resulting in 73 files loading! One of those is a
'cgi.rb' file, which itself loads tempfile twice. Looking in cgi.rb
shows the following def for the read_multipart method definition. Note
the two require 'tempfile' statements in one method definition.
Regards,
Tim
def read_multipart(boundary, content_length)
params = Hash.new([])
boundary = "--" + boundary
quoted_boundary = Regexp.quote(boundary, "n")
buf = ""
bufsize = 10 * 1024
boundary_end=""
# start multipart/form-data
stdinput.binmode if defined? stdinput.binmode
boundary_size = boundary.size + EOL.size
content_length -= boundary_size
status = stdinput.read(boundary_size)
if nil == status
raise EOFError, "no content body"
elsif boundary + EOL != status
raise EOFError, "bad content body"
end
loop do
head = nil
if 10240 < content_length
require "tempfile"
body = Tempfile.new("CGI")
else
begin
require "stringio"
body = StringIO.new
rescue LoadError
require "tempfile"
body = Tempfile.new("CGI")
end
end
body.binmode if defined? body.binmode
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
if (not head) and /#{EOL}#{EOL}/n.match(buf)
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
head = $1.dup
""
end
next
end
if head and ( (EOL + boundary + EOL).size < buf.size )
body.print buf[0 ... (buf.size - (EOL + boundary +
EOL).size)]
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
end
c = if bufsize < content_length
stdinput.read(bufsize)
else
stdinput.read(content_length)
end
if c.nil? || c.empty?
raise EOFError, "bad content body"
end
buf.concat(c)
content_length -= c.size
end
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}
([\r\n]{1,2}|--)/n) do
body.print $1
if "--" == $2
content_length = -1
end
boundary_end = $2.dup
""
end
body.rewind
/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;
\s]*))/ni.match(head)
filename = ($1 or $2 or "")
if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
/Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
(not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
filename = CGI::unescape(filename)
end
/Content-Type: ([^\s]*)/ni.match(head)
content_type = ($1 or "")
(class << body; self; end).class_eval do
alias local_path path
define_method
original_filename) {filename.dup.taint}
define_method
content_type) {content_type.dup.taint}
end
/Content-Disposition:.* name="?([^\";\s]*)"?/ni.match(head)
name = $1.dup
if params.has_key?(name)
params[name].push(body)
else
params[name] = [body]
end
break if buf.size == 0
break if content_length == -1
end
raise EOFError, "bad boundary end of body part" unless
boundary_end=~/--/
params
end # read_multipart