[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

circular 'require'

Shadowfirebird

7/23/2008 9:48:00 AM

Hi,

I'm new to ruby and I'm really enjoying it. However, there is one thing...

I gather from this list and my poor efforts that ruby does not like
files that 'require' each other circularly. I can find nothing in the
documentation about this, though; in fact, the description of require
seems to actually rule it out. Can anyone explain what is going on
here?

(This happens in 1.8 and 1.9 and JRuby 1.0. So it's definitely me,
not Ruby, that has the problem.)


To flesh things out a little:

'require' is supposed to load files, unless they have been already
loaded. It stores an array of loaded files in $" for this purpose.

Okay, suppose we have two programs in two different files:

# test1.rb
require "test2.rb"
class One
def self.testone(); Two.whoistwo; end
def self.whoisone(); puts "class one"; end
end

# test2.rb
require "test1.rb"
class Two
def self.testtwo(); One.whoisone; end
def self.whoistwo(); puts "class two"; end
end

>ruby -w test1.rb
/test1.rb:4: unititialized constant Two (NameError)

Why does this happen? I would have thought that test1 would load
test2; then test2 would go to load test1 but find it already loaded.
Apparently what happens is that test2 doesn't get loaded at all.

OTOH if I replace the 'require' with a homebrew, like this:

# test1.rb
$" << "test1.rb"
load("test2.rb") if !$".include?("test2.rb")
...

...then everything works fine, which seems to imply that 'replace'
isn't behaving as described in the slightest.

Anyone that can provide a description of what replace actually does
while staying high-level enough for my head not to spin, will earn my
undying gratitude.

Ta,
Shadowfirebird.

--
Me, I imagine places that I have never seen / The colored lights in
fountains, blue and green / And I imagine places that I will never go
/ Behind these clouds that hang here dark and low
But it's there when I'm holding you / There when I'm sleeping too /
There when there's nothing left of me / Hanging out behind the
burned-out factories / Out of reach but leading me / Into the
beautiful sea

28 Answers

Stefano Crocco

7/23/2008 10:18:00 AM

0

On Wednesday 23 July 2008, Shadowfirebird wrote:
> Hi,
>
> I'm new to ruby and I'm really enjoying it. However, there is one
> thing...
>
> I gather from this list and my poor efforts that ruby does not like
> files that 'require' each other circularly. I can find nothing in the
> documentation about this, though; in fact, the description of require
> seems to actually rule it out. Can anyone explain what is going on
> here?
>
> (This happens in 1.8 and 1.9 and JRuby 1.0. So it's definitely me,
> not Ruby, that has the problem.)
>
>
> To flesh things out a little:
>
> 'require' is supposed to load files, unless they have been already
> loaded. It stores an array of loaded files in $" for this purpose.
>
> Okay, suppose we have two programs in two different files:
>
> # test1.rb
> require "test2.rb"
> class One
> def self.testone(); Two.whoistwo; end
> def self.whoisone(); puts "class one"; end
> end
>
> # test2.rb
> require "test1.rb"
> class Two
> def self.testtwo(); One.whoisone; end
> def self.whoistwo(); puts "class two"; end
> end
>
> >ruby -w test1.rb
>
> ./test1.rb:4: unititialized constant Two (NameError)
>
> Why does this happen? I would have thought that test1 would load
> test2; then test2 would go to load test1 but find it already loaded.
> Apparently what happens is that test2 doesn't get loaded at all.

The required file is added to $" only after its contents have been processed,
so when the line

require 'test1.rb'

is executed, the file 'test1.rb' hasn't as yet inserted into $". To be more
specific, here's what happens when the line

require 'test1.rb'

is executed:

* the file test1.rb is read and parsed
* starts the execution of test1.rb
* the line require 'test2.rb' is executed. This means:
* the file test2.rb is read and parsed
* starts the execution of test2.rb
* the line require 'test1.rb' is executed. This means:
* the file test1.rb is read and parsed
* starts the execution of test1.rb
* the line require 'test2.rb' is executed. This means:
...

As you can see, this leads to an endless loop.

Can I ask you why do you need these circular requires? It's a situation which
seldom happens using ruby.

Also, using require, you don' need to specify the .rb extension.

I hope this helps

Stefano

Shadowfirebird

7/23/2008 10:45:00 AM

0

Thanks for the explanation, but this does rather worry me. It seems
to be a limitation without documentation or good reason. No doubt it
makes sense to those of you with more experience or Ruby and OOP in
general...


As to why I want to create files with circular references? That's a
perfectly good question (but, I think, a seperate one).

Principal-of-least-astonishment answer: I don't wish to be
contentious, but why should I not? There is nothing in the pickaxe or
the API docs that says this should be avoided. And it's not
unreasonable for my objects to talk to each other in a fairly complex
way. My program worked fine when it was a single file.

Technical answer: I have a bunch of classes that all inherit, directly
or indirectly, from a root class. The root class stores an array of
all objects created in a class variable. It also has some static
methods so that the child classes can get information from that array;
some of these classes use kind_of?() to tell different objects apart.
So the child classes call static methods in the root class, and these
static methods in turn refer to the child classes.

Whether this is good design or not is open to question. I'd be very
interested in suggestions for alternative approaches.

But, as I say, I think that's a seperate question. Can anyone tell me
why require isn't coded to cope with circular references, since it
appears to be a relatively simple problem to solve?

Alex Gutteridge

7/23/2008 11:19:00 AM

0

On 23 Jul 2008, at 10:48, Shadowfirebird wrote:

> Hi,
>
> I'm new to ruby and I'm really enjoying it. However, there is one
> thing...
>
> I gather from this list and my poor efforts that ruby does not like
> files that 'require' each other circularly. I can find nothing in the
> documentation about this, though; in fact, the description of require
> seems to actually rule it out. Can anyone explain what is going on
> here?
>
> (This happens in 1.8 and 1.9 and JRuby 1.0. So it's definitely me,
> not Ruby, that has the problem.)
>
>
> To flesh things out a little:
>
> 'require' is supposed to load files, unless they have been already
> loaded. It stores an array of loaded files in $" for this purpose.
>
> Okay, suppose we have two programs in two different files:
>
> # test1.rb
> require "test2.rb"
> class One
> def self.testone(); Two.whoistwo; end
> def self.whoisone(); puts "class one"; end
> end
>
> # test2.rb
> require "test1.rb"
> class Two
> def self.testtwo(); One.whoisone; end
> def self.whoistwo(); puts "class two"; end
> end
>
>> ruby -w test1.rb
> ./test1.rb:4: unititialized constant Two (NameError)
>
> Why does this happen? I would have thought that test1 would load
> test2; then test2 would go to load test1 but find it already loaded.
> Apparently what happens is that test2 doesn't get loaded at all.


eh? Your example works fine for me on 1.8.6 unless I'm missing
something? There also doesn't appear to be any mention of Two on line
4 of your test1.rb file, has something changed in the cut&paste?

[alexg@powerbook]/Users/alexg/Desktop(25): cat test1.rb
require "test2.rb"
class One
def self.testone(); Two.whoistwo; end
def self.whoisone(); puts "class one"; end
end
[alexg@powerbook]/Users/alexg/Desktop(26): cat test2.rb
require "test1.rb"
class Two
def self.testtwo(); One.whoisone; end
def self.whoistwo(); puts "class two"; end
end
[alexg@powerbook]/Users/alexg/Desktop(27): ruby test1.rb
[alexg@powerbook]/Users/alexg/Desktop(28): ruby test2.rb

Also, I don't think Stefano's explanation can be quite right - if it
went into an infinite loop as he describes you would never see an
error message, it would just never end...

Alex Gutteridge

Department of Biochemistry
University of Cambridge





Stefano Crocco

7/23/2008 11:39:00 AM

0

On Wednesday 23 July 2008, Alex Gutteridge wrote:
> Also, I don't think Stefano's explanation can be quite right - if it =A0
> went into an infinite loop as he describes you would never see an =A0
> error message, it would just never end...

You're perfectly right. I don't know what I was thinking of when I wrote th=
at=20
mail (actually, I do. I read the subject line and didn't pay enough attenti=
on=20
to the message body. Then, I didn't even think to check whether things were=
=20
indeed going as I thought). Forget my answer completely, and sorry for givi=
ng=20
a wrong information.

Stefano


Calamitas

7/23/2008 11:40:00 AM

0

On Wed, Jul 23, 2008 at 12:18 PM, Stefano Crocco
<stefano.crocco@alice.it> wrote:
> On Wednesday 23 July 2008, Shadowfirebird wrote:
>> >ruby -w test1.rb
>>
>> ./test1.rb:4: unititialized constant Two (NameError)
>
> require 'test1.rb'
>
> is executed:
>
> * the file test1.rb is read and parsed
> * starts the execution of test1.rb
> * the line require 'test2.rb' is executed. This means:
> * the file test2.rb is read and parsed
> * starts the execution of test2.rb
> * the line require 'test1.rb' is executed. This means:
> * the file test1.rb is read and parsed
> * starts the execution of test1.rb
> * the line require 'test2.rb' is executed. This means:
> ...

Actually, I'm neither seeing the error the OP got, neither the endless
loop you seem to get. Looks like a file that is required gets added to
$" before its contents is executed, and that a file passed on the
command line -- like the OP did, so not using the -r switch -- is not.
So basically the execution is as follows:

* the file test1.rb is read and parsed
* starts the execution of test1.rb
* the line require 'test2.rb' is executed. This means:
* the file test2.rb is read and parsed
* adds "test2.rb" to $"
* the line require 'test1.rb' is executed. This means:
* the file test1.rb is read and parsed
* adds "test1.rb" to $"
* the line require 'test1.rb' is executed. Does nothing as
"test1.rb" is in $"
* The rest of test1.rb is executed
* The rest of test2.rb is executed
* The rest of test2.rb is executed again

test1.rb is executed twice, but what probably caused the error in the
OP's original program (the OP pared it down to a point where it
actually did "work") is the fact that "the rest of test1.rb" is
executed before "the rest of test2.rb". It can probably be solved by
doing "ruby -r test1.rb" instead, but that's icky. Generally, if I
*were* to use circular requires, I'd never put "executable code" in
either file, just "declarations". I don't use them though. Not anymore
after I've been bitten a few times.

