circular 'require'

S

Shadowfirebird

::slaps heel of hand to forehead::

The END{ require } thing isn't needed. Setting $" solves the infinite
loop -- you can just call require.


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



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

Shadowfirebird

I take that back. But I don't understand why it's not true!


::slaps heel of hand to forehead::

The END{ require } thing isn't needed. Setting $" solves the infinite
loop -- you can just call require.


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



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

Sebastian Hungerecker

Shadowfirebird said:
# root.rb
require 'customer'
class Root
=A0 =A0@@list =3D []

=A0 =A0def self.create_data()
=A0 =A0 =A0 Customer.new("Dolly")
=A0 =A0 =A0 Customer.new("Reymond")
=A0 =A0end

=A0 =A0def self.customers()
=A0 =A0 =A0 return @@list.find_all{|x| x.kind_of?(Customer)}
=A0 =A0end

=A0 =A0attr_accessor :eek:id

=A0 =A0def initialize()
=A0 =A0 =A0 @oid =3D @@list.size
=A0 =A0 =A0 @@list << self
=A0 =A0end
end

# customer.rb
require 'root'
class Customer < Root
=A0 =A0attr_accessor :name

=A0 =A0def initialize(name)
=A0 =A0 =A0 super()
=A0 =A0 =A0 @name =3D name
=A0 =A0end
end

Just leave out the require 'root' and the whole thing will work. You're mak=
ing=20
things out to be way more complicated than they are. There is no need to=20
require the file that requires you.

HTH,
Sebastian
=2D-=20
Jabber: (e-mail address removed)
ICQ: 205544826
 
P

Pit Capitain

2008/7/24 Sebastian Hungerecker said:
Just leave out the require 'root' and the whole thing will work. You're making
things out to be way more complicated than they are. There is no need to
require the file that requires you.

Like the OP, I like to require files that are needed to execute the
current file, in order to not become dependent on the exact require
sequence. So another solution to the problem of his would be to place
the require calls at the appropriate places, for example in root.rb:

class Root

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

end

But instead of reading and trying to understand what others have
posted he keeps complaining about require being broken. I don't see
anything in the documentation of require that doesn't behave as
advertised.

Regards,
Pit
 
S

Shadowfirebird

I apologise if I seem to be complaining. That's not it. I found a
problem in what I was doing, worked out a way around it, and --partly
since it involved a "gotcha" that I'd never seen explained anywhere
else, partly because I'm still learning -- I posted it here to see if
anyone had any comment, had a better idea.

Pit, I think your solution is interesting because it imbeds a class
definition inside a class. Are there any downsides to that?



Like the OP, I like to require files that are needed to execute the
current file, in order to not become dependent on the exact require
sequence. So another solution to the problem of his would be to place
the require calls at the appropriate places, for example in root.rb:

class Root

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

end

But instead of reading and trying to understand what others have
posted he keeps complaining about require being broken. I don't see
anything in the documentation of require that doesn't behave as
advertised.

Regards,
Pit



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

Calamitas

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.

Require isn't broken. Essentially, you could say that require has its
internal version of $" that is set before executing a file (although
as Stefano pointed out, it is maintained as the difference with the
externally visible $").

Require works as advertised. But "ruby test1.rb" doesn't do a require
of test1.rb, "ruby -r test1.rb" does that. "ruby test1.rb" simply
executes the file without looking at or setting $", which is what
#load does too. This is by design. If you explicitly set $" in
test1.rb, you essentially give a load of that file some of the
semantics of a require.

Peter
 
S

Shadowfirebird

Ah! Thank you! That's brilliant!

Require isn't broken. Essentially, you could say that require has its
internal version of $" that is set before executing a file (although
as Stefano pointed out, it is maintained as the difference with the
externally visible $").

Require works as advertised. But "ruby test1.rb" doesn't do a require
of test1.rb, "ruby -r test1.rb" does that. "ruby test1.rb" simply
executes the file without looking at or setting $", which is what
#load does too. This is by design. If you explicitly set $" in
test1.rb, you essentially give a load of that file some of the
semantics of a require.

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
 
P

Pit Capitain

2008/7/24 Shadowfirebird said:
I apologise if I seem to be complaining.

No need to apologize, everybody should have the right to complain. I
thought that you either hadn't read or tried what I wrote in my first
mail and therefore got the (in my view) false impression that require
was broken. That's the reason for my maybe too harsh an answer, sorry.
Pit, I think your solution is interesting because it imbeds a class
definition inside a class. Are there any downsides to that?

Calling require inside a class definition or even inside a method
doesn't embed the loaded constants (class names are constants too)
into the "calling" class. It just changes the time when require is
called. In your case, it would mean that require is called *after*
creating the class Root, so that the required file can use that name.

Sometimes I've seen code that requires certain files only if some
method is called which needs the required functionality:

def method_with_special_requirements
require "rarely_used_library"
use_the_required_functionality
end

This avoids the overhead of loading all the files everytime, even if
the funcationality isn't used at all.

Regards,
Pit
 
S

Shadowfirebird

Pit, your idiom of putting require inside the class definition appears
to be considerably more robust than my half-baked notions.

No need to apologize, everybody should have the right to complain. I
thought that you either hadn't read or tried what I wrote in my first
mail and therefore got the (in my view) false impression that require
was broken. That's the reason for my maybe too harsh an answer, sorry.


Calling require inside a class definition or even inside a method
doesn't embed the loaded constants (class names are constants too)
into the "calling" class. It just changes the time when require is
called. In your case, it would mean that require is called *after*
creating the class Root, so that the required file can use that name.

Sometimes I've seen code that requires certain files only if some
method is called which needs the required functionality:

def method_with_special_requirements
require "rarely_used_library"
use_the_required_functionality
end

This avoids the overhead of loading all the files everytime, even if
the funcationality isn't used at all.

Regards,
Pit



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

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top