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

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

···

On 29.08.2010 12:25, Ammar Ali 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.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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

···

On Aug 29, 3:25 am, Ammar Ali <ammarabu...@gmail.com> wrote:

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,

···

On 29.08.2010 12:40, Robert Klemme wrote:

On 29.08.2010 12:25, Ammar Ali 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

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

···

On 29.08.2010 13:11, Markus Fischer wrote:

Hi,

On 29.08.2010 12:40, Robert Klemme wrote:

On 29.08.2010 12:25, Ammar Ali 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?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Markus Fischer 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?

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 29.08.2010 12:40, Robert Klemme wrote:

On 29.08.2010 12:25, Ammar Ali wrote:

Markus Fischer 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?

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

···

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

On 29.08.2010 12:40, Robert Klemme wrote:

On 29.08.2010 12:25, Ammar Ali wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/