Peter

Shadowfirebird

7/23/2008 11:40:00 AM

0

I think that Stefano is assuming that Ruby has some sort of defense
from infinite loops. Which is not unreasonable, but I've no idea if
he is right. Other than that, his explanation fits perfectly with my
perception of events.

Interesting. Yes, something got lost in the cut and paste: One
should inherit from Two. I guess I just fiddled with the example and
didn't retest it; dumb of me.

Very odd how it works if you remove that, since test1.rb is referring
to Two anyway.

test1.rb should read:

require "test2.rb"
class One < Two
def self.testone(); Two.whoistwo; end
def self.whoisone(); puts "class one"; end
end

$ ruby -w test1.rb
/test1.rb:2: uninitialized constant Two (NameError)
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in
`gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in
`require'
from ./test2.rb:1
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in
`gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in
`require'
from test1.rb:1




On Wed, Jul 23, 2008 at 12:18 PM, Alex Gutteridge
<alexg@ruggedtextile.com> wrote:
> On 23 Jul 2008, at 10:48, Shadowfirebird wrote:
>
>> Hi,
>>
>> I'm new to ruby and I'm really enjoying it. However, there is one
>> thing...
>>
>> I gather from this list and my poor efforts that ruby does not like
>> files that 'require' each other circularly. I can find nothing in the
>> documentation about this, though; in fact, the description of require
>> seems to actually rule it out. Can anyone explain what is going on
>> here?
>>
>> (This happens in 1.8 and 1.9 and JRuby 1.0. So it's definitely me,
>> not Ruby, that has the problem.)
>>
>>
>> To flesh things out a little:
>>
>> 'require' is supposed to load files, unless they have been already
>> loaded. It stores an array of loaded files in $" for this purpose.
>>
>> Okay, suppose we have two programs in two different files:
>>
>> # test1.rb
>> require "test2.rb"
>> class One
>> def self.testone(); Two.whoistwo; end
>> def self.whoisone(); puts "class one"; end
>> end
>>
>> # test2.rb
>> require "test1.rb"
>> class Two
>> def self.testtwo(); One.whoisone; end
>> def self.whoistwo(); puts "class two"; end
>> end
>>
>>> ruby -w test1.rb
>>
>> ./test1.rb:4: unititialized constant Two (NameError)
>>
>> Why does this happen? I would have thought that test1 would load
>> test2; then test2 would go to load test1 but find it already loaded.
>> Apparently what happens is that test2 doesn't get loaded at all.
>
>
> eh? Your example works fine for me on 1.8.6 unless I'm missing something?
> There also doesn't appear to be any mention of Two on line 4 of your
> test1.rb file, has something changed in the cut&paste?
>
> [alexg@powerbook]/Users/alexg/Desktop(25): cat test1.rb
> require "test2.rb"
> class One
> def self.testone(); Two.whoistwo; end
> def self.whoisone(); puts "class one"; end
> end
> [alexg@powerbook]/Users/alexg/Desktop(26): cat test2.rb
> require "test1.rb"
> class Two
> def self.testtwo(); One.whoisone; end
> def self.whoistwo(); puts "class two"; end
> end
> [alexg@powerbook]/Users/alexg/Desktop(27): ruby test1.rb
> [alexg@powerbook]/Users/alexg/Desktop(28): ruby test2.rb
>
> Also, I don't think Stefano's explanation can be quite right - if it went
> into an infinite loop as he describes you would never see an error message,
> it would just never end...
>
> Alex Gutteridge
>
> Department of Biochemistry
> University of Cambridge
>
>
>
>
>
>



