From: Shadowfirebird Date: 2008-07-24T17:33:28+09:00 Subject: Re: circular 'require' I take that back. But I don't understand why it's not true! On Thu, Jul 24, 2008 at 9:14 AM, Shadowfirebird wrote: > ::slaps heel of hand to forehead:: > > The END{ require } thing isn't needed. Setting $" solves the infinite > loop -- you can just call require. > > > On Thu, Jul 24, 2008 at 9:02 AM, Shadowfirebird > wrote: >> 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 :oid >> >> 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 $" << "" >> 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 :oid >> >> 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... >> >> > > > > -- > 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 > > -- 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