Log4r and Ruby 1.8.0 in Singleton problems

Somethings rotten…

david% irb
irb(main):001:0> require 'Singleton’
irb(main):002:0> class Something
irb(main):003:1> include Singleton
irb(main):004:1> end
irb(main):005:0> require ‘log4r’
/usr/local/lib/ruby/1.8/singleton.rb:119: warning: already initialized
constant FirstInstanceCall
NameError: undefined method extend_object' forSingleton’
from /usr/local/lib/ruby/1.8/singleton.rb:161:in undef_method' from /usr/local/lib/ruby/1.8/singleton.rb:161 from /usr/local/lib/ruby/site_ruby/1.8/log4r/repository.rb:4:inrequire’
from /usr/local/lib/ruby/site_ruby/1.8/log4r/repository.rb:4
from
/usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/
outputterfactory.rb:5:in require' from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputterfactory.rb:5 from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputter.rb:10:inrequire’
from
/usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputter.rb:10
from
/usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/iooutputter.rb:2:in
require' from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/iooutputter.rb:2 from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/fileoutputter.rb:4:inrequire’
from
/usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/fileoutputter.rb:4
from /usr/local/lib/ruby/site_ruby/1.8/log4r.rb:8:in require' from /usr/local/lib/ruby/site_ruby/1.8/log4r.rb:8 from (irb):5:inrequire’
from (irb):5
from
/usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/
outputter.rb:118irb(main):006:0>

It doens’t do this unless I make another Singleton-class first. Any got
a clue?

···


David Heinemeier Hansson,
http://www.loudthinking.com/ – Thoughts for free
http://www.nextangle.com/ – Thoughts for hire

irb(main):001:0> require 'Singleton'

                             ^^^^^^^^^

What is this module ?

Guy Decoux

David Heinemeier Hansson wrote:

Somethings rotten…

david% irb
irb(main):001:0> require ‘Singleton’
irb(main):002:0> class Something
irb(main):003:1> include Singleton
irb(main):004:1> end
irb(main):005:0> require ‘log4r’
/usr/local/lib/ruby/1.8/singleton.rb:119: warning: already initialized
constant FirstInstanceCall
NameError: undefined method extend_object' for Singleton’

On windows you will get the same error message. However, if you
write

···

require ‘singleton’
class A
include Singleton
end
require ‘log4r’

you’ll be fine. My guess is that ruby thinks'' that Singleton.rb"
and singleton.rb" (in my case installed in E:/Ruby/lib/ruby/1.8) are different files - however in the windows world or on a Macs HFS+ file-system Singleton.rb" and ``singleton.rb’’ refer to the
same file.

This difference results in unnecessary reload of ``singleton.rb’’
which in turn triggers the NameError of Ruby trying to undefine
an already undefined method (extend_object).

I suggest that you consistently use the lower case version require
‘singleton’. Alternatively change the line

161 > undef_method :extend_object

in singleton.rb to

161 > undef_method :extend_object unless defined? :extend_object

/Christoph

irb(main):001:0> require ‘Singleton’
^^^^^^^^^

What is this module ?

I’m sorry. I recreated the test case too quickly (and wrong). Here’s a
better one:

log4r.rbx:

require ‘log4r’

Log = Log4r::Logger.new(“AC”)
Log.add Log4r::StderrOutputter.new ‘console’

…which produce:

/usr/local/lib/ruby/1.8/singleton.rb:161:in undef_method': undefined method extend_object’ for Singleton' (NameError) from /usr/local/lib/ruby/1.8/singleton.rb:161 from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in load’
from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in require' from /usr/local/lib/ruby/site_ruby/1.8/log4r/repository.rb:4 from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in load’
from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in require' from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputterfactory.rb:5 from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in load’
… 12 levels…
from /usr/local/lib/ruby/1.8/auto-reload.rb:77:in require' from /Users/david/Sites/extranet/log4r.rbx:1 from /usr/local/lib/ruby/1.8/apache/ruby-run.rb:53:in load’
from /usr/local/lib/ruby/1.8/apache/ruby-run.rb:53:in `handler’

The contents of log4r.rbx works fine if used in IRB, but not under
mod_ruby.

/ David

log4r.rbx:

ruby is trying to load *twice* the same module 'singleton.rb' : it do this
because it don't have the same path, or name, or version, for singleton.rb

svg% cat b.rb
#!/usr/bin/ruby
require 'singleton'
class Something
   include Singleton
end
require 'log4r'
svg%

svg% b.rb
svg%

Now

svg% cat b.rb
#!/usr/bin/ruby
require '/usr/local/lib/ruby/1.8/singleton.rb'
class Something
   include Singleton
end
require 'log4r'
svg%

svg% b.rb
/usr/local/lib/ruby/1.8/singleton.rb:119: warning: already initialized constant FirstInstanceCall
/usr/local/lib/ruby/1.8/singleton.rb:161:in `undef_method': undefined method `extend_object' for `Singleton' (NameError)
        from /usr/local/lib/ruby/1.8/singleton.rb:161
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/repository.rb:4:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/repository.rb:4
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputterfactory.rb:5:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputterfactory.rb:5
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputter.rb:10:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/outputter.rb:10
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/iooutputter.rb:2:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/iooutputter.rb:2
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/fileoutputter.rb:4:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r/outputter/fileoutputter.rb:4
        from /usr/local/lib/ruby/site_ruby/1.8/log4r.rb:8:in `require'
        from /usr/local/lib/ruby/site_ruby/1.8/log4r.rb:8
        from ./b.rb:6:in `require'
        from ./b.rb:6
svg%

For ruby

   require '/usr/local/lib/ruby/1.8/singleton.rb'

is different (it think that it's another module)

   require 'singleton' # which is in repository.rb

This is why it give an error

Another reason is that you mix
   * ruby 1.8.0
   * mod_ruby built with ruby 1.6.8

Guy Decoux

For ruby

require ‘/usr/local/lib/ruby/1.8/singleton.rb’

is different (it think that it’s another module)

require ‘singleton’ # which is in repository.rb

This is why it give an error

Right. But the log4r.rbx doesn’t contain any requirements to include
the singleton module more than once. It’s only those three lines:

require ‘log4r’
Log = Log4r::Logger.new(“AC”)
Log.add Log4r::StderrOutputter.new ‘console’

…and since those three lines work just fine in IRB, I suspect
something fishy with mod_ruby.

Another reason is that you mix

  • ruby 1.8.0
  • mod_ruby built with ruby 1.6.8

I didn’t. Just double-checked it and even recompiled mod_ruby (1.1.1)
and installed it again. It’s surely ruby 1.8.0 and mod_ruby 1.1.1
compiled with the same version.

(Thanks for the suggestions, though…)

/ David

ts wrote:

For ruby

require ‘/usr/local/lib/ruby/1.8/singleton.rb’

is different (it think that it’s another module)

require ‘singleton’ # which is in repository.rb

This is why it give an error

I have never understood this: why doesn’t Ruby store the cannonical
(fully expanded) path name; that would eliminate this kind of problem.

ruby 1.8.0 installs its libraries in a different place… so just to be
sure, try
puts RUBY_VERSION
in your mod_ruby application and see what it reports.

Cheers,

Brian.

···

On Tue, Aug 05, 2003 at 08:22:39PM +0900, David Heinemeier Hansson wrote:

Another reason is that you mix

  • ruby 1.8.0
  • mod_ruby built with ruby 1.6.8

I didn’t. Just double-checked it and even recompiled mod_ruby (1.1.1)
and installed it again. It’s surely ruby 1.8.0 and mod_ruby 1.1.1
compiled with the same version.

Right. But the log4r.rbx doesn't contain any requirements to include
the singleton module more than once. It's _only_ those three lines:

What is your complete configuration. I can reproduce it with auto-load

svg% ruby -rauto-reload -rlog4r -rsingleton -e 1
/usr/local/lib/ruby/1.8/singleton.rb:119: warning: already initialized constant FirstInstanceCall
/usr/local/lib/ruby/1.8/singleton.rb:161:in `undef_method': undefined method `extend_object' for `Singleton' (NameError)
        from /usr/local/lib/ruby/1.8/singleton.rb:161
svg%

I didn't. Just double-checked it and even recompiled mod_ruby (1.1.1)
and installed it again. It's surely ruby 1.8.0 and mod_ruby 1.1.1
compiled with the same version.

The apache server was restarted ?

Try to give it the command

    HEAD / HTTP/1.0

to see what it say

Guy Decoux

I always thought this isn’t done, so the programmer isn’t deluded a
false sense of security: There isn’t always the cannonical
path. Think of filesystems that ignore the case in filenames. (Not to
mention (symbolic and hard) links or alias names (single~1.rb)

···

On Tue, Aug 05, 2003 at 11:09:19PM +0900, Dave Thomas wrote:

ts wrote:

require ‘/usr/local/lib/ruby/1.8/singleton.rb’

is different (it think that it’s another module)

require ‘singleton’ # which is in repository.rb

I have never understood this: why doesn’t Ruby store the cannonical
(fully expanded) path name; that would eliminate this kind of problem.


marko schulz

ruby 1.8.0 installs its libraries in a different place… so just to be
sure, try
puts RUBY_VERSION
in your mod_ruby application and see what it reports.

I’ve reduced the test case to this:

singleton.rbx:

require “singleton”

That doesn’t work (same complaining about trying to import the same
class twice). This does, though:

require “/usr/local/lib/ruby/1.6/singleton.rb”

But “puts RUBY_VERSION” from a .rbx gives me 1.8.0, so it must be
running from the newest version. But mod_ruby is obviously still using
the 1.6 singleton.rb file. I just can’t figure out why.

/ David

What is your complete configuration. I can reproduce it with auto-load

OS X 10.2.6, mod_ruby 1.1.1, Apache 1.3.27, ruby 1.8.0

The apache server was restarted ?

Yearh. I even rebooted the entire machine.

Try to give it the command

HEAD / HTTP/1.0

HTTP/1.1 200 OK
Server: Apache/1.3.27 (Darwin) mod_ruby/1.1.1 PHP/4.3.2
Content-Type: text/html

(Thanks again for helping me to locate this…)

/ David

···

Date: Tue, 05 Aug 2003 12:03:17 GMT

Marko Schulz wrote:

I have never understood this: why doesn’t Ruby store the cannonical
(fully expanded) path name; that would eliminate this kind of problem.

I always thought this isn’t done, so the programmer isn’t deluded a
false sense of security: There isn’t always the cannonical
path. Think of filesystems that ignore the case in filenames. (Not to
mention (symbolic and hard) links or alias names (single~1.rb)

  1. Would it be worse than it is now?

  2. Why couldn’t Ruby make the name cannonical. On a Unix-based box, for
    example, it could store the i-node number. Surely every operating system
    has some way to get to a single target identifier for a given file?

Cheers

Dave

I see the argument, but I don’t really like it. One thing I like about
Java (which seems more OO than Ruby) is that when you import something,
what you’re specifying is classes rather than files. I like this idea
because the classes are what you really care about anyhow.

Another problem which is starting to pop up is versioning. In Perl you
can require that what you’re using has a certain version number. This
is a good “sanity check” when you’re using libraries which may
fluctuate (like the CGI one for example).

Is it too late to come up with an alternative to ‘require’?

Ben

···

On Tuesday, August 5, 2003, at 10:35 AM, Marko Schulz wrote:

On Tue, Aug 05, 2003 at 11:09:19PM +0900, Dave Thomas wrote:

I have never understood this: why doesn’t Ruby store the cannonical
(fully expanded) path name; that would eliminate this kind of problem.

I always thought this isn’t done, so the programmer isn’t deluded a
false sense of security: There isn’t always the cannonical
path. Think of filesystems that ignore the case in filenames. (Not to
mention (symbolic and hard) links or alias names (single~1.rb)

I see the argument, but I don’t really like it. One thing I like about
Java (which seems more OO than Ruby) is that when you import something,
what you’re specifying is classes rather than files. I like this idea

Only because Java enforces a given directory structure. If you had the
same constraint in Ruby, require would be similar to import.

Is it too late to come up with an alternative to ‘require’?

Nothing prevents you from writing it, making it compatible with require
and getting some people to use it :slight_smile:

···

On Tue, Aug 05, 2003 at 11:50:07PM +0900, Ben Giddings wrote:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

How do you power off this machine?
– Linus, when upgrading linux.cs.helsinki.fi, and after using the machine for several months

I always thought this isn’t done, so the programmer isn’t deluded a
false sense of security: There isn’t always the cannonical
path. Think of filesystems that ignore the case in filenames. (Not to
mention (symbolic and hard) links or alias names (single~1.rb)

  1. Would it be worse than it is now?

Depends on whether you think false security is better than no
security. :slight_smile:

  1. Why couldn’t Ruby make the name cannonical. On a Unix-based
    box, for example, it could store the i-node number.

Technically, it would have to be the pair (device, i-node number),
but it’s a good thought. If the file to be loaded has the same
(d,i) as a file already loaded, don’t load it again. It would
require more OS-specific code in the loader, though.

Surely every operating system
has some way to get to a single target identifier for a given file?

I would be wary of making such an assumption. It seems logical,
but OSes are often illogical creatures, at least on the surface. :slight_smile:

-Mark

···

On Tue, Aug 05, 2003 at 11:46:07PM +0900, Dave Thomas wrote:

Marko Schulz wrote:

I have never understood this: why doesn’t Ruby store the cannonical
(fully expanded) path name; that would eliminate this kind of problem.

I always thought this isn’t done, so the programmer isn’t deluded a
false sense of security: There isn’t always the cannonical
path. Think of filesystems that ignore the case in filenames. (Not to
mention (symbolic and hard) links or alias names (single~1.rb)

  1. Would it be worse than it is now?

If there is a solution: Great!

I don’t really have enough cross-platform knowledge to decide whether
it is possible; but on the platforms I know, it already seems awkward,
to detect for sure, whether a file already has been read (maybe
somebody should look how others do it):

  1. Why couldn’t Ruby make the name cannonical. On a Unix-based box, for
    example, it could store the i-node number.

Besides the number of the i-node you would at least have to store the
mount point (or the device?). Here I have to files on different
devices with the same inode:
$ ls -di /usr/include/netax25 /home/marko/musik/Farin_Urlaub/Endlich_Urlaub
374641 /home/marko/musik/Farin_Urlaub/Endlich_Urlaub
374641 /usr/include/netax25

But even then, I wouldn’t be sure, that every filesystem on every
unices uses reliable i-node numbers.

Under Linux they are at least not stable across remounts for a FAT-FS:

$ ls -i /mnt/dos/autoexec.bat
67756 /mnt/dos/autoexec.bat
$ sudo umount /mnt/dos/
$ sudo mount /mnt/dos/
$ ls -i /mnt/dos/autoexec.bat
67757 /mnt/dos/autoexec.bat

Surely every operating system
has some way to get to a single target identifier for a given file?

Maybe just storing the full path is already enough (resolving all …/'s
and maybe symlinks). Maybe storing a md5sum for each file is enough.
Maybe somebody just has to try it.

···

On Tue, Aug 05, 2003 at 11:46:07PM +0900, Dave Thomas wrote:


marko schulz

I don’t really have enough cross-platform knowledge to decide whether
it is possible; but on the platforms I know, it already seems awkward,
to detect for sure, whether a file already has been read (maybe
somebody should look how others do it):

In C, the file itself is responsible for this:

#ifndef _myheader_h
#define _myheader_h
… do stuff
#endif

Maybe storing a md5sum for each file is enough.

Cool idea, except the cost of reading in a file every time you see
require ‘foo’
means that it would break things. I quite happily put a ‘require’ buried
inside a loop at the moment, knowing that the library will get read just the
first time it’s needed.

Cheers,

Brian.

···

On Wed, Aug 06, 2003 at 01:06:46AM +0900, Marko Schulz wrote:

You would also need the constraint that files and classes be named
the same. I am tending towards that constraint in my own code anyway,
primarily so the target of ‘require’ resolves as a ctag, easing
navigation.

Another difference between Java and Ruby here: Java allows only one
“public” class in each file. Not that Ruby has the concept of
packages anyway.

Gavin

···

On Wednesday, August 6, 2003, 1:01:44 AM, Mauricio wrote:

On Tue, Aug 05, 2003 at 11:50:07PM +0900, Ben Giddings wrote:

I see the argument, but I don’t really like it. One thing I like about
Java (which seems more OO than Ruby) is that when you import something,
what you’re specifying is classes rather than files. I like this idea

Only because Java enforces a given directory structure. If you had the
same constraint in Ruby, require would be similar to import.