--
Me, I imagine places that I have never seen / The colored lights in
fountains, blue and green / And I imagine places that I will never go
/ Behind these clouds that hang here dark and low
But it's there when I'm holding you / There when I'm sleeping too /
There when there's nothing left of me / Hanging out behind the
burned-out factories / Out of reach but leading me / Into the
beautiful sea

Shadowfirebird

7/23/2008 11:48:00 AM

0

*Is* there any executable code in my two example programs, other than
the "require"s?


On Wed, Jul 23, 2008 at 12:39 PM, Calamitas <calamitates@gmail.com> wrote:
> On Wed, Jul 23, 2008 at 12:18 PM, Stefano Crocco
> <stefano.crocco@alice.it> wrote:
>> On Wednesday 23 July 2008, Shadowfirebird wrote:
>>> >ruby -w test1.rb
>>>
>>> ./test1.rb:4: unititialized constant Two (NameError)
>>
>> require 'test1.rb'
>>
>> is executed:
>>
>> * the file test1.rb is read and parsed
>> * starts the execution of test1.rb
>> * the line require 'test2.rb' is executed. This means:
>> * the file test2.rb is read and parsed
>> * starts the execution of test2.rb
>> * the line require 'test1.rb' is executed. This means:
>> * the file test1.rb is read and parsed
>> * starts the execution of test1.rb
>> * the line require 'test2.rb' is executed. This means:
>> ...
>
> Actually, I'm neither seeing the error the OP got, neither the endless
> loop you seem to get. Looks like a file that is required gets added to
> $" before its contents is executed, and that a file passed on the
> command line -- like the OP did, so not using the -r switch -- is not.
> So basically the execution is as follows:
>
> * the file test1.rb is read and parsed
> * starts the execution of test1.rb
> * the line require 'test2.rb' is executed. This means:
> * the file test2.rb is read and parsed
> * adds "test2.rb" to $"
> * the line require 'test1.rb' is executed. This means:
> * the file test1.rb is read and parsed
> * adds "test1.rb" to $"
> * the line require 'test1.rb' is executed. Does nothing as
> "test1.rb" is in $"
> * The rest of test1.rb is executed
> * The rest of test2.rb is executed
> * The rest of test2.rb is executed again
>
> test1.rb is executed twice, but what probably caused the error in the
> OP's original program (the OP pared it down to a point where it
> actually did "work") is the fact that "the rest of test1.rb" is
> executed before "the rest of test2.rb". It can probably be solved by
> doing "ruby -r test1.rb" instead, but that's icky. Generally, if I
> *were* to use circular requires, I'd never put "executable code" in
> either file, just "declarations". I don't use them though. Not anymore
> after I've been bitten a few times.
>
> Peter
>
>



