circular 'require'

Discussion in 'Ruby' started by Shadowfirebird, Jul 23, 2008.

  1. 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
     
    Shadowfirebird, Jul 23, 2008
    #1
    1. Advertising

  2. 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
     
    Stefano Crocco, Jul 23, 2008
    #2
    1. Advertising

  3. 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?
     
    Shadowfirebird, Jul 23, 2008
    #3
  4. 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
     
    Alex Gutteridge, Jul 23, 2008
    #4
  5. 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
     
    Stefano Crocco, Jul 23, 2008
    #5
  6. Shadowfirebird

    Calamitas Guest

    On Wed, Jul 23, 2008 at 12:18 PM, Stefano Crocco
    <> 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
     
    Calamitas, Jul 23, 2008
    #6
  7. 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
    <> 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, Jul 23, 2008
    #7
  8. *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 <> wrote:
    > On Wed, Jul 23, 2008 at 12:18 PM, Stefano Crocco
    > <> 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
     
    Shadowfirebird, Jul 23, 2008
    #8
  9. Shadowfirebird

    Calamitas Guest

    On Wed, Jul 23, 2008 at 1:47 PM, Shadowfirebird
    <> 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, Jul 23, 2008
    #9
  10. Shadowfirebird

    Calamitas Guest

    On Wed, Jul 23, 2008 at 1:39 PM, Shadowfirebird
    <> 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
     
    Calamitas, Jul 23, 2008
    #10
  11. 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 <> wrote:
    > On Wed, Jul 23, 2008 at 1:47 PM, Shadowfirebird
    > <> 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
     
    Shadowfirebird, Jul 23, 2008
    #11
  12. Shadowfirebird

    Calamitas Guest

    On Wed, Jul 23, 2008 at 2:46 PM, Shadowfirebird
    <> wrote:
    > 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.)


    See my later post. ;-)

    Peter
     
    Calamitas, Jul 23, 2008
    #12
  13. On Wednesday 23 July 2008, Shadowfirebird wrote:
    > *Is* there any executable code in my two example programs, other than
    > the "require"s?


    The short answer to your question is that in ruby everything is "executable
    code", including class and method definitions.

    The "executable code" Peter refers to is that code which is executed
    immediately when the script is required/loaded/called. This is any kind of
    code except that enclosed in method definitions and blocks. Code in method
    bodies is executed only when the method is called. Code in blocks is executed
    only when the block is called. All other code is executed as soon as it's
    seen.

    This explains why the line

    def self.testone(); Two.whoistwo; end

    in class One doesn't cause trouble: the body of the method won't be executed
    until the method is called. Until then, ruby has no interest in looking for a
    constant called Two. This greatly reduces the need of recursive requires: a
    class or constant need only to be defined when the code which uses it is
    actually executed, not when it is 'read'.

    Regarding why your code doesn't work, I think Peter is almost correct in his
    analisys, except when he states that the name of the file is stored in $"
    before being loaded, in the require case. After actually testing your code
    (which I should have done before sending my first mail), I went looking in the
    ruby source code to try to understand why the behavior I described wasn't the
    observed one. From what I understand (I'm not very good at reading C code), it
    seems that ruby has a mechanism which avoids exactly this kind of endless
    loop: when it starts requiring a file, it stores it in some kind of table
    (which as nothing to do with $"), where it remains until it's been 'fully'
    required (that is, until the code it contains, including other requires, has
    been executed). While the file is in the table, attempts to require it again
    fail, just as it had been put in $". Only at the end of this process, the file
    is added to $". For those interested, the involved C functions are
    rb_require_safe and load_lock, both in eval.c in the ruby distribution.

    Unfortunately, my skills haven't been enough to understand what happens when
    the file is given as a argument to ruby, rather than required.

    Sorry for the wrong information I gave before

    Stefano
     
    Stefano Crocco, Jul 23, 2008
    #13
  14. [Note: parts of this message were removed to make it a legal post.]

    On Wed, 2008-07-23 at 20:39 +0900, Calamitas wrote:

    > 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.



    Here's what the OP was talking about, I suspect, taken down to its
    briefest possible example:


    $ cat test1.rb
    require 'test2'
    class NeededInTest2
    @@funny_stuff = NeededInTest1.new
    end

    michael@isolde:~/junk$ cat test2.rb
    require 'test1'
    class NeededInTest1
    @@funny_stuff = NeededInTest2.new
    end

    michael@isolde:~/junk$ ruby test1.rb
    ./test1.rb:3: uninitialized constant NeededInTest2::NeededInTest1 (NameError)
    from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    from ./test2.rb:1
    from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    from test1.rb:1


    This is the problem boiled down to its essence. There is no easy way
    out of this either that doesn't either a) mangle the architecture or b)
    fail. You can't use something like C/++'s "include guards" to get
    around this, nor can you use any kind of super-class elevation trick or
    the like. What would be needed to solve this issue would be for ruby to
    have a proper sense of modules and do linkage across those instead of
    treating the entire tree of requires as, in effect, one ginormous text
    file.

    --
    Michael T. Richter <> (GoogleTalk:
    )
    Theory is knowledge that doesn't work. Practice is when everything works
    and you don't know why. (Hermann Hesse)
     
    Michael T. Richter, Jul 23, 2008
    #14
  15. Shadowfirebird

    Calamitas Guest

    On Wed, Jul 23, 2008 at 3:02 PM, Stefano Crocco <> wrote:
    > Regarding why your code doesn't work, I think Peter is almost correct in his
    > analisys, except when he states that the name of the file is stored in $"
    > before being loaded, in the require case. After actually testing your code
    > (which I should have done before sending my first mail), I went looking in the
    > ruby source code to try to understand why the behavior I described wasn't the
    > observed one. From what I understand (I'm not very good at reading C code), it
    > seems that ruby has a mechanism which avoids exactly this kind of endless
    > loop: when it starts requiring a file, it stores it in some kind of table
    > (which as nothing to do with $"), where it remains until it's been 'fully'
    > required (that is, until the code it contains, including other requires, has
    > been executed). While the file is in the table, attempts to require it again
    > fail, just as it had been put in $". Only at the end of this process, the file
    > is added to $". For those interested, the involved C functions are
    > rb_require_safe and load_lock, both in eval.c in the ruby distribution.


    Good to know it works this way. I wanted to test whether it was
    actually stored in $", but I was too lazy. Dang! ;-) But either way,
    something had to break the infinite loop because there was none.

    Peter
     
    Calamitas, Jul 23, 2008
    #15
  16. I found a very easy way around it. This is what bothers me. If it's
    that easy, why doesn't require do it?

    # test1.rb
    $" << "test1.rb"
    load("test2.rb") unless $".include?("test2.rb")
    class One < Two
    def self.testone(); Two.whoistwo; end
    def self.whoisone(); puts "class one"; end
    end

    # test2.rb
    $" << "test2.rb"
    load("test1.rb") unless $".include?("test1.rb")
    class Two
    def self.testtwo(); One.whoisone; end
    def self.whoistwo(); puts "class two"; end
    end


    On Wed, Jul 23, 2008 at 2:03 PM, Michael T. Richter
    <> wrote:
    > On Wed, 2008-07-23 at 20:39 +0900, Calamitas wrote:
    >
    >> 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.

    >
    >
    > Here's what the OP was talking about, I suspect, taken down to its
    > briefest possible example:
    >
    >
    > $ cat test1.rb
    > require 'test2'
    > class NeededInTest2
    > @@funny_stuff = NeededInTest1.new
    > end
    >
    > michael@isolde:~/junk$ cat test2.rb
    > require 'test1'
    > class NeededInTest1
    > @@funny_stuff = NeededInTest2.new
    > end
    >
    > michael@isolde:~/junk$ ruby test1.rb
    > ./test1.rb:3: uninitialized constant NeededInTest2::NeededInTest1 (NameError)
    > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    > from ./test2.rb:1
    > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    > from test1.rb:1
    >
    >
    > This is the problem boiled down to its essence. There is no easy way
    > out of this either that doesn't either a) mangle the architecture or b)
    > fail. You can't use something like C/++'s "include guards" to get
    > around this, nor can you use any kind of super-class elevation trick or
    > the like. What would be needed to solve this issue would be for ruby to
    > have a proper sense of modules and do linkage across those instead of
    > treating the entire tree of requires as, in effect, one ginormous text
    > file.
    >
    > --
    > Michael T. Richter <> (GoogleTalk:
    > )
    > Theory is knowledge that doesn't work. Practice is when everything works
    > and you don't know why. (Hermann Hesse)
    >




    --
    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, Jul 23, 2008
    #16
  17. [Note: parts of this message were removed to make it a legal post.]

    On Wed, 2008-07-23 at 22:48 +0900, Shadowfirebird wrote:

    > I found a very easy way around it. This is what bothers me. If it's
    > that easy, why doesn't require do it?




    Try my code with that:


    michael@isolde:~/junk$ cat test1.rb
    $" << "test1.rb"
    load("test2.rb") unless $".include?("test2.rb")
    class NeededInTest2
    @@funny_stuff = NeededInTest1.new
    end

    michael@isolde:~/junk$ cat test2.rb
    $" << "test2.rb"
    load("test1.rb") unless $".include?("test1.rb")
    class NeededInTest1
    @@funny_stuff = NeededInTest2.new
    end

    michael@isolde:~/junk$ ruby test1.rb
    ./test2.rb:4: uninitialized constant NeededInTest1::NeededInTest2 (NameError)
    from test1.rb:2:in `load'
    from test1.rb:2


    The real solution remains a proper system of modules in Ruby.


    > # test1.rb
    > $" << "test1.rb"
    > load("test2.rb") unless $".include?("test2.rb")
    > class One < Two
    > def self.testone(); Two.whoistwo; end
    > def self.whoisone(); puts "class one"; end
    > end
    >
    > # test2.rb
    > $" << "test2.rb"
    > load("test1.rb") unless $".include?("test1.rb")
    > class Two
    > def self.testtwo(); One.whoisone; end
    > def self.whoistwo(); puts "class two"; end
    > end
    >
    >
    > On Wed, Jul 23, 2008 at 2:03 PM, Michael T. Richter
    > <> wrote:
    > > On Wed, 2008-07-23 at 20:39 +0900, Calamitas wrote:
    > >
    > >> 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.

    > >
    > >
    > > Here's what the OP was talking about, I suspect, taken down to its
    > > briefest possible example:
    > >
    > >
    > > $ cat test1.rb
    > > require 'test2'
    > > class NeededInTest2
    > > @@funny_stuff = NeededInTest1.new
    > > end
    > >
    > > michael@isolde:~/junk$ cat test2.rb
    > > require 'test1'
    > > class NeededInTest1
    > > @@funny_stuff = NeededInTest2.new
    > > end
    > >
    > > michael@isolde:~/junk$ ruby test1.rb
    > > ./test1.rb:3: uninitialized constant NeededInTest2::NeededInTest1 (NameError)
    > > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    > > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    > > from ./test2.rb:1
    > > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    > > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    > > from test1.rb:1
    > >
    > >
    > > This is the problem boiled down to its essence. There is no easy way
    > > out of this either that doesn't either a) mangle the architecture or b)
    > > fail. You can't use something like C/++'s "include guards" to get
    > > around this, nor can you use any kind of super-class elevation trick or
    > > the like. What would be needed to solve this issue would be for ruby to
    > > have a proper sense of modules and do linkage across those instead of
    > > treating the entire tree of requires as, in effect, one ginormous text
    > > file.
    > >
    > > --
    > > Michael T. Richter <> (GoogleTalk:
    > > )
    > > Theory is knowledge that doesn't work. Practice is when everything works
    > > and you don't know why. (Hermann Hesse)
    > >

    >
    >
    >


    --
    Michael T. Richter <> (GoogleTalk:
    )
    It's OK to figure out murder mysteries, but you shouldn't need to figure
    out code. You should be able to read it. (Steve McConnell)
     
    Michael T. Richter, Jul 23, 2008
    #17
  18. Yes, I can see now that in your example no order of loading will allow
    the objects to be defined before they are used. That is certainly a
    trap to avoid. Thanks, Michael.

    The annoying thing is that my actual code worked "fine" when it was
    all in one file but stopped working when I split the objects into
    different files. And of course, it's too long to post here.

    I may come back and ask different questions once I've had a bit of a
    think. Thanks to everyone.

    Shadowfirebird.

    On Wed, Jul 23, 2008 at 3:39 PM, Michael T. Richter
    <> wrote:
    > On Wed, 2008-07-23 at 22:48 +0900, Shadowfirebird wrote:
    >
    >> I found a very easy way around it. This is what bothers me. If it's
    >> that easy, why doesn't require do it?

    >
    >
    >
    > Try my code with that:
    >
    >
    > michael@isolde:~/junk$ cat test1.rb
    > $" << "test1.rb"
    > load("test2.rb") unless $".include?("test2.rb")
    > class NeededInTest2
    > @@funny_stuff = NeededInTest1.new
    > end
    >
    > michael@isolde:~/junk$ cat test2.rb
    > $" << "test2.rb"
    > load("test1.rb") unless $".include?("test1.rb")
    > class NeededInTest1
    > @@funny_stuff = NeededInTest2.new
    > end
    >
    > michael@isolde:~/junk$ ruby test1.rb
    > ./test2.rb:4: uninitialized constant NeededInTest1::NeededInTest2 (NameError)
    > from test1.rb:2:in `load'
    > from test1.rb:2
    >
    >
    > The real solution remains a proper system of modules in Ruby.
    >
    >
    >> # test1.rb
    >> $" << "test1.rb"
    >> load("test2.rb") unless $".include?("test2.rb")
    >> class One < Two
    >> def self.testone(); Two.whoistwo; end
    >> def self.whoisone(); puts "class one"; end
    >> end
    >>
    >> # test2.rb
    >> $" << "test2.rb"
    >> load("test1.rb") unless $".include?("test1.rb")
    >> class Two
    >> def self.testtwo(); One.whoisone; end
    >> def self.whoistwo(); puts "class two"; end
    >> end
    >>
    >>
    >> On Wed, Jul 23, 2008 at 2:03 PM, Michael T. Richter
    >> <> wrote:
    >> > On Wed, 2008-07-23 at 20:39 +0900, Calamitas wrote:
    >> >
    >> >> 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.
    >> >
    >> >
    >> > Here's what the OP was talking about, I suspect, taken down to its
    >> > briefest possible example:
    >> >
    >> >
    >> > $ cat test1.rb
    >> > require 'test2'
    >> > class NeededInTest2
    >> > @@funny_stuff = NeededInTest1.new
    >> > end
    >> >
    >> > michael@isolde:~/junk$ cat test2.rb
    >> > require 'test1'
    >> > class NeededInTest1
    >> > @@funny_stuff = NeededInTest2.new
    >> > end
    >> >
    >> > michael@isolde:~/junk$ ruby test1.rb
    >> > ./test1.rb:3: uninitialized constant NeededInTest2::NeededInTest1 (NameError)
    >> > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    >> > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    >> > from ./test2.rb:1
    >> > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    >> > from /home/michael/software/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    >> > from test1.rb:1
    >> >
    >> >
    >> > This is the problem boiled down to its essence. There is no easy way
    >> > out of this either that doesn't either a) mangle the architecture or b)
    >> > fail. You can't use something like C/++'s "include guards" to get
    >> > around this, nor can you use any kind of super-class elevation trick or
    >> > the like. What would be needed to solve this issue would be for ruby to
    >> > have a proper sense of modules and do linkage across those instead of
    >> > treating the entire tree of requires as, in effect, one ginormous text
    >> > file.
    >> >
    >> > --
    >> > Michael T. Richter <> (GoogleTalk:
    >> > )
    >> > Theory is knowledge that doesn't work. Practice is when everything works
    >> > and you don't know why. (Hermann Hesse)
    >> >

    >>
    >>
    >>

    >
    > --
    > Michael T. Richter <> (GoogleTalk:
    > )
    > It's OK to figure out murder mysteries, but you shouldn't need to figure
    > out code. You should be able to read it. (Steve McConnell)
    >




    --
    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, Jul 23, 2008
    #18
  19. Shadowfirebird

    Pit Capitain Guest

    2008/7/23 Shadowfirebird <>:
    > The annoying thing is that my actual code worked "fine" when it was
    > all in one file but stopped working when I split the objects into
    > different files.


    If you write everything in one file, it only works if you define class
    Two before class One. If you reverse the definitions, you get the same
    error. In Ruby you have to define a constant before you can use it.
    This is independent of require.

    If you create a file main.rb with

    require "test1"
    require "test2"

    the code is working. If you reverse those two lines, you get the
    error. This is the same behavior as in the one-file example. I suggest
    you add some debugging statements in your code to understand the
    sequence when loading files, for example in file test1.rb:

    puts "start test1"
    require "test2"
    puts "before test1.One"
    class One < Two
    puts "in test1.One"
    def self.testone(); Two.whoistwo; end
    def self.whoisone(); puts "class one"; end
    end
    puts "end test1"

    or something like this.

    Regards,
    Pit
     
    Pit Capitain, Jul 24, 2008
    #19
  20. I've had my think.

    I think I have some conclusions which might be useful for other people
    in the same boat, and since I've not seen them anywhere else, I hope
    you'll allow me to bore you with them. No doubt cooler heads than
    mine will see things differently, but there you go. And, maybe I'm
    missing the obvious, anyway.

    1) require is mildly broken (a). It doesn't set $" until after the
    given file is loaded. This can lead to errors when you have a
    circular call of requires. If it set $" first, circular references
    would just be ignored. Of course, you should never actually *need* to
    have circular 'require' referefences, but: see my next point.

    2) Require is mildly broken (b). Secondly -- and I suppose that this
    is less of a bug and more of a bugbear -- there is no way to
    distinguish between require-because-it-wont-load-otherwise and
    require-because-some-methods-wont-run-otherwise. This second sort
    isn't entirely necessary, but it's aesthetically pleasing and useful
    for unit testing.

    3) You can get around (1) by manually pushing the program name into $"
    at the start of each file. This done, you can use a new idiom -- END
    { require } -- to specify
    require-because-some-methods-wont-run-otherwise. Not the prettiest
    thing, but neither the ugliest, I think: "in the end, this is
    required."


    Now I suppose I'd better show my working if I'm going to say anything
    as contentious as all that. Apologies for the length of this, but
    then, you can stop reading now. OTOH, I'd really like to hear other
    points of view.

    A new set of example programs:

    # runner.rb
    require 'root'
    Root.create_data()
    Root.customers.each{|c| puts c.oid, c.name}

    # root.rb
    require 'customer'
    class Root
    @@list = []

    def self.create_data()
    Customer.new("Dolly")
    Customer.new("Reymond")
    end

    def self.customers()
    return @@list.find_all{|x| x.kind_of?(Customer)}
    end

    attr_accessor :eek:id

    def initialize()
    @oid = @@list.size
    @@list << self
    end
    end

    # customer.rb
    require 'root'
    class Customer < Root
    attr_accessor :name

    def initialize(name)
    super()
    @name = name
    end
    end


    Broadly speaking this follows the same approach as my actual code. As
    listed, of course, it doesn't work: run runner.rb and it loads
    root.rb, which starts by loading customer.rb, which starts by loading
    root.rb, which...

    I needed to make a distinction (#2 above) between code that referred
    to another object when it was loaded; and code that referred to
    another object when it was run. root.rb doesn't need a 'require
    'customer'; runner.rb does. But this is rather unhelpful when it
    comes to unit testing: when writing the test class for Root I
    shouldn't have to remember to require customer. Ideally I should be
    able to put the require in root.rb somehow.

    For experimental purposes lets try putting the "require 'customer'" at
    the *end* of root.rb. This has practical implications -- no
    programmer is going to look for it there -- but it works; running
    runner.rb produces no errors. Oddly enough, putting "END {require
    'customer'}" at the start of the file does not work. But, wait.

    What is more interesting, is that if you run root.rb or customer.rb
    you get warnings about redefined methods. You shouldn't see this if
    require works as described -- a file should never be called twice.
    What appears to be happening is that $" is set after the file is
    loaded -- that is, it's not being set until the chain of requires has
    finished. We can defeat that behaviour by adding $" << "<filename>"
    to the start of root.rb and customer.rb. The warnings now disappear.
    (And that seems like a bug to me, or at least an undocumented feature.)

    This is really odd: if we now try END{ require 'customer'} at the
    start of root.rb -- well, after the $" thing -- it *works*. Really
    odd, but nice.

    So, root.rb now looks like this:

    # root.rb
    $" << "root.rb"
    END { require 'customer' }
    class Root
    @@list = []

    def self.create_data()
    Customer.new("Dolly")
    Customer.new("Reymond")
    end

    def self.customers()
    return @@list.find_all{|x| x.kind_of?(Customer)}
    end

    attr_accessor :eek:id

    def initialize()
    @oid = @@list.size
    @@list << self
    end
    end

    And I can live with that. But I'm open to suggestions -- any
    suggestions, except rude ones...
     
    Shadowfirebird, Jul 24, 2008
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dan C Douglas

    Circular references are not supported

    Dan C Douglas, Aug 13, 2003, in forum: ASP .Net
    Replies:
    1
    Views:
    1,419
    Steve C. Orr, MCSD
    Aug 13, 2003
  2. Bill Jones

    Re: Circular References

    Bill Jones, Apr 8, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    431
    =?Utf-8?B?RGF2ZQ==?=
    Apr 8, 2004
  3. =?Utf-8?B?S2VpdGggRi4=?=

    Circular References in Visual Studio

    =?Utf-8?B?S2VpdGggRi4=?=, Aug 6, 2004, in forum: ASP .Net
    Replies:
    3
    Views:
    6,314
    Cowboy \(Gregory A. Beamer\) [MVP]
    Aug 6, 2004
  4. Kiuhnm
    Replies:
    16
    Views:
    747
    Jonathan Mcdougall
    Jan 3, 2005
  5. Adam P.
    Replies:
    0
    Views:
    160
    Adam P.
    Dec 15, 2010
Loading...

Share This Page