require from a method

A

Ammar Ali

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
 
R

Robert Klemme

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.

Not technically. It might even be more efficient if you require a
particular functionality only rarely and include the "require" in the
method that needs that functionality. OTOH I find "require" in the
middle of the code difficult to spot which makes it harder to keep track
of dependencies. Also, errors might surface later (e.g. you copy your
app to a different machine, start it and believe it works while in
reality you forgot to install a gem or lib which is only used for
particular use cases).

Kind regards

robert
 
M

Markus Fischer

Hi,

Not technically. It might even be more efficient if you require a
particular functionality only rarely and include the "require" in the
method that needs that functionality.

Sorry for hijacking, but I'm wondering how require exactly works in the
context of a class or method; does scoping play any role if I've defined
classes/modules in the required file?

thanks,
- Markus
 
R

Robert Klemme

Hi,



Sorry for hijacking, but I'm wondering how require exactly works in the
context of a class or method; does scoping play any role if I've defined
classes/modules in the required file?

No. It would be bad because then if the require statements would be
executed in a different order totally unpredictable behavior would occur.

Robert@babelfish ~
$ echo 'p self' >x.rb

Robert@babelfish ~
$ ruby19 -r x -e 1
main

Robert@babelfish ~
$ ruby19 -r x -e 'class X; def m;require "x"; end; end; X.new.m'
main

Robert@babelfish ~
$

Cheers

robert
 
J

Jörg W Mittag

Markus said:
Sorry for hijacking, but I'm wondering how require exactly works in the
context of a class or method; does scoping play any role if I've defined
classes/modules in the required file?

No. Required files always get evaluated in the global context. Which
is actually another gotcha: if you call require inside a module or
class, then it *looks like* you jail the library into a namespace,
when in fact you don't. Code that looks like it's doing one thing, but
does another thing, is always dangerous.

Note that Kernel#load takes an optional boolean argument telling it to
load the file into an anonymous module instead of the global
namespace. (Unfortunately, there doesn't seem to be any way to
actually *get* at that module[1], so this API looks rather useless.
Also, the code in that file can always use ::Object to gain access to
the global namespace.)

jwm

[1] except for ugly hacks with ObjectSpace
 
T

timr

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
 
R

Robert Klemme

No. Required files always get evaluated in the global context. Which
is actually another gotcha: if you call require inside a module or
class, then it *looks like* you jail the library into a namespace,
when in fact you don't. Code that looks like it's doing one thing, but
does another thing, is always dangerous.

Which is one good reason for not doing it at all. :)
Note that Kernel#load takes an optional boolean argument telling it to
load the file into an anonymous module instead of the global
namespace. (Unfortunately, there doesn't seem to be any way to
actually *get* at that module[1], so this API looks rather useless.
Also, the code in that file can always use ::Object to gain access to
the global namespace.)

jwm

[1] except for ugly hacks with ObjectSpace

The usage of load with module I can think of is to load a file which
registers instances or classes somewhere in some kind of global registry
(hence you need global scope access). That way you do not have to care
how everybody names their classes and they don't interfere with each
other - which is good for this plugin like scenario.

But I agree, more direct access to the scope used would increase utility
of that feature a lot.

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,058
Latest member
QQXCharlot

Latest Threads

Top