--
Me, I imagine places that I have never seen / The colored lights in
fountains, blue and green / And I imagine places that I will never go
/ Behind these clouds that hang here dark and low
But it's there when I'm holding you / There when I'm sleeping too /
There when there's nothing left of me / Hanging out behind the
burned-out factories / Out of reach but leading me / Into the
beautiful sea

Calamitas

7/23/2008 12:33:00 PM

0

On Wed, Jul 23, 2008 at 1:47 PM, Shadowfirebird
<shadowfirebird@gmail.com> wrote:
> *Is* there any executable code in my two example programs, other than
> the "require"s?

The code you posted here works as is. Nowhere is the reference to Two
executed *in the code you posted*. If I execute your code *as you
posted it here*, it doesn't raise the exception, it only gives some
warnings about redefining methods. Note that he error message you
posted refers to a reference to the constant Two in line 4, but in the
code *you posted here*, the only reference is on line 3. So I can only
assume that the error you get is caused by some code you have cut out
before you posted. Something needed to call one.testone, but it is not
*in the code you posted*. You *must* have had executable code when you
got that error.

Peter

Calamitas

7/23/2008 12:43:00 PM

0

On Wed, Jul 23, 2008 at 1:39 PM, Shadowfirebird
<shadowfirebird@gmail.com> wrote:
> I think that Stefano is assuming that Ruby has some sort of defense
> from infinite loops. Which is not unreasonable, but I've no idea if
> he is right. Other than that, his explanation fits perfectly with my
> perception of events.
>
> Interesting. Yes, something got lost in the cut and paste: One
> should inherit from Two. I guess I just fiddled with the example and
> didn't retest it; dumb of me.
>
> Very odd how it works if you remove that, since test1.rb is referring
> to Two anyway.
>
> test1.rb should read:
>
> require "test2.rb"
> class One < Two
> def self.testone(); Two.whoistwo; end
> def self.whoisone(); puts "class one"; end
> end

