Require from a method

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

On 29.08.2010 12:25, Ammar A. wrote:

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

Hi,

On 29.08.2010 12:40, Robert K. wrote:

On 29.08.2010 12:25, Ammar A. wrote:

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.

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

On 29.08.2010 13:11, Markus F. wrote:

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?

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

[email protected] ~
$ echo ‘p self’ >x.rb

[email protected] ~
$ ruby19 -r x -e 1
main

[email protected] ~
$ ruby19 -r x -e ‘class X; def m;require “x”; end; end; X.new.m’
main

[email protected] ~
$

Cheers

robert

On Aug 29, 3:25 am, Ammar A. [email protected] wrote:

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

Markus F. wrote:

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

On 08/29/2010 03:34 PM, Jörg W Mittag wrote:

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.

Which is one good reason for not doing it at all. :slight_smile:

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs