Feature #8393
closedA class who's parent class is in a module can go wrong if files are required in the wrong order
Description
Hi,
I have found that inheritance is not done properly in a certain case. Let's say we have the following files:
animal.rb -
class Animal
def bark
puts 'fuck.'
end
end
dog.rb -
module Bark
class Dog < Animal
end
end
bark.rb -
module Bark
class Animal
def bark
puts 'woof'
end
end
end
If these files are required in that order (or any order where Bark::Animal is not required before Animal), Bark::Dog.new.bark will output "fuck.", showing the inheritance was done wrong, because in the case that there are two classes from which it can inherit (Animal and Bark::Animal), it should inherit from the class inside its module (Bark).
A workaround for this is defining Dog as Dog < Bark::Animal, that forces Dog to use the correct Animal class.
I found this on the latest 1.8.7, 1.9.2, 1.9.3 and 2.0.0dev, both using rvm and without using it. I could not find information about this on the issue tracker or on Google.
In my opinion a way to fix this is to check when a file is required if any of our current files could inherit from something in a module of the file that is imported, but that looks like it can be complicated with nested modules, etc.. so I'm all ears for better design decisions.
I would like to fix this myself as my first Ruby core contribution, but I was unsure if this is an actual bug. To me it looks like this behavior is totally unexpected, let me know if anything is wrong here.
Thanks!
Updated by jeremyevans0 (Jeremy Evans) over 11 years ago
This isn't a bug. If you load animal.rb then dog.rb, at the point Bark::Dog is defined, Bark::Animal does not exist, so it finds Animal at the top level (standard ruby constant lookup). If you want to force the behavior you desire, you should probably be specific about the superclass (class Dog < Bark::Animal) and require bark.rb at the top of dog.rb (to make sure Bark::Animal is defined before Bark::Dog).
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
- Tracker changed from Bug to Feature
- Status changed from Open to Feedback
It's a feature by the design.
I can't get what you expect from your code.
You will need more concrete proposal.
Updated by eLobato (Daniel Lobato Garcia) over 11 years ago
jeremyevans0: That's what I did when I found a problem caused by this on production code. Nonetheless you have to take into account that sometimes the order of the requires is not something you control.
nobu: In order to get these results, create the files, open IRB and run:
require 'animal.rb'
require 'dog.rb'
require 'bark.rb'
Bark::Dog.new.bark
fuck. <--- This should be woof because Bark::Dog should inherit from Bark::Animal.
Let me know if I am missing anything.
Updated by injekt (Lee Jarvis) over 11 years ago
Nobu you don't need the requires to reproduce. This is simply the same thing:
#!/usr/bin/env ruby
class Animal
def bark
puts 'fuck.'
end
end
module Bark
class Dog < Animal
end
end
module Bark
class Animal
def bark
puts 'woof'
end
end
end
Bark::Dog.new.bark
I don't think this is unexpected at all. I think Jeremy pretty much nailed the reasons why.
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
I know the reason of course.
The point is how others reading your code can guess your intention from the code.
Can you explain why the result is "unexpected"?
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
One idea I'm thinking now is warning when a constant already introduced from the top-level is assigned.
Updated by eLobato (Daniel Lobato Garcia) over 11 years ago
This error showed up in a Rails app, on my code I had two different files (ProxyAPI::Resource and ProxyAPI::BMC < Resource), and somehow there was a separated Resource class defined by a loaded gem. Then ProxyAPI::BMC was inheriting from that one instead of ProxyAPI::Resource, which caused problems.
The warning could happen at initialization, but if a constant inside the module is added, IMO it should be inherited. Nonetheless I don't know too much about constant lookup at the moment, could you point me to the relevant parts of the code base or something like that?
My only concern with the warning showing up just when the class is defined, is that maybe it will be hard to notice, but it's surely better than no warning at all.
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
The warning could happen at initialization, but if a constant inside the module is added, IMO it should be inherited.
You can't inherit a constant which is not defined at that time yet.
Even if same name constant is defined later, it isn't concerned.
Updated by Anonymous over 11 years ago
OMG, why puts 'fuck'? ;_;
Updated by jonforums (Jon Forums) over 11 years ago
boris_stitnicky (Boris Stitnicky) wrote:
OMG, why puts 'fuck'? ;_;
It's bait to see who gets distracted by irrelevant minutiae.
People curse. Some even scamper promisingly close to wit. Who cares. Move on. ;)
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
OMG, why puts 'fuck'? ;_;
Ruby doesn't prevent you from fucking yourself.
Updated by rkh (Konstantin Haase) over 11 years ago
eLobato (Daniel Lobato Garcia) wrote:
This error showed up in a Rails app, on my code I had two different files (ProxyAPI::Resource and ProxyAPI::BMC < Resource), and somehow there was a separated Resource class defined by a loaded gem. Then ProxyAPI::BMC was inheriting from that one instead of ProxyAPI::Resource, which caused problems.
The warning could happen at initialization, but if a constant inside the module is added, IMO it should be inherited. Nonetheless I don't know too much about constant lookup at the moment, could you point me to the relevant parts of the code base or something like that?
My only concern with the warning showing up just when the class is defined, is that maybe it will be hard to notice, but it's surely better than no warning at all.
Warnings would need to be really really smart or a lot of libraries will start printing warnings (for instance, rack defines Rack::File). I have to say, the whole issue would not have occurred if you had either required bark from dog (which is what require is for) or, if you want to use Rails' autoloading magic, used the qualified constant name (ie inherit from Bark::Animal).
Updated by eLobato (Daniel Lobato Garcia) over 11 years ago
Fair point rkh, feel free to close this unless there is something to avoid printing lots of warnings.
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
- Status changed from Feedback to Rejected