I see. I quoted "executable code" because declarations in Ruby too are
executable code. Everything is. Even require is. The execution order I
posted in my other post shows you the problem: everything after the
require in test1.rb is executed before everything after the require in
test2.rb, so you inherit from Two before Two is defined. To me, the
reference to Two in the class definition is executable code. Really,
it is, you can put any expression there:

inherit_from_two = true

class One < (inherit_from_two ? Two : Three)
end

Peter

Shadowfirebird

7/23/2008 12:46:00 PM

0

I did indeed miss something out from the code I originally posted --
but it was an inheritance, not something executable. the definition
of One in testa.rb should read "Class One < Two". (See my earlier
post.)



On Wed, Jul 23, 2008 at 1:32 PM, Calamitas <calamitates@gmail.com> wrote:
> On Wed, Jul 23, 2008 at 1:47 PM, Shadowfirebird
> <shadowfirebird@gmail.com> wrote:
>> *Is* there any executable code in my two example programs, other than
>> the "require"s?
>
> The code you posted here works as is. Nowhere is the reference to Two
> executed *in the code you posted*. If I execute your code *as you
> posted it here*, it doesn't raise the exception, it only gives some
> warnings about redefining methods. Note that he error message you
> posted refers to a reference to the constant Two in line 4, but in the
> code *you posted here*, the only reference is on line 3. So I can only
> assume that the error you get is caused by some code you have cut out
> before you posted. Something needed to call one.testone, but it is not
> *in the code you posted*. You *must* have had executable code when you
> got that error.
>
> Peter
>
>



--
Me, I imagine places that I have never seen / The colored lights in
fountains, blue and green / And I imagine places that I will never go
/ Behind these clouds that hang here dark and low
But it's there when I'm holding you / There when I'm sleeping too /
There when there's nothing left of me / Hanging out behind the
burned-out factories / Out of reach but leading me / Into the
beautiful sea