Forum: Ruby How can I prevent require duplicate files

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Zhao Y. (Guest)
on 2009-01-05 05:17
In a big ruby project, how to prevent requiring a file multiple times?
Like the #ifdef in C.
Phlip (Guest)
on 2009-01-05 05:30
(Received via mailing list)
Zhao Yi wrote:

> In a big ruby project, how to prevent requiring a file multiple times?
> Like the #ifdef in C.

require() already prevents multiple includes. load() forces an
unquestioned input.

If two modules require the same module...

   require 'foo'

...and if both modules specify the same path to foo (or no path), then
the file
only imports once.

However, if you require some elaborate path, and if another module
requires a
different path to the same file, Ruby does not care if the file is the
same - it
will load() and execute the same source twice.
Zhao Y. (Guest)
on 2009-01-05 07:50
Phlip wrote:
require() already prevents multiple includes. load() forces an
> unquestioned input.
>
> If two modules require the same module...
>
>    require 'foo'
>
> ...and if both modules specify the same path to foo (or no path), then
> the file
> only imports once.
>
> However, if you require some elaborate path, and if another module
> requires a
> different path to the same file, Ruby does not care if the file is the
> same - it
> will load() and execute the same source twice.

If there are some variables defined in a module which is included by
more than one files, there will be a warning indicating "warning:
already initialized". If ruby only imports once, why such warning is
thrown?
Phlip (Guest)
on 2009-01-05 08:00
(Received via mailing list)
Zhao Yi wrote:

> If there are some variables defined in a module which is included by
> more than one files, there will be a warning indicating "warning:
> already initialized". If ruby only imports once, why such warning is
> thrown?

I don't know about all cases but I suspect the biggest culprit is
require 'foo'
twice with different paths.

If you have a Rails project, it has a Magic Loader that automagically
loads the
file foo.rb if the interpreter sees a Foo reference and has not seen a
definition for Foo yet. This is a miracle and a pain in the butt,
because a
subsequent require 'foo' line might not have the same path, and you get
the
double-require issue.

If the problem persists, given a warning on Bar, write:

   unless defined? Bar
     Bar = 42 # or whatever
   end

That's a total hack, but it works well enough.
Zhao Y. (Guest)
on 2009-01-05 11:03
Phlip wrote:
>
> I don't know about all cases but I suspect the biggest culprit is
> require 'foo'
> twice with different paths.
>
> If you have a Rails project, it has a Magic Loader that automagically
> loads the
> file foo.rb if the interpreter sees a Foo reference and has not seen a
> definition for Foo yet. This is a miracle and a pain in the butt,
> because a
> subsequent require 'foo' line might not have the same path, and you get
> the
> double-require issue.
>
> That's a total hack, but it works well enough.

But there is only one file named foo.rb, how could require 'foo' happen?
Is there a way to avoid printing the warning message?
LAMBEAU Bernard (Guest)
on 2009-01-05 11:20
(Received via mailing list)
There must be a mistake, below is the test I've made. No warning at
all, even with warning level set to 2

Could you send some example?

% cat test.rb
require 'thedef'
require 'test1'
require 'test2'

% cat thedef.rb
THEDEF = 1

% cat test1.rb
require 'thedef'

% cat test2.rb
require 'thedef'

% ruby -W2 test.rb
%

blambeau
Brian C. (Guest)
on 2009-01-05 11:32
LAMBEAU Bernard wrote:
> There must be a mistake, below is the test I've made. No warning at
> all, even with warning level set to 2

Now try:

require 'test1'
require './test1'
require '/home/someuser/test1'
LAMBEAU Bernard (Guest)
on 2009-01-05 11:56
(Received via mailing list)
Well, we know that ruby 1.8.x doesn't ensure unique loading with that
kind of require ...

There's another hack below, that should be consolidated however,

module Kernel
    alias_method :old_require, :require
    def require(path)
      old_require(File.expand_path(path))
    end
end

Otherwise, you can use 'require File.expand_path('oldpath')' each
time, but that's even more ugly.

blambeau
Brian C. (Guest)
on 2009-01-05 15:48
LAMBEAU Bernard wrote:
> Well, we know that ruby 1.8.x doesn't ensure unique loading with that
> kind of require ...
>
> There's another hack below, that should be consolidated however,
>
> module Kernel
>     alias_method :old_require, :require
>     def require(path)
>       old_require(File.expand_path(path))
>     end
> end

Yuk, that will break any program which uses any sort of external
library.

irb(main):001:0> File.expand_path("net/http")
=> "/home/candlerb/net/http"
irb(main):002:0> require File.expand_path("net/http")
LoadError: no such file to load -- /home/candlerb/net/http
  from (irb):2:in `require'
  from (irb):2
  from :0
irb(main):003:0> require 'net/http'
=> true

The original question said:

"In a big ruby project, how to prevent requiring a file multiple times?
Like the #ifdef in C"

Well, nothing stops you using the same trick in Ruby:

unless defined? _FOO
  _FOO = 1
  ... rest
end

But in general, as long as you use require sensibly (i.e. consistently
require 'foo' and not mixed with require '/path/to/foo') then this isn't
a problem.

So it seems to me the OP is doing something out-of-the-ordinary which is
causing the warnings (not errors) that he sees.

However, since he didn't post the exact warnings seen, or the code which
produces them, it's hard to help him further.
Zhao Y. (Guest)
on 2009-01-06 04:23
Brian C. wrote:
> However, since he didn't post the exact warnings seen, or the code which
> produces them, it's hard to help him further.

Hi,

This is the case, I have three files, a.rb, b.rb and c.rb.

a.rb:

module A
  TEST='test'
end

b.rb:

require 'a'
require 'c'
class B
  include A
end

c.rb:

require File.dirname(__FILE__)+'/a.rb'
class C
  include A
end

when run b.rb, I will get the warning: "./a.rb:2: warning: already
initialized constant TEST"
Brian C. (Guest)
on 2009-01-06 11:16
Zhao Yi wrote:
> c.rb:
>
> require File.dirname(__FILE__)+'/a.rb'

That's the source of the problem.

The solution is to set up $LOAD_PATH appropriately at the start of your
program, so that you never have to do require with an absolute path.

For example, let's say you have the following directory structure for
your project:

  bin/foo
  bin/bar
  lib/a
  lib/b
  lib/c

Stick the following at the top of bin/foo and bin/bar:

#!/usr/bin/ruby -w
$LOAD_PATH.unshift File.dirname(__FILE__)+"/../lib"

From this point onwards you can do require 'a', require 'b' etc without
any absolute paths. This also has the advantage that it doesn't matter
what the current directory is when you start the program. e.g. you can
do

$ cd /tmp
$ /home/me/bin/foo

and it will work as expected.

Hints:

- When running scripts from the command line, you can use -Ilib to get
the 'lib' dir added to the load path.

- If you're playing with Rails, look at config/boot.rb
Zhao Y. (Guest)
on 2009-01-06 11:23
Brian C. wrote:
> #!/usr/bin/ruby -w
> $LOAD_PATH.unshift File.dirname(__FILE__)+"/../lib"
>

this works. thanks.
This topic is locked and can not be replied to.